From 1c7e4a6a81e625495639f03b9f53b8f355ab6d6d Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Wed, 30 Mar 2022 16:36:52 +0200 Subject: [PATCH 1/3] Metadata to make a GitHub Action --- README.md | 22 ++++++++++++++++++++++ action.sh | 25 +++++++++++++++++++++++++ action.yml | 33 +++++++++++++++++++++++++++++++++ build.sh | 0 4 files changed, 80 insertions(+) create mode 100755 action.sh create mode 100644 action.yml mode change 100644 => 100755 build.sh diff --git a/README.md b/README.md index 28982a0..20754f1 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,28 @@ jobs: 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@v2 + - uses: manastech/ci-docker-builder@ + # with: + # skip-login: + # repository: "" + # repository-suffix: "" + # build-directory: "" +``` + ## Functions ### `dockerSetup [--skip-login] [latest]` diff --git a/action.sh b/action.sh new file mode 100755 index 0000000..101729d --- /dev/null +++ b/action.sh @@ -0,0 +1,25 @@ +#! /bin/bash +# Build script for the GitHub Action +set -eo pipefail + +# Collect action inputs to pass as command options +SETUP_OPTS="" +[ "$INPUT_SKIP_LOGIN" = "true" ] && SETUP_OPTS="$SETUP_OPTS --skip-login" +[ "$INPUT_LATEST" = "true" ] && SETUP_OPTS="$SETUP_OPTS latest" + +BUILD_OPTS="" +[ -z $INPUT_REPOSITORY ] || BUILD_OPTS="$BUILD_OPTS -r $INPUT_REPOSITORY" +[ -z $INPUT_REPOSITORY_SUFFIX ] || BUILD_OPTS="$BUILD_OPTS -s $INPUT_REPOSITORY_SUFFIX" +[ -z $INPUT_BUILD_DIRECTORY ] || BUILD_OPTS="$BUILD_OPTS -d $INPUT_BUILD_DIRECTORY" + +# Loads the script from this repository +source ./build.sh + +# Prepare the build +dockerSetup $SETUP_OPTS + +# Write a VERSION file for the footer +echo $VERSION > VERSION + +# Build and push the Docker image +dockerBuildAndPush $BUILD_OPTS diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..5eda5c8 --- /dev/null +++ b/action.yml @@ -0,0 +1,33 @@ +# Metadata for the GitHub Action +name: CI Docker Builder +author: Manas +description: Build and publish Docker image(s) + +inputs: + skip-login: + type: boolean + + latest: + type: boolean + + repository: + type: string + + repository-suffix: + type: string + + build-directory: + type: string + +runs: + using: composite + + steps: + - run: ./action.sh + env: + INPUT_SKIP_LOGIN: ${{ inputs.skip-login }} + INPUT_LATEST: ${{ inputs.latest }} + INPUT_REPOSITORY: ${{ inputs.repository }} + INPUT_REPOSITORY_SUFFIX: ${{ inputs.repository-suffix }} + INPUT_BUILD_DIRECTORY: ${{ inputs.build-directory }} + shell: bash diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 From 3ec303de7da0db9c1ada05353a4dff232d14ce13 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Wed, 30 Mar 2022 18:30:19 +0200 Subject: [PATCH 2/3] Add options to suffix a tag and pass docker build options --- README.md | 4 ++++ action.sh | 5 +++-- action.yml | 8 ++++++++ build.sh | 17 +++++++++++++---- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 20754f1..48805d0 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,9 @@ jobs: # skip-login: # repository: "" # repository-suffix: "" + # tag-suffix: "" # build-directory: "" + # build-options: "" ``` ## Functions @@ -160,7 +162,9 @@ It can receive these optional arguments: * `-r `: Override the repository where the image is pushed * `-s `: Override the repository by adding a suffix to the one specified by the environment variable + * `-t `: Append a suffix to the image tags (e.g. `-next`) * `-d `: Build from the specified directory + * `-o ""`: 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: diff --git a/action.sh b/action.sh index 101729d..f80a90c 100755 --- a/action.sh +++ b/action.sh @@ -1,6 +1,6 @@ #! /bin/bash # Build script for the GitHub Action -set -eo pipefail +set -xeo pipefail # Collect action inputs to pass as command options SETUP_OPTS="" @@ -10,6 +10,7 @@ SETUP_OPTS="" BUILD_OPTS="" [ -z $INPUT_REPOSITORY ] || BUILD_OPTS="$BUILD_OPTS -r $INPUT_REPOSITORY" [ -z $INPUT_REPOSITORY_SUFFIX ] || BUILD_OPTS="$BUILD_OPTS -s $INPUT_REPOSITORY_SUFFIX" +[ -z $INPUT_TAG_SUFFIX ] || BUILD_OPTS="$BUILD_OPTS -t $INPUT_TAG_SUFFIX" [ -z $INPUT_BUILD_DIRECTORY ] || BUILD_OPTS="$BUILD_OPTS -d $INPUT_BUILD_DIRECTORY" # Loads the script from this repository @@ -22,4 +23,4 @@ dockerSetup $SETUP_OPTS echo $VERSION > VERSION # Build and push the Docker image -dockerBuildAndPush $BUILD_OPTS +dockerBuildAndPush $BUILD_OPTS -o "$INPUT_BUILD_OPTIONS" diff --git a/action.yml b/action.yml index 5eda5c8..73edd43 100644 --- a/action.yml +++ b/action.yml @@ -16,9 +16,15 @@ inputs: repository-suffix: type: string + tag-suffix: + type: string + build-directory: type: string + build-options: + type: string + runs: using: composite @@ -29,5 +35,7 @@ runs: INPUT_LATEST: ${{ inputs.latest }} INPUT_REPOSITORY: ${{ inputs.repository }} INPUT_REPOSITORY_SUFFIX: ${{ inputs.repository-suffix }} + INPUT_TAG_SUFFIX: ${{ inputs.tag-suffix }} INPUT_BUILD_DIRECTORY: ${{ inputs.build-directory }} + INPUT_BUILD_OPTIONS: ${{ inputs.build-options }} shell: bash diff --git a/build.sh b/build.sh index fd19e03..945c1ce 100755 --- a/build.sh +++ b/build.sh @@ -102,8 +102,9 @@ dockerBuildAndPush() { local REPO=$DOCKER_REPOSITORY local DIR="." local OPTIND + 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 @@ -117,21 +118,29 @@ 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}" + docker build ${BUILD_OPTS} -t "${IMAGE}" "${DIR}" echo "Pushing ${IMAGE}" docker push "${IMAGE}" if [[ -n "$EXTRA_DOCKER_TAG" ]]; then - local EXTRA_IMAGE="${REPO}:${EXTRA_DOCKER_TAG}" + local EXTRA_IMAGE="${REPO}:${EXTRA_DOCKER_TAG}${TAG_SUFFIX}" echo "Tagging also as $EXTRA_IMAGE" docker tag "${IMAGE}" "${EXTRA_IMAGE}" From 404e79d61e395349ca8c941d9eedbf79c0cba3df Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Thu, 31 Mar 2022 14:18:07 +0200 Subject: [PATCH 3/3] 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... --- action.js | 35 +++++++++++++++++++++++++++++++++++ action.sh | 26 -------------------------- action.yml | 15 ++------------- 3 files changed, 37 insertions(+), 39 deletions(-) create mode 100644 action.js delete mode 100755 action.sh diff --git a/action.js b/action.js new file mode 100644 index 0000000..4671dcb --- /dev/null +++ b/action.js @@ -0,0 +1,35 @@ +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") +if (getInput("latest") === "true") setupOpts.push("latest") + +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) }) diff --git a/action.sh b/action.sh deleted file mode 100755 index f80a90c..0000000 --- a/action.sh +++ /dev/null @@ -1,26 +0,0 @@ -#! /bin/bash -# Build script for the GitHub Action -set -xeo pipefail - -# Collect action inputs to pass as command options -SETUP_OPTS="" -[ "$INPUT_SKIP_LOGIN" = "true" ] && SETUP_OPTS="$SETUP_OPTS --skip-login" -[ "$INPUT_LATEST" = "true" ] && SETUP_OPTS="$SETUP_OPTS latest" - -BUILD_OPTS="" -[ -z $INPUT_REPOSITORY ] || BUILD_OPTS="$BUILD_OPTS -r $INPUT_REPOSITORY" -[ -z $INPUT_REPOSITORY_SUFFIX ] || BUILD_OPTS="$BUILD_OPTS -s $INPUT_REPOSITORY_SUFFIX" -[ -z $INPUT_TAG_SUFFIX ] || BUILD_OPTS="$BUILD_OPTS -t $INPUT_TAG_SUFFIX" -[ -z $INPUT_BUILD_DIRECTORY ] || BUILD_OPTS="$BUILD_OPTS -d $INPUT_BUILD_DIRECTORY" - -# Loads the script from this repository -source ./build.sh - -# Prepare the build -dockerSetup $SETUP_OPTS - -# Write a VERSION file for the footer -echo $VERSION > VERSION - -# Build and push the Docker image -dockerBuildAndPush $BUILD_OPTS -o "$INPUT_BUILD_OPTIONS" diff --git a/action.yml b/action.yml index 73edd43..e52ca08 100644 --- a/action.yml +++ b/action.yml @@ -26,16 +26,5 @@ inputs: type: string runs: - using: composite - - steps: - - run: ./action.sh - env: - INPUT_SKIP_LOGIN: ${{ inputs.skip-login }} - INPUT_LATEST: ${{ inputs.latest }} - INPUT_REPOSITORY: ${{ inputs.repository }} - INPUT_REPOSITORY_SUFFIX: ${{ inputs.repository-suffix }} - INPUT_TAG_SUFFIX: ${{ inputs.tag-suffix }} - INPUT_BUILD_DIRECTORY: ${{ inputs.build-directory }} - INPUT_BUILD_OPTIONS: ${{ inputs.build-options }} - shell: bash + using: node16 + main: action.js