Merge remote-tracking branch 'origin/main' into fix/cd-artifact-fastpath-20260524
# Conflicts: # package.json
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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: {},
|
||||
|
||||
Reference in New Issue
Block a user