repowarden 4.7.1 → 4.8.0-next.12

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/bin/bots ADDED
@@ -0,0 +1,370 @@
1
+ #!/usr/bin/env bash
2
+ # Bots du dépôt GitHub courant, configurés avec l'accord de l'utilisateur :
3
+ # repowarden bots questions, aperçu, puis confirmation
4
+ # repowarden install --bots (idem, après l'installation des hooks)
5
+ # repowarden install --sans-bots
6
+ # ne plus jamais le proposer (repowarden.bots = off)
7
+ # Rien n'est fait sans accord : une question globale (non par défaut), le
8
+ # flux (main seul, ou develop → main), un accord par bot (CI, releases,
9
+ # nettoyage, tickets, App, protection, npm), l'aperçu de ce qui sera créé ou
10
+ # modifié, puis une confirmation finale. Un fichier existant n'est jamais
11
+ # écrasé. Relançable pour compléter. Demande un terminal, gh connecté et les
12
+ # droits d'administration du dépôt.
13
+ set -euo pipefail
14
+
15
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
16
+ # shellcheck source=../hooks/lib/common.sh
17
+ source "$ROOT/hooks/lib/common.sh"
18
+
19
+ aide() { sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//'; }
20
+
21
+ # Commandes enchaînées (remplaçables dans les tests)
22
+ BIN="${REPOWARDEN_BIN:-$ROOT/bin}"
23
+ DEPUIS_INSTALL=""
24
+ while [ $# -gt 0 ]; do
25
+ case "$1" in
26
+ --depuis-install) DEPUIS_INSTALL=1 ;;
27
+ -h | --help) aide; exit 0 ;;
28
+ *) err_t install.unknown_arg "$1"; exit 1 ;;
29
+ esac
30
+ shift
31
+ done
32
+
33
+ # Rien n'est proposé : une ligne dit pourquoi, et comment le faire plus tard
34
+ pas_maintenant() { # $1 clé de la raison
35
+ t "$1"
36
+ info_t bots.skip "$REPLY"
37
+ exit 0
38
+ }
39
+
40
+ # Refus permanent (install --sans-bots) : silence depuis l'installation
41
+ if [ -n "$DEPUIS_INSTALL" ] && [ "$(git config --get repowarden.bots 2>/dev/null || true)" = off ]; then
42
+ exit 0
43
+ fi
44
+ [ -n "$GIT_TOPLEVEL" ] || pas_maintenant bots.why_repo
45
+ cd "$GIT_TOPLEVEL"
46
+ [ "$FORGE" = github ] || pas_maintenant bots.why_github
47
+ command -v gh >/dev/null || pas_maintenant bots.why_gh
48
+ gh auth status >/dev/null 2>&1 || pas_maintenant bots.why_gh_login
49
+ REPO="$(gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null || true)"
50
+ [ -n "$REPO" ] || pas_maintenant bots.why_github
51
+ [ "$(gh api "repos/$REPO" --jq .permissions.admin 2>/dev/null || true)" = true ] || pas_maintenant bots.why_admin
52
+ # Terminal (ou réponses fournies, tests) : sinon, jamais de question
53
+ [ -t 0 ] || [ -n "${REPOWARDEN_INTERACTIF:-}" ] || pas_maintenant bots.why_tty
54
+
55
+ # Question fermée : $1 clé, $2 défaut (o ou n) ; vrai si oui
56
+ demander() {
57
+ local reponse choix="o/N"
58
+ [ "$2" = n ] || choix="O/n"
59
+ t "$1"
60
+ printf '%s [%s] ' "$REPLY" "$choix" >&2
61
+ IFS= read -r reponse || reponse=""
62
+ case "$reponse" in
63
+ "") [ "$2" = o ] ;;
64
+ [oOyY]*) return 0 ;;
65
+ *) return 1 ;;
66
+ esac
67
+ }
68
+
69
+ brand "bots · $REPO"
70
+ demander bots.ask_global n || { info_t bots.later; exit 0; }
71
+
72
+ # --- Flux ------------------------------------------------------------------
73
+ cfg_r integrationBranch ""
74
+ INTEGRATION="$REPLY"
75
+ DEFAUT="$(gh api "repos/$REPO" --jq .default_branch)"
76
+ PRINCIPALE=main
77
+ [ "$DEFAUT" = master ] && PRINCIPALE=master
78
+ echo >&2
79
+ t bots.flow_question
80
+ printf '%s\n' "$REPLY" >&2
81
+ t bots.flow_main "$PRINCIPALE"
82
+ printf ' 1) %s\n' "$REPLY" >&2
83
+ t bots.flow_develop "$PRINCIPALE"
84
+ printf ' 2) %s\n' "$REPLY" >&2
85
+ # develop → main par défaut : seul develop entre dans main
86
+ choix_flux=2
87
+ read -r -p "[$choix_flux] : " reponse || reponse=""
88
+ case "${reponse:-$choix_flux}" in
89
+ 2*) FLUX=develop; INTEGRATION="${INTEGRATION:-develop}" ;;
90
+ *) FLUX=main; INTEGRATION="" ;;
91
+ esac
92
+ CREER_DEVELOP=""
93
+ if [ "$FLUX" = develop ] &&
94
+ [ -z "$(gh api "repos/$REPO/branches/$INTEGRATION" --jq .name 2>/dev/null || true)" ]; then
95
+ if demander bots.ask_create_develop o; then
96
+ CREER_DEVELOP=1
97
+ else
98
+ FLUX=main INTEGRATION=""
99
+ fi
100
+ fi
101
+
102
+ # --- Un accord par bot -----------------------------------------------------
103
+ echo >&2
104
+ info_t bots.one_by_one
105
+ APP_PRESENTE=""
106
+ [ -z "$(gh variable get REPOWARDEN_APP_CLIENT_ID --repo "$REPO" 2>/dev/null || true)" ] || APP_PRESENTE=1
107
+ NPM_POSSIBLE=""
108
+ [ ! -f package.json ] || grep -qE '"private": *true' package.json || NPM_POSSIBLE=1
109
+ BOTS=()
110
+ for bot in ci release nettoyage tickets app proteger npm; do
111
+ case "$bot" in
112
+ nettoyage) [ "$FLUX" = develop ] || continue ;;
113
+ app) [ -z "$APP_PRESENTE" ] || { info_t bots.app_present; continue; } ;;
114
+ npm) [ -n "$NPM_POSSIBLE" ] || continue ;;
115
+ esac
116
+ dim_t "bots.desc_$bot"
117
+ demander "bots.ask_$bot" o && BOTS+=("$bot")
118
+ done
119
+ [ "${#BOTS[@]}" -gt 0 ] || [ -n "$CREER_DEVELOP" ] || { info_t bots.later; exit 0; }
120
+ choisi() { [[ " ${BOTS[*]+"${BOTS[*]}"} " == *" $1 "* ]]; }
121
+ AVEC_APP=""
122
+ { [ -n "$APP_PRESENTE" ] || choisi app; } && AVEC_APP=1
123
+
124
+ # --- Modèles des workflows -------------------------------------------------
125
+ modele_ci() {
126
+ cat <<'YAML'
127
+ # Vérifications repowarden en CI (obligatoires pour merger : repowarden proteger)
128
+ name: repowarden
129
+
130
+ on:
131
+ pull_request:
132
+ # edited : revérifie quand le titre de la PR change (message du commit en squash)
133
+ types: [opened, synchronize, reopened, edited]
134
+ push:
135
+ YAML
136
+ if [ "$FLUX" = develop ]; then
137
+ echo " branches: [$PRINCIPALE, $INTEGRATION]"
138
+ else
139
+ echo " branches: [$PRINCIPALE]"
140
+ fi
141
+ cat <<'YAML'
142
+ # Lancé par le workflow de release sur sa PR (release-auto)
143
+ workflow_dispatch:
144
+
145
+ permissions:
146
+ contents: read
147
+ YAML
148
+ [ "$FLUX" != develop ] || echo " pull-requests: write # fix-pr : PR mal visée reciblée"
149
+ cat <<'YAML'
150
+
151
+ jobs:
152
+ repowarden:
153
+ runs-on: ubuntu-latest
154
+ steps:
155
+ - uses: actions/checkout@v7
156
+ with:
157
+ fetch-depth: 0 # historique complet : messages de commit, diff depuis la base
158
+ # Outils du projet nécessaires au formatage et aux tests, par exemple :
159
+ # - uses: actions/setup-node@v7
160
+ # with: { node-version: 24, cache: npm }
161
+ # - run: npm ci
162
+ - uses: simdevWarden/repowarden@v4
163
+ YAML
164
+ if [ "$FLUX" = develop ]; then
165
+ cat <<'YAML'
166
+ with:
167
+ # strict: true # outil absent bloquant, une fois l'outillage installé ci-dessus
168
+ # Seule la branche d'intégration entre dans la principale : une PR
169
+ # de travail mal visée est reciblée avant les vérifications
170
+ fix-pr: "true"
171
+ YAML
172
+ else
173
+ cat <<'YAML'
174
+ # with:
175
+ # strict: true # outil absent bloquant, une fois l'outillage installé ci-dessus
176
+ YAML
177
+ fi
178
+ }
179
+
180
+ modele_release() {
181
+ if [ "$FLUX" = develop ]; then
182
+ cat <<YAML
183
+ # Releases, flux $INTEGRATION → $PRINCIPALE (mode tag) : préversion vX.Y.Z-next.N le
184
+ # soir si $INTEGRATION a changé ; le vendredi matin (ou à la main), une demande
185
+ # de livraison $INTEGRATION → $PRINCIPALE, qui publie le tag vX.Y.Z et la release.
186
+ name: release
187
+
188
+ on:
189
+ push:
190
+ branches: [$PRINCIPALE, $INTEGRATION]
191
+ schedule:
192
+ - cron: "0 19 * * *" # préversion du soir, si $INTEGRATION a changé
193
+ - cron: "0 7 * * 5" # fenêtre de livraison : le vendredi matin
194
+ workflow_dispatch: # à la main (et urgence) : préversion et demande de livraison
195
+
196
+ # Une file par branche et par déclencheur ; une demande manuelle ou planifiée
197
+ # remplace celle en attente ; un push n'annule jamais un run en cours
198
+ concurrency:
199
+ group: release-\${{ github.ref_name }}-\${{ github.event.schedule || github.event_name }}
200
+ cancel-in-progress: \${{ github.event_name != 'push' }}
201
+
202
+ permissions: {}
203
+
204
+ jobs:
205
+ release:
206
+ uses: simdevWarden/repowarden/.github/workflows/release-auto.yml@v4
207
+ permissions: { contents: write, pull-requests: write, actions: write, checks: read, statuses: write }
208
+ with:
209
+ mode: tag
210
+ integration-branch: $INTEGRATION
211
+ main-branch: $PRINCIPALE
212
+ delivery-schedule: "0 7 * * 5" # même cron que la fenêtre ci-dessus
213
+ YAML
214
+ if [ -n "$AVEC_APP" ]; then
215
+ cat <<'YAML'
216
+ delivery-environment: production # « Approve and deploy » valide la livraison
217
+ app-client-id: ${{ vars.REPOWARDEN_APP_CLIENT_ID }} # la GitHub App merge la livraison approuvée
218
+ secrets:
219
+ app-key: ${{ secrets.REPOWARDEN_APP_KEY }}
220
+ YAML
221
+ fi
222
+ else
223
+ cat <<'YAML'
224
+ # Releases automatiques : à chaque merge sur la branche principale, la version
225
+ # (semver) est calculée depuis les Conventional Commits ; une PR « release
226
+ # x.y.z » est validée par la CI puis mergée, et le tag et la release créés.
227
+ name: release
228
+
229
+ on:
230
+ push:
231
+ YAML
232
+ echo " branches: [$PRINCIPALE]"
233
+ cat <<'YAML'
234
+ workflow_dispatch:
235
+
236
+ # Une release à la fois
237
+ concurrency:
238
+ group: release
239
+ cancel-in-progress: false
240
+
241
+ permissions: {}
242
+
243
+ jobs:
244
+ release:
245
+ uses: simdevWarden/repowarden/.github/workflows/release-auto.yml@v4
246
+ permissions: { contents: write, pull-requests: write, actions: write, checks: read, statuses: write }
247
+ YAML
248
+ if [ -n "$AVEC_APP" ]; then
249
+ cat <<'YAML'
250
+ with:
251
+ app-client-id: ${{ vars.REPOWARDEN_APP_CLIENT_ID }}
252
+ secrets:
253
+ app-key: ${{ secrets.REPOWARDEN_APP_KEY }}
254
+ YAML
255
+ fi
256
+ fi
257
+ }
258
+
259
+ modele_nettoyage() {
260
+ cat <<'YAML'
261
+ # Branches de PR supprimées après merge (la suppression automatique de GitHub
262
+ # est désactivée dans le flux develop : elle supprimerait la branche
263
+ # d'intégration au merge de la livraison). Les branches longues et toute
264
+ # branche encore source d'une PR ouverte sont conservées.
265
+ name: nettoyage
266
+
267
+ on:
268
+ pull_request:
269
+ types: [closed]
270
+
271
+ permissions: {}
272
+
273
+ jobs:
274
+ nettoyage:
275
+ uses: simdevWarden/repowarden/.github/workflows/nettoyage-branches.yml@v4
276
+ permissions:
277
+ contents: write
278
+ pull-requests: read
279
+ YAML
280
+ }
281
+
282
+ fichier_du() { # $1 bot → REPLY : chemin de son workflow (vide : aucun)
283
+ case "$1" in
284
+ ci) REPLY=.github/workflows/repowarden.yml ;;
285
+ release) REPLY=.github/workflows/release.yml ;;
286
+ nettoyage) REPLY=.github/workflows/nettoyage.yml ;;
287
+ *) REPLY="" ;;
288
+ esac
289
+ }
290
+
291
+ # --- Aperçu, puis confirmation ---------------------------------------------
292
+ echo >&2
293
+ step_t bots.preview
294
+ [ -z "$CREER_DEVELOP" ] || info_t bots.prev_develop "$INTEGRATION" "$PRINCIPALE"
295
+ if [ "$FLUX" = develop ]; then
296
+ info_t bots.prev_conf "$INTEGRATION"
297
+ [ -n "$AVEC_APP" ] || info_t bots.prev_review
298
+ fi
299
+ for bot in ${BOTS[@]+"${BOTS[@]}"}; do
300
+ fichier_du "$bot"
301
+ f="$REPLY"
302
+ if [ -n "$f" ]; then
303
+ if [ -e "$f" ]; then
304
+ ecart="$(diff <("modele_$bot") "$f" | grep -c '^[<>]' || true)"
305
+ info_t bots.prev_kept "$f" "$ecart"
306
+ else
307
+ info_t bots.prev_file "$f"
308
+ fi
309
+ else
310
+ info_t "bots.prev_$bot"
311
+ fi
312
+ done
313
+ demander bots.ask_confirm n || { info_t bots.later; exit 0; }
314
+
315
+ # --- Application -----------------------------------------------------------
316
+ echecs=0
317
+ lancer() { # $1 bot, puis la commande : une erreur n'arrête pas les autres
318
+ local bot="$1"
319
+ shift
320
+ echo >&2
321
+ step_t "bots.run_$bot"
322
+ if ! "$@"; then
323
+ err_t bots.failed_one "$bot"
324
+ echecs=$((echecs + 1))
325
+ fi
326
+ }
327
+
328
+ if [ -n "$CREER_DEVELOP" ]; then
329
+ sha="$(gh api "repos/$REPO/git/ref/heads/$DEFAUT" --jq .object.sha)"
330
+ gh api -X POST "repos/$REPO/git/refs" -f "ref=refs/heads/$INTEGRATION" -f "sha=$sha" --silent
331
+ ok_t bots.develop_created "$INTEGRATION" "$DEFAUT"
332
+ fi
333
+ if [ "$FLUX" = develop ]; then
334
+ conf="$GIT_TOPLEVEL/.repowarden.conf"
335
+ git config -f "$conf" repowarden.integrationBranch "$INTEGRATION"
336
+ git config -f "$conf" --get repowarden.protectedBranches >/dev/null ||
337
+ git config -f "$conf" repowarden.protectedBranches "$PRINCIPALE $INTEGRATION"
338
+ if [ -z "$AVEC_APP" ]; then
339
+ git config -f "$conf" --get repowarden.deliveryApproval >/dev/null ||
340
+ git config -f "$conf" repowarden.deliveryApproval review
341
+ fi
342
+ ok_t bots.conf_written .repowarden.conf
343
+ fi
344
+ for bot in ${BOTS[@]+"${BOTS[@]}"}; do
345
+ fichier_du "$bot"
346
+ f="$REPLY"
347
+ if [ -n "$f" ]; then
348
+ if [ -e "$f" ]; then
349
+ info_t bots.kept "$f"
350
+ else
351
+ mkdir -p "${f%/*}"
352
+ "modele_$bot" >"$f"
353
+ ok_t bots.written "$f"
354
+ fi
355
+ fi
356
+ done
357
+ choisi tickets && lancer tickets bash "$BIN/tickets" init --repo "$REPO"
358
+ choisi app && lancer app bash "$BIN/app" init --repo "$REPO"
359
+ choisi proteger && lancer proteger bash "$BIN/proteger" --repo "$REPO"
360
+ choisi npm && lancer npm bash "$BIN/npm-publication" --repo "$REPO"
361
+
362
+ echo >&2
363
+ if [ "$echecs" = 0 ]; then
364
+ ok_t bots.done
365
+ else
366
+ attention_t bots.done_errors "$echecs"
367
+ fi
368
+ [ -z "$(git status --porcelain -- .github .repowarden.conf 2>/dev/null)" ] || info_t bots.commit_files
369
+ dim_t bots.verify
370
+ [ "$echecs" = 0 ]
package/bin/migrer ADDED
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env bash
2
+ # Passage de repogarde à repowarden dans le projet courant (docs/migration-v4.md) :
3
+ # repowarden migrer applique la migration dans l'arbre de travail
4
+ # repowarden migrer --pr sur une branche chore/migration-repowarden-vN :
5
+ # commit, push et PR (rien si une PR est ouverte)
6
+ # repowarden migrer --hooks déplace aussi les hooks lefthook résiduels de
7
+ # .git/hooks dans .git/hooks-lefthook/
8
+ # Configuration (.repowarden.conf, .repowarden/), workflows (uses: vers le
9
+ # dépôt repowarden et sa version majeure, anciens noms, permissions), puis la
10
+ # liste de ce qui reste à faire à la main. Relançable : rien n'est fait deux fois.
11
+ set -euo pipefail
12
+
13
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
14
+ # shellcheck source=../hooks/lib/common.sh
15
+ source "$ROOT/hooks/lib/common.sh"
16
+
17
+ aide() { sed -n '2,10p' "$0" | sed 's/^# \{0,1\}//'; }
18
+
19
+ PR="" HOOKS_LEFTHOOK=""
20
+ while [ $# -gt 0 ]; do
21
+ case "$1" in
22
+ --pr) PR=1 ;;
23
+ --hooks) HOOKS_LEFTHOOK=1 ;;
24
+ -h | --help) aide; exit 0 ;;
25
+ *) err_t install.unknown_arg "$1"; exit 1 ;;
26
+ esac
27
+ shift
28
+ done
29
+ [ -n "$GIT_TOPLEVEL" ] || { err_t install.not_repo; exit 1; }
30
+ cd "$GIT_TOPLEVEL"
31
+
32
+ # Version majeure dont le tag flottant (vN) est publié : celle de repowarden ;
33
+ # préversion d'une nouvelle majeure (X.0.0-next) : X-1, vX n'existant pas encore
34
+ majeure_r() {
35
+ local v
36
+ repowarden_version_r "$ROOT"
37
+ v="$REPLY"
38
+ REPLY="${v%%.*}"
39
+ REPLY="${REPLY%%-*}"
40
+ [[ "$v" != *.0.0-* ]] || REPLY=$((REPLY - 1))
41
+ }
42
+ if [ -n "${REPOWARDEN_MAJEURE:-}" ]; then
43
+ MAJEURE="$REPOWARDEN_MAJEURE"
44
+ else
45
+ majeure_r
46
+ MAJEURE="$REPLY"
47
+ fi
48
+ [[ "$MAJEURE" =~ ^[1-9][0-9]*$ ]] || { err_t mig.no_major "$MAJEURE"; exit 1; }
49
+ BRANCHE="chore/migration-repowarden-v$MAJEURE"
50
+
51
+ if [ -n "$PR" ]; then
52
+ command -v gh >/dev/null || { err_t bot.no_gh; exit 1; }
53
+ [ -z "$(git status --porcelain)" ] || { err_t mig.dirty; exit 1; }
54
+ if [ -n "$(gh pr list --head "$BRANCHE" --state open --json number --jq '.[].number' 2>/dev/null)" ]; then
55
+ info_t mig.pr_open "$BRANCHE"
56
+ exit 0
57
+ fi
58
+ git switch -q -c "$BRANCHE"
59
+ fi
60
+
61
+ FAITS=0
62
+ fait() { FAITS=$((FAITS + 1)); ok_t "$@"; }
63
+
64
+ # Fichier réécrit par sed (-E), sans -i (BSD et GNU diffèrent)
65
+ reecrire() { # $1 fichier, $2… expressions sed
66
+ local f="$1" tmp
67
+ shift
68
+ tmp="$(mktemp)"
69
+ sed -E "$@" "$f" >"$tmp"
70
+ if cmp -s "$f" "$tmp"; then
71
+ rm -f "$tmp"
72
+ return 1
73
+ fi
74
+ cat "$tmp" >"$f"
75
+ rm -f "$tmp"
76
+ }
77
+
78
+ # 1. Configuration du projet
79
+ if [ -f .repogarde.conf ]; then
80
+ if [ -e .repowarden.conf ]; then
81
+ attention_t mig.both .repogarde.conf .repowarden.conf
82
+ else
83
+ git mv .repogarde.conf .repowarden.conf 2>/dev/null || mv .repogarde.conf .repowarden.conf
84
+ reecrire .repowarden.conf -e 's/repogarde/repowarden/g' \
85
+ -e "s/^([[:space:]]*version[[:space:]]*=[[:space:]]*).*/\\1$MAJEURE/" || true
86
+ fait mig.conf
87
+ fi
88
+ fi
89
+ if [ -d .repogarde ]; then
90
+ if [ -e .repowarden ]; then
91
+ attention_t mig.both .repogarde/ .repowarden/
92
+ else
93
+ git mv .repogarde .repowarden 2>/dev/null || mv .repogarde .repowarden
94
+ fait mig.dir
95
+ fi
96
+ fi
97
+
98
+ # 2. Workflows
99
+ secrets=""
100
+ for wf in .github/workflows/*.yml .github/workflows/*.yaml; do
101
+ [ -f "$wf" ] || continue
102
+ grep -qi repogarde "$wf" || continue
103
+ # shellcheck disable=SC2013 # noms sans espace
104
+ for nom in $(grep -oE '(secrets|vars)\.REPOGARDE_[A-Z0-9_]+' "$wf" | sort -u); do
105
+ secrets="$secrets ${nom/REPOGARDE_/REPOWARDEN_}"
106
+ done
107
+ # uses: vers l'ancien dépôt, toute référence (tag, branche d'essai) ;
108
+ # entrée retirée (version désormais fixée par uses:) ; anciens noms ;
109
+ # tickets.yml de la v4 : création de branches (contents: write)
110
+ change=""
111
+ reecrire "$wf" \
112
+ -e "s#(uses:[[:space:]]*)[A-Za-z0-9_.-]+/repogarde((/[^@[:space:]]+)?)@[^[:space:]]+#\\1simdevWarden/repowarden\\2@v$MAJEURE#" \
113
+ -e '/^[[:space:]]*repogarde-ref:/d' \
114
+ -e 's#SimBienvenueHoulBoumi/repogarde#simdevWarden/repowarden#g' \
115
+ -e 's/repogarde/repowarden/g' -e 's/REPOGARDE_/REPOWARDEN_/g' && change=1
116
+ # permissions du job (pas celles du workflow, souvent « {} »)
117
+ if grep -q 'repowarden/.github/workflows/tickets.yml' "$wf"; then
118
+ reecrire "$wf" -e '/^[[:space:]]*permissions:[[:space:]]*\{[^}]*issues:/{/contents:/!s/\{[[:space:]]*/{ contents: write, /;}' && change=1
119
+ fi
120
+ [ -z "$change" ] || fait mig.workflow "$wf"
121
+ done
122
+ if [ -f .github/workflows/repogarde.yml ] && [ ! -e .github/workflows/repowarden.yml ]; then
123
+ git mv .github/workflows/repogarde.yml .github/workflows/repowarden.yml 2>/dev/null ||
124
+ mv .github/workflows/repogarde.yml .github/workflows/repowarden.yml
125
+ fait mig.renamed .github/workflows/repogarde.yml .github/workflows/repowarden.yml
126
+ fi
127
+
128
+ # Suivi des tickets d'une version antérieure : sans l'événement push, le
129
+ # premier push d'une branche de ticket n'ouvre pas la PR brouillon préremplie
130
+ # (ni revue, assignation, fermeture : statuts figés). Section on: remplacée
131
+ # par celle du modèle (repowarden tickets init), le reste gardé.
132
+ for wf in .github/workflows/*.yml .github/workflows/*.yaml; do
133
+ [ -f "$wf" ] || continue
134
+ grep -q 'repowarden/.github/workflows/tickets.yml' "$wf" || continue
135
+ grep -qE '^ push:' "$wf" && continue
136
+ tmp="$(mktemp)"
137
+ awk '
138
+ /^on:/ {
139
+ print "on:"
140
+ print " pull_request_target:"
141
+ print " types: [opened, reopened, edited, synchronize, ready_for_review, converted_to_draft, closed]"
142
+ print " pull_request_review:"
143
+ print " types: [submitted]"
144
+ print " issues:"
145
+ print " types: [opened, labeled, assigned, closed]"
146
+ print " push:"
147
+ print " branches-ignore: [main, master, develop]"
148
+ dans = 1
149
+ next
150
+ }
151
+ dans && /^[^ #]/ { dans = 0 }
152
+ !dans { print }
153
+ ' "$wf" >"$tmp"
154
+ cat "$tmp" >"$wf"
155
+ rm -f "$tmp"
156
+ fait mig.tickets_events "$wf"
157
+ info_t mig.tickets_labels
158
+ done
159
+
160
+ # 3. Hooks lefthook résiduels (lefthook n'est plus pris en charge)
161
+ hooks_dir="$(git rev-parse --git-common-dir)/hooks"
162
+ residuels=""
163
+ for h in "$hooks_dir"/*; do
164
+ [ -f "$h" ] && [[ "$h" != *.sample ]] && grep -qs lefthook "$h" && residuels="$residuels ${h##*/}"
165
+ done
166
+ if [ -n "$residuels" ]; then
167
+ if [ -n "$HOOKS_LEFTHOOK" ]; then
168
+ mkdir -p "$hooks_dir-lefthook"
169
+ for h in $residuels; do mv "$hooks_dir/$h" "$hooks_dir-lefthook/$h"; done
170
+ ok_t mig.hooks_moved "$residuels" "$hooks_dir-lefthook/"
171
+ else
172
+ attention_t mig.hooks_left "$residuels"
173
+ fi
174
+ fi
175
+ for f in lefthook.yml lefthook.yaml .lefthook.yml .lefthook.yaml; do
176
+ [ -f "$f" ] && attention_t mig.lefthook_conf "$f"
177
+ done
178
+
179
+ # 4. À faire à la main
180
+ [ -z "$secrets" ] || attention_t mig.secrets "$secrets"
181
+ restants="$(git grep -il repogarde -- . ':!CHANGELOG.md' 2>/dev/null | tr '\n' ' ' || true)"
182
+ [ -z "$restants" ] || info_t mig.remaining "$restants"
183
+
184
+ if [ "$FAITS" = 0 ]; then
185
+ ok_t mig.nothing
186
+ if [ -n "$PR" ]; then
187
+ git switch -q -
188
+ git branch -q -D "$BRANCHE"
189
+ fi
190
+ exit 0
191
+ fi
192
+ info_t mig.next
193
+
194
+ [ -n "$PR" ] || exit 0
195
+ git add -A
196
+ t mig.commit_subject "$MAJEURE"
197
+ sujet="$REPLY"
198
+ t mig.commit_body
199
+ bash "$ROOT/bin/commit" -m "$sujet" --type chore --scope migration --body "$REPLY"
200
+ git push -q -u origin "$BRANCHE"
201
+ t mig.pr_body
202
+ corps="$REPLY"
203
+ t mig.pr_after
204
+ gh pr create --head "$BRANCHE" --title "chore(migration): $sujet" --body "$corps"$'\n\n'"$REPLY" >/dev/null
205
+ ok_t mig.pr_created "$BRANCHE"