Releases Go avec GoReleaser
GoReleaser génère changelog, binaires multi-arch, archives, images Docker et charts Helm depuis un fichier de config. Tout se déclenche sur git push --tags.
Avant le release, le CI
GoReleaser intervient une fois le tag poussé. Ce qui tourne avant, sur chaque push et PR (test, lint, scan de vulnérabilités, durcissement des workflows), est détaillé dans CI GitHub Actions durcie pour un projet Go.
Flow
Installation
# macOS
brew install goreleaser
# Linux (binaire direct)
curl -sfL https://goreleaser.com/static/run | bash
Configuration de base
version: 2
before:
hooks:
- go mod tidy
- go mod verify
builds:
- id: mon-binaire
binary: mon-binaire
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
ignore:
- goos: windows
goarch: arm64
flags:
- -trimpath
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.commit={{.Commit}}
- -X main.date={{.Date}}
archives:
- formats: [tar.gz]
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
format_overrides:
- goos: windows
formats: [zip]
files:
- README.md
- LICENSE*
checksum:
name_template: checksums.txt
release:
draft: false
prerelease: auto
Réduire la taille des binaires
Trois flags suffisent pour gagner ~33% :
| Flag | Effet |
|---|---|
-s | Supprime la table des symboles |
-w | Supprime les infos de debug DWARF |
-trimpath | Supprime les chemins locaux embarqués dans le binaire |
CGO_ENABLED=0 | Désactive CGO → binaire statique pur |
Résultat mesuré sur gopen : 3,0 Mo → 2,0 Mo (-33%).
Compression UPX (Linux uniquement)
UPX compresse le binaire ; il se décompresse au lancement.
Gains mesurés sur la layer binaire de kubearch (ghcr.io/pixibixi/kubearch) :
| Arch | Sans UPX (v0.2.0) | Avec UPX (v0.3.1) | Gain |
|---|---|---|---|
| amd64 | 14,91 MB | 8,34 MB | -44% |
| arm64 | — | 6,61 MB | — |
UPX et macOS/Windows
Sur macOS, UPX casse la notarisation Apple. Sur Windows, les antivirus le signalent. À limiter à goos: [linux].
Signature des artefacts (cosign)
Cosign signe en mode keyless, sans clé à gérer. L'identité vient de l'OIDC GitHub Actions ; chaque signature est enregistrée dans le transparency log de Sigstore.
.goreleaser.yml
signs:
- cmd: cosign
signature: "${artifact}.sigstore.json"
args:
- sign-blob
- "--bundle=${signature}"
- "${artifact}"
- "--yes"
artifacts: checksum # signe checksums.txt, qui couvre tous les binaires/archives
docker_signs:
- cmd: cosign
args:
- sign
- "--yes"
- "${artifact}"
artifacts: manifests # signe les manifests multi-arch, pas les images individuelles
Pourquoi signer le checksum plutôt que chaque archive ?
checksums.txt contient les SHA256 de tous les artefacts. Signer ce fichier couvre tout : on vérifie la signature une fois, puis le SHA256 de chaque archive.
GitHub Actions — permissions requises
Deux ajouts par rapport au workflow de base :
permissions:
contents: write
packages: write
id-token: write # requis pour la signature OIDC keyless
Vérifier les artefacts (côté utilisateur)
# 1. Vérifier la signature du checksum
cosign verify-blob \
--certificate-identity 'https://github.com/monorg/mon-repo/.github/workflows/release.yml@refs/tags/vX.Y.Z' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
--bundle checksums.txt.sigstore.json \
checksums.txt
# 2. Vérifier l'intégrité des binaires
sha256sum --ignore-missing -c checksums.txt
# 3. Vérifier l'image Docker
cosign verify \
--certificate-identity 'https://github.com/monorg/mon-repo/.github/workflows/release.yml@refs/tags/vX.Y.Z' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
ghcr.io/monorg/mon-image:vX.Y.Z
Images OCI multi-arch (ko)
ko est un builder d'images OCI natif Go : il cross-compile le binaire et l'emballe dans une image distroless sans Dockerfile, sans Docker daemon, sans buildx. GoReleaser v2 l'intègre nativement via la clé kos:.
kos:
- id: mon-binaire
repositories:
- ghcr.io/monorg/mon-image
base_image: gcr.io/distroless/static:nonroot
platforms:
- linux/amd64
- linux/arm64
tags:
- "{{ .Version }}" # 1.2.3
- "{{ .Major }}.{{ .Minor }}" # 1.2
- "{{ .Major }}" # 1
- '{{ if not .Prerelease }}latest{{ end }}'
bare: true # ghcr.io/monorg/mon-image:1.2.3 (sans suffixe binaire)
Ko génère automatiquement un manifest multi-arch et publie des SBOMs par image. Il utilise le GITHUB_TOKEN pour s'authentifier sur ghcr.io — aucun docker login ni step buildx requis dans le workflow CI.
Pas de Dockerfile nécessaire
Pour un binaire Go pur (CGO_ENABLED=0), ko remplace entièrement le duo Dockerfile.release + docker_manifests. Pour les builds locaux de développement (avec un vrai Dockerfile multi-stage), on peut garder un Dockerfile séparé.
Visibilité GHCR
La première image publiée est privée par défaut sur GHCR, même pour un repo public. À rendre public manuellement : GitHub → Packages → mon-image → Package settings → Change visibility.
Helm chart vers OCI
GoReleaser ne gère pas nativement le push Helm, mais on l'ajoute en step post-GoReleaser dans le workflow :
- name: Push Helm chart to OCI
run: |
CHART_VERSION="${GITHUB_REF_NAME#v}"
helm registry login ghcr.io \
--username "${{ github.repository_owner }}" \
--password "${{ secrets.GITHUB_TOKEN }}"
helm package charts/mon-chart \
--version "$CHART_VERSION" \
--app-version "$CHART_VERSION"
helm push "mon-chart-${CHART_VERSION}.tgz" oci://ghcr.io/monorg/charts
GitHub Actions
name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
packages: write # push vers ghcr.io
id-token: write # signature cosign keyless
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # GoReleaser a besoin de l'historique complet pour le changelog
- uses: actions/setup-go@v5
with:
go-version: stable
- name: Install cosign
uses: sigstore/cosign-installer@v3
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
version: '~> v2'
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
Sans fetch-depth: 0, GitHub Actions fait un clone superficiel. GoReleaser ne peut pas générer le changelog car il n'a pas accès aux commits précédents.
Commandes utiles
# Valider la config sans déclencher de release
goreleaser check
# Tester localement (snapshot = pas de tag requis, pas de push)
goreleaser release --snapshot --clean
# Release manuelle (si pas de CI)
git tag -a v1.2.3 -m "Release v1.2.3"
git push origin v1.2.3
Changelog
GoReleaser génère le changelog entre deux tags. On peut filtrer et regrouper :