Serverless Consulting

Automate Versioning of Your Serverless Deployments

Using Semantic-Release

Automate Versioning of Your Serverless Deployments

By
Benoit Boure
July 29, 2022

Introduction

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).

Configuration

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).

  
npm i semantic-release @semantic-release/exec -D
  

We also need to configure it. Create a .releaserc.json file with the following content.

  
{
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    [
      "@semantic-release/exec",
      {
        "verifyConditionsCmd": "aws sts get-caller-identity",
        "publishCmd": "npx sls deploy"
      }
    ],
    "@semantic-release/github"
  ]
}
  

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.

For example:

  
feat(orders): Send email to users when order is confirmed
fix(payment): Add missing IAM permission
  

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.

CI/CD pipeline

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.

  
name: Deploy

# ...

# semantic-release needs extra permissions to
permissions:
  contents: write # create tags and releases
  issues: write # comment on issues when a fix is included in a release
  pull-requests: write # comment on PRs when they are included in a release

jobs:
  deploy:
    name: deploy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
			# build, run tests, etc.
      - name: Deploy a new release
        env:
          # semantic release needs the GITHUB_TOKEN to work
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        # run semantic-release
        run: npx semantic-release
  

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.

Release and release notes
Pull Request comment

Conclusion

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.