feat(claude-code): skill /push pour release automatisée
Le ritual de release Rubis demande de bumper `apps/web/src/version.ts`
et d'ajouter `apps/landing/src/content/changelog/<x.y.z>.md` dans le
*même* commit — sinon le toast SPA pointe sur une ancre absente côté
`/changelog`. Ce skill orchestre les deux pour éliminer la classe entière
des releases désynchronisées.
Workflow :
1. `git status` + `git diff` pour inspecter les changements pendants
2. Détection heuristique du type (feature/improvement/fix) → bump
semver correspondant (minor pour feature, patch sinon)
3. AskUserQuestion pour titre + highlights (au ton brand : produit-only,
pas de jargon tech, une phrase d'attitude max)
4. Edit `version.ts` + Write nouveau `.md` avec frontmatter Zod-validé
5. Stage + commit Conventional Commits (scope `release`)
6. Push gitea/main
Edge cases couverts : aucun changement à committer, branche ≠ main,
build cassé pré-existant (ne bloque pas la release, juste flag), conflits
remote, hook pre-commit échoue (jamais d'amend / no-verify), version
déjà utilisée, versionnage incohérent entre `version.ts` et le dossier
changelog.
Trigger : `/push`, "release ça", "sors une nouvelle version", "push avec
changelog", "deploy" (sauf si l'user dit explicitement "sans bump").
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
77d52ea95c
commit
fc66775109
220
.claude/skills/push/SKILL.md
Normal file
220
.claude/skills/push/SKILL.md
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
---
|
||||||
|
name: push
|
||||||
|
description: "Release automatisée Rubis : bump de version, ajout d'une entrée changelog, commit conventionnel et push. À utiliser quand l'utilisateur tape `/push` ou demande explicitement de déployer / sortir une nouvelle version / pousser les changements. Le skill orchestre les fichiers couplés (`apps/web/src/version.ts` + `apps/landing/src/content/changelog/<x.y.z>.md`) pour éviter de pusher une version SPA dont le toast pointe sur une ancre absente."
|
||||||
|
allowed-tools:
|
||||||
|
- Bash
|
||||||
|
- Read
|
||||||
|
- Write
|
||||||
|
- Edit
|
||||||
|
---
|
||||||
|
|
||||||
|
# `/push` — Release automatisée Rubis
|
||||||
|
|
||||||
|
Ce skill exécute le ritual de release complet du repo Rubis :
|
||||||
|
|
||||||
|
1. **Inspection** des changements en attente (`git status` + `git diff`)
|
||||||
|
2. **Détermination du type** de release (`feature` / `improvement` / `fix`)
|
||||||
|
3. **Bump semver** de `APP_VERSION` dans `apps/web/src/version.ts`
|
||||||
|
4. **Création** de `apps/landing/src/content/changelog/<new-version>.md`
|
||||||
|
5. **Stage + commit** avec message conventionnel (scope + body)
|
||||||
|
6. **Push** sur `gitea/main`
|
||||||
|
|
||||||
|
Le double-fichier `version.ts` + `<version>.md` **doit** atterrir dans le même commit, sinon le toast SPA `<VersionToast/>` pointera sur une ancre absente côté `/changelog`. C'est précisément pour éviter cette désynchronisation qu'on a un skill dédié.
|
||||||
|
|
||||||
|
## Quand utiliser ce skill
|
||||||
|
|
||||||
|
Déclenche-toi automatiquement quand :
|
||||||
|
- L'utilisateur tape `/push`
|
||||||
|
- L'utilisateur demande "sors une nouvelle version", "release ça", "push avec changelog", "deploy"
|
||||||
|
- L'utilisateur dit "commit + bump" ou variantes
|
||||||
|
|
||||||
|
**Ne déclenche PAS** ce skill quand :
|
||||||
|
- L'utilisateur veut juste un commit local sans push (`/commit`)
|
||||||
|
- L'utilisateur veut push une branche autre que `main`
|
||||||
|
- L'utilisateur dit explicitement "sans bump" ou "pas de release" → fais un commit + push standard à la place
|
||||||
|
|
||||||
|
## Step 1 — Inspecter les changements
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git status --short
|
||||||
|
git diff --stat HEAD
|
||||||
|
git log --oneline -5 # pour le style de commit message
|
||||||
|
```
|
||||||
|
|
||||||
|
Lis aussi `apps/web/src/version.ts` pour récupérer la version courante :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -E '^export const APP_VERSION' apps/web/src/version.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Extrais le semver (`X.Y.Z`).
|
||||||
|
|
||||||
|
**Si aucun changement** (`git status --short` vide) → stop, dis à l'utilisateur "Rien à committer". Ne crée pas une release vide.
|
||||||
|
|
||||||
|
**Si la branche n'est pas `main`** → demande confirmation avant de continuer (`git branch --show-current`). Si l'utilisateur est sur une branche feature, peut-être qu'il veut juste commit, pas release.
|
||||||
|
|
||||||
|
## Step 2 — Déterminer le type de release
|
||||||
|
|
||||||
|
Analyse les diffs et propose un type. Heuristiques :
|
||||||
|
|
||||||
|
| Signal dans les diffs | Type suggéré | Bump |
|
||||||
|
|---|---|---|
|
||||||
|
| Nouveau fichier de feature (component, route, page) | `feature` | minor (X.**Y**.0 → X.**Y+1**.0) |
|
||||||
|
| Modif comportement utilisateur observable, nouvelle interaction | `feature` | minor |
|
||||||
|
| Amélioration UI/UX, refactor visible, perf | `improvement` | patch (X.Y.**Z** → X.Y.**Z+1**) |
|
||||||
|
| Correction d'un bug | `fix` | patch |
|
||||||
|
| Modif uniquement docs (`/docs/`, `*.md` hors changelog) ou tests | → suggère de skip le bump | — |
|
||||||
|
| Modif uniquement assets/marketing (`/docs/*.html`, images) | → suggère de skip le bump | — |
|
||||||
|
|
||||||
|
Présente la suggestion à l'utilisateur et **demande confirmation** :
|
||||||
|
|
||||||
|
```
|
||||||
|
Type détecté : feature
|
||||||
|
Nouvelle version : 1.10.0 → 1.11.0
|
||||||
|
|
||||||
|
Tu valides, ou tu veux changer (feature/improvement/fix/skip) ?
|
||||||
|
```
|
||||||
|
|
||||||
|
Si `skip` → bascule vers un commit + push standard sans bump ni changelog.
|
||||||
|
|
||||||
|
## Step 3 — Collecter le contenu du changelog
|
||||||
|
|
||||||
|
Pose 2 questions courtes à l'utilisateur :
|
||||||
|
|
||||||
|
1. **Titre** (court, product-focused, en français, sans préfixe `v1.x.0 —`) — propose un titre tiré des diffs (commit messages déjà existants, noms de fichiers, etc.)
|
||||||
|
2. **Highlights** (1 à 5 bullets) — chaque bullet décrit ce que l'utilisateur peut faire de nouveau. Pas de jargon tech (`refactor`, `stack`, `monorepo`). Propose-les d'abord depuis les diffs, l'utilisateur valide ou modifie.
|
||||||
|
|
||||||
|
**Optionnel** : si la release est suffisamment riche, propose aussi un **body markdown** (2-4 paragraphes max) qui explique le pourquoi. Sinon, body vide = entry courte uniquement avec les highlights, c'est ok.
|
||||||
|
|
||||||
|
**Ton** (rappel — ce sont les règles brand) :
|
||||||
|
- Direct, concret, chaleureux, précis (cf. `/CLAUDE.md` §Voix)
|
||||||
|
- "On parle comme un bon associé, pas comme une DAF"
|
||||||
|
- Une phrase d'attitude max par entrée ("Un client remercié est un client qui revient")
|
||||||
|
- Jamais de "we are excited to announce", jamais de superlatifs
|
||||||
|
- Jamais de "recouvrement"
|
||||||
|
- Variables markdown en backticks : `` `{{client.nom}}` ``
|
||||||
|
|
||||||
|
## Step 4 — Écrire les fichiers
|
||||||
|
|
||||||
|
### 4a. Bumper `apps/web/src/version.ts`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export const APP_VERSION = "<new-version>";
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit (pas Write — préserve les commentaires) avec `replace_all: false`.
|
||||||
|
|
||||||
|
### 4b. Créer `apps/landing/src/content/changelog/<new-version>.md`
|
||||||
|
|
||||||
|
Format strict (validé par Zod via `apps/landing/src/content.config.ts`) :
|
||||||
|
|
||||||
|
```md
|
||||||
|
---
|
||||||
|
version: "<X.Y.Z>"
|
||||||
|
date: <YYYY-MM-DD>
|
||||||
|
title: "<titre court fourni par l'user>"
|
||||||
|
type: feature # ou improvement / fix
|
||||||
|
highlights:
|
||||||
|
- "<highlight 1>"
|
||||||
|
- "<highlight 2>"
|
||||||
|
- "<highlight 3>"
|
||||||
|
---
|
||||||
|
|
||||||
|
<body markdown optionnel — 2-4 paragraphes max>
|
||||||
|
```
|
||||||
|
|
||||||
|
Règles :
|
||||||
|
- `version` doit matcher le regex `^\d+\.\d+\.\d+$` et être identique à la valeur dans `version.ts`
|
||||||
|
- `date` au format `YYYY-MM-DD` — utilise `date +%Y-%m-%d` côté bash pour avoir la vraie date du jour
|
||||||
|
- `title` entre 3 et 80 caractères, sans préfixe `v1.x.0 —`
|
||||||
|
- `type` ∈ `{feature, improvement, fix}`
|
||||||
|
- `highlights` : 1 à 8 items, idéalement 2-5
|
||||||
|
|
||||||
|
## Step 5 — Commit + push
|
||||||
|
|
||||||
|
### 5a. Génère le message de commit
|
||||||
|
|
||||||
|
Suis la convention du repo (Conventional Commits avec scope) :
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <description courte>
|
||||||
|
|
||||||
|
<body : 1-3 lignes qui expliquent le pourquoi, pas le quoi>
|
||||||
|
<résumé des changements majeurs si plusieurs fichiers>
|
||||||
|
|
||||||
|
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||||||
|
```
|
||||||
|
|
||||||
|
Mappings :
|
||||||
|
| Release type | Commit type |
|
||||||
|
|---|---|
|
||||||
|
| `feature` | `feat` |
|
||||||
|
| `improvement` | `feat` ou `refactor` selon la nature |
|
||||||
|
| `fix` | `fix` |
|
||||||
|
|
||||||
|
Scope : à inférer depuis les diffs. Examples :
|
||||||
|
- Diffs uniquement dans `apps/web/` → scope `web`
|
||||||
|
- Diffs uniquement dans `apps/landing/` → scope `landing`
|
||||||
|
- Diffs croisés (typique d'une release car version.ts + changelog/) → scope `release` ou pas de scope, et la description mentionne `v<x.y.z>`
|
||||||
|
|
||||||
|
Exemple :
|
||||||
|
```
|
||||||
|
feat(release): v1.11.0 — réécriture IA améliorée
|
||||||
|
|
||||||
|
Le bouton "Reformule" passe de 1 ton (ferme) à 3 (ferme, chaleureux,
|
||||||
|
court), avec un aperçu diff côté à côté avant validation. Bumpe APP_VERSION
|
||||||
|
et ajoute l'entrée changelog correspondante.
|
||||||
|
|
||||||
|
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5b. Stage + commit
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stage les changements existants ET les nouveaux fichiers (version.ts + .md)
|
||||||
|
git add apps/web/src/version.ts apps/landing/src/content/changelog/<new-version>.md
|
||||||
|
# Plus tous les autres changements en attente (sauf si l'user a dit "stage only X")
|
||||||
|
git add <fichiers détectés au step 1>
|
||||||
|
|
||||||
|
git commit -m "$(cat <<'EOF'
|
||||||
|
<message complet via HEREDOC>
|
||||||
|
EOF
|
||||||
|
)"
|
||||||
|
```
|
||||||
|
|
||||||
|
⚠️ **Ne JAMAIS** :
|
||||||
|
- Utiliser `git add -A` ou `git add .` (peut catch des secrets, des fichiers temporaires, etc.)
|
||||||
|
- Utiliser `--no-verify` (les hooks pre-commit existent pour une raison)
|
||||||
|
- `git commit --amend` (toujours un nouveau commit propre)
|
||||||
|
|
||||||
|
### 5c. Push
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git push gitea main
|
||||||
|
```
|
||||||
|
|
||||||
|
Le remote `gitea` pointe sur `https://git.arthurbarre.fr/ordinarthur/rubis.git`. Le CI Gitea déclenche le build et le rollout K3s automatiquement.
|
||||||
|
|
||||||
|
## Step 6 — Récap à l'utilisateur
|
||||||
|
|
||||||
|
Affiche un tableau récap après le push :
|
||||||
|
|
||||||
|
```
|
||||||
|
Pushed sur gitea/main :
|
||||||
|
- <commit hash> <commit subject>
|
||||||
|
- Version : <old> → <new>
|
||||||
|
- Changelog : apps/landing/src/content/changelog/<new>.md
|
||||||
|
- Toast SPA : effectif au prochain reload pour les users dont
|
||||||
|
localStorage["rubis:last-seen-version"] < <new>
|
||||||
|
|
||||||
|
Le CI Gitea va déclencher le rebuild des images landing + web
|
||||||
|
(et la propagation sur K3s). Vérifie sur git.arthurbarre.fr.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Edge cases
|
||||||
|
|
||||||
|
- **Build SPA cassé** (typecheck erreurs pré-existantes) : commit/push quand même mais flag clairement que le CI risque d'échouer. Ne tente pas de fixer ces erreurs dans le release commit — c'est un commit séparé.
|
||||||
|
- **Conflits avec le remote** : si `git push` échoue avec `[rejected] non-fast-forward`, fais `git pull --rebase gitea main` puis re-push. Si rebase échoue → stop, demande à l'user.
|
||||||
|
- **Pre-commit hook échoue** : NE fais PAS `--amend`, NE skip PAS avec `--no-verify`. Lis l'erreur, propose un fix, puis re-commit (nouveau commit, hash neuf).
|
||||||
|
- **Version déjà existante** (le `.md` existe déjà sous ce numéro) : stop. Soit l'user a oublié de bumper `version.ts`, soit il y a un conflit de release parallèle. Demande clarification.
|
||||||
|
- **Versionnage incohérent** : si `version.ts` est `1.10.0` mais le dernier `.md` est `1.12.0`, signale-le avant tout bump. Probablement quelqu'un a oublié de commit le bump correspondant.
|
||||||
Loading…
x
Reference in New Issue
Block a user