repowarden 4.7.0 → 4.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,29 +27,47 @@
27
27
 
28
28
  **Plus de 25 technologies** : Java (Maven, Gradle), JavaScript / TypeScript, Python, Go, Rust, PHP, Ruby, .NET, Dart / Flutter, Swift, Elixir, C / C++, Shell, Terraform, Packer, Ansible, Helm, Kubernetes, Docker, GitHub Actions — [détail](https://simdevwarden.github.io/repowarden/technologies/). Linux, macOS, Windows.
29
29
 
30
- ## Ce que tu fais à la main
31
-
32
- Le reste est automatique. Pas à pas et pièges : [où cliquer](https://simdevwarden.github.io/repowarden/validation/#ou-cliquer).
33
-
34
- | Geste | Où |
35
- |---|---|
36
- | Valider un ticket | étiquette `validé` (d'office si un mainteneur l'ouvre) |
37
- | Écrire la conception, avant le code | commentaire contenant « Conception retenue » sur le ticket |
38
- | Terminer une PR | **Ready for review** : elle se merge seule quand la CI est verte |
39
- | Livrer en production (flux `develop`) | *Actions → release → Run workflow* (ou la demande du vendredi), puis **Review deployments → Approve and deploy** sur le run |
40
- | Livrer un correctif urgent | étiquette `urgent` (à créer si absente) sur la PR `fix/…` |
41
- | Relire une PR, si exigé | *Files changed → Review changes → Approve* |
30
+ ## Le cycle d'une modification
31
+
32
+ De l'idée à la production, chaque changement suit le même chemin. Tu n'agis qu'aux étapes **en gras** ; le reste est automatique, et chaque étape est vérifiée par la CI.
33
+
34
+ ```mermaid
35
+ flowchart LR
36
+ A[Ticket] -->|validé| B[Conception]
37
+ B --> C[Branche du ticket]
38
+ C -->|commits + push| D[PR brouillon]
39
+ D -->|Ready for review| E{CI verte ?}
40
+ E -->|oui| F[develop]
41
+ E -->|non| C
42
+ F -->|vendredi, ou à la main| G[Demande de livraison]
43
+ G -->|Approve and deploy| H[main : release]
44
+ ```
42
45
 
43
- Une PR `develop` → `main` ouverte à la main ne reçoit jamais d'approbation : lancer le workflow `release`, qui ouvre la sienne.
46
+ | # | Étape | Qui | Ce qui se passe | Statut du ticket |
47
+ |---|---|---|---|---|
48
+ | 1 | Ouvrir un ticket (issue) | toi | rien ne se code sans ticket | à valider |
49
+ | 2 | **Valider le ticket** | toi | étiquette `validé` (posée d'office si un mainteneur ouvre le ticket) | backlog |
50
+ | 3 | **Écrire la conception** | toi | commentaire « Conception retenue » sur le ticket : ce qui change, fichiers touchés, hors périmètre. Sans elle, la vérification `ticket` de la PR échoue | backlog |
51
+ | 4 | **Prendre le ticket** | toi | `repowarden ticket 12` : assigné, branche `fix/12-sujet` créée depuis `develop` | en cours |
52
+ | 5 | Coder et committer | toi | `git cc` ; les hooks vérifient message, branche, secrets, formatage, puis les tests au push | en cours |
53
+ | 6 | Premier push | auto | PR **brouillon** vers `develop` ouverte, titre et description tirés du ticket | en cours |
54
+ | 7 | **Sortir du brouillon** | toi | bouton *Ready for review* (`gh pr ready`) | en relecture |
55
+ | 8 | Vérifier et intégrer | auto | CI complète ; verte → **merge automatique** (squash) dans `develop`. Rouge → corriger et pousser, retour en 5 | préprod |
56
+ | 9 | Préversion | auto | chaque soir si `develop` a changé : `vX.Y.Z-next.N` (paquet `repowarden@next`), pour tester avant la prod | préprod |
57
+ | 10 | Demande de livraison | auto | le vendredi matin, ou à la main (*Actions → release → Run workflow*, ou étiquette `urgent` sur une PR `fix/…`) : instantané `livraison/vX.Y.Z` et sa PR vers `main` | préprod |
58
+ | 11 | **Approuver la livraison** | toi | sur la page du run : **Review deployments → Approve and deploy** (pas « Approve » sur la PR, qui est une simple relecture) | préprod |
59
+ | 12 | Publier | auto | la GitHub App merge vers `main` : tag `vX.Y.Z`, release, paquet npm, documentation | done, ticket fermé |
60
+
61
+ Seul `develop` entre dans `main`, et jamais sans l'approbation de l'étape 11. Une PR `develop` → `main` ouverte à la main est fermée automatiquement, avec la marche à suivre. Pas à pas, commandes et pièges : [où cliquer](https://simdevwarden.github.io/repowarden/validation/#ou-cliquer).
44
62
 
45
63
  ## Démarrage
46
64
 
47
65
  ### 1. Sur ton poste, en une minute (tous tes dépôts)
48
66
 
49
67
  ```bash
50
- npm install -g repowarden # 1. le paquet (yarn global add, pnpm add -g : idem)
51
- repowarden install --global # 2. active les hooks : demande la langue, installe git cc
52
- repowarden # 3. vérifie : état du poste et étape suivante
68
+ npm install -g repowarden # 1. le paquet (yarn global add, pnpm add -g : idem)
69
+ repowarden install --global # 2. active les hooks : demande la langue, installe git cc
70
+ repowarden # 3. vérifie : état du poste et étape suivante
53
71
  ```
54
72
 
55
73
  L'étape 2 est indispensable : par sécurité, le paquet n'exécute rien à l'installation (aucun script `postinstall`, que npm 12 bloque d'ailleurs). Ensuite, dans **n'importe quel dépôt**, les hooks s'appliquent dès le prochain commit. Rien n'est ajouté aux projets ni à leur `package.json`.
@@ -59,7 +77,7 @@ cd mon-projet
59
77
  git switch -c feat/panier # nom de branche vérifié
60
78
  git add . && git cc # assistant : type, scope, description… puis commit
61
79
  git push -u origin feat/panier # tests des projets touchés avant l'envoi
62
- repowarden code-mort # nouveau code mort de ta branche
80
+ repowarden code-mort # nouveau code mort de ta branche
63
81
  ```
64
82
 
65
83
  | Action | Commande |
@@ -67,25 +85,17 @@ repowarden code-mort # nouveau code mort de ta branche
67
85
  | État du poste, étape suivante | `repowarden` (ou `repowarden statut`) |
68
86
  | Toutes les commandes | `repowarden --help` (`cc`, `code-mort`, `proteger`, `npm-publication`…) |
69
87
  | Mettre à jour | `npm update -g repowarden` (les hooks suivent) |
88
+ | Tester la prochaine version | `npm install -g repowarden@next` (retour : `repowarden@latest`) ; en CI : `uses: simdevWarden/repowarden@develop` |
70
89
  | Désinstaller | `repowarden uninstall --global`, **puis** `npm uninstall -g repowarden` (dans cet ordre : npm n'exécute rien à la désinstallation, les hooks resteraient branchés sur un dossier supprimé) |
71
90
 
72
91
  Sans Node : `git clone https://github.com/simdevWarden/repowarden.git ~/repowarden && ~/repowarden/install.sh --global` (mise à jour : `git -C ~/repowarden pull`).
73
92
 
74
- ### 4. GitHub App pour l'automatisation (merge auto, tickets, releases)
93
+ **Nouvelle machine** : ces trois commandes suffisent. Les règles du dépôt, l'environnement de livraison, la GitHub App, ses secrets et la publication npm vivent sur GitHub et npm : rien à reconfigurer. La clé privée de l'App ne doit jamais se trouver sur un poste.
75
94
 
76
- Pour que les workflows déclenchent les autres workflows (merge auto, tickets, releases), il faut une GitHub App au lieu du `GITHUB_TOKEN` :
77
-
78
- ```bash
79
- repowarden app init
80
- ```
81
-
82
- 1. Ouvre la page locale → clique "Create GitHub App" sur GitHub
83
- 2. Clique "Install" sur ton repo/org
84
- 3. Les secrets `REPOWARDEN_APP_CLIENT_ID` / `REPOWARDEN_APP_KEY` sont configurés automatiquement
85
-
86
- Sans cette étape, le `GITHUB_TOKEN` des Actions est utilisé : **ce qu'il fait ne déclenche pas les autres workflows** (merge auto, tickets, préversions).
87
-
88
- **Tester la prochaine version** (avant sa sortie en production) : `npm install -g repowarden@next`, ou `uses: simdevWarden/repowarden@develop` en CI. Retour à la version stable : `npm install -g repowarden@latest`. Les problèmes se signalent en [issue](https://github.com/simdevWarden/repowarden/issues).
95
+ > [!NOTE]
96
+ > **GitHub CLI (`gh`) : facultatif.**
97
+ > Sans `gh` : tous les hooks (commits, branches, secrets, formatage, tests), `git cc`, `repowarden code-mort`, `repowarden statut`, et la CI (GitHub ou GitLab), qui n'utilise jamais le `gh` du poste.
98
+ > Avec `gh` connecté (`gh auth login`), en plus : les tickets (`repowarden ticket`, `tickets`, branches de tes tickets créées en local) et la gestion du dépôt (`proteger`, `app init`, `npm-publication`, `pr-*`, `run-*`). Utile au mainteneur du dépôt GitHub ; chaque commande le signale s'il manque.
89
99
 
90
100
  ### 2. Dans un projet d'équipe : les mêmes règles pour tous
91
101
 
@@ -107,22 +117,44 @@ L'essentiel des deux fichiers :
107
117
  ```ini
108
118
  # .repowarden.conf : un poste en retard est prévenu, avec la commande de mise à jour
109
119
  [repowarden]
110
- version = 3
120
+ version = 4
111
121
  ```
112
122
 
113
- Puis rendre la CI obligatoire pour merger : `repowarden proteger` (protection de `main`, check `repowarden` exigé).
123
+ Puis rendre la CI obligatoire pour merger (`gh` connecté, admin du dépôt) :
124
+
125
+ ```bash
126
+ repowarden proteger --checks "repowarden,tests" # toutes les vérifications exigées, séparées par des virgules
127
+ repowarden proteger --checks "repowarden,tests" --verifier # plus tard : règles en ligne à jour ? (lecture seule)
128
+ ```
129
+
130
+ Toujours passer **toutes** les vérifications à `--checks` : sans lui, seule `repowarden` reste exigée. Avec un flux `develop` → `main`, `proteger` crée aussi l'étiquette `urgent` et exige l'approbation de chaque livraison : utiliser le [modèle de release du flux develop](https://simdevwarden.github.io/repowarden/releases/), sinon `main` attendrait un déploiement que rien ne crée (`proteger` le signale).
114
131
 
115
132
  Exemple complet : [repowarden-demo](https://github.com/simdevWarden/repowarden-demo) · Releases automatiques : [documentation](https://simdevwarden.github.io/repowarden/releases/).
116
133
 
117
- ### 3. Contribuer à repowarden
134
+ ### 3. GitHub App pour l'automatisation (merge auto, tickets, releases)
135
+
136
+ Pour que les workflows déclenchent les autres workflows (merge auto, tickets, releases), il faut une GitHub App au lieu du `GITHUB_TOKEN` :
137
+
138
+ ```bash
139
+ repowarden app init
140
+ ```
141
+
142
+ 1. Ouvre la page locale → clique « Create GitHub App » sur GitHub
143
+ 2. Clique « Install » sur ton dépôt ou ton organisation
144
+ 3. Les secrets `REPOWARDEN_APP_CLIENT_ID` / `REPOWARDEN_APP_KEY` sont configurés automatiquement
145
+
146
+ Sans cette étape, le `GITHUB_TOKEN` des Actions est utilisé : **ce qu'il fait ne déclenche pas les autres workflows** (merge auto, tickets, préversions).
147
+
148
+ ### 4. Contribuer à repowarden
118
149
 
119
150
  ```bash
120
151
  git clone https://github.com/simdevWarden/repowarden.git && cd repowarden
121
152
  ./install.sh # repowarden se vérifie lui-même (hooks de ce dépôt)
122
- bats test/ # tests (bats-core), aussi lancés au push
153
+ brew install shellcheck bats-core actionlint gitleaks # outils des vérifications (ou équivalent Linux)
154
+ bats test/ # tests, aussi lancés au push
123
155
  ```
124
156
 
125
- Les PR visent `develop` ; `main` ne reçoit que les livraisons, qui déclenchent les releases. Voir [CONTRIBUTING.md](CONTRIBUTING.md).
157
+ Les PR visent `develop` ; `main` ne reçoit que les livraisons, qui déclenchent les releases. Tout part d'un ticket : voir [AGENTS.md](AGENTS.md) et [CONTRIBUTING.md](CONTRIBUTING.md). Les problèmes se signalent en [issue](https://github.com/simdevWarden/repowarden/issues).
126
158
 
127
159
  **Dans VS Code** : l'extension [repowarden-vscode](https://github.com/simdevWarden/repowarden-vscode) guide la rédaction des commits, signale une branche mal nommée et des hooks inactifs. En attendant sa publication sur le Marketplace : `.vsix` dans ses [releases](https://github.com/simdevWarden/repowarden-vscode/releases), puis `code --install-extension repowarden-X.Y.Z.vsix`.
128
160
 
package/bin/commit CHANGED
@@ -6,7 +6,7 @@
6
6
  # sans cela, la ligne suivante serait lue
7
7
  # comme une nouvelle réponse)
8
8
  # git cc -m "description" [--type T] [--scope S] [--breaking NOTE]
9
- # [--body TEXTE] [--refs "Closes #12"]
9
+ # [--body TEXTE] [--refs "Ticket : #12"]
10
10
  # sans question (scripts, agents) : mêmes
11
11
  # contrôles, toute réponse refusée arrête
12
12
  # tout ; type et scope déduits de la
@@ -173,14 +173,19 @@ while :; do
173
173
  type="$answer"
174
174
  break
175
175
  fi
176
- t cc.type_invalid "$answer" "${#TYPES[@]}"
176
+ type_proche_r "$(printf '%s' "$answer" | tr '[:upper:]' '[:lower:]')"
177
+ if [ -n "$REPLY" ]; then
178
+ t cc.type_invalid_suggest "$answer" "$REPLY" "${#TYPES[@]}"
179
+ else
180
+ t cc.type_invalid "$answer" "${#TYPES[@]}"
181
+ fi
177
182
  refuse "$REPLY"
178
183
  done
179
184
 
180
185
  # 2. Scope (optionnel)
181
186
  t cc.step2
182
187
  etape 2 "$REPLY"
183
- t cc.scope_hint
188
+ if [ -n "$default_scope" ]; then t cc.scope_hint_default "$default_scope"; else t cc.scope_hint; fi
184
189
  hint "$REPLY"
185
190
  while :; do
186
191
  if [ -n "$default_scope" ]; then
@@ -352,7 +357,8 @@ fi
352
357
  if [ -n "$ticket" ]; then
353
358
  t cc.refs_prompt_ticket "$ticket"
354
359
  else
355
- t cc.refs_prompt "$FORGE_REFS"
360
+ cfg_r integrationBranch ""
361
+ if [ -n "$REPLY" ]; then t cc.refs_prompt "Ticket : #12"; else t cc.refs_prompt "$FORGE_REFS"; fi
356
362
  fi
357
363
  ANSWER="$OPT_REFS"
358
364
  ask "$REPLY"
package/hooks/commit-msg CHANGED
@@ -39,6 +39,30 @@ if [[ "$SUBJECT" =~ ^(Merge|Revert|fixup!|squash!) ]]; then
39
39
  exit 0
40
40
  fi
41
41
 
42
+ # En-tête tenté mais refusé : type inconnu (ou en majuscules), description vide
43
+ if ! [[ "$SUBJECT" =~ $CC_PATTERN ]] &&
44
+ [[ "$SUBJECT" =~ ^([A-Za-z]+)(\([^\)]*\))?(!?):[[:space:]]*(.*)$ ]]; then
45
+ typ="${BASH_REMATCH[1]}" portee="${BASH_REMATCH[2]}" bang="${BASH_REMATCH[3]}" desc="${BASH_REMATCH[4]}"
46
+ typ_min="$(printf '%s' "$typ" | tr '[:upper:]' '[:lower:]')"
47
+ propose="$typ_min"
48
+ [[ "$typ_min" =~ ^($CC_TYPES)$ ]] || { type_proche_r "$typ_min"; propose="$REPLY"; }
49
+ if [ -z "$desc" ]; then
50
+ err_t hook.commit_msg.desc_empty "${propose:-$typ}$portee$bang:"
51
+ echo_t hook.commit_msg.kept >&2
52
+ exit 1
53
+ fi
54
+ if [ "$propose" != "$typ" ]; then
55
+ if [ -n "$propose" ]; then
56
+ err_t hook.commit_msg.type_unknown "$typ" "$propose$portee$bang: $desc"
57
+ else
58
+ err_t hook.commit_msg.type_none "$typ"
59
+ types_help
60
+ fi
61
+ echo_t hook.commit_msg.kept >&2
62
+ exit 1
63
+ fi
64
+ fi
65
+
42
66
  if ! [[ "$SUBJECT" =~ $CC_PATTERN ]]; then
43
67
  if [ "${REPOWARDEN_LANG:-}" = en ]; then
44
68
  cat >&2 <<EOF
@@ -80,6 +104,15 @@ EOF
80
104
  exit 1
81
105
  fi
82
106
 
107
+ # Point final retiré, comme dans git cc (pas des points de suspension) ; seule
108
+ # la première ligne est réécrite, le corps reste intact
109
+ if [[ "$SUBJECT" == *. && "$SUBJECT" != *.. ]]; then
110
+ SUBJECT="${SUBJECT%.}"
111
+ contenu="$(awk -v s="$SUBJECT" '!fait && !/^#/ { print s; fait = 1; next } { print }' "$MSG_FILE")"
112
+ printf '%s\n' "$contenu" >"$MSG_FILE"
113
+ info_t hook.commit_msg.dot >&2
114
+ fi
115
+
83
116
  authored_length "$SUBJECT"
84
117
  if [ "$REPLY" -gt 72 ]; then
85
118
  err_t hook.commit_msg.1 "$REPLY" >&2
@@ -84,6 +84,23 @@ cfg() {
84
84
  CC_TYPES="feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert"
85
85
  CC_PATTERN="^($CC_TYPES)(\([a-z0-9._-]+\))?!?: .+"
86
86
 
87
+ # Type le plus proche d'un type inconnu : alias courants, puis préfixe commun
88
+ # (doc → docs, feature → feat, Feat → feat) ; vide si aucun
89
+ type_proche_r() {
90
+ local t="$1" c
91
+ case "$t" in
92
+ feature | features) REPLY="feat"; return ;;
93
+ doc | documentation) REPLY="docs"; return ;;
94
+ fixe | fixed | fixes | bugfix | hotfix) REPLY="fix"; return ;;
95
+ tests) REPLY="test"; return ;;
96
+ refacto | refactoring) REPLY="refactor"; return ;;
97
+ esac
98
+ REPLY=""
99
+ for c in ${CC_TYPES//|/ }; do
100
+ if [[ "$c" == "$t"* || "$t" == "$c"* ]]; then REPLY="$c"; return; fi
101
+ done
102
+ }
103
+
87
104
  # REPLY = longueur de la première ligne écrite par l'auteur : le suffixe
88
105
  # « (#123) » ajouté par GitHub lors d'un merge squash n'est pas compté.
89
106
  authored_length() {
@@ -25,8 +25,10 @@ _msg_en() {
25
25
  cc.option_value) _M="Option %s expects a value." ;;
26
26
  cc.batch_message_required) _M="Without questions, the description is required: git cc -m \"<description>\" [--type T] [--scope S] [--body …] [--refs …] [--breaking …]." ;;
27
27
  cc.type_invalid) _M="\"%s\": pick a number (1 to %s) or a name from the list." ;;
28
+ cc.type_invalid_suggest) _M="\"%s\": unknown type. Did you mean \"%s\"? Number (1 to %s) or a name from the list." ;;
28
29
  cc.step2) _M="Scope (optional)" ;;
