EasyBuild.ShipIt
1.0.0
dotnet tool install --global EasyBuild.ShipIt --version 1.0.0
dotnet new tool-manifest
dotnet tool install --local EasyBuild.ShipIt --version 1.0.0
#tool dotnet:?package=EasyBuild.ShipIt&version=1.0.0
nuke :add-package EasyBuild.ShipIt --version 1.0.0
EasyBuild.ShipIt
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.
Features
- Easy integration into your CI/CD pipeline 🛠
- Automatic versioning based on commit messages 🚀
- Can create/update pull request for automatic releases 🔧
- Support for monorepo 🔥
How does it work?
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"?
Usage
# Install the tool
dotnet tool install EasyBuild.ShipIt
# Run the tool
dotnet shipit
CLI manual
DESCRIPTION:
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
How is the version calculated?
Stable versions
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.0featcommits will bump the minor version* chore: release 1.2.10 * feat: first feature * feat: second feature # => 1.3.0perfcommits will bump the minor version* chore: release 1.2.10 * perf: first performance improvement * perf: second performance improvement # => 1.3.0fixcommits 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
Pre-release versions
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.1If 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.11If 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
Moving out of pre-release
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
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.
Overriding the computed version
If the computed version is not what you want, you can use force_version configuration to override the computed version for a specific release.
Commit conventions
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
Extra changelog content
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 ===
Recommendations
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 }}
CLI options
--remote-hostname, --remote-owner, --remote-repo
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.
--allow-branch
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.
--skip-invalid-commit
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.
--skip-merge-commit
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.
--pre-release
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.
--mode
type: string
default: pull-request
Control the mode in which the tool operates.
local: Only generate the changelog file locallypull-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.
Configuration
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.
...
last_commit_released
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.
include
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/
It always include files in the same directory as the changelog file, so you don't need to include it in the configuration.
exclude
type: string[]
Allows to exclude commits from specific paths.
exclude:
- tests/
pre_release
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: beta
priority
type: 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: 1
name
type: 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.
force_version
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.
This is not persisted in the generated changelog file, it is only used for the current run.
force_version: 2.0.0
updaters
type: 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.
regex
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. |
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>)
package.json
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.json
json
type: 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/version
xml
type: 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/Version
command
type: 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}"
Monorepo support
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.
Recipes
Prod / staging environments
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.
GitHub Actions (auto release)
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.
Requirements
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 run --project src/ -c SHIPIT_EXCEPTION
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 }}
Why the name "ShipIt"?
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.
Exit codes
0: Success1: Error100: Help was requested
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
### 🏗️ Breaking changes
* Make PR title decision based on the presence of multiple CHANGELOG.md and not just the ones updated (#5) ([2c99db5](https://github.com/easybuild-org/EasyBuild.ShipIt/commit/2c99db550a7d742c10c92e5f27fcade930ea23a6))
<strong><small>[View changes on Github](https://github.com/easybuild-org/EasyBuild.ShipIt/compare/82fe7dd954f97861e55f8f3b328f89123b947dc3..2c99db550a7d742c10c92e5f27fcade930ea23a6)</small></strong>