mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
ci(process): раунд ревью ждёт Validate событием, а не сном раннера (#636)
Стадия prepare спала ≈ 28 минут на раунд, пока шёл Validate с мутантами на материале (модель работает 10–12); за неделю ≈ 420–500 job-минут простоя и потолок бюджета стадии 55 минут. - validate-gate.mjs: `--no-wait` — гейт диспатчит прогон, убеждается, что тот встал на материал (#539 сохранён), и возвращает `pending` (код 2) вместо ожидания; завершённый зелёный/красный отдаёт сразу, как прежде. - process.yml prepare: третий исход `proceed=pending`: запечатанный маркер `review-pending-<issue>-<run>-<attempt>` (issue, stage, branch, material_sha, validate run) и выход; модель и интеграция не запускаются; возврат автору — только на явном `false`. - process-resume.yml + scripts/process-resume.mjs: на `workflow_run: completed` Validate по ветке issue/* — если метка S7 стоит, активного прогона нет и последний прогон оставил маркер на этот SHA, переставить S7 (HP_PROCESS_TOKEN); новый прогон находит завершённый dispatch сразу. Без маркера не будит. - process-reconcile.mjs: читает маркер и состояние Validate на материале; идёт — wait, завершился/пропал без продолжения — retry; без маркера — прежний escalate. Общий loadSealedArtifact, экспорт processRuns/artifactNames. - preflight сверяет process-resume.yml между main и dev наравне с process.yml. - Тесты: validate-gate (4), process-resume (8, включая контракт трёх workflow), process-reconcile (2); мутанты gate-no-wait-still-sleeps, resume-wakes-round-without-marker, resume-ignores-active-run, reconcile-wakes-pending-while-validate-active. PROCESS.md §10.4, AGENTS.md. Issue: #636 User-Visible: no
This commit is contained in:
@@ -7,10 +7,15 @@
|
||||
* доказательство для ревью — dispatch-прогон на точном SHA материала. Push-
|
||||
* прогон на том же SHA зелёный не считается: в нём мутантов нет.
|
||||
*
|
||||
* node scripts/validate-gate.mjs --repo=<owner/repo> --ref=<ветка> --sha=<sha> [--workflow=validate.yml]
|
||||
* node scripts/validate-gate.mjs --repo=<owner/repo> --ref=<ветка> --sha=<sha> [--workflow=validate.yml] [--no-wait]
|
||||
*
|
||||
* Печатает `result=green|failed|missing` и `url=…` (и в $GITHUB_OUTPUT, если он
|
||||
* задан); код выхода 0 только при green. Логика — чистая функция `validateGate`
|
||||
* Печатает `result=green|failed|missing|pending`, `url=…`, `run_id=…` (и в
|
||||
* $GITHUB_OUTPUT, если он задан); код выхода 0 только при green, 2 — pending.
|
||||
* `--no-wait` (#636): гейт диспатчит прогон и убеждается, что тот появился на
|
||||
* материале, но не ждёт его завершения — раннер конвейера освобождается, а
|
||||
* раунд продолжает `process-resume.yml` по событию `workflow_run` (страховка —
|
||||
* reconcile). Зелёный или красный завершённый прогон и с `--no-wait`
|
||||
* возвращается сразу. Логика — чистая функция `validateGate`
|
||||
* поверх инъектируемых `ops`, чтобы тесты и мутанты гоняли её без gh.
|
||||
*/
|
||||
import { spawnSync } from 'node:child_process';
|
||||
@@ -58,9 +63,14 @@ export function provesMutants(jobs) {
|
||||
* @param {string} p.ref ветка, на которой запускать
|
||||
* @param {string} p.sha SHA материала
|
||||
* @param {object} p.ops GitHub run/proof operations plus dispatch, sleep and clock.
|
||||
* @returns {Promise<{result:'green'|'failed'|'missing', url:string|null, note:string}>}
|
||||
* @param {boolean} [p.wait] `false` — не ждать идущий прогон, а вернуть `pending` (#636):
|
||||
* раннер конвейера не спит 28 минут; продолжение разбудит событие
|
||||
* `workflow_run` (process-resume.yml) либо reconcile.
|
||||
* @returns {Promise<{result:'green'|'failed'|'missing'|'pending', url:string|null, note:string, runId?:number}>}
|
||||
*/
|
||||
export async function validateGate({ ref, sha, ops, appearMs = VALIDATE_APPEAR_MS, totalMs = VALIDATE_TOTAL_MS, pollMs = POLL_MS }) {
|
||||
export async function validateGate({
|
||||
ref, sha, ops, appearMs = VALIDATE_APPEAR_MS, totalMs = VALIDATE_TOTAL_MS, pollMs = POLL_MS, wait = true,
|
||||
}) {
|
||||
const started = ops.now();
|
||||
const candidateTree = await ops.candidateTree(sha);
|
||||
const ignored = new Set(); // завершённые dispatch без применимого proof
|
||||
@@ -84,6 +94,13 @@ export async function validateGate({ ref, sha, ops, appearMs = VALIDATE_APPEAR_M
|
||||
tracked = null;
|
||||
continue;
|
||||
}
|
||||
if (!wait) {
|
||||
// #636: прогон найден и идёт — ждать его будет событие, не раннер.
|
||||
return {
|
||||
result: 'pending', url: run.url || null, runId: run.databaseId,
|
||||
note: `Validate с мутантами идёт (${run.status}); продолжение — по завершении прогона`,
|
||||
};
|
||||
}
|
||||
} else if (dispatchedAt === null) {
|
||||
await ops.dispatch(ref);
|
||||
dispatchedAt = ops.now();
|
||||
@@ -151,9 +168,11 @@ if (invokedDirectly) {
|
||||
console.error('usage: validate-gate.mjs --repo=<owner/repo> --ref=<branch> --sha=<sha> [--workflow=validate.yml]');
|
||||
process.exit(2);
|
||||
}
|
||||
const outcome = await validateGate({ ref, sha, ops: realOps({ repo, workflow: arg('workflow') || 'validate.yml' }) });
|
||||
const lines = [`result=${outcome.result}`, `url=${outcome.url || ''}`, `note=${outcome.note}`];
|
||||
const wait = !process.argv.includes('--no-wait');
|
||||
const outcome = await validateGate({ ref, sha, wait, ops: realOps({ repo, workflow: arg('workflow') || 'validate.yml' }) });
|
||||
const lines = [`result=${outcome.result}`, `url=${outcome.url || ''}`, `run_id=${outcome.runId || ''}`, `note=${outcome.note}`];
|
||||
for (const line of lines) console.log(line);
|
||||
if (process.env.GITHUB_OUTPUT) appendFileSync(process.env.GITHUB_OUTPUT, `${lines.join('\n')}\n`);
|
||||
process.exit(outcome.result === 'green' ? 0 : 1);
|
||||
// 0 — зелёный, 2 — идёт (только с --no-wait), 1 — красный/пропавший.
|
||||
process.exit(outcome.result === 'green' ? 0 : outcome.result === 'pending' ? 2 : 1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user