From cfe3c2c6ee58d85c3964aabc7790016355b6fbb2 Mon Sep 17 00:00:00 2001 From: Jay Cap <3140504+mrjcap@users.noreply.github.com> Date: Thu, 8 Oct 2026 09:49:12 +0300 Subject: [PATCH 1/5] fix(ci): prevent older release tags from overwriting Docker latest tag --- .github/workflows/community_release.yml | 17 +++++++++++++++-- .github/workflows/pro_selfhosted_beta.yml | 4 ++++ .github/workflows/pro_selfhosted_release.yml | 15 +++++++++++++-- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/.github/workflows/community_release.yml b/.github/workflows/community_release.yml index 03c6a6abeb..e054e3e026 100644 --- a/.github/workflows/community_release.yml +++ b/.github/workflows/community_release.yml @@ -69,6 +69,19 @@ jobs: steps: - name: Checkout source uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Check if latest version + id: is_latest + run: | + git fetch --tags --force + HIGHEST_TAG=$(git tag -l | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1) + if [ "${{ github.ref_name }}" = "$HIGHEST_TAG" ]; then + echo "is_latest=true" >> "$GITHUB_OUTPUT" + else + echo "is_latest=false" >> "$GITHUB_OUTPUT" + fi - name: Setup qemu id: qemu @@ -100,7 +113,7 @@ jobs: tags: | type=raw,value=${{ github.ref_name }} flavor: | - latest=true + latest=${{ steps.is_latest.outputs.is_latest }} - name: Server build uses: docker/build-push-action@v7 @@ -156,7 +169,7 @@ jobs: tags: | type=raw,value=${{ github.ref_name }} flavor: | - latest=true + latest=${{ steps.is_latest.outputs.is_latest }} - name: Runner build uses: docker/build-push-action@v7 diff --git a/.github/workflows/pro_selfhosted_beta.yml b/.github/workflows/pro_selfhosted_beta.yml index d92c04015e..e7ca057446 100644 --- a/.github/workflows/pro_selfhosted_beta.yml +++ b/.github/workflows/pro_selfhosted_beta.yml @@ -150,6 +150,8 @@ jobs: maintainer=Semaphore UI tags: | type=raw,value=${{ github.ref_name }} + flavor: | + latest=false - name: Server build uses: docker/build-push-action@v7 @@ -230,6 +232,8 @@ jobs: maintainer=Semaphore UI tags: | type=raw,value=${{ github.ref_name }} + flavor: | + latest=false - name: Runner build uses: docker/build-push-action@v7 diff --git a/.github/workflows/pro_selfhosted_release.yml b/.github/workflows/pro_selfhosted_release.yml index 2e01052ea3..34be21a81a 100644 --- a/.github/workflows/pro_selfhosted_release.yml +++ b/.github/workflows/pro_selfhosted_release.yml @@ -86,6 +86,17 @@ jobs: with: fetch-depth: 0 + - name: Check if latest version + id: is_latest + run: | + git fetch --tags --force + HIGHEST_TAG=$(git tag -l | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1) + if [ "${{ github.ref_name }}" = "$HIGHEST_TAG" ]; then + echo "is_latest=true" >> "$GITHUB_OUTPUT" + else + echo "is_latest=false" >> "$GITHUB_OUTPUT" + fi + - name: Determine PRO branch uses: ./.github/actions/determine-pro-branch with: @@ -133,7 +144,7 @@ jobs: tags: | type=raw,value=${{ github.ref_name }} flavor: | - latest=true + latest=${{ steps.is_latest.outputs.is_latest }} - name: Server build uses: docker/build-push-action@v7 @@ -215,7 +226,7 @@ jobs: tags: | type=raw,value=${{ github.ref_name }} flavor: | - latest=true + latest=${{ steps.is_latest.outputs.is_latest }} - name: Runner build uses: docker/build-push-action@v7 From 66a44183487fb5516132f8545908a9190a7ef3e8 Mon Sep 17 00:00:00 2001 From: Jay Cap <3140504+mrjcap@users.noreply.github.com> Date: Thu, 8 Oct 2026 09:59:47 +0300 Subject: [PATCH 2/5] fix(ci): pass ref_name via env var to prevent command injection --- .github/workflows/community_release.yml | 4 +++- .github/workflows/pro_selfhosted_release.yml | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/community_release.yml b/.github/workflows/community_release.yml index e054e3e026..35ae480fd2 100644 --- a/.github/workflows/community_release.yml +++ b/.github/workflows/community_release.yml @@ -74,10 +74,12 @@ jobs: - name: Check if latest version id: is_latest + env: + RELEASE_TAG: ${{ github.ref_name }} run: | git fetch --tags --force HIGHEST_TAG=$(git tag -l | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1) - if [ "${{ github.ref_name }}" = "$HIGHEST_TAG" ]; then + if [ "$RELEASE_TAG" = "$HIGHEST_TAG" ]; then echo "is_latest=true" >> "$GITHUB_OUTPUT" else echo "is_latest=false" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/pro_selfhosted_release.yml b/.github/workflows/pro_selfhosted_release.yml index 34be21a81a..a61d943fd2 100644 --- a/.github/workflows/pro_selfhosted_release.yml +++ b/.github/workflows/pro_selfhosted_release.yml @@ -88,10 +88,12 @@ jobs: - name: Check if latest version id: is_latest + env: + RELEASE_TAG: ${{ github.ref_name }} run: | git fetch --tags --force HIGHEST_TAG=$(git tag -l | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1) - if [ "${{ github.ref_name }}" = "$HIGHEST_TAG" ]; then + if [ "$RELEASE_TAG" = "$HIGHEST_TAG" ]; then echo "is_latest=true" >> "$GITHUB_OUTPUT" else echo "is_latest=false" >> "$GITHUB_OUTPUT" From 445b9f396d39f5e8d391f344111466298c20e868 Mon Sep 17 00:00:00 2001 From: Jay Cap <3140504+mrjcap@users.noreply.github.com> Date: Thu, 8 Oct 2026 10:13:58 +0300 Subject: [PATCH 3/5] ci: serialize release runs to prevent out-of-order image publishing --- .github/workflows/community_release.yml | 4 ++++ .github/workflows/pro_selfhosted_release.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/community_release.yml b/.github/workflows/community_release.yml index 35ae480fd2..23f54cbf52 100644 --- a/.github/workflows/community_release.yml +++ b/.github/workflows/community_release.yml @@ -6,6 +6,10 @@ name: Community Release - 'v[0-9]+.[0-9]+.[0-9]+' - v*-rc* +concurrency: + group: community-release + cancel-in-progress: false + permissions: contents: write diff --git a/.github/workflows/pro_selfhosted_release.yml b/.github/workflows/pro_selfhosted_release.yml index a61d943fd2..33552b5aec 100644 --- a/.github/workflows/pro_selfhosted_release.yml +++ b/.github/workflows/pro_selfhosted_release.yml @@ -5,6 +5,10 @@ name: Pro Self-Hosted Release tags: - 'v[0-9]+.[0-9]+.[0-9]+' +concurrency: + group: pro-selfhosted-release + cancel-in-progress: false + permissions: contents: write From 08a1b4ced2222f2635db2b7c1311c87418cdb974 Mon Sep 17 00:00:00 2001 From: Jay Cap <3140504+mrjcap@users.noreply.github.com> Date: Thu, 8 Oct 2026 10:23:03 +0300 Subject: [PATCH 4/5] ci: scope release concurrency group to ref to prevent dropping pending runs --- .github/workflows/community_release.yml | 2 +- .github/workflows/pro_selfhosted_release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/community_release.yml b/.github/workflows/community_release.yml index 23f54cbf52..62714c0d11 100644 --- a/.github/workflows/community_release.yml +++ b/.github/workflows/community_release.yml @@ -7,7 +7,7 @@ name: Community Release - v*-rc* concurrency: - group: community-release + group: community-release-${{ github.ref }} cancel-in-progress: false permissions: diff --git a/.github/workflows/pro_selfhosted_release.yml b/.github/workflows/pro_selfhosted_release.yml index 33552b5aec..1c17a06dcf 100644 --- a/.github/workflows/pro_selfhosted_release.yml +++ b/.github/workflows/pro_selfhosted_release.yml @@ -6,7 +6,7 @@ name: Pro Self-Hosted Release - 'v[0-9]+.[0-9]+.[0-9]+' concurrency: - group: pro-selfhosted-release + group: pro-selfhosted-release-${{ github.ref }} cancel-in-progress: false permissions: From 1e1f5f57c94a5e1f154b2a167aa66510fcd6e6d4 Mon Sep 17 00:00:00 2001 From: Jay Cap <3140504+mrjcap@users.noreply.github.com> Date: Fri, 9 Oct 2026 22:51:32 +0300 Subject: [PATCH 5/5] fix(ci): serialize releases with shared concurrency queue and harden tag check --- .github/workflows/community_release.yml | 6 +++--- .github/workflows/pro_selfhosted_release.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/community_release.yml b/.github/workflows/community_release.yml index 62714c0d11..ce07c44d91 100644 --- a/.github/workflows/community_release.yml +++ b/.github/workflows/community_release.yml @@ -7,8 +7,9 @@ name: Community Release - v*-rc* concurrency: - group: community-release-${{ github.ref }} + group: community-release cancel-in-progress: false + queue: max permissions: contents: write @@ -81,8 +82,7 @@ jobs: env: RELEASE_TAG: ${{ github.ref_name }} run: | - git fetch --tags --force - HIGHEST_TAG=$(git tag -l | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1) + HIGHEST_TAG=$(git tag -l --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1 || true) if [ "$RELEASE_TAG" = "$HIGHEST_TAG" ]; then echo "is_latest=true" >> "$GITHUB_OUTPUT" else diff --git a/.github/workflows/pro_selfhosted_release.yml b/.github/workflows/pro_selfhosted_release.yml index 1c17a06dcf..3f6699565e 100644 --- a/.github/workflows/pro_selfhosted_release.yml +++ b/.github/workflows/pro_selfhosted_release.yml @@ -6,8 +6,9 @@ name: Pro Self-Hosted Release - 'v[0-9]+.[0-9]+.[0-9]+' concurrency: - group: pro-selfhosted-release-${{ github.ref }} + group: pro-selfhosted-release cancel-in-progress: false + queue: max permissions: contents: write @@ -95,8 +96,7 @@ jobs: env: RELEASE_TAG: ${{ github.ref_name }} run: | - git fetch --tags --force - HIGHEST_TAG=$(git tag -l | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1) + HIGHEST_TAG=$(git tag -l --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1 || true) if [ "$RELEASE_TAG" = "$HIGHEST_TAG" ]; then echo "is_latest=true" >> "$GITHUB_OUTPUT" else