GitHub Workflows: Overriding A Service's Entrypoint
Lately I've been migrating an ancient Jenkins CI system over to GitHub Actions/Workflows. One of our Node.js package's tests rely on being able to hit local MongoDB and etcd containers.
To get set up for running unit tests within our Node.js package our Jenkins CI would build everything from a docker-compose.yml that looks about like this:
version: '3'
services:
our_node_package:
build: .
environment:
ETCD: etcd:2379
NODE_ENV: ci
links:
- mongodb
- etcd
mongodb:
image: mongo:4.0
hostname: mongodb
container_name: build_mongodb
command: mongod --nojournal --noprealloc --smallfiles
etcd:
image: microbox/etcd:2.1.1
entrypoint: "etcd --listen-client-urls http://0.0.0.0:2379 --advertise-client-urls http://0.0.0.0:2379"
hostname: etcd
container_name: build_etcd
expose:
- 2379You can assume there's aDockerfilethat includes Node and all that. Hence thebuild: .which just builds theDockerfilein the repositories root directory.
Translating this into a workflow might look something like this:
name: Node CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
mongo-version: [4.0]
services:
mongodb:
image: mongo:${{ matrix.mongo-version }}
ports:
- 27017:27017
etcd:
image: microbox/etcd:2.1.1
ports:
- 2379:2379
options: --hostname etcd --name build_etcd
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
registry-url: 'https://registry.npmjs.org'
- name: Install node modules
run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Run unit tests
run: npm test
env:
NODE_ENV: 'ci'
ETCD: etcd:2379However, there's one thing missing... The etcd entrypoint override from our docker-compose.yml. This bit:
entrypoint: "etcd --listen-client-urls http://0.0.0.0:2379 --advertise-client-urls http://0.0.0.0:2379"Overriding the Entrypoint
How can we do it? If we look at the documentation, there's no entrypoint field for a service like there is in a docker compose file. Jobs have it but not services. What services do have, is an options field. This allows you to add arbitrary docker create options, one of which is --entrypoint, which allows you to override a container's entrypoint.
But there's a problem.
Because we care about the parameters (--listen-client-urls http://0.0.0.0:2379 --advertise-client-urls http://0.0.0.0:2379) and this confusing shit, it ain't gonna work.
Here's the proof Actions doesn't support it (as of this writing).
The Workaround
Since GitHub Workflows are just yaml files defining commands to run on a VM there are a few ways around this.
My suggestion is, in your workflow, just run the friggin' docker-compose.yml. It'll handle entrypoint overrides properly.
Assuming there's a docker-compose.yml in the root of your repository Here's what the new workflow might look like:
name: Node CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build
run: docker-compose build --pull --force-rm our_node_package
- name: Test
run: docker-compose run --rm our_node_package test
- name: Cleanup
run: docker-compose down && docker-compose rm --forceThis will download images and run the containers just like you want, overridden entrypoint and all.
If you're pulling private Docker images you'll need to add adocker loginstep with adocker logoutstep at the end. Warning: Make sure you aren't hard coding any secrets into this file.
The Benefits
Using a docker compose file instead of defining the entire build process in a Workflow loosens the coupling between your CI system and GitHub's Workflow platform.
In my case we'll probably be using Docker containers for a long time but who knows how long we'll stick with GitHub for our CI system. A docker compose should work on any platform that runs Docker, including your dev environment.
It also means that you can easily run your tests in a container that matches what'd be running in production. It reduces the risk of something working on GitHub's VM but blowing up in the container you're running on whatever infrastructure your production system is in.
So, if you can, just run a docker-compose from your action.