29
30
  cc.scope_hint) _M="Part of the project affected, e.g. api, auth, chart; Enter for none." ;;
31
+ cc.scope_hint_default) _M="Part of the project, e.g. api, auth, chart; Enter keeps \"%s\", \"-\" for none." ;;
30
32
  cc.scope_prompt_default) _M="Scope (\"-\" for none) [%s]: " ;;
31
33
  cc.scope_prompt) _M="Scope: " ;;
32
34
  cc.scope_fixed) _M="Scope: lowercase letters, digits, . _ - only. Suggested: %s (Enter to accept)." ;;
@@ -40,7 +42,7 @@ _msg_en() {
40
42
  cc.breaking_note_prompt) _M="What changes: " ;;
41
43
  cc.breaking_note_invalid) _M="Describe the change (at least 10 characters); otherwise answer no to the previous question." ;;
42
44
  cc.step4) _M="Description" ;;
43
- cc.desc_hint) _M="Imperative mood, no initial capital or final period; %s characters max." ;;
45
+ cc.desc_hint) _M="Imperative mood, no final period, lowercase start except acronyms or proper nouns; %s characters max." ;;
44
46
  cc.desc_required) _M="The description is required." ;;
45
47
  cc.header_too_long) _M="Header too long: %s characters (72 max). Shorten the description." ;;
