repowarden 4.9.0 → 4.9.1-next.5

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 CHANGED
@@ -71,6 +71,13 @@ demander() {
71
71
  }
72
72
 
73
73
  brand "bots · $REPO"
74
+ # Dépôt privé d'une offre gratuite : ni protection, ni rulesets, ni merge
75
+ # automatique ; dit avant toute question, pour décider en connaissance de cause
76
+ SANS_RULESETS=""
77
+ if github_sans_rulesets "$REPO"; then
78
+ SANS_RULESETS=1
79
+ attention_t bots.plan_limited "$REPO"
80
+ fi
74
81
  demander bots.ask_global n || { info_t bots.later; exit 0; }
75
82
 
76
83
  # --- Flux ------------------------------------------------------------------
@@ -125,6 +132,11 @@ done
125
132
  choisi() { [[ " ${BOTS[*]+"${BOTS[*]}"} " == *" $1 "* ]]; }
126
133
  AVEC_APP=""
127
134
  { [ -n "$APP_PRESENTE" ] || choisi app; } && AVEC_APP=1
135
+ # Livraison approuvée par l'environnement (« Approve and deploy ») : avec
136
+ # l'App, sauf sur un dépôt sans rulesets (ni règle de déploiement exigée, ni
137
+ # environnement avec approbation sur un dépôt privé gratuit) : revue de sa PR
138
+ LIVRAISON_ENV=""
139
+ [ -z "$AVEC_APP" ] || [ -n "$SANS_RULESETS" ] || LIVRAISON_ENV=1
128
140
 
129
141
  # --- Livraison (flux develop, releases) : mode, rythme, préversion -----------
130
142
  # Défauts : le release.yml d'aujourd'hui (approbation, regroupé, préversion
@@ -133,7 +145,10 @@ LIVRAISON_AUTO="" A_CHAQUE_TICKET="" PREVERSION_OBLIGATOIRE=1
133
145
  if [ "$FLUX" = develop ] && choisi release; then
134
146
  echo >&2
135
147
  dim_t bots.desc_delivery
136
- if demander bots.ask_delivery_auto n; then
148
+ if [ -n "$SANS_RULESETS" ]; then
149
+ # Livraison automatique = merge automatique, ignoré par GitHub ici
150
+ info_t bots.no_delivery_auto
151
+ elif demander bots.ask_delivery_auto n; then
137
152
  LIVRAISON_AUTO=1
138
153
  # Le merge automatique passe par la GitHub App (jeton des Actions : pas de release)
139
154
  [ -n "$AVEC_APP" ] || attention_t bots.auto_no_app
@@ -369,7 +384,7 @@ YAML
369
384
  echo " preversion-obligatoire: false # version stable possible sans préversion"
370
385
  if [ -n "$LIVRAISON_AUTO" ]; then
371
386
  echo " delivery-auto: true # la GitHub App merge la livraison, vérifications vertes"
372
- elif [ -n "$AVEC_APP" ]; then
387
+ elif [ -n "$LIVRAISON_ENV" ]; then
373
388
  echo " delivery-environment: production # « Approve and deploy » valide la livraison"
374
389
  fi
375
390
  if [ -n "$AVEC_APP" ]; then
@@ -479,7 +494,7 @@ if [ "$FLUX" = develop ]; then
479
494
  info_t bots.prev_conf "$INTEGRATION"
480
495
  if [ -n "$LIVRAISON_AUTO" ]; then
481
496
  info_t bots.prev_conf_auto
482
- elif [ -z "$AVEC_APP" ]; then
497
+ elif [ -z "$LIVRAISON_ENV" ]; then
483
498
  info_t bots.prev_review
484
499
  fi
485
500
  fi
@@ -545,7 +560,7 @@ if [ "$FLUX" = develop ]; then
545
560
  # Choix explicite : remplace une approbation déjà réglée
546
561
  [ "$(git config -f "$conf" --get repowarden.deliveryApproval || true)" = auto ] ||
547
562
  git config -f "$conf" repowarden.deliveryApproval auto
548
- elif [ -z "$AVEC_APP" ]; then
563
+ elif [ -z "$LIVRAISON_ENV" ]; then
549
564
  git config -f "$conf" --get repowarden.deliveryApproval >/dev/null ||
550
565
  git config -f "$conf" repowarden.deliveryApproval review
551
566
  fi
@@ -46,7 +46,14 @@ if ! node -e 'const [a,b]=process.argv[1].split(".").map(Number);process.exit(a>
46
46
  fi
