fix: isolate artifact rebuild from release suites
Memind CI / Test, build, and release guards (push) Has been cancelled
Memind CI / Test, build, and release guards (push) Has been cancelled
This commit is contained in:
@@ -2,6 +2,9 @@ export const AUTOMATION_SUITES = Object.freeze([
|
|||||||
{
|
{
|
||||||
id: 'runtime-reproducibility',
|
id: 'runtime-reproducibility',
|
||||||
mode: 'upgrade',
|
mode: 'upgrade',
|
||||||
|
// Rebuilds .runtime/portal in place. Keep this suite isolated from all
|
||||||
|
// suites that boot or inspect the shared candidate runtime.
|
||||||
|
exclusive: true,
|
||||||
scenarios: ['REL-03'],
|
scenarios: ['REL-03'],
|
||||||
cases: {
|
cases: {
|
||||||
'REL-03': ['candidate plus two clean rebuilds produce the same sorted runtime tree SHA256'],
|
'REL-03': ['candidate plus two clean rebuilds produce the same sorted runtime tree SHA256'],
|
||||||
|
|||||||
+25
-1
@@ -72,6 +72,30 @@ export async function runWithConcurrency(items, concurrency, worker) {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some suites intentionally mutate the shared candidate artifact. Run those
|
||||||
|
// suites before the parallel batch so they cannot race suites that boot the
|
||||||
|
// same runtime while it is being rebuilt.
|
||||||
|
export async function runSuitesWithConcurrency(items, concurrency, worker) {
|
||||||
|
const results = new Array(items.length);
|
||||||
|
const parallel = [];
|
||||||
|
for (let index = 0; index < items.length; index += 1) {
|
||||||
|
if (items[index].exclusive) {
|
||||||
|
results[index] = await worker(items[index], index);
|
||||||
|
} else {
|
||||||
|
parallel.push({ item: items[index], index });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const parallelResults = await runWithConcurrency(
|
||||||
|
parallel,
|
||||||
|
concurrency,
|
||||||
|
({ item, index }) => worker(item, index),
|
||||||
|
);
|
||||||
|
for (let index = 0; index < parallel.length; index += 1) {
|
||||||
|
results[parallel[index].index] = parallelResults[index];
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
export async function runCommand(command, args, {
|
export async function runCommand(command, args, {
|
||||||
cwd = ROOT,
|
cwd = ROOT,
|
||||||
timeoutMs = 15 * 60 * 1000,
|
timeoutMs = 15 * 60 * 1000,
|
||||||
@@ -220,7 +244,7 @@ export async function executeReleaseGate(options) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const suites = AUTOMATION_SUITES.filter((candidate) => suiteIsInMode(candidate, options.mode));
|
const suites = AUTOMATION_SUITES.filter((candidate) => suiteIsInMode(candidate, options.mode));
|
||||||
const executions = await runWithConcurrency(
|
const executions = await runSuitesWithConcurrency(
|
||||||
suites,
|
suites,
|
||||||
options.suiteConcurrency,
|
options.suiteConcurrency,
|
||||||
(suite) => runSuite(suite, outputDir, options.timeoutMs),
|
(suite) => runSuite(suite, outputDir, options.timeoutMs),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import test from 'node:test';
|
import test from 'node:test';
|
||||||
|
|
||||||
import { parseRunnerArgs, runWithConcurrency } from './runner.mjs';
|
import { parseRunnerArgs, runSuitesWithConcurrency, runWithConcurrency } from './runner.mjs';
|
||||||
|
|
||||||
test('runner accepts a bounded suite concurrency and rejects unsafe values', () => {
|
test('runner accepts a bounded suite concurrency and rejects unsafe values', () => {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
@@ -31,3 +31,24 @@ test('runWithConcurrency preserves result order and never exceeds its worker bud
|
|||||||
assert.deepEqual(results, ['result-0', 'result-1', 'result-2', 'result-3']);
|
assert.deepEqual(results, ['result-0', 'result-1', 'result-2', 'result-3']);
|
||||||
assert.equal(peak, 2);
|
assert.equal(peak, 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('exclusive suites finish before parallel suites start', async () => {
|
||||||
|
const events = [];
|
||||||
|
const results = await runSuitesWithConcurrency(
|
||||||
|
[
|
||||||
|
{ id: 'mutating', exclusive: true },
|
||||||
|
{ id: 'parallel-a' },
|
||||||
|
{ id: 'parallel-b' },
|
||||||
|
],
|
||||||
|
2,
|
||||||
|
async (suite) => {
|
||||||
|
events.push(`start:${suite.id}`);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 5));
|
||||||
|
events.push(`end:${suite.id}`);
|
||||||
|
return suite.id;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert.deepEqual(results, ['mutating', 'parallel-a', 'parallel-b']);
|
||||||
|
assert.ok(events.indexOf('end:mutating') < events.indexOf('start:parallel-a'));
|
||||||
|
assert.ok(events.indexOf('end:mutating') < events.indexOf('start:parallel-b'));
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user