46
48
  cc.step5) _M="Details (optional)" ;;
@@ -48,7 +50,8 @@ _msg_en() {
48
50
  cc.refs_prompt_ticket) _M="References (\"-\" for none) [%s]: " ;;
49
51
  cc.refs_prompt) _M="References (e.g. %s; Enter for none): " ;;
50
52
  tk.cli.no_ticket) _M="Branch %s has no ticket: this work will not be linked to any ticket (not blocking)." ;;
51
- tk.cli.propose) _M="Start it from a ticket: repowarden ticket nouveau \"<title>\" (or answer yes in git cc)." ;;
53
+ tk.cli.propose) _M="Start it from a ticket: repowarden ticket nouveau \"<title>\"." ;;
54
+ tk.cli.propose_cc) _M="Start it from a ticket: repowarden ticket nouveau \"<title>\" (or answer yes in git cc)." ;;
52
55
  tk.cli.silence) _M="Project without ticket tracking? git config repowarden.skip tickets to stop seeing this." ;;
53
56
  tk.cli.fetch) _M="Branch of ticket #%s…" ;;
54
57
  tk.cli.switched) _M="On branch %s (ticket #%s)." ;;
@@ -111,6 +114,11 @@ _msg_en() {
111
114
  hook.pre_commit.6) _M="gitleaks not installed, secret detection skipped (install: %s)." ;;
