From fc6677510933fe96d09c87f2ef313b61bc0792e7 Mon Sep 17 00:00:00 2001 From: ordinarthur <@arthurbarre.js@gmail.com> Date: Mon, 11 May 2026 00:49:39 +0200 Subject: [PATCH] =?UTF-8?q?feat(claude-code):=20skill=20/push=20pour=20rel?= =?UTF-8?q?ease=20automatis=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le ritual de release Rubis demande de bumper `apps/web/src/version.ts` et d'ajouter `apps/landing/src/content/changelog/.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 --- .claude/skills/push/SKILL.md | 220 +++++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 .claude/skills/push/SKILL.md diff --git a/.claude/skills/push/SKILL.md b/.claude/skills/push/SKILL.md new file mode 100644 index 0000000..26dc530 --- /dev/null +++ b/.claude/skills/push/SKILL.md @@ -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/.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/.md` +5. **Stage + commit** avec message conventionnel (scope + body) +6. **Push** sur `gitea/main` + +Le double-fichier `version.ts` + `.md` **doit** atterrir dans le même commit, sinon le toast SPA `` 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 = ""; +``` + +Edit (pas Write — préserve les commentaires) avec `replace_all: false`. + +### 4b. Créer `apps/landing/src/content/changelog/.md` + +Format strict (validé par Zod via `apps/landing/src/content.config.ts`) : + +```md +--- +version: "" +date: +title: "" +type: feature # ou improvement / fix +highlights: + - "" + - "" + - "" +--- + + +``` + +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) : + +``` +(): + + + + +Co-Authored-By: Claude Opus 4.7 +``` + +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` + +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 +``` + +### 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/.md +# Plus tous les autres changements en attente (sauf si l'user a dit "stage only X") +git add + +git commit -m "$(cat <<'EOF' + +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 : +- +- Version : +- Changelog : apps/landing/src/content/changelog/.md +- Toast SPA : effectif au prochain reload pour les users dont + localStorage["rubis:last-seen-version"] < + +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.