@actionutils/gh-release-notes
v0.7.0
Published
PR-based release notes generator. Release-drafter compatible CLI using release-drafter internals.
Readme
gh-release-notes
PR-based release notes generator with strong compatibility with GitHub’s Generate Release Notes and release-drafter. Zero-config works out of the box, and you can keep using your existing .github/release.yml or .github/release-drafter.yml.
Highlights
- Flexible templating with MiniJinja for finer control than release-drafter templates
- JSON output to drive any templating engine or downstream tooling
- Remote config/template via GitHub purl for easy organization-wide sharing
- Works with read-only permissions; no write permission needed (see Permissions)
Use cases
- Generate release notes from PRs with existing configs
- Automate in CI/CD or preview locally
- Shape the output via JSON or your favorite templating engine
Installation
Quick install (latest)
- Install the binary
curl -sSfL https://github.com/actionutils/gh-release-notes/releases/latest/download/install.sh | sh- Or run directly without installation
curl -sSfL https://github.com/actionutils/gh-release-notes/releases/latest/download/run.sh | sh# Choose the script to execute
SCRIPT="install.sh" # or "run.sh"
DOWNLOAD_URL="https://github.com/actionutils/gh-release-notes/releases/latest/download"
curl -sL "${DOWNLOAD_URL}/${SCRIPT}" | \
(tmpfile=$(mktemp); cat > "$tmpfile"; \
cosign verify-blob \
--certificate-identity-regexp '^https://github.com/actionutils/trusted-go-releaser/.github/workflows/trusted-release-workflow.yml@.*$' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
--certificate "${DOWNLOAD_URL}/${SCRIPT}.pem" \
--signature "${DOWNLOAD_URL}/${SCRIPT}.sig" \
"$tmpfile" && \
sh "$tmpfile"; rm -f "$tmpfile")Specific version (simple)
Install
VERSION="<version>" # e.g., v0.6.0
curl -sSfL "https://github.com/actionutils/gh-release-notes/releases/download/${VERSION}/install.sh" | shRun without installation
VERSION="<version>" # e.g., v0.6.0
curl -sSfL "https://github.com/actionutils/gh-release-notes/releases/download/${VERSION}/run.sh" | shSpecific version (verified with cosign)
Install (verified)
VERSION="<version>" # e.g., v0.6.0
BASE="https://github.com/actionutils/gh-release-notes/releases/download/${VERSION}"
curl -sL "${BASE}/install.sh" | \
(tmpfile=$(mktemp); cat > "$tmpfile"; \
cosign verify-blob \
--certificate-identity-regexp '^https://github.com/actionutils/trusted-go-releaser/.github/workflows/trusted-release-workflow.yml@.*$' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
--certificate "${BASE}/install.sh.pem" \
--signature "${BASE}/install.sh.sig" \
"$tmpfile" && \
sh "$tmpfile"; rm -f "$tmpfile")Run without installation (verified)
VERSION="<version>" # e.g., v0.6.0
BASE="https://github.com/actionutils/gh-release-notes/releases/download/${VERSION}"
curl -sL "${BASE}/run.sh" | \
(tmpfile=$(mktemp); cat > "$tmpfile"; \
cosign verify-blob \
--certificate-identity-regexp '^https://github.com/actionutils/trusted-go-releaser/.github/workflows/trusted-release-workflow.yml@.*$' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
--certificate "${BASE}/run.sh.pem" \
--signature "${BASE}/run.sh.sig" \
"$tmpfile" && \
sh "$tmpfile"; rm -f "$tmpfile")GitHub CLI extension
Install
gh extension install actionutils/gh-release-notesUpgrade
gh extension upgrade actionutils/gh-release-notesnpx / pnpx / bunx (no install)
npx @actionutils/gh-release-notes
pnpx @actionutils/gh-release-notes
bunx @actionutils/gh-release-notesUsage
- Generate from latest changes (auto-detects previous release):
As gh extension:gh-release-notesgh release-notes - Specify previous and next tags:
gh-release-notes --prev-tag v1.0.0 --tag v1.1.0 - Target a branch/revision:
gh-release-notes --target main - Use a MiniJinja template:
gh-release-notes --template ./templates/github.md.jinja gh-release-notes --template pkg:github/actionutils/gh-release-notes@main#templates/table.md.jinja - JSON output (for scripting or custom rendering):
gh-release-notes --json > release.json
Tip: You can read the resolved version fields from the output (e.g., release.resolvedVersion) to decide tagging and releasing logic in your pipeline.
Configuration and Compatibility
Zero-config works
- You can run
gh-release-noteswithout any config and get a sensible, GitHub-like changelog. Use existing configs .github/release.yml(GitHub Generate Release Notes).github/release-drafter.yml(release-drafter)
Getting a config
- Start from a minimal, compatible config via
gh-release-notes init(writes.github/release-drafter.yml, use-o -to print,--forceto overwrite) - Already on GitHub’s
.github/release.yml? Convert it withgh-release-notes migrate(supports--source/--outputand-o -)
Remote configs/templates
- Prefer GitHub purl:
pkg:github/OWNER/REPO@REF#path/to/config.yaml- Recommended over raw.githubusercontent.com due to rate limits
- Great for shared organization-wide config and templates
- HTTPS URLs and local files are also supported
gh-release-notes --config pkg:github/actionutils/gh-release-notes@main#.github/release-drafter.ymlTemplates and Output
Default behavior
- Uses release-drafter–style templates from your release-drafter config
- Adds two extra placeholders:
$FULL_CHANGELOG_LINK,$NEW_CONTRIBUTORS - Fetches only fields required by your template to keep API calls minimal
MiniJinja for maximum flexibility
--template <path|https|purl>renders with the same data as--json- Bring your own structure and style (or use community-maintained templates)
JSON structure (example)
release:name,tag,body,targetCommitish,resolvedVersion,majorVersion,minorVersion,patchVersionpullRequests{ <pr-number>: <PR data> }: map of PR number to PR object containingnumber,title,url,mergedAt,author{ login, type, url, avatarUrl, sponsorsListing? },labels[]mergedPullRequests[]: array of PR numbers in display ordercategorizedPullRequests:uncategorized[]: array of PR numberscategories[]:title,labels[],collapse_after?,pullRequests[](array of PR numbers)
pullRequestsByLabel: PR numbers grouped by labellabels{ <label>: number[] }: map from label name to PR numbers (each PR appears under all its labels; order matchesmergedPullRequests)unlabeled[]: PR numbers without any labels
contributors[]: all PR authorsnewContributors[] | null: first-time contributors (containsloginandfirstPullRequestas PR number)owner,repo,defaultBranch,lastRelease,fullChangelogLink
Example (MiniJinja)
## Release {{ release.tag }}
### ✨ Highlights
{% for pr_number in mergedPullRequests %}
{% if loop.index <= 5 %}
- {{ pullRequests[pr_number|string].title }} (#{{ pr_number }}) by @{{ pullRequests[pr_number|string].author.login }}
{% endif %}
{% endfor %}
**Full Changelog**: {{ fullChangelogLink }}Contributors' Sponsor Information
- Contributors may include their GitHub Sponsors listing URL in the output
- Fetching modes:
auto,graphql,html,noneauto: intelligently selects based on token type and usagegraphql: requires a user token (PAT, etc.); App tokens (includingGITHUB_TOKEN) are blocked by GitHub for this fieldhtml: HEAD-requests sponsor pages (experimental); backs off on errors/rate limits
Contributors of reviewdog v0.21.0
## Contributors of reviewdog v0.21.0
<table>
<tbody>
<tr>
{%- for contributor in contributors %}
<td align="center" valign="top" width="100">
<a href="{{ contributor.url }}">
<img src="https://wsrv.nl/?url={{ contributor.avatarUrl }}&w=64&h=64&mask=circle&fit=cover&maxage=1w" width="64px" alt="{{ contributor.login }}"/><br />
</a>
<sub>@{{ contributor.login }}{% if contributor.type == 'Bot' %}[bot]{% endif %}</sub>
{%- if contributor.sponsorsListing %}
<br><br>
<a href="{{ contributor.sponsorsListing.url }}" title="Sponsor {{ contributor.login }}">
<img src="https://img.shields.io/badge/-Sponsor-ea4aaa?style=for-the-badge&logo=github&logoColor=white" height="24" />
</a>
{%- endif %}
</td>
{%- if loop.index % 6 == 0 and not loop.last %}
</tr>
<tr>
{%- endif %}
{%- endfor %}
</tr>
</tbody>
</table>
{%- endif %}Differences from release-drafter
- Extra placeholders available:
$FULL_CHANGELOG_LINK,$NEW_CONTRIBUTORS - Zero-config works; also easy to standardize via purl remote config/templates
- JSON output enables fully custom rendering pipelines
- Optional sponsor enrichment (adds sponsors listing URL when available)
Permissions
- Public repositories: works without any token for most operations
- Private repositories: requires only read scopes
contents: readpull_requests: read
- Unlike GitHub’s Release Notes Generator API, this tool does not need
contents: write
License
MIT
