Home

Awesome

Project Automation for docker-compose

doco is an extensible tool for project automation and literate devops using docker-compose. All docker-compose subcommands are subcommands of doco, along with any custom subcommands you define in your project, user, or global configuration files.

In addition to letting you create custom commands and apply them to a configuration, you can also define your docker-compose configuration and custom commands as YAML, jq code, and shell functions embedded in a markdown file. In this way, you can document your project's configuration and custom commands directly alongside the code that implements them.

Last, but not least, doco supports many ways of applying commands to one or more containers, which can be explicitly listed, qualified by jq queries against your configuration, or specified using service groups. If group1 and group2 are groups you've defined in your configuration, then doco group1 group2 ps will expand to docker-compose ps [services in group1] [services in group 2]. You can also create subcommands that apply to a specific group or service by default, run single-container commands against each container in a group, and more.

Installation and Basic Use

doco is a mashup of loco (for project configuration and subcommands) and jqmd (for literate programming and jq support). You can install it with basher (i.e. via basher install bashup/doco), or just copy the binary to a directory on your PATH. You will need jq and docker-compose on your PATH as well, along with either a yaml2json command (such as this one), PyYAML, or yaml2json.php.

In its simplest use, doco can be used to add custom commands to an existing docker-compose project: just create a .doco file alongside your docker-compose.yml, and define bash functions in it using the doco API and CLI. A function named doco.X will define a doco subcommand X.

(Note: due to array handling bugs and quirks in earlier versions of bash, doco requires bash 4.4.)

Literate DevOps

While doco's basic project automation facilities are nice, doco's "killer app" is doing literate devops: combining infrastructure code, container configuration, documentation and possibly even tests in a single, well organized document. For example, this very README.md is a cram test as well as an example doco project file.

To create a new project, just make a file whose name ends in .doco.md, e.g.:

    $ cp $TESTDIR/README.md readme-example.doco.md

In that file, you can intermix docker-compose YAML blocks, jq code, and shell script to define your configuration and custom commands, like so:

# A `yaml` block defining some configuration
version: "2.1"
services:
  example1:
    image: bash
networks:
  default: {external: {name: bridge}}
# A `jq` block defining some filter code that alters the supplied YAML
.services.example1.command = "bash -c 'echo hello world; echo'"
# A `shell` block defining a new doco subcommand
doco.example() { echo "this is an example command"; }

Your commands and containers can then be used on the command line:

    $ doco example
    this is an example command

    $ doco --dry-run run --rm example1  # --dry-run prints commands instead of running them
    docker-compose run --rm example1

Your project document can include as many shell, yaml, and jq blocks as you like. yaml and jq blocks are processed in order, with the yaml being treated as if it were a jq filter assigning the contents of the block. The project document is processed using jqmd, so all of the languages and metaprogramming tricks of both jqmd and mdsh are supported. (You can define jq functions in jq defs blocks, for example, or generate code using mdsh blocks.)

Of course, you won't want to put sensitive data directly in your project document. So, just like with docker-compose, you can use an .env file.

You're also not limited to just the contents of your main project document to do configuration. The shell code embedded in your project document can use export-env to process additional docker-compose format .env files, or include to source other markdown documents with the same syntax. This can be useful for projects that want to be extensible, where a user can define local extension documents alongside a main project document that's kept in revision control.

Caching

doco uses various caches to speed up its operation, and must therefore have write access to your project's root directory. Currently, the cache files used are .doco-cache.json (which contains your project's generated configuration in JSON format) and .doco-cache.sh (which contains the compiled version of your *.doco.md file, if applicable). If you include any markdown files, their compiled versions are cached in .doco-cache/includes/, unless you explicitly declare a cache path for each file.

All of these files are only used by doco while it's running, so if it's not running, you can safely remove them. (But the next run of doco may be slower, since it will need to regenerate them.)