Generate release notes from the changelog section
release-github.yml now checks out the tag and composes the release body by extracting that version's section from CHANGELOG.md, failing hard when it is missing, and appends the full-changelog link plus a sigit.si/code footnote backlink. The sigit-code-release skill records that the release body is generated, never hand-edited on GitHub. Co-Authored-By: siGit Code <noreply@sigit.si>
Seto Elkahfi committed
Sep 13, 2026 at 16:36 UTC
e563c96fccbe99d7c435287c19c920235017d7c4
2 files changed
+37
.agents/skills/sigit-code-release/SKILL.md
+1
@@ -35,6 +35,7 @@ Use this skill when preparing a release for this repository.
35
- The crate is published to crates.io (`release-crates.yml`) and the Homebrew tap is updated (`release-homebrew.yml`) as part of the tag-driven flow. Per the siGit release flow, Homebrew is auto-triggered — do not dispatch it manually.
36
- `release-github.yml` builds the binaries, attaches them (plus a `.deb`/`.rpm` per Linux target built with nfpm from `packaging/nfpm.yaml`) to the GitHub release, then dispatches `release-homebrew`, `release-scoop`, `release-winget`, and `release-aur`. A dispatch failure in any one of those four is logged as a warning, not a hard failure — an unconfigured channel doesn't block the rest of the release. `release-nuget.yml` and `release-mcp-registry.yml`, by contrast, are tag-triggered directly like the other language-registry workflows, not dispatched from `release-github.yml`.
37
- Every release asset carries a `.sha256` sidecar (not just the macOS Homebrew tarball). Scoop, winget, and the AUR PKGBUILD each consume the raw binaries plus their sidecar checksum, rather than the tarball.
38
+- The GitHub release body is generated, not hand-written: `release-github.yml`'s release job checks out the tag, awk-extracts the `## <version>` section from `CHANGELOG.md` (a missing section is a hard failure), appends a full-changelog link and a `sigit.si/code` footnote, and publishes it via `body_path: release_notes.md`. The only file a release-prep change needs to touch is `CHANGELOG.md` — never edit release notes in the GitHub UI, because the next tag push would overwrite them.
39
- npm and NuGet publish via OIDC trusted publishing — neither has a stored token in the repo. On npm this is configured per package (each `@getsigit/*` name has a trusted publisher pointing at `release-npm.yml`), so a new package needs the same setup before its first publish will work. OIDC requires npm >= 11.5.1 and Node >= 22.14.0.
40
- The NuGet package (`SiGit.Code`, `dotnet tool install --global SiGit.Code`) bundles all six platform binaries under `native/<os>-<arch>/` behind a managed shim, rather than shipping one package per platform like npm/PyPI. Watch the nuget.org 250 MB package-size limit if it ever grows.
41
- The baked-in official MCP server is separately listed in the public MCP Registry as `si.sigit/sigit` (`release-mcp-registry.yml`, driven by `server.json` at the repo root). It's a remote Streamable-HTTP listing, so it's verified by a DNS TXT record on the `sigit.si` domain rather than the GitHub-OIDC scheme used for package listings.
.github/workflows/release-github.yml
+36
@@ -240,6 +240,41 @@ jobs:
240
echo "tag=${{ github.ref_name }}" >> "$GITHUB_OUTPUT"
241
fi
242
243
+ - name: Checkout tag
244
+ # The release job otherwise only downloads artifacts; the changelog
245
+ # has to be read from the tree at the tag being released.
246
+ uses: actions/checkout@v6
247
+ with:
248
+ ref: ${{ steps.tag.outputs.tag }}
249
+
250
+ - name: Compose release notes
251
+ # The release page carries the section of CHANGELOG.md for this
252
+ # version, not a bare "Full changelog" default. A missing section is
253
+ # a hard failure: publishing a release whose notes don't match its
254
+ # changelog is worse than stopping the job.
255
+ shell: bash
256
+ run: |
257
+ version="${{ steps.tag.outputs.tag }}"
258
+ version="${version#v}"
259
+ section="$(awk -v ver="${version}" '
260
+ $0 == "## " ver { found = 1; next }
261
+ found && /^## / { exit }
262
+ found { print }
263
+ ' CHANGELOG.md)"
264
+ if [ -z "${section//[$' \t\n']/}" ]; then
265
+ echo "No ## ${version} section found in CHANGELOG.md" >&2
266
+ exit 1
267
+ fi
268
+ {
269
+ printf '%s\n' "${section}"
270
+ printf '\n---\n\n'
271
+ printf 'Full changelog: https://github.com/%s/blob/%s/CHANGELOG.md\n' \
272
+ "${GITHUB_REPOSITORY}" "${{ steps.tag.outputs.tag }}"
273
+ printf '\n---\n\n'
274
+ printf 'About siGit Code: [sigit.si/code](https://sigit.si/code)\n'
275
+ } > release_notes.md
276
+ cat release_notes.md
277
+
278
- name: Download binary artifacts
279
uses: actions/download-artifact@v4
280
with:
@@ -258,6 +293,7 @@ jobs:
293
uses: softprops/action-gh-release@v2
294
with:
295
tag_name: ${{ steps.tag.outputs.tag }}
296
+ body_path: release_notes.md
297
files: release/*
298
299
# These channels all publish somewhere outside this repo (a tap, a Scoop