Merge remote-tracking branch 'origin/main' into fix/cd-artifact-fastpath-20260524

# Conflicts:
#	package.json
This commit is contained in:
Codex
2026-05-24 06:18:34 +00:00
11 changed files with 219 additions and 27 deletions
+2 -2
View File
@@ -2687,7 +2687,7 @@ export async function runDevCdApply(argv, io = {}) {
kind: "runtime-k8s-job",
jobNamePrefix: "hwlab-runtime-provision",
commandArgs: [
"scripts/dev-runtime-provisioning.mjs",
"cmd/hwlab-cloud-api/provision.mjs",
"--apply",
"--confirm-dev",
"--confirmed-non-production",
@@ -2704,7 +2704,7 @@ export async function runDevCdApply(argv, io = {}) {
kind: "runtime-k8s-job",
jobNamePrefix: "hwlab-runtime-migrate",
commandArgs: [
"scripts/dev-runtime-migration.mjs",
"cmd/hwlab-cloud-api/migrate.mjs",
"--apply",
"--confirm-dev",
"--confirmed-non-production",
+14 -4
View File
@@ -1337,8 +1337,7 @@ test("transaction runs phases, allows internal side-effect env, releases lock, a
const publishCall = commandLog.find((entry) => entry.args.includes("scripts/dev-artifact-publish.mjs"));
const applyCall = commandLog.find((entry) => entry.args.includes("scripts/dev-deploy-apply.mjs"));
const provisioningCall = commandLog.find((entry) => entry.args.includes("scripts/dev-runtime-provisioning.mjs"));
const migrationCall = commandLog.find((entry) => entry.args.includes("scripts/dev-runtime-migration.mjs"));
const provisioningCall = commandLog.find((entry) => entry.args.includes("cmd/hwlab-cloud-api/provision.mjs"));
const postflightCall = commandLog.find((entry) => entry.args.includes("scripts/dev-runtime-postflight.mjs"));
const refreshCall = commandLog.find((entry) => entry.args.includes("scripts/refresh-artifact-catalog.mjs"));
assert.ok(publishCall?.env.HWLAB_CD_TRANSACTION_ID);
@@ -1355,8 +1354,19 @@ test("transaction runs phases, allows internal side-effect env, releases lock, a
);
assert.ok(provisioningJobApply);
assert.ok(migrationJobApply);
assert.equal(JSON.parse(provisioningJobApply.input).spec.template.spec.containers[0].args[0], "scripts/dev-runtime-provisioning.mjs");
assert.equal(JSON.parse(migrationJobApply.input).spec.template.spec.containers[0].args[0], "scripts/dev-runtime-migration.mjs");
const provisioningJob = JSON.parse(provisioningJobApply.input);
const migrationJob = JSON.parse(migrationJobApply.input);
assert.equal(provisioningJob.spec.template.spec.containers[0].args[0], "cmd/hwlab-cloud-api/provision.mjs");
assert.equal(migrationJob.spec.template.spec.containers[0].args[0], "cmd/hwlab-cloud-api/migrate.mjs");
assert.equal(
provisioningJob.spec.template.spec.containers[0].env.some((entry) => entry.name === "HWLAB_CD_TRANSACTION_ID"),
true
);
assert.equal(
migrationJob.spec.template.spec.containers[0].env.some((entry) => entry.name === "HWLAB_CD_TRANSACTION_ID"),
true
);
assert.equal(provisioningCall, undefined);
assert.equal(postflightCall?.env.HWLAB_CD_TRANSACTION_ID, publishCall.env.HWLAB_CD_TRANSACTION_ID);
assert.deepEqual(postflightCall.args.slice(0, 4), ["scripts/dev-runtime-postflight.mjs", "--live", "--confirm-dev", "--confirmed-non-production"]);
assert.equal(refreshCall.args[refreshCall.args.indexOf("--target-ref") + 1], "abc1234abc1234abc1234abc1234abc1234abc1");
+11 -1
View File
@@ -699,13 +699,15 @@ function usage() {
}
export function formatDevRuntimeMigrationFailure(error) {
const message = redactFailureText(error instanceof Error ? error.message : String(error));
return {
issue,
conclusion: {
status: "blocked",
summary: "DEV runtime migration command failed before producing a report."
},
error: error instanceof Error ? error.message : String(error),
error: message,
traceId: sha256(message).slice(0, 16),
safety: {
devOnly: true,
prodAllowed: false,
@@ -713,3 +715,11 @@ export function formatDevRuntimeMigrationFailure(error) {
}
};
}
function redactFailureText(value) {
return String(value)
.replace(/postgres(?:ql)?:\/\/[^\s"'<>]+/giu, "[redacted-postgres-url]")
.replace(/(password\s*[=:]\s*)[^\s,;]+/giu, "$1[redacted]")
.replace(/(token\s*[=:]\s*)[^\s,;]+/giu, "$1[redacted]")
.replace(/(kubeconfig\s*[=:]\s*)[^\s,;]+/giu, "$1[redacted]");
}
@@ -1,5 +1,8 @@
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import path from "node:path";
import test from "node:test";
import { promisify } from "node:util";
import {
buildDevRuntimeMigrationReport,
@@ -11,6 +14,9 @@ import {
CLOUD_RUNTIME_DURABLE_TABLE_COLUMNS
} from "../../internal/db/schema.mjs";
const execFileAsync = promisify(execFile);
const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), "../..");
test("source check is non-secret and does not attempt live DB access", async () => {
const report = await buildDevRuntimeMigrationReport(parseArgs(["--check"]), {
env: {
@@ -33,6 +39,48 @@ test("source check is non-secret and does not attempt live DB access", async ()
assert.equal(JSON.stringify(report).includes("db.example.test"), false);
});
test("cloud-api image migration entrypoint exposes the same non-secret source check", async () => {
const { stdout } = await execFileAsync(
process.execPath,
["cmd/hwlab-cloud-api/migrate.mjs", "--check"],
{
cwd: repoRoot,
env: {
...process.env,
HWLAB_CLOUD_DB_URL: "postgresql://hwlab:secret-password@db.example.test:5432/hwlab?sslmode=require",
HWLAB_CLOUD_DB_SSL_MODE: "disable"
}
}
);
const report = JSON.parse(stdout);
assert.equal(report.conclusion.status, "ready");
assert.equal(report.actions.liveDbReadAttempted, false);
assert.equal(report.actions.liveDbWriteAttempted, false);
assert.equal(report.safety.printsSecretValues, false);
assert.equal(report.safety.dbUrlValueRedacted, true);
assert.equal(report.migration.id, CLOUD_CORE_MIGRATION_ID);
assert.equal(JSON.stringify(report).includes("secret-password"), false);
assert.equal(JSON.stringify(report).includes("db.example.test"), false);
});
test("cloud-api image migration entrypoint redacts invalid DSN-shaped argv failures", async () => {
const secretArg = "postgresql://hwlab:secret-password@db.example.test:5432/hwlab?sslmode=require";
await assert.rejects(
execFileAsync(process.execPath, ["cmd/hwlab-cloud-api/migrate.mjs", secretArg], { cwd: repoRoot }),
(error) => {
const report = JSON.parse(error.stdout);
assert.equal(report.conclusion.status, "blocked");
assert.equal(report.error, "unknown argument: [redacted-postgres-url]");
assert.match(report.traceId, /^[0-9a-f]{16}$/u);
assert.equal(report.safety.secretValuesPrinted, false);
assert.equal(JSON.stringify(report).includes("secret-password"), false);
assert.equal(JSON.stringify(report).includes("db.example.test"), false);
return true;
}
);
});
test("apply mode refuses missing DEV confirmation before touching query client", async () => {
const queryClient = createFakeQueryClient({ migrationReady: false });
const report = await buildDevRuntimeMigrationReport(parseArgs(["--apply"]), {
@@ -1,11 +1,17 @@
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import path from "node:path";
import test from "node:test";
import { promisify } from "node:util";
import {
buildDevRuntimeProvisioningReport,
parseArgs
} from "./dev-runtime-provisioning.mjs";
const execFileAsync = promisify(execFile);
const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), "../..");
test("source check parses target role/database without leaking DB URL material", async () => {
const report = await buildDevRuntimeProvisioningReport(parseArgs(["--check"]), {
env: {
@@ -25,6 +31,31 @@ test("source check parses target role/database without leaking DB URL material",
assertNoFixtureSecrets(report);
});
test("cloud-api image provisioning entrypoint exposes non-secret source check", async () => {
const { stdout } = await execFileAsync(
process.execPath,
["cmd/hwlab-cloud-api/provision.mjs", "--check"],
{
cwd: repoRoot,
env: {
...process.env,
HWLAB_CLOUD_DB_URL: fixturePostgresUrl({ password: fixtureSecret("super") }),
HWLAB_CLOUD_DB_SSL_MODE: "disable"
}
}
);
const report = JSON.parse(stdout);
assert.equal(report.conclusion.status, "ready");
assert.equal(report.actions.liveDbReadAttempted, false);
assert.equal(report.actions.liveDbWriteAttempted, false);
assert.equal(report.db.targetRoleNamePresent, true);
assert.equal(report.db.targetDatabaseNamePresent, true);
assert.equal(report.db.targetPasswordPresent, true);
assert.equal(report.safety.secretValuesPrinted, false);
assertNoFixtureSecrets(report);
});
test("source check without injected DB URL is ready and performs no live access", async () => {
const report = await buildDevRuntimeProvisioningReport(parseArgs(["--check"]), {
env: {},