47
47
  cd "$GIT_TOPLEVEL"
48
48
  [ -f package.json ] || { err_t npm.no_package; exit 1; }
49
- [ -n "$REPO" ] || REPO="$(gh repo view --json nameWithOwner -q .nameWithOwner)"
49
+ if [ -z "$REPO" ]; then
50
+ REPO="$(gh repo view --json nameWithOwner -q .nameWithOwner)"
51
+ else
52
+ # Dépôt vérifié avant toute action : une faute de frappe (« own/nom, »)
53
+ # serait sinon prise plus loin pour un dépôt sans release
54
+ [[ "$REPO" =~ ^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$ ]] || { err_t npm.bad_repo "$REPO"; exit 1; }
55
+ gh repo view "$REPO" --json nameWithOwner >/dev/null || { err_t npm.repo_unreachable "$REPO"; exit 1; }
56
+ fi
50
57
  name="$(node -p 'require("./package.json").name')"
51
58
  # Préfixe des jetons temporaires de l'ancien parcours, révoqués à la fin
52
59
  TOKEN_PREFIX="repowarden-ci-${REPO##*/}"
@@ -133,8 +140,17 @@ publie() { [ -n "$(npm view "$name" name 2>/dev/null || true)" ]; }
133
140
  # créé ni stocké, aucun mot de passe saisi par repowarden
134
141
  if ! publie; then
135
142
  step_t npm.step_first
136
- tag="$(gh release view --repo "$REPO" --json tagName -q .tagName 2>/dev/null || true)"
143
+ # Seul « release not found » signifie « aucune release » ; toute autre
144
+ # erreur de gh (réseau, droits) est rapportée telle quelle
145
+ gh_err="$(mktemp "${TMPDIR:-/tmp}/repowarden-gh.XXXXXX")"
146
+ tag="$(gh release view --repo "$REPO" --json tagName -q .tagName 2>"$gh_err")" || tag=""
147
+ msg="$(cat "$gh_err")"
148
+ rm -f "$gh_err"
137
149
  if [ -z "$tag" ]; then
150
+ if [ -n "$msg" ] && ! grep -qi 'release not found' <<<"$msg"; then
151
+ err_t npm.release_error "$REPO" "$msg"
152
+ exit 1
153
+ fi
138
154
  attention_t npm.no_release "$name"
139
155
  exit 0
140
156
  fi
package/bin/proteger CHANGED
@@ -404,9 +404,7 @@ erreur_github() {
404
404
  # en 403) : les réglages du dépôt restent appliqués, les rulesets sautés avec
405
405
  # un message clair au lieu d'un arrêt sans explication
406
406
  RULESETS_OK=1
407
- if ! reponse="$(gh api "repos/$REPO/rulesets" 2>&1)" && [[ "$reponse" == *"403"* ]]; then
408
- RULESETS_OK=""
409
- fi
407
+ ! github_sans_rulesets "$REPO" || RULESETS_OK=""
410
408
  ANCIENS=""
411
409
  [ -z "$RULESETS_OK" ] || anciens_rulesets
412
410
 
@@ -459,7 +457,11 @@ if [ -n "$integration" ] && ! gh api "repos/$REPO/labels/urgent" --silent 2>/dev
459
457
  fi
460
458
 
461
459
  if [ -n "$DRY_RUN" ]; then
462
- [ -n "$RULESETS_OK" ] || attention_t ci.proteger.rulesets_unavailable "$REPO"
460
+ if [ -z "$RULESETS_OK" ]; then
461
+ attention_t ci.proteger.rulesets_unavailable "$REPO"
462
+ # Même offre : merge automatique ignoré par GitHub (PATCH accepté)
463
+ [ "$auto_merge" = false ] || attention_t ci.proteger.automerge_unavailable
464
+ fi
463
465
  printf '%s\n' "${rulesets[@]}"
464
466
  [ -z "$env_json" ] || printf '%s\n' "$env_json"
465
467
  exit 0
@@ -472,15 +474,42 @@ if [ -z "$RULESETS_OK" ] && [ -n "$integration" ]; then
472
474
  squash=false
473
475
  attention_t ci.proteger.no_squash
474
476
  fi
477
+ echecs=0
478
+ # Appel de l'API dont le refus n'arrête pas le reste : $1 clé i18n de ce qui
479
+ # est réglé, puis la commande ; refus affiché avec le détail de GitHub (sans
480
+ # --silent, gh garde la réponse) et compté comme échec
481
+ appliquer() {
482
+ local quoi="$1" reponse detail
483
+ shift
484
+ reponse="$("$@" 2>&1)" && return 0
485
+ erreur_github "$reponse"
486
+ detail="$REPLY"
487
+ t "$quoi"
488
+ err_t ci.proteger.settings_error "$REPLY" "$detail"
489
+ echecs=$((echecs + 1))
490
+ return 1
491
+ }
475
492
  settings=(-F allow_squash_merge="$squash" -F allow_merge_commit="$merge_commit" -F allow_rebase_merge=false
476
- -F delete_branch_on_merge="$delete_on_merge" -F allow_auto_merge="$auto_merge"
477
- -f squash_merge_commit_title=PR_TITLE -f squash_merge_commit_message=PR_BODY)
493
+ -F delete_branch_on_merge="$delete_on_merge" -F allow_auto_merge="$auto_merge")
494
+ # Titre et corps du squash seulement si le squash est autorisé : envoyés avec
495
+ # allow_squash_merge=false (dépôt sans rulesets, flux develop), GitHub refuse
496
+ # tout le PATCH en 422 « Validation Failed »
497
+ [ "$squash" = false ] || settings+=(-f squash_merge_commit_title=PR_TITLE -f squash_merge_commit_message=PR_BODY)
478
498
  [ -z "$integration" ] || settings+=(-f default_branch="$integration")
