Semantic release
I love semantic-release and use it for every one of my own NPM packages. I do not publish a new version to NPM from local box. Instead, the CI publishes a new version but only if the tests pass and there are changes to publish. CI determines if there are changes (and what the version bump should be) based on commit messages. There are several conventions, but I follow the simplest one from simple-commit-message.
- If there is a commit with
patch: something ...
orfix: something
- the 3rd numberz
in the version's triplex.y.z
should be incremented by one. - If a commit has form
feat: ....
orminor: ...
then the second number is incremented. - Finally, if the commit starts with
major: ...
orbreak: ...
then this is a major API change, thus the package getsx+1.y.z
bump - If there are multiple commits, the largest one wins.
Usually I push every 1-2 commits to the master, releasing new versions often. Thanks to gitub-post-release plugin, every published NPM version gets a corresponding GitHub release with nice release summary.
Any issue closed by the commits as part of the release also gets a comment to notify users that the new version that solves the issue has been published.
I love love love semantic-release
. I even created my own Yeoman generator
for Node projects that has yo node-bahmutov:release command. It
runs semantic-release-cli setup
and installs my own release plugins I often
need. Overall, I almost never think about the publishing mechanics, instead
I spend time thinking about what the roadmap should be,
what features to implement and in what order they should be released. The
NPM release is just shipping and handling - it just happens.
Yet I found myself limited by what semantic-release
can do. Partly because
the project itself has been dormant (hope we can reinvigorate it with
Gregor and Hutson),
but partly because it is really closely tied to TravisCI, GitHub and NPM
registry.
Semantic action
What I really want to do is to drive any action based on semantic analysis
of commit messages. Not everything we do is a new NPM module version; often
we need to deploy a static website or a server. To do this we need to make
semantic-release
a little more generic. So I forked it into
semantic-action to freely experiment and change - the
beauty of open source is that it freely allows everyone to play and
adapt other projects to your needs.
Any automation is a replacement for a manual step. So what do we do manually
that might benefit from semantic-action
? The simplest thing that I often
do is publishing a static Hexo blog, just like this one. See
this repo test-semantic-action for the source code
for the simplest case; it is deployed to GitHub static pages hosted at
https://glebbahmutov.com/test-semantic-deploy/.
You can see the deploy command in the .circleci/config.yml executed on CircleCI.
1 | - run: npm run semantic-deploy || true |
That is it - the CI command is super simple, because we already connected the CI to GitHub via SSH keys, so the new static content can be pushed. But if you look at the CircleCI log there is a lot going on, even discounting verbose logging I have turned on. The NPM script command has 3 parts:
1 | { |
Notice the "pre" and "post" scripts - they are controlled by semantic-action
and can be customized by using plugins.
Pre is the safety switch
The "pre" step looks at the environment and tries to decide if a semantic
action is necessary. Usually the "pre" step will halt if the
environment variable CI
is undefined - forcing us to only run this action
on the continuous integration server.
The original semantic-release
also looks for NPM_TOKEN
and
GH_TOKEN
environment variables, because it assumes we are going to publish
to NPM registry and upload the release notes on GitHub.
My semantic-action
removes this check, because we are not necessarily
publishing to NPM, and if there is such action, the action itself would check.
The most important thing the "pre" step does - it inspects the commits since the previous semantic action to decide if there are new public commits. For this, it needs two things:
Commit SHA when the semantic action ran last time.
semantic-release
used the version tag fetched from NPM registry to find it. In my case I extracted this to a plugin. For example, if you deploy commit SHA with your static site as a json file, you can fetch it during "pre" step. Just tell it in the "release" object of the package.json to use url-to-sha plugin and point at the deployed URL1
2
3
4
5
6
7
8
9{
"release": {
"getLastRelease": {
"path": "url-to-sha",
"url": "https://glebbahmutov.com/test-semantic-deploy/build.json",
"property": "id"
}
}
}I generate the
build.json
file during the deployment using git-last module and NPM script command1
2
3
4
5{
"scripts": {
"postgenerate": "git-last -f public/build.json && cat public/build.json"
}
}You can see the deployed version yourself
1
2
3
4
5
6
7$ curl https://glebbahmutov.com/test-semantic-deploy/build.json
{
"id": "60839bb21998cb30772c01b3a0f6efe17d1892aa",
"short": "60839bb",
"savedAt": "2017-08-04T05:57:38.675Z",
"version": "1.3.0"
}I find having such easy way to see what I have deployed that most of my servers have version-middleware installed.
List of commits since the last action commit SHA. This is pretty standard and just uses Git command.
Action
Between "pre" and "post" lies the real action. The command could be
anything - npm publish
, hexo deploy
,
immutable deploy using now, git push [email protected]
- any
command that you execute manually is fair game for automation.
Could be even a very simple tedious task - create a tag in the
repo to mark when specific feature or fix commit was pushed.
Post is for notifications
The "post" step is for running tasks after semantic action has happened. For example we might want to upload other artifacts to storage or to Docker Hub, or push a new tag, or send an email, or post a GIF to a Slack channel.
In the coming months I hope to have a flexible declarative way (all via a
JSON object in the package.json
file) to execute arbitrary deployment
actions based on commits and files affected by these commits. For example
for larger monorepos we might want to deploy static documentation but only
if the semantic commits have touched files in the docs
folder.
If I can borrow an image from semantic-release
README:
Links
- semantic-action
- semantic-release
- test-semantic-action shows static deploy with
semantic-action