Focus on writing code, not on writing changelog! 🚀
Tool for generating changelog based on Git history based on Conventional Commits. It is using EasyBuild.CommitParser to parse commit messages check their documentation for more information about configuration.
- Easy integration into your CI/CD pipeline 🛠
- Automatic versioning based on commit messages 🚀
- Can create/update pull request for automatic releases 🔧
- Support for monorepo 🔥
EasyBuild.ShipIt search for any CHANGELOG.md, for each of them look at the commits since the last
released commit (based on the last_commit_released configuration) and generate a new changelog entry based on the commit messages.
Then, if the --skip-pull-request option is not passed, it will create a pull request with the updated changelog file.
Learn more about:
- How is the version calculated?
- Commit conventions
- CLI options
- Configuration
- Monorepo support
- Recipes
- Why the name "ShipIt"?
# Install the tool
dotnet tool install EasyBuild.ShipIt
# Run the tool
dotnet shipitDESCRIPTION:
Automate changelog generation based on conventional commit messages and create pull requests for releases.
The tool will do its best to automatically detect the Git provider based on the git remote URL.
You can force it to use a specific provider using sub-commands, e.g. 'shipit github' to force using GitHub.
Learn more at https://github.com/easybuild-org/EasyBuild.ShipIt
USAGE:
shipit [OPTIONS] [COMMAND]
OPTIONS:
DEFAULT
-h, --help Prints help information
--allow-branch <VALUES> main List of branches that are allowed to be used to generate the changelog
--mode <MODE> pull-request Mode of operation. Possible values are 'local', 'pull-request' and 'push'
--pre-release [PREFIX] beta Indicate that the generated version is a pre-release version. Optionally, you can provide a prefix for the beta version. Default is 'beta'
--remote-hostname <HOSTNAME> Git remote hostname, e.g. github.com, gitlab.com
--remote-owner <OWNER> Git remote owner or organization name
--remote-repo <REPO> Git remote repository name
--skip-invalid-commit False Skip invalid commits instead of failing
--skip-merge-commit False Skip merge commits when generating the changelog (commit messages starting with 'Merge ')
-v, --version Show version information
COMMANDS:
version
conventions List supported Conventional Commit types
github Publish to GitHub
The version is calculated based on the commit messages since last released, who are contributing to the changelog file (based on the include and exclude configuration).
Rules are the following:
-
A
breaking changecommit will bump the major version* chore: release 1.2.10 * feat!: first feature # => 2.0.0 -
featcommits will bump the minor version* chore: release 1.2.10 * feat: first feature * feat: second feature # => 1.3.0 -
perfcommits will bump the minor version* chore: release 1.2.10 * perf: first performance improvement * perf: second performance improvement # => 1.3.0 -
fixcommits will bump the patch version* chore: release 1.2.10 * fix: first fix * fix: second fix # => 1.2.11
You can mix different types of commits, the highest version will be used (breaking change > feat or perf > fix).
* chore: release 1.2.10
* feat: first feature
* perf: first performance improvement
* fix: first fix # => 1.3.0
A pre-release will be generated if you set pre_release configuration or if you pass --pre-release CLI option.
Rules are the following:
-
If the previous version is stable, then we compute the standard version bump and start a new pre-release version.
* chore: release 1.2.10 * feat: first feature * fix: first fix # => 1.3.0-beta.1 -
If the previous version is a pre-release, with the same suffix, then we increment the pre-release version.
* chore: release 1.3.0-beta.10 * feat: first feature * fix: first fix # => 1.3.0-beta.11 -
If the previous version is a pre-release, with a different suffix, then we use the same base version and start a new pre-release version.
* chore: release 1.3.0-alpha.10 * feat: first feature * fix: first fix # => 1.3.0-beta.1
💡 Tips
EasyBuild.Changelog use the last version in the changelog file to compute the next version.
For this reason, while working on a pre-release, it is advised to work in a separate branch from the main branch. This allows you to work on the pre-release while still being able to release new versions on the main branch.
* chore: release 1.2.10
| \
| * feat!: remove `foo` API
| * feat: add `bar` API # => 2.0.0.beta.1
| * fix: fix `baz` API
* fix: fix `qux` API
* chore: release 1.2.11
| * fix: fix `qux` API # => 2.0.0.beta.2
| /
* chore: release 2.0.0 # => 2.0.0
If you want to move out of pre-release, you need to remove the pre_release configuration or stop passing the --pre-release CLI option.
Then the next version will be released using the base version of the previous pre-release.
* chore: release 1.3.0-beta.10
* feat: first feature
* fix: first fix # => 1.3.0
Tip
If you are not sure what will be calculated, you can use the --mode local option to see the result without creating a pull request or committing any changes.
You can then reset the changelog using git restore path/to/CHANGELOG.md before re-running the command.
If the computed version is not what you want, you can use force_version configuration to override the computed version for a specific release.
EasyBuild.Shipit follows the Conventional Commits specification.
<type>[optional scope][optional !]: <description>
[optional body]
[optional footer]
-
[optional body]is a free-form text.This is a single line body.This is a multi-line body. -
[optional footer]is inspired by git trailer formatkey: valuebut also allowskey #valueBREAKING CHANGE: <description> Signed-off-by: Alice <alice@example.com> Signed-off-by: Bob <bob@example.com> Refs #123 Tag: cli
The following commit types are supported:
- feat : A new feature
- fix : A bug fix
- ci : Changes to CI/CD configuration
- chore : Changes to the build process or auxiliary tools and libraries such as documentation generation
- docs : Documentation changes
- test : Adding missing tests or correcting existing tests
- style : Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
- refactor : A code change that neither fixes a bug nor adds a feature
- perf : A code change that improves performance
- revert : Reverts a previous commit
- build : Changes that affect the build system or external dependencies
You can define additional content to be added in your changelog entry by surrounding it with === changelog === markers in your commit message body.
feat: add `String.split` API
=== changelog ===
```fs
let parts = String.split " " "Hello World"
```
=== changelog ===Because EasyBuild.ShipIt relies on your commit messages, it is recommended to use Squash and Merge or Rebase and Merge when merging pull requests to keep a clean commit history.
This avoid the creation of invalid commits like Merge pull request ....
To help enforce this convention, you can go to your Org/Repo GitHub settings:
- Disable the
Allow merge commitsoption in theGeneral > Pull Requestssection. - Enable
Allow squash mergingoption and choosePull request titlein the dropdown.
Additionally, you can configure this GitHub Actions to validate PRs titles.
# conventional-pr-title.yml
name: 'Lint PR'
on:
pull_request_target:
types:
- opened
- reopened
- edited
# - synchronize (if you use required Actions)
jobs:
main:
name: Validate PR title
runs-on: ubuntu-slim
permissions:
pull-requests: read
steps:
- uses: amannn/action-semantic-pull-request@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}These options allow you to specify the Git remote information. This is useful when the tool is not able to automatically detect the Git provider based on the git remote URL.
This information is used to create links to commits, diff, etc. in the generated changelog file.
type: string[]
default: main
Restrict the branches that can be used to generate the changelog.
This is a safety measure to avoid generating changelog from a branch that is not intended to be released, e.g. a feature branch.
type: bool default: false
When this option is passed, the tool will skip any commit that is not following the Conventional Commits format instead of failing.
type: bool default: false
When this option is passed, the tool will skip any merge commit (commit messages starting with 'Merge ') when generating the changelog.
type: string
When this option is passed, the generated version will be a pre-release version.
Optionsally, you can provide a prefix for the pre-release version. If no prefix is provided, it will default to beta.
type: string
default: pull-request
Control the mode in which the tool operates.
-
local: Only generate the changelog file locally -
pull-request: Create a pull request with the updated changelog file (default) -
push: Push the updated changelog file directly to the current branch🚨 IMPORTANT: Make sure to configure
--allow-branchoption to avoid pushing changes to unintended branches.
EasyBuild.ShipIt configuration lives as a front matter in your CHANGELOG.md file(s).
---
last_commit_released: abcd1234
include:
- ../Shared/
---
# Changelog
All notable changes to this project will be documented in this file.
...
type: string
This is the commit hash of the last released commit. It is used to determine which commits should be considered for the next release.
You should set it up manually only if you are adopting EasyBuild.ShipIt in an existing project. If you are starting a new project, you can leave it empty and it will be automatically set to the latest commit hash when you run the tool for the first time.
type: string[]
Allows to include commits from other paths. This is useful for monorepo where you have multiple projects in the same repository.
include:
- ../Shared/
- ../Lib/Note
It always include files in the same directory as the changelog file, so you don't need to include it in the configuration.
type: string[]
Allows to exclude commits from specific paths.
exclude:
- tests/type: string
When set to a non-empty value, the generated version will be a pre-release version with the provided value as prefix.
pre_release: betatype: int
Lowest number has the highest priority. This is useful to determine which changelog file should be updated first when generating a new release.
If a changelog has no priority, it will be considered as having the lowest priority (i.e. it will be updated last).
priority: 1type: string
Allows to override the name of the project used when reporting in PR.
By default, the project is named after the parent directory of the changelog file.
type: string
Allows to force the version to be used in the changelog. This is useful when you want to override the calculated version for a specific release.
Important
This is not persisted in the generated changelog file, it is only used for the current run.
force_version: 2.0.0type: Updater[]
List of updaters to run after generating the changelog. This allows you to automatically update other files in your repository, e.g. package.json or AssemblyInfo.cs, with the new version.
type: object
Use a regex pattern to find the text to replace with the new version.
| Property | Description |
|---|---|
file |
Relative path to the file to update. It is relative to the changelog file. |
pattern |
Pattern used to find the text to replace. |
Important
The regex with replace the full match with the new version. Make sure to use a regex that only matches the version part of the file.
For example, if you want to update:
<Metadata>
<Version>1.0.0</Version>
</Metadata>Your regex should be (?<=<Version>).*(?=</Version>) to only replace the version part and not the full match.
updaters:
- regex:
file: Metadata.xml
pattern: (?<=<Version>).*(?=</Version>)type: object
Update the version field in a package.json file.
| Property | Description |
|---|---|
file |
Relative path to the package.json file to update. It is relative to the changelog file. |
updaters:
- package.json:
file: path/to/package.jsontype: object
Update a JSON file using a JSON Patch. It uses the JSON Patch format to specify the changes to be made to the JSON file.
| Property | Description |
|---|---|
file |
Relative path to the JSON file to update. It is relative to the changelog file. |
pointer |
JSON pointer to the field to update. |
updaters:
- json:
file: path/to/file.json
pointer: /metadata/versiontype: object
Update an XML file using an XPath expression to find the node to update.
| Property | Description |
|---|---|
file |
Relative path to the XML file to update. It is relative to the changelog file. |
selector |
XPath expression to find the node to update. |
updaters:
- xml:
file: path/to/file.xml
selector: /Metadata/Versiontype: string
Run an arbitrary command to update the version. The command will receive the new version as an argument in place of {version}.
updaters:
- command: "./update-version.sh {version}"EasyBuild.ShipIt supports monorepo.
For example, if you have the following repository structure:
repo/
├── project-a/
│ ├── CHANGELOG.md
│ └── ...
├── project-b/
│ ├── CHANGELOG.md
│ └── ...
└── Shared/
└── ...
It means that 2 projects will be released, project-a and project-b. By default, only the commits that are in the same directory as the changelog file will be considered for the release of each project.
If you want to include commits from the Shared directory for both projects, you can use the include configuration to include the Shared directory for both changelog files.
Learn more about the include configuration in the Configuration section.
When working with production and staging environments, you can use the pre_release configuration to generate pre-release versions for the staging environment and stable versions for the production environment.
To do that, you need use --pre-release CLI option in your staging environment and not use it in your production environment.
We want to use the CLI option here instead of the configuration as it allows to easily switch between pre-release and stable versions without having to change the configuration file.
EasyBuild.ShipIt has been designed to make it easy to integrate into your CI/CD pipeline, and in particular with GitHub Actions.
Below is an example of how to use it, so it update the CHANGELOG.md file in a pull request. Once the pull request is merged, it will trigger a workflow to publish the packages.
-
Go to GitHub settings of your Org or Repo, and enable
Actions > General > Allow GitHub Actions to create and approve pull requests.If you prefers, you can also create a Personal Access Token (PAT) and use it instead of
secrets.GITHUB_TOKEN. -
Create a
.github/workflows/easybuild-shipit.ymlfile with the following content:
on:
push:
branches:
- main
permissions:
contents: write
pull-requests: write
id-token: write
name: EasyBuild ShipIt
jobs:
# Run tests and generate a Pull Request to prepare a release
shipit-pr:
name: ShipIt - Pull Request
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
# We need to fetch more than the last commit to be able to generate the changelog based on the commit history.
# Adapt this value based on your needs.
fetch-depth: 50
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
global-json-file: global.json
# Run your tests before generating the changelog to make sure everything is working fine before creating a pull request.
- name: Build and test
run: echo "Run your build and tests here"
- name: ShipIt (Pull Request)
run: dotnet shipit
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release:
name: Release
runs-on: ubuntu-latest
# Only trigger this workflow when a commit starting with `chore: release ` is pushed.
if: "startsWith(github.event.head_commit.message, 'chore: release ')"
steps:
- uses: actions/checkout@v6
# Configure how you want to publish your package, e.g. using the NuGet CLI, dotnet CLI, NPM, etc.
# It is recommanded to use Trusted Publishing to avoid having to manage API keys in your repository.
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
global-json-file: global.json
# Below is an example, for NuGet packages (to demonstrate that we are getting
# the API key from the OIDC token exchange and not from a secret in the repository).
- name: NuGet login (OIDC → temp API key)
uses: NuGet/login@v1
id: login
with:
# Secret is your NuGet username, e.g. andrewlock
# This is actually a public information, so we can using a secret is probably overkill
user: ${{ secrets.NUGET_USER }}
- name: Build, Test and Release
run: |
dotnet pack src/ -c Release -o ./nupkgs
dotnet nuget push ./nupkgs/*.nupkg --api-key $NUGET_KEY
env:
NUGET_KEY: ${{ steps.login.outputs.NUGET_API_KEY }}The name "ShipIt" is a reference to the famous phrase "Ship it!".
It is also to avoid future potential conflicts in case .NET team decide to add dotnet release in .NET CLI.
I felt like shipit was a fun and less risky in this regard.
0: Success1: Error100: Help was requested