diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6830dea..dfc8721 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -131,11 +131,26 @@ jobs: found {print}' CHANGELOG.md > release-notes.md cat release-notes.md - - name: Create GitHub Release + # Converge rather than create. `gh release create` refuses an existing + # tag, so re-dispatching a release that already got this far failed here + # every time — after build and publish had both succeeded, which is the + # most confusing place to stop. workflow_dispatch was added to this + # workflow so a release could be re-driven through a webhook outage; a + # final step that cannot survive a second run takes that back. + - name: Create or update GitHub Release env: GH_TOKEN: ${{ github.token }} run: | - gh release create "${TAG}" just-bashit.tar.gz \ - --title "just-bashit ${TAG}" \ - --latest \ - --notes-file release-notes.md + if gh release view "${TAG}" >/dev/null 2>&1; then + echo "Release ${TAG} already exists — updating it in place" + gh release edit "${TAG}" \ + --title "just-bashit ${TAG}" \ + --latest \ + --notes-file release-notes.md + gh release upload "${TAG}" just-bashit.tar.gz --clobber + else + gh release create "${TAG}" just-bashit.tar.gz \ + --title "just-bashit ${TAG}" \ + --latest \ + --notes-file release-notes.md + fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 20c9b97..a6e3f2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ ## [Unreleased] +### Fixed + +- **`make ship` could report a release as failed while it succeeded.** + `release-watch` took the newest release run with `--limit 1`, but `ship` + is `tag-release` then `release-watch`, and the tag push returns before + GitHub has created the run — so the newest row was still the *previous* + release. Shipping v0.4.1 watched a run that had failed the night before + and exited 1 while the real release went on to succeed. A watcher that can + report the wrong run is worse than no watcher, because the failure it + invents looks exactly like a real one. It now selects the run by tag — + `--branch "v$VERSION"`, which is what a tag-triggered run carries — and + waits up to 60s for it to exist, erroring clearly if it never does. + +- **A re-dispatched release always failed at the last step.** Since + `gh release create` refuses an existing tag, re-driving a release that had + already got as far as publishing died on "a release with the same tag name + already exists" — after build and publish had both succeeded. The + `workflow_dispatch` trigger was added to this workflow so a release could + be re-driven through a webhook outage; a final step that cannot survive a + second run takes that ability back. It now updates the release in place + when one already exists. + ## [0.4.1] - 2026-08-06 ### Added diff --git a/Makefile b/Makefile index bfeb6ac..0056411 100644 --- a/Makefile +++ b/Makefile @@ -152,7 +152,34 @@ just-runit|sed -n 's/^_VERSION="\(.*\)"/\1/p' src/just_bashit/just-runit src/ headers|grep -h '^# PACKAGE' src/just_bashit/*.sh | sed 's/.*version \([0-9.]*\).*/\1/' | sort -u endef -RELEASE_WATCH_CMD = gh run watch --exit-status $$(gh run list \ - --workflow=release.yml --limit 1 --json databaseId -q '.[0].databaseId') +# Select the run BY TAG, and wait for it to exist. +# +# `--limit 1` alone is a race: `ship` is `tag-release` then `release-watch`, and +# the push returns before GitHub has created the workflow run, so the newest +# row is still the PREVIOUS release. Shipping v0.4.1 watched the run that had +# failed the night before and reported the release as failed while it went on +# to succeed — a watcher that can report the wrong run is worse than none, +# because the failure it invents is indistinguishable from a real one. +# +# A tag-triggered run carries the tag in headBranch, so --branch pins it to +# exactly this release. The wait covers creation latency, which is a second or +# two; 60s of headroom costs nothing on the happy path and turns "no run yet" +# into a clear error rather than an empty run ID. +define RELEASE_WATCH_CMD +@tag="v$(VERSION)"; id=""; \ + for _ in $$(seq 1 30); do \ + id=$$(gh run list --workflow=release.yml --branch "$$tag" \ + --limit 1 --json databaseId -q '.[0].databaseId' 2>/dev/null); \ + [ -n "$$id" ] && break; \ + sleep 2; \ + done; \ + if [ -z "$$id" ]; then \ + echo "ERROR: no release run for $$tag after 60s"; \ + echo " check: gh run list --workflow=release.yml --branch $$tag"; \ + exit 1; \ + fi; \ + echo "Watching release run $$id ($$tag)"; \ + gh run watch --exit-status "$$id" +endef include standard.mk