fix: a pass that does nothing has to say why #30
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: images | |
| on: | |
| push: | |
| branches: [main] | |
| tags: ["v*"] | |
| # Files outside the images: a docs only push does not rebuild them. | |
| paths-ignore: | |
| - "*.md" | |
| - "docs/**" | |
| - "tests/**" | |
| - "playwright*.ts" | |
| - "install.sh" | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| packages: write | |
| concurrency: | |
| group: images-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| # One job per image and per architecture, each on a runner of that | |
| # architecture. Emulation would take half an hour on the worker image, and | |
| # arm64 runners are free on a public repository, so nothing is emulated. | |
| build: | |
| name: build ${{ matrix.name }} ${{ matrix.arch }} | |
| if: github.repository == 'DigiHold/LinkedGrow' | |
| runs-on: ${{ matrix.runner }} | |
| permissions: | |
| contents: read | |
| packages: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: app | |
| dockerfile: docker/Dockerfile.app | |
| image: ghcr.io/digihold/linkedgrow | |
| arch: amd64 | |
| platform: linux/amd64 | |
| runner: ubuntu-latest | |
| - name: app | |
| dockerfile: docker/Dockerfile.app | |
| image: ghcr.io/digihold/linkedgrow | |
| arch: arm64 | |
| platform: linux/arm64 | |
| runner: ubuntu-24.04-arm | |
| - name: worker | |
| dockerfile: docker/Dockerfile.worker | |
| image: ghcr.io/digihold/linkedgrow-worker | |
| arch: amd64 | |
| platform: linux/amd64 | |
| runner: ubuntu-latest | |
| - name: worker | |
| dockerfile: docker/Dockerfile.worker | |
| image: ghcr.io/digihold/linkedgrow-worker | |
| arch: arm64 | |
| platform: linux/arm64 | |
| runner: ubuntu-24.04-arm | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to ghcr.io | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| # Pushed without a tag, so the 2 architectures cannot overwrite each | |
| # other. The merge job below is what gives the pair its tags. | |
| - name: Build and push by digest | |
| id: build | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ${{ matrix.dockerfile }} | |
| platforms: ${{ matrix.platform }} | |
| outputs: type=image,name=${{ matrix.image }},push-by-digest=true,name-canonical=true,push=true | |
| cache-from: type=gha,scope=${{ matrix.name }}-${{ matrix.arch }} | |
| cache-to: type=gha,mode=max,scope=${{ matrix.name }}-${{ matrix.arch }} | |
| # The file name is the digest without its algorithm prefix, because an | |
| # artifact name cannot hold a colon. | |
| - name: Keep the digest | |
| run: | | |
| mkdir -p /tmp/digests | |
| digest="${{ steps.build.outputs.digest }}" | |
| touch "/tmp/digests/${digest#sha256:}" | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: digest-${{ matrix.name }}-${{ matrix.arch }} | |
| path: /tmp/digests/* | |
| retention-days: 1 | |
| # The tag people pull points at a manifest list holding both architectures, | |
| # so latest works on an amd64 server and on an arm64 laptop alike. | |
| merge: | |
| name: tag ${{ matrix.name }} | |
| needs: build | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: app | |
| image: ghcr.io/digihold/linkedgrow | |
| - name: worker | |
| image: ghcr.io/digihold/linkedgrow-worker | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: digest-${{ matrix.name }}-* | |
| path: /tmp/digests | |
| merge-multiple: true | |
| - name: Set up Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to ghcr.io | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Tags and labels | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ matrix.image }} | |
| tags: | | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| type=semver,pattern=v{{version}} | |
| type=semver,pattern=v{{major}}.{{minor}} | |
| type=sha | |
| - name: Create the manifest list | |
| working-directory: /tmp/digests | |
| run: | | |
| docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ | |
| $(printf '${{ matrix.image }}@sha256:%s ' *) | |
| env: | |
| DOCKER_METADATA_OUTPUT_JSON: ${{ steps.meta.outputs.json }} | |
| - name: What was published | |
| run: docker buildx imagetools inspect ${{ matrix.image }}:${{ steps.meta.outputs.version }} | |
| smoke: | |
| name: smoke | |
| needs: merge | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Log in to ghcr.io | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| # The install a person actually runs: the compose file on its own, in a | |
| # directory with nothing else in it. Only the image tag is pinned, so a | |
| # build tests itself rather than whatever latest happens to be. | |
| - name: The compose file, and nothing else | |
| run: | | |
| mkdir -p /home/runner/lgsmoke | |
| cp docker-compose.yml /home/runner/lgsmoke/ | |
| echo "LINKEDGROW_VERSION=sha-$(echo "${GITHUB_SHA}" | cut -c1-7)" >> "$GITHUB_ENV" | |
| ls -a /home/runner/lgsmoke | |
| - name: Start the stack | |
| working-directory: /home/runner/lgsmoke | |
| run: docker compose up -d | |
| - name: Wait for the health check | |
| run: | | |
| i=0 | |
| while [ "$i" -lt 90 ]; do | |
| code=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/api/health || true) | |
| if [ "$code" = "200" ]; then | |
| echo "the app answered 200 after $((i * 2)) seconds" | |
| exit 0 | |
| fi | |
| i=$((i + 1)) | |
| sleep 2 | |
| done | |
| echo "the app never answered 200 on /api/health" | |
| exit 1 | |
| - name: The sign up page answers | |
| run: | | |
| code=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/sign-up) | |
| echo "sign-up: $code" | |
| [ "$code" = "200" ] | |
| - name: Logs | |
| if: failure() | |
| working-directory: /home/runner/lgsmoke | |
| run: docker compose logs | |
| - name: Tear down | |
| if: always() | |
| working-directory: /home/runner/lgsmoke | |
| run: docker compose down -v |