From d5e56ef745c201c69ba693dcb158f756591a3fb8 Mon Sep 17 00:00:00 2001 From: Niklas Dusenlund Date: Fri, 16 Aug 2024 10:28:19 +0200 Subject: [PATCH] ci: Build and publish container in CI * Check that container builds on PR * Publish container from master branch --- .ci/README.md | 23 ++++++++++++++++++++ .ci/build-container | 8 +++++++ .ci/check-container-sources-modified | 14 +++++++++++++ .ci/check-container-version-published | 14 +++++++++++++ .ci/publish-container | 9 ++++++++ .github/workflows/ci.yml | 16 +++++++++++++- .github/workflows/pr-ci.yml | 30 +++++++++++++++++++++++++++ 7 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 .ci/README.md create mode 100755 .ci/build-container create mode 100755 .ci/check-container-sources-modified create mode 100755 .ci/check-container-version-published create mode 100755 .ci/publish-container diff --git a/.ci/README.md b/.ci/README.md new file mode 100644 index 0000000000..286b1643ed --- /dev/null +++ b/.ci/README.md @@ -0,0 +1,23 @@ +CI Design guidelines + +* Keep as much of scripting as possible in scripts and outside of github action yaml files +* The docker image is rebuilt if the `Dockerfile` or `.containerversion` file is modified. +* If there are changes in the `Dockerfile`, then `.containerversion` must be updated with an + unpublished version number. +* When there are changes to `Dockerfile` and `.containerversion` the master branch job will + publish that version as the latest to docker hub. +* On pull request events github will checkout a version of the tree that is PR branch merged into + the base branch. When we look for what is modifed we can diff HEAD^1 to HEAD. + + o-----o <-- Pull requst branch + / \ + o--o--o------o <-- (HEAD) + \ + github.base_ref (base being merged into, typically master) + +* On push events we get hashes of last commit before and after the push. And the last commit after + is checked out. When we look for what changed we can diff github.event.before to HEAD. + + o--o--o------o <-- github.event.after (HEAD) + \ + github.event.before diff --git a/.ci/build-container b/.ci/build-container new file mode 100755 index 0000000000..679842bc59 --- /dev/null +++ b/.ci/build-container @@ -0,0 +1,8 @@ +#!/bin/bash + +set -e + +CONTAINER_REPO=shiftcrypto/firmware_v2 +CONTAINER_VERSION=$(cat .containerversion) + +docker build --no-cache -t $CONTAINER_REPO:latest -t $CONTAINER_REPO:$CONTAINER_VERSION . diff --git a/.ci/check-container-sources-modified b/.ci/check-container-sources-modified new file mode 100755 index 0000000000..dbb458b3a1 --- /dev/null +++ b/.ci/check-container-sources-modified @@ -0,0 +1,14 @@ +#!/bin/bash +# +# This script works on merge commits. ^1 means the first parent of . +# +# When the github action creates a temporary merge commit for a pull request, the first parent will +# be the base (the branch being merged into). + +set -e + +if git diff --name-only HEAD^1 HEAD | grep -E '^(\.containerversion|Dockerfile)' >/dev/null; then + echo "modified=true" + exit +fi +echo "modified=false" diff --git a/.ci/check-container-version-published b/.ci/check-container-version-published new file mode 100755 index 0000000000..b9f21bf97a --- /dev/null +++ b/.ci/check-container-version-published @@ -0,0 +1,14 @@ +#!/bin/bash + +set -e + +CONTAINER_REPO=shiftcrypto/firmware_v2 +CONTAINER_VERSION=$(cat .containerversion) + +# docker manifest returns 1 (error) if the container doesn't exist and 0 (success) if it does. +if docker manifest inspect $CONTAINER_REPO:$CONTAINER_VERSION > /dev/null; then + >&2 echo Container version \'$CONTAINER_VERSION\' exists. + echo container-published=true + exit +fi +echo container-published=false diff --git a/.ci/publish-container b/.ci/publish-container new file mode 100755 index 0000000000..8ce8cff900 --- /dev/null +++ b/.ci/publish-container @@ -0,0 +1,9 @@ +#!/bin/bash + +set -e + +CONTAINER_REPO=shiftcrypto/firmware_v2 +CONTAINER_VERSION=$(cat .containerversion) + +docker push $CONTAINER_REPO:latest +docker push $CONTAINER_REPO:$CONTAINER_VERSION diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e78a80439b..5b05e12387 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ on: - master jobs: - linux-docker: + ci: runs-on: ubuntu-22.04 steps: - name: Clone the repo @@ -17,7 +17,21 @@ jobs: fetch-depth: 0 fetch-tags: true submodules: recursive + + - name: Check if container should be published + id: checks + run: ./.ci/check-container-version-published >> $GITHUB_OUTPUT + + - name: Build container + if: steps.checks.outputs.container-published == 'false' + run: ./.ci/build-container + + - name: Publish container + if: steps.checks.outputs.container-published == 'false' + run: ./.ci/publish-container + - name: Pull CI container image run: ./.ci/pull-container + - name: Run CI in container run: ./.ci/run-container-ci ${{github.workspace}} ${{ github.event.before }} diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml index a2d0b6851b..4c049d6151 100644 --- a/.github/workflows/pr-ci.yml +++ b/.github/workflows/pr-ci.yml @@ -14,6 +14,21 @@ jobs: submodules: recursive fetch-depth: 0 + - name: Check if container files was modified and if container version already exists + id: checks + run: | + ./.ci/check-container-sources-modified >> "$GITHUB_OUTPUT" + ./.ci/check-container-version-published >> "$GITHUB_OUTPUT" + + - name: Build container image + if: steps.checks.outputs.modified == 'true' + run: | + if "${{ steps.checks.outputs.container-published }}" == "true"; then + echo "::error::Container modified but version $(cat .containerversion) already published" + exit 1 + fi + ./.ci/build-container + - name: Pull container image run: ./.ci/pull-container @@ -64,6 +79,21 @@ jobs: echo "merge commit parents:" git log -1 --format="Head %H, Parents %P" + - name: Check if container files was modified and if container version already exists + id: checks + run: | + ./.ci/check-container-sources-modified >> "$GITHUB_OUTPUT" + ./.ci/check-container-version-published >> "$GITHUB_OUTPUT" + + - name: Build container image + if: steps.checks.outputs.modified == 'true' + run: | + if "${{ steps.checks.outputs.container-published }}" == "true"; then + echo "::error::Container modified but version $(cat .containerversion) already published" + exit 1 + fi + ./.ci/build-container + - name: Pull container image run: ./.ci/pull-container