Semantic versioning com Git tags automatizadas

1. Fundamentos do Semantic Versioning (SemVer)

O Semantic Versioning (SemVer) é um padrão de versionamento que comunica o impacto de mudanças em software através de uma estrutura numérica de três partes: MAJOR.MINOR.PATCH. Cada componente segue regras rígidas de incremento:

  • MAJOR: incrementado quando há mudanças incompatíveis com versões anteriores (breaking changes)
  • MINOR: incrementado quando novas funcionalidades são adicionadas de forma compatível (novas features)
  • PATCH: incrementado quando correções de bugs são feitas sem quebrar compatibilidade

Além disso, o SemVer permite sufixos opcionais para pré-release e metadados de build:

1.0.0-alpha.1+build.123

Onde -alpha.1 indica uma versão de pré-lançamento e +build.123 carrega metadados de compilação. Esses sufixos são ignorados na ordenação de versões estáveis.

2. Git Tags como Marcadores de Versão

Git tags são referências imutáveis que apontam para commits específicos. Existem dois tipos principais:

Lightweight tags: apenas um ponteiro para um commit, sem metadados adicionais.

Annotated tags: armazenam autor, data, mensagem e podem ser assinadas com GPG. São recomendadas para releases por serem objetos completos no repositório.

Criando e listando tags manualmente:

# Criar uma annotated tag
git tag -a v1.0.0 -m "Release v1.0.0"

# Listar tags existentes
git tag -l "v*"

# Compartilhar tags com o repositório remoto
git push origin v1.0.0

# Ou enviar todas as tags de uma vez
git push --tags

Boas práticas de nomenclatura incluem prefixo v (ex: v2.3.1) e consistência com o padrão SemVer.

3. Automação com Hooks e Scripts Locais

Hooks do Git permitem automatizar o versionamento localmente. Um script pós-commit pode extrair a mensagem do commit e decidir qual versão gerar.

Exemplo de script shell para incremento automático baseado no último commit:

#!/bin/bash
# post-commit hook - incrementa versão baseada no tipo de commit

LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
VERSION=${LAST_TAG#v}

IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"

COMMIT_MSG=$(git log -1 --pretty=%B)

if echo "$COMMIT_MSG" | grep -qi "BREAKING CHANGE"; then
    MAJOR=$((MAJOR + 1))
    MINOR=0
    PATCH=0
elif echo "$COMMIT_MSG" | grep -qi "^feat"; then
    MINOR=$((MINOR + 1))
    PATCH=0
elif echo "$COMMIT_MSG" | grep -qi "^fix"; then
    PATCH=$((PATCH + 1))
else
    exit 0
fi

NEW_TAG="v$MAJOR.$MINOR.$PATCH"
git tag -a "$NEW_TAG" -m "Release $NEW_TAG"

O comando git describe é particularmente útil para gerar versões baseadas em tags existentes:

git describe --tags --always --dirty
# Exemplo de saída: v1.2.3-5-gabc1234

Isso retorna a tag mais recente, o número de commits desde ela, o hash do commit e um sufixo -dirty se houver mudanças não commitadas.

4. Ferramentas de Automação de Versionamento

Ferramentas como standard-version e semantic-release automatizam o versionamento analisando mensagens de commit no padrão Conventional Commits.

Standard-version (Node.js):

npm install -g standard-version

# Executa análise de commits e gera nova tag automaticamente
standard-version

Semantic-release (mais complexo, integrado a CI/CD):

# Configuração no package.json
{
  "release": {
    "branches": ["main"],
    "plugins": [
      "@semantic-release/commit-analyzer",
      "@semantic-release/release-notes-generator",
      "@semantic-release/npm",
      "@semantic-release/github"
    ]
  }
}

As regras de bump seguem o padrão:

  • fix: → incrementa PATCH
  • feat: → incrementa MINOR
  • BREAKING CHANGE: no corpo do commit → incrementa MAJOR

Ambas as ferramentas geram automaticamente tags e changelogs, eliminando erros manuais.

5. Pipeline CI/CD com Tags Automatizadas

Em ambientes CI/CD, como GitHub Actions, é possível automatizar a criação de tags a partir de pushs em branches específicas.

Exemplo de workflow GitHub Actions:

name: Automated Versioning
on:
  push:
    branches: [main]

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install dependencies
        run: npm ci

      - name: Create Release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          git config user.name "github-actions"
          git config user.email "actions@github.com"
          npx semantic-release

Estratégias para evitar duplicação de tags:

  • Utilizar fetch-depth: 0 para histórico completo
  • Verificar se a tag já existe antes de criar
  • Usar tokens de acesso com permissões adequadas
  • Implementar locks ou verificações de concorrência em pipelines paralelos

6. Gerenciamento de Versões em Repositórios Monorepo

Monorepos exigem versionamento independente por pacote. Tags prefixadas são a abordagem padrão:

@scope/package@1.2.3

Ferramentas especializadas facilitam esse processo:

Lerna:

lerna version --conventional-commits

Changesets (mais moderno):

npx changeset
npx changeset version
npx changeset tag

Ambas analisam mudanças em subdiretórios e geram tags múltiplas automaticamente. O versionamento atômico garante que todos os pacotes afetados sejam atualizados consistentemente.

7. Boas Práticas e Troubleshooting

Versionamento em branches de release vs. trunk-based:

Em trunk-based development, tags são criadas diretamente na branch principal após merge. Em branches de release, tags são criadas ao final do ciclo de estabilização.

Corrigindo tags incorretas:

# Remover tag local
git tag -d v1.0.0

# Remover tag remota
git push --delete origin v1.0.0

# Recriar a tag correta
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0

Segurança com GPG:

Tags assinadas garantem autenticidade:

git tag -s v1.0.0 -m "Release v1.0.0"
git tag -v v1.0.0

Em pipelines, configure a verificação de assinatura:

git verify-tag v1.0.0

Isso impede que tags não autorizadas sejam usadas em releases oficiais, aumentando a segurança do processo.

Referências