112
115
  hook.pre_commit.7) _M="Pre-commit OK" ;;
113
116
  hook.commit_msg.1) _M="First line too long (%s > 72 characters)." ;;
117
+ hook.commit_msg.type_unknown) _M="Unknown type \"%s\". Did you mean: %s" ;;
118
+ hook.commit_msg.type_none) _M="Unknown type \"%s\". Possible types:" ;;
119
+ hook.commit_msg.desc_empty) _M="Missing description after \"%s\": describe the change in the imperative mood." ;;
120
+ hook.commit_msg.kept) _M=" Your message is not lost: git commit -e -F .git/COMMIT_EDITMSG" ;;
121
+ hook.commit_msg.dot) _M="Final period removed from the description (same rule as git cc)." ;;
114
122
  hook.pre_push.1) _M="Push refused: branch name does not follow the convention." ;;
115
123
  hook.pre_push.2) _M="Direct push to '%s' refused: it only changes through pull requests." ;;
116
124
  hook.pre_push.3) _M=" Create a branch: git switch -c feat/my-feature, then open a PR." ;;
@@ -25,8 +25,10 @@ _msg_fr() {
25
25
  cc.option_value) _M="L'option %s attend une valeur." ;;
26
26
  cc.batch_message_required) _M="Sans question, la description est obligatoire : git cc -m \"<description>\" [--type T] [--scope S] [--body …] [--refs …] [--breaking …]." ;;
