This Action returns a markdown formatted changelog between two git references. There are other projects that use milestones, labeled PRs, etc. Those are just too much work for simple projects.
I just wanted a simple way to populate the body of a GitHub Release.
A GITHUB_TOKEN with the ability to pull from the repo in question. This is required.
Why do we need myToken? Read more here: https://help.github.com/en/actions/automating-your-workflow-with-github-actions/authenticating-with-the-github_token#about-the-github_token-secret
The name of the head reference. Default ${{github.sha}}.
The name of the second branch. Defaults to the tag_name of the latest GitHub release. This must be a GitHub release. Git tags or branches will not work.
Whether the order of commits should be printed in reverse. Default: 'false'
Whether this action should pull in all other branches and tags. Default: 'true'
Markdown formatted changelog. One line per commit:
- [2c37a1c](http://github.com/owner/repo/commit/2c37a1c...) - ` feat: add the thing `The commit subject is wrapped in an inline-code span, so it renders as
literal text. A commit message is untrusted input -- anyone who can land a
commit chooses it -- and these lines are pasted straight into release notes.
Without the code span a subject could add its own links, images, @mentions,
issue references, raw HTML, or extra changelog entries to your release.
The practical consequences: subjects render monospace, and #123, GH-123,
bare SHAs, and :emoji: inside a subject no longer autolink. The generated
commit link at the start of each line is unaffected. Control characters,
line and paragraph separators, and bidirectional-control characters are
replaced with spaces, so a subject can never span more than its own line.
There are two blocks you will need:
First you will need to generate the changelog itself. To get the changelog between the SHA of the commit that triggered the action and the tag of the latest release:
- name: Generate changelog
id: changelog
uses: metcalfc/changelog-generator@v5.0.1
with:
myToken: ${{ secrets.GITHUB_TOKEN }}Or, if you have two specific references you want:
- name: Generate changelog
id: changelog
uses: metcalfc/changelog-generator@v5.0.1
with:
myToken: ${{ secrets.GITHUB_TOKEN }}
head-ref: 'v0.0.2'
base-ref: 'v0.0.1'If you want to point to a branch containing forward slashes (#179) do the following:
# let the checkout action do the fetching
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Generate changelog
id: changelog
uses: metcalfc/changelog-generator@v5.0.1
with:
myToken: ${{ secrets.GITHUB_TOKEN }}
head-ref: 'origin/my/branch/with/slashes' #add 'origin/` in front of your branch name
base-ref: 'v5.0.1'
fetch: falseThen you can use the resulting changelog:
- name: Get the changelog
run: |
cat << "EOF"
${{ steps.changelog.outputs.changelog }}
EOFSome folks have asked if the action can support changing the output. For example:
- Reverse order UPDATE as of 2021/11/22 chronological is the default and it can be reversed by setting
reverse: 'true'in the workflow. - Ignore entries that include this string.
- Etc
In order to keep this action as simple as possible we aren't planning to add more flags or options. However since the output is just text you can write a command line to do anything you want. In issue #93 we had a user that wanted to list the changelog in reverse order and drop any entries with gh-pages. Here is how they can do that but using Bumping as the restrict word because it shows up in this projects history:
- name: Modify the changelog
id: modified
env:
CHANGELOG: ${{ steps.changelog.outputs.changelog }}
run: |
set -euo pipefail
log=$(printf '%s\n' "$CHANGELOG" | grep -v Bumping | grep -v '^[[:space:]]*$' | tac)
delimiter=$(openssl rand -hex 16)
{
echo "log<<$delimiter"
echo "$log"
echo "$delimiter"
} >> "$GITHUB_OUTPUT"
- name: Print the modified changelog
run: |
cat << "EOF"
${{ steps.modified.outputs.log }}
EOFThat heredoc is how you return a multiline value. A plain echo "log=$log" >> $GITHUB_OUTPUT keeps only the first line.
The changelog is read from the environment rather than interpolated into the script. ${{ }} inside a run: block is textual substitution, so a value pasted into a heredoc picks up that block's YAML indentation on its first line -- which Markdown then renders as a code block -- and a commit subject is untrusted input besides.
This example used to percent-encode the newlines as %0A instead, which the long-gone ::set-output command decoded. $GITHUB_OUTPUT does not, so that version produced a single line with literal %0A in it. Use a random delimiter rather than a fixed one: the value is built from commit subjects, and a fixed marker is something a commit subject could contain in order to write additional keys into $GITHUB_OUTPUT.
Generating the release notes for a GitHub Release.
Issues are for folks who are actively using the action and running into an "issue" (bug, missing doc, etc).
Feature requests should be in the discussion section.. Just to set expectations the bar for a new feature getting added is going to be very high. There is a cost to adding features in the development and maintainance of the feature. So if you want to jump in and help develop and maintain lets discuss. If you want to fire off feature ideas, go for it. Just understand its very likely that without someone willing to take up the task, they won't get implemented.
Releases of this action include build provenance attestations signed via Sigstore, so you can verify that the code you're running was built from this repository.
Always pin actions to a full commit SHA instead of a mutable tag. Tags can be overwritten (this is how the March 2025 tj-actions supply chain attack worked). A SHA is immutable:
# Mutable tag (risky)
uses: metcalfc/changelog-generator@v4
# Immutable SHA (safe)
uses: metcalfc/changelog-generator@3f82cef08fe5dcf57c591fe165e70e1d5032e15a # v5.0.1Use Dependabot to keep SHA-pinned actions up to date automatically.
You can verify that a release was built by this repository's CI:
gh attestation verify --repo metcalfc/changelog-generator dist/index.jsThe action renders every commit subject as literal text rather than active
Markdown. See changelog for what that changes in your release
notes and why.
Please report security issues via GitHub's private vulnerability reporting, not public issues.
Since Dependabot
has native GitHub Actions support,
to enable it on your GitHub repo all you need to do is add the .github/dependabot.yml file:
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
interval: 'daily'Error: Not Found
If you are seeing this error its likely that you do not yet have a GitHub release. You might have a git tag and that shows up in the release tab. The API this Action uses only works with GitHub Releases. Convert one of your tags to a release and you'll be on your way. You can check out how this repository uses this action and GitHub releases for an example.
I took the basic framework for this action from: jessicalostinspace/commit-difference-action. Thanks @jessicalostinspace.
