How to be smart about releasing software -Semantic Release Conventional Commits and GitHub actions
Published: 2023-02-13 09:07:45

There are a multitude of ways to release software. In this article we will be discussing deployment of software with Semantic Release, Conventional Commits and GitHub actions. 

In an Agile world we *want* to release software often. So the question becomes how do we release software in such a way that we *can* release often, not break existing deployments and keep track of what was released - without the need for manual reporting on features implemented and bugs fixed.

So how do we go about releasing and deploying this.

## Starting with Conventional commits

Conventional commits are git commit messages that have a defined structure to make commits human and machine readable. Here are what a few typical commits may look like.

`feat(data): add JSON file with 1 meme entry`

`fix(data): correct the path of meme`

`build: add new script command for tests`

`feat(header): add Header component`

Each commit is prefixed with a type - `feat`, `fix`, `docs`, `build` etc - see documentation for full list. The type is followed by optional scope surrounded by parenthesis to describe the thing being worked on `feat(component-name)`, `docs(component-name)` etc.

As a team, all developers must follow this convention in order for Semantic Release to work correctly. This is because these commits will be what get added in the release notes generated by Semantic Release.

[Developers sign up for this convention typically at the start of a project]

## Semantic Versioning

Semantic versioning or SemVer for short is a specification that describes how software should be versioned. When you see a software version like `12.6.1`, which is very common these days, the `12` in this case is the MAJOR version, the `6` the MINOR version and the `1` a PATCH version. 

A MAJOR version number is incremented by 1 (usually) to describe this new version is incompatible with the previous version, so it is a major change. The MINOR version number is also incremented by 1 which describes new functionality has been added in a backwards compatible manner. The PATCH version describes, what usually are, bug fixes to the existing software version.

## Semantic Release

Semantic Release uses the specification designed by Semantic Versioning (SemVer) and couples that with Conventional Commits and a CI (continuous integration) environment to create fully automated releases.

![Example of Semantic Release in GitHub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ntp0in7h369q6uupsqfs.png)

Example of Semantic Release in GitHub

### Semantic Release for GitHub

So we know that we need to use Conventional Commits, and that Semantic Releases uses those to generate releases and increment the software version. But how do releases get generated in GitHub? This is where the GitHub Semantic Release package comes in.

## GitHub actions

GitHub Actions is a tool used for automating software development workflows, such as building, testing, and deploying code. When a workflow is triggered, GitHub Actions runs a series of steps in a virtual environment, such as running a script, checking out code, or deploying to a server. We will create a GitHub action by adding a `release.yml` file in to the `.github/workflows` folder in the repo.

## Getting Started

Let’s now create a barebones repository structure to demonstrate how Conventional Commits, Semantic Release and GitHub Actions all work together. Be sure to have the latest Node installed on your machine.

1. Create new repo and clone it to your machine
   
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bn0jwcpqkuieta9ahq6s.png)
2. Change directory to cloned repository and initialise an NPM project
    
    ```bash
    cd dev-project-2023
    npm init -y
    ```
    
    This create a new Node project which allows us to take advantage of NPM and many packages that facilitate our development
    
3. Install Jest 
    
    ```bash
    yarn add jest
    ```
    
    Jest is a testing framework, setup here just as an example for the project.
    
4. Set up files
    1. Create an `index.js` file - this is typically where our application might start from
    
    ```jsx
    function get2() {
      return 2;
    }
    
    module.exports = get2;
    ```
    
    b. Create a `test.js` file
    
    ```jsx
    const get2 = require("./index.js");
    
    test("Runs get2 returns 2", () => {
      expect(get2()).toBe(2);
    });
    ```
    
5. Install SR and GHSR - Lets install SemanticRelease and GitHub Semantic Release packages
    
    ```bash
    yarn add semantic-release github-semantic-release
    ```
    
6. Create an `.releaser.json` file
    
    ```bash
    {
      "plugins": [
        "@semantic-release/commit-analyzer",
        "@semantic-release/release-notes-generator",
        "@semantic-release/github"
      ],
      "branches": ["main"]
    }
    ```
    
7. Create a `.release.yml` file inside `.github/workflows` folder
    
    ```yaml
    name: Release
    "on":
      push:
        branches:
          - main
    jobs:
      release:
        name: release
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v3
          - uses: actions/setup-node@v3
            with:
              node-version: 14.17
          - run: yarn install --frozen-lockfile
          - run: npx semantic-release
            env:
              GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
    ```
    
8. Secrets
    
    Secrets are encrypted variables that we use for authentication needed for the GitHub Semantic Release package to use. The secret (`secrets.GH_TOKEN`) will contain your personal access token that you will need to generate in the settings tab of the repo. Follow the instructions here [https://github.com/semantic-release/github#configuration](https://github.com/semantic-release/github#configuration)
    
9. Create a new branch called `next` off `main`
    1. Create a feature branch called `feature-branch-x` off `next`
10. Conventional commits
    1. Make a change any change, lets say to the function and test file. And commit:
    
    `feat(index): change function`
    
    ```jsx
    function get3() {
      return 3;
    }
    
    module.exports = get3;
    ```
    
    `test(index): change test file`
    
    ```jsx
    const get3 = require("./index.js");
    
    test("Runs get3 returns 3", () => {
      expect(get3()).toBe(3);
    });
    ```
    
11. Merge feature branch back into `next`
12. Then do a pull request into `main` and merge
    ![PR into main branch](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4tbnk8eobeaby9ctergh.png)
13. See GitHub action running generating tag and release
   ![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/en2x8pu401alcoza0x8v.png)
14. List of tags and releases
    
![list of tags in GitHub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v7917cwvd7wrheh7gut9.png)
![List of releases generated by Semantic Release in GitHub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tirvyctczhiovdtd5sg0.png)

Here we can see that after making several changes, how a tag is created and a subsequent release generated.

### Conclusion

Together Semantic versioning, conventional commits, Github Semantic Release, and Github Actions form a powerful suite of tools that can help to streamline the software development process and ensure the delivery of high-quality software releases. By utilizing these tools, developers can work more efficiently, reduce the risk of errors, and focus on delivering valuable software to users.