--- 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.