Github release notes and changelog generator
Updates:
Since this package was forked, many changes have been made to support my particular use-case.
- I only desire to make changelogs based on merged PRs.
- I feel that the process should be idempotent, so I focus on rebuilding the entire file each time.
- The order of the commits/PRs should be in descending order and none should be excluded.
- The produced file should pass a markdown linter (e.g. markdownlint)
- For compatibility with release tools, the PRs that have landed since the last tag should be included.
- The latest version number should appear at the top of the changelog.
- Because not all commits you need to track changes of have PRs (security vulnerabilities), there is an
overridePrsconfig value for which you can specify PRs to be included.- It should return a function that returns a list of PRs.
- It will be passed the current list of PRs.
- For most cases,
commitNotesdescribes those changes without code.
gren is a small helpful robot that will do for you just create a release from a tag and compile the release notes using issues or commits.
It also can generate a CHANGELOG.md file based on the release notes (or generate a brand new).
Everyone loves neat, transparent, informative release notes. Everyone would also rather avoid maintaining them. What a hassle to have to evaluate what issues have been solved between two points in project's timeline, what types of problems they were, are they important to inform the users about, what issues solved them, etc.
Wouldn't it be great to get fantastic release notes compiled for you automatically based on all the hard work you put into your GitHub issues and pull requests?
The main motivation for bringing gren to life was the need for auto-generating release notes for every tag in a project.
The process, as explained here, requires the tagger to go to your project's releases page in GitHub, draft that tag as a new release and manually add what has changed.
Let gren take care of that for you. It automates this process and also writes release notes for you, creating something like this:
## v0.6.0 (14/03/2017)
#### Framework Enhancements
- [#32](https://github.com/github-tools/github-release-notes/issues/32) Unwrap github-api promises
- [#26](https://github.com/github-tools/github-release-notes/issues/26) Use external config file
- [#23](https://github.com/github-tools/github-release-notes/issues/23) Introduce templates for the issues
- [#19](https://github.com/github-tools/github-release-notes/issues/19) Add an "ignore label" flag
- [#12](https://github.com/github-tools/github-release-notes/issues/12) Add the chance to rebuild the history of release notes
#### Bug Fixes
- [#29](https://github.com/github-tools/github-release-notes/issues/29) Remove escaping character on regex
- [#24](https://github.com/github-tools/github-release-notes/issues/24) The changelog action doesn't compile latest release(yes, this is one of 🤖 's actual releases)
Where is the data coming from? There are three main options:
If you manage your project with issues, that's where all the information about a change are.
Issue labels increase the level of depth of what the release notes should show, helping gren to group the notes.
e.g. if you see the example above, the issues are grouped by the two labels enhancement and bug, then customised via a config file.
gren generates those notes by collecting all the issues closed between a tag (defaults to latest) and the tag before it (or a tag that you specify).
If you want to be more accurate on the issues that belong to a release, you can group them in milestones and use only the issues that belong to that Milestone.
The output above is a result of release notes built from issues.
In order to have splendidly generated release notes, we recommend to follow these conventions:
- Start the title with a verb (e.g. Change header styles)
- Use the imperative mood in the title (e.g. Fix, not Fixed or Fixes header styles)
- Use labels wisely and assign one label per issue.
grenhas the option to ignore issues that have one of the specified labels.
The simplest way of getting data is from the commits you write. Even though it doesn't require a machine-readable commit, it is still better to have them in a nice format.
The output then uses commit messages (title + description) to look something like:
## v0.9.0 (17/05/2017)
- Filter milestones (#75)
- Create milestones data-source option
- Add documentation for the milestones option
- Support GitHub enterprise (#73)
- Support GitHub enterprise
- Add api-url to options documentation
- Update CHANGELOG.mdIn order to have splendidly generated release notes, we recommend to follow these conventions:
- Start the subject line with a verb (e.g. Change header styles)
- Use the imperative mood in the subject line (e.g. Fix, not Fixed or Fixes header styles)
- Limit the subject line to about 50 characters
- Do not end the subject line with a period
- Separate subject from body with a blank line
- Wrap the body at 72 characters
- Use the body to explain what and why not how
With --data-source=prs (or prs-with-issues), gren lists the merged pull requests in each release.
A release contains the commits reachable from its tag but not from any tag with a lower version.
Each commit belongs to the lowest release that contains it, so every release's section is the same whichever branch you run gren on.
This works across release lines. For example, with 7.x on master and 6.x on a 6.x branch:
- 6.2.0 can be cut from
master, or from6.xafter mergingmasterinto it. The merge bringsmaster's pull requests into 6.2.0. - 7.0.0 then leaves out what 6.2.0 already released.
- A 6.2.1 made after 7.0.0 lists only its own pull requests, and appears in the changelog on both branches.
Merge one release line into another with a merge commit. A squash merge hides the commits, and their pull requests, that it brings in.
The version being prepared (from package.json) gets a section for the commits on --head that no tag contains yet.
Once that version is tagged those commits are its own, and anything the branch gains afterwards is gathered under an Unreleased heading until the next version is prepared, which template.unreleased renames.
Without --tags, gren writes that section and the latest tag --head contains, so on a maintenance branch it works on that line's latest release rather than on the highest version in the repository.
--tags=<new-tag>..<old-tag> puts everything since the old tag in the new tag's section, taking in the releases between them.
gren reads commits from your local clone, so fetch the tags first, e.g. git fetch upstream --tags.
It stops if a tag on GitHub is missing locally or points elsewhere.
It needs the whole history too, so run git fetch --unshallow in a shallow clone, and check out with fetch-depth: 0 in GitHub Actions.
A commit matches a pull request whose merge commit it is. The commits a merge-commit pull request brings in are covered by that pull request. Any other commit is looked up on GitHub, which finds rebase merges.
If the project's older pull requests are in another repository, such as an upstream, a fork or where the project used to live, list it in pullRequestRepos:
pullRequestRepos: ["node-saml/passport-saml"],A cherry-pick is a new commit, so it belongs to the release that contains it and matches the pull request that merged the cherry-pick, if it had one. The pull request it was taken from keeps its own entry in its own release, as both releases really do contain the change. A cherry-pick pushed straight to a branch matches nothing, and is reported like any other commit without a pull request.
Commits that no pull request describes, such as a security fix or a direct push, are not added to the changelog.
gren prints them after generating, with entries you can copy into commitNotes in your configuration file:
commitNotes: {
"8ac6118f3a": {
title: "Fix a signature wrapping vulnerability",
labels: ["security"],
url: "https://github.com/OWNER/REPO/security/advisories/GHSA-xxxx-xxxx-xxxx",
text: "GHSA-xxxx-xxxx-xxxx",
author: "reporter-login",
},
"ec309ec36b": {
pr: 330,
},
},Keys are commit SHAs of at least 7 characters. Every field is optional. An entry is placed in the release that contains its commit and grouped by its labels like a pull request. If the commit is a pull request's merge commit, the entry's fields replace that pull request's.
pr points at a pull request, as 330, "#330" or "owner/repo#330", and the entry takes its title, labels, author and link from it.
Use it for a cherry-pick that was pushed without a pull request of its own: the change is listed under the pull request it came from, in the release the cherry-pick landed in, and editing that pull request's title or labels still changes both releases' sections.
If the same pull request is already in that release, the two are listed once.
To stop a commit being reported, match it with ignoreCommitsWith, e.g. ["^Release \\d"].
To fix a section, edit the pull request's title or labels, or commitNotes, and generate the changelog again.
Rebuilding every section can change sections you have reviewed, and early history may not have pull requests at all.
--frozen-before (frozenBefore in the configuration file) takes a date, a tag or a commit SHA.
Releases made before it are copied from the existing changelog as they are, matched by the version in each section's first line; newer releases are generated.
gren changelog --generate --override --data-source=prs --frozen-before=v6.0.0gren requires Node.js 24 or newer (the current LTS).
Install github-release-notes via npm:
npm install @cjbarth/github-release-notes -gFirst, generate a GitHub token, with repo scope, at this link.
Then add this line to ~/.bash_profile (or ~/.zshrc):
export GREN_GITHUB_TOKEN=your_token_hereShow the internet that you use gren for automating your release notes ->
[](https://github.com/cjbarth/github-release-notes)gren gets the repo information directly from the folder where git is initialised.
# Navigate to your project directory
cd ~/Path/to/repo
# Run the task (see below)
gren releaseOtherwise, you can run it anywhere passing the repo information:
gren release --username=[username] --repo=[repo name]If you don't want to save the token, you can specify one as an option:
gren release --token=[your token]There are two main commands that can be ran with 🤖:
gren will look for the latest tag, draft a new release using the issues closed between when that tag and the one before were created and publish that release in your release panel in your GitHub repo. (@see how to feed 🤖).
Create a CHANGELOG.md file using all the release notes of the repo (like the ones generated by 🤖 ).
If the file exists already, use the --override option to proceed.
gren changelog --overrideTo generate a brand new release notes, using the same approach as per the releases, you have to run the command with the --generate option.
gren changelog --generategren is using Commander.js which generates the --help section.
To trigger the help of a command, run:
# General usage
gren --help
# Command usage
gren help release # or gren release --helpIt's also possible to see all the examples below, or directly in the terminal:
gren examples releaseYou can create a configuration file where the task will be run to specify your options. See all the available options. The accepted file extensions are the following:
.grenrc.grenrc.json.grenrc.yml.grenrc.yaml.grenrc.js.grenrc.cjs.grenrc.mjs
A .grenrc.js file is interpreted according to the type field of the nearest
package.json, so use export default {...} in an ESM project and
module.exports = {...} in a CommonJS one. Use .grenrc.mjs or .grenrc.cjs to
pick one explicitly. A config hosted remotely (via the gren field in
package.json) must be CommonJS.
If you need help to create the configuration file, you can run the following command and follow the instructions
gren initEvery option can be passed on the command line, or set in a configuration file.
| Option | Value | Description | Default |
|---|---|---|---|
-u, --username |
<repo owner> |
The username of the repo e.g. cjbarth | |
-r, --repo |
<repository name> |
The repository name e.g. github-release-notes | |
-T, --token |
<github token> |
The token generated with repo access | |
-a, --api-url |
<url> |
Override the GitHub API URL, allows gren to connect to a private GHE installation | |
-o, --override |
Override the release notes if exist | ||
-B, --debug |
Run the command in debugging mode | ||
-t, --tags |
<new-tag>..<old-tag> |
Write release notes for <new-tag> using data collected until <old-tag>. If only one tag is specified, will use data until the previous tag. To run gren for all the tags, use --tags=all | |
-l, --limit |
<number> |
Just produce release notes for the <number> last releases. | |
-D, --data-source |
<issues|commits|milestones|prs|prs-with-issues> |
The informations you want to use to build release notes. [issues] | issues |
-h, --head |
<string> |
Which branch would you like to include commits from after the last tag? | |
-N, --include-messages |
<merge|commits|all> |
Filter the messages added to the release notes. Only used when --data-source used is commits [commits] | commits |
-i, --ignore-tags-with |
<string1>,<string2> |
Ignore tags that contain one of the specified strings. | |
-C, --ignore-commits-with |
<string1>,<string2> |
Ignore commits that contain one of the specified strings. | |
-p, --prefix |
<name prefix> |
Add a prefix to the tag version. e.g. 'v' | |
-g, --group-by |
<label> |
Group the issues using the labels as group headings. You can set custom headings for groups of labels from a configuration file. | |
-L, --ignore-labels |
<label1>,<label2> |
Ignore the specified labels. | |
-I, --ignore-issues-with |
<label1>,<label2> |
Ignore issues that contains one of the specified labels. | |
-R, --pull-request-repos |
<owner/repo1>,<owner/repo2> |
Other repositories to look for pull requests in, such as an upstream, a fork or where the project used to live. Only used when --data-source is prs or prs-with-issues. | |
-M, --milestone-match |
<prefix> |
The title that the script needs to match to link the release to the milestone. e.g. v will match v0.1.0 [Release {{tag_name}}] | Release {{tag_name}} |
-m, --only-milestones |
Add to the release bodies only the issues that have a milestone | ||
-q, --quiet |
Run command without console logs. | ||
-c, --config |
<string> |
Specify a custom config filename |
| Option | Value | Description | Default |
|---|---|---|---|
-d, --draft |
Set the release notes as a draft. | ||
-P, --prerelease |
Set the release as a prerelease. |
| Option | Value | Description | Default |
|---|---|---|---|
-G, --generate |
Generate the changelog with gren rather than using the repo releases | ||
-f, --changelog-filename |
<filename.md> |
The name of the changelog file. [CHANGELOG.md] | CHANGELOG.md |
-F, --frozen-before |
<date|tag|sha> |
Copy the sections of releases made before this date, or before this tag's or commit's date, from the existing changelog instead of generating them. Only used with --generate and the prs or prs-with-issues data source. |
Help — Show the general help of the gren tool
gren
gren --help
gren -hVersion — Show the using version
gren --version
gren -vGet help for the release options
gren help releaseManual repo infos — Run gren outside of the project folder.
gren release --username=REPO_USER --repo=REPO_NAMEOverride an existing release — By default, gren won't override an existing release and it will flag Skipping 4.0.0 (use --override to replace it). If you want to override, as it suggests, use:
gren release --overrideCreate release notes for a specific tag — Create release notes from the commits or issues closed for the specified tag and the one before.
gren release --tags=4.0.0Create release notes from the commits or the issues between two specified tags.
gren release --tags=4.0.0..3.0.0Create release notes for all the tags — Create release notes for all the tags in the repository.
gren release --tags=allIgnore the tags including an Array of strings
gren release --tags=all --ignore-tags-with="-rc","-alpha","-beta"Work with milestones — Create release notes for a tag using the belonging to a milestone that matches the name of the tag. e.g. If the tag is 4.0.0, gren is going to match the milestone "Release 4.0.0".
gren release --data-source=milestones --milestone-match="Release {{tag_name}}"Otherwise, you can just filter the issues that belong to a milestone
gren release --only-milestonesUse commit messages — Generate release notes based on commit messages
gren release --data-source=commitsCustom changelog — Create a changelog with a custom filename
gren changelog --generate --override --changelog-filename=RELEASE_NOTES.mdThanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!