repowarden 4.4.0 → 4.7.0
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 +45 -16
- package/bin/commit +2 -2
- package/bin/pr-checks +35 -0
- package/bin/pr-label +51 -0
- package/bin/proteger +87 -9
- package/bin/repowarden +13 -1
- package/bin/run-list +58 -0
- package/bin/run-rerun +65 -0
- package/bin/statut +1 -0
- package/bin/ticket +5 -0
- package/bin/ticket-comment +32 -0
- package/bin/ticket-label +52 -0
- package/bin/tickets +14 -1
- package/ci/tickets/issue.sh +19 -4
- package/ci/tickets/pr.sh +23 -7
- package/ci/tickets/push.sh +95 -13
- package/ci/tickets/registre.sh +19 -0
- package/ci/tickets/release.sh +1 -0
- package/hooks/lib/i18n/en.sh +41 -0
- package/hooks/lib/i18n/fr.sh +41 -0
- package/hooks/lib/tickets.sh +71 -4
- package/hooks/pre-push +89 -2
- package/package.json +4 -4
package/bin/ticket-label
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Gérer les labels d'un ticket GitHub
|
|
3
|
+
# repowarden ticket label <n> <label> [--remove]
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
|
|
7
|
+
# shellcheck source=../hooks/lib/common.sh
|
|
8
|
+
source "$ROOT/hooks/lib/common.sh"
|
|
9
|
+
# shellcheck source=../hooks/lib/i18n.sh
|
|
10
|
+
source "$ROOT/hooks/lib/i18n.sh"
|
|
11
|
+
|
|
12
|
+
aide() {
|
|
13
|
+
cat <<EOF
|
|
14
|
+
Usage: repowarden ticket label <n> <label> [--remove]
|
|
15
|
+
|
|
16
|
+
Ajoute ou retire un label sur le ticket #<n>.
|
|
17
|
+
|
|
18
|
+
Options:
|
|
19
|
+
--remove Retire le label au lieu de l'ajouter
|
|
20
|
+
|
|
21
|
+
Exemples:
|
|
22
|
+
repowarden ticket label 123 "validé"
|
|
23
|
+
repowarden ticket label 123 "statut: backlog"
|
|
24
|
+
repowarden ticket label 123 "wip" --remove
|
|
25
|
+
EOF
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
[ $# -ge 2 ] || { aide >&2; exit 1; }
|
|
29
|
+
command -v gh >/dev/null || { err_t bot.no_gh; exit 1; }
|
|
30
|
+
|
|
31
|
+
n="${1}"
|
|
32
|
+
label="${2}"
|
|
33
|
+
remove=false
|
|
34
|
+
|
|
35
|
+
shift 2
|
|
36
|
+
while [ $# -gt 0 ]; do
|
|
37
|
+
case "$1" in
|
|
38
|
+
--remove) remove=true ;;
|
|
39
|
+
*) err_t install.unknown_arg "$1"; exit 1 ;;
|
|
40
|
+
esac
|
|
41
|
+
shift
|
|
42
|
+
done
|
|
43
|
+
|
|
44
|
+
if [ "$remove" = true ]; then
|
|
45
|
+
step_t tk.label.removing "$label" "$n"
|
|
46
|
+
gh issue edit "$n" --remove-label "$label"
|
|
47
|
+
ok_t tk.label.removed "$label" "$n"
|
|
48
|
+
else
|
|
49
|
+
step_t tk.label.adding "$label" "$n"
|
|
50
|
+
gh issue edit "$n" --add-label "$label"
|
|
51
|
+
ok_t tk.label.added "$label" "$n"
|
|
52
|
+
fi
|
package/bin/tickets
CHANGED
|
@@ -123,11 +123,14 @@ permissions: {}
|
|
|
123
123
|
|
|
124
124
|
jobs:
|
|
125
125
|
tickets:
|
|
126
|
-
uses:
|
|
126
|
+
uses: simdevWarden/repowarden/.github/workflows/tickets.yml@v4
|
|
127
127
|
permissions: { contents: write, issues: write, pull-requests: write, statuses: write }
|
|
128
128
|
# GitHub App (repowarden app init) : ses actions déclenchent les autres workflows
|
|
129
|
+
# auto-create : true = crée un ticket « à valider » pour PR sans ticket,
|
|
130
|
+
# false = échec de la vérification avec la marche à suivre (défaut false)
|
|
129
131
|
with:
|
|
130
132
|
app-client-id: ${{ vars.REPOWARDEN_APP_CLIENT_ID }}
|
|
133
|
+
auto-create: false
|
|
131
134
|
secrets:
|
|
132
135
|
app-key: ${{ secrets.REPOWARDEN_APP_KEY }}
|
|
133
136
|
YAML
|
|
@@ -136,6 +139,16 @@ YAML
|
|
|
136
139
|
step_t tk.init_adopt
|
|
137
140
|
GH_REPO="$REPO" AUTO_VALIDATE="${AUTO_VALIDATE:-maintainers}" "$ROOT/ci/tickets.sh" adopter | sed 's/^/ /'
|
|
138
141
|
|
|
142
|
+
# Afficher l'état de ticketAutoCreate
|
|
143
|
+
echo
|
|
144
|
+
if git config --get repowarden.ticketAutoCreate | grep -qE '^(true|oui|yes)$'; then
|
|
145
|
+
t tk.init_auto_create_on
|
|
146
|
+
hint "$_T"
|
|
147
|
+
else
|
|
148
|
+
t tk.init_auto_create_off
|
|
149
|
+
hint "$_T"
|
|
150
|
+
fi
|
|
151
|
+
|
|
139
152
|
echo
|
|
140
153
|
ok_t tk.init_done
|
|
141
154
|
dim_t tk.init_next
|
package/ci/tickets/issue.sh
CHANGED
|
@@ -52,10 +52,22 @@ cmd_issue() {
|
|
|
52
52
|
accepter "$ISSUE"
|
|
53
53
|
;;
|
|
54
54
|
assigned)
|
|
55
|
-
# Pris en charge : sa branche (s'il est validé), en cours s'il
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
# Pris en charge : sa branche (s'il est validé), en cours s'il est
|
|
56
|
+
# validé ET assigné (quelle que soit la présence du label backlog,
|
|
57
|
+
# pour gérer la race validation+assignation quasi simultanée).
|
|
58
|
+
# Ne passe en cours que s'il n'est pas déjà plus loin (relecture, préprod, done).
|
|
59
|
+
if est_valide "$ISSUE"; then
|
|
60
|
+
creer_branche "$ISSUE"
|
|
61
|
+
local labels
|
|
62
|
+
labels="$(etiquettes "$ISSUE")"
|
|
63
|
+
if grep -qxF "$S_AVALIDER" <<<"$labels" || grep -qxF "$S_BACKLOG" <<<"$labels"; then
|
|
64
|
+
# Pas de label plus avancé (relecture, préprod, done) ?
|
|
65
|
+
grep -qxF "$S_RELECTURE" <<<"$labels" && return 0
|
|
66
|
+
grep -qxF "$S_PREPROD" <<<"$labels" && return 0
|
|
67
|
+
grep -qxF "$S_DONE" <<<"$labels" && return 0
|
|
68
|
+
statut "$ISSUE" "$S_ENCOURS"
|
|
69
|
+
fi
|
|
70
|
+
fi
|
|
59
71
|
return 0
|
|
60
72
|
;;
|
|
61
73
|
closed)
|
|
@@ -71,7 +83,10 @@ cmd_issue() {
|
|
|
71
83
|
_tr tk.reopened "$fpr" "$fbase"
|
|
72
84
|
gh issue comment "$ISSUE" --body "$_T" >/dev/null
|
|
73
85
|
notice "$_T"
|
|
86
|
+
return 0
|
|
74
87
|
fi
|
|
88
|
+
# Terminé à la main ou en production : branche restante nettoyée
|
|
89
|
+
nettoyer_branche "$ISSUE"
|
|
75
90
|
return 0
|
|
76
91
|
fi
|
|
77
92
|
# Abandonné : PR fermées, branche supprimée (un ticket terminé est
|
package/ci/tickets/pr.sh
CHANGED
|
@@ -22,6 +22,14 @@ cmd_pr() {
|
|
|
22
22
|
_tr tk.pr_backmerge_empty "$HEAD" "$BASE"
|
|
23
23
|
gh pr close "$PR" --comment "$_T" >/dev/null
|
|
24
24
|
notice "$_T"
|
|
25
|
+
# Livraison ouverte par le workflow de release : celle d'une personne
|
|
26
|
+
# n'aurait jamais d'approbation de déploiement, elle resterait bloquée
|
|
27
|
+
elif [ "$HEAD" = "${INTEGRATION:-develop}" ] && [ "${DELIVERY_WORKFLOW:-false}" = true ] &&
|
|
28
|
+
[[ "${AUTHOR:-}" != *"[bot]" ]]; then
|
|
29
|
+
_tr tk.pr_delivery_manual "$HEAD" "$BASE" "$HEAD" "$HEAD"
|
|
30
|
+
gh pr close "$PR" --comment "$_T" >/dev/null
|
|
31
|
+
notice "$_T"
|
|
32
|
+
return 0
|
|
25
33
|
fi
|
|
26
34
|
fi
|
|
27
35
|
# Bots de dépendances, PR de release, livraisons et retours : pas de ticket
|
|
@@ -47,14 +55,22 @@ cmd_pr() {
|
|
|
47
55
|
return 0
|
|
48
56
|
fi
|
|
49
57
|
|
|
50
|
-
# Aucun ticket : créé à partir de la PR, à valider,
|
|
58
|
+
# Aucun ticket : si auto-create activé, créé à partir de la PR, à valider,
|
|
59
|
+
# et relié à la PR ; sinon, échec de la vérification avec la marche à suivre.
|
|
51
60
|
if [ -z "$tickets" ]; then
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
--
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
if [ "${AUTO_CREATE:-false}" = true ]; then
|
|
62
|
+
_tr tk.created_body "$PR"
|
|
63
|
+
url="$(gh issue create --title "$(gh pr view "$PR" --json title -q .title)" \
|
|
64
|
+
--body "$_T" --label "$S_AVALIDER")"
|
|
65
|
+
tickets="${url##*/}"
|
|
66
|
+
gh pr edit "$PR" --body "$(printf '%s\n\nTicket : #%s\n' "$body" "$tickets")" >/dev/null
|
|
67
|
+
notice_t tk.created "$tickets" "$PR"
|
|
68
|
+
else
|
|
69
|
+
_tr tk.created_body "$PR"
|
|
70
|
+
gh api "repos/$GH_REPO/statuses/$HEAD_SHA" -f state=failure -f context=ticket \
|
|
71
|
+
-f description="PR sans ticket : créez-en un avec 'repowarden ticket nouveau \"<titre>\"' ou citez 'Ticket : #N' dans la description" >/dev/null
|
|
72
|
+
return 0
|
|
73
|
+
fi
|
|
58
74
|
fi
|
|
59
75
|
|
|
60
76
|
# Statut du travail (seulement pour un ticket validé)
|
package/ci/tickets/push.sh
CHANGED
|
@@ -4,12 +4,91 @@
|
|
|
4
4
|
# Chargé par ci/tickets.sh (après hooks/lib/common.sh et hooks/lib/tickets.sh).
|
|
5
5
|
# shellcheck disable=SC2154 # statuts, réglages et compteurs définis dans les autres modules
|
|
6
6
|
|
|
7
|
+
# REPLY = rang d'impact du sujet de commit $1 : 0 rupture, 1 feat, 2 fix/perf,
|
|
8
|
+
# 3 autre type conforme
|
|
9
|
+
rang_impact() {
|
|
10
|
+
case "$1" in
|
|
11
|
+
*"!: "*) REPLY=0 ;;
|
|
12
|
+
feat*:*) REPLY=1 ;;
|
|
13
|
+
fix*:* | perf*:*) REPLY=2 ;;
|
|
14
|
+
*) REPLY=3 ;;
|
|
15
|
+
esac
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
# REPLY = sujet du commit conforme de plus fort impact de la branche $1, vide
|
|
19
|
+
# si aucun commit conforme (repli du titre de PR quand le ticket n'en donne pas)
|
|
20
|
+
commit_impact_r() {
|
|
21
|
+
local s rang meilleur="" meilleur_rang=""
|
|
22
|
+
REPLY=""
|
|
23
|
+
while IFS= read -r s; do
|
|
24
|
+
[ -n "$s" ] || continue
|
|
25
|
+
[[ "$s" =~ $CC_PATTERN ]] || continue
|
|
26
|
+
rang_impact "$s"
|
|
27
|
+
rang="$REPLY"
|
|
28
|
+
if [ -z "$meilleur" ] || [ "$rang" -lt "$meilleur_rang" ]; then
|
|
29
|
+
meilleur="$s"
|
|
30
|
+
meilleur_rang="$rang"
|
|
31
|
+
fi
|
|
32
|
+
done < <(gh api "repos/$GH_REPO/compare/$INTEGRATION...$1" \
|
|
33
|
+
-q '.commits[].commit.message | split("\n")[0]' 2>/dev/null || true)
|
|
34
|
+
REPLY="$meilleur"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# REPLY = titre de la PR brouillon du ticket $1 (branche $2) : « type(portée):
|
|
38
|
+
# sujet » depuis le ticket, sinon le commit conforme de plus fort impact de la
|
|
39
|
+
# branche, sinon le titre déduit de la branche
|
|
40
|
+
titre_pr_brouillon_r() {
|
|
41
|
+
local n="$1" branche="$2" type
|
|
42
|
+
ticket_type_r "$(etiquettes "$n")"
|
|
43
|
+
type="$REPLY"
|
|
44
|
+
ticket_pr_title_r "$type" "$(gh issue view "$n" --json title -q .title)"
|
|
45
|
+
[ -z "$REPLY" ] || return 0
|
|
46
|
+
commit_impact_r "$branche"
|
|
47
|
+
[ -z "$REPLY" ] || return 0
|
|
48
|
+
suggest_pr_title_r "$branche"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# $1 = corps d'un ticket → REPLY : les puces de ses critères d'acceptation
|
|
52
|
+
# (« Terminé quand… », « Critères d'acceptation ») en cases à cocher « - [ ] »,
|
|
53
|
+
# le reste inchangé
|
|
54
|
+
criteres_coches_r() {
|
|
55
|
+
local corps="$1" ligne dedans=0 sortie=""
|
|
56
|
+
while IFS= read -r ligne; do
|
|
57
|
+
if [[ "$ligne" =~ ^#{1,6}[[:space:]] ]]; then
|
|
58
|
+
dedans=0
|
|
59
|
+
case "$ligne" in
|
|
60
|
+
*[Tt]ermin* | *[Cc]rit* | *[Aa]cceptance* | *[Dd]one*) dedans=1 ;;
|
|
61
|
+
esac
|
|
62
|
+
elif [ "$dedans" = 1 ]; then
|
|
63
|
+
# « - x » → « - [ ] x » ; une case déjà là n'est pas dupliquée
|
|
64
|
+
ligne="$(printf '%s' "$ligne" |
|
|
65
|
+
sed -E 's/^([[:space:]]*[-*][[:space:]]+)(\[[xX ]\][[:space:]]+)?/\1[ ] /')"
|
|
66
|
+
fi
|
|
67
|
+
sortie="$sortie$ligne"$'\n'
|
|
68
|
+
done <<<"$corps"
|
|
69
|
+
REPLY="${sortie%$'\n'}"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# REPLY = description de la PR brouillon du ticket $1 (branche $2) : le ticket
|
|
73
|
+
# (critères en cases à cocher), puis la liste des commits, puis « Ticket : #N »
|
|
74
|
+
description_pr_brouillon_r() {
|
|
75
|
+
local n="$1" branche="$2" corps commits
|
|
76
|
+
criteres_coches_r "$(gh issue view "$n" --json body -q .body)"
|
|
77
|
+
corps="$REPLY"
|
|
78
|
+
commits="$(gh api "repos/$GH_REPO/compare/$INTEGRATION...$branche" \
|
|
79
|
+
-q '.commits[] | "- \(.sha[0:7]) \(.commit.message | split("\n")[0])"' 2>/dev/null || true)"
|
|
80
|
+
[ -z "$commits" ] || corps="$corps"$'\n\n## Commits\n'"$commits"
|
|
81
|
+
REPLY="$corps"$'\n\nTicket : #'"$n"
|
|
82
|
+
}
|
|
83
|
+
|
|
7
84
|
# Premier push sur la branche d'un ticket : PR brouillon vers la branche
|
|
8
|
-
# d'intégration,
|
|
9
|
-
#
|
|
10
|
-
# tourne au push suivant, ou au
|
|
85
|
+
# d'intégration, au titre et à la description repris du ticket, citant le
|
|
86
|
+
# ticket, assignée à l'auteur du push ; ticket en cours. Ouverte par le jeton
|
|
87
|
+
# des Actions, elle ne lance pas la CI : elle tourne au push suivant, ou au
|
|
88
|
+
# passage « prête pour revue ». Tant qu'elle est en brouillon, chaque push met
|
|
89
|
+
# à jour son titre et sa description ; une PR prête n'est plus touchée.
|
|
11
90
|
cmd_push() {
|
|
12
|
-
local n pr titre
|
|
91
|
+
local n pr titre corps url
|
|
13
92
|
: "${PUSH_BRANCH:?}" "${SHA:?}"
|
|
14
93
|
[ "${DELETED:-false}" != true ] || return 0
|
|
15
94
|
branch_ticket_r "$PUSH_BRANCH"
|
|
@@ -18,17 +97,20 @@ cmd_push() {
|
|
|
18
97
|
# Branche faite hors du ticket (à la main) : inscrite sur le ticket
|
|
19
98
|
inscrire_branche "$n" "$PUSH_BRANCH"
|
|
20
99
|
pr="$(gh pr list --state open --head "$PUSH_BRANCH" --json number -q '.[0].number // empty')"
|
|
21
|
-
|
|
100
|
+
# PR existante : rien à faire si elle n'est plus en brouillon
|
|
101
|
+
[ -z "$pr" ] || [ "$(gh pr view "$pr" --json isDraft -q .isDraft)" = true ] || return 0
|
|
22
102
|
[ "$(gh api "repos/$GH_REPO/compare/$INTEGRATION...$PUSH_BRANCH" -q .ahead_by)" != 0 ] || return 0
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
103
|
+
titre_pr_brouillon_r "$n" "$PUSH_BRANCH"
|
|
104
|
+
titre="$REPLY"
|
|
105
|
+
description_pr_brouillon_r "$n" "$PUSH_BRANCH"
|
|
106
|
+
corps="$REPLY"
|
|
107
|
+
if [ -n "$pr" ]; then
|
|
108
|
+
gh pr edit "$pr" --title "$titre" --body "$corps" >/dev/null
|
|
109
|
+
notice_t tk.draft_updated "$pr" "$n"
|
|
110
|
+
return 0
|
|
111
|
+
fi
|
|
30
112
|
url="$(gh pr create --draft --base "$INTEGRATION" --head "$PUSH_BRANCH" --title "$titre" \
|
|
31
|
-
--body "
|
|
113
|
+
--body "$corps" ${PUSHER:+--assignee "$PUSHER"})"
|
|
32
114
|
[ -z "${PUSHER:-}" ] || gh issue edit "$n" --add-assignee "$PUSHER" >/dev/null || true
|
|
33
115
|
est_valide "$n" && statut "$n" "$S_ENCOURS"
|
|
34
116
|
notice_t tk.draft_opened "${url##*/}" "$n"
|
package/ci/tickets/registre.sh
CHANGED
|
@@ -50,6 +50,25 @@ branche_du_ticket_r() {
|
|
|
50
50
|
done < <(gh api "repos/$GH_REPO/git/matching-refs/heads/" --paginate -q '.[].ref' 2>/dev/null || true)
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
# Ticket $1 terminé : sa branche, si elle existe encore (travail passé par une
|
|
54
|
+
# autre branche), est supprimée quand elle n'apporte rien à la branche
|
|
55
|
+
# d'intégration et qu'aucune PR ouverte n'en part ; sinon gardée et signalée
|
|
56
|
+
nettoyer_branche() {
|
|
57
|
+
local n="$1" branche avance prs
|
|
58
|
+
branche_du_ticket_r "$n"
|
|
59
|
+
branche="$REPLY"
|
|
60
|
+
[ -n "$branche" ] || return 0
|
|
61
|
+
prs="$(gh pr list --state open --head "$branche" --json number -q 'map("#\(.number)") | join(" ")')"
|
|
62
|
+
avance="$(gh api "repos/$GH_REPO/compare/${INTEGRATION:-develop}...$branche" -q .ahead_by 2>/dev/null || true)"
|
|
63
|
+
if [ -z "$prs" ] && [ "$avance" = 0 ]; then
|
|
64
|
+
gh api -X DELETE "repos/$GH_REPO/git/refs/heads/$branche" >/dev/null
|
|
65
|
+
notice_t tk.branch_deleted "$branche" "$n"
|
|
66
|
+
else
|
|
67
|
+
_tr tk.branch_kept "$branche" "${avance:-?}" "${prs:--}"
|
|
68
|
+
gh issue comment "$n" --body "$_T" >/dev/null
|
|
69
|
+
fi
|
|
70
|
+
}
|
|
71
|
+
|
|
53
72
|
# Ticket $1 validé : sa branche naît de la branche d'intégration, liée au
|
|
54
73
|
# ticket (panneau « Development »). Rien si elle existe déjà, ou si une PR
|
|
55
74
|
# ouverte cite déjà le ticket (travail commencé hors de sa branche).
|
package/ci/tickets/release.sh
CHANGED
package/hooks/lib/i18n/en.sh
CHANGED
|
@@ -118,6 +118,8 @@ _msg_en() {
|
|
|
118
118
|
hook.pre_push.5) _M="%s: no changed file, nothing to test." ;;
|
|
119
119
|
hook.pre_push.6) _M="Nothing to test (branch deletion only)." ;;
|
|
120
120
|
hook.pre_push.7) _M="Pre-push OK" ;;
|
|
121
|
+
hook.pre_push.lint.missing) _M="%s not installed, %s not checked (install: %s)." ;;
|
|
122
|
+
hook.pre_push.lint.run) _M="%s: %s" ;;
|
|
121
123
|
hook.branch_hint.1) _M="Branch name \"%s\" does not follow the convention: commits and pushes will be refused. Rename:" ;;
|
|
122
124
|
hook.branch_hint.2) _M=" Format <type>/<topic> (e.g. feat/signup); full help on the next commit." ;;
|
|
123
125
|
hook.post_merge.1) _M="🧹 Local branch deleted (merged and deleted on the server): %s" ;;
|
|
@@ -262,6 +264,7 @@ _msg_en() {
|
|
|
262
264
|
ci.proteger.5) _M=" v* tags: %s" ;;
|
|
263
265
|
ci.proteger.6) _M=" default branch: %s" ;;
|
|
264
266
|
ci.proteger.7) _M=" required approvals: %s; CODEOWNERS review: %s; approval of unattributed changes: %s" ;;
|
|
267
|
+
ci.proteger.solo) _M=" solo repository (a single person with write access): no approval by another person required" ;;
|
|
265
268
|
ci.proteger.8) _M=" (admins keep an explicit bypass, recorded in the PR history)" ;;
|
|
266
269
|
ci.proteger.9) _M=" \"%s\" environment: approval by %s; protected branches and v* tags" ;;
|
|
267
270
|
ci.proteger.10) _M="Ruleset \"%s\" updated." ;;
|
|
@@ -276,6 +279,13 @@ _msg_en() {
|
|
|
276
279
|
ci.proteger.unattributed_invalid) _M="unattributedApproval: true or false." ;;
|
|
277
280
|
ci.proteger.no_branch) _M="None of the branches \"%s\" exists on %s." ;;
|
|
278
281
|
ci.proteger.no_cleanup) _M="Automatic branch deletion disabled (it would delete develop): add a workflow calling nettoyage-branches.yml, otherwise merged branches remain (see In an organisation)." ;;
|
|
282
|
+
ci.proteger.no_delivery_env) _M="main will require a successful \"%s\" deployment, but no workflow creates that deployment: a deployment nothing creates would block main forever. Call release-auto.yml with delivery-environment (see docs/releases.md, develop flow), or set deliveryApproval = review in .repowarden.conf." ;;
|
|
283
|
+
ci.proteger.stale_concurrency) _M="%s: a pending delivery request (manual or scheduled) would silently block the next ones. Set cancel-in-progress: \${{ github.event_name != 'push' }} (a new request replaces the pending one) and one group per trigger: release-\${{ github.ref_name }}-\${{ github.event.schedule || github.event_name }}." ;;
|
|
284
|
+
ci.proteger.urgent) _M=" \"urgent\" label created (its merge triggers the delivery request)" ;;
|
|
285
|
+
ci.proteger.verify_missing) _M="%s: ruleset missing online" ;;
|
|
286
|
+
ci.proteger.verify_diff) _M="%s: online rules differ\n online: %s\n expected: %s" ;;
|
|
287
|
+
ci.proteger.verify_fix) _M="Outdated rules: run repowarden proteger again to update them." ;;
|
|
288
|
+
ci.proteger.verify_ok) _M="Online rules up to date." ;;
|
|
279
289
|
ci.proteger.delivery) _M=" delivery into main: %s approval(s) required (moving to a new version); auto-merge of work PRs into develop: %s" ;;
|
|
280
290
|
ci.proteger.methods_flow) _M="main: merge commit; %s: squash or merge commit" ;;
|
|
281
291
|
ci.proteger.tags_org) _M="reserved for workflows" ;;
|
|
@@ -363,6 +373,7 @@ _msg_en() {
|
|
|
363
373
|
st.repo_version_old) _M="The project requires repowarden %s; installed: %s" ;;
|
|
364
374
|
st.repo_version_ok) _M="Version required by the project (%s): met" ;;
|
|
365
375
|
st.repo_forge) _M="Platform: %s" ;;
|
|
376
|
+
st.repo_rules) _M="GitHub rules up to date? repowarden proteger --verifier (read-only)" ;;
|
|
366
377
|
st.tools) _M="Tools present: %s · missing: %s (depending on the projects' languages)" ;;
|
|
367
378
|
st.next) _M="Next step:" ;;
|
|
368
379
|
st.ready) _M="All set: git cc to commit, repowarden code-mort on a branch." ;;
|
|
@@ -403,13 +414,16 @@ _msg_en() {
|
|
|
403
414
|
tk.validated_waiting) _M="Ticket validated, waiting to be taken: its branch will be created when it is assigned, or by taking it yourself with \`repowarden ticket %s\`." ;;
|
|
404
415
|
tk.pr_inverted) _M="PR closed automatically: a PR from \`%s\` to \`%s\` is pointless. A persistent branch only goes out for a delivery (develop → main) or a back-merge (main → develop). To update a work branch: on that branch, \`git merge origin/%s\`, then \`git push\`." ;;
|
|
405
416
|
tk.pr_backmerge_empty) _M="PR closed automatically: \`%s\` brings no content to \`%s\`. The back-merge is done automatically after a release, only when there is something to bring back." ;;
|
|
417
|
+
tk.pr_delivery_manual) _M="PR closed automatically: the \`%s\` → \`%s\` delivery is opened by the release workflow; a PR opened by hand would never get a deployment approval. Run *Actions → release → Run workflow* on \`%s\` (or \`gh workflow run release.yml --ref %s\`), then **Approve and deploy** on the run." ;;
|
|
406
418
|
tk.branch_registered) _M="Ticket branch: \`%s\`." ;;
|
|
407
419
|
tk.branch_second) _M="Second branch for this ticket: \`%s\` (already: %s). One ticket, one branch: group the work, or open a separate ticket." ;;
|
|
408
420
|
tk.adopted) _M="Existing tickets: %s sorted, %s branch(es) registered." ;;
|
|
409
421
|
tk.init_adopt) _M="Existing tickets and branches" ;;
|
|
410
422
|
tk.abandoned) _M="Ticket #%s abandoned: PR closed, branch deleted." ;;
|
|
411
423
|
tk.branch_deleted) _M="Branch %s deleted (ticket #%s abandoned)" ;;
|
|
424
|
+
tk.branch_kept) _M="Branch \`%s\` kept: %s commit(s) missing from the integration branch, open PR(s): %s. Delete it by hand if the work lives elsewhere." ;;
|
|
412
425
|
tk.draft_opened) _M="Draft PR #%s opened for ticket #%s" ;;
|
|
426
|
+
tk.draft_updated) _M="PR #%s (ticket #%s): title and description taken from the ticket" ;;
|
|
413
427
|
tk.created_body) _M="Ticket created automatically for PR #%s, which cited none. To be validated by a maintainer (\"validé\" label)." ;;
|
|
414
428
|
tk.created) _M="Ticket #%s created for PR #%s and linked in its description: to be validated by a maintainer." ;;
|
|
415
429
|
tk.init_labels) _M="Labels (statuses and validation)" ;;
|
|
@@ -417,9 +431,36 @@ _msg_en() {
|
|
|
417
431
|
tk.init_exists) _M="%s already exists: left as is." ;;
|
|
418
432
|
tk.init_written) _M="%s created." ;;
|
|
419
433
|
tk.init_done) _M="Ticket tracking ready." ;;
|
|
434
|
+
tk.init_auto_create_on) _M="Auto ticket creation: ENABLED (ticketAutoCreate = true)" ;;
|
|
435
|
+
tk.init_auto_create_off) _M="Auto ticket creation: DISABLED (default). To enable: git config repowarden.ticketAutoCreate true" ;;
|
|
420
436
|
tk.init_next) _M="Next: commit these files; require the \"ticket\" check (repowarden proteger --checks \"…,ticket\"); in release.yml, wire the tickets job (see the Tickets docs)." ;;
|
|
421
437
|
tk.prerelease) _M="Available in pre-release **%s**: to be tested before production." ;;
|
|
422
438
|
tk.released) _M="Released in **%s**." ;;
|
|
439
|
+
# Ticket comment
|
|
440
|
+
tk.comment.adding) _M="Adding comment to ticket #%s..." ;;
|
|
441
|
+
tk.comment.added) _M="Comment added to ticket #%s." ;;
|
|
442
|
+
# Ticket label
|
|
443
|
+
tk.label.adding) _M="Adding label \"%s\" to ticket #%s..." ;;
|
|
444
|
+
tk.label.added) _M="Label \"%s\" added to ticket #%s." ;;
|
|
445
|
+
tk.label.removing) _M="Removing label \"%s\" from ticket #%s..." ;;
|
|
446
|
+
tk.label.removed) _M="Label \"%s\" removed from ticket #%s." ;;
|
|
447
|
+
# PR label
|
|
448
|
+
pr.label.adding) _M="Adding label \"%s\" to PR #%s..." ;;
|
|
449
|
+
pr.label.added) _M="Label \"%s\" added to PR #%s." ;;
|
|
450
|
+
pr.label.removing) _M="Removing label \"%s\" from PR #%s..." ;;
|
|
451
|
+
pr.label.removed) _M="Label \"%s\" removed from PR #%s." ;;
|
|
452
|
+
# Run rerun
|
|
453
|
+
run.rerun.failed) _M="Rerunning last failed run..." ;;
|
|
454
|
+
run.rerun.workflow) _M="Rerunning last run of workflow \"%s\"..." ;;
|
|
455
|
+
run.rerun.running) _M="Rerunning run #%s..." ;;
|
|
456
|
+
run.rerun.done) _M="Run #%s rerun." ;;
|
|
457
|
+
run.rerun.none_failed) _M="No failed run found." ;;
|
|
458
|
+
run.rerun.none_workflow) _M="No run found for workflow \"%s\"." ;;
|
|
459
|
+
# Run list
|
|
460
|
+
run.listing.all) _M="Listing recent runs..." ;;
|
|
461
|
+
run.listing.workflow) _M="Listing runs for workflow \"%s\"..." ;;
|
|
462
|
+
# PR checks
|
|
463
|
+
pr.checks.checking) _M="Checking checks for PR #%s..." ;;
|
|
423
464
|
*) _M="" ;;
|
|
424
465
|
esac
|
|
425
466
|
}
|
package/hooks/lib/i18n/fr.sh
CHANGED
|
@@ -116,6 +116,8 @@ _msg_fr() {
|
|
|
116
116
|
hook.pre_push.5) _M="%s : aucun fichier modifié, rien à tester." ;;
|
|
117
117
|
hook.pre_push.6) _M="Rien à tester (suppression de branche uniquement)." ;;
|
|
118
118
|
hook.pre_push.7) _M="Pre-push OK" ;;
|
|
119
|
+
hook.pre_push.lint.missing) _M="%s absent, %s non vérifié (installation : %s)." ;;
|
|
120
|
+
hook.pre_push.lint.run) _M="%s : %s" ;;
|
|
119
121
|
hook.branch_hint.1) _M="Nom de branche « %s » non conforme : commits et push seront refusés. Renommer :" ;;
|
|
120
122
|
hook.branch_hint.2) _M=" Format <type>/<sujet> (ex. feat/inscription) ; aide complète au prochain commit." ;;
|
|
121
123
|
hook.post_merge.1) _M="🧹 Branche locale supprimée (mergée et supprimée sur le serveur) : %s" ;;
|
|
@@ -262,6 +264,7 @@ _msg_fr() {
|
|
|
262
264
|
ci.proteger.5) _M=" tags v* : %s" ;;
|
|
263
265
|
ci.proteger.6) _M=" branche par défaut : %s" ;;
|
|
264
266
|
ci.proteger.7) _M=" approbations exigées : %s ; revue des CODEOWNERS : %s ; approbation des changements non attribués : %s" ;;
|
|
267
|
+
ci.proteger.solo) _M=" dépôt solo (une seule personne avec l'écriture) : aucune approbation par une autre personne exigée" ;;
|
|
265
268
|
ci.proteger.8) _M=" (les admins gardent un contournement explicite, tracé dans l'historique de la PR)" ;;
|
|
266
269
|
ci.proteger.9) _M=" environnement « %s » : approbation de %s ; branches protégées et tags v*" ;;
|
|
267
270
|
ci.proteger.10) _M="Ruleset « %s » mis à jour." ;;
|
|
@@ -276,6 +279,13 @@ _msg_fr() {
|
|
|
276
279
|
ci.proteger.unattributed_invalid) _M="unattributedApproval : true ou false." ;;
|
|
277
280
|
ci.proteger.no_branch) _M="Aucune des branches « %s » n'existe sur %s." ;;
|
|
278
281
|
ci.proteger.no_cleanup) _M="Suppression automatique des branches désactivée (elle supprimerait develop) : ajouter un workflow qui appelle nettoyage-branches.yml, sinon les branches mergées restent (voir En organisation)." ;;
|
|
282
|
+
ci.proteger.no_delivery_env) _M="main exigera un déploiement « %s » réussi, mais aucun workflow ne crée ce déploiement : un déploiement que rien ne crée bloquerait main pour toujours. Appeler release-auto.yml avec delivery-environment (voir docs/releases.md, flux develop), ou deliveryApproval = review dans .repowarden.conf." ;;
|
|
283
|
+
ci.proteger.stale_concurrency) _M="%s : une demande de livraison (manuelle ou planifiée) en attente bloquerait en silence les suivantes. Mettre cancel-in-progress: \${{ github.event_name != 'push' }} (une nouvelle demande remplace celle en attente) et un groupe par déclencheur : release-\${{ github.ref_name }}-\${{ github.event.schedule || github.event_name }}." ;;
|
|
284
|
+
ci.proteger.urgent) _M=" étiquette « urgent » créée (son merge lance la demande de livraison)" ;;
|
|
285
|
+
ci.proteger.verify_missing) _M="%s : ruleset absent en ligne" ;;
|
|
286
|
+
ci.proteger.verify_diff) _M="%s : règles en ligne différentes\n en ligne : %s\n attendu : %s" ;;
|
|
287
|
+
ci.proteger.verify_fix) _M="Règles périmées : relancer repowarden proteger pour les mettre à jour." ;;
|
|
288
|
+
ci.proteger.verify_ok) _M="Règles en ligne à jour." ;;
|
|
279
289
|
ci.proteger.delivery) _M=" livraison vers main : %s approbation(s) exigée(s) (passage à une nouvelle version) ; merge automatique des PR de travail vers develop : %s" ;;
|
|
280
290
|
ci.proteger.methods_flow) _M="main : merge commit ; %s : squash ou merge commit" ;;
|
|
281
291
|
ci.proteger.tags_org) _M="réservés aux workflows" ;;
|
|
@@ -363,6 +373,7 @@ _msg_fr() {
|
|
|
363
373
|
st.repo_version_old) _M="Le projet demande repowarden %s ; installé : %s" ;;
|
|
364
374
|
st.repo_version_ok) _M="Version demandée par le projet (%s) : respectée" ;;
|
|
365
375
|
st.repo_forge) _M="Plateforme : %s" ;;
|
|
376
|
+
st.repo_rules) _M="Règles GitHub à jour ? repowarden proteger --verifier (lecture seule)" ;;
|
|
366
377
|
st.tools) _M="Outils présents : %s · absents : %s (selon les langages des projets)" ;;
|
|
367
378
|
st.next) _M="Étape suivante :" ;;
|
|
368
379
|
st.ready) _M="Tout est prêt : git cc pour commiter, repowarden code-mort sur une branche." ;;
|
|
@@ -403,13 +414,16 @@ _msg_fr() {
|
|
|
403
414
|
tk.validated_waiting) _M="Ticket validé, en attente d'être pris : sa branche sera créée à l'assignation, ou en le prenant soi-même avec \`repowarden ticket %s\`." ;;
|
|
404
415
|
tk.pr_inverted) _M="PR fermée automatiquement : une PR de \`%s\` vers \`%s\` est sans objet. Une branche persistante ne part que pour une livraison (develop → main) ou un retour (main → develop). Pour mettre une branche de travail à jour : sur cette branche, \`git merge origin/%s\`, puis \`git push\`." ;;
|
|
405
416
|
tk.pr_backmerge_empty) _M="PR fermée automatiquement : \`%s\` n'apporte aucun contenu à \`%s\`. Le retour est fait automatiquement après une release, seulement quand il y a quelque chose à rapporter." ;;
|
|
417
|
+
tk.pr_delivery_manual) _M="PR fermée automatiquement : la livraison \`%s\` → \`%s\` est ouverte par le workflow de release ; une PR ouverte à la main n'aurait jamais d'approbation de déploiement. Lancer *Actions → release → Run workflow* sur \`%s\` (ou \`gh workflow run release.yml --ref %s\`), puis **Approve and deploy** sur le run." ;;
|
|
406
418
|
tk.branch_registered) _M="Branche du ticket : \`%s\`." ;;
|
|
407
419
|
tk.branch_second) _M="Seconde branche pour ce ticket : \`%s\` (déjà : %s). Un ticket, une branche : regrouper le travail, ou ouvrir un ticket séparé." ;;
|
|
408
420
|
tk.adopted) _M="Tickets existants : %s rangé(s), %s branche(s) inscrite(s)." ;;
|
|
409
421
|
tk.init_adopt) _M="Tickets et branches existants" ;;
|
|
410
422
|
tk.abandoned) _M="Ticket #%s abandonné : PR fermée, branche supprimée." ;;
|
|
411
423
|
tk.branch_deleted) _M="Branche %s supprimée (ticket #%s abandonné)" ;;
|
|
424
|
+
tk.branch_kept) _M="Branche \`%s\` gardée : %s commit(s) absent(s) de la branche d'intégration, PR ouverte(s) : %s. À supprimer à la main si le travail est ailleurs." ;;
|
|
412
425
|
tk.draft_opened) _M="PR #%s ouverte en brouillon pour le ticket #%s" ;;
|
|
426
|
+
tk.draft_updated) _M="PR #%s (ticket #%s) : titre et description repris du ticket" ;;
|
|
413
427
|
tk.created_body) _M="Ticket créé automatiquement pour la PR #%s, qui n'en citait aucun. À valider par un mainteneur (étiquette « validé »)." ;;
|
|
414
428
|
tk.created) _M="Ticket #%s créé pour la PR #%s et relié à sa description : à valider par un mainteneur." ;;
|
|
415
429
|
tk.init_labels) _M="Étiquettes (statuts et validation)" ;;
|
|
@@ -417,9 +431,36 @@ _msg_fr() {
|
|
|
417
431
|
tk.init_exists) _M="%s existe déjà : laissé tel quel." ;;
|
|
418
432
|
tk.init_written) _M="%s créé." ;;
|
|
419
433
|
tk.init_done) _M="Suivi des tickets prêt." ;;
|
|
434
|
+
tk.init_auto_create_on) _M="Création auto de ticket : ACTIVÉE (ticketAutoCreate = true)" ;;
|
|
435
|
+
tk.init_auto_create_off) _M="Création auto de ticket : DÉSACTIVÉE (défaut). Pour l'activer : git config repowarden.ticketAutoCreate true" ;;
|
|
420
436
|
tk.init_next) _M="Ensuite : committer ces fichiers ; exiger la vérification « ticket » (repowarden proteger --checks \"…,ticket\") ; dans release.yml, brancher le job tickets (voir la doc Tickets)." ;;
|
|
421
437
|
tk.prerelease) _M="Disponible en préversion **%s** : à tester avant la production." ;;
|
|
422
438
|
tk.released) _M="Publié dans **%s**." ;;
|
|
439
|
+
# Ticket comment
|
|
440
|
+
tk.comment.adding) _M="Ajout d'un commentaire sur le ticket #%s..." ;;
|
|
441
|
+
tk.comment.added) _M="Commentaire ajouté au ticket #%s." ;;
|
|
442
|
+
# Ticket label
|
|
443
|
+
tk.label.adding) _M="Ajout du label « %s » sur le ticket #%s..." ;;
|
|
444
|
+
tk.label.added) _M="Label « %s » ajouté au ticket #%s." ;;
|
|
445
|
+
tk.label.removing) _M="Retrait du label « %s » du ticket #%s..." ;;
|
|
446
|
+
tk.label.removed) _M="Label « %s » retiré du ticket #%s." ;;
|
|
447
|
+
# PR label
|
|
448
|
+
pr.label.adding) _M="Ajout du label « %s » sur la PR #%s..." ;;
|
|
449
|
+
pr.label.added) _M="Label « %s » ajouté à la PR #%s." ;;
|
|
450
|
+
pr.label.removing) _M="Retrait du label « %s » de la PR #%s..." ;;
|
|
451
|
+
pr.label.removed) _M="Label « %s » retiré de la PR #%s." ;;
|
|
452
|
+
# Run rerun
|
|
453
|
+
run.rerun.failed) _M="Relance du dernier run échoué..." ;;
|
|
454
|
+
run.rerun.workflow) _M="Relance du dernier run du workflow « %s »..." ;;
|
|
455
|
+
run.rerun.running) _M="Relance du run #%s..." ;;
|
|
456
|
+
run.rerun.done) _M="Run #%s relancé." ;;
|
|
457
|
+
run.rerun.none_failed) _M="Aucun run échoué trouvé." ;;
|
|
458
|
+
run.rerun.none_workflow) _M="Aucun run trouvé pour le workflow « %s »." ;;
|
|
459
|
+
# Run list
|
|
460
|
+
run.listing.all) _M="Liste des runs récents..." ;;
|
|
461
|
+
run.listing.workflow) _M="Liste des runs du workflow « %s »..." ;;
|
|
462
|
+
# PR checks
|
|
463
|
+
pr.checks.checking) _M="Vérification des checks de la PR #%s..." ;;
|
|
423
464
|
*) _M="" ;;
|
|
424
465
|
esac
|
|
425
466
|
}
|
package/hooks/lib/tickets.sh
CHANGED
|
@@ -24,21 +24,73 @@ branch_ticket_r() {
|
|
|
24
24
|
if [[ "$1" =~ ^[a-z]+/([0-9]+)(-|$) ]]; then REPLY="${BASH_REMATCH[1]}"; fi
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
# $1 → sans accents (un à un : les classes [àâ] sont fausses en locale C) ;
|
|
28
|
+
# les autres caractères sont conservés, le nettoyage reste à l'appelant
|
|
29
|
+
sans_accents() {
|
|
30
|
+
printf '%s' "$1" | sed -e 's/à/a/g' -e 's/â/a/g' -e 's/ä/a/g' -e 's/é/e/g' -e 's/è/e/g' -e 's/ê/e/g' -e 's/ë/e/g' -e 's/î/i/g' -e 's/ï/i/g' -e 's/ô/o/g' -e 's/ö/o/g' -e 's/ù/u/g' -e 's/û/u/g' -e 's/ü/u/g' -e 's/ç/c/g' -e 's/œ/oe/g' -e 's/æ/ae/g' -e 's/À/a/g' -e 's/Â/a/g' -e 's/Ä/a/g' -e 's/É/e/g' -e 's/È/e/g' -e 's/Ê/e/g' -e 's/Ë/e/g' -e 's/Î/i/g' -e 's/Ï/i/g' -e 's/Ô/o/g' -e 's/Ö/o/g' -e 's/Ù/u/g' -e 's/Û/u/g' -e 's/Ü/u/g' -e 's/Ç/c/g' -e 's/Œ/oe/g' -e 's/Æ/ae/g'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# Mots vides qu'un nom de branche (ou une portée) ne garde pas en fin de coupe :
|
|
34
|
+
# « …-garde-la-trace-de » → « …-garde-la-trace »
|
|
35
|
+
MOTS_VIDES="a au aux de des du en et l la le les of on ou par pour sur the to un une"
|
|
36
|
+
|
|
37
|
+
# $1 (minuscules, tirets) → sans ses mots vides finaux
|
|
38
|
+
sans_mots_vides() {
|
|
39
|
+
local slug="$1" mot
|
|
40
|
+
while [[ "$slug" == *-* ]]; do
|
|
41
|
+
mot="${slug##*-}"
|
|
42
|
+
[[ " $MOTS_VIDES " == *" $mot "* ]] || break
|
|
43
|
+
slug="${slug%-*}"
|
|
44
|
+
done
|
|
45
|
+
printf '%s' "$slug"
|
|
46
|
+
}
|
|
47
|
+
|
|
27
48
|
# REPLY = nom de branche d'un ticket : $1 numéro, $2 titre, $3 type (feat
|
|
28
49
|
# par défaut) → feat/12-ajoute-le-panier (minuscules, sans accents, 40
|
|
29
|
-
# caractères
|
|
50
|
+
# caractères au plus, coupé sur un tiret puis sur un mot entier)
|
|
30
51
|
ticket_branch_r() {
|
|
31
52
|
local n="$1" titre="$2" type="${3:-feat}" slug
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
LC_ALL=C tr '[:upper:]' '[:lower:]' | LC_ALL=C sed -e 's/[^a-z0-9]\{1,\}/-/g' -e 's/^-//' -e 's/-$//')"
|
|
53
|
+
slug="$(sans_accents "$titre" | LC_ALL=C tr '[:upper:]' '[:lower:]' |
|
|
54
|
+
LC_ALL=C sed -e 's/[^a-z0-9]\{1,\}/-/g' -e 's/^-//' -e 's/-$//')"
|
|
35
55
|
if [ "${#slug}" -gt 40 ]; then
|
|
36
56
|
slug="${slug:0:40}"
|
|
37
57
|
[[ "$slug" == *-* ]] && slug="${slug%-*}"
|
|
38
58
|
fi
|
|
59
|
+
slug="$(sans_mots_vides "$slug")"
|
|
39
60
|
REPLY="$type/$n${slug:+-$slug}"
|
|
40
61
|
}
|
|
41
62
|
|
|
63
|
+
# REPLY = titre de PR d'un ticket : $1 type (« feat » par défaut), $2 titre du
|
|
64
|
+
# ticket → « feat(portée): sujet ». Portée = préfixe du titre avant le premier
|
|
65
|
+
# « : » (« Tickets : … » → « tickets »), 20 caractères au plus, omise si vide ;
|
|
66
|
+
# sujet = le reste, première lettre abaissée. 72 caractères au plus, coupé sur
|
|
67
|
+
# un mot entier. Vide si le titre ne donne rien (l'appelant décide du repli).
|
|
68
|
+
ticket_pr_title_r() {
|
|
69
|
+
local type="${1:-feat}" titre="$2" portee sujet tete res
|
|
70
|
+
sujet="$titre"
|
|
71
|
+
portee=""
|
|
72
|
+
if [[ "$titre" =~ ^([^:]{1,30})[[:space:]]*:[[:space:]]+(.+)$ ]]; then
|
|
73
|
+
portee="$(sans_accents "${BASH_REMATCH[1]}" | LC_ALL=C tr '[:upper:]' '[:lower:]' |
|
|
74
|
+
LC_ALL=C sed -e 's/[^a-z0-9._-]\{1,\}/-/g' -e 's/^-//' -e 's/-$//')"
|
|
75
|
+
[ "${#portee}" -le 20 ] || portee=""
|
|
76
|
+
sujet="${BASH_REMATCH[2]}"
|
|
77
|
+
fi
|
|
78
|
+
sujet="$(printf '%s' "${sujet:0:1}" | LC_ALL=C tr '[:upper:]' '[:lower:]')${sujet:1}"
|
|
79
|
+
tete="$type${portee:+($portee)}: "
|
|
80
|
+
REPLY=""
|
|
81
|
+
[ -n "$sujet" ] || return 0
|
|
82
|
+
res="$tete$sujet"
|
|
83
|
+
# 72 caractères au plus, coupé sur un mot entier (authored_length → REPLY)
|
|
84
|
+
authored_length "$res"
|
|
85
|
+
while [ "$REPLY" -gt 72 ] && [ -n "$sujet" ]; do
|
|
86
|
+
if [[ "$sujet" == *" "* ]]; then sujet="${sujet% *}"; else sujet="${sujet%?}"; fi
|
|
87
|
+
res="$tete$sujet"
|
|
88
|
+
authored_length "$res"
|
|
89
|
+
done
|
|
90
|
+
[ -n "$sujet" ] || res=""
|
|
91
|
+
REPLY="$res"
|
|
92
|
+
}
|
|
93
|
+
|
|
42
94
|
# REPLY = type de branche d'après les étiquettes d'un ticket (une par ligne) :
|
|
43
95
|
# « type: fix », « bug » → fix ; feat par défaut
|
|
44
96
|
ticket_type_r() {
|
|
@@ -90,6 +142,21 @@ tickets_managed() {
|
|
|
90
142
|
[ "$TICKETS_MANAGED" = oui ]
|
|
91
143
|
}
|
|
92
144
|
|
|
145
|
+
# Vrai si la création automatique de ticket est activée : réglage
|
|
146
|
+
# ticketAutoCreate = true (défaut false)
|
|
147
|
+
TICKET_AUTO_CREATE=""
|
|
148
|
+
ticket_auto_create() {
|
|
149
|
+
if [ -z "$TICKET_AUTO_CREATE" ]; then
|
|
150
|
+
TICKET_AUTO_CREATE=non
|
|
151
|
+
cfg_r ticketAutoCreate ""
|
|
152
|
+
case "$REPLY" in
|
|
153
|
+
true | oui | yes) TICKET_AUTO_CREATE=oui ;;
|
|
154
|
+
false | non | no | "") ;;
|
|
155
|
+
esac
|
|
156
|
+
fi
|
|
157
|
+
[ "$TICKET_AUTO_CREATE" = oui ]
|
|
158
|
+
}
|
|
159
|
+
|
|
93
160
|
# Branche de travail sans ticket : avertissement, jamais bloquant. Création
|
|
94
161
|
# proposée seulement si repowarden gère la vie des tickets du projet (et que
|
|
95
162
|
# gh est là pour la faire) ; sinon, comment faire taire l'avertissement.
|