Oie! Se você já usou bibliotecas, ou viu repositórios open-source, você já deve ter visto um arquivo chamado CHANGELOG.md
. Até dá pra fazer ele manualmente, mas...
Você luta contra muitos fatores e perde muito tempo. Além disso, é muito simples fazer de forma automática. Só precisamos:
- Definir um padrão para os nossos commits
- Usar pacotes para auxiliar nesses padrões
- Usar um pacote para gerar o CHANGELOG
Então, vamos começar a gerar nosso CHANGELOG.
Preparação
Na época que esse guia foi preparado, foram usadas as seguintes tecnologias:
tecnologia | versão | instalação |
---|---|---|
Node.js | 11.6.0 | como instalar |
NPM | 6.13.4 | Já vem com o Node.js |
Todo o código desse projeto está no GitHub, então, caso você se perca em qualquer parte, pode fazer uma comparação direta. Além disso, todos os passos desse artigo estão lá de forma resumida:
klauskpm / changelog-cicd
Repositório para ensinar a criar CHANGELOG automaticamente
changelog-cicd
Repositório com passo-a-passo de como gerar um CHANGELOG automaticamente.
Esse passo-a-passo é a versão muito resumida do artigo: Como gerar CHANGELOG automaticamente
Gerando um CHANGELOG automaticamente
1) Instale as dependências
npm install --global commitizen
# dentro do seu projeto
npm install --save-dev husky @commitlint/cli @commitlint/config-conventional standard-version
2) Configure o commitizen
# dentro do seu projeto
commitizen init cz-conventional-changelog --save-dev --save-exact
3) Crie o arquivo commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional']
};
4) Altere o seu package.json
{
...,
// opcional
"repository": {
"url": "git@gitlab.com:meu-usuario/meu-repo.git"
},
...,
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"scripts": {
"release": "standard-version"
}
}
5) Crie o arquivo .versionrc
{
"types": [
{"type": "feat", "section": "Funcionalidades"}
{"type": "fix"
…Padronizando as mensagens de commit
O padrão que vamos seguir para as mensagens é o conventional commit specification(especificação convencional de commit). Esse padrão é escrito da seguinte forma:
<tipo>[(escopo opcional)]: <descrição>
[corpo opcional]
[rodapé opcional]
campo | obrigatório | descrição |
---|---|---|
tipo | ✅ | Tipo do commit que vai ser feito. Verificar lista de tipos permitidos. |
escopo | ❌ | Arquivo, domínio, ou módulo que aquele commit se refere |
descrição | ✅ | Uma descrição curta sobre o commit |
corpo | ❌ | Uma descrição maior, caso não consiga explicar com clareza tudo o que tem no commit |
rodapé | ❌ | Fechamento de tarefas e/ou informação sobre breaking changes |
Lista de tipos permitidos
- feat: Uma funcionalidade
- fix: Um ajuste de erro/bug
- docs: Modificação na documentação
- style: Mudança de estilo (ponto, vírgula, indentação)
- refactor: Mudança de código que não adiciona funcionalidade ou arruma um erro
- perf: Mudança que altera performance
- test: Novos testes ou correção de antigos
- build: Mudanças que afetam o build ou dependências exeternas (gulp, npm)
- ci: Mudanças na configuração da Integração Contínua (Travis, Circle)
- chore: Outras mudanças que não são nos arquivos de src ou test
- revert: Reversão de um commit
Exemplos de commit
feat(cadastro): adiciona integração com Gugou Sign-in
fix(pagamento): muda a chave do RecebaSeguro
A chave que estava sendo usada era de desenvolvimento,
mas acabou sendo enviada para produção
Fecha a tarefa #457
refactor(produto): remove o método #adicionarAoCarrinho
O método já tinha sido depreciado e agora foi removido
BREAKING CHANGE: o método públic #adicionarAoCarrinho foi removido
Facilitando a padronização
Apesar da padronização facilitar a automatização, ela pode ser muita coisa pra gravar de primeira. Então, para facilitar a aplicação desse padrão, nós vamos usar o Commitizen e o Commitlint.
Commitlint
O commitlint
vai verificar se nossos commits estão seguindo o Conventional Commit Specification. Ele vai fazer isso com a ajuda do husky
, que irá disparar o commitlint
toda vez que for feito um commit.
1) Instale as dependências
# dentro do seu projeto
npm install --save-dev husky @commitlint/cli @commitlint/config-conventional
2) Crie o commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional']
};
3) Configure o husky
no package.json
{
...,
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
}
4) Teste para ver se está funcionando (opcional)
A configuração está pronta e já deve estar funcionando. Se quiser, pode fazer uma adição no seu projeto e commitar como teste para quebrar
. Você deve receber um erro explicando o que está faltando no commit.
Commitizen
Agora que temos algo para verificar nossos commits, precisamos fazer eles de uma maneira mais fácil. Com o commitizen
, nós vamos commitar usando o comando git cz
, que vai fazer perguntas passo-a-passo até ter o commit final.
1) Instale e configure o commitizen
npm install --global commitizen
# dentro do seu projeto
commitizen init cz-conventional-changelog --save-dev --save-exact
2) Teste para ver se está funcionando
Faça uma pequena alteração, adicione ela com git add .
e faça um git cz
.
Automatizando o CHANGELOG
Agora que nós temos os commits seguindo um padrão, podemos gerar o nosso arquivo CHANGELOG. Os dois pacotes mais utilizados para fazer essa tarefa hoje, são o semantic-release
e o standard-version
. Vamos seguir com o standard-version
pela facilidade do uso.
standard-version
Esse pacote vai ser responsável por analisar nossos commits, gerar uma nova versão pro projeto e gerar o CHANGELOG.
1) Instale o standard-version
npm install --save-dev standard-version
2) Altere seu package.json
{
"scripts": {
"release": "standard-version"
}
}
3) Faça seu primeiro release
Agora que tudo está configurado, commite essas alterações e execute o seguinte comando:
npm run release -- --first-release
O
npm run release
executa as seguintes etapas:
- Modifica a versão do seu
package.json
- Usa o
conventional-changelog
para criar/atualizar oCHANGELOG.md
- Commita o
package.json
e oCHANGELOG.md
- Gera uma nova tag
A diferença dele para o
--first-release
, é que o--first-release
ignora o passo 1.
🎉 CHANGELOG gerado 🎉
Pode comparar o seu progresso com a tag v1.0.0 do repositório
Explorando o CHANGELOG
Agora que temos tudo, vamos entender como o CHANGELOG é gerado e o que podemos fazer com ele.
Se você for no repositório na v2.1.0, você verá que foram criados vários commits e versões. Olhando essas versões criadas, podemos observar que:
- Uma feat representa um minor (X.1.X) do SemVer;
- Um fix representa um patch (X.X.1) do SemVer;
- Uma BREAKING CHANGE representa um major (1.X.X) do SemVer;
- Só aumenta um número por vez. 3 feats aumentam o mesmo que 1;
- O maior número é que manda. Se tem feat, não importa o fix;
- Só aparecem os commits com feat e fix;
- Todos os links estão apontando para o repositório do GitHub;
Tirando os dois últimos pontos, o resto são regras do próprio SemVer (Semantic Versioning), que é quem dita as regras do versionamento (major.minor.patch).
Esses dois últimos pontos ganharam destaque porque eles são muito interessantes. Fazendo uma reflexão, parece que você teria que fazer uma solução própria caso quisesse exibir commits de outros tipos ou se quisesse usar o GitLab. Felizmente, o standard-version
tem uma solução bem prática pra isso. Você só precisa passar um arquivo de configuração.
Configurando a geração do CHANGELOG
Podemos passar a configuração pro standard-version
de diversas formas, mas vamos focar no .versionrc
Esse arquivo segue a especificação de configuração do conventional changelog. Então, se quiser exibir um outro tipo de commit, botar em português, e usar o GitLab:
1) Crie o arquivo .versionrc
Aqui nós estamos adicionando o tipo perf ao CHANGELOG.md
, ao mesmo tempo que estamos também definindo os nomes das sessões em português. Por último, estamos adicionando o [skip ci]
na mensagem de commit que é gerada pelo standard-version
. Isso vai ser útil no futuro.
{
"types": [
{"type": "feat", "section": "Funcionalidades"},
{"type": "fix", "section": "Errors Corrigidos"},
{"type": "chore", "hidden": true},
{"type": "docs", "hidden": true},
{"type": "style", "hidden": true},
{"type": "refactor", "hidden": true},
{"type": "perf", "section": "Melhorias de Performance"},
{"type": "test", "hidden": true}
],
"releaseCommitMessageFormat": "chore(release): {{currentTag}} [skip ci]"
}
2) Altere o package.json
(Opcional: GitLab)
Se quiser mesmo usar outro repositório, é bem provável que você só precise adicionar o repository.url
no seu package.json
. O standard-version
usa essa URL para definir as URLs do CHANGELOG:
{
...,
"repository": {
"url": "git@gitlab.com:meu-usuario/meu-repo.git"
}
}
Se tiver problema nas URLs que foram geradas no seu
CHANGELOG.md
, você pode adicionar uma configuração customizada para o seguintes campos no seu.versionrc
:commitUrlFormat
,compareUrlFormat
eissueUrlFormat
.
Pode comparar o seu progresso com a tag v2.1.1 do repositório
Automatizando a automatização com integração contínua
O nosso CHANGELOG já é gerado graças ao standard-version
, mas ainda precisamos rodar o script de forma manual. O ideal é que isso aconteça sempre que o código atinja a versão final / produção. Para isso, vamos usar o GitHub Actions como nossa ferramenta de integração contínua (CI - Continuous Integration).
No repositório do GitHub, na aba Ações/Actions, vamos criar um workflow de Node.js.
E esse é o arquivo que é gerado por padrão nesse workflow, mas não vamos usa-lo:
# nome do processo
name: Node CI
# o processo roda quando tiver um push
on: [push]
jobs:
build:
# roda o build no ubuntu
runs-on: ubuntu-latest
strategy:
# o processo vai executar uma vez para cada configuração do node
matrix:
node-version: [8.x, 10.x, 12.x]
steps:
# esse passo pega uma cópia dos seu repositório
- uses: actions/checkout@v1
# esse passo instala a versão do node
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
# esse passo faz a instalação das dependências, roda o build e o teste
- name: npm install, build, and test
run: |
npm ci
npm run build --if-present
npm test
env:
CI: true
Nós vamos usar esse arquivo como base para construir o nosso próprio. Se olhar bem, nós não precisamos realizar um teste, criar uma build ou muito menos rodar em várias versões do node, mas precisamos gerar o CHANGELOG e ele precisa de permissões para commitar e pushar no GitHub.
Além disso, tem um ponto que precisamos prestar muita atenção. Como o nosso processo de automatização gera um commit e roda a cada push, nós vamos criar um loop infinito. Felizmente, eu já passei 1 dia resolvendo isso, e depois de 50 commits disparados automaticamente, achei a solução.
Nos outros sistemas de integração contínua (CI) se fizermos um commit com skip ci
- se lembra do .versionrc
? - esse commit é automaticamente ignorado. Pena que não funciona assim no GitHub. Felizmente, a internet é um lugar maravilhoso e as pessoas conseguiram desenvolver uma solução para isso.
Juntando os fatores que precisávamos para gerar o CHANGELOG.md
e aplicando essa solução que previne o loop, temos o seguinte arquivo:
.github/workflows/gerador-de-changelog.yml
name: Gerador de CHANGELOG
# só executa no push de commit da branch master
on:
push:
branches:
- master
jobs:
build:
# só deixa executar se o último commit não conter 'skip ci' na mensagem
if: "!contains(github.event.head_commit.message, 'skip ci')"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
# configura o GITHUB_TOKEN
# https://help.github.com/en/actions/automating-your-workflow-with-github-actions/authenticating-with-the-github_token
- name: Configura o GitHub token
uses: fregante/setup-git-token@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: "Gerador de changelog"
email: "changelog@users.noreply.github.com"
# instala a versão 11.x do Node.js
- name: Instala o Node.js 11.x
uses: actions/setup-node@v1
with:
node-version: 11.x
# instala as dependências, vai para a master
# executa o standard-version, e envia o commit e tag
- name: Gera o CHANGELOG
run: |
npm ci
git checkout master
npm run release
git push origin master --follow-tags
Se quiser saber mais sobre como configurar um workflow, como funciona a sintaxe do workflow do GitHub Actions e o que é o GITHUB_TOKEN, sugiro seguir esses links.
Pronto! Agora nossa automatização está completa!
Pode comparar o seu progresso com a tag v2.1.2 do repositório
Última graça
Por último, só para fazer uma graça, podemos adicionar um status badge (distintivo/indicador de status) para o nosso build. Isso é uma imagem que indica se o build passou ou não. Podemos adicionar isso no nosso README.md
seguindo esse padrão de URL:
URL
![](https://github.com/<CONTA>/<REPOSITORIO>/workflows/<NOME_WORKFLOW>/badge.svg)
Exemplo do meu repositório:
![](https://github.com/klauskpm/changelog-cicd/workflows/Gerador%20de%20CHANGELOG/badge.svg)
Sendo klauskpm
meu usuário, changelog-cicd
o repositório que estou usando, e Gerador de CHANGELOG
o nome do workflow, não o do arquivo, com %20
em vez de espaços.
Assim que sua badge será exibida no seu README.md
:
Pode comparar o seu progresso com a tag v2.1.3 do repositório
Conclusão
Com esse artigo nós:
- Melhoramos a qualidade dos commits
- Automatizamos a geração de CHANGELOG e subida de versão
- Fizemos integração contínua com GitHub Actions
Caso algo tenha dado errado, pode sempre consultar o repositório com a versão final e um passo-a-passo super resumido desse artigo.
klauskpm / changelog-cicd
Repositório para ensinar a criar CHANGELOG automaticamente
changelog-cicd
Repositório com passo-a-passo de como gerar um CHANGELOG automaticamente.
Esse passo-a-passo é a versão muito resumida do artigo: Como gerar CHANGELOG automaticamente
Gerando um CHANGELOG automaticamente
1) Instale as dependências
npm install --global commitizen
# dentro do seu projeto
npm install --save-dev husky @commitlint/cli @commitlint/config-conventional standard-version
2) Configure o commitizen
# dentro do seu projeto
commitizen init cz-conventional-changelog --save-dev --save-exact
3) Crie o arquivo commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional']
};
4) Altere o seu package.json
{
...,
// opcional
"repository": {
"url": "git@gitlab.com:meu-usuario/meu-repo.git"
},
...,
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"scripts": {
"release": "standard-version"
}
}
5) Crie o arquivo .versionrc
{
"types": [
{"type": "feat", "section": "Funcionalidades"}
{"type": "fix"
…Não deixe de me seguir caso queira receber mais artigos como esse, e compartilhar com seus colegas caso tenha ❤️. Não deixe de comentar se tiver algo que acha que eu deva mudar no artigo.
Um forte abraço e te vejo na próxima 😬
Top comments (2)
ola, se eu tiver uma branch dev e fazer o merge para a master ele ira fazer esta processo?
e se na mensagem do merge tem que haver os padroes da mensagens como feat fix etc??
vlw
Ola o/
Se usar esse processo, que é de
push
na master, ele deve ativar no merge sim.O merge em si não precisa ter esses padrões nas mensagens. Se ele não tiver, ele só vai ser ignorado no changelog.
Se quiser fazer um merge usando squash, recomendo colocar esses padrões na mensagem do merge, pois você vai perder todas as outras mensagens e não teria nada para gerar no novo changelog, só uma mudança de versões.