This post describes how you can use both semantic-release
and Github Actions to package and attach to a release a new version of your product — automatically and based on your commit messages. For the purpose of this demo, we are packaging a very simple zip
file with a single txt
file inside, but hopefully you get the idea.
These are the concepts I will demonstrate in the demo:
- Running your tests in CI
- Determining whether CI needs to package a new release
- Setting up the correct repo permissions
- Using a build script to create your new package using
version
andrelease notes
fromsemantic-release
- Generating a new
Release
with your package attached - Committing any changed files during packaging back into the repo
I've also create a demo project for the purpose of this post: https://github.com/whomwah/demo-semantic-release-gh-actions
During the post we'll take a look at the various files in this repo and talk about what they do, and how you could change them to suit your project.
Running your tests in CI
This first concept is something you probably do on all your projects (you have tests right?) and that is run your tests suite. In the context of semantic-release
it doesn't matter what tech you are using here. Just because semantic-release
is a JavaScript project, it doesn't mean you're limited to JavaScript projects.
So in the case of this demo we just have a very simple mod.ts
file and a supporting mod.test.ts
which we want our CI to run and pass before we build anything. We also a have a .github/workflows/tests.yml
action file. This will simply run our tests for this project in Github. We are using Deno
to run our tests as it's not only a great tool, but is a very easy way to run specs on TypeScript files without having to pull in a build system.
Determining whether CI needs to package a new release
Once your tests have run, we then need to know whether we should start a new release. If we're just running the tests in a PR branch for example we don't. This is where our .github/workflows/package.yml
file kicks in.
...
on:
workflow_run:
workflows: [Tests]
types:
- completed
release:
if: ${{ github.ref_name == 'main' && github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
steps:
...
Firstly you can see the workflow_run
section. Here we are referencing our Tests
workflow which runs the tests. This is basically saying "run this workflow once the Tests
workflow has completed". The next part is making sure that we only run the steps in the workflow if we are currently on the main
branch and also that the conclusion of the previous workflow was a success
, ie: the tests passed.
Setting up the correct repo permissions
Before we get any further into the package
workflow, we need to first make sure our repo has the correct permission in order to be able to run the workflow completely. At the start of each workflow run, GitHub automatically creates a unique GITHUB_TOKEN
secret to use in your workflow. You can use the GITHUB_TOKEN
to authenticate in a workflow run. This is great but unfortunately the token does not have enough permissions to be able to create a new commit, which we will need to do in our demo because we are changing the VERSION
file in the root. We want to commit this updated file as part of the release process. In order have enough permission for this we need to create a new ENV to use.
Firstly we visit https://github.com/settings/tokens and create a new access token. When creating the token we need to make sure that all the scopes in the repo
section are ticked. Once we have created the token, make sure it is visible to copy as you will need it for the next bit. Now visit https://github.com/<your_repo>/settings/secrets/actions
where you will create a new secret. We'll call this secret GH_AUTH_TOKEN
and the value will be the access token you created before that. The workflow will now be able to access this value via secrets.GH_AUTH_TOKEN
.
Using a build script to create your new package
In this demo, we are using a build script to create our final release asset. We do a couple of interesting things for the purpose of the demo which you will find in the comments of the build file. The important thing is though that we are passing the build script the version
of the next deployable version and also any release notes
that were gleaned from the commits that triggered that new release.
Generating a new Release
with your package attached
Now we have created our new release package, we want to trigger semantic-release
to do it's stuff. Let's go back to our .github/workflows/package.yml
file.
...
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
token: ${{ secrets.GH_AUTH_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 16
...
This first section installs the action dependencies we need. Ie: We need to install NodeJS
in order to be able to install semantic-release
and run it.
The next section does all the magic.
...
- name: Setup package.json
run: echo '{"name":"demo", "devDependencies":{"@semantic-release/git":"^10.0.1","@semantic-release/exec":"^6.0.3","semantic-release":"^19.0.5"}}' > package.json
- name: Install dependencies
run: npm install
- name: Release
run: npx semantic-release
env:
GH_TOKEN: ${{ secrets.GH_AUTH_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
...
Firstly, we generate a package.json
file inline for the project. It contains all the dependencies we need in order to run semantic-release
for this demo. We use @semantic-release/git
and @semantic-release/exec
to both commit our changes back to our repo and also to run our build
script. You can then see that npm install
is then run which will install all these dependencies. The final step than actually runs semantic-release
which will generate the new release for us if required by our commit messages.
So let's go through the .releaserc
file and explain what's going on. It's this file that will be picked up when npx semantic-release
is called.
This first section basically says only run this if we are on the main
branch. We do this check in our Github Action too so this is just a double check.
{
"branches": [
"main"
],
....
}
This next section uses the built in commit-analyzer
and release-notes-generator
plugins which are the ones that check your commit messages and see if any of them mean a new release should be created. The final section that calls @semantic-release/exec
is the important one. It's this command that calls our build
script and passed it the new version
we're building and any release notes
we have.
{
...
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/exec",
{
"prepareCmd": "./bin/build_release ${nextRelease.version} \"${nextRelease.notes}\""
}
],
]
...
}
Next we attach our built package to the new release we are creating by pointing the file/s we want to attach. See we can use the nextRelease.version
to argument the text.
...
[
"@semantic-release/github",
{
"assets": [
{
"path": "releases/*.zip",
"label": "Our Demo Package (${nextRelease.version})"
}
]
}
],
...
Committing any changed files during packaging back into the repo
During our build step, you know that we made a change the to the local VERSION
file in our repo? That change was simply to show how you can make change to files in your repo as part of the package workflow. This final step in process will actually commit that change back into the repo.
...
[
"@semantic-release/git",
{
"assets": [
"VERSION"
],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
...
You can see an example of that commit in the demo project https://github.com/whomwah/demo-semantic-release-gh-actions/commit/19bde86d89c871ef4e08d8f882ba757d84f174aa.
Finished
So that's a run through of building and packaging new release assets. If you look in the releases
section for the demo repo you can see all the releases created and how the zip
file we packaged up is attached.
I hope you find this useful. Here are some useful links to support the post:
- https://github.com/semantic-release/
- https://semantic-release.gitbook.io
- https://semantic-release.gitbook.io/semantic-release/extending/plugins-list
- https://docs.github.com/en/actions
- https://github.com/whomwah/demo-semantic-release-gh-actions
We are Kyan, a technology agency powered by people.
Previously from Duncan:
Using Swift/SwiftUI to build a modern macOS Menu Bar app
A curious case of the QR code, Christmas Day, and 20 million downloads
Building our own office Jukebox using Mopidy, NodeJS and React