Maintaining an open source project involves a lot of responsibilities. Especially if you want to ensure ease of contribution, use, and longevity. There is a lot of talk around this topic at the moment, but it is not a solved problem yet.
One of the often overlooked, but incredible important aspects of a successful open source project is documentation, and clear communication with your users. Release notes, and semantic versioning are two parts of this equation, but can be hard to maintain manually over time.
What you will need
Before we dive into adding the above mentioned tools, you will need the following information handy: From the CLI repo:
- Whether your GitHub repository is public or private
- Which npm registry you want to use (Default: registry.npmjs.org)
- Your npm username (unless passwords were previously saved to keychain)
- Your npm email
- Your npm password
- Your GitHub username
- Your GitHub password (unless passwords were previously saved to keychain)
- Which continuous integration system you want to use. (Options: Travis CI / Pro / Enterprise / CircleCI, or Other)
- [Travis only] Whether you want to test a single node.js version (e.g. — 8) or multiple node.js versions (e.g. — 4, 6, 8, etc.)
As indicated above, some questions may be skipped depending on what is available in your keychain, and
Auto-setup using semantic-release-cli
With the above information at hand, run the following:
> npm i -g semantic-release-cli
Because this is not a dev dependency, or direct dependency of the project, it is safe to install this globally. At the root of your project run the following:
> semantic-release-cli setup
If you use two factor authentication on Github, and you should, you will be prompted for the code.
Also, if you chose Travis CI above and do not currently have a
travis.yml file in your repo, you will be prompted with the following question:
“Do you want a `.travis.yml` file with semantic-release setup?” ~ You can go ahead and say yes here.
As semantic-release kicks off and does it’s thing, you will notice an email from Github informing you that a personal access token has been added. This is expected. You can read here what the CLI is doing for you.
If you look at your
package.json you notice that your
version has been changed to
"version": 0.0.0-development. This is because semantic-release will take control over the version numbers from here on out. Kent C. Dodds uses a clever default, that indicates that the version number in your
package.json is being handled by semantic-release. Go ahead and change it to read:
As hinted at, you no longer need to be concerned about the version number in your local
Ensure semantic commit messages
The way semantic-release determines how to tag your next release, and generate you release notes, is through your commit messages. It is therefore important to ensure a consistent format, and follow a clearly defined convention.
Here again we have some tools and existing conventions we can follow. First, let us look at the commit message format we will be using. The format was defined by the Angular team, has been adopted by many projects, and is known as the conventional changelog.
You are encouraged to read the full explanation on their repo, but here is the crux of the format and how it maps to semantic-release:
Setting up Commitizen
Knowing and understanding the format is important, adding an entry to your README explaining it is an important next step. We do not however want to entirely rely on ourselves, or contributors to remember, and follow the format. This can easily lead to frustration when someone opens a pull request, and the first interaction is quibbling about the commit message format.
This is where commitizen steps in to save the day. The next step then, is to add it to our project workflow.
As the CLI(command line interface) portion is a tool you will use across multiple projects, it is another one that is save to install globally. Run the following:
> npm i -g commitizen
Note: Be sure to add a note on this dependency to you CONTRIBUTING document.
Next we need to install and initialise the conventional changelog adapter:
> commitizen init cz-conventional-changelog --save-dev --save-exact
To make it easy for yourself and contributors to use commitizen, add the following to the scripts block in
The last step is to make a note about this in your contribution docs. Contributors now need to do
npm run cz when they are ready to commit, and no longer
You might also want to review your
travis.yml file. By default the file that is generated will specify four node version to run the CI against, for example:
If this matches your intent, you need not change anything. For my purposes though, I am going to narrow it down to only versions 8 and 9 of Nodejs.
Also, if this is a new project, be sure to clear out the default value for the
test property in your
package.json or else all your builds will fail.
Ok, so we have done a bunch of work here. We are now ready for automated releases, and we are ensuring a predictable commit flow. Time for some badge action. You can find a badge for commitizen here, and for semantic release here.
Using our toolchain aka Dogfooding
Time to test that all of this works 🤞
Using our commit conventions, the work we have done so far is most appropriately labeled as a chore. Before we commit though, we need to ensure everything we want to add is accounted for, and then stage it for commit.
First, ensure the following is present in your
After confirming the above, we can move forward.
> git status# if all is good
> git add .
Time to commit
> npm run cz
If everything goes according to plan, you will now be presented with a screen that looks something like the following:
From the above select, chore
Next we need to define the scope. As you will note from the prompt, this can be a component or even a file name. For this commit I am going to define the scope as tooling.
The next prompt asks you to “Write a short, imperative tense description of the change”. You can read more on what this means in the docs, but the tl;dr is, use “change” and not “changed” for example.
Note: This will be the subject line of your commit
For this line I am going to use “Add semantic-release and commitizen”
Next you will be asked to provide a longer description(the body of your commit) for your commit. Depending on the commit, it may be appropriate to skip the longer description. For the purposes of this, and because the scope is rather large, I am going to add the following.
Add semantic-release and commitizen to automate releases of this repo. This also enforces the conventional changelog convention for commit messages
You will now be asked if there are any breaking changes. For this commit, there are none. If there were, you would enter y and then be asked to describe those changes.
Another neat feature you can take advantage of here is to specify an issue that this commit closes. Using certain keywords here will cause Github to automatically close those for you.
This is what the next prompt will ask you about. I created an issue on a new project of mine that I will reference here. As this is the first issue on this repo, I will enter “Closes #1”
With that, we are ready to push to our repo.
Pushing, the pull request, and testing merge to master
> git push origin issue#1-add-semantic-release-commitizen
Change the above to match your local feature branch. Head over to your repo and open the pull request. Here is a screenshot of what my pull request looked like:
I am sure you will agree that, that is pretty darn sweet ;p Once you open the pull request, you will see that a Travis-CI build is kicked off.
Once your builds have passed, it is time to merge to master. So go ahead, click that big green button.
Did it work?
Well, yes and no. Unless you ran into any build time errors, all should have gone smoothly. You will however notice that no release was published, and nothing was published to NPM either. Why?
Because our commit above was marked as a chore, it does not trigger a release. So no, it did not publish a release, but yes, it worked exactly as it should have. You will also have noticed that, because we referenced an issue in our commit message, it has been automatically closed.
Trigger a Release
It would be no fun to leave this at that though, so let’s trigger a release. First thing is to decide on a small feature or fix you can add to your project. The go ahead and create an issue for it.
Pull your changes from before, and then create a new feature branch for the work. Once you have completed the work, add your changes, and commit. Remembering to specify this as either a feature, or a fix.
Once the above is complete, open your pull request and wait for Travis-CI to complete your build. Merge to master.
What just happened?
We have our first release! That is not all though, not by a long shot. Let’s see what these tools just automatically did for us. First, you will notice that your repo now has a release published. Second you will see that not only did it publish a release, but a beautifully formatted release with release notes. You can take a look at mine here or in the screenshot below:
Next, look at the issue that was referenced in this pull request. You will notice that semantic release has added a neat comment.
Head over to your pull request and, yes indeed, semantic-release has added a comment for you here as well, much in the same format as the above. And as if all of that was not already enough to justify the set-up, head over to your packages list on NPM.
Yup, this has also been done.
I hope this eases the burden of maintaining your open source projects, and ensures continued, predictable releases.
This post would not be complete however without saying a huge thank you to the amazing team behind semantic-release. If you run into any problem, do not hesitate to reach out to them. They respond super fast, and seriously know their projects inside out.
What other tips and tricks are you using to streamline managing an open source project? Let me know in the comment.