Preface

I recently worked on a Node project that used Docker. Docker generally uses a lot of commands that point to the same container name or file. Our build and deploy workflow was based on branch name so we could avoid accidental pushes from dev to production. It ends up being a lot of duplicate strings, which is no good.

Command Substitution

When I started out, everything was hardcoded. Obviously this wasn't going to last, so I set out to pull values dynamically. My tags looked something like this: reponame-branchname-servicename, which would eventually render something like express-o-dev-transcoder.

This command gives the remote repository name (requires git v2.7)

basename $(git remote get-url origin) .git

This command gives the current branch

git rev-parse --abbrev-ref HEAD

NOTE: these are Bash commands.

To concatenate the output of these two commands, I used command substitution.

Running $(basename $(git remote get-url origin) .git)-$(git rev-parse --abbrev-ref HEAD) will output reponame-branchname. This was great because I could run commands based on the current repo and branch without having to edit commands or remember to include branch name as an argument. Plus, it ensured I was on the appropriate branch for the build / deploy target.

Nesting commands in NPM scripts

If you're not familiar, NPM scripts is a convenient way to keep track of your regularly-used commands. It is an entry in your project's package.json. You can define a script with a key (the ref used to run it) and a value (the actual command to run).

Here is my script definition for info:repo which simply outputs the name of the remote repository.

  "scripts": {
    "info:repo": "basename $(git remote get-url origin) .git"
}

Run the command npm run info:repo to execute the script. You'll notice a few logs that npm spits out for you.

  $ npm run info:repo

> express-o@2.3.0 info:repo /express-o
> basename -s .git `git config --get remote.origin.url`

express-o

Now, we really only care about the output of the command. Adding --silent or -s to the npm run command will make that happen.

  $ npm run -s info:repo
express-o

At this point, we can start composing commands to output tags, and use those tags in other commands. Below, there are commands for getting repository name, current branch, service name, and a general tag that can be used in other commands. These info commands are used to create a docker tag, which is then used to build the container.

  "scripts":{
    "info:repo": "basename $(git remote get-url origin) .git",
    "info:branch": "git rev-parse --abbrev-ref HEAD",
    "info:service": "echo transcoder",
    "info:tag": "echo $(npm run -s info:repo)-$(npm run -s info:branch)-$(npm run -s info:service)",
    "docker:tag": "echo gcr.io/$(npm run -s info:repo)/$(npm run -s info:tag)",
    "docker:build": "docker build -t $(npm run -s docker:tag) ./"
}

Further exploration

Going forward, I might experiment with saving the command output to a variable, then referencing the variable. It would be more efficient than running the same command multiple times, and it might mean less typing overall. For now, this is working nicely.

I have my work on express-o, a repo I use to stash project boilerplate code. Here are the docker scripts.

If you have comments or suggestions, I'd love to hear them.


434 0 5