fix(ci): close clean runner gaps

This commit is contained in:
whyour
2026-08-22 12:17:11 +08:00
parent 1a4acaa56f
commit ca8ddf34b8
4 changed files with 74 additions and 23 deletions
+5 -1
View File
@@ -45,7 +45,9 @@ jobs:
- name: Install dependencies without lifecycle scripts
run: pnpm install --frozen-lockfile --ignore-scripts
- name: Initialize the public test environment
run: cp .env.example .env
run: |
cp .env.example .env
mkdir -p data/db
- name: Rebuild the reviewed native SQLite binding
run: pnpm rebuild @whyour/sqlite3
- name: Build backend and QL3 workspace from source
@@ -951,6 +953,8 @@ jobs:
docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_runtime LOGIN PASSWORD 'ql3_runtime_test'"
docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_admin LOGIN PASSWORD 'ql3_admin_test'"
docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_automation_manager LOGIN PASSWORD 'ql3_automation_manager_test'"
docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_approval_manager LOGIN PASSWORD 'ql3_approval_manager_test'"
docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_run_manager LOGIN PASSWORD 'ql3_run_manager_test'"
docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_package_manager LOGIN PASSWORD 'ql3_package_manager_test'"
docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_package_executor LOGIN PASSWORD 'ql3_package_executor_test'"
docker exec ${{ job.services.postgres.id }} psql -U postgres -d ql3_contract -v ON_ERROR_STOP=1 -c "CREATE ROLE ql3_worker_credential_manager LOGIN PASSWORD 'ql3_worker_credential_manager_test'"
+41 -19
View File
@@ -54,11 +54,21 @@ function docker(args, options = {}) {
}
function runCertificateTool(directory, args) {
const uid = process.getuid?.();
const gid = process.getgid?.();
if (
!Number.isSafeInteger(uid) ||
uid < 0 ||
!Number.isSafeInteger(gid) ||
gid < 0
) {
throw new Error('cannot resolve the host certificate owner');
}
docker([
'run',
'--rm',
'--user',
'0:0',
`${uid}:${gid}`,
'--volume',
`${directory}:/work`,
'--workdir',
@@ -187,6 +197,7 @@ function preparePostgresFileOwnership(directory) {
'chown',
`${identity[0]}:${identity[1]}`,
'/work/server.key',
'/work/active.crt',
]);
}
@@ -221,24 +232,34 @@ async function waitFor(operation, description, timeoutMs = WAIT_TIMEOUT_MS) {
}
async function waitForPostgres(containerName) {
await waitFor(
() =>
docker(
[
'exec',
containerName,
'pg_isready',
'-h',
'127.0.0.1',
'-U',
USER,
'-d',
DATABASE,
],
{ allowFailure: true },
).status === 0,
'TLS PostgreSQL readiness',
);
try {
await waitFor(
() =>
docker(
[
'exec',
containerName,
'pg_isready',
'-h',
'127.0.0.1',
'-U',
USER,
'-d',
DATABASE,
],
{ allowFailure: true },
).status === 0,
'TLS PostgreSQL readiness',
);
} catch (error) {
const logs = docker(['logs', containerName], { allowFailure: true });
const detail = [logs.stderr, logs.stdout].filter(Boolean).join('\n');
throw new Error(
`${error instanceof Error ? error.message : String(error)}${
detail ? `\nPostgreSQL container logs:\n${detail}` : ''
}`,
);
}
}
function databaseUrl(port) {
@@ -340,6 +361,7 @@ async function expectTlsRejection(
async function reloadCertificate(containerName, directory, generation) {
activateServerCertificate(directory, generation);
preparePostgresFileOwnership(directory);
docker(['kill', '--signal', 'HUP', containerName]);
await delay(500);
}
+14 -3
View File
@@ -39,6 +39,7 @@ test('pins backend CI to the released Node line and bootstraps a clean checkout'
assertOrdered(backend, [
'pnpm install --frozen-lockfile --ignore-scripts',
'cp .env.example .env',
'mkdir -p data/db',
'pnpm rebuild @whyour/sqlite3',
'pnpm build:back',
'pnpm run build:packages:ql3',
@@ -47,6 +48,18 @@ test('pins backend CI to the released Node line and bootstraps a clean checkout'
assert.doesNotMatch(backend, /if: matrix\.node == '24'/);
});
test('provisions every role required by the PostgreSQL migration stream', () => {
const postgres = jobSource('cluster-postgres', 'cluster-postgres-ha');
for (const role of [
'ql3_automation_manager',
'ql3_approval_manager',
'ql3_run_manager',
'ql3_package_manager',
]) {
assert.match(postgres, new RegExp(`CREATE ROLE ${role} LOGIN PASSWORD`));
}
});
test('bootstraps jobs that previously depended on local modules or artifacts', () => {
const serviceManager = jobSource(
'service-manager-bridge',
@@ -99,9 +112,7 @@ test('rebuilds the only native legacy binding used by resource evidence', () =>
});
test('does not forward a literal separator into recovery evidence CLIs', () => {
const recovery = jobSource(
'cluster-plugin-package-recovery-e2e',
);
const recovery = jobSource('cluster-plugin-package-recovery-e2e');
assert.doesNotMatch(
recovery,
/pnpm (?:test|audit):plugin-package-recovery-e2e:ql3 -- \\/,
@@ -60,7 +60,21 @@ test('builds cluster-postgres before the standalone TLS rotation contract', () =
'pg_stat_ssl',
'pg_is_in_recovery()',
"current_setting('transaction_read_only')",
'PostgreSQL container logs:',
]) {
assert.ok(source.includes(required), `missing TLS evidence: ${required}`);
}
assert.match(source, /const uid = process\.getuid\?\.\(\)/);
assert.match(source, /const gid = process\.getgid\?\.\(\)/);
const certificateTool = source.match(
/function runCertificateTool[\s\S]*?\n}\n/,
)?.[0];
assert.ok(certificateTool);
assert.match(certificateTool, /'--user',\s*`\$\{uid}:\$\{gid}`/);
assert.doesNotMatch(certificateTool, /'0:0'/);
assert.match(source, /'\/work\/server\.key',\s*'\/work\/active\.crt'/);
assert.match(
source,
/activateServerCertificate\(directory, generation\);\s*preparePostgresFileOwnership\(directory\);\s*docker\(\['kill'/,
);
});