479
- gh api -X PATCH "repos/$REPO" --silent "${settings[@]}"
480
- gh api -X PUT "repos/$REPO/actions/permissions/workflow" --silent \
481
- -f default_workflow_permissions=read -F can_approve_pull_request_reviews=true
499
+ if appliquer ci.proteger.what_repo gh api -X PATCH "repos/$REPO" "${settings[@]}" &&
500
+ [ "$auto_merge" = true ] &&
501
+ [ "$(gh api "repos/$REPO" --jq .allow_auto_merge 2>/dev/null || true)" != true ]; then
502
+ # Merge automatique demandé mais resté désactivé : GitHub l'ignore sans
503
+ # erreur (dépôt privé d'une offre gratuite)
504
+ attention_t ci.proteger.automerge_ignored "$REPO"
505
+ if [ -n "$integration" ] && [ "$DELIVERY" = auto ]; then
506
+ err_t ci.proteger.automerge_delivery
507
+ echecs=$((echecs + 1))
508
+ fi
509
+ fi
510
+ appliquer ci.proteger.what_workflow gh api -X PUT "repos/$REPO/actions/permissions/workflow" \
511
+ -f default_workflow_permissions=read -F can_approve_pull_request_reviews=true || true
482
512
 
483
- echecs=0
484
513
  [ -n "$RULESETS_OK" ] || { attention_t ci.proteger.rulesets_unavailable "$REPO"; rulesets=(); }
485
514
  for ruleset in ${rulesets[@]+"${rulesets[@]}"}; do
486
515
  name="$(sed -n 's/^ "name": "\(.*\)",$/\1/p' <<<"$ruleset")"
@@ -535,7 +564,9 @@ while IFS=$'\t' read -r id name _; do
535
564
  echecs=$((echecs + 1))
536
565
  done <<<"$ANCIENS"
537
566
  if [ -n "$env_json" ]; then
538
- gh api -X PUT "repos/$REPO/environments/$ENVIRONMENT" --input - --silent <<<"$env_json"
567
+ appliquer ci.proteger.what_environment gh api -X PUT "repos/$REPO/environments/$ENVIRONMENT" --input - <<<"$env_json" || env_json=""
568
+ fi
569
+ if [ -n "$env_json" ]; then
539
570
  policies="$(gh api "repos/$REPO/environments/$ENVIRONMENT/deployment-branch-policies" \
540
571
  --jq '.branch_policies[] | "\(.type):\(.name)"')"
541
572
  for policy in $(for b in $existing; do echo "branch:$b"; done) "tag:v*"; do
package/bin/tickets CHANGED
@@ -2,7 +2,7 @@
2
2
  # Prépare le suivi des tickets d'un dépôt GitHub (relançable sans risque) :
3
3
  # repowarden tickets init [--repo propriétaire/nom] [--sans-validation-auto]
4
4
  # repowarden tickets sync branches locales des tickets qui me sont assignés
