Release Go modules automatically
Posted on 02 November 2020 - 2 min read
If you happen to have some awesome Golang codes, you probably want to share it with others. Once sharing it, you will need to turn it into a Go module and to version it. Therefore it can be managed easier and to be more friendly to users.
Well, releasing a Go module isn't so difficult but neither is it straightforward. Moreover, you're an engineer so you want to automate everything including this manual work.
Below is my experience when trying to automate the process of releasing Go modules via semantic-release
and Github Actions. It wasn't so smooth but life is much easier after that.
Prerequisites
Commit message format
Our commit messages format must follow a convention that is understood by semantic-release
. By default, semantic-release
uses Angular Commit Message Conventions but it can be changed by the configuration.
Here is an example of the release type that will be done based on commit messages:
Commit message | Release type |
---|---|
fix(pencil): stop graphite breaking when too much pressure applied | Patch Release |
feat(pencil): add 'graphiteWidth' option | Minor (Feature) Release |
perf(pencil): remove graphiteWidth option BREAKING CHANGE: The graphiteWidth option has been removed. The default graphite width of 10mm is always used for performance reasons. | Major (Breaking) Release |
Automated tests
We all want our modules to be released with its best in quality. The only way to achieve that is to take testing seriously. Since releasing is done automatically, so be testing. Therefore, I would assume the CI is already implemented with proper automated testing. And we will only trigger the release step if all test cases are passed.
Setup semantic-release
Configuration
For Node modules, we can straightly use the default configuration. For Go modules, it requires some modifications as Golang doesn't use NPM repository. Thus, we will need to add .releaserc.json
, which is semantic-release
's configure file, to the root folder in our repository:
{
"branches": [
"main",
{
"name": "beta",
"prerelease": true
}
],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/github"
]
}
There are a few things I would like to highlight here:
@semantic-release/npm
is removed from the defaultplugins
config because we don't need to publish our Go module to NPM repo. It's obvious, right?main
is used instead ofmaster
which is the release branch by default. For those who may not know, Github recently renames the default branch frommaster
tomain
. Reference.beta
is used as a pre-release branch when we're in a heavy development phase with frequent breaking changes. To learn more about release workflow, you can look intosemantic-release
wiki. I'm really impressed by how well it's documented.
For generating changelog, we will need to include two more plugins:
@semantic-release/changlog
: to generate the changelog file. By default, it'sCHANGELOG.md
.@semantic-release/git
: to commit the generated changelog file to the repository.
I don't see CHANGELOG.md
scale well and we already have git history so not including these steps makes things easier.
Github Actions
Then, we add a new job in Github Actions workflow for releasing. It should look simple like this:
jobs:
test:
# an amazing test configs
release:
name: Release
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release
needs: test
means the release job is only executed if thetest
job is successful.npx semantic-release
is the command to executesemantic-release
.GITHUB_TOKEN
will be needed for tagging.semantic-release
is able to detect if the job is triggered by pull_request and ignore it. Therefore, we won't need to worry about skipping the job from pull requests.
Pre-releases
Sometimes, the module is in a heavy development and it's expected to have multiple breaking changes. In this situation, a pre-release version like v1.0.0-beta.12 is needed before a stable one. semantic-release
supports this pretty well. All we need to do is:
- Create a
beta
branch and commit your changes here. Relevant commits in this branch will triggersemantic-release
to create a new pre-release. - Once the module becomes stable, we merge it to the
main
branch. The merge should triggersemantic-release
to create a new stable version.
Summary
semantic-release
is a handy library. It can free your hands from release modules in a timely manner. One downside is that a buggy module can be released if automated tests are not properly implemented.
semantic-release
is not the only package out there to automate the process. Another alternative written in Golang is go-semantic-release
. go-semantic-release
doesn't have as many plugins as semantic-release
but it works better with Go module. Especially, it allows starting versioning with 0.x.x
which is a Go's convention for pre-releases.
References
- https://github.com/semantic-release/semantic-release/blob/master/docs/recipes/pre-releases.md
- https://dev.to/zaracooper/18-essential-go-module-tidbits-for-a-newbie-4455
- Sample Repo: https://github.com/bongnv/inject
- https://github.com/semantic-release/semantic-release/blob/master/docs/recipes/github-actions.md
Tagged with: golang, semantic-release
Previous: Create a blazing fast blog via NextJS