From bb712a1a2089c5ca466ee6e9e821653594552942 Mon Sep 17 00:00:00 2001 From: Victor Kuznetsov Date: Tue, 21 Jul 2026 19:50:27 +0400 Subject: [PATCH 1/3] [infra] Fail release preflight on existing target Reject release dispatches with an existing draft, release, or tag before starting the expensive build matrix while retaining the final race check. --- .github/workflows/nightly.yml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index be89f63..9874400 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -27,6 +27,8 @@ jobs: check-changes: name: Check for recent changes runs-on: ubuntu-latest + permissions: + contents: read outputs: should_build: ${{ steps.decide.outputs.should_build }} is_release: ${{ steps.decide.outputs.is_release }} @@ -82,6 +84,27 @@ jobs: echo "Skipping: no commits in last 24h, force=${FORCE}, release=${IS_RELEASE}" fi + - name: Verify release target is available + if: steps.decide.outputs.is_release == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.decide.outputs.release_tag }} + run: | + set -euo pipefail + existing_release=$( + gh api --paginate "repos/${GITHUB_REPOSITORY}/releases?per_page=100" \ + --jq ".[] | select(.tag_name == \"${TAG}\") | [.id, .draft, .html_url] | @tsv" + ) + if [ -n "$existing_release" ]; then + echo "::error::A draft or published release already targets ${TAG}. Delete an obsolete draft explicitly before rebuilding; published releases must not be replaced." + echo "$existing_release" + exit 1 + fi + if gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${TAG}" >/dev/null 2>&1; then + echo "::error::Git tag ${TAG} already exists. Refusing to move or replace it." + exit 1 + fi + build-and-test: name: "${{ matrix.name }}" needs: check-changes @@ -667,7 +690,8 @@ jobs: path: release-assets merge-multiple: true - - name: Verify release does not already exist + # Recheck after the matrix to catch a release or tag created since preflight. + - name: Recheck release target is available env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAG: ${{ needs.check-changes.outputs.release_tag }} From efc8c6dbb2e947999c3e5092a14eba8f8eed538e Mon Sep 17 00:00:00 2001 From: Victor Kuznetsov Date: Tue, 21 Jul 2026 22:04:04 +0400 Subject: [PATCH 2/3] [infra] Recheck release target before artifact download Avoid downloading release assets when a target created during the build has already made publication unavailable. --- .github/workflows/nightly.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 9874400..3e1b2eb 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -683,13 +683,6 @@ jobs: actions: read contents: write # Required by gh release create below. steps: - - name: Download release artifacts - uses: actions/download-artifact@v8 - with: - pattern: cuvslam-* - path: release-assets - merge-multiple: true - # Recheck after the matrix to catch a release or tag created since preflight. - name: Recheck release target is available env: @@ -711,6 +704,13 @@ jobs: exit 1 fi + - name: Download release artifacts + uses: actions/download-artifact@v8 + with: + pattern: cuvslam-* + path: release-assets + merge-multiple: true + - name: Validate release assets and prepare notes env: PACKAGE_VERSION: ${{ needs.check-changes.outputs.package_version }} From a6809fa613044dc9f1b1f625db688d9c8cb384ba Mon Sep 17 00:00:00 2001 From: Victor Kuznetsov Date: Tue, 21 Jul 2026 22:51:26 +0400 Subject: [PATCH 3/3] [infra] Fail release checks on tag API errors Query matching refs so absent tags return an empty successful result while authentication, network, and server failures stop publication. --- .github/workflows/nightly.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 3e1b2eb..8e74285 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -100,7 +100,11 @@ jobs: echo "$existing_release" exit 1 fi - if gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${TAG}" >/dev/null 2>&1; then + existing_tag=$( + gh api "repos/${GITHUB_REPOSITORY}/git/matching-refs/tags/${TAG}" \ + --jq ".[] | select(.ref == \"refs/tags/${TAG}\") | .ref" + ) + if [ -n "$existing_tag" ]; then echo "::error::Git tag ${TAG} already exists. Refusing to move or replace it." exit 1 fi @@ -699,7 +703,11 @@ jobs: echo "$existing_release" exit 1 fi - if gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${TAG}" >/dev/null 2>&1; then + existing_tag=$( + gh api "repos/${GITHUB_REPOSITORY}/git/matching-refs/tags/${TAG}" \ + --jq ".[] | select(.ref == \"refs/tags/${TAG}\") | .ref" + ) + if [ -n "$existing_tag" ]; then echo "::error::Git tag ${TAG} already exists. Refusing to move or replace it." exit 1 fi