ci(actions): create GitHub Release directly in publish-tag job (#187)

The default GITHUB_TOKEN cannot trigger downstream workflows (GitHub
prevents the chain to avoid infinite loops). publish-tag's `git push
origin "$TAG"` therefore never fires release.yml's `push: tags:`
listener — observed on v0.1.0-dev.50, where the tag landed but the
Release object was missing until manually created.

Fold the Release creation into the publish-tag job using `gh release
create` with GH_TOKEN=secrets.GITHUB_TOKEN. Direct API calls work fine
with the default token; only the push→workflow chain is blocked.

- Idempotent: skips if release already exists.
- Notes: auto-generated from `git log $prev..$TAG` plus a Verification
  block linking back to the CI run.
- Prerelease flag respects steps.version.outputs.prerelease (true for
  dev/rc tags, false for vX.Y.Z final tags on main).

release.yml stays untouched as a fallback for manually-pushed tags
(humans pushing tags directly CAN trigger workflows).

Closes #186
This commit was merged in pull request #187.
This commit is contained in:
james.maes authored and GitHub committed 2026-05-13 18:28:07 -05:00
1 parent a26f49fdbd
commit 774e60a020
1 file changed
+48
+48
View File
@@ -431,3 +431,51 @@ jobs:
fi
git tag -a "$TAG" -m "Automated tag from CI for $GH_REF"
git push origin "$TAG"
# GitHub Actions GITHUB_TOKEN cannot trigger downstream workflows. The
# separate `release.yml` workflow listens for `push: tags:` events but
# never sees a CI-pushed tag — observed on v0.1.0-dev.50 (tag created,
# Release object missing, had to be manually created). Create the
# Release object directly here in the same job so the Releases page
# reflects CI-pushed tags without a manual step. `release.yml` stays
# as a fallback for manually-pushed tags (which CAN trigger workflows).
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.version.outputs.tag }}
PRERELEASE: ${{ steps.version.outputs.prerelease }}
RUN_URL: ${{ format('https://github.com/{0}/actions/runs/{1}', github.repository, github.run_id) }}
run: |
set -euo pipefail
if ! git rev-parse "refs/tags/$TAG" >/dev/null 2>&1; then
echo "Tag $TAG not present locally; nothing to release." >&2
exit 0
fi
if gh release view "$TAG" >/dev/null 2>&1; then
echo "Release $TAG already exists — skipping create (idempotent)."
exit 0
fi
prev=$(git tag --list 'v*' --sort=-v:refname | grep -v "^${TAG}$" | head -n 1 || true)
notes_file=$(mktemp)
{
echo "## Changes"
echo
if [ -n "$prev" ]; then
git log --pretty='format:- %s (%h)' "${prev}..${TAG}"
else
git log --pretty='format:- %s (%h)' -20
fi
echo
echo
echo "## Verification"
echo
echo "- CI build: $RUN_URL"
echo "- Boundary gates, webview, app-build, 13/13 SPM packages: all green on this commit"
} > "$notes_file"
flag=()
if [ "$PRERELEASE" = "true" ]; then
flag+=("--prerelease")
fi
gh release create "$TAG" \
--title "$TAG" \
--notes-file "$notes_file" \
"${flag[@]}"