From 1e492c8e2b147997cb9478e4a7003087becf265d Mon Sep 17 00:00:00 2001 From: Vyncint Ng <115854244+vyncint@users.noreply.github.com> Date: Sat, 22 Aug 2026 12:34:43 +0700 Subject: [PATCH 1/3] ci(release): move the floating vN tag on publish The README tells users to write `vyncint/launchbound/action@v1`, which makes that tag the contract for everyone who followed the documentation. Moving it was a manual step, and a manual step in a release is a step that gets skipped: v1 sat on 1.0.2 through both 1.1.0 and 1.2.0. The effect was not cosmetic. The only thing that changed in the action between 1.0.2 and 1.2.0 is the default reconverge-version, 0.1.11 to 0.3.0 -- so every consumer on the documented ref, including the demo repository that exists to exercise this action, kept gating with the old analyzer while 1.2.0's entire subject was moving that pin. Runs after a successful publish only, and only on a tag push, so the floating tag can never point at a release that did not reach crates.io. The workflow's trigger requires three numeric components, so pushing v1 cannot re-enter it. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com> --- .github/workflows/release.yml | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2de6141..ae65a2c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -99,3 +99,43 @@ jobs: fi done done + + # Move the floating major tag to the release that was just published. + # + # The README tells users to write `vyncint/launchbound/action@v1`, so that + # tag is the contract for everyone who followed the documentation. It was a + # manual step, and a manual step in a release is a step that gets skipped: + # `v1` sat on 1.0.2 through both 1.1.0 and 1.2.0, so every consumer on the + # documented ref kept getting an action pinned to reconverge 0.1.11 while + # 1.2.0's entire subject was moving that pin to 0.3.0. + # + # Runs only after a successful publish, and only for a tag push, so the + # floating tag can never point at a release that did not reach crates.io. + # The trigger filter above requires three numeric components, so pushing + # `v1` here cannot re-enter this workflow. + float-major-tag: + needs: publish + if: github.event_name == 'push' && github.ref_type == 'tag' + runs-on: ubuntu-latest + permissions: + contents: write # moves refs/tags/vN + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Point vN at ${{ github.ref_name }} + env: + TAG: ${{ github.ref_name }} + run: | + set -euo pipefail + major="v${TAG#v}" + major="${major%%.*}" + case "$major" in + v[0-9]*) ;; + *) echo "::error::cannot derive a major tag from '$TAG'"; exit 1 ;; + esac + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git tag -f "$major" "$TAG^{commit}" + git push -f origin "refs/tags/$major" + echo "$major now points at $TAG ($(git rev-parse --short "$TAG^{commit}"))" From 1890a70f97f2d9988506de37bbe4bcd97e9296bd Mon Sep 17 00:00:00 2001 From: Vyncint Ng <115854244+vyncint@users.noreply.github.com> Date: Sat, 22 Aug 2026 12:40:48 +0700 Subject: [PATCH 2/3] ci(release): notify the repository that tests this tool on publish Each tool has a repository that exercises it against a real subject, and each of those resolves "latest" from crates.io on a schedule of its own -- so a new version is picked up without anything being edited, but only at the next tick. That is up to a day for the daily checks and up to a week for the weekly ones. A repository_dispatch on publish starts the deep run immediately. It is best-effort on purpose: continue-on-error, and a missing token is a notice rather than a failure. The schedules stay exactly as they were, because a dispatch that never arrives -- an expired token, a release cut by hand, a fork without the secret -- must cost latency and nothing else. A published crate is published whether or not the notification landed. Needs a PAT with contents: write on the testing repository, stored as TESTING_REPO_DISPATCH_TOKEN. Without it the job says so and exits clean. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com> --- .github/workflows/release.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ae65a2c..66051c4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -139,3 +139,38 @@ jobs: git tag -f "$major" "$TAG^{commit}" git push -f origin "refs/tags/$major" echo "$major now points at $TAG ($(git rev-parse --short "$TAG^{commit}"))" + + # Tell the repository that tests this tool against real kernels that a new + # version exists, so the deep run happens now rather than at its next + # scheduled tick. + # + # Best-effort by design. launchbound-action-demo resolves the newest + # published launchbound-cli from crates.io on a schedule of its own, so a + # dispatch that never arrives -- an expired token, a release cut by hand -- + # costs latency and nothing else. This job never fails the release: a + # published crate is published whether or not the notification landed. + # + # Needs a PAT with `contents: write` on launchbound-action-demo, stored as + # TESTING_REPO_DISPATCH_TOKEN. Without it the job says so and exits clean. + notify-testing-repo: + needs: publish + if: github.event_name == 'push' && github.ref_type == 'tag' + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Dispatch to launchbound-action-demo + continue-on-error: true + env: + GH_TOKEN: ${{ secrets.TESTING_REPO_DISPATCH_TOKEN }} + TAG: ${{ github.ref_name }} + run: | + if [ -z "${GH_TOKEN:-}" ]; then + echo "::notice::TESTING_REPO_DISPATCH_TOKEN is not set; launchbound-action-demo will pick this up on its own schedule instead" + exit 0 + fi + gh api repos/vyncint/launchbound-action-demo/dispatches \ + -f event_type=tool-published \ + -F "client_payload[version]=${TAG#v}" \ + && echo "dispatched tool-published to launchbound-action-demo" \ + || echo "::warning::could not dispatch to launchbound-action-demo; it will pick this up on its own schedule" From 3187100e2e8dc4121018c8a650092d8d7c5e1b3f Mon Sep 17 00:00:00 2001 From: Vyncint Ng <115854244+vyncint@users.noreply.github.com> Date: Sat, 22 Aug 2026 12:47:40 +0700 Subject: [PATCH 3/3] ci(release): name the notify job zizmor audits at pedantic level, where a job without a `name:` is an anonymous-definition finding. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com> --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 66051c4..59ab4a0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -153,6 +153,7 @@ jobs: # Needs a PAT with `contents: write` on launchbound-action-demo, stored as # TESTING_REPO_DISPATCH_TOKEN. Without it the job says so and exits clean. notify-testing-repo: + name: notify-testing-repo needs: publish if: github.event_name == 'push' && github.ref_type == 'tag' runs-on: ubuntu-latest