From 2adceb73c13c76317b1dda5dac3db06167a9bfd5 Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Thu, 9 Apr 2026 10:17:14 -0600 Subject: [PATCH 01/10] Push latest floating tag for apollo-ci images On non-PR builds (tag/main pushes), also push a -latest tag alongside the versioned tag. This allows consumers to reference a stable floating tag without needing to update on every release. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/build.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 93ae24cc..ef530568 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -37,6 +37,13 @@ jobs: id: build-and-push-image with: image-flavor: "${{ matrix.image-flavor }}" + - name: Push latest tag + if: startsWith(github.ref, 'refs/tags/') + run: | + IMAGE="${{ steps.build-and-push-image.outputs.image-tag }}" + LATEST_IMAGE="quay.io/stackrox-io/apollo-ci:${{ matrix.image-flavor }}-latest" + docker tag "${IMAGE}" "${LATEST_IMAGE}" + docker push "${LATEST_IMAGE}" - name: Save image info run: | mkdir -p image-info @@ -67,6 +74,13 @@ jobs: id: build-and-push-image with: image-flavor: "${{ matrix.image-flavor }}" + - name: Push latest tag + if: startsWith(github.ref, 'refs/tags/') + run: | + IMAGE="${{ steps.build-and-push-image.outputs.image-tag }}" + LATEST_IMAGE="quay.io/stackrox-io/apollo-ci:${{ matrix.image-flavor }}-latest" + docker tag "${IMAGE}" "${LATEST_IMAGE}" + docker push "${LATEST_IMAGE}" - name: Save image info run: | mkdir -p image-info From b9117ca839b7b1dd4cac5f62ff7e0b93aa6b7d55 Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Thu, 9 Apr 2026 10:28:22 -0600 Subject: [PATCH 02/10] test: verify latest tag is not pushed on PR builds Co-Authored-By: Claude Opus 4.6 (1M context) From 5881f63e931b359b2f62c07c2b25702b6ec865d3 Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Thu, 9 Apr 2026 11:12:03 -0600 Subject: [PATCH 03/10] Add workflow to promote images to stable tag Adds a manually triggered workflow that retags an existing versioned image as stable without rebuilding. Usage: gh workflow run promote-stable.yaml -f version=0.5.7 Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/promote-stable.yaml | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/promote-stable.yaml diff --git a/.github/workflows/promote-stable.yaml b/.github/workflows/promote-stable.yaml new file mode 100644 index 00000000..68e880d4 --- /dev/null +++ b/.github/workflows/promote-stable.yaml @@ -0,0 +1,38 @@ +name: Promote to stable + +on: + workflow_dispatch: + inputs: + version: + description: "Version to promote (e.g. 0.5.7)" + required: true + +env: + QUAY_STACKROX_IO_RW_USERNAME: ${{ secrets.QUAY_STACKROX_IO_RW_USERNAME }} + QUAY_STACKROX_IO_RW_PASSWORD: ${{ secrets.QUAY_STACKROX_IO_RW_PASSWORD }} + +jobs: + promote-stable: + runs-on: ubuntu-latest + strategy: + matrix: + image-flavor: + - scanner-build + - scanner-test + - stackrox-build + - stackrox-test + - stackrox-ui-test + - jenkins-plugin + fail-fast: false + steps: + - name: Log in to Quay + run: | + docker login -u "$QUAY_STACKROX_IO_RW_USERNAME" --password-stdin <<<"$QUAY_STACKROX_IO_RW_PASSWORD" quay.io + - name: Retag as stable + run: | + SRC="quay.io/stackrox-io/apollo-ci:${{ matrix.image-flavor }}-${{ inputs.version }}" + DST="quay.io/stackrox-io/apollo-ci:${{ matrix.image-flavor }}-stable" + docker pull "${SRC}" + docker tag "${SRC}" "${DST}" + docker push "${DST}" + echo "Promoted ${SRC} → ${DST}" From 985556392501f0cd1de8df8ee172df87a209c0e5 Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Thu, 9 Apr 2026 11:13:57 -0600 Subject: [PATCH 04/10] Simplify promote-stable: single job, server-side retag Use docker buildx imagetools create for server-side manifest copy instead of pull/tag/push. Run all flavors in one job. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/promote-stable.yaml | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/.github/workflows/promote-stable.yaml b/.github/workflows/promote-stable.yaml index 68e880d4..b6c036ae 100644 --- a/.github/workflows/promote-stable.yaml +++ b/.github/workflows/promote-stable.yaml @@ -14,25 +14,16 @@ env: jobs: promote-stable: runs-on: ubuntu-latest - strategy: - matrix: - image-flavor: - - scanner-build - - scanner-test - - stackrox-build - - stackrox-test - - stackrox-ui-test - - jenkins-plugin - fail-fast: false steps: - name: Log in to Quay run: | docker login -u "$QUAY_STACKROX_IO_RW_USERNAME" --password-stdin <<<"$QUAY_STACKROX_IO_RW_PASSWORD" quay.io - - name: Retag as stable + - name: Retag all flavors as stable run: | - SRC="quay.io/stackrox-io/apollo-ci:${{ matrix.image-flavor }}-${{ inputs.version }}" - DST="quay.io/stackrox-io/apollo-ci:${{ matrix.image-flavor }}-stable" - docker pull "${SRC}" - docker tag "${SRC}" "${DST}" - docker push "${DST}" - echo "Promoted ${SRC} → ${DST}" + VERSION="${{ inputs.version }}" + for flavor in scanner-build scanner-test stackrox-build stackrox-test stackrox-ui-test jenkins-plugin; do + SRC="quay.io/stackrox-io/apollo-ci:${flavor}-${VERSION}" + DST="quay.io/stackrox-io/apollo-ci:${flavor}-stable" + echo "Promoting ${SRC} → ${DST}" + docker buildx imagetools create --tag "${DST}" "${SRC}" + done From ff6a9c7fc07264df45ee383c75dff93487f62481 Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Thu, 9 Apr 2026 11:14:25 -0600 Subject: [PATCH 05/10] Default promote-stable version to 'latest' Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/promote-stable.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/promote-stable.yaml b/.github/workflows/promote-stable.yaml index b6c036ae..e12678b1 100644 --- a/.github/workflows/promote-stable.yaml +++ b/.github/workflows/promote-stable.yaml @@ -4,8 +4,9 @@ on: workflow_dispatch: inputs: version: - description: "Version to promote (e.g. 0.5.7)" - required: true + description: "Version to promote (e.g. 0.5.7). Defaults to 'latest'." + required: false + default: "latest" env: QUAY_STACKROX_IO_RW_USERNAME: ${{ secrets.QUAY_STACKROX_IO_RW_USERNAME }} From aa293dd4e36f6374742e0568de0e7bb53af353db Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Thu, 9 Apr 2026 11:21:38 -0600 Subject: [PATCH 06/10] test: add tag trigger to test promote-stable workflow Temporary: adds push tag trigger and shell default for VERSION. Will revert after testing. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/promote-stable.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/promote-stable.yaml b/.github/workflows/promote-stable.yaml index e12678b1..378b0d6d 100644 --- a/.github/workflows/promote-stable.yaml +++ b/.github/workflows/promote-stable.yaml @@ -1,6 +1,9 @@ name: Promote to stable on: + push: + tags: + - "test-stable" workflow_dispatch: inputs: version: @@ -22,6 +25,7 @@ jobs: - name: Retag all flavors as stable run: | VERSION="${{ inputs.version }}" + VERSION="${VERSION:-latest}" for flavor in scanner-build scanner-test stackrox-build stackrox-test stackrox-ui-test jenkins-plugin; do SRC="quay.io/stackrox-io/apollo-ci:${flavor}-${VERSION}" DST="quay.io/stackrox-io/apollo-ci:${flavor}-stable" From 5d0d269bf5e6a9924004dd4b3d6ccba4354c3b3f Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Thu, 9 Apr 2026 11:22:57 -0600 Subject: [PATCH 07/10] Revert test trigger from promote-stable workflow Remove temporary push tag trigger used for testing. Keep shell default for VERSION. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/promote-stable.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/promote-stable.yaml b/.github/workflows/promote-stable.yaml index 378b0d6d..392b6994 100644 --- a/.github/workflows/promote-stable.yaml +++ b/.github/workflows/promote-stable.yaml @@ -1,9 +1,6 @@ name: Promote to stable on: - push: - tags: - - "test-stable" workflow_dispatch: inputs: version: From 7f6e57069eb761667f055256ed70f7ce11eef51a Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Thu, 9 Apr 2026 21:54:44 -0600 Subject: [PATCH 08/10] Only push latest tag for the newest version on main Check that the current tag is the highest semver tag reachable from main before pushing latest. This prevents older tags or tags on non-main branches from overwriting the latest images. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/build.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index ef530568..9f12707e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -40,6 +40,12 @@ jobs: - name: Push latest tag if: startsWith(github.ref, 'refs/tags/') run: | + CURRENT="${{ github.ref_name }}" + LATEST="$(git tag --merged origin/main --sort=-version:refname | head -1)" + if [[ "${CURRENT}" != "${LATEST}" ]]; then + echo "Skipping latest push: ${CURRENT} is not the newest tag on main (${LATEST} is)" + exit 0 + fi IMAGE="${{ steps.build-and-push-image.outputs.image-tag }}" LATEST_IMAGE="quay.io/stackrox-io/apollo-ci:${{ matrix.image-flavor }}-latest" docker tag "${IMAGE}" "${LATEST_IMAGE}" @@ -77,6 +83,12 @@ jobs: - name: Push latest tag if: startsWith(github.ref, 'refs/tags/') run: | + CURRENT="${{ github.ref_name }}" + LATEST="$(git tag --merged origin/main --sort=-version:refname | head -1)" + if [[ "${CURRENT}" != "${LATEST}" ]]; then + echo "Skipping latest push: ${CURRENT} is not the newest tag on main (${LATEST} is)" + exit 0 + fi IMAGE="${{ steps.build-and-push-image.outputs.image-tag }}" LATEST_IMAGE="quay.io/stackrox-io/apollo-ci:${{ matrix.image-flavor }}-latest" docker tag "${IMAGE}" "${LATEST_IMAGE}" From 0b19a432113df405b439cf9fcf51b24a80377077 Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Thu, 9 Apr 2026 21:55:33 -0600 Subject: [PATCH 09/10] Also verify tagged commit is on main before pushing latest Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/build.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 9f12707e..5a8b79ce 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -40,6 +40,10 @@ jobs: - name: Push latest tag if: startsWith(github.ref, 'refs/tags/') run: | + if ! git merge-base --is-ancestor "${{ github.sha }}" origin/main; then + echo "Skipping latest push: tagged commit is not on main" + exit 0 + fi CURRENT="${{ github.ref_name }}" LATEST="$(git tag --merged origin/main --sort=-version:refname | head -1)" if [[ "${CURRENT}" != "${LATEST}" ]]; then @@ -83,6 +87,10 @@ jobs: - name: Push latest tag if: startsWith(github.ref, 'refs/tags/') run: | + if ! git merge-base --is-ancestor "${{ github.sha }}" origin/main; then + echo "Skipping latest push: tagged commit is not on main" + exit 0 + fi CURRENT="${{ github.ref_name }}" LATEST="$(git tag --merged origin/main --sort=-version:refname | head -1)" if [[ "${CURRENT}" != "${LATEST}" ]]; then From f0d10add73a0292e745a05c468ce0b5dd05a8881 Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Thu, 9 Apr 2026 21:57:15 -0600 Subject: [PATCH 10/10] Reword skip message for latest tag push Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5a8b79ce..71342bcd 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -47,7 +47,7 @@ jobs: CURRENT="${{ github.ref_name }}" LATEST="$(git tag --merged origin/main --sort=-version:refname | head -1)" if [[ "${CURRENT}" != "${LATEST}" ]]; then - echo "Skipping latest push: ${CURRENT} is not the newest tag on main (${LATEST} is)" + echo "Skipping latest push: ${CURRENT} is not the highest version tag on main (${LATEST} is)" exit 0 fi IMAGE="${{ steps.build-and-push-image.outputs.image-tag }}" @@ -94,7 +94,7 @@ jobs: CURRENT="${{ github.ref_name }}" LATEST="$(git tag --merged origin/main --sort=-version:refname | head -1)" if [[ "${CURRENT}" != "${LATEST}" ]]; then - echo "Skipping latest push: ${CURRENT} is not the newest tag on main (${LATEST} is)" + echo "Skipping latest push: ${CURRENT} is not the highest version tag on main (${LATEST} is)" exit 0 fi IMAGE="${{ steps.build-and-push-image.outputs.image-tag }}"