forked from heavy-duty/ceremony
feat(changelog): the terminal issue cite joins the fragment guard
Refs #262
This commit is contained in:
parent
101f7bd309
commit
72fa3e0b4d
1 changed files with 70 additions and 6 deletions
|
|
@ -115,8 +115,22 @@ changelog_fragments() {
|
||||||
# splits the measured history: every healthy entry passes untouched,
|
# splits the measured history: every healthy entry passes untouched,
|
||||||
# the drift cluster does not. mawk's length() counts bytes; prose here
|
# the drift cluster does not. mawk's length() counts bytes; prose here
|
||||||
# is ASCII and the fuzz is acceptable.
|
# is ASCII and the fuzz is acceptable.
|
||||||
|
# - every entry ends with its issue citation (#262): one '(' group of
|
||||||
|
# '#N', 'repo#N' or 'owner/repo#N' references separated by ', ', then
|
||||||
|
# ')', then the final '.' and nothing after it. Stated as style and
|
||||||
|
# enforced by nobody, this rule cost #255 a full four-bot round on a
|
||||||
|
# missing '(#248)'; the fragment rules that live in this guard drew no
|
||||||
|
# review comment at all across the same fifteen PRs. Measured on the
|
||||||
|
# same normalized entry as the bound above, so a citation that wraps
|
||||||
|
# onto a continuation line still counts. The repo token is the one the
|
||||||
|
# filename rule already admits, so '<repo>-<issue>.md' and its cite
|
||||||
|
# cannot drift apart; the two halves of one convention. A single group
|
||||||
|
# is what makes 'terminal' checkable — '(#236, #250).' lands two issues
|
||||||
|
# in one entry, '(#236) and (#250).' does not. The citation need not
|
||||||
|
# name the file's own issue: the filename already carries the
|
||||||
|
# authorizing one, so a fragment may cite the incident beside it.
|
||||||
changelog_fragment_problem() {
|
changelog_fragment_problem() {
|
||||||
local file="$1" base problem
|
local file="$1" base problem kind detail rest
|
||||||
base="${file##*/}"
|
base="${file##*/}"
|
||||||
|
|
||||||
if ! printf '%s\n' "$base" | grep -qE '^([a-z][a-z0-9-]*-)?[0-9]+\.md$'; then
|
if ! printf '%s\n' "$base" | grep -qE '^([a-z][a-z0-9-]*-)?[0-9]+\.md$'; then
|
||||||
|
|
@ -157,9 +171,35 @@ changelog_fragment_problem() {
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# One walk of the entries, two rules, and the order between them is
|
||||||
|
# deliberate: an over-long entry anywhere outranks a citation problem
|
||||||
|
# anywhere, so the length diagnosis a fragment already draws is the same
|
||||||
|
# one it drew before the citation rule existed. Both read the entry the
|
||||||
|
# same normalizer produces, which is the whole reason they share a pass.
|
||||||
problem="$(
|
problem="$(
|
||||||
awk -v max=300 '
|
awk -v max=300 '
|
||||||
function flush( len, e) {
|
# cite_problem <entry> — "", "uncited" or "misplaced". Counting the
|
||||||
|
# groups is what distinguishes the two admitted shapes: one group
|
||||||
|
# closing the entry passes however many references it carries, and a
|
||||||
|
# second group anywhere means no single group is terminal.
|
||||||
|
function cite_problem(e, rest, groups, consumed, group_end) {
|
||||||
|
rest = e
|
||||||
|
groups = 0
|
||||||
|
consumed = 0
|
||||||
|
while (match(rest, /\((([A-Za-z0-9._-]+\/)?[a-z][a-z0-9-]*)?#[0-9]+(, (([A-Za-z0-9._-]+\/)?[a-z][a-z0-9-]*)?#[0-9]+)*\)/)) {
|
||||||
|
groups++
|
||||||
|
group_end = consumed + RSTART + RLENGTH - 1
|
||||||
|
consumed = group_end
|
||||||
|
rest = substr(rest, RSTART + RLENGTH)
|
||||||
|
}
|
||||||
|
if (groups == 0) return e ~ /#[0-9]/ ? "misplaced" : "uncited"
|
||||||
|
if (groups > 1) return "misplaced"
|
||||||
|
return (group_end == length(e) - 1 && substr(e, group_end + 1) == ".") ? "" : "misplaced"
|
||||||
|
}
|
||||||
|
function excerpt(e) {
|
||||||
|
return length(e) > 60 ? substr(e, 1, 60) "…" : e
|
||||||
|
}
|
||||||
|
function flush( len, e, kind) {
|
||||||
if (entry == "") return 0
|
if (entry == "") return 0
|
||||||
e = entry
|
e = entry
|
||||||
entry = ""
|
entry = ""
|
||||||
|
|
@ -168,9 +208,14 @@ changelog_fragment_problem() {
|
||||||
sub(/ $/, "", e)
|
sub(/ $/, "", e)
|
||||||
len = length(e)
|
len = length(e)
|
||||||
if (len > max) {
|
if (len > max) {
|
||||||
printf "%d\t%s\n", len, substr(e, 1, 60)
|
printf "long\t%d\t%s\n", len, excerpt(e)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
kind = cite_problem(e)
|
||||||
|
if (kind != "" && cite_kind == "") {
|
||||||
|
cite_kind = kind
|
||||||
|
cite_excerpt = excerpt(e)
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
/^### / { if (flush()) exit; next }
|
/^### / { if (flush()) exit; next }
|
||||||
|
|
@ -182,12 +227,31 @@ changelog_fragment_problem() {
|
||||||
}
|
}
|
||||||
/^[[:space:]]*$/ { next }
|
/^[[:space:]]*$/ { next }
|
||||||
entry != "" { entry = entry " " $0 }
|
entry != "" { entry = entry " " $0 }
|
||||||
END { flush() }
|
END {
|
||||||
|
if (flush()) exit
|
||||||
|
if (cite_kind != "") printf "%s\t\t%s\n", cite_kind, cite_excerpt
|
||||||
|
}
|
||||||
' "$file"
|
' "$file"
|
||||||
)"
|
)"
|
||||||
if [ -n "$problem" ]; then
|
if [ -n "$problem" ]; then
|
||||||
printf "fragment '%s' has a %s-character entry — '%s…' — the bound is 300: split it into multiple '- ' entries in this same fragment\n" \
|
kind="${problem%%$'\t'*}"
|
||||||
"$file" "${problem%%$'\t'*}" "${problem#*$'\t'}"
|
rest="${problem#*$'\t'}"
|
||||||
|
detail="${rest%%$'\t'*}"
|
||||||
|
rest="${rest#*$'\t'}"
|
||||||
|
case "$kind" in
|
||||||
|
long)
|
||||||
|
printf "fragment '%s' has a %s-character entry — '%s' — the bound is 300: split it into multiple '- ' entries in this same fragment\n" \
|
||||||
|
"$file" "$detail" "$rest"
|
||||||
|
;;
|
||||||
|
uncited)
|
||||||
|
printf "fragment '%s' has an entry with no issue citation — '%s' — end it with the issue it comes from: '(#N).'\n" \
|
||||||
|
"$file" "$rest"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf "fragment '%s' has an entry whose issue citation is not terminal — '%s' — exactly one '(#N)' group ends the entry, the final '.' after it\n" \
|
||||||
|
"$file" "$rest"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue