tenequm/go-dev

Configuration Go opinionée avec golangci-lint v2, gofumpt, gotestsum, golang-migrate et just. À utiliser pour démarrer un projet Go, configurer le lint, le formatage, les tests, la couverture ou le CI, rédiger un Justfile, câbler des migrations, ou quitter un workflow Makefile.

Qu'est-ce que go-dev ?

Ce skill fournit une pile de développement Go moderne et opinionée, organisée autour d'un outil par responsabilité sans chevauchement. Il est conçu pour les développeurs qui démarrent un nouveau projet Go, ajoutent une infrastructure de lint/formatage/tests, mettent en place du CI/CD, ou remplacent un Makefile par un Justfile. Le stack inclut Go 1.27+, golangci-lint v2.14+ (méta-linter avec 100+ linters), gofumpt v0.12+ (formateur strict), gotestsum v1.13+ (exécuteur de tests avec sortie lisible, mode watch, JUnit XML), just 1.58+ (lanceur de tâches), golang-migrate v4.20+ (migrations de base de données) et lefthook v2.1+ (hooks Git). Le skill insiste sur le versionnage explicite : les versions minimales sont contraignantes et doivent correspondre à la toolchain Go utilisée. Il fournit des configurations prêtes à l'emploi (.golangci.yml, Justfile, lefthook.yml) ainsi qu'une procédure de démarrage rapide, et met en garde contre les pièges comme l'installation de golangci-lint via le pattern d'outils Go ou l'utilisation de `@latest`. Il s'intègre avec Claude Code, Cursor, Codex et d'autres agents IA capables d'exécuter des tâches ou de générer du code Go.

Compatible avec✓Claude Code✓Codex CLI✓Cursor
npx skills add https://github.com/tenequm/skills/tree/HEAD/skills/go-dev

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Pile de développement Go

Configuration Go moderne et opinionée. Un outil par préoccupation, zéro chevauchement.

Quand l'utiliser

  • Démarrer un nouveau projet Go à partir de zéro
  • Ajouter une infrastructure de lint, formatage ou tests
  • Mettre en place du CI/CD pour un service ou une bibliothèque Go
  • Créer un Justfile pour remplacer un Makefile
  • Ajouter des outils de migration de base de données
  • Migrer d'invocations dispersées de gofmt/govet/staticcheck vers une configuration unifiée

Pas pour un fork qui fusionne régulièrement avec l'amont : remplacer son Makefile et reformater l'arborescence avec gofumpt entre en conflit à chaque fusion et enterre les vrais changements dans un diff de reformatage. Conservez le gofmt et les outils de construction de l'amont, et ajoutez uniquement des vérifications.

La pile

OutilVersionRôleRemplace
Go1.27+Langage, chaîne d'outils, go mod, go fix-
golangci-lintv2.14+Méta-linter (100+ linters + formateurs + commande fmt)gofmt, govet, staticcheck, errcheck exécutés séparément
gofumptv0.12+Formateur strict (sur-ensemble de gofmt, 19 règles par défaut)gofmt
gotestsumv1.13+Exécuteur de tests avec sortie lisible, mode watch, XML JUnitgo test brut
just1.58+Lanceur de tâchesMakefile
golang-migratev4.20+Migrations de base de données (CLI + bibliothèque + embed.FS)Scripts SQL manuels
lefthookv2.1+Hooks Git (binaire unique, parallèle)pre-commit (Python)

Les versions minimales sont contraignantes. golangci-lint « prend en charge les versions de Go inférieures ou égales à la version de Go utilisée pour le compiler » - une version antérieure à votre chaîne d'outils Go échoue complètement. La prise en charge de Go 1.27 est arrivée dans golangci-lint v2.13.0, donc v2.13 est le minimum pour un projet Go 1.27 ; ce skill fixe v2.14.0, la première version qui intègre gofumpt v0.12.0. Deux autres versions minimales ont récemment bougé : gofumpt v0.12.0 « est basé sur gofmt de Go 1.27 et nécessite Go 1.26 ou plus récent », et le chemin d'installation go install de lefthook nécessite maintenant Go 1.26+.

Démarrage rapide : Nouveau projet

# 1. Créer le module
mkdir myapp && cd myapp
go mod init github.com/yourorg/myapp

# 2. Créer les répertoires
mkdir -p cmd/myapp internal migrations

# 3. Installer golangci-lint en tant que binaire, pas en tant qu'outil de module (voir note ci-dessous)
curl -sSfL https://golangci-lint.run/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.14.0

# 4. Enregistrer le reste dans go.mod (directive d'outil Go 1.24+). Versionnez explicitement - jamais @latest,
#    qui recompile l'outil à chaque exécution CI et dérive entre les machines.
go get -tool mvdan.cc/[email protected]
go get -tool gotest.tools/[email protected]

# golang-migrate nécessite une balise de construction, donc installez-le directement
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/[email protected]

# 5. Créer les fichiers de configuration (modèles ci-dessous)
# 6. Exécuter : just check

N'installez pas golangci-lint via le modèle d'outils. L'amont est explicite : « L'utilisation de go install/go get, du "modèle d'outils", et des commandes/directives tool n'est pas garantie de fonctionner. Nous recommandons une installation binaire. » La raison pour laquelle cela compte dans un dépôt partagé est la contamination des dépendances - « les dépendances d'un outil peuvent modifier les dépendances d'un autre outil ou de votre projet ». Si vous devez l'avoir dans go.mod, isolez-le derrière son propre -modfile - voir la Référence golangci-lint.

go get -tool trace ; go tool exécute. La directive d'outil enregistre la dépendance dans go.mod mais ne met rien dans votre PATH. Invoquez soit via la chaîne d'outils - go tool gofumpt -l ., go tool gotestsum --format testname - soit go install tool une fois pour remplir $(go env GOPATH)/bin. Le Justfile ci-dessous appelle les binaires nus, donc il suppose la route go install tool (ou une installation système via Homebrew - mais « Homebrew peut utiliser une version inattendue de Go pour construire le binaire » pour golangci-lint, et il ne peut pas épingler une version). Notez que go tool résout par rapport au module dans le répertoire courant - « des outils supplémentaires peuvent être définis dans le go.mod du module courant » - donc dans un monorepo, il échoue avec go: no such tool "..." sauf si la recette définit [working-directory(...)].

Deux comportements de la commande Go à connaître avant le premier commit :

  • go mod init sous une chaîne d'outils 1.N écrit go 1.(N-1).0, pas 1.N - « Exécuter go mod init en utilisant une chaîne d'outils de version 1.N.X créera un fichier go.mod spécifiant la version de Go go 1.(N-1).0. » Augmentez la directive délibérément si vous voulez des fonctionnalités de langage 1.N.
  • Définissez une ligne toolchain go1.27.1 dans go.mod, mais sachez qu'il s'agit d'un minimum, pas d'un verrouillage : sous la valeur par défaut GOTOOLCHAIN=auto, la commande go ne change que « si ... <tname> est plus récent que la chaîne d'outils Go par défaut », donc un go local plus récent gagne. Pour une chaîne d'outils exacte, définissez GOTOOLCHAIN=go1.27.1 (CI ou go env -w) ; GODEBUG=toolchaintrace=1 go version montre laquelle a été choisie. Gardez la ligne à la version de correctif actuelle, pas la .0 : elle sélectionne le go contre lequel govulncheck analyse les avis de la bibliothèque standard sur toute machine dont le propre go est plus ancien, donc un correctif obsolète échoue le CI tout seul - voir les Pièges ci-dessous.

.golangci.yml

version: "2"

run:
  timeout: 5m
  build-tags:
    - integration   # sinon les fichiers derrière la balise d'intégration du Justfile ne sont jamais lintés

linters:
  default: standard
  enable:
    - bodyclose
    - copyloopvar
    - dupl
    - durationcheck
    - err113
    - errname
    - errorlint
    - exhaustive
    - exptostd
    - fatcontext
    - goconst
    - gocritic
    - gosec
    - intrange
    - misspell
    - modernize
    - musttag
    - nakedret
    - nestif
    - nilerr
    - noctx
    - nolintlint
    - nonamedreturns
    - perfsprint
    - prealloc
    - revive
    - sqlclosecheck
    - testifylint
    - thelper
    - unconvert
    - unparam
    - usestdlibvars
    - usetesting
    - wastedassign
    - whitespace
    - wrapcheck
  settings:
    govet:
      enable:
        - shadow
    gocritic:
      enabled-checks:
        - nestingReduce
    revive:
      enable-all-rules: true
      rules:
        # enable-all-rules active `unhandled-error`, qui signale `fmt.Println` dans main.
        # Sous enable-all-rules, les `arguments` d'une règle sont ignorés (la règle s'enregistre
        # deux fois), donc une liste d'autorisation ne fonctionne pas ici - seul `disabled` prend effet.
        - name: unhandled-error
          disabled: true
    errcheck:
      check-type-assertions: true
  exclusions:
    generated: strict
    presets:
      - comments
      - std-error-handling
      - common-false-positives
    rules:
      - path: _test\.go
        linters:
          - errcheck
          - dupl
          - gosec
          - wrapcheck

formatters:
  enable:
    - gofumpt
    - goimports
  settings:
    gofumpt:
      # Sélectionnez les règles individuellement. `extra-rules: true` est obsolète, et il active
      # aussi `balance_calls`, que gofumpt lui-même a rétrogradé comme controversé.
      extra:
        group-params: true
        clothe-returns: true
        balance-calls: false
  exclusions:
    generated: strict
    paths:
      - vendor/

output:
  formats:
    text:
      path: stdout
      print-linter-name: true
      colors: true
  sort-order:
    - linter
    - file
  show-stats: true

Justfile

set shell := ["bash", "-euo", "pipefail", "-c"]
set dotenv-load

binary := "myapp"

[private]
default:
    @just --list --unsorted

# ── Qualité du code ──────────────────────────────────────────

# Formater tout le code Go
[group('quality')]
fmt:
    golangci-lint fmt ./...

# Vérifier le formatage sans modifier (sûr pour le CI)
[group('quality')]
fmt-check:
    golangci-lint fmt --diff ./...

# Exécuter le linter
[group('quality')]
lint:
    golangci-lint run ./...

# Exécuter le linter avec correction automatique
[group('quality')]
lint-fix:
    golangci-lint run --fix ./...

# Exécuter la vérification de vulnérabilité
[group('quality')]
vuln:
    govulncheck ./...

# ── Tests ───────────────────────────────────────────────

# Exécuter tous les tests avec détection de course
[group('test')]
test *args="./...":
    gotestsum --format testname -- -race {{ args }}

# Exécuter les tests avec couverture
[group('test')]
test-cov:
    gotestsum --format testname -- -race -coverprofile=coverage.out -covermode=atomic ./...
    go tool cover -func=coverage.out

# Ouvrir le rapport de couverture dans le navigateur
[group('test')]
coverage: test-cov
    go tool cover -html=coverage.out

# Exécuter les tests d'intégration
[group('test')]
test-integration:
    gotestsum --format testname -- -race -tags=integration ./...

# Surveiller les tests pendant le développement
[group('test')]
test-watch:
    gotestsum --watch --watch-clear --format testname

# Exécuter les benchmarks
[group('test')]
bench:
    go test -bench=. -benchmem ./...

# ── Construction ─────────────────────────────────────────────────

# Construire le binaire
[group('build')]
build:
    go build -o {{ binary }} ./cmd/{{ binary }}

# Construire le binaire de release optimisé
[group('build')]
build-release:
    CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o {{ binary }} ./cmd/{{ binary }}

# ── Dépendances ──────────────────────────────────────────

# Nettoyer et vérifier les modules
[group('deps')]
tidy:
    go mod tidy
    go mod verify

# Échouer si go.mod/go.sum ne sont pas propres, sans les toucher (sûr pour le CI)
[group('deps')]
tidy-check:
    go mod tidy -diff

# Exécuter les générateurs de code
[group('deps')]
generate:
    go generate ./...

# ── Base de données ──────────────────────────────────────────────

# Appliquer toutes les migrations en attente
[group('db')]
migrate-up:
    migrate -path migrations -database "$DATABASE_URL" up

# Revenir à la dernière migration
[group('db')]
migrate-down:
    migrate -path migrations -database "$DATABASE_URL" down 1

# Créer une nouvelle migration
[group('db')]
migrate-create name:
    migrate create -ext sql -dir migrations -seq {{ name }}

# ── CI ────────────────────────────────────────────────────

# Porte CI complète (vérification du formatage + lint + tests)
[group('ci')]
check: fmt-check lint test
    @echo "Toutes les vérifications ont réussi"

# Nettoyer les artefacts de construction
[group('ci')]
clean:
    go clean
    rm -f {{ binary }} coverage.out

check omet délibérément vuln : govulncheck appelle le réseau, et une mise à jour de la base de vulnérabilités peut faire devenir la porte rouge sur un changement qui n'a rien touché. Exécutez-le comme son propre job CI à la place. golangci-lint run exécute également les formateurs activés et signale leurs problèmes, donc fmt-check duplique cette vérification - gardez-le quand même pour le diff lisible.

Configuration Lefthook

Lefthook est préféré à pre-commit pour les projets Go - c'est un binaire Go unique, exécute les hooks en parallèle, et ne nécessite pas Python.

