Pular para conteúdo

git-cliff e Geração Automática de CHANGELOG

O que é o git-cliff

git-cliff é um gerador de CHANGELOG escrito em Rust que analisa o histórico de commits git e transforma mensagens no formato Conventional Commits em um arquivo CHANGELOG.md estruturado. No Vox AI, ele é invocado via a GitHub Action orhun/git-cliff-action@v4 dentro do workflow production_pipeline.yml.

O Arquivo cliff.toml — Configuração Completa

O cliff.toml na raiz do repositório define como os commits são classificados, filtrados e renderizados no CHANGELOG.

Seção [changelog]:

[changelog]
# Template Tera (Jinja2-like) para o corpo do CHANGELOG
body = """
{% if version %}\
# [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%d/%m/%Y") }}
{% else %}\
# [Não Lançado]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
## {{ group | upper_first }}
{% for commit in commits %}
- {% if commit.scope %}**{{ commit.scope }}**: {% endif %}{{ commit.message | upper_first }}\
{% if commit.breaking %} [BREAKING]{% endif %} \
([{{ commit.id | truncate(length=7, end="") }}]({{ remote.url }}/commit/{{ commit.id }}))\
{% endfor %}
{% endfor %}\n
"""

# Adiciona as mudanças do commit mais recente ao topo do arquivo
prepend = "CHANGELOG.md"

# Não inclui commits cujas mensagens sejam apenas o número da versão
trim = true

Seção [git]:

[git]
# Analisa apenas commits com mensagens no formato Conventional Commits
conventional_commits = true

# Ignora commits que não seguem a convenção (não bloqueia, apenas descarta)
filter_unconventional = true

# Ordena commits por data dentro de cada grupo
sort_commits = "newest"

# Padrão de tags consideradas como releases
tag_pattern = "v[0-9].*"

# Commits a IGNORAR (não aparecem no CHANGELOG):
ignore_tags = "dev-v.*"   # Tags de desenvolvimento (prefixo dev-v*)
skip_tags = "^$"

# Mapeamento tipo → grupo no CHANGELOG:
commit_parsers = [
  { message = "^feat",     group = "Funcionalidades"  },
  { message = "^fix",      group = "Correções de Bug" },
  { message = "^docs",     group = "Documentação"     },
  { message = "^perf",     group = "Performance"      },
  { message = "^refactor", skip = true                },
  { message = "^style",    skip = true                },
  { message = "^test",     skip = true                },
  { message = "^ci",       skip = true                },
  { message = "^build",    skip = true                },
  { message = "^chore",    skip = true                },
  { message = "\\[skip ci\\]", skip = true            },
]

Fluxo de Geração no production_pipeline.yml

A geração do CHANGELOG ocorre no Job 2 (release_prod) do pipeline de produção, executado apenas após os testes passarem. O fluxo completo:

Etapa 1 — Cálculo da próxima versão:

# Busca a última tag v* (release de produção)
LAST_TAG=$(git tag --list 'v*' --sort=-version:refname | head -n 1)
# Resultado exemplo: v3.2.14

# Extrai MAJOR, MINOR e PATCH
IFS='.' read -r MAJOR MINOR PATCH <<< "${LAST_TAG#v}"
# MAJOR=3, MINOR=2, PATCH=14

# Incrementa PATCH (estratégia padrão do pipeline automático)
NEW_PATCH=$((PATCH + 1))
NEW_VERSION="v${MAJOR}.${MINOR}.${NEW_PATCH}"
# Resultado: v3.2.15

Para incrementar MINOR ou MAJOR, usa-se o workflow manual_release.yml.

Etapa 2 — Invocação do git-cliff:

- name: "📝 Gerar CHANGELOG"
  uses: orhun/git-cliff-action@v4
  with:
    config: cliff.toml
    args: --verbose --tag ${{ env.NEW_VERSION }}
  env:
    OUTPUT: CHANGELOG.md
    GITHUB_REPO: ${{ github.repository }}

O git-cliff analisa todos os commits desde a tag anterior (LAST_TAG..HEAD), filtra pelos commit_parsers do cliff.toml, e renderiza o template Tera, adicionando a nova seção no topo do CHANGELOG.md existente.

Etapa 3 — Commit e tag com [skip ci]:

git config user.name  "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

git add CHANGELOG.md
git commit -m "chore(release): $NEW_VERSION [skip ci]"

git tag "$NEW_VERSION"
git push origin main --follow-tags

A flag [skip ci] na mensagem do commit é reconhecida pelo commit_parser do cliff.toml (regra skip = true) E também pelo próprio production_pipeline.yml via filtro de path:

on:
  push:
    branches: [main]
    paths-ignore:
      - 'CHANGELOG.md'

Isso cria um duplo mecanismo anti-loop: o commit do CHANGELOG não reaparece no histórico do próximo CHANGELOG, e o workflow não é re-acionado.

Estratégia de Versioning por Branch

Branch Padrão de Tag Gerador Trigger
main v{MAJOR}.{MINOR}.{PATCH} production_pipeline.yml Automático em todo push
develop dev-v{MAJOR}.{MINOR}.{PATCH} versioning_dev.yml Automático em todo push
Qualquer v{X}.{Y}.{Z} manual_release.yml Manual via workflow_dispatch

As tags dev-v* são ignoradas pelo git-cliff (ignore_tags = "dev-v.*"), então nunca aparecem no CHANGELOG de produção.

Exemplo de Saída do CHANGELOG

# [3.2.15] - 21/04/2026

## Funcionalidades
- **tts**: Adiciona botão de conversão de texto em áudio na resposta ([a1b2c3d](https://github.com/emn-f/vox-ai/commit/a1b2c3d...))
- Suporte a upload de arquivos PDF na base de conhecimento ([7f8e9d0](https://github.com/emn-f/vox-ai/commit/7f8e9d0...))

## Correções de Bug
- **ui**: Corrige crash da sidebar no mobile com texto longo ([2c3d4e5](https://github.com/emn-f/vox-ai/commit/2c3d4e5...))

## Documentação
- Atualiza README com instruções de setup local ([9a8b7c6](https://github.com/emn-f/vox-ai/commit/9a8b7c6...))