Skip to content

Commit

Permalink
GitHub Action + Docker tag suffix (#16)
Browse files Browse the repository at this point in the history
* Metadata to make a GitHub Action

* Add options to suffix a tag and pass docker build options

* GHA: use a JavaScript action

The composite action didn't work as expected (the action's
`action.sh` and `build.sh` weren't accessible), so I replaced the
shell script with a tiny JavaScript bootstrap, that will properly
collect the action inputs and generate a simple local build.sh and
calls it.

Note that we couldn't use a Docker container action, because we
couldn't access the host docker ending, and would need to add both
docker cli and docker server to the docker container...

* fix: disable shellcheck warning (BUILD_OPTS _must_ be splat)

* Tests for -t suffix and -o buildOptions build arguments

* Remove already removed `latest` flag

See 5d99055

---------

Co-authored-by: Matias Garcia Isaia <[email protected]>
  • Loading branch information
ysbaddaden and matiasgarciaisaia authored Dec 11, 2023
1 parent 0558902 commit 0084f2b
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 18 deletions.
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,35 @@ jobs:
DOCKER_USER: ${{ secrets.DOCKER_USER }}
DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Build image & push to Docker Hub
run: ./build.sh
```
Alternatively you may skip the local `build.sh` script and use this repository
as a step action directly.

```yaml
jobs:
build:
runs-on: ubuntu-latest
# needs: test # you can declare a `test` job and uncomment this to test the app before building
env:
DOCKER_REPOSITORY: 'dockerhub_org/dockerhub_repo'
DOCKER_USER: ${{ secrets.DOCKER_USER }}
DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
steps:
- uses: actions/checkout@v3
- uses: manastech/ci-docker-builder@<sha1>
# with:
# skip-login: <true|false>
# repository: ""
# repository-suffix: ""
# tag-suffix: ""
# build-directory: ""
# build-options: ""
```

## Functions

### `dockerSetup [--skip-login]`
Expand Down Expand Up @@ -152,7 +176,9 @@ It can receive these optional arguments:

* `-r <repo>`: Override the repository where the image is pushed
* `-s <suffix>`: Override the repository by adding a suffix to the one specified by the environment variable
* `-t <suffix>`: Append a suffix to the image tags (e.g. `-next`)
* `-d <dir>`: Build from the specified directory
* `-o "<options>"`: Options to directly pass to the docker build command

This function can be called several times to build different images within the same build. For example:

Expand Down
34 changes: 34 additions & 0 deletions action.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const process = require("process")
const { spawn } = require("child_process")
const fs = require("fs")

// basic implementation of @action/core.getInput()
function getInput(name) {
return process.env[`INPUT_${name.toUpperCase()}`].trim()
}

// collect action inputs:
let setupOpts = []
if (getInput("skip-login") === "true") setupOpts.push("--skip-login")

let buildOpts = [], value
if ((value = getInput("repository")) !== "") buildOpts.push(`-r ${value}`)
if ((value = getInput("repository-suffix")) !== "") buildOpts.push(`-s ${value}`)
if ((value = getInput("tag-suffix")) !== "") buildOpts.push(`-t ${value}`)
if ((value = getInput("build-directory")) !== "") buildOpts.push(`-d ${value}`)
if ((value = getInput("build-options")) !== "") buildOpts.push(`-o "${value}"`)

// generate a local build.sh script:
fs.writeFileSync("build.sh", `#! bash
source ${__dirname}/build.sh
dockerSetup ${setupOpts.join(" ")}
echo $VERSION > VERSION
dockerBuildAndPush ${buildOpts.join(" ")}
`)

// execute it:
const build = spawn("bash", ["build.sh"], { maxBuffer: 100 * 1024 * 1024 })
build.stdout.on("data", data => { process.stdout.write(data) })
build.stderr.on("data", data => { process.stderr.write(data) })
build.on("error", error => { console.error(error) })
build.on("exit", code => { process.exit(code) })
27 changes: 27 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Metadata for the GitHub Action
name: CI Docker Builder
author: Manas
description: Build and publish Docker image(s)

inputs:
skip-login:
type: boolean

repository:
type: string

repository-suffix:
type: string

tag-suffix:
type: string

build-directory:
type: string

build-options:
type: string

runs:
using: node16
main: action.js
21 changes: 16 additions & 5 deletions build.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ dockerSetup() {
VERSION="dev-${COMMIT::7} (build $BUILD_NUMBER)"
DOCKER_TAG="dev"
;;

"$STABLE_BRANCH")
VERSION="rc-${COMMIT::7} (build $BUILD_NUMBER)"
DOCKER_TAG="rc"
Expand Down Expand Up @@ -116,8 +116,10 @@ dockerBuildAndPush() {
local REPO=$DOCKER_REPOSITORY
local DIR="."
local OPTIND
local TAG_SUFFIX
local BUILD_OPTS

while getopts ":r:d:s:" opt "$@"; do
while getopts ":r:d:s:t:o:" opt "$@"; do
case ${opt} in
r)
REPO=$OPTARG
Expand All @@ -131,21 +133,30 @@ dockerBuildAndPush() {
DIR=$OPTARG
;;

t)
TAG_SUFFIX=$OPTARG
;;

o)
BUILD_OPTS=$OPTARG
;;

*)
;;
esac
done

local IMAGE="${REPO}:${DOCKER_TAG}"
local IMAGE="${REPO}:${DOCKER_TAG}${TAG_SUFFIX}"

echo "Building image ${IMAGE} from ${DIR}"
docker build -t "${IMAGE}" "${DIR}"
# shellcheck disable=SC2086
docker build ${BUILD_OPTS} -t "${IMAGE}" "${DIR}"

echo "Pushing ${IMAGE}"
docker push "${IMAGE}"

if [[ -n "$EXTRA_DOCKER_TAG" ]]; then
__dockerTagAndPush "$EXTRA_DOCKER_TAG"
__dockerTagAndPush "${EXTRA_DOCKER_TAG}${TAG_SUFFIX}"
fi

if [[ -n "$DOCKER_TAG_AS_LATEST" ]]; then
Expand Down
36 changes: 24 additions & 12 deletions test/test-suite.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,6 @@ testMainAsDefaultDevelopmentBranch() {
assertNull "${DOCKER_CALLS[1]}"
}

testStableBranchDoesNotHaveADefault() {
branch "stable"
dockerSetup > /dev/null

assertEquals "0" "$?"
assertNull "$VERSION"
assertNull "$DOCKER_TAG"
assertNull "$EXTRA_DOCKER_TAG"
assertNull "$DOCKER_TAG_AS_LATEST"
assertNull "${DOCKER_CALLS[0]}"
}

testCustomStableBranch() {
branch "stable"
STABLE_BRANCH=stable dockerSetup > /dev/null
Expand Down Expand Up @@ -168,6 +156,30 @@ testBuildWithExtraTagAndLatest() {
assertNull "${DOCKER_CALLS[6]}"
}

testBuildWithTagSuffix() {
DOCKER_TAG="tag"
EXTRA_DOCKER_TAG="extra"
DOCKER_TAG_AS_LATEST="true"
dockerBuildAndPush -t "-next" > /dev/null

assertEquals "build -t repository:tag-next ." "${DOCKER_CALLS[0]}"
assertEquals "push repository:tag-next" "${DOCKER_CALLS[1]}"
assertEquals "tag repository:tag-next repository:extra-next" "${DOCKER_CALLS[2]}"
assertEquals "push repository:extra-next" "${DOCKER_CALLS[3]}"
assertEquals "tag repository:tag-next repository:latest" "${DOCKER_CALLS[4]}"
assertEquals "push repository:latest" "${DOCKER_CALLS[5]}"
assertNull "${DOCKER_CALLS[6]}"
}

testBuildWithBuildOptions() {
DOCKER_TAG="tag"
dockerBuildAndPush -o "--build-arg gemfile=Gemfile.next" > /dev/null

assertEquals "build --build-arg gemfile=Gemfile.next -t repository:tag ." "${DOCKER_CALLS[0]}"
assertEquals "push repository:tag" "${DOCKER_CALLS[1]}"
assertNull "${DOCKER_CALLS[2]}"
}

testBuildCustomRepo() {
DOCKER_TAG="tag"
dockerBuildAndPush -r myrepo > /dev/null
Expand Down

0 comments on commit 0084f2b

Please sign in to comment.