27
27
  cc.type_invalid) _M="« %s » : choisis un numéro (1 à %s) ou un nom de la liste." ;;
28
+ cc.type_invalid_suggest) _M="« %s » : type inconnu. Voulais-tu « %s » ? Numéro (1 à %s) ou nom de la liste." ;;
28
29
  cc.step2) _M="Scope (optionnel)" ;;
29
30
  cc.scope_hint) _M="Partie du projet concernée, ex. api, auth, chart ; Entrée pour aucun." ;;
31
+ cc.scope_hint_default) _M="Partie du projet concernée, ex. api, auth, chart ; Entrée garde « %s », « - » pour aucun." ;;
30
32
  cc.scope_prompt_default) _M="Scope (« - » pour aucun) [%s] : " ;;
31
33
  cc.scope_prompt) _M="Scope : " ;;
32
34
  cc.scope_fixed) _M="Scope : minuscules, chiffres, . _ - uniquement. Proposé : %s (Entrée pour l'accepter)." ;;
@@ -40,7 +42,7 @@ _msg_fr() {
40
42
  cc.breaking_note_prompt) _M="Ce qui change : " ;;
41
43
  cc.breaking_note_invalid) _M="Décris le changement (10 caractères au moins) ; sinon, réponds non à la question précédente." ;;
