Actually you don’t need 'semantic-release' for semantic release
Anton Golub
Posted on May 23, 2021
I’m a big fan of semantic-release since it appeared. I followed its development, studied its inners. I made in-house reports, held workshops and finally brought semrel to our build infrastructure. I wrote plugins, plugin-factories and testing-tools for it. For several years now, I've been trying to combine semantic releases and monorepositories in many OSS projects:
Etc, etc, so on. I’m just trying to say, that semrel had a significant impact on my professional life.
Semrel goal
The main purpose of semantic-release is to transform semantic (conventional) commits into build artifacts and deployments. With version bumping, changelogs, tagging, pkg publishing. “Fully-automated release” — is the true. There are also dozens on plugins, so you’ll most likely find a solution for any standard case. It really saves times.
Of course, you may fork semrel and remove the mentioned line. Or create a plugin/hook, that will override loaded execa module with patched one version, than just skips git notes invocation (this is really frustrating, I did smth similar). Or… {{ another crazy workaround goes here }}.
This is a watershed moment. Once you start to fight against the tool, it's time to just pick another one. The new dilemma:
Spend days and days for searching, tuning and testing analogs.
Write your own semantic-release.
My opinionated suggestion: if your case is very simple or, conversely, very complex, the second option will be optimal. Release script — is not a rocket science!
140 lines alternative
Let's take a look at what exactly each release consists of, if we discard the high-level tool contracts. I use zx in the examples, but it could be execa or native child_process.exec too.
1. Git configuration
To make a commit you need a committer: just name and email that will be associated with author. Also PAT or SSH token is required to push the commit.
Conventional commits are just a prefixed strings in git log. We should define some rules on how to associate messages substrings with corresponding release types:
semanticChanges=[{group:'Fixes & improvements',releaseType:'patch',change:'perf: use git for tags sorting',subj:'perf: use git for tags sorting',body:'',short:'a1abdae',hash:'a1abdaea801824d0392e69f9182daf4d5f4b97db'},{group:'Fixes & improvements',releaseType:'patch',change:'refactor: minor simplifications',subj:'refactor: minor simplifications',body:'',short:'be847a2',hash:'be847a26e2b0583e889403ec00db45f9f9555e30'},{group:'Fixes & improvements',releaseType:'patch',change:'fix: fix commit url template',subj:'fix: fix commit url template',body:'',short:'3669edd',hash:'3669edd7eb440e29dc0fcf493c76fbfc04271023'}]
await$`npm --no-git-tag-version version ${nextVersion}`
7. Git release.
Create commit. Create tag. Push them.
constreleaseMessage=`chore(release): ${nextVersion} [skip ci]`await$`git add -A .`await$`git commit -am ${releaseMessage}`await$`git tag -a ${nextTag} HEAD -m ${releaseMessage}`await$`git push --follow-tags origin HEAD:refs/heads/master`
await$`npm config set registry https://registry.npmjs.org`await$`npm publish --no-git-tag-version`await$`echo "\`jq '.name="@${repoName}"' package.json\`" > package.json`await$`npm config set registry https://npm.pkg.github.com`await$`npm publish --no-git-tag-version`
Conclusions
This solution does not cover corner cases and has significant limitations of usage. Ultimately, you don't care if other tools have 99.99999% applicability until they ignore just one specific case — yours. But now you have completely taken back release flow control. You're able to improve and modify this snippet as you wish and whenever you like.
Sometimes bloody enterprise enforces you not to use any third-party solutions for sensitive operations (like release, deploy, so on)
Old good script copy-paste hurries to the rescue!
# Just replace GIT* env values with your own
GIT_COMMITTER_NAME=antongolub GIT_COMMITER_EMAIL=mailbox@antongolub.ru GITHUB_TOKEN=token npx zx ./release.mjs