Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions .github/scripts/pass-label.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,19 @@ const SHORT = 12

const firstLine = (body) => String(body ?? '').split('\n').map((line) => line.trim()).find((line) => line !== '') ?? ''

// The gates compare a `Written-by:` name with the policy's roster case-insensitively (#1131:
// `review-gate` lowercases both sides before `writers.has(...)`), so every reader of a conclusion here
// does the same — one rule, one place, and a label cannot go missing where the gate accepted the PASS.
const isListedWriter = (writers, name) =>
new Set(writers.map((w) => String(w).toLowerCase())).has(String(name).toLowerCase())

// Does this body decide that the tip was PASSed? `Correction from …` never does, whatever it quotes.
export function verdictOf(body, { writers = [], association } = {}) {
if (!ASSOCIATIONS.has(String(association ?? '').toUpperCase())) return null
if (/^Correction\b/i.test(firstLine(body))) return null
const report = parseReport(body)
if (report.verdict !== 'PASS') return null
if (!report.writer || !writers.includes(report.writer)) return null
if (!report.writer || !isListedWriter(writers, report.writer)) return null
return report
}

Expand Down Expand Up @@ -71,8 +77,7 @@ export function strangerPassOf(body, { writers = [], association } = {}) {
if (/^Correction\b/i.test(firstLine(body))) return null
const report = parseReport(body)
if (report.verdict !== 'PASS' || !report.tip || !report.writer) return null
const listed = new Set(writers.map((w) => String(w).toLowerCase()))
if (listed.has(String(report.writer).toLowerCase())) return null
if (isListedWriter(writers, report.writer)) return null
return { writer: report.writer, tip: report.tip }
}

Expand Down
16 changes: 16 additions & 0 deletions .github/scripts/pass-label.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,22 @@ Written-by: Reviewer`
assert.equal(verdictOf(conclusion('PASS', head, 'Stranger'), { writers, association: 'MEMBER' }), null, 'the name must be in writers')
})

// The gates compare `Written-by:` with the policy's names case-insensitively: `review-gate` lowercases
// both sides before `writers.has(...)`, and `strangerPassOf` here does the same. `verdictOf` compared with
// `writers.includes(report.writer)`, so a PASS written as `reviewer` or `dev4` counted for the gate and was
// silently neither labelled nor noted — the record #739 exists for went missing exactly where the gate
// had accepted it, and the `pass` job reported nothing at all.
test('#739: a writer matches the way the gates match it, whatever its case', () => {
for (const writer of ['reviewer', 'REVIEWER', 'dev4']) {
const body = conclusion('PASS', head, writer)
assert.ok(verdictOf(body, { writers, association: 'MEMBER' }), `${writer} is a listed name in another case`)
assert.equal(planComment({ pr: 7, body, association: 'MEMBER', writers }).add, true, `${writer} labels the pull request`)
}
// Not a widening: a name the policy does not list is not a conclusion in any case.
assert.equal(verdictOf(conclusion('PASS', head, 'reviewer3'), { writers, association: 'MEMBER' }), null)
assert.equal(verdictOf(conclusion('PASS', head, 'Reviewer Extra'), { writers, association: 'MEMBER' }), null)
})

test('#739: a replacement PASS labels like a normal PASS, and a second run adds nothing', () => {
const replacement = `Verdict: PASS at ${head}
Written-by: Reviewer
Expand Down
1 change: 1 addition & 0 deletions changelog.d/2544-pass-label-writer-case.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A pull request whose PASS verdict names its writer in a different letter case than the review policy roster no longer loses that record: the `pass` label job compares names the way `review-gate` and `merge-gate` already do, so a PASS the gates accept now labels the pull request instead of being silently dropped ([#2544](https://github.com/ThinkFlowLab/FlyBot/issues/2544)).
Loading