Cenário
Venho trabalhando na criação de uma biblioteca para componentizar um design system e durante este processo, senti a necessidade de automatizar a atualização de versão para evitar precisar fazer isso manualmente toda vez.
Pensei em criar algo que pudesse pelo menos semi-automatizar os incrementos de cada seção da versão, assim, toda vez que houvesse um merge na branch master, a versão seria incrementada. Já que normalmente a master é a linha do tempo estável, para produção.
Contexto
No GitFlow a master pode ser atualizada de duas maneiras: a primeira é pelo fechamento de uma release. A outra, pelo fechamento de uma hotfix.
Normalmente, o que é tratado em uma hotfix
tende a ser ajustes pontuais ou correções de bugs, sendo assim, atualizar apenas o patch está de bom tamanho.
Já para o que vem de uma release é um conteúdo mais substancial, então eu queria a possibilidade de escolher se eu atualizo a major, minor ou patch da nova versão.
Abordagem
Pensei em criar um script para rodar na linha de comando para lidar com estes casos, e aliar isso aos hooks do git acaba sendo bastante pertinente.
O git hook
Para o hook, dado o contexto, faz sentido usar o de post-merge
, que promete ser invocado sempre que um merge é realizado com sucesso. Contei com a ajuda da lib husky
para lidar melhor com os hooks.
#!/bin/bash
current_branch=$(git rev-parse --abbrev-ref HEAD)
commit_message=$(git log -1 --pretty=%B)
if [ "$current_branch" == "master" ]; then
npm test
if [[ "$commit_message" == *"release/"* ]]; then
start node bump.js ; exit
elif [[ "$commit_message" == *"hotfix/"* ]]; then
npm version patch
fi
fi
O comando git rev-parse --abbrev-ref HEAD
nos diz qual é a branch atual e o git log -1 --pretty=%B
recupera qual foi a última mensagem de commit.
No GitFlow, sempre quando mergeamos uma branch, uma mensagem de commit é aplicada no modelo Merge branch release/nome_da_release, o mesmo acontece com a hotfix. Por isso recuperar a última mensagem de commit é importante, é nela que terá nosso fator condicional.
Um dos requisitos é que funcionasse apenas na branch master o que explica a primeira condicional, uma vez que estamos na master, é verificado se o ultimo commit vem de uma release ou hotflix, como está no trecho acima.
O script de escolha
Essa é a parte do texto onde confesso que nunca tinha feito um CLI antes. Não sabia por onde começar. Foi então que lembrei que o CLI do Vite é bem legalzinho e fui ver o que eles usam por lá.
Para minha felicidade, é feito em JavaScript. Usando um conjuntos dos pacotes cross-spawn
, prompts
e kolorist
-
cross-spawn
: para rodar comandos de linha de comando. -
prompts
: para criar prompts interativos e inquerir informações do usuário -
kolorist
: adiciona cores aos prompts
//./bump.js
import spawn from 'cross-spawn';
import prompts from "prompts";
import pkg from "./package.json" assert { type: 'json' };
import {
cyan,
green,
magenta,
yellow
} from 'kolorist';
(async () => {
const current_major = Number(pkg.version[0])
const current_minor = Number(pkg.version[2])
const current_patch = Number(pkg.version[4])
const choices = [
{
title: green(`Major`),
description: `${pkg.version} ➡️ ${`${current_major + 1}.0.0`}`,
value: 'npm version major',
},
{
title: cyan(`Minor`),
description: `${pkg.version} ➡️ ${current_major}.${current_minor + 1}.0`,
value: 'npm version minor',
},
{
title: yellow("Patch"),
description: `${pkg.version} ➡️ ${current_major}.${current_minor}.${current_patch + 1}`,
value: 'npm version patch',
}
]
const response = await prompts({
type: 'select',
name: 'value',
message: `Você acabou de realizar o fechamento de uma release.
Para que o fluxo possa continuar, atualize a versão da lib ${magenta(pkg.name)}.
Qual das opções abaixo mais faz sentido para as alterações presentes nesta release?
Lembrando:
${green('MAJOR')}: é a versão que contém mudanças incopatíveis, breaking changes.
${cyan('MINOR')}: é a versão que adiciona funcionalidades, com campatibilidade.
${yellow('PATCH')}: é a versão que adiciona ajustes gerais ou de bugs, mantendo compatibilidade.
Escolha 👇
`,
initial: 2,
choices
});
const { status } = spawn.sync(response.value, [], {
stdio: 'inherit',
})
process.exit(status ?? 0)
})();
No código, a ideia foi pegar a versão atual importando o package.json e em cima disso, calcular as possíveis novas versões para major, minor e patch e transformar isso em escolhas para o usuário. Dependendo da escolha, o comando npm é rodado para atualizar a versão.
Com isso, o objetivo foi atingido. agora, sempre que a master for alimentada pela hotfix ou release, a versão será incrementada para patch ou abrirá o terminal para escolha dependendo da qualidade do conteúdo da release, a escolha do usuário.
Top comments (0)