Development · July 2019

Automating releases for UI Kits

How to automate release logs and builds for your style guides.

Isabel SáDesigner & Developer
llustration by Fred Jesus
llustration by Fred Jesus

As of lately design systems have been all the rage and the benefits of producing one seem to be clear. Buying into a system might seem like a straight forward decision, but keeping everyone in the loop of changes is exponentially more complicated. If you work on the development end of any type of component library you know the silent difficulties of building and publishing processes.

The boring parts are a crucial part of the process. If you maintain projects that rely on versioning you’ll probably find yourself repeatedly doing the same task. And while semver as general practise is great, it’s difficult to enforce and to not decide on versions arbitrarily. When the times comes to inform stakeholder of choices and implementation details one month has gone by, and you’ve forgotten about most of the features and fixes implemented. And what good is a system that no one remembers?

“What’s the status on this component? Did we ship this already? When did we start getting this Type Error?”

This article covers a small part of that process, giving you a few practical tips on how to maintaina clear log and automatically build changes. This method can be applied to anything, from UI Kits, to packages and apps.

In order to create this flow we’ll go through a few steps:

  1. Creating good commit messages. Using conventional commits.
  2. Enforcing good practises. With pre-commit hooks.
  3. Creating releases. Our release cookbook.

This article has some companion gists. 🛠

The automated result of what we'll be working on

Creating good commit messages

The first step is to follow a commit convention so you can generate your changelog without any manual work. Your contributions will be grouped by commit type and will look a bit like this type(scope): short description for commit with the type ranging from FEAT to TEST, determining what type of changes went into your code. To generate a template for commit messages we‘ll be using commitizen’s CLI.

Since we’re going to want this to be used on all of your projects right out of the gate we’re going to do some global installation with npm install -g commitizen.

Every project requires different things, and commit messages are part of that. So depending on the format you need you can install one of several adapters. From emoji filled commit messages to JIRA integrations. For the purposes of this guide, we’ll use cz-conventional-changelog. We’ll now initiate the adapter.

1commitizen init cz-conventional-changelog --save-dev --save-exact

Then create a .czrc on your root folder (~) with the following content:

1{ "path": "cz-conventional-changelog" }

Now, every time you’re in a git repository if you use the command git czthe CLI will show up, asking you to catalogue your commit according to the type of changes you made.

The cz-cli and some git aliases at work.

Enforcing good practises

This is all great, but it won’t add up to much if you write DID_STUFF as the scope of your change. That’s certainly invalid and won’t help much when generating your logs. To solve this problem we’ll put into place a commit validator and a pre-commit hook which will validate our commit messages before they go through. To create the hook we’ll use commitlint and husky.

Add the extra layer of security to be run on pre-commit with npm install -g @commitlint/cli @commitlint/config-conventional. And now your pre-commit checker with npm install husky --save-dev.

Create a .huksyrc file with the commitlint reference on "commit-msg".

1{ "hooks": { "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", "pre-commit": "npm run lint" }}

And create a configuration file to hold your preferred tests, in any of the following formats: 

  • commitlint.config.js 
  • .commitlintrc.js.commitlintrc.json 
  • .commitlintrc.yml.

Then add a reference to the adapter you chose on the first step:

1module.exports= { extends: ["@commitlint/config-conventional"] };

This will result in a verification triggering the husky > commit-msg validation, which might either pass or return a message with the problem at fault. Bear in mind that if you use flags such as --no-verify this validation won’t occur.

The pre-commit hook catching the WILL_THRIW_ERR invalid scrope.

Creating releases

With the message formatting sorted we can now focus on the actual bundling and releasing of your UI. We’ll now create a script to be executed on your CI (or locally with (dry-mode) environment), that’ll look at your commit history and do all the magic. We’re gonna create script with the dependency on you package.json:

2 "name": "automation-for-ui-kits",
3 "version": "0.1.0",
4 "main": "dist/bundle.js",
5 "license": "MIT",
6 "private": false,
7 "repository": {
8 "type": "git",
9 "url": ""
10 },
11 "dependencies": {
13 },
14 "scripts": {
16 "build": "YOUR_METHOD_OF_CHOICE",
17 "release": "semantic-release",
18 "lint": "eslint src/ --fix"
19 },
20 "eslintConfig": {
21 "extends": "react-app"
22 },
23 "devDependencies": {
25 "@semantic-release/commit-analyzer": "^6.1.0",
26 "@semantic-release/git": "^7.0.8",
27 "@semantic-release/github": "^5.2.10",
28 "@semantic-release/release-notes-generator": "^7.1.4",
29 "semantic-release": "^15.13.12"
30 }

Now we’ll create our recipe for releases. These will vary depending on the project, technologies and implementation details but our core recipe revolves around these plugins:

2 "plugins": [
3 "@semantic-release/commit-analyzer",
4 "@semantic-release/release-notes-generator",
5 ["@semantic-release/github", {
6 "assets": [
7 {"path": "dist/bundle.js", "label": "JS distribution"}
8 ]
9 }],
10 ["@semantic-release/git", {
11 "assets": ["dist/**/*.{js,css}"],
12 "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
13 }]
14 ]

Why all the @semantic-release/plugin?

/COMMIT-ANALYZER: This is what powers all of the release, and this is why our commits must be formatted exactly as specified by the chosen convention. This will ensure that certain types will be associated with a patch, minor or major release. This will be visible in your terminal or the virtual environment you’re running. You can see what each type of commit triggers.

Release being executed on CI Enviromment and checking commit types.

/RELEASE-NOTES-GENERATOR: What generates your changelog based on the conventional commit standard defined above. Which will result in a release log similar to the one below.

Latest release posted automatically.

/GIT and /GITHUB: This is the final part of the configuration enabling you to push commits with the bundled coded to your folder of choice, with the fomat defined on the .releaserc. It’ll also release, comment and tag your pull requests and issues. Magical isn’t it?

The automated released label on all closed PRs.


Like most automation scripts the configuration is cumbersome, but the time and stability you’ll gain in the long run is worth the effort. Since we’re using computers we might as well use that to our advantage and do things in a smart and efficient way.

All the examples here were quickly built for the purpose of this article and should be adapted to your project’s reality, so feel free to adapt your build scripts and the message conventions to the format that’ll best suit your needs.

Design SystemsAutomationDynamic ChangelogSemantic ReleaseCommit

Isabel Sá

Designer & Developer @ Significa

Isabel is a Front-End Developer at Significa. Hold on! Isabel is a Designer at Significa! Wait, what?! True story, Isabel is like a Swiss Army knife, tiny but loaded with great tools!