5
- # (aussi automatique : réglage ticketSync = auto|manual|off)
5
+ # (automatique si réglage ticketSync = auto ; défaut manual)
6
6
  # - étiquettes : « validé », les statuts (à valider, backlog, en cours,
7
7
  # en relecture, préprod, done) et les types (type: feat, type: fix…), qui
8
8
  # donnent le préfixe de la branche du ticket ;
@@ -24,11 +24,30 @@ pr_tickets_r() {
24
24
  REPLY="${found# }"
25
25
  }
26
26
 
27
- # Statut $2 sur le ticket $1 : remplace les autres étiquettes « statut: »
27
+ # Rang d'un statut dans le cycle (REPLY) : à valider 0 … done 5, -1 sinon
28
+ rang_statut_r() {
29
+ case "$1" in
30
+ "$S_AVALIDER") REPLY=0 ;; "$S_BACKLOG") REPLY=1 ;; "$S_ENCOURS") REPLY=2 ;;
31
+ "$S_RELECTURE") REPLY=3 ;; "$S_PREPROD") REPLY=4 ;; "$S_DONE") REPLY=5 ;;
32
+ *) REPLY=-1 ;;
33
+ esac
34
+ }
35
+
36
+ # Statut $2 sur le ticket $1 : remplace les autres étiquettes « statut: ».
37
+ # Monotone pour un travail livré : un ticket en préprod ou done ne recule
38
+ # jamais (événement en retard, branche réapparue, réassignation…), signalé.
28
39
  statut() {
29
- local n="$1" s="$2" l retirer=()
40
+ local n="$1" s="$2" l retirer=() cible
41
+ rang_statut_r "$s"
42
+ cible="$REPLY"
30
43
  while IFS= read -r l; do
31
- [[ "$l" == "statut: "* && "$l" != "$s" ]] && retirer+=(--remove-label "$l")
44
+ [[ "$l" == "statut: "* && "$l" != "$s" ]] || continue
45
+ rang_statut_r "$l"
46
+ if [ "$REPLY" -ge 4 ] && [ "$REPLY" -gt "$cible" ]; then
47
+ notice_t tk.status_kept "$n" "${l#statut: }" "${s#statut: }"
48
+ return 0
49
+ fi
50
+ retirer+=(--remove-label "$l")
32
51
  done < <(gh issue view "$n" --json labels -q '.labels[].name' 2>/dev/null || true)
33
52
  # ${a[@]+…} : tableau vide accepté sous set -u par le bash 3.2 de macOS
34
53
  gh issue edit "$n" --add-label "$s" ${retirer[@]+"${retirer[@]}"} >/dev/null
package/ci/tickets/pr.sh CHANGED
@@ -54,6 +54,9 @@ cmd_pr() {
54
54
  done
55
55
  return 0
56
56
  fi
57
+ # PR déjà mergée, événement en retard (edited, synchronize…) : le ticket est
58
+ # livré, ni statut, ni vérification, ni merge automatique
59
+ [ "${MERGED:-false}" != true ] || return 0
57
60
 
58
61
  # Aucun ticket : si auto-create activé, créé à partir de la PR, à valider,
59
62
  # et relié à la PR ; sinon, échec de la vérification avec la marche à suivre.
@@ -37,3 +37,11 @@ forge_init() {
37
37
  }
38
38
 
39
39
  forge_init
40
+
41
+ # Dépôt GitHub $1 sans rulesets ni protection de branches (dépôt privé d'une
42
+ # offre gratuite : GitHub les refuse en 403, « Upgrade to GitHub Pro or make
43
+ # this repository public ») ; le merge automatique y est aussi ignoré
44
+ github_sans_rulesets() {
45
+ local reponse
46
+ ! reponse="$(gh api "repos/$1/rulesets" 2>&1)" && [[ "$reponse" == *"403"* ]]
47
+ }
@@ -341,6 +341,13 @@ _msg_en() {
341
341
  ci.proteger.incomplete) _M="Incomplete protection: see the errors above." ;;
342
342
  ci.proteger.rulesets_unavailable) _M="Rulesets unavailable for %s: GitHub refuses them on a private repository of a free account. Make the repository public, or move to GitHub Pro / Team; the other settings are applied." ;;
343
343
  ci.proteger.no_squash) _M="Without rulesets, the merge method cannot be enforced per branch: squash disabled (merge commit only), otherwise a squashed delivery would break the main branch history." ;;
