fix(release): name policy rules instead of internal numbers in output

fold, run --defer and the run's own stderr printed "rule 101" for an
original merged before its line started under the policy, and "rule
100" for backport/skip. Those numbers exist only inside the tool; the
policy has six rules, so a reader looking for rule 101 finds nothing.
Output now names such a rule in words, and the policy's own rules by
their number.

Assisted-By: LLM
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
This commit is contained in:
Myasnikov Daniil committed 2026-11-09 14:53:29 +00:00
1 parent 35c47f20c1
commit 015fc51c0d
5 files changed
+43 -4

No files matched your search

+1 -1
View File
@@ -279,7 +279,7 @@ func (a *app) finish(b *batch, r *report, branch string, date time.Time, push, r
}
for _, rule := range left {
for _, v := range r.skipped[rule] {
fmt.Fprintf(a.stderr, "skipped by rule %d: %s %s (%s)\n", rule, v.o.url(), terminalSafe(v.o.title), terminalSafe(v.reason))
fmt.Fprintf(a.stderr, "skipped by %s: %s %s (%s)\n", ruleName(rule), v.o.url(), terminalSafe(v.o.title), terminalSafe(v.reason))
}
}
for _, p := range r.waiting {
+13 -1
View File
@@ -297,6 +297,18 @@ type report struct {
shapes map[*verdict]string
}
// ruleName is how output names what kept an original out: the policy's
// number for its own rules, and words for what the policy does not number.
func ruleName(rule int) string {
switch rule {
case ruleSkipLabel:
return labelSkip
case ruleBeforeStart:
return "rule 1, before the line's start"
}
return "rule " + strconv.Itoa(rule)
}
func (r *report) ruleTitle(rule int) string {
switch rule {
case ruleBeforeStart:
@@ -417,7 +429,7 @@ func (r *report) outcome(v *verdict) string {
case slices.ContainsFunc(r.waiting, func(p pick) bool { return p.v == v }):
return "waiting for a maintainer: it changes " + codeSpan(workflowsDir)
case v.rule != 0:
return "skipped by rule " + strconv.Itoa(v.rule) + ", " + titleSpan(v.reason)
return "skipped by " + ruleName(v.rule) + ", " + titleSpan(v.reason)
}
return "not picked"
}
+1 -1
View File
@@ -209,7 +209,7 @@ func refuseFold(b *batch, n int, commit string) (string, error) {
case v.rule == ruleSkipLabel:
return fmt.Sprintf("#%d carries %s", n, labelSkip), nil
case v.rule != 0:
return fmt.Sprintf("#%d is not one the batch takes: rule %d, %s", n, v.rule, v.reason), nil
return fmt.Sprintf("#%d is not one the batch takes: %s, %s", n, ruleName(v.rule), v.reason), nil
case b.pr == nil:
return fmt.Sprintf("no batch pull request is open on %s", b.line.branch()), nil
case v.onBatch != "":
+27
View File
@@ -17,6 +17,7 @@ limitations under the License.
package main
import (
"regexp"
"strings"
"testing"
"unicode"
@@ -359,6 +360,32 @@ func TestCheckDispositions(t *testing.T) {
}
}
// Output names a rule as the policy does: the numbers the tool keeps for
// what is no rule of the policy never reach a reader.
func TestOutputNamesNoInternalRule(t *testing.T) {
f := newFixture(t)
f.lines()
f.pr(5, "fix(app): five", "", map[string]string{"app/five.txt": "5\n"})
f.pr(6, "fix(app): six", "", map[string]string{"app/six.txt": "6\n"})
f.setPolicy("release-1.1: 2099-01-01\n")
gh := &fakeGH{labels: map[string][]int{labelInclude: {6}}}
head := openBatch(t, f, gh)
internal := regexp.MustCompile(`rule 1[0-9][0-9]`)
for _, args := range [][]string{
{"fold", "--line", "release-1.1", "--original", "5", "--commit", head},
{"run", "--line", "release-1.1", "--defer", "5", "--reason", "x"},
} {
code, stdout, stderr := f.exec(gh, args...)
f.git("switch", "-q", "main")
if code == 0 || !strings.Contains(stdout+stderr, "#5 is not one the batch takes: rule 1, before the line's start, merged ") {
t.Errorf("%s = %d, want a refusal naming rule 1:\n%s%s", args[0], code, stdout, stderr)
}
if m := internal.FindString(stdout + stderr); m != "" {
t.Errorf("%s prints %q:\n%s%s", args[0], m, stdout, stderr)
}
}
}
type errString string
func (e errString) Error() string { return string(e) }
+1 -1
View File
@@ -82,7 +82,7 @@ func (a *app) rebuild(b *batch, numbers []int, reason string, push bool) int {
case v == nil:
return a.fail(fmt.Errorf("#%d did not merge into main after %s branched", n, b.line.branch()))
case v.rule != 0 && !slices.Contains(stale, v):
return a.fail(fmt.Errorf("#%d is not one the batch takes: rule %d, %s", n, v.rule, terminalSafe(v.reason)))
return a.fail(fmt.Errorf("#%d is not one the batch takes: %s, %s", n, ruleName(v.rule), terminalSafe(v.reason)))
}
hold[n] = reason
}