From 20c0e596e47c91a362b15b3fc555e010ba5aebfb Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Fri, 29 May 2026 10:34:41 -0400 Subject: [PATCH] gate :latest tag and CloudFront deploy to GitHub's latest release; skip docker rebuild if it's in dockerhub already --- .github/workflows/release-quickstart.yml | 33 +++++++++++++++++++----- dist/scripts/release-quickstart-image.sh | 16 +++++++++--- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release-quickstart.yml b/.github/workflows/release-quickstart.yml index 7aa1963ee..2b3409fc4 100644 --- a/.github/workflows/release-quickstart.yml +++ b/.github/workflows/release-quickstart.yml @@ -3,14 +3,14 @@ name: Release Quickstart Workflow # CloudFront function for a given release tag. # # Triggers: -# - release: published (a GitHub release has been published, with its -# assets already uploaded) +# - release: published (new release published -> build + push the versioned image, move :latest, deploy CloudFront) +# - release: edited (e.g. "Latest release" moved -> re-point :latest and CloudFront only; the image is NOT rebuilt) # - workflow_dispatch (manual; required `release_tag` input) # # Both jobs are idempotent and independently re-runnable from the Actions UI. on: release: - types: [published] + types: [published, edited] workflow_dispatch: inputs: release_tag: @@ -42,6 +42,7 @@ jobs: tag: ${{ steps.resolve.outputs.tag }} sha: ${{ steps.resolve.outputs.sha }} is_latest: ${{ steps.resolve.outputs.is_latest }} + eligible: ${{ steps.resolve.outputs.eligible }} steps: - name: Resolve release tag and commit SHA id: resolve @@ -64,9 +65,12 @@ jobs: fi if ! [[ "${TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "ERROR: release tag '${TAG}' is not a release semver (expected vMAJOR.MINOR.PATCH)" >&2 - exit 1 + echo "INFO: '${TAG}' is not a release semver (prerelease/draft/other edit); skipping image + CloudFront." + echo "eligible=false" | tee -a "$GITHUB_OUTPUT" + echo "is_latest=false" | tee -a "$GITHUB_OUTPUT" + exit 0 fi + echo "eligible=true" | tee -a "$GITHUB_OUTPUT" # resolve the commit the tag points at; this is the snapshot we will # check out for both jobs so the Dockerfile, routes.yml, and python @@ -105,6 +109,13 @@ jobs: # rename without updating that file too. name: Release Quickstart Job needs: resolve-tag + # eligible, AND (a manual dispatch, OR a publish, OR it's now the latest). + # A non-latest edit (e.g. a typo fix on an old release) skips the job entirely. + if: >- + needs.resolve-tag.outputs.eligible == 'true' + && (github.event_name != 'release' + || github.event.action == 'published' + || needs.resolve-tag.outputs.is_latest == 'true') runs-on: ubuntu-24.04 env: ZITI_QUICKSTART_IMAGE: ${{ vars.ZITI_QUICKSTART_IMAGE || 'docker.io/openziti/quickstart' }} @@ -132,6 +143,8 @@ jobs: path: release-src - name: Set Up QEMU + # only needed to build the multi-arch image; an edit just re-points :latest + if: ${{ !(github.event_name == 'release' && github.event.action == 'edited') }} uses: docker/setup-qemu-action@v4 with: platforms: amd64,arm64 @@ -159,11 +172,18 @@ jobs: if [[ "${{ github.event.inputs.force_latest }}" == "true" ]]; then FORCE_LATEST_FLAG="--force-latest" fi + SKIP_BUILD_FLAG="" + if [[ "${{ github.event_name }}" == "release" && "${{ github.event.action }}" == "edited" ]]; then + # an edit (e.g. moving "Latest release") must not rebuild the image; + # only re-point :latest to the now-latest tag + SKIP_BUILD_FLAG="--skip-build" + fi bash workflow-src/dist/scripts/release-quickstart-image.sh \ --tag "${{ needs.resolve-tag.outputs.tag }}" \ --image-repo "${ZITI_QUICKSTART_IMAGE}" \ --context-dir release-src/quickstart/docker/image \ - ${FORCE_LATEST_FLAG} + ${FORCE_LATEST_FLAG} \ + ${SKIP_BUILD_FLAG} release-quickstart-cloudfront: name: Deploy get.openziti.io CloudFront Function @@ -176,6 +196,7 @@ jobs: # newest release should re-point it. Older maintenance releases skip. if: >- github.repository_owner == 'openziti' + && needs.resolve-tag.outputs.eligible == 'true' && (needs.resolve-tag.outputs.is_latest == 'true' || github.event.inputs.force_cloudfront == 'true') runs-on: ubuntu-24.04 steps: diff --git a/dist/scripts/release-quickstart-image.sh b/dist/scripts/release-quickstart-image.sh index 6efdd74ef..3a29c161b 100644 --- a/dist/scripts/release-quickstart-image.sh +++ b/dist/scripts/release-quickstart-image.sh @@ -7,11 +7,13 @@ # - moves :latest only when this tag IS the GitHub "Latest release" (or when # --force-latest is passed), AND :latest does not already point at the same # digest as :vX.Y.Z +# - --skip-build skips the :vX.Y.Z build/push entirely and only (re)evaluates +# the :latest move; used when re-pointing :latest without rebuilding # # Designed to be safely re-runnable from CI or a developer laptop. # # Usage: -# release-quickstart-image.sh --tag vX.Y.Z [--image-repo R] [--force-latest] [--dry-run] +# release-quickstart-image.sh --tag vX.Y.Z [--image-repo R] [--force-latest] [--skip-build] [--dry-run] # # Required environment: # - docker CLI with buildx + an active builder (workflow does this) @@ -30,6 +32,7 @@ IMAGE_REPO="${ZITI_QUICKSTART_IMAGE:-docker.io/openziti/quickstart}" FORCE_LATEST="false" DRY_RUN="false" CONTEXT_DIR="" +SKIP_BUILD="false" usage() { cat <&2 exit 2 fi @@ -110,6 +115,7 @@ echo " Image repo: $IMAGE_REPO" echo " Tagged ref: $TAGGED_REF" echo " Latest ref: $LATEST_REF" echo " Force :latest: $FORCE_LATEST" +echo " Skip build: $SKIP_BUILD" echo " Build context: $CONTEXT_DIR" echo " Dry run: $DRY_RUN" echo "============================================================" @@ -120,7 +126,9 @@ echo "" echo "---- Step 1: build & push $TAGGED_REF -----------------------" echo "" -if image_exists "$TAGGED_REF"; then +if [[ "$SKIP_BUILD" == "true" ]]; then + echo "INFO: --skip-build set; not building or pushing $TAGGED_REF (expecting it to already exist)." +elif image_exists "$TAGGED_REF"; then echo "INFO: $TAGGED_REF already exists in the registry; skipping build & push." else echo "INFO: $TAGGED_REF not found in the registry; building and pushing." @@ -177,7 +185,7 @@ else if [[ "$DRY_RUN" == "true" ]]; then echo "[dry-run] would have built $TAGGED_REF in step 1; skipping :latest digest compare." else - echo "ERROR: $TAGGED_REF has no digest after the build; aborting :latest move." >&2 + echo "ERROR: $TAGGED_REF has no digest in the registry; cannot move :latest (was it built/pushed?)." >&2 exit 1 fi elif [[ "$TAGGED_DIGEST" == "$LATEST_DIGEST" ]]; then