mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-28 19:01:34 +00:00
fix: exempt pipeline review documents from the branch-name gate rule (#305)
Правило 2 локального process-gate блокировало пуш issue-ветки после того, как конвейер «Привести ветку к dev» вносил в неё свежую историю dev с чужим коммитом «docs: review document for #NNN» — локальный origin/dev автора отставал и не вычитал его из диапазона, а нарушение в истории не чинится следующим коммитом (единственный выход был --no-verify по §12/17). Исключение доказуемое и fail-closed: точный subject документа ревью И дифф только в docs/reviews/ (files обязателен — ownCommits теперь читаются с filesOf). Любой код рядом с документом или пустой список файлов возвращают правило 2 в строй; юниты фиксируют оба контура. Issue: #305 User-Visible: no
This commit is contained in:
@@ -202,12 +202,26 @@ export function evaluateCommit(c) {
|
||||
// уезжает назад, и в диапазон попадают коммиты `dev` с чужими номерами issue —
|
||||
// каждый из них выглядел бы нарушением. Проверено на реальной истории: сидя на
|
||||
// issue/89 с диапазоном по dev, гейт дал 26 ложных отказов из 26 коммитов.
|
||||
// Документ ревью, доказанно созданный конвейером (#305): точный subject
|
||||
// «docs: review document for #NN» и дифф ТОЛЬКО в docs/reviews/. Такой коммит
|
||||
// попадает в чужую issue-ветку легитимно — шаг «Привести ветку к dev»
|
||||
// вносит свежую историю dev, где конвейер только что опубликовал документ
|
||||
// соседней задачи, а локальный origin/dev автора мог отстать и не вычесть его
|
||||
// из диапазона. Кода в таком коммите нет по построению (files-критерий), и
|
||||
// судить им правило 2 — ложный отказ, чинимый только --no-verify.
|
||||
export function isPipelineReviewDocCommit(c) {
|
||||
return /^docs: review document for #\d+$/.test(c.subject ?? '')
|
||||
&& (c.files?.length ?? 0) > 0
|
||||
&& c.files.every((f) => f.startsWith('docs/reviews/'));
|
||||
}
|
||||
|
||||
export function checkBranchRule(branch, commits) {
|
||||
const m = (branch ?? '').match(/^issue\/(\d+)-/);
|
||||
if (!m) return [];
|
||||
const want = `#${m[1]}`;
|
||||
const out = [];
|
||||
for (const c of commits) {
|
||||
if (isPipelineReviewDocCommit(c)) continue;
|
||||
for (const t of c.issues) {
|
||||
if (t !== want) {
|
||||
out.push({
|
||||
@@ -541,8 +555,11 @@ function main(argv) {
|
||||
const hasDev = spawnSync('git', ['-C', repo, 'rev-parse', '--verify', 'origin/dev'],
|
||||
{ encoding: 'utf8' }).status === 0;
|
||||
if (!hasDev) return checkedCommits;
|
||||
// filesOf обязателен: исключение #305 доказывается диффом коммита
|
||||
// (только docs/reviews/), а без файлов оно не срабатывает fail-closed.
|
||||
const own = parseRecords(
|
||||
git(['log', '--reverse', `--pretty=format:${LOG_FORMAT}`, 'origin/dev..HEAD'], repo),
|
||||
filesOf,
|
||||
);
|
||||
return commitsNeedingTargetValidation(own, { targetRef, isCommitOnMain });
|
||||
})()
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
STRICT_STATUS,
|
||||
buildReport,
|
||||
checkBranchRule,
|
||||
isPipelineReviewDocCommit,
|
||||
checkIssueStatuses,
|
||||
checkReviewDocLimit,
|
||||
REVIEW_DOC_LIMIT,
|
||||
@@ -648,3 +649,34 @@ test('the CLI waives the issue status for a class-B-only range but not with clas
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('rule 2 exempts a pipeline review document proven by subject and diff (#305)', () => {
|
||||
const doc = makeCommit({
|
||||
sha: 'a'.repeat(40),
|
||||
subject: 'docs: review document for #304',
|
||||
body: 'Issue: #304\nUser-Visible: no',
|
||||
files: ['docs/reviews/CODE-REVIEW-304-r1.md'],
|
||||
});
|
||||
assert.equal(isPipelineReviewDocCommit(doc), true);
|
||||
assert.deepEqual(checkBranchRule('issue/302-junction-node-material', [doc]), []);
|
||||
|
||||
// Any code beside the document voids the proof — rule 2 fires again.
|
||||
const forged = makeCommit({
|
||||
sha: 'b'.repeat(40),
|
||||
subject: 'docs: review document for #304',
|
||||
body: 'Issue: #304',
|
||||
files: ['docs/reviews/CODE-REVIEW-304-r1.md', 'src/houseplan-card.ts'],
|
||||
});
|
||||
assert.equal(isPipelineReviewDocCommit(forged), false);
|
||||
assert.equal(checkBranchRule('issue/302-junction-node-material', [forged]).length, 1);
|
||||
|
||||
// Fail-closed: without a file list the exemption cannot prove itself.
|
||||
const blind = makeCommit({
|
||||
sha: 'c'.repeat(40),
|
||||
subject: 'docs: review document for #304',
|
||||
body: 'Issue: #304',
|
||||
files: [],
|
||||
});
|
||||
assert.equal(isPipelineReviewDocCommit(blind), false);
|
||||
assert.equal(checkBranchRule('issue/302-junction-node-material', [blind]).length, 1);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user