25be530d84
Memind CI / Test, build, and release guards (push) Successful in 3m33s
A change to one run-release-gate script must not force every live and upgrade suite to rerun. Co-authored-by: Cursor <cursoragent@cursor.com>
89 lines
2.9 KiB
JavaScript
89 lines
2.9 KiB
JavaScript
import fs from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
|
|
const REPOSITORY_CHECK_IDS = new Set(['REL-01', 'REL-02', 'REL-04']);
|
|
|
|
export function commandFilesForSuite(suite) {
|
|
return (suite.command ?? [])
|
|
.slice(1)
|
|
.filter((arg) => typeof arg === 'string' && !arg.startsWith('-'))
|
|
.map((arg) => arg.replaceAll('\\', '/'));
|
|
}
|
|
|
|
export function suiteInvalidatedByChanges(suite, changedPaths) {
|
|
const normalized = new Set((changedPaths ?? []).map((item) => String(item).replaceAll('\\', '/')));
|
|
if (normalized.has('release-gate/coverage.mjs')) return true;
|
|
return commandFilesForSuite(suite).some((filePath) => normalized.has(filePath));
|
|
}
|
|
|
|
export function previousScenarioMap(report) {
|
|
return new Map((report?.scenarios ?? []).map((scenario) => [scenario.id, scenario]));
|
|
}
|
|
|
|
export function shouldRerunImpactSuite({
|
|
suite,
|
|
selectedIds,
|
|
previousReport,
|
|
artifactSha256,
|
|
commitSha,
|
|
changedPathsSincePrevious = [],
|
|
}) {
|
|
const selectedInSuite = suite.scenarios.filter((scenarioId) => selectedIds.has(scenarioId));
|
|
if (selectedInSuite.length === 0) return false;
|
|
if (!previousReport || previousReport.artifact_sha256 !== artifactSha256) return true;
|
|
if (suiteInvalidatedByChanges(suite, changedPathsSincePrevious)) return true;
|
|
if (suite.mode === 'scenarios' && previousReport.commit_sha !== commitSha) return true;
|
|
|
|
const previous = previousScenarioMap(previousReport);
|
|
return selectedInSuite.some((scenarioId) => previous.get(scenarioId)?.status !== 'passed');
|
|
}
|
|
|
|
export function carryForwardScenario(previousScenario) {
|
|
return {
|
|
...previousScenario,
|
|
evidence: [
|
|
...(previousScenario.evidence ?? []),
|
|
'carried_forward=true',
|
|
],
|
|
};
|
|
}
|
|
|
|
export async function findImpactResumeReport({
|
|
reportRoot,
|
|
artifactSha256,
|
|
commitSha,
|
|
}) {
|
|
let entries = [];
|
|
try {
|
|
entries = await fs.readdir(reportRoot, { withFileTypes: true });
|
|
} catch (error) {
|
|
if (error.code === 'ENOENT') return null;
|
|
throw error;
|
|
}
|
|
|
|
const candidates = [];
|
|
for (const entry of entries) {
|
|
if (!entry.isDirectory() || entry.name === 'local') continue;
|
|
const reportPath = path.join(reportRoot, entry.name, 'report.json');
|
|
try {
|
|
const report = JSON.parse(await fs.readFile(reportPath, 'utf8'));
|
|
if (report?.mode !== 'impact') continue;
|
|
if (report.artifact_sha256 !== artifactSha256) continue;
|
|
if (!Array.isArray(report.scenarios)) continue;
|
|
candidates.push({ report, reportPath, commitSha: report.commit_sha });
|
|
} catch {
|
|
// ignore unreadable reports
|
|
}
|
|
}
|
|
if (candidates.length === 0) return null;
|
|
|
|
const sameCommit = candidates.find((candidate) => candidate.commitSha === commitSha);
|
|
if (sameCommit) return sameCommit;
|
|
candidates.sort((left, right) => (
|
|
Date.parse(right.report.completed_at ?? 0) - Date.parse(left.report.completed_at ?? 0)
|
|
));
|
|
return candidates[0];
|
|
}
|
|
|
|
export { REPOSITORY_CHECK_IDS };
|