Merge pull request #427 from pikasTech/fix/cloud-api-image-migrate-entry
产品化 cloud-api 镜像内 DB 运维入口
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env node
|
||||
import {
|
||||
formatDevRuntimeMigrationFailure,
|
||||
runDevRuntimeMigrationCli
|
||||
} from "../../scripts/src/dev-runtime-migration.mjs";
|
||||
|
||||
try {
|
||||
process.exitCode = await runDevRuntimeMigrationCli(process.argv.slice(2));
|
||||
} catch (error) {
|
||||
process.stdout.write(`${JSON.stringify(formatDevRuntimeMigrationFailure(error), null, 2)}\n`);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env node
|
||||
import {
|
||||
formatDevRuntimeProvisioningFailure,
|
||||
runDevRuntimeProvisioningCli
|
||||
} from "../../scripts/src/dev-runtime-provisioning.mjs";
|
||||
|
||||
try {
|
||||
process.exitCode = await runDevRuntimeProvisioningCli(process.argv.slice(2));
|
||||
} catch (error) {
|
||||
process.stdout.write(`${JSON.stringify(formatDevRuntimeProvisioningFailure(error), null, 2)}\n`);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
@@ -390,15 +390,18 @@ The DEV CD transaction owns the durable runtime support sequence before final
|
||||
live postflight:
|
||||
|
||||
```sh
|
||||
node scripts/dev-runtime-provisioning.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/report.json
|
||||
node scripts/dev-runtime-migration.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/report.json
|
||||
node cmd/hwlab-cloud-api/provision.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/report.json
|
||||
node cmd/hwlab-cloud-api/migrate.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/report.json
|
||||
node scripts/dev-runtime-postflight.mjs --live --confirm-dev --confirmed-non-production --target api --report /tmp/hwlab-dev-gate/report.json
|
||||
```
|
||||
|
||||
These commands are called by `scripts/dev-cd-apply.mjs` under the transaction
|
||||
Lease. Runners without explicit rollout authorization may run the source-only
|
||||
`--check` variants and prepare PRs/artifacts, but must leave DEV apply,
|
||||
rollout, live verification, and lock ownership to the host commander.
|
||||
Lease. Provisioning and migration run from the current `hwlab-cloud-api` image
|
||||
as stable image-internal operator entrypoints; they receive DB input only
|
||||
through SecretRef-backed env and print only redacted structured status. Runners
|
||||
without explicit rollout authorization may run the source-only `--check`
|
||||
variants and prepare PRs/artifacts, but must leave DEV apply, rollout, live
|
||||
verification, and lock ownership to the host commander.
|
||||
|
||||
The previous verified manual path for `hwlab-cloud-web` was:
|
||||
|
||||
|
||||
@@ -220,11 +220,23 @@ below are true.
|
||||
|
||||
## DEV DB Provisioning Automation
|
||||
|
||||
The repo-owned DEV DB provisioning entrypoint is:
|
||||
The cloud-api image owns a stable DEV DB provisioning entrypoint:
|
||||
|
||||
```sh
|
||||
node cmd/hwlab-cloud-api/provision.mjs --check
|
||||
node cmd/hwlab-cloud-api/provision.mjs --dry-run --allow-live-db-read --confirm-dev --report /tmp/hwlab-dev-gate/dev-runtime-provisioning-report.json
|
||||
node cmd/hwlab-cloud-api/provision.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/dev-runtime-provisioning-report.json
|
||||
```
|
||||
|
||||
This is the preferred image-internal operator command for `hwlab-cloud-api`.
|
||||
It delegates to the same repo-owned provisioning implementation as the root
|
||||
script and receives admin/target DB inputs only through SecretRef-backed env.
|
||||
|
||||
The repo-level compatibility entrypoint remains:
|
||||
|
||||
```sh
|
||||
node scripts/dev-runtime-provisioning.mjs --check
|
||||
node scripts/dev-runtime-provisioning.mjs --dry-run --allow-live-db-read --confirm-dev --report /tmp/hwlab-dev-gate/report.json
|
||||
node scripts/dev-runtime-provisioning.mjs --dry-run --allow-live-db-read --confirm-dev --report /tmp/hwlab-dev-gate/dev-runtime-provisioning-report.json
|
||||
```
|
||||
|
||||
The default `--check` path validates the redacted target DB URL contract only:
|
||||
@@ -237,7 +249,7 @@ kubeconfig material.
|
||||
An authorized DEV provisioning apply is:
|
||||
|
||||
```sh
|
||||
node scripts/dev-runtime-provisioning.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/report.json
|
||||
node scripts/dev-runtime-provisioning.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/dev-runtime-provisioning-report.json
|
||||
```
|
||||
|
||||
The apply path uses `HWLAB_CLOUD_DB_URL` from
|
||||
@@ -258,11 +270,27 @@ untracked manual DB write.
|
||||
|
||||
## Runtime Migration Automation
|
||||
|
||||
The repo-owned runtime migration entrypoint is:
|
||||
The cloud-api image owns a stable runtime migration entrypoint:
|
||||
|
||||
```sh
|
||||
node cmd/hwlab-cloud-api/migrate.mjs --check
|
||||
node cmd/hwlab-cloud-api/migrate.mjs --dry-run --report /tmp/hwlab-dev-gate/dev-runtime-migration-report.json
|
||||
node cmd/hwlab-cloud-api/migrate.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/dev-runtime-migration-report.json
|
||||
```
|
||||
|
||||
This is the preferred image-internal operator command for `hwlab-cloud-api`.
|
||||
It delegates to the same repo-owned migration implementation as the root
|
||||
script, so there is only one migration contract and one output format. The
|
||||
cloud-api artifact build copies `cmd/`, `scripts/`, `internal/`, `package.json`,
|
||||
and runtime dependencies into the image; the DEV CD runtime migration Job uses
|
||||
`node cmd/hwlab-cloud-api/migrate.mjs` from the current cloud-api image and
|
||||
injects DB inputs only through SecretRef-backed env.
|
||||
|
||||
The repo-level compatibility entrypoint remains:
|
||||
|
||||
```sh
|
||||
node scripts/dev-runtime-migration.mjs --check
|
||||
node scripts/dev-runtime-migration.mjs --dry-run --report /tmp/hwlab-dev-gate/report.json
|
||||
node scripts/dev-runtime-migration.mjs --dry-run --report /tmp/hwlab-dev-gate/dev-runtime-migration-report.json
|
||||
```
|
||||
|
||||
Those default paths are source-only. They validate
|
||||
@@ -275,14 +303,14 @@ An authorized operator may run a read-only live verification only after
|
||||
injecting the DEV DB URL through the existing Secret/env path:
|
||||
|
||||
```sh
|
||||
node scripts/dev-runtime-migration.mjs --dry-run --allow-live-db-read --confirm-dev --report /tmp/hwlab-dev-gate/report.json
|
||||
node scripts/dev-runtime-migration.mjs --dry-run --allow-live-db-read --confirm-dev --report /tmp/hwlab-dev-gate/dev-runtime-migration-report.json
|
||||
```
|
||||
|
||||
A real DEV migration apply is intentionally separate and requires all explicit
|
||||
write flags:
|
||||
|
||||
```sh
|
||||
node scripts/dev-runtime-migration.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/report.json
|
||||
node scripts/dev-runtime-migration.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/dev-runtime-migration-report.json
|
||||
```
|
||||
|
||||
The apply command is scoped to DEV Postgres schema objects declared by
|
||||
@@ -306,7 +334,7 @@ The repo-owned postflight entrypoint is:
|
||||
|
||||
```sh
|
||||
node scripts/dev-runtime-postflight.mjs --check
|
||||
node scripts/dev-runtime-postflight.mjs --live --confirm-dev --confirmed-non-production --target api --report /tmp/hwlab-dev-gate/report.json
|
||||
node scripts/dev-runtime-postflight.mjs --live --confirm-dev --confirmed-non-production --target api --report /tmp/hwlab-dev-gate/dev-runtime-postflight-report.json
|
||||
```
|
||||
|
||||
Default `--check` is source-only. Live mode first reads
|
||||
@@ -335,13 +363,13 @@ authorizes the DEV apply command and the preconditions below are already true.
|
||||
|
||||
| Check | Safe for normal runners | Evidence allowed | Not allowed |
|
||||
| --- | --- | --- | --- |
|
||||
| Source migration contract | Yes | `node scripts/dev-runtime-migration.mjs --check` and `node scripts/dev-runtime-migration.mjs --dry-run --report /tmp/hwlab-dev-gate/report.json` | DB connection, Secret read, DEV/PROD write |
|
||||
| Source migration contract | Yes | `node scripts/dev-runtime-migration.mjs --check` and `node scripts/dev-runtime-migration.mjs --dry-run --report /tmp/hwlab-dev-gate/dev-runtime-migration-report.json` | DB connection, Secret read, DEV/PROD write |
|
||||
| Static runtime boundary contract | Yes | `node scripts/validate-runtime-boundary.mjs`, source docs, manifests, and redacted Secret refs | Live mutation, service restart, Secret value output |
|
||||
| Public health observation | Yes, when command scope is read-only | `/health/live` fields such as `db.liveDbEvidence`, `runtime.adapter`, `runtime.durable`, `runtime.blocker`, `readiness.durability.blockedLayer`, `requiredEvidence`, and redaction/safety flags | Printing DB URLs, passwords, tokens, kubeconfig material, or Secret data |
|
||||
| Live provisioning apply | No, unless explicitly authorized by commander/operator | `node scripts/dev-runtime-provisioning.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/report.json` with role/database created/existed booleans and no secret output | PROD apply, Secret value output, manual DB shell, schema migration, service restart, M3 acceptance promotion |
|
||||
| Live DB read verification | Only with explicit DEV read authorization | `--dry-run --allow-live-db-read --confirm-dev --report /tmp/hwlab-dev-gate/report.json` output with redacted endpoint and `secretValuesPrinted=false` | Kubernetes Secret reads, writes, migration apply, PROD target |
|
||||
| Live migration/repair apply | No, unless explicitly authorized by commander/operator | `--apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/report.json` plus post-apply health evidence | PROD apply, Secret value output, service restart, Code Agent POST, hardware write, or acceptance promotion |
|
||||
| Durable runtime postflight | Only after durable runtime readiness is already green | `node scripts/dev-runtime-postflight.mjs --live --confirm-dev --confirmed-non-production --target api --report /tmp/hwlab-dev-gate/report.json` with `/health/live`, `/v1`, and M3 true/false durable green evidence | Running M3 writes while runtime durable readiness is blocked, Secret output, PROD, or manual rollout |
|
||||
| Live provisioning apply | No, unless explicitly authorized by commander/operator | `node scripts/dev-runtime-provisioning.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/dev-runtime-provisioning-report.json` with role/database created/existed booleans and no secret output | PROD apply, Secret value output, manual DB shell, schema migration, service restart, M3 acceptance promotion |
|
||||
| Live DB read verification | Only with explicit DEV read authorization | `--dry-run --allow-live-db-read --confirm-dev --report /tmp/hwlab-dev-gate/dev-runtime-migration-report.json` output with redacted endpoint and `secretValuesPrinted=false` | Kubernetes Secret reads, writes, migration apply, PROD target |
|
||||
| Live migration/repair apply | No, unless explicitly authorized by commander/operator | `--apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/dev-runtime-migration-report.json` plus post-apply health evidence | PROD apply, Secret value output, service restart, Code Agent POST, hardware write, or acceptance promotion |
|
||||
| Durable runtime postflight | Only after durable runtime readiness is already green | `node scripts/dev-runtime-postflight.mjs --live --confirm-dev --confirmed-non-production --target api --report /tmp/hwlab-dev-gate/dev-runtime-postflight-report.json` with `/health/live`, `/v1`, and M3 true/false durable green evidence | Running M3 writes while runtime durable readiness is blocked, Secret output, PROD, or manual rollout |
|
||||
|
||||
Secret handling rules are the same for every row: verify env names,
|
||||
`secretKeyRef` names/keys, configured provider/model/base URL, redacted endpoint
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -2681,7 +2681,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",
|
||||
@@ -2698,7 +2698,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",
|
||||
|
||||
@@ -1287,8 +1287,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);
|
||||
@@ -1305,8 +1304,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: {},
|
||||
|
||||
@@ -215,6 +215,13 @@ function assertDurableRuntimeRunbook(value) {
|
||||
"Moved:",
|
||||
"Still blocked:",
|
||||
"No full M3, M4, or M5 acceptance is allowed while runtime durability is blocked",
|
||||
"node cmd/hwlab-cloud-api/provision.mjs --check",
|
||||
"node cmd/hwlab-cloud-api/provision.mjs --dry-run --allow-live-db-read --confirm-dev --report /tmp/hwlab-dev-gate/dev-runtime-provisioning-report.json",
|
||||
"node cmd/hwlab-cloud-api/provision.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/dev-runtime-provisioning-report.json",
|
||||
"node cmd/hwlab-cloud-api/migrate.mjs --check",
|
||||
"node cmd/hwlab-cloud-api/migrate.mjs --dry-run --report /tmp/hwlab-dev-gate/dev-runtime-migration-report.json",
|
||||
"node cmd/hwlab-cloud-api/migrate.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/dev-runtime-migration-report.json",
|
||||
"DEV CD runtime migration Job uses",
|
||||
"node scripts/dev-runtime-provisioning.mjs --check",
|
||||
"node scripts/dev-runtime-provisioning.mjs --dry-run --allow-live-db-read --confirm-dev --report /tmp/hwlab-dev-gate/dev-runtime-provisioning-report.json",
|
||||
"node scripts/dev-runtime-provisioning.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/dev-runtime-provisioning-report.json",
|
||||
@@ -229,6 +236,31 @@ function assertDurableRuntimeRunbook(value) {
|
||||
}
|
||||
}
|
||||
|
||||
function assertCloudApiImageRuntimeEntrypoints({ packageJson, ciJson, artifactPublisher, devCdApplySource }) {
|
||||
const label = "cloud-api image runtime maintenance entrypoints";
|
||||
for (const expected of [
|
||||
"COPY internal ./internal",
|
||||
"COPY cmd ./cmd",
|
||||
"COPY scripts ./scripts",
|
||||
"COPY package.json ./package.json"
|
||||
]) {
|
||||
assertIncludes(artifactPublisher, expected, `${label} artifact publisher`);
|
||||
}
|
||||
for (const expected of [
|
||||
"cmd/hwlab-cloud-api/provision.mjs",
|
||||
"cmd/hwlab-cloud-api/migrate.mjs"
|
||||
]) {
|
||||
assertIncludes(devCdApplySource, expected, `${label} DEV CD job command`);
|
||||
assertIncludes(packageJson.scripts?.check, `node --check ${expected}`, `${label} package check`);
|
||||
assertIncludes(packageJson.scripts?.check, `node ${expected} --check`, `${label} package runtime check`);
|
||||
}
|
||||
assert.equal(
|
||||
ciJson.commands?.some((command) => command.run === "npm run check"),
|
||||
true,
|
||||
`${label} CI must run npm run check`
|
||||
);
|
||||
}
|
||||
|
||||
function assertGuardFixture(guard) {
|
||||
assert.equal(guard.guard, "hwlab-runtime-boundary");
|
||||
assert.equal(guard.schemaVersion, "v1");
|
||||
@@ -406,6 +438,12 @@ assertProdDisabled(
|
||||
await readJSON(guard.requiredK3sSkeleton.prodDisabledPath)
|
||||
);
|
||||
assertDurableRuntimeRunbook(await readText("docs/reference/dev-runtime-boundary.md"));
|
||||
assertCloudApiImageRuntimeEntrypoints({
|
||||
packageJson: await readJSON("package.json"),
|
||||
ciJson: await readJSON("CI.json"),
|
||||
artifactPublisher: await readText("scripts/dev-artifact-publish.mjs"),
|
||||
devCdApplySource: await readText("scripts/src/dev-cd-apply.mjs")
|
||||
});
|
||||
|
||||
console.log(
|
||||
`validated HWLAB runtime boundary guard for ${SERVICE_IDS.length} services; forbidden substitutes: ${forbiddenRuntimeSubstitutes.join(", ")}`
|
||||
|
||||
Reference in New Issue
Block a user