GitHub Releases Auto-Generated from PRs
Maintaining a consistent, informative changelog and crafting release notes are often seen as necessary evils in the software development lifecycle. For many engineering teams, this task is an afterthought, hastily cobbled together just before a release, or worse, skipped entirely. The result? Users are left guessing what's new, product managers lack clear communication points, and developers spend valuable time on manual, repetitive work.
But what if your release notes could practically write themselves? What if the very process you use to integrate code – Pull Requests (PRs) – could become the primary source of truth for your release documentation? This isn't a pipe dream; it's an increasingly common and effective strategy. Let's explore how you can leverage your existing GitHub workflow to auto-generate release notes, transforming a tedious chore into a streamlined, automated process.
The Challenge of Manual Release Notes
Think about your last release. Did you or a teammate spend hours sifting through commit history, trying to remember what each merged branch actually delivered? Did you then struggle to translate technical changes into user-friendly language, only to have a product manager request revisions because it wasn't clear enough?
This manual approach is fraught with problems:
- Time-consuming: It diverts valuable engineering time away from building new features or fixing bugs.
- Error-prone: Human memory is fallible. Details get missed, or descriptions might be inaccurate.
- Inconsistent: Different people write release notes differently, leading to varied quality and format.
- Delayed: Often, release notes are the last thing to be done, holding up the actual release or causing a scramble.
- Lacks context: Without direct input from the developer who implemented the change, the release note might miss crucial context or impact.
In today's fast-paced development environments, these inefficiencies add up quickly.
Why Pull Requests are a Great Source of Truth
Pull Requests are more than just a mechanism for code review; they are a rich repository of information about every change introduced into your codebase. When used effectively, a PR encapsulates:
- The "What": The code changes themselves.
- The "Why": The problem being solved or the feature being implemented.
- The "How": Often detailed in the description, explaining the approach.
- The "Who": The author and reviewers.
- The "When": The timestamp of the merge.
Crucially, a well-written PR title and description often contain exactly the kind of user-facing language you want in your release notes. If a PR is reviewed and merged, it implies that its description is a reasonably accurate summary of the change. By treating your merged PRs as the atomic units of your changelog, you're tapping into information that's already been created, reviewed, and approved.
Strategies for Auto-Generating Release Notes
There are two main approaches to automating release note generation from PRs: building your own solution or leveraging specialized tools.
Option 1: Manual Scripting (The DIY Approach)
For teams with unique requirements or a desire for ultimate control, building a custom script is an option. This typically involves:
-
Identifying Merged PRs: You'd use
git logto find merge commits between your last release tag and the currentHEADof your main branch.bash git log --pretty=format:"%s (%an)" --merges <last_release_tag>..HEADThis command fetches the subject line (which for merge commits often contains the PR title) and the author of each merge commit. You'd then parse these subjects to extract PR numbers (e.g.,Merge pull request #123 from user/feature-branch). -
Fetching PR Details: With the PR numbers, you'd then use the GitHub API (via a tool like
ghCLI or a library like Octokit) to fetch the full PR title, description, labels, and other metadata.bash # Example using gh CLI to get a PR title and body gh pr view 123 --json title,body --jq '.title, .body' -
Parsing and Formatting: You'd write logic to parse the PR titles and descriptions, categorize them (e.g., "Features," "Bug Fixes," "Chores"), and format them into Markdown or another desired output.
Pitfalls of the DIY Approach:
- Maintenance Burden: GitHub API changes, new PR templates, or different merge strategies (squash vs. merge commits) can break your script.
- Complexity: Extracting meaningful, user-friendly information from raw PR descriptions can be complex, especially if PR hygiene isn't perfect.
- Categorization: Automatically classifying changes beyond simple keywords requires sophisticated parsing or adherence to strict conventions.
- Authentication & Rate Limits: Dealing with GitHub API authentication and avoiding rate limits requires careful handling.
- Squash Merges: If you use squash merges, the commit history on your main branch will be clean, but the merge commit messages might not directly reference the original PR number in a consistent way, making it harder to link back to the full PR context without additional API calls based on commit messages.
Option 2: Leveraging Specialized Tools (e.g., Shipnote)
This is where dedicated tools shine. They abstract away the complexity of API interactions, parsing, and formatting, allowing you to focus on defining your release strategy. These tools often rely on conventions within your PRs to intelligently categorize and present changes.
Real-world Example 1: Conventional Commits in PR Titles
Many tools, including Shipnote, heavily leverage conventions like Conventional Commits. This standard provides a lightweight but powerful way to add semantic meaning to your commit messages and, by extension, your PR titles.
A typical Conventional Commit structure looks like this: type(scope): description.
type:feat(feature),fix(bug fix),docs(documentation),style(formatting),refactor(code refactoring),perf(performance),test(adding tests),chore(routine tasks),build(build system changes).scope(optional): Specifies the part of the codebase affected (e.g.,api,ui,auth).description: A concise, imperative summary of the change.
Example PR Title:
feat(api): add endpoint for user profiles (#123)
Example PR Description:
``
This PR introduces a new/users/{id}endpoint to retrieve user profile information.
It includes:
- A newUserProfile` model.
- Authentication middleware for the endpoint.
- Unit tests for the new API route.
BREAKING CHANGE: The old /profile endpoint is deprecated and will be removed in v2.0.0