{"id":13646640,"url":"https://github.com/blablacar/dgr","last_synced_at":"2025-04-21T21:31:21.192Z","repository":{"id":34873597,"uuid":"38881222","full_name":"blablacar/dgr","owner":"blablacar","description":"Container build and runtime tool","archived":true,"fork":false,"pushed_at":"2021-03-12T15:13:59.000Z","size":94801,"stargazers_count":249,"open_issues_count":40,"forks_count":21,"subscribers_count":60,"default_branch":"master","last_synced_at":"2024-11-09T20:37:44.925Z","etag":null,"topics":["aci","appc","builder","containers","ep","owner-ep","pod","rkt"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/blablacar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-07-10T13:14:29.000Z","updated_at":"2024-10-16T10:43:09.000Z","dependencies_parsed_at":"2022-09-13T04:02:23.389Z","dependency_job_id":null,"html_url":"https://github.com/blablacar/dgr","commit_stats":null,"previous_names":["blablacar/cnt"],"tags_count":98,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blablacar%2Fdgr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blablacar%2Fdgr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blablacar%2Fdgr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blablacar%2Fdgr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/blablacar","download_url":"https://codeload.github.com/blablacar/dgr/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250136683,"owners_count":21380876,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aci","appc","builder","containers","ep","owner-ep","pod","rkt"],"created_at":"2024-08-02T01:03:01.674Z","updated_at":"2025-04-21T21:31:17.286Z","avatar_url":"https://github.com/blablacar.png","language":"Go","readme":"# dgr - container build and runtime tool\n\n[![Go Report Card](https://goreportcard.com/badge/github.com/blablacar/dgr)](https://goreportcard.com/report/github.com/blablacar/dgr)\n[![GoDoc](https://img.shields.io/badge/godoc-reference-5874B0.svg)](https://godoc.org/github.com/blablacar/dgr)\n[![Build Status](https://img.shields.io/travis/blablacar/dgr/master.svg)](https://travis-ci.org/blablacar/dgr)\n\n\u003cimg src=\"https://raw.githubusercontent.com/blablacar/dgr/gh-pages/logo.png\" width=\"300\"\u003e\n\n**dgr** (pronounced \"*digg-er*\") is a command line utility designed to build and to configure at runtime App Containers Images ([ACI](https://github.com/appc/spec/blob/master/spec/aci.md)) and App Container Pods ([POD](https://github.com/appc/spec/blob/master/spec/pods.md)) based on convention over configuration.\n\ndgr allows you to build generic container images for a service and to configure them at runtime. Therefore you can use the same image for different environments, clusters, or nodes by overriding the appropriate attributes when launching the container.\n\ndgr will follow evolution of **rkt**. Since rkt will tend to support [Open Container Initiative](https://coreos.com/blog/oci-image-specification.html), dgr will support **oci** too.\nAt some point, since **rkt** and **docker** will support **oci**, we will probably support building with and for both.\n\n_dgr is actively used at blablacar to build and run more than 300 different aci and pod to [run all our platforms](http://blablatech.com/blog/why-and-how-blablacar-went-full-containers)._\n\n\n## Build the ACI once, configure your app at runtime.\n\n_You can also have a look at [examples](https://github.com/blablacar/dgr/tree/master/examples) that uses most of the features_\n\ndgr provides various resources to build and configure an ACI:\n\n- scripts at runlevels (build, prestart...)\n- templates and attributes\n- static files\n- images dependencies\n\n**Scripts** are executed at the image build, before your container is started and more. See [runlevels](#runlevels) for more information.\n\n**Templates** and **attributes** are the way dgr deals with environment-specific configurations. **Templates** are stored in the image and resolved at runtime ; **attributes** are inherited from different contexts (aci -\u003e pod -\u003e environment).\n\n**Static files** are copied to the same path in the container.\n\n**Image dependencies** are used as defined in [APPC spec](https://github.com/appc/spec/blob/master/spec/aci.md#dependency-matching).\n\n\n![demo](https://raw.githubusercontent.com/blablacar/dgr/gh-pages/aci-dummy.gif)\n\n## Installation\n\nDownload the executable from the [releases page](https://github.com/blablacar/dgr/releases) and put it somewhere. We currently only build for linux 64 bit.\n\n## Commands\n\n```bash\n$ dgr init          # init a sample project\n$ dgr build         # build the image\n$ dgr clean         # clean the build\n$ dgr clean build   # just building, clean is always run before building\n$ dgr clean install # clean, build and install aci in the local rkt\n$ dgr clean push    # clean, build and push aci to remote storage\n$ dgr clean test    # clean, build and test aci\n$ dgr install       # use already built aci in target directory to install in rkt\n$ dgr push          # use already built aci in target directory to push to remote storage\n$ dgr test          # run tests on already built aci\n$ dgr try           # run templating only to target/try (experimental)\n$ dgr graph         # generate graph image of .dot file of dependencies (app, builder and tester)\n```\n\nThere is a lot of different flags on each command. use the helper to see them :\n```bash\n$ dgr --help\n...\n$ dgr build --help\n...\n```\n\n## Configuration file\n\n*Global configuration is optional to start as soon as you have rkt in $PATH*\n\ndgr global configuration is a yaml file located at `~/.config/dgr/config.yml`. Home is the home of starting user (the caller user if running with sudo).\n\n**targetWorkDir** is used to indicate the target work directory where dgr will work to build and create the ACI\n**push*** contain informations on how to push the aci/pod to remote storage\n**rkt** if you are not using rkt in your path, or want to create specif config\n\nExample of configuration:\n\n```yml\ntargetWorkDir: /tmp/target      # if you want to use another directory for all builds\nrkt:                            # arguments to rkt. See rkt --help\n  path:\n  insecureOptions: [image]\n  dir: /var/lib/rkt\n  localConfig: /etc/rkt\n  systemConfig: /usr/lib/rkt\n  userConfig:\n  trustKeysFromHttps: false\n  noStore: false                # can be set by command line\n  storeOnly: false              # can be set by command line\n```\n\n\n# Building an ACI\n\n## Initializing a new project\n\nRun the following commands to initialize a new complete sample project:\n\n```bash\n$ mkdir aci-myapp\n$ cd aci-myapp\n$ dgr init\n```\n\nIt will generate the following file tree:\n\n```text\n.\n|-- attributes\n|   `-- attributes.yml                 # Attributes files that will be merged and used to resolve templates\n|-- aci-manifest.yml                   # Manifest\n|-- templates\n|   |-- etc\n|   |   |-- templated.tmpl             # template file that will end up at /etc/templated\n|   |   `-- templated.tmpl.cfg         # configuration of the targeted file, like user and mode (optional file)\n|   `-- header.partial                 # template part that can be included in template files\n|-- files\n|   `-- dummy                          # Files to be copied to the same location in the target rootfs\n|-- runlevels\n|   |-- builder\n|   |   `-- 10.prepare.sh              # Scripts to be run inside the builder to prepare the aci for build\n|   |-- build\n|   |   `-- 10.install.sh              # Scripts to be run when building inside aci's rootfs\n|   |-- build-late\n|   |   `-- 10.build-late.sh           # Scripts to be run when building inside aci's rootfs after the copy of files\n|   |-- inherit-build-early\n|   |   `-- 10.inherit-build-early.sh  # Scripts stored in ACI and executed while used as a dependency\n|   |-- inherit-build-late\n|   |   `-- 10.inherit-build-late.sh   # Scripts stored in ACI and executed while used as a dependency\n|   |-- prestart-early\n|   |   `-- 10.prestart-early.sh       # Scripts to be run when starting ACI before templating\n|   `-- prestart-late\n|       `-- 10.prestart-late.sh        # Scripts to be run when starting ACI after templating\n`-- tests\n    |-- dummy.bats                     # Bats tests for this ACI\n    `-- wait.sh                        # Script to wait until the service is up before running tests\n```\n\nThis project is already valid which means that you can build it and it will result in a runnable ACI (dgr always adds busybox to the ACI). But you probably want to customize it at this point.\n\nThe only mandatory information is the `aci-manifest.yml`, with only the aci `name:`. You can remove everything else depending on your needs.  \n\n## Nice other features\n\n- builder runlevel with dependencies allow you build a project of any kind (java, php, go, node, ...) and release an aci without anything else than dgr and rkt on the host\n- dgr will tell you if you are not using the latest version of a dependency and will tell you which version is the latest\n- integrated test system that can be extended to support any kind of test system\n- working with [pods](https://github.com/appc/spec/blob/master/spec/pods.md) as a unit during build too\n- build application version based on container name\n- extract aci version from the version of the software during installation (templating of manifest)\n\n## How it's working\n\u003cimg style=\"margin: 10px 30px 40px 0\" src=\"https://docs.google.com/drawings/d/1bSP6Z2X79xkp6deSNaZ-ShrAPjAPa4bzyjL4df2HLwk/pub?w=850\"\u003e\n\ndgr uses the **builder** information from the **aci-manifest.yml** to construct a rkt stage1. dgr then start rkt with this stage1 on an empty container with the final manifest of your aci (to have dependencies during build).\n\nInside rkt, the builder isolate the build process inside a **systemd-nspawn** on the builder's rootfs (with mount point on the final aci's rootfs and aci's home) and run the following steps :\n- use internal dgr filesystem (busybox, openssl, wget, curl) for the builder if no dependencies (nothing in /usr/bin)\n- run **builder** runlevel\n- copy **templater** and **inherit** runlevels\n- isolate on final rootfs and run **build** runlevels\n- copy **prestart**, **attributes**, **files**, **templates**\n- isolate on final rootfs and run **build-late** runlevels\n\nTo import dynamically environment vars to builder, you can use `dgr --set-env=TOTO=TITI`. This is usefull to import proxy settings without explicitly knowing the proxy inside the build process. \n\nAlso all `DGR_ENV_*` vars are automatically imported. \nex: having `export DGR_ENV_no_proxy=127.0.0.1`  on the host will end up having `no_proxy=127.0.0.1` in builder.\n\n## Customizing\n\n### The manifest\n\nThe dgr manifest looks like a light ACI manifest with extra builder and tester info. \ndgr will take the `aci` part and convert it to the format defined in the APPC spec.\n\nExample of a *aci-manifest.yml*:\n\n```yaml\nname: example.com/myapp:0.1\n\nbuilder:\n  dependencies:\n    - example.com/base:1\n\ntester:\n  aci:\n    dependencies:\n      - example.com/base:1\n    \naci:\n  dependencies:\n    - example.com/base:1\n  app:\n    exec:\n      - /bin/myapp\n      - -c\n      - /etc/myapp/myapp.cfg\n    mountPoints:\n      - name: myapp-data\n        path: /var/lib/myapp\n        readOnly: false\n```\n\nThe **name**, well, is the name of the ACI you are building.\n\n#### Builder\n\n**builder** node is configuration of the filesystem you will use to build your ACI.\nBy default, this filesystem only contain a busybox. When you set builder dependencies to handle specific build mechanism. (like archlinux or gentoo in the examples)\n\n```yaml\nbuilder:\n  dependencies:\n    - example.com/aci-maven\n    - example.com/aci-java\n  mountPoints:\n    - {from: ../, to: /code}\n    - {from: ~/.m2, to: /root/.m2} \n```\n\nThere is also a `mountPoints` node to mount directories to the builder. This is usefull to have some external cache between builds (like `.m2` maven directory) and also to trigger code build inside the builder, and release as an aci.\n\nHere is an example of a builder script that can be used to do so.\n```bash\n#!/dgr/bin/busybox sh\nset -e\nsource /dgr/bin/functions.sh\nisLevelEnabled \"debug\" \u0026\u0026 set -x\n\nmvn -f /code clean verify\ncp /code/target/project.jar ${ROOTFS}/\nmvn -f /code clean\n```\n\n#### ACI\n\nUnder the **aci** key, you can add every key that is defined in the [APPC spec](https://github.com/appc/spec/blob/master/spec/aci.md) such as:\n\n- **exec** which contains the absolute path to the executable your want to run at the start of the ACI and its args.\n- **mountPoints** even though you can do it on the command line with recent versions of RKT.\n- **isolators**...\n\nExcept **handlers** that are directly mapped to **prestart** runlevels \n\n### Runlevels\n\nThe scripts in `runlevels/build` dir are executed during the build to install in the ACI everything you need. For instance if your dependencies are based on debian, a build script could look like:\n\n```bash\n#!/bin/bash\napt-get update\napt-get install -y myapp\n```\n\n### Templates\n\nYou can create templates in your ACI. Templates are stored in the ACI as long as attributes and are resolved at start of the container.\n\nExample:\n\n*templates/etc/resolv.conf.tmpl* \n\nYou can also use _templates/etc/resolv.tmpl.conf_ filename format to keep IDE language detection\n\n```\n{{ range .dns.nameservers -}}\nnameserver {{ . }}\n{{ end }}\n\n{{ if .dns.search -}}\nsearch {{ range .dns.search }} {{.}} {{end}}\n{{end}}\n```\n\n*templates/etc/resolv.conf.tmpl.cfg*\n\n```\nuid: 0\ngid: 0\nmode: 0644\ncheckCmd: /dgr/bin/busybox true\n```\n\n`checkCmd` is a command to run after the templating to check that the configuration is valid or fail container start.\n\nWhen you have to reuse the same part in multiple templates, you can create a partial template like defined in the [go templating](https://golang.org/pkg/text/template/#hdr-Nested_template_definitions).\n\n*templates/header.partial*\n\n```\n{{define \"header\"}}\nwhatever\n{{end}}\n```\n\nand include it in a template:\n\n```\n{{template \"header\" .}}\n```\n\nTemplater provides functions to manipulate data inside the template. Here is the list:\n\n| Tables    |      Function        |  Description                                                |\n|-----------|:---------------------|:------------------------------------------------------------|\n| base      | path.Base            |                                                             |\n| split     | strings.Split        |                                                             |\n| json      | UnmarshalJsonObject  |                                                             |\n| jsonArray | UnmarshalJsonArray   |                                                             |\n| dir       | path.Dir             |                                                             |\n| getenv    | os.Getenv            |                                                             |\n| join      | strings.Join         |                                                             |\n| datetime  | time.Now             |                                                             |\n| toUpper   | strings.ToUpper      |                                                             |\n| toLower   | strings.ToLower      |                                                             |\n| contains  | strings.Contains     |                                                             |\n| replace   | strings.Replace      |                                                             |\n| orDef     | orDef                | if first element is nil, use second as default              |\n| orDefs    | orDefs               | if first array param is empty use second element to fill it |\n| ifOrDef   | ifOrDef              | if first param is not nil, use second, else third           |\n| add       | add                  | add 2 numbers                                               |\n| mul       | mul                  | mutiply 2 numbers                                           |\n| div       | div                  | divide 2 numbers                                            |\n| sub       | sub                  | substract 2 numbers                                         |\n| mod       | mod                  | modulo of 2 numbers                                         |\n| howDeep   | HowDeep              | deep level of an object in tree structure. usefull for indent|\n| isMapLast | isMapLast            | is last element of a sorted keys map                        |\n| isMapFirst| isMapLast            | is first element of a sorted keys map                       |\n| isString  | isString             |                                                             |\n| isArray   | isArray              |                                                             |\n| isMap     | isMap                |                                                             |\n| isKind    | isKind               |                                                             |\n| isType    | isType               |                                                            |\n\nIt also provide all function defined by [gtf project](https://github.com/leekchan/gtf)\n\n*We can add functions on demand*\n\n### Attributes\n\nAll the YAML files in the directory **attributes** are read by dgr. The first node of the YAML has to be \"default\" as it can be overridden in a POD or with a json in the env variable TEMPLATER_OVERRIDE in the cmd line.\n\n*attributes/resolv.conf.yml*\n\n```\ndefault:\n  dns:\n    nameservers:\n      - \"8.8.8.8\"\n      - \"8.8.4.4\"\n    search:\n      - bla.com\n  myAttribute: \"{{index .dns.nameservers 0}}\" # example of templating in attributes\n```\n\n### Prestart\n\ndgr uses the \"pre-start\" eventHandler of the ACI to customize the ACI rootfs before the run depending on the instance or the environment.\nIt resolves at that time the templates so it has all the context needed to do that.\nYou can also run custom scripts before (prestart-early) or after (prestart-late) this template resolution. This is useful if you want to initialize a mountpoint with some data before running your app for instance.\n\n*runlevels/prestart-late/init.sh*\n\n```bash\n#!/bin/bash\nset -e\n/usr/bin/myapp-init\n```\n\n\n\n\n## Running the aci\n\nAt this stage you should have a runnable aci. During build, dgr integrated into the aci a prestart that will take care of running templater using `templates` and `attributes`\n\n### log level\nTemplates and default attribute values are integrated into the aci.\nAt start you can change log level of prestart scripts and the templater with the environment variable `--set-env=LOG_LEVEL=trace`.\ndefault level is info. At `debug`, prestart shell script will activate debug (set -x). At level `trace`, templater will display the result of templating.\n\n\n### Override template's attributes\nDefault attributes values integrated in the aci can be overridden by adding a json tree in the environment variable `TEMPLATER_OVERRIDE`\n\n\n### example\n```\n# sudo rkt --set-env=LOG_LEVEL=trace  --net=host --insecure-options=image run --interactive target/image.aci '--set-env=TEMPLATER_OVERRIDE={\"dns\":{\"nameservers\":[\"10.11.254.253\",\"10.11.254.254\"]}}'\n```\n\n## Troubleshoot\ndgr start by default with info log level. You can change this level with the `-L` command line argument.\nThe log level is also propagated to all runlevels with the environment variable: **LOG_LEVEL**.\n\nYou can activate debug on demand by including this code in your scripts:\n\n```\n#!/dgr/bin/busybox sh\nset -e\n. /dgr/bin/functions.sh\nisLevelEnabled \"debug\" \u0026\u0026 set -x\n```\nBuild it\n```bash\n$ dgr -L debug build\n```\n\nYou can also debug the start of your container (prestart, templates) the same way\n```bash\n$ rkt run --set-env=LOG_LEVEL=debug example.com/my-app\n```\n\n**trace** loglevel, will tell the templater to display the result\n\n# Push an aci and run from repository\n\ndgr is compatible with the appc push spec.\nHere is a example of how to test the push on local, without **tls** and **signature** \n\nFirst you need an appc push spec compatible server. [acserver](http://github.com/appc/acserver) is an official minimal implementation, but require aci signature.\nHere is a fork version where you can push non signed aci [github.com/blablacar/acserver](http://github.com/blablacar/acserver)\n\n### Start the server\n\nRun the server :\n```bash\n$ mkdir /tmp/acserver\n$ cd /tmp/acserver\n$ wget https://github.com/blablacar/acserver/releases/download/0.1/acserver.tar.gz\n$ tar xvzf acserver.tar.gz\n$ rm acserver.tar.gz\n$ sudo ./acserver -port 80 /tmp/acis admin password\n2016/06/14 10:27:21 Listening on :80\n```\n\nTell your system that aci.example.com is localhost :\n\n/etc/hosts\n```\n...\n127.0.0.1 aci.example.com\n```\n\n### Build and push the aci\n\nTell dgr using rkt conf, how to access the server with authentication :\n\n/etc/rkt/auth.d/aci.example.com.json\n```json\n{\n\t\"rktKind\": \"auth\",\n\t\"rktVersion\": \"v1\",\n\t\"domains\": [\"aci.example.com\"],\n\t\"type\": \"basic\",\n\t\"credentials\": {\n\t\t\"user\": \"admin\",\n\t\t\"password\": \"password\"\n\t}\n}\n```\n\ntell dgr that you do not support **tls** (and **image** signature) :\n\n~/.config/dgr/config.yml\n```\n...\nrkt:\n  insecureOptions: [http, image]\n```\n\nInit an aci that belong to aci.example.com\n```bash\n$ sudo dgr -W /tmp/aci-dummy init\n...\n```\n\nBuild and push the aci to your repository\n```bash\n$ sudo dgr -W /tmp/aci-dummy clean push\n```\n\n### Run the aci\n\nRun the aci fetching from repository\n```bash\n$ sudo rkt run --insecure-options http,image --no-store aci.example.com/aci-dummy:1\n...\n```\n\n\n# Building a POD\n\nA pod is a group of aci that will build and run together as a single unit.\n\n### Standard FileTree for POD\nTODO\n\n```bash\n├── aci-elasticsearch               # Directory that match the pod app shortname (or name)\n│   ├── attributes\n│   │   └── attributes.yml          # Attributes file for templating in this ACI\n│   ├── files                       # Files to be inserted into this ACI\n│   ...\n├── pod-manifest.yml            # Pod Manifest\n```\n\n\n# Ok, but concretely how should I use it?\n\n*have a look at the examples/ directory where you can find aci for various distrib*\n\nDepending on distrib, package manager and what you want to do, you will not work the same way. but globally there is 2 way of building an aci.\n\n#### Building directly inside the aci\nThis is what you will see everywhere else in docker or rkt. You use the **build** and **build-late** runlevels and run commands on the the final rootfs (like apt-get install...)\n\n#### Building outside of the aci\nIf you are using a package manager that support working outside of the target's rootfs or want to build a project, you will work outside of the stage1 directly inside the builder.\nFor example if you are buiding an aci for a go project from sources. you will prepare a **builder** with **go** to be able to build the project on the stage1 and put the binary on the aci's rootfs (go is not needed to run the aci).\n\n*At this step, everybody can build any kind of project, since nothing on the host is used to build the project and the aci.*\n\nAlso, if you are using a package manager like `pacman` or `emerge`, you can build and install packages on the final **rootfs** without build dependencies nor the package manager.\n\n#### Note About dependencies\n\nMost package manager are not design for overlay and are working with a db file for installed software. this means than when your aci have multiple dependencies on the aci, the db files will overlap and the package manager will only see half of package installed.\n\nAs far as I know only `pacman`, that uses a file tree structure for install package, can support overlay.\nIf you are using a debian or similar. I recommand to limit the dependencies to only 2 layers. The base aci with debian minimal fs and one with the application you want.\n\n\n# Comparison with alternatives\n\n### dgr vs Dockerfile\nA Dockerfile is purely configuration, describing the steps to build the container. It does not provide a common way of building containers across a team.\nIt does not provide scripts levels, ending with very long bash scripting for the run option in the dockerfile.\nIt does not handle configuration, nor at build time nor at runtime and does not support any kind of build outside of the container feature.\n\n### dgr vs acbuild\nacbuild is a command line tools to build ACIs. It is more flexible than Dockerfiles as it can be wrapped by other tools such as Makefiles but like Dockerfiles it doesn't provide a standard way of configuring the images.\n\n\n# Requirement\n- [rkt](https://github.com/rkt/rkt) in your `$PATH` or configured in dgr global conf\n- being root is required to call rkt\n- linux \u003e= 3.18 with overlay filesystem\n\n\n# I want to extend dgr\nIf you think your idea can be integrated directly in the core of dgr, please create an issue or a pull request.\n\nIf you want want to extend the way the **builder** is working (attributes, templates, files, ...), you can create a new **stage1 builder** and replace the internal one with : \n```\n...\nbuilder:\n  image: blablacar.github.io/dgr/aci-builder:1\n...\n```\nYou can do the same for the **tester**.\n\n\n\n\n","funding_links":[],"categories":["Filesystem","Go"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblablacar%2Fdgr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblablacar%2Fdgr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblablacar%2Fdgr/lists"}