Automate Versioning of Your Serverless Deployments
Semantic Versioning is a well-known system for keeping track of all the changes that happen in a library or package. You probably already use it every day to control the dependencies of your projects. But do you keep semantic versioning of your own application?
It might not look like an obvious or necessary thing to do. After all, if your app is meant to be deployed in the cloud, it’s not like your users/consumers will need, or be able to, chose which version they want to use. Your service will always serve whatever was last deployed. So what is the point?
While semantic versioning might not be strictly necessary for a serverless application, I still think that at least some sort of versioning brings some benefits:
- You easily know what version (and therefore what code) is currently running on production
- It helps keeping track of what was deployed and when (provided you also keep a changelog)
- It can be useful when tracking issues (e.g.: since what version has this bug been happening?)
In this blog post, I will show you how, at Serverless Guru, for one of our clients, we automate our serverless applications deployments and keep a version, and a changelog, for every one of them.
To achieve this, we will use semantic-release. Semantic-release is a tool that helps automate the versioning and publication of packages. It does so by analyzing the commit messages that developers push to the repository. While it was originally built for publishing packages to the npm repository, it is highly configurable and can be used for many other purposes. In our case, instead of pushing a package to npm, we can use it to deploy our application using our favourite deployment tool (in our case: serverless framework, but it will work for any other: CDK, SAM, etc).
First, we’ll need to install semantic-release. To do so is as simple as adding it to our dependencies. Since we won’t use the default behaviour, we’ll also need to install the @semantic-release/exec plugin that we’ll use to execute custom commands during the process (more on that in a second).
We also need to configure it. Create a .releaserc.json file with the following content.
The above configuration configures the different plugins that we need:
-@semantic-release/commit-analyzer is a plugin that analyses the commits pushed since the last release. It is used to determine the next version of the application.
-@semantic-release/release-notes-generator generates a markdown snippet containing a summary of all the changes pushed since the last release.
-@semantic-release/exec is used to execute custom commands in the “publish” phase of the process. We need to configure some parameters for it:
- publishCmd takes a command that should be executed for “publishing the release” (what would normally be npm publish). For us, it means a deployment, so we specify the deploy command. At Serverless Guru, we use the Serverless Framework, so we’re using sls deploy
- verifyConditionsCmd is called before the publishCmd. It is like a pre-condition check that you can use to do any kind of validation. Here, we’re using it in order to check that the current user has valid AWS credentials configured by getting the current caller identity.
-@semantic-release/github does the following on your GitHub repository (note: there is also a gitlab plugin that you can install separately):
- Create a git tag in the repo for every version
- Create a release for that tag
- Save a release note with the changes since the last deployment
Notes: You do not need to install the commit-analyzer, release-notes-generator, and github plugins, as they come with semantic-release.
Commit message conventions
Before you can release your first version, you will need to follow a few simple rules when pushing new commits and/or merging pull requests. By default, semantic-release uses the angular commit message convention (You can change that if you want - see doc). Every commit message will be analyzed to determine the next version number.
The most common prefixes that you will probably use are feat which will create a MINOR version and fix which bumps the PATCH number.
It is important to note that the commit-analyzer will ignore commits that do not match the used convention. If there is no matching commit, semantic-release will not create or publish (deploy) any new version.
With everything configured, and after pushing your first commits, you could go ahead and run npx semantic-release, but that would not do much. Indeed, semantic-release is meant to be executed in a CI environment. When executed in a local environment, semantic-release runs in dry-run mode by default.
It is always useful to test it, though, in order to check what it would do. (Note that you will also need to set up a GITHUB_TOKEN in your local environment variables containing a personal access token).
But since we want to automate our releases anyways, what we’ll do next is configure our CI/CD pipeline.
I will not go into the details on how to build a CI/CD pipeline to deploy your serverless stack here. It will depend on each individual use case, and you probably already have one anyway. For the purpose of this article, I will show a short example using Github Actions and keep only the relevant parts that should change.
The first important part is the permissions section. It tells Github to create a GITHUB_TOKEN with the necessary write permissions to your repository. If you use another service for your pipeline (CircleCI, Jenkins, etc.), you will then probably
need to generate a new access token with those permissions and add it to your environment variables/secrets.
Finally, the key part is the deployment command, add or replace your existing deployment step with npx semantic-release. semantic-release will then take care of creating a new release with release notes, a git tag pointing at the latest commit, and will call the publishCmd command that you configured earlier. When it’s done, it will also post comments on the pull requests that were included in the release.
Here you have it! With very few lines of code, we fully automated the versioning of our application. Semantic versioning might not be the best fit for versioning a deployed application, but semantic-release made it dead simple to accomplish and it still serves its purpose: keeping better track of your releases.