diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 00000000..bbf66619 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,79 @@ +name: Docker image + +# Builds visualregressiontracker/ui for linux/amd64 and linux/arm64 (Apple Silicon, +# Graviton), so docker compose runs it natively instead of under emulation. +# - pushing a vX.Y.Z tag publishes the image as X.Y.Z +# - "Run workflow" with an existing tag (re)publishes it, e.g. to add arm64 to a released version +# - pull requests that touch the image only build it, without pushing +# Publishing needs the DOCKERHUB_USERNAME and DOCKERHUB_TOKEN repository secrets. + +on: + push: + tags: + - 'v*' + pull_request: + branches: [master] + paths: + - Dockerfile + - env.sh + - nginx/** + - .github/workflows/docker.yml + workflow_dispatch: + inputs: + tag: + description: Existing tag to publish, e.g. v5.6.0 + required: true + +# GITHUB_TOKEN is only used to check out the code; image pushes use the Docker Hub secrets +permissions: + contents: read + +jobs: + docker: + runs-on: ubuntu-latest + + steps: + - name: Resolve version + id: version + env: + TAG: ${{ inputs.tag || github.ref_name }} + run: | + if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then + echo "push=false" >> "$GITHUB_OUTPUT" + elif [ "${TAG#v}" != "$TAG" ]; then + echo "push=true" >> "$GITHUB_OUTPUT" + echo "version=${TAG#v}" >> "$GITHUB_OUTPUT" + else + echo "::error::Tag $TAG does not look like vX.Y.Z" + exit 1 + fi + + - uses: actions/checkout@v4 + with: + # a manual run checks out the tag itself, never a branch with the same name + ref: ${{ inputs.tag && format('refs/tags/{0}', inputs.tag) || github.ref }} + # keep the token out of .git/config, which is part of the build context + persist-credentials: false + + - name: Set up QEMU + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + + - name: Log in to Docker Hub + if: steps.version.outputs.push == 'true' + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: ${{ steps.version.outputs.push == 'true' }} + tags: visualregressiontracker/ui:${{ steps.version.outputs.version || 'ci' }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile index 83d49312..16485a46 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,7 @@ ### STAGE 1: Build ### # This image is around 50 megabytes -FROM node:20-alpine AS builder +# The built ui is static files, so build it once on the native platform for every target platform +FROM --platform=$BUILDPLATFORM node:20-alpine AS builder # Create app directory WORKDIR /app