42
44
  cc.step4) _M="Description" ;;
43
- cc.desc_hint) _M="À l'impératif, sans majuscule initiale ni point final ; %s caractères au plus." ;;
45
+ cc.desc_hint) _M="À l'impératif, sans point final, minuscule initiale sauf sigle ou nom propre ; %s caractères au plus." ;;
44
46
  cc.desc_required) _M="La description est obligatoire." ;;
45
47
  cc.header_too_long) _M="En-tête trop long : %s caractères (72 max). Raccourcis la description." ;;
46
48
  cc.step5) _M="Détails (optionnels)" ;;
@@ -48,7 +50,8 @@ _msg_fr() {
48
50
  cc.refs_prompt_ticket) _M="Références (« - » pour aucune) [%s] : " ;;
49
51
  cc.refs_prompt) _M="Références (ex. %s ; Entrée pour aucune) : " ;;
50
52
  tk.cli.no_ticket) _M="Branche %s sans ticket : ce travail ne sera relié à aucun ticket (non bloquant)." ;;
51
- tk.cli.propose) _M="Le partir d'un ticket : repowarden ticket nouveau \"<titre>\" (ou réponds oui à git cc)." ;;
53
+ tk.cli.propose) _M="Partir d'un ticket : repowarden ticket nouveau \"<titre>\"." ;;
54
+ tk.cli.propose_cc) _M="Partir d'un ticket : repowarden ticket nouveau \"<titre>\" (ou réponds oui à git cc)." ;;
52
55
  tk.cli.silence) _M="Projet sans suivi de tickets ? git config repowarden.skip tickets pour ne plus le voir." ;;
