From dbcd1e0d03cfa3d606d984459271750b430d2b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 26 Oct 2026 19:26:43 +0100 Subject: [PATCH] :construction_worker: Include the version in the bundle and image build key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bundle stamps the version into version.txt, so a commit that gets a new tag (2.18.1-RC1 -> 2.18.1) needs a new bundle and image set. Keying them by commit sha alone reused the RC artifacts for the final release. Signed-off-by: David Barragán Merino --- .github/workflows/build-bundle.yml | 32 ++++++++++------- .github/workflows/build-docker.yml | 53 ++++++++++++++++------------ .github/workflows/build-renovate.yml | 22 +++++++----- 3 files changed, 62 insertions(+), 45 deletions(-) diff --git a/.github/workflows/build-bundle.yml b/.github/workflows/build-bundle.yml index 88c04a2634..eb4b335313 100644 --- a/.github/workflows/build-bundle.yml +++ b/.github/workflows/build-bundle.yml @@ -61,6 +61,7 @@ jobs: outputs: gh_ref: ${{ steps.vars.outputs.gh_ref }} bundle_version: ${{ steps.vars.outputs.bundle_version }} + build_key: ${{ steps.vars.outputs.build_key }} short_sha: ${{ steps.vars.outputs.short_sha }} sha: ${{ steps.vars.outputs.sha }} commit_title: ${{ steps.vars.outputs.commit_title }} @@ -79,16 +80,22 @@ jobs: env: GH_REF: ${{ inputs.gh_ref || github.ref_name }} run: | + # Same command the build uses to stamp version.txt, so the key + # always matches what ends up inside the bundle. + BUNDLE_VERSION=$(./manage.sh version) + SHORT_SHA=$(git rev-parse --short=12 HEAD) echo "gh_ref=$GH_REF" >> $GITHUB_OUTPUT - echo "bundle_version=$(git describe --tags --always)" >> $GITHUB_OUTPUT - echo "short_sha=$(git rev-parse --short=12 HEAD)" >> $GITHUB_OUTPUT + echo "bundle_version=$BUNDLE_VERSION" >> $GITHUB_OUTPUT + echo "build_key=sha-${SHORT_SHA}-${BUNDLE_VERSION}" >> $GITHUB_OUTPUT + echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT echo "commit_title=$(git log -1 --pretty=%s)" >> $GITHUB_OUTPUT - # Keyed by commit sha, same as build-docker.yml's marker check, so - # both pipelines agree on what "already built" means: any ref that - # points at an already-bundled commit skips the build, regardless of - # which ref built it first. `force` bypasses this check entirely. + # Keyed by commit sha AND version, same as build-docker.yml's marker + # check, so both pipelines agree on what "already built" means. The + # version is stamped into the bundle, so a new tag on an already + # bundled commit (2.18.1-RC1 -> 2.18.1) must produce a new bundle. + # `force` bypasses this check entirely. - name: Check if this bundle is already built id: check env: @@ -97,8 +104,7 @@ jobs: AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }} S3_BUCKET: ${{ secrets.S3_BUCKET }} FORCE: ${{ inputs.force }} - SHORT_SHA: ${{ steps.vars.outputs.short_sha }} - BUNDLE_VERSION: ${{ steps.vars.outputs.bundle_version }} + BUILD_KEY: ${{ steps.vars.outputs.build_key }} run: | if [ "$FORCE" = "true" ]; then echo "exists=false" >> $GITHUB_OUTPUT @@ -112,7 +118,7 @@ jobs: BUNDLE_EXISTS=$(aws s3api head-object \ --bucket "$S3_BUCKET" \ - --key "penpot-sha-${SHORT_SHA}.zip" \ + --key "penpot-${BUILD_KEY}.zip" \ > /dev/null 2>&1 && echo "true" || echo "false") if [ "$BUNDLE_EXISTS" = "true" ]; then @@ -120,7 +126,7 @@ jobs: { echo "### ⏭️ Bundle build skipped" echo "" - echo "The bundle in S3 was already built from \`sha-${SHORT_SHA}\` (\`${BUNDLE_VERSION}\`)." + echo "The bundle in S3 was already built for \`${BUILD_KEY}\`." } >> "$GITHUB_STEP_SUMMARY" else echo "exists=false" >> $GITHUB_OUTPUT @@ -164,11 +170,11 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }} S3_BUCKET: ${{ secrets.S3_BUCKET }} - SHORT_SHA: ${{ needs.check.outputs.short_sha }} + BUILD_KEY: ${{ needs.check.outputs.build_key }} BUNDLE_VERSION: ${{ needs.check.outputs.bundle_version }} run: | aws s3 cp zips/penpot.zip \ - "s3://${S3_BUCKET}/penpot-sha-${SHORT_SHA}.zip" \ + "s3://${S3_BUCKET}/penpot-${BUILD_KEY}.zip" \ --metadata "bundle-version=${BUNDLE_VERSION}" - name: Write step summary @@ -180,7 +186,7 @@ jobs: { echo "### ✅ Bundle built" echo "" - echo "- Version: \`${BUNDLE_VERSION}\` (\`git describe --tags --always\`)" + echo "- Version: \`${BUNDLE_VERSION}\` (\`./manage.sh version\`)" echo "- Commit: [\`${SHORT_SHA}\`](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/${SHORT_SHA}) — ${COMMIT_TITLE}" echo "- Built at: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index a8d6a1976d..9c674eba33 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -85,6 +85,7 @@ jobs: bundle_version: ${{ steps.vars.outputs.bundle_version }} sha: ${{ steps.vars.outputs.sha }} short_sha: ${{ steps.vars.outputs.short_sha }} + build_key: ${{ steps.vars.outputs.build_key }} commit_title: ${{ steps.vars.outputs.commit_title }} exists: ${{ steps.check.outputs.exists }} @@ -105,10 +106,14 @@ jobs: REF_SLUG=$(printf '%s' "$GH_REF" | tr -c 'A-Za-z0-9._-' '-' | cut -c1-100) echo "gh_ref=$GH_REF" >> $GITHUB_OUTPUT echo "ref_slug=$REF_SLUG" >> $GITHUB_OUTPUT + # Must match the key computed by build-bundle.yml. + BUNDLE_VERSION=$(./manage.sh version) + SHORT_SHA=$(git rev-parse --short=12 HEAD) echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - echo "short_sha=$(git rev-parse --short=12 HEAD)" >> $GITHUB_OUTPUT + echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT echo "commit_title=$(git log -1 --pretty=%s)" >> $GITHUB_OUTPUT - echo "bundle_version=$(git describe --tags --always)" >> $GITHUB_OUTPUT + echo "bundle_version=$BUNDLE_VERSION" >> $GITHUB_OUTPUT + echo "build_key=sha-${SHORT_SHA}-${BUNDLE_VERSION}" >> $GITHUB_OUTPUT # The image set is a single block, so a single set-level check is # enough: `promote` drops a marker object in S3 only after every @@ -123,14 +128,14 @@ jobs: AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }} S3_BUCKET: ${{ secrets.S3_BUCKET }} FORCE: ${{ inputs.force }} - SHORT_SHA: ${{ steps.vars.outputs.short_sha }} + BUILD_KEY: ${{ steps.vars.outputs.build_key }} run: | if [ "$FORCE" = "true" ]; then echo "exists=false" >> $GITHUB_OUTPUT mkdir -p "$BUNDLE_CACHE" find "$BUNDLE_CACHE" -type f -mtime +1 -delete || true - ZIP="$BUNDLE_CACHE/penpot-sha-${SHORT_SHA}.zip" - aws s3 cp "s3://${S3_BUCKET}/penpot-sha-${SHORT_SHA}.zip" "$ZIP.$$.tmp" + ZIP="$BUNDLE_CACHE/penpot-${BUILD_KEY}.zip" + aws s3 cp "s3://${S3_BUCKET}/penpot-${BUILD_KEY}.zip" "$ZIP.$$.tmp" mv "$ZIP.$$.tmp" "$ZIP" { echo "### 🔁 Image set build forced" @@ -142,13 +147,13 @@ jobs: if aws s3api head-object \ --bucket "$S3_BUCKET" \ - --key "markers/images-sha-${SHORT_SHA}" \ + --key "markers/images-${BUILD_KEY}" \ > /dev/null 2>&1; then echo "exists=true" >> $GITHUB_OUTPUT { echo "### ⏭️ Image set build skipped" echo "" - echo "The whole set was already built and promoted for \`sha-${SHORT_SHA}\`." + echo "The whole set was already built and promoted for \`${BUILD_KEY}\`." } >> "$GITHUB_STEP_SUMMARY" else echo "exists=false" >> $GITHUB_OUTPUT @@ -158,9 +163,9 @@ jobs: # prune stale bundles while at it. mkdir -p "$BUNDLE_CACHE" find "$BUNDLE_CACHE" -type f -mtime +1 -delete || true - ZIP="$BUNDLE_CACHE/penpot-sha-${SHORT_SHA}.zip" + ZIP="$BUNDLE_CACHE/penpot-${BUILD_KEY}.zip" if [ ! -f "$ZIP" ]; then - aws s3 cp "s3://${S3_BUCKET}/penpot-sha-${SHORT_SHA}.zip" "$ZIP.$$.tmp" + aws s3 cp "s3://${S3_BUCKET}/penpot-${BUILD_KEY}.zip" "$ZIP.$$.tmp" mv "$ZIP.$$.tmp" "$ZIP" fi fi @@ -231,14 +236,14 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }} S3_BUCKET: ${{ secrets.S3_BUCKET }} - SHORT_SHA: ${{ needs.prepare.outputs.short_sha }} + BUILD_KEY: ${{ needs.prepare.outputs.build_key }} IMAGE: ${{ matrix.image }} run: | - ZIP="$BUNDLE_CACHE/penpot-sha-${SHORT_SHA}.zip" + ZIP="$BUNDLE_CACHE/penpot-${BUILD_KEY}.zip" if [ ! -f "$ZIP" ]; then echo "Bundle not found in host cache; falling back to S3." mkdir -p "$BUNDLE_CACHE" - aws s3 cp "s3://${S3_BUCKET}/penpot-sha-${SHORT_SHA}.zip" "$ZIP.$$.tmp" + aws s3 cp "s3://${S3_BUCKET}/penpot-${BUILD_KEY}.zip" "$ZIP.$$.tmp" mv "$ZIP.$$.tmp" "$ZIP" fi # Extract only the bundle this job needs. @@ -274,7 +279,7 @@ jobs: sbom: true # Immutable tag only; branch tags are moved atomically for the # whole image set by the `promote` job. - tags: ${{ secrets.DOCKER_REGISTRY }}/${{ matrix.image }}:sha-${{ needs.prepare.outputs.short_sha }} + tags: ${{ secrets.DOCKER_REGISTRY }}/${{ matrix.image }}:${{ needs.prepare.outputs.build_key }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ matrix.image }}:buildcache cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ matrix.image }}:buildcache,mode=max @@ -305,13 +310,13 @@ jobs: env: DOCKER_REGISTRY: ${{ secrets.DOCKER_REGISTRY }} REF_SLUG: ${{ needs.prepare.outputs.ref_slug }} - SHORT_SHA: ${{ needs.prepare.outputs.short_sha }} + BUILD_KEY: ${{ needs.prepare.outputs.build_key }} run: | set -e for image in $ALL_IMAGES; do docker buildx imagetools create \ -t "${DOCKER_REGISTRY}/${image}:${REF_SLUG}" \ - "${DOCKER_REGISTRY}/${image}:sha-${SHORT_SHA}" + "${DOCKER_REGISTRY}/${image}:${BUILD_KEY}" done # The marker is written LAST: its presence certifies that all five @@ -322,26 +327,27 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }} S3_BUCKET: ${{ secrets.S3_BUCKET }} - SHORT_SHA: ${{ needs.prepare.outputs.short_sha }} + BUILD_KEY: ${{ needs.prepare.outputs.build_key }} run: | echo "$GITHUB_RUN_ID" | aws s3 cp - \ - "s3://${S3_BUCKET}/markers/images-sha-${SHORT_SHA}" + "s3://${S3_BUCKET}/markers/images-${BUILD_KEY}" - name: Write step summary env: REF_SLUG: ${{ needs.prepare.outputs.ref_slug }} SHORT_SHA: ${{ needs.prepare.outputs.short_sha }} + BUILD_KEY: ${{ needs.prepare.outputs.build_key }} BUNDLE_VERSION: ${{ needs.prepare.outputs.bundle_version }} COMMIT_TITLE: ${{ needs.prepare.outputs.commit_title }} run: | { echo "### ✅ Image set promoted" echo "" - echo "- Version: \`${BUNDLE_VERSION}\` (\`git describe --tags --always\`)" + echo "- Version: \`${BUNDLE_VERSION}\` (\`./manage.sh version\`)" echo "- Commit: [\`${SHORT_SHA}\`](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/${SHORT_SHA}) — ${COMMIT_TITLE}" echo "- Built at: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" echo "" - echo "All \`:${REF_SLUG}\` tags now point to \`sha-${SHORT_SHA}\`." + echo "All \`:${REF_SLUG}\` tags now point to \`${BUILD_KEY}\`." } >> "$GITHUB_STEP_SUMMARY" # ── 3b. Skip path: make sure THIS ref's tags point to the existing @@ -374,30 +380,31 @@ jobs: env: DOCKER_REGISTRY: ${{ secrets.DOCKER_REGISTRY }} REF_SLUG: ${{ needs.prepare.outputs.ref_slug }} - SHORT_SHA: ${{ needs.prepare.outputs.short_sha }} + BUILD_KEY: ${{ needs.prepare.outputs.build_key }} run: | set -e for image in $ALL_IMAGES; do docker buildx imagetools create \ -t "${DOCKER_REGISTRY}/${image}:${REF_SLUG}" \ - "${DOCKER_REGISTRY}/${image}:sha-${SHORT_SHA}" + "${DOCKER_REGISTRY}/${image}:${BUILD_KEY}" done - name: Write step summary env: REF_SLUG: ${{ needs.prepare.outputs.ref_slug }} SHORT_SHA: ${{ needs.prepare.outputs.short_sha }} + BUILD_KEY: ${{ needs.prepare.outputs.build_key }} BUNDLE_VERSION: ${{ needs.prepare.outputs.bundle_version }} COMMIT_TITLE: ${{ needs.prepare.outputs.commit_title }} run: | { echo "### ✅ Image set already built (branch tags ensured)" echo "" - echo "- Version: \`${BUNDLE_VERSION}\` (\`git describe --tags --always\`)" + echo "- Version: \`${BUNDLE_VERSION}\` (\`./manage.sh version\`)" echo "- Commit: [\`${SHORT_SHA}\`](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/${SHORT_SHA}) — ${COMMIT_TITLE}" echo "- Checked at: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" echo "" - echo "All \`:${REF_SLUG}\` tags now point to \`sha-${SHORT_SHA}\`." + echo "All \`:${REF_SLUG}\` tags now point to \`${BUILD_KEY}\`." } >> "$GITHUB_STEP_SUMMARY" # ── 4. Single failure notification for the whole workflow ───────────── diff --git a/.github/workflows/build-renovate.yml b/.github/workflows/build-renovate.yml index 6156aed086..09f8a51aa0 100644 --- a/.github/workflows/build-renovate.yml +++ b/.github/workflows/build-renovate.yml @@ -27,7 +27,7 @@ jobs: permissions: contents: read outputs: - short_sha: ${{ steps.bundle.outputs.short_sha }} + bundle_key: ${{ steps.bundle.outputs.bundle_key }} images: ${{ steps.changes.outputs.images }} devenv: ${{ steps.changes.outputs.devenv }} imagemagick: ${{ steps.changes.outputs.imagemagick }} @@ -60,7 +60,9 @@ jobs: } >> "$GITHUB_OUTPUT" # Renovate only changes Dockerfiles and package manager versions, so - # the most recent develop bundle is enough to build the images. + # the most recent develop bundle is enough to build the images. The + # version part of the key does not matter here: take any bundle of + # the commit. - name: Find the latest develop bundle id: bundle env: @@ -72,10 +74,12 @@ jobs: run: | for SHA in $(git rev-list --first-parent --max-count=50 "$BASE_SHA"); do SHORT_SHA=$(git rev-parse --short=12 "$SHA") - if aws s3api head-object --bucket "$S3_BUCKET" \ - --key "penpot-sha-${SHORT_SHA}.zip" > /dev/null 2>&1; then - echo "short_sha=$SHORT_SHA" >> "$GITHUB_OUTPUT" - echo "Using bundle \`penpot-sha-${SHORT_SHA}.zip\`" >> "$GITHUB_STEP_SUMMARY" + BUNDLE_KEY=$(aws s3api list-objects-v2 --bucket "$S3_BUCKET" \ + --prefix "penpot-sha-${SHORT_SHA}-" \ + --query 'Contents[0].Key' --output text) + if [ -n "$BUNDLE_KEY" ] && [ "$BUNDLE_KEY" != "None" ]; then + echo "bundle_key=$BUNDLE_KEY" >> "$GITHUB_OUTPUT" + echo "Using bundle \`${BUNDLE_KEY}\`" >> "$GITHUB_STEP_SUMMARY" exit 0 fi done @@ -135,13 +139,13 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }} S3_BUCKET: ${{ secrets.S3_BUCKET }} - SHORT_SHA: ${{ needs.prepare.outputs.short_sha }} + BUNDLE_KEY: ${{ needs.prepare.outputs.bundle_key }} IMAGE: ${{ matrix.image }} run: | - ZIP="$BUNDLE_CACHE/penpot-sha-${SHORT_SHA}.zip" + ZIP="$BUNDLE_CACHE/${BUNDLE_KEY}" if [ ! -f "$ZIP" ]; then mkdir -p "$BUNDLE_CACHE" - aws s3 cp "s3://${S3_BUCKET}/penpot-sha-${SHORT_SHA}.zip" "$ZIP.$$.tmp" + aws s3 cp "s3://${S3_BUCKET}/${BUNDLE_KEY}" "$ZIP.$$.tmp" mv "$ZIP.$$.tmp" "$ZIP" fi pushd docker/images