From 3f4127625d2dde78436c9478bb027077a686858d Mon Sep 17 00:00:00 2001 From: masarray Date: Tue, 4 Aug 2026 13:49:26 +0700 Subject: [PATCH 1/6] chore: add release workflow hardening helper --- ...patch_release_workflow_post_publication.py | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 scripts/patch_release_workflow_post_publication.py diff --git a/scripts/patch_release_workflow_post_publication.py b/scripts/patch_release_workflow_post_publication.py new file mode 100644 index 00000000..39e8381f --- /dev/null +++ b/scripts/patch_release_workflow_post_publication.py @@ -0,0 +1,84 @@ +from pathlib import Path + +root = Path(__file__).resolve().parents[1] + + +def replace_exact(relative: str, old: str, new: str, count: int = 1) -> None: + path = root / relative + text = path.read_text(encoding="utf-8") + if text.count(old) < count: + raise SystemExit(f"Patch anchor missing in {relative}: {old[:120]!r}") + path.write_text(text.replace(old, new, count), encoding="utf-8") + + +replace_exact( + ".github/workflows/release-windows.yml", + ''' $request = $body | ConvertTo-Json -Compress + $request | gh api --method PUT $apiPath --input - *> $null + if ($LASTEXITCODE -ne 0) { throw "Failed to record verified release publication." }''', + ''' $request = $body | ConvertTo-Json -Compress + $requestPath = Join-Path $env:RUNNER_TEMP "arsas-published-release-request.json" + [System.IO.File]::WriteAllText( + $requestPath, + $request, + [System.Text.UTF8Encoding]::new($false)) + gh api --method PUT $apiPath --input $requestPath *> $null + if ($LASTEXITCODE -ne 0) { throw "Failed to record verified release publication." }''', +) + +replace_exact( + ".github/workflows/sync-release-documentation.yml", + ''' mkdir -p _release-sync + gh api "repos/$GITHUB_REPOSITORY/releases/tags/$RELEASE_TAG" > _release-sync/release.json + gh release download "$RELEASE_TAG" \\ + --repo "$GITHUB_REPOSITORY" \\ + --dir _release-sync \\ + --pattern 'ARSAS-Windows-x64-SHA256SUMS.txt' \\ + --clobber + git fetch --tags --force''', + ''' mkdir -p _release-sync + release_ready=false + for attempt in $(seq 1 30); do + if gh api "repos/$GITHUB_REPOSITORY/releases/tags/$RELEASE_TAG" > _release-sync/release.tmp.json 2>/dev/null; then + mv _release-sync/release.tmp.json _release-sync/release.json + release_ready=true + break + fi + echo "Release $RELEASE_TAG is not visible yet (attempt $attempt/30); retrying in 10 seconds." + sleep 10 + done + if [[ "$release_ready" != "true" ]]; then + echo "Published release $RELEASE_TAG did not become visible within the synchronization window." >&2 + exit 1 + fi + + checksum_ready=false + for attempt in $(seq 1 12); do + if gh release download "$RELEASE_TAG" \\ + --repo "$GITHUB_REPOSITORY" \\ + --dir _release-sync \\ + --pattern 'ARSAS-Windows-x64-SHA256SUMS.txt' \\ + --clobber; then + checksum_ready=true + break + fi + echo "Checksum asset is not downloadable yet (attempt $attempt/12); retrying in 10 seconds." + sleep 10 + done + if [[ "$checksum_ready" != "true" ]]; then + echo "Checksum asset for $RELEASE_TAG did not become downloadable." >&2 + exit 1 + fi + git fetch --tags --force''', +) + +replace_exact( + ".github/workflows/publish-verified-release.yml", + ''' - ".release/publish-verified.json" + - "landing/release-notes.json" + - ".github/workflows/publish-verified-release.yml"''', + ''' - ".release/publish-verified.json" + - ".github/workflows/publish-verified-release.yml"''', +) + +print("Hardened release publication JSON, release-evidence synchronization retries, and legacy workflow triggers.") From c2a06d1d00555373e35c45eed250457366409d2d Mon Sep 17 00:00:00 2001 From: masarray Date: Tue, 4 Aug 2026 13:49:46 +0700 Subject: [PATCH 2/6] ci: apply release workflow post-publication hardening --- ...atch-release-workflow-post-publication.yml | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/patch-release-workflow-post-publication.yml diff --git a/.github/workflows/patch-release-workflow-post-publication.yml b/.github/workflows/patch-release-workflow-post-publication.yml new file mode 100644 index 00000000..aa95ef1b --- /dev/null +++ b/.github/workflows/patch-release-workflow-post-publication.yml @@ -0,0 +1,37 @@ +name: Apply release workflow post-publication hardening + +on: + pull_request: + branches: [ main ] + paths: + - ".github/workflows/patch-release-workflow-post-publication.yml" + - "scripts/patch_release_workflow_post_publication.py" + +permissions: + contents: write + +jobs: + patch: + if: github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: fix/release-workflow-post-publication + fetch-depth: 0 + + - name: Apply release workflow hardening + run: python scripts/patch_release_workflow_post_publication.py + + - name: Commit hardened workflows + shell: bash + run: | + set -euo pipefail + rm .github/workflows/patch-release-workflow-post-publication.yml + rm scripts/patch_release_workflow_post_publication.py + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git diff --cached --check + git commit -m "fix: harden release post-publication workflows" + git push origin HEAD:fix/release-workflow-post-publication From 145c6473050c963be85a5abeea57638c2eaa4903 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 06:50:13 +0000 Subject: [PATCH 3/6] fix: harden release post-publication workflows --- ...atch-release-workflow-post-publication.yml | 37 -------- .../workflows/publish-verified-release.yml | 1 - .github/workflows/release-windows.yml | 7 +- .../workflows/sync-release-documentation.yml | 38 +++++++-- ...patch_release_workflow_post_publication.py | 84 ------------------- 5 files changed, 38 insertions(+), 129 deletions(-) delete mode 100644 .github/workflows/patch-release-workflow-post-publication.yml delete mode 100644 scripts/patch_release_workflow_post_publication.py diff --git a/.github/workflows/patch-release-workflow-post-publication.yml b/.github/workflows/patch-release-workflow-post-publication.yml deleted file mode 100644 index aa95ef1b..00000000 --- a/.github/workflows/patch-release-workflow-post-publication.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Apply release workflow post-publication hardening - -on: - pull_request: - branches: [ main ] - paths: - - ".github/workflows/patch-release-workflow-post-publication.yml" - - "scripts/patch_release_workflow_post_publication.py" - -permissions: - contents: write - -jobs: - patch: - if: github.event.pull_request.head.repo.full_name == github.repository - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: fix/release-workflow-post-publication - fetch-depth: 0 - - - name: Apply release workflow hardening - run: python scripts/patch_release_workflow_post_publication.py - - - name: Commit hardened workflows - shell: bash - run: | - set -euo pipefail - rm .github/workflows/patch-release-workflow-post-publication.yml - rm scripts/patch_release_workflow_post_publication.py - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - git diff --cached --check - git commit -m "fix: harden release post-publication workflows" - git push origin HEAD:fix/release-workflow-post-publication diff --git a/.github/workflows/publish-verified-release.yml b/.github/workflows/publish-verified-release.yml index 5593cfe4..e3366484 100644 --- a/.github/workflows/publish-verified-release.yml +++ b/.github/workflows/publish-verified-release.yml @@ -5,7 +5,6 @@ on: branches: [ main ] paths: - ".release/publish-verified.json" - - "landing/release-notes.json" - ".github/workflows/publish-verified-release.yml" push: branches: [ main ] diff --git a/.github/workflows/release-windows.yml b/.github/workflows/release-windows.yml index 489854da..b7eff45b 100644 --- a/.github/workflows/release-windows.yml +++ b/.github/workflows/release-windows.yml @@ -450,5 +450,10 @@ jobs: if ($existingSha) { $body.sha = $existingSha } $request = $body | ConvertTo-Json -Compress - $request | gh api --method PUT $apiPath --input - *> $null + $requestPath = Join-Path $env:RUNNER_TEMP "arsas-published-release-request.json" + [System.IO.File]::WriteAllText( + $requestPath, + $request, + [System.Text.UTF8Encoding]::new($false)) + gh api --method PUT $apiPath --input $requestPath *> $null if ($LASTEXITCODE -ne 0) { throw "Failed to record verified release publication." } diff --git a/.github/workflows/sync-release-documentation.yml b/.github/workflows/sync-release-documentation.yml index 7cb6e05c..8a90b1d9 100644 --- a/.github/workflows/sync-release-documentation.yml +++ b/.github/workflows/sync-release-documentation.yml @@ -47,12 +47,38 @@ jobs: run: | set -euo pipefail mkdir -p _release-sync - gh api "repos/$GITHUB_REPOSITORY/releases/tags/$RELEASE_TAG" > _release-sync/release.json - gh release download "$RELEASE_TAG" \ - --repo "$GITHUB_REPOSITORY" \ - --dir _release-sync \ - --pattern 'ARSAS-Windows-x64-SHA256SUMS.txt' \ - --clobber + release_ready=false + for attempt in $(seq 1 30); do + if gh api "repos/$GITHUB_REPOSITORY/releases/tags/$RELEASE_TAG" > _release-sync/release.tmp.json 2>/dev/null; then + mv _release-sync/release.tmp.json _release-sync/release.json + release_ready=true + break + fi + echo "Release $RELEASE_TAG is not visible yet (attempt $attempt/30); retrying in 10 seconds." + sleep 10 + done + if [[ "$release_ready" != "true" ]]; then + echo "Published release $RELEASE_TAG did not become visible within the synchronization window." >&2 + exit 1 + fi + + checksum_ready=false + for attempt in $(seq 1 12); do + if gh release download "$RELEASE_TAG" \ + --repo "$GITHUB_REPOSITORY" \ + --dir _release-sync \ + --pattern 'ARSAS-Windows-x64-SHA256SUMS.txt' \ + --clobber; then + checksum_ready=true + break + fi + echo "Checksum asset is not downloadable yet (attempt $attempt/12); retrying in 10 seconds." + sleep 10 + done + if [[ "$checksum_ready" != "true" ]]; then + echo "Checksum asset for $RELEASE_TAG did not become downloadable." >&2 + exit 1 + fi git fetch --tags --force source_commit="$(git rev-list -n 1 "$RELEASE_TAG")" if [[ ! "$source_commit" =~ ^[0-9a-f]{40}$ ]]; then diff --git a/scripts/patch_release_workflow_post_publication.py b/scripts/patch_release_workflow_post_publication.py deleted file mode 100644 index 39e8381f..00000000 --- a/scripts/patch_release_workflow_post_publication.py +++ /dev/null @@ -1,84 +0,0 @@ -from pathlib import Path - -root = Path(__file__).resolve().parents[1] - - -def replace_exact(relative: str, old: str, new: str, count: int = 1) -> None: - path = root / relative - text = path.read_text(encoding="utf-8") - if text.count(old) < count: - raise SystemExit(f"Patch anchor missing in {relative}: {old[:120]!r}") - path.write_text(text.replace(old, new, count), encoding="utf-8") - - -replace_exact( - ".github/workflows/release-windows.yml", - ''' $request = $body | ConvertTo-Json -Compress - $request | gh api --method PUT $apiPath --input - *> $null - if ($LASTEXITCODE -ne 0) { throw "Failed to record verified release publication." }''', - ''' $request = $body | ConvertTo-Json -Compress - $requestPath = Join-Path $env:RUNNER_TEMP "arsas-published-release-request.json" - [System.IO.File]::WriteAllText( - $requestPath, - $request, - [System.Text.UTF8Encoding]::new($false)) - gh api --method PUT $apiPath --input $requestPath *> $null - if ($LASTEXITCODE -ne 0) { throw "Failed to record verified release publication." }''', -) - -replace_exact( - ".github/workflows/sync-release-documentation.yml", - ''' mkdir -p _release-sync - gh api "repos/$GITHUB_REPOSITORY/releases/tags/$RELEASE_TAG" > _release-sync/release.json - gh release download "$RELEASE_TAG" \\ - --repo "$GITHUB_REPOSITORY" \\ - --dir _release-sync \\ - --pattern 'ARSAS-Windows-x64-SHA256SUMS.txt' \\ - --clobber - git fetch --tags --force''', - ''' mkdir -p _release-sync - release_ready=false - for attempt in $(seq 1 30); do - if gh api "repos/$GITHUB_REPOSITORY/releases/tags/$RELEASE_TAG" > _release-sync/release.tmp.json 2>/dev/null; then - mv _release-sync/release.tmp.json _release-sync/release.json - release_ready=true - break - fi - echo "Release $RELEASE_TAG is not visible yet (attempt $attempt/30); retrying in 10 seconds." - sleep 10 - done - if [[ "$release_ready" != "true" ]]; then - echo "Published release $RELEASE_TAG did not become visible within the synchronization window." >&2 - exit 1 - fi - - checksum_ready=false - for attempt in $(seq 1 12); do - if gh release download "$RELEASE_TAG" \\ - --repo "$GITHUB_REPOSITORY" \\ - --dir _release-sync \\ - --pattern 'ARSAS-Windows-x64-SHA256SUMS.txt' \\ - --clobber; then - checksum_ready=true - break - fi - echo "Checksum asset is not downloadable yet (attempt $attempt/12); retrying in 10 seconds." - sleep 10 - done - if [[ "$checksum_ready" != "true" ]]; then - echo "Checksum asset for $RELEASE_TAG did not become downloadable." >&2 - exit 1 - fi - git fetch --tags --force''', -) - -replace_exact( - ".github/workflows/publish-verified-release.yml", - ''' - ".release/publish-verified.json" - - "landing/release-notes.json" - - ".github/workflows/publish-verified-release.yml"''', - ''' - ".release/publish-verified.json" - - ".github/workflows/publish-verified-release.yml"''', -) - -print("Hardened release publication JSON, release-evidence synchronization retries, and legacy workflow triggers.") From cd74f19773526ca660ee131121374de4f21a25f2 Mon Sep 17 00:00:00 2001 From: masarray Date: Tue, 4 Aug 2026 13:54:33 +0700 Subject: [PATCH 4/6] chore: add legacy release self-trigger cleanup helper --- scripts/remove_legacy_workflow_self_trigger.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 scripts/remove_legacy_workflow_self_trigger.py diff --git a/scripts/remove_legacy_workflow_self_trigger.py b/scripts/remove_legacy_workflow_self_trigger.py new file mode 100644 index 00000000..b15bd110 --- /dev/null +++ b/scripts/remove_legacy_workflow_self_trigger.py @@ -0,0 +1,13 @@ +from pathlib import Path + +path = Path(__file__).resolve().parents[1] / ".github" / "workflows" / "publish-verified-release.yml" +text = path.read_text(encoding="utf-8") +old = ''' paths: + - ".release/publish-verified.json" + - ".github/workflows/publish-verified-release.yml"''' +new = ''' paths: + - ".release/publish-verified.json"''' +if old not in text: + raise SystemExit("Legacy publication workflow self-trigger anchor was not found") +path.write_text(text.replace(old, new, 1), encoding="utf-8") +print("Removed the legacy release workflow self-trigger.") From 7cc03168456ec935a0ac6d1bd5054d77f1b8180d Mon Sep 17 00:00:00 2001 From: masarray Date: Tue, 4 Aug 2026 13:54:48 +0700 Subject: [PATCH 5/6] ci: remove legacy release workflow self-trigger --- .../remove-legacy-workflow-self-trigger.yml | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/remove-legacy-workflow-self-trigger.yml diff --git a/.github/workflows/remove-legacy-workflow-self-trigger.yml b/.github/workflows/remove-legacy-workflow-self-trigger.yml new file mode 100644 index 00000000..899c6bff --- /dev/null +++ b/.github/workflows/remove-legacy-workflow-self-trigger.yml @@ -0,0 +1,37 @@ +name: Remove legacy release workflow self-trigger + +on: + pull_request: + branches: [ main ] + paths: + - ".github/workflows/remove-legacy-workflow-self-trigger.yml" + - "scripts/remove_legacy_workflow_self_trigger.py" + +permissions: + contents: write + +jobs: + patch: + if: github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: fix/release-workflow-post-publication + fetch-depth: 0 + + - name: Remove legacy workflow self-trigger + run: python scripts/remove_legacy_workflow_self_trigger.py + + - name: Commit cleaned workflow + shell: bash + run: | + set -euo pipefail + rm .github/workflows/remove-legacy-workflow-self-trigger.yml + rm scripts/remove_legacy_workflow_self_trigger.py + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git diff --cached --check + git commit -m "fix: remove legacy release workflow self-trigger" + git push origin HEAD:fix/release-workflow-post-publication From 583ace5f1150e90071e18fb9a35ff4358827b73f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 06:55:11 +0000 Subject: [PATCH 6/6] fix: remove legacy release workflow self-trigger --- .../workflows/publish-verified-release.yml | 1 - .../remove-legacy-workflow-self-trigger.yml | 37 ------------------- .../remove_legacy_workflow_self_trigger.py | 13 ------- 3 files changed, 51 deletions(-) delete mode 100644 .github/workflows/remove-legacy-workflow-self-trigger.yml delete mode 100644 scripts/remove_legacy_workflow_self_trigger.py diff --git a/.github/workflows/publish-verified-release.yml b/.github/workflows/publish-verified-release.yml index e3366484..ae0dda9b 100644 --- a/.github/workflows/publish-verified-release.yml +++ b/.github/workflows/publish-verified-release.yml @@ -5,7 +5,6 @@ on: branches: [ main ] paths: - ".release/publish-verified.json" - - ".github/workflows/publish-verified-release.yml" push: branches: [ main ] paths: diff --git a/.github/workflows/remove-legacy-workflow-self-trigger.yml b/.github/workflows/remove-legacy-workflow-self-trigger.yml deleted file mode 100644 index 899c6bff..00000000 --- a/.github/workflows/remove-legacy-workflow-self-trigger.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Remove legacy release workflow self-trigger - -on: - pull_request: - branches: [ main ] - paths: - - ".github/workflows/remove-legacy-workflow-self-trigger.yml" - - "scripts/remove_legacy_workflow_self_trigger.py" - -permissions: - contents: write - -jobs: - patch: - if: github.event.pull_request.head.repo.full_name == github.repository - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: fix/release-workflow-post-publication - fetch-depth: 0 - - - name: Remove legacy workflow self-trigger - run: python scripts/remove_legacy_workflow_self_trigger.py - - - name: Commit cleaned workflow - shell: bash - run: | - set -euo pipefail - rm .github/workflows/remove-legacy-workflow-self-trigger.yml - rm scripts/remove_legacy_workflow_self_trigger.py - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - git diff --cached --check - git commit -m "fix: remove legacy release workflow self-trigger" - git push origin HEAD:fix/release-workflow-post-publication diff --git a/scripts/remove_legacy_workflow_self_trigger.py b/scripts/remove_legacy_workflow_self_trigger.py deleted file mode 100644 index b15bd110..00000000 --- a/scripts/remove_legacy_workflow_self_trigger.py +++ /dev/null @@ -1,13 +0,0 @@ -from pathlib import Path - -path = Path(__file__).resolve().parents[1] / ".github" / "workflows" / "publish-verified-release.yml" -text = path.read_text(encoding="utf-8") -old = ''' paths: - - ".release/publish-verified.json" - - ".github/workflows/publish-verified-release.yml"''' -new = ''' paths: - - ".release/publish-verified.json"''' -if old not in text: - raise SystemExit("Legacy publication workflow self-trigger anchor was not found") -path.write_text(text.replace(old, new, 1), encoding="utf-8") -print("Removed the legacy release workflow self-trigger.")