344
+ ci.proteger.automerge_unavailable) _M="Auto-merge unavailable on this repository (GitHub ignores it on a private repository of a free account): work PRs and deliveries are merged by hand; automatic delivery impossible." ;;
345
+ ci.proteger.automerge_ignored) _M="Auto-merge requested but still disabled on %s: GitHub ignores it without an error (private repository of a free account). Work PRs and deliveries are merged by hand. Make the repository public, or move to GitHub Pro / Team, then run repowarden proteger again." ;;
346
+ ci.proteger.automerge_delivery) _M="deliveryApproval = auto cannot work without auto-merge: the delivery PR will not merge by itself." ;;
347
+ ci.proteger.settings_error) _M="%s: rejected by GitHub: %s" ;;
348
+ ci.proteger.what_repo) _M="Repository settings (merge methods, default branch)" ;;
349
+ ci.proteger.what_workflow) _M="Workflow permissions" ;;
350
+ ci.proteger.what_environment) _M="Deployment environment" ;;
344
351
  ci.proteger.tags_fallback) _M="Tags ruleset: \"GitHub Actions\" bypass refused in this organization; tags protected as on a personal account (not moved or deleted, creation allowed)." ;;
345
352
  ci.version.usage) _M="Usage: ci/version.sh next|notes|notes-livraison [ref]" ;;
346
353
  ci.version.breaking) _M="Breaking changes" ;;
@@ -393,6 +400,8 @@ _msg_en() {
393
400
  npm.npx_global) _M="npx uses a temporary folder: global hooks would point to a deleted folder. Install the package first: npm install -g repowarden, then repowarden install --global." ;;
394
401
  npm.no_npm) _M="npm required: https://nodejs.org" ;;
395
402
  npm.no_package) _M="No package.json at the repository root." ;;
403
+ npm.bad_repo) _M="Invalid repository: \"%s\". Expected form: owner/name (e.g. simdevWarden/repowarden). Nothing was done." ;;
404
+ npm.repo_unreachable) _M="Repository %s not found or not accessible with gh (see above): nothing was done." ;;
396
405
  npm.old_npm) _M="npm %s is too old: 11.15 or later required (npm install -g npm@11)." ;;
397
406
  npm.step_login) _M="1/3 npm account" ;;
398
407
  npm.logged) _M="Logged in to npm: %s" ;;
@@ -400,6 +409,7 @@ _msg_en() {
400
409
  npm.hint_2fa) _M="Enable it: npm profile enable-2fa auth-and-writes (QR code for an authenticator app), or https://www.npmjs.com/settings/%s/tfa; then run this command again." ;;
401
410
  npm.step_first) _M="2/3 First publication, from this machine (latest release)" ;;
402
411
  npm.no_release) _M="No release yet: nothing is published. Run this command again after the first release of %s." ;;
412
+ npm.release_error) _M="Cannot read the releases of %s: %s. Nothing is published or configured; run this command again." ;;
403
413
  npm.would_publish) _M="[dry-run] %s@%s would be published from release %s (npm publish, in this terminal)" ;;
404
414
  npm.publishing) _M="Publishing %s@%s (release %s): npm may ask for a validation (browser or code)." ;;
405
415
  npm.publish_failed) _M="Publication of %s refused (see above): nothing else was configured. Run this command again." ;;
@@ -446,6 +456,7 @@ _msg_en() {
446
456
  tk.waiting) _M="Ticket %s awaiting validation: a maintainer adds the \"%s\" label to the ticket." ;;
447
457
  tk.ok) _M="Ticket %s validated." ;;
448
458
  tk.merged) _M="Merged by PR #%s into %s: in pre-production, available in the next pre-release." ;;
459
+ tk.status_kept) _M="Ticket #%s kept in %s: delivered work does not go back to %s." ;;
449
460
  tk.branch_created) _M="Ticket validated: its branch \`%s\` is created from \`%s\`.\n\nTo work on it:\n\n\`\`\`bash\nrepowarden ticket %s\n\`\`\`\n\n(or \`git fetch && git switch %s\`). The first push opens the draft PR." ;;
450
461
  tk.branch_notice) _M="Branch %s created for ticket #%s" ;;
451
462
  tk.multi) _M="PR linked to several tickets (%s): one PR = one ticket, split it." ;;
@@ -533,6 +544,8 @@ _msg_en() {
533
544
  bots.ask_npm) _M="npm publishing?" ;;
