From cb4959bc281e46d8de399197f075d9326888c2fc Mon Sep 17 00:00:00 2001 From: Markus Rambossek <2+mram@noreply.localhost> Date: Wed, 26 Aug 2026 13:28:38 +0200 Subject: [PATCH] Update .gitea/workflows/build.yaml --- .gitea/workflows/build.yaml | 155 ++++++++++++++++++++++++++---------- 1 file changed, 114 insertions(+), 41 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index d510080..b838c45 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -1,38 +1,48 @@ -name: Build flutter-node image +# ci-images/flutter-node/.gitea/workflows/build.yaml +# +# Daily check, build ONLY when an input actually changed. +# +# Inputs that define an image: upstream base digests + the Dockerfile itself. +# Their combined hash is published as a marker tag `src-` next to the image. +# If that tag already exists in the registry, this exact image was already built +# and the run stops before doing any work. +# +# Tags pushed on a real build: +# :stable moving, what runners pull by default +# :YYYYMMDD immutable, for pinning / rollback +# :src- marker, 856 bytes, makes the next check a single registry lookup + +name: build -# Triggers: -# - push to main → immediate rebuild (any change in the repo) -# - weekly cron → picks up upstream patch updates in -# cirruslabs/flutter:stable and node:20 -# without a source change -# - manual dispatch → on-demand on: + schedule: + - cron: "0 3 * * *" # daily instead of weekly - costs nothing when nothing changed push: branches: [main] - schedule: - # Sun 03:00 UTC. cirruslabs typically ships :stable bumps a few - # hours after the upstream Flutter stable channel — adjust freely. - - cron: "0 3 * * 0" workflow_dispatch: + inputs: + force: + description: "Build even if no upstream change was detected" + type: boolean + default: false + +env: + IMAGE: ${{ vars.REGISTRY_HOST }}/ci-images/flutter-node + FLUTTER_IMAGE: ghcr.io/cirruslabs/flutter:stable + NODE_IMAGE: node:20-bookworm-slim jobs: - build: - # Skip the job entirely (no runner provisioning) when the registry - # host isn't configured — shows as "skipped" in the UI rather than - # burning minutes on a build that can't be pushed. Secrets can't be - # referenced in job-level if:, so REGISTRY_TOKEN is still checked - # in the first step below. - if: vars.REGISTRY_HOST != '' + check: runs-on: ubuntu-docker - permissions: - packages: write + outputs: + changed: ${{ steps.cmp.outputs.changed }} + hash: ${{ steps.hash.outputs.hash }} + flutter: ${{ steps.up.outputs.flutter }} + node: ${{ steps.up.outputs.node }} + date: ${{ steps.date.outputs.date }} steps: - name: Validate registry token - run: | - if [ -z "${{ secrets.REGISTRY_TOKEN }}" ]; then - echo "::error::Missing required config: secrets.REGISTRY_TOKEN" - exit 1 - fi + run: test -n "${{ secrets.REGISTRY_TOKEN }}" || { echo "REGISTRY_TOKEN missing"; exit 1; } - name: Checkout uses: actions/checkout@v4 @@ -40,18 +50,75 @@ jobs: - name: Set up Buildx uses: docker/setup-buildx-action@v3 - - name: Compute date tag - id: date - run: echo "date=$(date -u +%Y%m%d)" >> "$GITHUB_OUTPUT" - - # Same registry + PAT pattern other projects on this Gitea - # instance use. The auto-injected gitea token is rejected by the - # registry path, hence the manually-issued PAT in REGISTRY_TOKEN. - name: Log in to Gitea registry uses: docker/login-action@v3 with: registry: ${{ vars.REGISTRY_HOST }} - username: ${{ gitea.actor }} + username: ${{ github.actor }} + password: ${{ secrets.REGISTRY_TOKEN }} + + # Resolve the mutable upstream tags to immutable digests, right now. + - name: Resolve upstream digests + id: up + run: | + set -euo pipefail + f=$(docker buildx imagetools inspect "$FLUTTER_IMAGE" --format '{{.Manifest.Digest}}') + n=$(docker buildx imagetools inspect "$NODE_IMAGE" --format '{{.Manifest.Digest}}') + echo "flutter=$f" >> "$GITHUB_OUTPUT" + echo "node=$n" >> "$GITHUB_OUTPUT" + echo "flutter: $f" + echo "node: $n" + + - name: Compute input hash + id: hash + run: | + set -euo pipefail + h=$(printf '%s\n' \ + "${{ steps.up.outputs.flutter }}" \ + "${{ steps.up.outputs.node }}" \ + "$(sha256sum Dockerfile | cut -d' ' -f1)" \ + | sha256sum | cut -c1-12) + echo "hash=$h" >> "$GITHUB_OUTPUT" + echo "input hash: $h" + + # The whole gate: does a marker tag for this exact input set already exist? + - name: Compare against what is already published + id: cmp + run: | + set -euo pipefail + if [ "${{ github.event_name }}" != "schedule" ] || [ "${{ inputs.force }}" = "true" ]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + echo "non-scheduled or forced run - building" + exit 0 + fi + if docker buildx imagetools inspect "$IMAGE:src-${{ steps.hash.outputs.hash }}" >/dev/null 2>&1; then + echo "changed=false" >> "$GITHUB_OUTPUT" + echo "no change since last build - skipping" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + echo "upstream or Dockerfile changed - building" + fi + + - name: Compute date tag + id: date + run: echo "date=$(date -u +%Y%m%d)" >> "$GITHUB_OUTPUT" + + build: + needs: check + if: needs.check.outputs.changed == 'true' + runs-on: ubuntu-docker + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Gitea registry + uses: docker/login-action@v3 + with: + registry: ${{ vars.REGISTRY_HOST }} + username: ${{ github.actor }} password: ${{ secrets.REGISTRY_TOKEN }} - name: Build and push @@ -59,13 +126,19 @@ jobs: with: context: . push: true - # Two tags: - # :stable — moving, what runners pull by default - # :YYYYMMDD — immutable, for pinning / rollback - tags: | - ${{ vars.REGISTRY_HOST }}/ci-images/flutter-node:stable - ${{ vars.REGISTRY_HOST }}/ci-images/flutter-node:${{ steps.date.outputs.date }} + pull: true # re-resolve bases; without this the cron is a no-op + provenance: false # no attestation manifest -> half the package versions + cache-from: type=registry,ref=${{ env.IMAGE }}:stable + cache-to: type=inline build-args: | IMAGE_SOURCE=https://${{ vars.REGISTRY_HOST }}/ci-images/flutter-node - cache-from: type=registry,ref=${{ vars.REGISTRY_HOST }}/ci-images/flutter-node:stable - cache-to: type=inline + FLUTTER_IMAGE=${{ env.FLUTTER_IMAGE }}@${{ needs.check.outputs.flutter }} + NODE_IMAGE=${{ env.NODE_IMAGE }}@${{ needs.check.outputs.node }} + labels: | + org.opencontainers.image.base.name=${{ env.FLUTTER_IMAGE }} + org.opencontainers.image.base.digest=${{ needs.check.outputs.flutter }} + org.opencontainers.image.revision=${{ github.sha }} + tags: | + ${{ env.IMAGE }}:stable + ${{ env.IMAGE }}:${{ needs.check.outputs.date }} + ${{ env.IMAGE }}:src-${{ needs.check.outputs.hash }}