This post aims to guide you through the process of automating release and publishing of a package stored on GitHub. We are going to use release-it tool that strikes a good balance between features and simplicity of configuration.
Table of contents
Open Table of contents
The “Why”
There are several steps required to be performed every time we want to release a new version of our package. They usually consist of:
- Bumping version inside
package.json
- Creating a release on GitHub, pushing a new tag and adding release notes
- Publishing the package to npm
- Updating changelog if it’s stored in a separate file (
CHANGELOG.md
)
Every manual process comes with a cost of time and possible human errors, e.g.
- Forgetting to bump version or setting a wrong version (e.g.
v1.1.0
instead ofv1.0.1
) - Forgetting to update changelog
- Forgetting to push a new tag
- Misalignment between tag and
package.json
version
Fortunately, there are tools that can help us with fully automating this whole process so we can focus on important parts (like adding shiny new features to our library or fixing bugs users are complaining about).
The “How”
How all of this is going to work (or at least is supposed to):
- When PR is created, it’s title needs to align with Conventional Changelog, after the PR is merged, it’s commit name will be used to determine release process:
- The most commonly used commit labels include
fix
,feat
,docs
andchore
fix
andfeat
trigger a new release (fix
is treated as a patch release andfeat
is treated as a minor release)docs
andchore
do not change the behavior for end-user in any way so they won’t trigger a new release- Adding
BREAKING CHANGE:
into commit’s body will trigger a release with breaking version change (vX.0.0
)
- The most commonly used commit labels include
- Once the PR is merged, a new github release is created and published to npm
- A new version is automatically generated based on Semantic Versioning
- A new commit is pushed to the main branch that updates
package.json
andCHANGELOG.md
files
Here are the steps required to achieve this:
1. Adding required credentials
In order to perform release and publish process we will need a couple tokens - for Github and npm.
After you generate npm token, add it to repository secrets as NPM_TOKEN
here.
Github token get’s created automatically and should work out of the box. If it doesn’t, create one yourself.
2. Installing packages and adding npm scripts
We will also add should-semantic-release package that will block certain commits from triggering new releases (e.g. Renovate/Dependabot ones), we don’t want to release a new version every time a dependency is updated.
npm install -D release-it @release-it/conventional-changelog should-semantic-release
yarn add -D release-it @release-it/conventional-changelog should-semantic-release
pnpm add -D release-it @release-it/conventional-changelog should-semantic-release
/* file: package.json */
"scripts": {
"release": "release-it",
"should-semantic-release": "should-semantic-release --verbose",
}
3. Setting up release-it configuration
Don’t hesitate to modify below config and adjust it to your needs, there are a lot of possible options to choose from.
/* file: .release-it.json */
{
"git": {
"requireBranch": "main", // Your main branch name
"requireCommits": true,
"commitMessage": "chore: release v${version}"
},
"github": {
"autoGenerate": true,
"release": true,
"releaseName": "v${version}"
},
"npm": {
"publish": true,
"publishArgs": ["--provenance"] // https://docs.npmjs.com/generating-provenance-statements
},
"plugins": {
"@release-it/conventional-changelog": {
"preset": {
"name": "conventionalcommits"
},
"infile": "CHANGELOG.md",
"header": "# Changelog"
}
}
}
4. Adding github workflows
-
Validate non-code contents of a PR (title, body, origin branch).
# file: .github/workflows/compliance.yml name: Compliance on: pull_request: branches: - main types: - edited - opened - reopened - synchronize permissions: pull-requests: write jobs: compliance: runs-on: ubuntu-latest steps: - uses: mtfoley/pr-compliance-action@main with: body-auto-close: false ignore-authors: |- allcontributors allcontributors[bot] renovate renovate[bot] dependabot dependabot[bot] ignore-team-members: false issue-labels-auto-close: false
-
Add a comment inside merged PR when it gets included into a new release.
# file: .github/workflows/post-release.yml name: Post Release on: release: types: - published jobs: post_release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - run: echo "npm_version=$(npm pkg get version | tr -d '"')" >> "$GITHUB_ENV" - uses: apexskier/github-release-commenter@v1 with: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} comment-template: | :tada: This is included in version {release_link} :tada: The release is available on: * [GitHub releases](https://github.com/JoshuaKGoldberg/create-typescript-app/releases/tag/{release_tag}) * [npm package (@latest dist-tag)](https://www.npmjs.com/package/create-typescript-app/v/${{ env.npm_version }})
-
Execute
release-it-action
to perform release and publish process if newly merged commit passesshould-semantic-release
check. The action takes care of saving branch protection settings, disabling them temporarily for the release commit (version and changelog update) and restoring them afterwards.# file: .github/workflows/release.yml name: Release on: push: branches: - main permissions: contents: write id-token: write concurrency: cancel-in-progress: true group: ${{ github.workflow }} jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 ref: master - uses: actions/setup-node@v3 with: node-version-file: package.json - name: Install dependencies run: npm ci - name: Release uses: JoshuaKGoldberg/[email protected] env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # use below in case there are 403 errors with npm authentication # with: # npm-token: ${{ secrets.NPM_TOKEN }}
Once all above workflows complete successfully, you should be able to see a new release on GitHub’s releases page.
A release commit should be added to the main branch.
New version should be visible in changelog.
New version should be published in npm.
A comment will be visible in merged PR.
Working example can be found in this repository.
Credits
- Joshua Goldberg for his awesome work on JS/TS ecosystem tooling, check out his create-typescript-app repo that was an inspiration for this blog post.