GitHub Actions can automatically generate changelogs from your commit messages, saving you the manual drudgery.
Let’s see it in action. Imagine a simple GitHub repository with a workflow file named .github/workflows/changelog.yml.
name: Generate Changelog
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Generate Changelog
id: changelog
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const commits = await github.rest.repos.listCommits({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.sha,
per_page: 100
});
let changelogContent = "# Changelog\n\n";
commits.data.forEach(commit => {
changelogContent += `- ${commit.commit.message.split('\n')[0]}\n`;
});
fs.writeFileSync('CHANGELOG.md', changelogContent);
console.log(changelogContent);
- name: Upload Changelog
uses: actions/upload-artifact@v3
with:
name: changelog
path: CHANGELOG.md
When you push a commit to main, this workflow triggers. The actions/checkout@v3 action pulls your repository’s code. Then, the actions/github-script@v6 action runs a Node.js script. This script uses the GitHub API to fetch the last 100 commits. For each commit, it extracts the first line of the commit message (assuming this is your summary) and appends it to a CHANGELOG.md file. Finally, actions/upload-artifact@v3 makes this generated CHANGELOG.md available as a build artifact.
The core problem this solves is the tedious, error-prone process of manually updating a changelog. Developers often forget to log their changes, or they do so inconsistently. This automation ensures that every commit contributes to a living changelog, providing a clear history of changes.
Internally, the github-script action provides access to the github object, which is a pre-authenticated Octokit client. This allows direct interaction with the GitHub API. context provides information about the workflow run, such as the repository owner, name, and the commit SHA. The script leverages github.rest.repos.listCommits to retrieve commit data. The sha: context.sha parameter ensures we get commits from the current HEAD of the branch. The per_page: 100 parameter limits the number of commits fetched in a single API call. The script then iterates through this list, using commit.commit.message.split('\n')[0] to grab the subject line of each commit message. This subject line is then formatted and written to a local CHANGELOG.md file.
The actions/upload-artifact action is crucial for making the generated changelog accessible. After the workflow runs, you can download this artifact from the Actions tab of your repository. This isn’t the final destination, but a step towards integrating it into your release process.
A common pitfall is assuming all commit messages are consistently formatted. If your commit messages include prefixes like "feat:", "fix:", "chore:", or detailed body text, the simple split('\n')[0] might not yield the desired changelog entry. You’d need to refine the script to parse these messages more intelligently, perhaps using regular expressions or dedicated commit message parsing libraries. For instance, a more robust approach might involve looking for specific patterns or filtering out certain types of commits (like chore:).
The next step in this process is often integrating this generated changelog into a release process, potentially using a "Create Release" action that tags a commit and attaches the changelog.