Como automatizar o processo de versionamento de um projeto em Javascript
Tags: tutorial, github, javascript, programming
Published: 2022-06-11 16:00:39

## Introdução

Como desenvolvedores, amamos automatizar tarefas rotineiras. Nesta publicação demonstrarei a automação do fluxo de release utilizando a biblioteca **semantic-release**. Esta library permite automatizar todo o oprocesso de geração de release; Determinando o número da próxima versão, criando a nova tag, gerando as novas release notes e a publicação do pacote no NPM, caso seja necessário.

Para tudo isso funcionar, a biblioteca faz uso das mensagens de commits para determinar o número da próxima versão, o Changelog e a publicação. Por padrão o **semantic-release** utiliza a especificação de mensagens de commits do Angular o [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) e a convenção [semver](https://semver.org/) para o número de versão.

Resumindo o SemVer nos diz como devem ser os números de nossas versões, como por exemplo: `MAJOR.MINOR.PATCH`. Já o Conventional Commits nos diz como devem ser nossos commits, para que o de/para do commit pro SemVer seja feito de forma automática.

Abaixo mostrarei uma imagem desse de/para:

![De/para do SemVer para Tag de versionamento](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fle3zxztn9nv43o2izhz.png)

No CI o **semantic-release** deve ser executado após o build ser feito com sucesso no branch de release, por exemplo.

## Como funciona

O **semantic-release** verifica os commits de um branch entre a última tag gerada até o último commit deste branch, para então acionar a geração de uma nova tag. Em seguida ele cria ou atualiza o arquivo de Changelog com base nos commits entre as duas tags.

Ná prática fica da seguinte forma, como é mostrado na imagem a seguir: 

![Na imagem mostra uma linha do tempo de commits](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mxqcqhhews840a2fsyt7.png)

Observe que no final imagem temos uma tag `1.17.1` e após realizarmos um commit novo no [branch de release](https://github.com/andraderaul/random-fy/commits/main), o **semantic-release** é acionado e cria uma nova tag com a numeração `1.17.2`. Em seguida, realizamos dois commits um de docs e outro de fix, quando esses commits vão para o branch de release é acionado novamente e temos uma nova tag `1.17.3`. Depois disso, temos mais um commit de feat no branch de release e nossa nova tag é `1.18.0`. 

E nosso arquivo de [Changelog](https://github.com/andraderaul/random-fy/blob/main/CHANGELOG.md) fica da seguinte maneira:

![Na imagem mostra a linha do tempo do Changelog](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yfo3a2w8kg7k256fae78.png)

É importante notar que cada tag foi associada a uma release, a tag `1.17.1` tem uma release associada e nas notas de release temos os commits que foram feitos naquela tag.

Observe que existe um commit de docs e ele não foi adicionado nos Changelogs, porém há formas de configurar para que ele apareça.

Por último, mas não menos importante é possível executar o **semantic-release** sem acionar a geração de uma nova release, basta executar ele como `dry-run`. Isso é uma salvação quando estamos no processo de configuração ou queremos ver o Changelog daquela versão.

## Configuração

Nessa seção descreverei passo a passo de como configurar o **semantic-release** em um projeto. Uma observação é possível que as versões dos pacotes estejam diferentes, ou seja, mais atualizados após a publicação desse blog post. 

- Criar um repositório git e adiciona-lo ao remoto no Github ou usar um repositório já existente; 

```bash
git init
```

- Realizar as configurações necessárias do [**semantic-release**](https://www.npmjs.com/package/semantic-release). 
Note que, se for um projeto novo necessita de `yarn init`; 

```bash
yarn init
```
- Agora precisamos adicionar o pacote do **semantic-release** nas dev dependencies; 
```bash
yarn add semantic-release -D
```
- Em seguida precisamos adicionar os plugins do **semantic-release**;

```bash
yarn add @commitlint/cli @commitlint/config-conventional @semantic-release/changelog @semantic-release/git -D
```
- Cada plugin tem um propósito especifico:
  - `@commitlint/cli`: É um analisador de mensagem de commit para verificar se a mensagem do commit está segundo o padrão;
  - `@commitlint/config-conventional`: É o configurador de um padrão a ser seguido nas mensagens de commit, como por exemplo o padrão do Angular;
  - `@semantic-release/changelog`: Plugin que atualiza ou cria o arquivo de Changelog;
  - `@semantic-release/git`: Plugin que realiza os commits de release e artefatos gerados;

- Adicionar o pacote [Husky](https://www.npmjs.com/package/husky) (opcional):
   - Esse passo é interessante pois com o husky evitamos de subir commits fora do padrão;

- Criar um arquivo `.releaserc.json` exemplo disponível na [doc](https://semantic-release.gitbook.io/semantic-release/usage/configuration): 
   - O arquivo `.releaserc.json` ficou da seguinte maneira;
```json
{
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    "@semantic-release/github",
    "@semantic-release/changelog",
    [
      "@semantic-release/npm",
      {
        "npmPublish": false
      }
    ],
    {
      "path": "@semantic-release/git",
      "assets": ["package.json", "package-lock.json", "CHANGELOG.md"],
      "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
    }
  ],
  "branches": [
    "main"
  ]
}
```

- Criar uma workflow para ser executado no Github action:
    - Criar um `.env` com a seguinte key **GITHUB_TOKEN** e o seu access token do Github;
    - Criar um diretório Github workflows com um arquivo dentro: `.github/workflows/release.yml`;
    - Temos o arquivo `.github/workflows/release.yml` dessa forma;
```yml
name: Release
on:
  push:
    branches:
      - main

jobs:
  release:
    name: Release

    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 'lts/*'

      - name: Install dependencies
        run: yarn install

      - name: Release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: yarn release
```


- Adicionar o comando do **semantic-release** nos scripts do `package.json`:
   - Por último o `package.json` está assim;
```json
{
  "name": "poc-release",
  "version": "0.1.0",
  "main": "index.js",
  "repository": {
    "type": "git",
    "url": "git@github.com:andraderaul/poc-release.git"
  },
  "author": "Raul Andrade",
  "license": "MIT",
  "scripts": {
    "test": "jest",
    "release": "semantic-release",
    "release:dry": "semantic-release --dry-run",
  },
  "devDependencies": {
    "@commitlint/cli": "^16.2.3",
    "@commitlint/config-conventional": "^16.2.1",
    "@semantic-release/changelog": "^6.0.1",
    "@semantic-release/git": "^10.0.1",
    "husky": "^7.0.4",
    "jest": "^27.5.1",
    "semantic-release": "^19.0.2"
  }
}
```

## Repositórios

Para ver o código de configuração na íntegra, acesse o repositório no [github](https://github.com/andraderaul/poc-release).
As imagens utilizadas nos exemplos são de outro [repositório] (https://github.com/andraderaul/random-fy).

## Referências

[semantic-release](https://semantic-release.gitbook.io/semantic-release/usage/configuration)
[Semantic Versioning 2.0.0](https://semver.org/)
[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)