534
545
  bots.desc_delivery) _M="develop → main delivery: automatic (the GitHub App merges the delivery PR as soon as the checks are green; human validation is the merge of each ticket) or human approval (default)" ;;
535
546
  bots.ask_delivery_auto) _M="Automatic delivery? (no: human approval)" ;;
547
+ bots.plan_limited) _M="%s is a private repository on a free plan: GitHub offers neither branch protection, rulesets nor auto-merge there. So these will not work: required PRs and checks, work PRs merging by themselves, automatic delivery (PRs will be merged by hand). To get everything: make the repository public, or move to GitHub Pro / Team, then run repowarden bots again." ;;
548
+ bots.no_delivery_auto) _M="Automatic delivery not offered (no auto-merge on this repository): the delivery PR will be merged by hand." ;;
536
549
  bots.auto_no_app) _M="Automatic delivery without a GitHub App: the automatic merge requires the App (repowarden app init); without it, the delivery PR has to be merged by hand." ;;
537
550
  bots.desc_rythme) _M="Pace: on every ticket (prerelease and delivery request on every merge) or grouped (default: evening prerelease, Friday delivery window)" ;;
538
551
  bots.ask_each_ticket) _M="On every ticket? (no: grouped)" ;;
@@ -540,7 +553,7 @@ _msg_en() {
540
553
  bots.preview) _M="Preview" ;;
541
554
  bots.prev_develop) _M="branch %s created on GitHub from %s" ;;
542
555
  bots.prev_conf) _M=".repowarden.conf: integrationBranch = %s, protected branches" ;;
543
- bots.prev_review) _M=".repowarden.conf: delivery approved by reviewing its PR (no GitHub App)" ;;
556
+ bots.prev_review) _M=".repowarden.conf: delivery approved by reviewing its PR (no GitHub App or approval environment)" ;;
544
557
  bots.prev_conf_auto) _M=".repowarden.conf: deliveryApproval = auto (automatic delivery)" ;;
545
558
  bots.prev_delivery_auto) _M="release.yml: automatic delivery (delivery-auto), the GitHub App merges the delivery PR as soon as the checks are green" ;;
546
559
  bots.prev_delivery_approval) _M="release.yml: delivery approved by a person" ;;
@@ -341,6 +341,13 @@ _msg_fr() {
341
341
  ci.proteger.incomplete) _M="Protection incomplète : voir les erreurs ci-dessus." ;;
342
342
  ci.proteger.rulesets_unavailable) _M="Rulesets indisponibles pour %s : GitHub les refuse sur un dépôt privé d'un compte gratuit. Rendre le dépôt public, ou passer à GitHub Pro / Team ; les autres réglages sont appliqués." ;;
343
343
  ci.proteger.no_squash) _M="Sans rulesets, le mode de merge ne peut pas être imposé par branche : squash désactivé (merge commit seul), sinon une livraison squashée casserait l'historique de la branche principale." ;;
344
+ ci.proteger.automerge_unavailable) _M="Merge automatique indisponible sur ce dépôt (GitHub l'ignore sur un dépôt privé d'un compte gratuit) : PR de travail et livraison à merger à la main ; livraison automatique impossible." ;;
345
+ ci.proteger.automerge_ignored) _M="Merge automatique demandé mais resté désactivé sur %s : GitHub l'ignore sans erreur (dépôt privé d'un compte gratuit). PR de travail et livraison à merger à la main. Rendre le dépôt public, ou passer à GitHub Pro / Team, puis relancer repowarden proteger." ;;
346
+ ci.proteger.automerge_delivery) _M="deliveryApproval = auto ne peut pas fonctionner sans merge automatique : la PR de livraison ne se mergera pas seule." ;;
347
+ ci.proteger.settings_error) _M="%s : refusé par GitHub : %s" ;;
348
+ ci.proteger.what_repo) _M="Réglages du dépôt (modes de merge, branche par défaut)" ;;
349
+ ci.proteger.what_workflow) _M="Permissions des workflows" ;;
350
+ ci.proteger.what_environment) _M="Environnement de déploiement" ;;
344
351
  ci.proteger.tags_fallback) _M="Ruleset des tags : exception « GitHub Actions » refusée dans cette organisation ; tags protégés comme sur un compte personnel (ni déplacés ni supprimés, création libre)." ;;
345
352
  ci.version.usage) _M="Usage : ci/version.sh next|notes|notes-livraison [ref]" ;;
346
353
  ci.version.breaking) _M="Changements incompatibles" ;;
