This Bash script automates the generation of Markdown release notes from Git history following the Conventional Commits specification. It is designed for CI pipelines and release automation, ensuring consistent, readable, and categorized summaries. It parses raw commit logs, groups them by impact (features, fixes, performance), and formats them into a clean Markdown structure.
- Automated Categorization: Automatically groups commits into meaningful sections based on their type (e.g.,
feat,fix,perf,chore). - Breaking Change Detection: Highlights critical updates by detecting
!in the commit header orBREAKING CHANGE:in the body/footer. These are placed at the very top of the notes. - Scope Formatting: bolds the scope of a commit for better readability (e.g., api: update endpoint).
- Author Exclusion: Easily filter out commits from bots or specific users using
--exclude-authors(e.g.,dependabot[bot]). - Noise Reduction: Filters out irrelevant commits using
--exclude-types(e.g., removingchoreordocs) or--exclude-scopes(e.g., removinginternal). - Strict Mode: Optionally excludes non-conventional commits entirely via
--exclude-invalid-commits.
- Optimized Parsing: Uses native Bash parameter expansion instead of subshells (
sed/awk) for string manipulation, making it capable of processing thousands of commits in seconds. - Dependency Free: Requires only
gitandbash. No Python, Node.js, or compiled binaries required.
- Clickable Links: Generates hyperlinks for commit hashes if a
--repo-urlis provided. - Standard Formatting: Output is written to standard output (stdout), ready to be piped directly into release
bodies (GitHub Releases, GitLab Tags, or
RELEASE_NOTES.mdfiles).
Usage: release-notes.sh <from_ref> <to_ref> [OPTION]...
Generates Markdown release notes from Git history based on Conventional Commits.
Output is written to standard output (stdout).
Arguments:
<from_ref> Previous release tag or commit hash (exclusive).
<to_ref> Current release tag, commit hash, or HEAD (inclusive).
Options:
-r, --repo-url <url>
Base URL of the repository (e.g., "https://github.com/user/repo").
Used to generate hyperlinks for commit hashes.
-v, --version <version>
Title version string for the header (default: "Unreleased").
--exclude-authors <list>
Comma-separated list of commit authors to ignore.
Example: --exclude-authors "dependabot\[bot\]"
--exclude-types <list>
Comma-separated list of commit types to ignore.
Example: --exclude-types "chore,docs,test"
--exclude-scopes <list>
Comma-separated list of scopes to ignore.
Example: --exclude-scopes "internal,deps"
--exclude-invalid-commits
Omit commits that do not adhere to the Conventional Commits
specification (usually listed under "Other Changes").
-h, --help Display this help message and exit.
The script requires a range of commits.
from_ref: The specific Git SHA of the previous deployment/release.to_ref: The specific Git SHA of the current deployment/release.
If provided, the script appends the commit hash to this URL to create clickable links.
- Format:
[<short_hash>](<repo-url>/commit/<full_hash>) - Header Link: It also creates a "Compare" link in the release header.
Allows you to curate the notes to reduce noise.
--exclude-authors: Removes commits from specific Git authors (exact match).--exclude-types: Useful for hiding internal changes likeciortest.--exclude-scopes: Useful for hiding changes to specific sub-systems.--exclude-invalid-commits: Removes the "Other Changes" section entirely.
-
Basic usage between two specific commit hashes:
./release-notes.sh 5011dfa 3884114
-
Generate notes with hyperlinks for GitHub:
./release-notes.sh 5011dfa 3884114 --repo-url "https://github.com/my/repo" -
Filter out bots and maintenance commits:
./release-notes.sh 5011dfa 3884114 \ --version "1.1.0" \ --exclude-authors "dependabot\[bot\]" \ --exclude-types "chore,ci,docs"
-
Strict mode (Only Conventional Commits) between a hash and HEAD:
./release-notes.sh 5011dfa HEAD --exclude-invalid-commits
Here is the How It Works section, formatted to match the style of the rest of the documentation.
The script reads the Git log between the specified references (<from_ref> to <to_ref>). It utilizes a strict regex
pattern to parse the header of every commit to identify components of
the Conventional Commits specification:
type(scope)!: subject
- Type: Determines the categorization (e.g.,
featmaps to "✨ New Features"). - Scope: Optional context (e.g.,
(api)). The script formats this in bold to make scanning easier. - Breaking Changes: The script scans both the header and the footer for breaking indicators. A commit is flagged as
breaking if:
- The header contains a
!(e.g.,feat!: change api). - The body or footer contains the text
BREAKING CHANGE:orBREAKING CHANGES:.
- The header contains a
Note: If a commit is identified as a Breaking Change, it is promoted to the ⚠ BREAKING CHANGES section at the very top of the release notes, regardless of its original type, to ensure visibility.
To ensure the most critical information is read first, sections are rendered in a specific priority order based on user impact rather than alphabetical order:
- ⚠ BREAKING CHANGES (Critical for upgrades)
- ✨ New Features (Value add)
- 🐛 Bug Fixes (Stability)
- ⚡ Performance (Improvements)
- ⏪ Reverts (Rollbacks)
- 🔨 Refactors (Code structure)
- 💄 Styling (Formatting)
- 📚 Documentation (Docs)
- 🧪 Tests / 🏗️ Build / 🤖 CI / 🧹 Maintenance
- 🔹 Other Changes (Non-conventional commits, unless excluded)
Here is the CI/CD Integration section, adapted for release-notes.sh while maintaining the exact structure and
professional tone of your reference text.
We provide comprehensive guides and reusable workflows to integrate release-notes.sh into your CI/CD pipelines on both GitHub and GitLab. These guides cover the automation of your release documentation.
For detailed instructions and complete workflow examples, please see the platform-specific guides: