Releases automatiques avec cocogitto
Cocogitto valide les conventional commits, bumpe le semver automatiquement et génère un CHANGELOG. Tout se déclenche sur git push vers master, sans PR intermédiaire.
Cocogitto vs release-please
Les deux outils s'appuient sur les Conventional Commits et automatisent le versioning. Ils ont des philosophies opposées.
| cocogitto | release-please | |
|---|---|---|
| Déclencheur | Push direct sur master | Merge d'une Release PR |
| Validation locale | cog check, hook pre-commit | Aucune |
| Bump | Immédiat au push | À la merge de la PR |
| CHANGELOG | Format cog (- - -) | Keep a Changelog |
| GitHub Release | gh release create à ajouter | Créée automatiquement |
| Config | cog.toml | release-please-config.json |
| Mainteneur | Communauté |
En pratique, les deux flows divergent dès le premier push :
Release-please accumule les commits dans une PR ; la release n'a lieu que si quelqu'un merge manuellement, c'est un gate humain intégré. Cocogitto bumpe au push : zéro friction, mais zéro gate, il faut de la discipline sur les branches.
Bumps à répétition
Pousser plusieurs commits fix: directement sur master = autant de patch bumps. Travailler sur une branche feature et merger en une fois produit un seul bump.
Installation
# macOS
brew install cocogitto
# Linux (binaire direct)
curl -L https://github.com/cocogitto/cocogitto/releases/latest/download/cocogitto-x86_64-unknown-linux-musl.tar.gz \
| tar xz -C ~/.local/bin
Configuration
ignore_merge_commits = true
tag_prefix = "v" # obligatoire si vos tags existants sont en v*
[commit_types]
# Types custom pour le changelog
refacto = { changelog_title = "Refactoring" }
[changelog]
path = "CHANGELOG.md"
template = "remote"
remote = "github.com"
repository = "mon-repo"
owner = "mon-org"
authors = []
tag_prefix
Sans tag_prefix = "v", cocogitto cherche des tags 1.2.3 (sans v). Si vos tags existants sont v1.2.3 (format release-please, goreleaser…), ce champ est obligatoire.
CHANGELOG.md
Cocogitto requiert un séparateur - - - pour savoir où insérer chaque nouvelle section. Le fichier minimal :
À chaque bump, cog insère le nouveau bloc entre le header et le - - - précédent :
# Changelog
- - -
## [v1.3.0](https://github.com/...) - 2026-05-11
#### Features
- (**api**) add pagination endpoint - ([abc1234](...)) - John Doe
- - -
## [v1.2.0](https://github.com/...) - 2026-05-01
...
Migration depuis release-please
Le CHANGELOG généré par release-please n'a pas de - - -. Il faut en ajouter un manuellement avant le premier cog bump sinon l'erreur cannot find default separator bloque le CI.
Validation locale
# Valider tous les commits depuis le dernier tag
cog check
# Valider uniquement depuis le dernier tag (plus rapide)
cog check --from-latest-tag
# Voir ce que le prochain bump produirait
cog bump --auto --dry-run
# Bumper manuellement
cog bump --patch # 1.2.3 → 1.2.4
cog bump --minor # 1.2.3 → 1.3.0
cog bump --major # 1.2.3 → 2.0.0
Hook pre-commit
Cog peut valider le message de commit avant qu'il soit enregistré :
repos:
- repo: local
hooks:
- id: cog-verify
name: Conventional commit check
language: system
entry: cog verify
stages: [commit-msg]
args: ["--file", ".git/COMMIT_EDITMSG"]
GitHub Actions
.github/workflows/release.yml
name: Release
on:
push:
branches: [master]
permissions:
contents: write
packages: write
jobs:
release:
runs-on: ubuntu-latest
outputs:
bumped: ${{ steps.bump.outputs.bumped }}
tag_name: ${{ steps.bump.outputs.tag_name }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # obligatoire : cog a besoin de tout l'historique
token: ${{ secrets.GITHUB_TOKEN }}
- uses: oknozor/cocogitto-action@v3
with:
git-user: github-actions[bot]
git-user-email: github-actions[bot]@users.noreply.github.com
check-latest-tag-only: true # ne valide que les commits depuis le dernier tag
- name: Bump version
id: bump
run: |
BEFORE=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
cog bump --auto || true
AFTER=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ "$BEFORE" != "$AFTER" ]; then
git push
git push origin "$AFTER"
echo "bumped=true" >> "$GITHUB_OUTPUT"
echo "tag_name=$AFTER" >> "$GITHUB_OUTPUT"
else
echo "bumped=false" >> "$GITHUB_OUTPUT"
fi
- name: Create GitHub release
if: steps.bump.outputs.bumped == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="${{ steps.bump.outputs.tag_name }}"
NOTES=$(awk "/^## .*${TAG#v}/{found=1; next} found && /^- - -/{exit} found{print}" CHANGELOG.md)
gh release create "$TAG" \
--title "$TAG" \
--notes "${NOTES:-Release $TAG}" \
--verify-tag
build-push:
needs: release
if: needs.release.outputs.bumped == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.release.outputs.tag_name }}
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}},value=${{ needs.release.outputs.tag_name }}
type=semver,pattern={{major}}.{{minor}},value=${{ needs.release.outputs.tag_name }}
type=raw,value=latest # non généré automatiquement hors push de tag natif
type=sha,prefix=sha-
- uses: docker/build-push-action@v6
with:
push: true
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
tag latest non automatique
docker/metadata-action ne génère latest automatiquement que sur un push de tag GitHub (on: push: tags: ['v*']). Ici le trigger est un push de branche, donc il faut type=raw,value=latest explicitement.
Logique de bump
| Commit | Bump |
|---|---|
fix: | patch (1.2.3 → 1.2.4) |
feat: | minor (1.2.3 → 1.3.0) |
feat!: ou BREAKING CHANGE | major (1.2.3 → 2.0.0) |
chore:, docs:, ci: | aucun bump |
Quand choisir quoi
Cocogitto si :
- Projet solo ou petite équipe, merge direct sur main
- Tu veux valider les commits localement avant push
- Tu veux des releases immédiates sans étape manuelle
release-please si :
- Équipe avec review obligatoire avant release
- Tu veux un gate humain explicite sur chaque release
- Tu travailles déjà avec les PR GitHub comme unité de travail