mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
fix: waive the issue status for a class-A-free range in the process gate
Rule 8 demanded a working S-label from every class A/B commit's issue, while owner decision #118 sends infrastructure work outside the S1..S8 flow entirely — such an issue has no status label by construction. The two rules contradicted each other and the machine-checked one won, so Validate on dev went red on every infrastructure commit (#175, #191, #202, #206) and the catch-up signal stopped meaning anything. A gate that is always red is not a gate. The waiver keys on the diff, not on a permission label: a range with no class A file at all. An `infra` label could be pinned on a product task to walk a product commit past the status check; ceasing to touch class A without ceasing to be infrastructure work is not possible. Issue existence, open state, `blocked` and fail-closed on an unreachable gh all still apply, and the waiver prints a visible warning rather than passing in silence. Mutation-checked both ways: unwiring the waiver reddens the CLI test, and letting class A keep the waiver reddens both new tests. Issue: #207 User-Visible: no
This commit is contained in:
@@ -289,6 +289,21 @@ export function commitsUnderRuleOne(commits) {
|
||||
);
|
||||
}
|
||||
|
||||
// Инфраструктурная работа по решению владельца #118: признак механический —
|
||||
// в диапазоне НЕТ ни одного файла класса A. Такая задача идёт вне продуктового
|
||||
// флоу и по построению не имеет статусной метки, поэтому проверка 8 требовала у
|
||||
// неё невозможного и краснела на каждом инфра-коммите (#207): Validate на dev
|
||||
// был красным систематически, и сигнал догоняющей проверки обесценился.
|
||||
//
|
||||
// Исключение опирается на diff, а не на метку-разрешение: метку `infra` можно
|
||||
// поставить продуктовой задаче и увести продуктовый коммит от проверки статуса,
|
||||
// а перестать трогать класс A, не перестав быть инфраструктурной задачей,
|
||||
// нельзя. Существование issue, его открытость и `blocked` проверяются всё равно.
|
||||
export function isInfrastructureRange(commits) {
|
||||
if (!commits.length) return false;
|
||||
return !commits.some((c) => c.classes.has('A'));
|
||||
}
|
||||
|
||||
export function isStableTarget(targetRef) {
|
||||
return /^(?:refs\/heads\/)?main$/.test(targetRef ?? '');
|
||||
}
|
||||
@@ -325,7 +340,9 @@ export function commitsNeedingIssueStatus(
|
||||
|
||||
// 8. статус issue. Fail closed: недоступный или закрытый issue — отказ, а не
|
||||
// пропуск. Гейт, который молчит при недоступном источнике правды, бесполезен.
|
||||
export function checkIssueStatuses(numbers, runner, { allowed = ALLOWED_STATUS } = {}) {
|
||||
export function checkIssueStatuses(
|
||||
numbers, runner, { allowed = ALLOWED_STATUS, statusOptional = false } = {},
|
||||
) {
|
||||
const out = [];
|
||||
for (const nn of numbers) {
|
||||
const r = runner(nn);
|
||||
@@ -355,7 +372,7 @@ export function checkIssueStatuses(numbers, runner, { allowed = ALLOWED_STATUS }
|
||||
continue;
|
||||
}
|
||||
const names = (issue.labels ?? []).map((l) => (typeof l === 'string' ? l : l.name));
|
||||
if (!names.some((n) => allowed.includes(n))) {
|
||||
if (!statusOptional && !names.some((n) => allowed.includes(n))) {
|
||||
const status = names.filter((n) => /^S\d-/.test(n));
|
||||
out.push({
|
||||
level: 'fail', rule: 8, sha: '-',
|
||||
@@ -544,7 +561,16 @@ function main(argv) {
|
||||
if (!cache.has(nn)) cache.set(nn, runner(nn));
|
||||
return cache.get(nn);
|
||||
};
|
||||
findings.push(...checkIssueStatuses(numbers, cached, { allowed }));
|
||||
const statusOptional = isInfrastructureRange(checkedCommits);
|
||||
if (statusOptional && numbers.length) {
|
||||
// Пропуск обязан быть виден: иначе исключение однажды скроет настоящее
|
||||
// нарушение и никто не узнает, что проверка не выполнялась.
|
||||
findings.push({
|
||||
level: 'warn', rule: 8, sha: '-',
|
||||
msg: `инфраструктурный диапазон (#118): файлов класса A нет, статусная метка issue не требуется`,
|
||||
});
|
||||
}
|
||||
findings.push(...checkIssueStatuses(numbers, cached, { allowed, statusOptional }));
|
||||
labelsOf = (nn) => {
|
||||
const r = cached(nn);
|
||||
if (!r || r.ok !== true) return null;
|
||||
|
||||
Reference in New Issue
Block a user