@@ -393,6 +400,8 @@ _msg_fr() {
393
400
  npm.npx_global) _M="npx utilise un dossier temporaire : des hooks globaux pointeraient vers un dossier effacé. Installe d'abord le paquet : npm install -g repowarden, puis repowarden install --global." ;;
394
401
  npm.no_npm) _M="npm requis : https://nodejs.org" ;;
395
402
  npm.no_package) _M="Aucun package.json à la racine du dépôt." ;;
403
+ npm.bad_repo) _M="Dépôt invalide : « %s ». Forme attendue : propriétaire/nom (ex. simdevWarden/repowarden). Rien n'a été fait." ;;
404
+ npm.repo_unreachable) _M="Dépôt %s introuvable ou inaccessible avec gh (voir ci-dessus) : rien n'a été fait." ;;
396
405
  npm.old_npm) _M="npm %s trop ancien : 11.15 au minimum (npm install -g npm@11)." ;;
397
406
  npm.step_login) _M="1/3 Compte npm" ;;
398
407
  npm.logged) _M="Connecté à npm : %s" ;;
@@ -400,6 +409,7 @@ _msg_fr() {
400
409
  npm.hint_2fa) _M="Activer : npm profile enable-2fa auth-and-writes (QR code pour une application d'authentification), ou https://www.npmjs.com/settings/%s/tfa ; puis relancer cette commande." ;;
401
410
  npm.step_first) _M="2/3 Première publication, depuis ce poste (dernière release)" ;;
402
411
  npm.no_release) _M="Aucune release encore : rien n'est publié. Relance cette commande après la première release de %s." ;;
412
+ npm.release_error) _M="Lecture des releases de %s impossible : %s. Rien n'est publié ni configuré ; relance cette commande." ;;
403
413
  npm.would_publish) _M="[dry-run] %s@%s serait publié depuis la release %s (npm publish, dans ce terminal)" ;;
404
414
  npm.publishing) _M="Publication de %s@%s (release %s) : npm peut demander une validation (navigateur ou code)." ;;
405
415
  npm.publish_failed) _M="Publication de %s refusée (voir ci-dessus) : rien d'autre n'a été configuré. Relance cette commande." ;;
@@ -446,6 +456,7 @@ _msg_fr() {
446
456
  tk.waiting) _M="Ticket %s à valider : un mainteneur pose l'étiquette « %s » sur le ticket." ;;
447
457
  tk.ok) _M="Ticket %s validé." ;;
448
458
  tk.merged) _M="Mergé par la PR #%s dans %s : en préprod, disponible dans la prochaine préversion." ;;
459
+ tk.status_kept) _M="Ticket #%s laissé en %s : un travail livré ne repasse pas en %s." ;;
449
460
  tk.branch_created) _M="Ticket validé : sa branche \`%s\` est créée depuis \`%s\`.\n\nPour y travailler :\n\n\`\`\`bash\nrepowarden ticket %s\n\`\`\`\n\n(ou \`git fetch && git switch %s\`). Le premier push ouvre la PR en brouillon." ;;
450
461
  tk.branch_notice) _M="Branche %s créée pour le ticket #%s" ;;
451
462
  tk.multi) _M="PR liée à plusieurs tickets (%s) : une PR = un ticket, la découper." ;;
@@ -533,6 +544,8 @@ _msg_fr() {
533
544
  bots.ask_npm) _M="Publication npm ?" ;;
534
545
  bots.desc_delivery) _M="Livraison develop → main : automatique (la GitHub App merge la PR de livraison dès que les vérifications sont vertes ; la validation humaine est le merge de chaque ticket) ou approbation humaine (défaut)" ;;
535
546
  bots.ask_delivery_auto) _M="Livraison automatique ? (non : approbation humaine)" ;;
547
+ bots.plan_limited) _M="%s est un dépôt privé d'une offre gratuite : GitHub n'y offre ni protection de branches, ni rulesets, ni merge automatique. Ne fonctionneront donc pas : les PR et vérifications exigées, la fusion seule des PR de travail, la livraison automatique (les PR seront à merger à la main). Pour tout avoir : rendre le dépôt public, ou passer à GitHub Pro / Team, puis relancer repowarden bots." ;;
548
+ bots.no_delivery_auto) _M="Livraison automatique non proposée (pas de merge automatique sur ce dépôt) : la PR de livraison sera mergée à la main." ;;
536
549
  bots.auto_no_app) _M="Livraison automatique sans GitHub App : le merge automatique exige l'App (repowarden app init) ; sans elle, la PR de livraison reste à merger à la main." ;;