53
56
  tk.cli.fetch) _M="Branche du ticket #%s…" ;;
54
57
  tk.cli.switched) _M="Sur la branche %s (ticket #%s)." ;;
@@ -109,6 +112,11 @@ _msg_fr() {
109
112
  hook.pre_commit.6) _M="gitleaks absent, détection de secrets ignorée (installation : %s)." ;;
110
113
  hook.pre_commit.7) _M="Pre-commit OK" ;;
111
114
  hook.commit_msg.1) _M="Première ligne trop longue (%s > 72 caractères)." ;;
115
+ hook.commit_msg.type_unknown) _M="Type « %s » inconnu. Voulais-tu : %s" ;;
116
+ hook.commit_msg.type_none) _M="Type « %s » inconnu. Types possibles :" ;;
117
+ hook.commit_msg.desc_empty) _M="Description manquante après « %s » : décris le changement à l'impératif." ;;
118
+ hook.commit_msg.kept) _M=" Ton message n'est pas perdu : git commit -e -F .git/COMMIT_EDITMSG" ;;
119
+ hook.commit_msg.dot) _M="Point final retiré de la description (même règle que git cc)." ;;
112
120
  hook.pre_push.1) _M="Push refusé : nom de branche non conforme." ;;
113
121
  hook.pre_push.2) _M="Push direct refusé sur '%s' : elle n'évolue que par Pull Request." ;;
114
122
  hook.pre_push.3) _M=" Crée une branche : git switch -c feat/ma-feature, puis ouvre une PR." ;;
