diff --git a/scripts/issue-lifecycle/github.ts b/scripts/issue-lifecycle/github.ts index 30a37da7d..7a2321f23 100644 --- a/scripts/issue-lifecycle/github.ts +++ b/scripts/issue-lifecycle/github.ts @@ -28,6 +28,14 @@ export function makeOctokit(token: string): Octokit { return new Octokit({ auth: token }); } +// GitHub label names are case-insensitive (you cannot have both "Stale" and +// "stale"), so match them that way — otherwise a label applied with different +// casing (e.g. the "Stale" label from the PR stale action) is missed. +export function hasLabel(issue: Issue, label: string): boolean { + const target = label.toLowerCase(); + return issue.labels.some((l) => l.toLowerCase() === target); +} + export function isBotUser( user: { login: string; type: string } | null, ): boolean { diff --git a/scripts/issue-lifecycle/rules/approval-label-sync.ts b/scripts/issue-lifecycle/rules/approval-label-sync.ts index a7da478c7..199dc752d 100644 --- a/scripts/issue-lifecycle/rules/approval-label-sync.ts +++ b/scripts/issue-lifecycle/rules/approval-label-sync.ts @@ -1,9 +1,9 @@ import { LABELS } from "../config"; -import type { Action, Issue } from "../github"; +import { type Action, hasLabel, type Issue } from "../github"; export function syncApprovalLabel(issue: Issue): Action[] { - const hasApproved = issue.labels.includes(LABELS.APPROVED); - const hasNotApproved = issue.labels.includes(LABELS.NOT_APPROVED); + const hasApproved = hasLabel(issue, LABELS.APPROVED); + const hasNotApproved = hasLabel(issue, LABELS.NOT_APPROVED); const milestoned = issue.milestone !== null; const actions: Action[] = []; diff --git a/scripts/issue-lifecycle/rules/stale-closer.ts b/scripts/issue-lifecycle/rules/stale-closer.ts index fbfb22609..895f4cb8e 100644 --- a/scripts/issue-lifecycle/rules/stale-closer.ts +++ b/scripts/issue-lifecycle/rules/stale-closer.ts @@ -4,6 +4,7 @@ import { type Action, type Issue, type IssueComment, + hasLabel, isBotUser, listIssueComments, } from "../github"; @@ -35,12 +36,12 @@ export async function checkStale( now: Date = new Date(), ): Promise { if (issue.milestone !== null) return []; - if (issue.labels.includes(LABELS.KEEP_OPEN)) return []; + if (hasLabel(issue, LABELS.KEEP_OPEN)) return []; const comments = await listIssueComments(octokit, issue.number); const lastActivityIso = latestNonBotActivityIso(issue, comments); const daysSinceActivity = daysBetween(lastActivityIso, now); - const hasStaleLabel = issue.labels.includes(LABELS.STALE); + const hasStaleLabel = hasLabel(issue, LABELS.STALE); const authorLogin = issue.user?.login ?? "there"; if (hasStaleLabel && daysSinceActivity < STALE_WARN_DAYS) {