537
550
  bots.desc_rythme) _M="Rythme : à chaque ticket (préversion et demande de livraison à chaque merge) ou regroupé (défaut : préversion le soir, fenêtre de livraison le vendredi)" ;;
538
551
  bots.ask_each_ticket) _M="À chaque ticket ? (non : regroupé)" ;;
@@ -540,7 +553,7 @@ _msg_fr() {
540
553
  bots.preview) _M="Aperçu" ;;
541
554
  bots.prev_develop) _M="branche %s créée sur GitHub depuis %s" ;;
542
555
  bots.prev_conf) _M=".repowarden.conf : integrationBranch = %s, branches protégées" ;;
543
- bots.prev_review) _M=".repowarden.conf : livraison validée par la revue de sa PR (sans GitHub App)" ;;
556
+ bots.prev_review) _M=".repowarden.conf : livraison validée par la revue de sa PR (ni GitHub App, ni environnement d'approbation)" ;;
544
557
  bots.prev_conf_auto) _M=".repowarden.conf : deliveryApproval = auto (livraison automatique)" ;;
545
558
  bots.prev_delivery_auto) _M="release.yml : livraison automatique (delivery-auto), la GitHub App merge la PR de livraison dès que les vérifications sont vertes" ;;
546
559
  bots.prev_delivery_approval) _M="release.yml : livraison approuvée par une personne" ;;
@@ -201,15 +201,17 @@ ticket_remote_branch_r() {
201
201
  }
202
202
 
203
203
  # Branches locales des tickets qui me sont assignés (repowarden tickets sync ;
204
- # hooks post-merge et post-checkout en mode auto, au plus toutes les 15 min) :
204
+ # hooks post-merge et post-checkout seulement si ticketSync = auto est posé,
205
+ # au plus toutes les 15 min) :
205
206
  # branche distante retrouvée, ou créée et liée au ticket (validé) ; branche
206
- # locale de suivi créée sans changer la branche courante. Ticket qui ne m'est
207
+ # locale de suivi créée sans changer la branche courante (jamais pour un travail
208
+ # livré : préprod, done, ou en relecture sans branche). Ticket qui ne m'est
207
209
  # plus assigné : signalé, sa branche locale conservée. Réglage ticketSync :
208
- # auto (défaut), manual (commande seulement), off. $1 = force (commande).
210
+ # manual (défaut : commande seulement, un pull ne touche à rien), auto, off. $1 = force (commande).
209
211
  tickets_sync() {
210
212
  local force="${1:-}" stamp now last base n titre etiquettes branche b qui etat
211
213
  local miens=" "
212
- cfg_r ticketSync auto
214
+ cfg_r ticketSync manual
213
215
  [ "$REPLY" != off ] || return 0
214
216
  if [ -z "$force" ]; then
215
217
  [ "$REPLY" = auto ] && tickets_managed && command -v gh >/dev/null || return 0
@@ -231,6 +233,8 @@ tickets_sync() {
231
233
  ticket_remote_branch_r "$n"
232
234
  branche="$REPLY"
233
235
  if [ -z "$branche" ]; then
236
+ # En relecture sans branche : PR mergée, branche supprimée ; jamais recréée
237
+ grep -qxF "$S_RELECTURE" <<<"$etiquettes" && continue
234
238
  if tickets_managed && ! grep -qxF "$VALIDE" <<<"$etiquettes"; then
235
239
  info_t tk.sync.not_validated "$n"
236
240
  continue
@@ -21,6 +21,8 @@ if ! skipped branch-name && ! branch_name_valid "$BRANCH"; then
21
21
  fi
22
22
  # Branche de travail sans ticket : prévenu, jamais bloqué
23
23
  ticket_hint "$BRANCH"
24
- # Branches locales des tickets qui me sont assignés (au plus toutes les 15 min)
25
- tickets_sync || true
24
+ # Branches locales des tickets qui me sont assignés (au plus toutes les 15 min).
25
+ # Pas au clonage (ancienne référence nulle) : un clone ne touche ni GitHub ni
26
+ # les branches ; la synchronisation viendra au changement de branche suivant.
27
+ [[ "${1:-}" =~ ^0+$ ]] || tickets_sync || true
26
28
  exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repowarden",
3
- "version": "4.9.0",
3
+ "version": "4.9.1-next.5",
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",