go install github.com/evilmartians/lefthook/[email protected]   # nécessite Go 1.26+
lefthook install
# lefthook.yml
assert_lefthook_installed: true   # échouer bruyamment au lieu de sauter toutes les règles

pre-commit:
  piped: true   # échouer rapidement - s'arrêter au premier job qui échoue
  commands:
    fmt:
      glob: "*.go"
      run: golangci-lint fmt {staged_files}
      stage_fixed: true
    lint:
      glob: "*.go"
      # Ne jamais passer une simple liste de fichiers à `golangci-lint run` : une liste s'étendant
      # sur deux répertoires est rejetée immédiatement, et un fichier d'un package multi-fichier
      # signale des erreurs de type fantômes `undefined:`. Linter les packages à la place.
      run: printf '%s\n' {staged_files} | xargs -n1 dirname | sort -u | xargs golangci-lint run --fix
      stage_fixed: true
    mod-tidy:
      glob: "*.{go,mod,sum}"
      run: go mod tidy

pre-push:
  commands:
    test:
      run: go test -race ./...

piped: true est un échec rapide, pas un ordre - lefthook « exécute les commandes et les scripts séquentiellement par défaut », et piped ajoute « Arrêter l'exécution des commandes et des scripts si l'un d'eux échoue ». Il ne peut pas être combiné avec parallel: true.

jobs: (ajouté dans lefthook 1.10.0) est la primitive plus récente à côté de la division commands:/scripts: - « Les jobs offrent un moyen flexible de définir des tâches, prenant en charge à la fois les commandes et les scripts. Les jobs peuvent être regroupés pour un contrôle de flux avancé. » commands: n'est pas obsolète et reste entièrement documenté ; utilisez jobs: lorsque vous avez besoin de regroupement, de contrôle de flux imbriqué, ou d'un mélange de commandes en ligne et de scripts dans un même hook.

Quatre autres éléments à câbler :

  • assert_lefthook_installed: true, ci-dessus, est l'antidote au piège de dormance ci-dessous : « échouer (avec un code de sortie 1) si l'exécutable lefthook ne peut pas être trouvé dans $PATH ».
  • lefthook validate dans le CI attrape un lefthook.yml mal formé avant qu'il ne désactive silencieusement les hooks ; lefthook dump affiche la configuration effective fusionnée lorsqu'un hook ne se comporte pas comme écrit.
  • Un fichier lefthook-local.yml ignoré par git permet à un développeur d'ajouter ou de sauter des jobs sans l'imposer à ses coéquipiers - « Ceci est utile lorsque vous souhaitez utiliser lefthook localement sans l'imposer à vos coéquipiers. »
  • Dans un monorepo, donnez à chaque job un root: pointant vers son répertoire de module ; sans cela, go mod tidy et go tool s'exécutent contre la racine du dépôt et échouent.
  • skip: [merge, rebase] sur les jobs mod-tidy et lint les maintient en dehors des commits effectués pendant une fusion ou un rebase en cours, de sorte que la résolution des conflits ne remette pas en scène une réécriture tidy ou des modifications --fix.

La suite de tests pre-push répète ce que le CI exécute déjà. Une fois qu'elle prend des minutes, supprimez-la ou limitez-la aux packages modifiés - sinon les développeurs apprennent à pousser avec LEFTHOOK=0. Les tests exécutés depuis un hook héritent également de l'environnement de hook de git (GIT_DIR, GIT_INDEX_FILE, GIT_WORK_TREE), donc un helper de test qui exécute git init ou git commit dans t.TempDir() agit sur le dépôt réel - effacez ces variables dans cmd.Env.

Une git merge sans conflit n'exécute jamais pre-commit : git invoque pre-merge-commit à la place, et exécute pre-commit seulement lorsque vous terminez une fusion conflictuelle avec git commit. Miroirez les vérifications sous une clé pre-merge-commit:, ou les fusions arriveront non lintées.

Les commandes de hook résolvent les outils à partir du PATH de l'appelant, donc un binaire manquant fait échouer le commit avec un simple exit 127. Les exécuter sous forme de go tool <nom> (directive d'outil) rend le hook aussi reproductible que la construction.

Bêta, mais bon à savoir : ai: déclare des hooks d'agent LLM dans le même fichier - « Pendant lefthook install, lefthook génère le fichier de paramètres spécifique au fournisseur afin que l'agent appelle lefthook run <hook> lorsque l'…

[Document tronqué pour la traduction]

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

Skills associés