@@ -168,7 +168,8 @@ ticket_hint() {
168
168
  [ -z "$REPLY" ] || return 0
169
169
  attention_t tk.cli.no_ticket "$branch"
170
170
  if tickets_managed && has gh; then
171
- dim_t tk.cli.propose
171
+ # git cc ne propose de créer le ticket qu'avec ticketAutoCreate
172
+ if ticket_auto_create; then dim_t tk.cli.propose_cc; else dim_t tk.cli.propose; fi
172
173
  else
173
174
  dim_t tk.cli.silence
174
175
  fi
package/hooks/lib/ui.sh CHANGED
@@ -31,7 +31,27 @@ repowarden_version_r() {
31
31
  local tag=""
32
32
  REPLY="$(sed -n 's/^ "version": *"\([^"]*\)".*/\1/p' "$1/package.json" 2>/dev/null | sed -n 1p)"
33
33
  if [ -e "$1/.git" ]; then
34
- tag="$(git -C "$1" describe --tags --abbrev=0 --match 'v[0-9]*' 2>/dev/null || true)"
34
+ # Sous-shell sans les variables que git exporte aux hooks (worktree) :
35
+ # elles l'emportent sur -C, ce seraient les tags du projet. Tag le plus
36
+ # proche de HEAD (git describe), remplacé par un tag de release dont le
37
+ # commit mergé est au moins aussi proche : la release est taguée sur le
38
+ # merge de la livraison dans main, invisible depuis develop pour
39
+ # describe ; à égalité, la version stable l'emporte sur la préversion
40
+ tag="$(
41
+ unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE GIT_COMMON_DIR
42
+ best="$(git -C "$1" describe --tags --abbrev=0 --match 'v[0-9]*' 2>/dev/null || true)"
43
+ dist=""
44
+ [ -z "$best" ] || dist="$(git -C "$1" rev-list --count "$best..HEAD")"
45
+ while IFS= read -r t; do
46
+ git -C "$1" merge-base --is-ancestor "$t^2" HEAD 2>/dev/null || continue
47
+ d="$(git -C "$1" rev-list --count "$t^2..HEAD")"
48
+ if [ -z "$best" ] || [ "$d" -lt "$dist" ] ||
49
+ { [ "$d" -eq "$dist" ] && [[ "$t" != *-* && "$best" == *-* ]]; }; then
50
+ best="$t" dist="$d"
51
+ fi
52
+ done < <(git -C "$1" tag --no-merged HEAD --list 'v[0-9]*' 2>/dev/null)
53
+ echo "$best"
54
+ )"
35
55
  [ -z "$tag" ] || REPLY="${tag#v}"
36
56
  fi
37
57
  }
@@ -2,7 +2,7 @@
2
2
  # Hook prepare-commit-msg : déduit le préfixe Conventional Commits du nom de branche.
3
3
  # branche feat/bean : git commit → éditeur prérempli avec "feat(bean): "
4
4
  # git commit -m "ajoute X" → "feat(bean): ajoute X"
5
- # Un message déjà conforme n'est pas modifié.
5
+ # Un message déjà conforme, ou qui a la forme d'un en-tête, n'est pas modifié.
6
6
  set -euo pipefail
7
7
 
8
8
  source "$(dirname "$0")/lib/common.sh"
@@ -36,6 +36,9 @@ fi
36
36
 
37
37
  SUBJECT="$(grep -v '^#' "$MSG_FILE" | sed -n 1p || true)"
38
38
  [[ "$SUBJECT" =~ $CC_PATTERN ]] && exit 0
39
+ # En-tête déjà tenté (mot: ou mot(portée):), même raté : jamais enveloppé,
40
+ # commit-msg le juge (« doc: x » deviendrait « feat(y): doc: x », accepté)
41
+ [[ "$SUBJECT" =~ ^[A-Za-z]+(\([^\)]*\))?!?: ]] && exit 0
39
42
  [[ "$SUBJECT" =~ ^(Merge|Revert|fixup!|squash!) ]] && exit 0
40
43
 
41
44
  # Ajoute le préfixe en tête de la première ligne (vide ou non)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repowarden",
3
- "version": "4.7.0",
3
+ "version": "4.7.1",
4
4
  "description": "Hooks git et CI multi-langages : messages de commit, nommage des branches, secrets, formatage, tests ciblés, code mort, releases.",
5
5
  "keywords": [
6
6
  "git-hooks",