refactor: remove legacy dev cd scripts
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
import { runCicdWorker } from "./cicd-jobs.mjs";
|
||||
|
||||
const jobId = process.argv[2];
|
||||
if (!jobId) {
|
||||
process.stderr.write(`${JSON.stringify({ ok: false, code: "missing-job-id", message: "usage: node tools/hwlab-cli/lib/cicd-job-worker.mjs <jobId>" }, null, 2)}\n`);
|
||||
process.exitCode = 1;
|
||||
} else {
|
||||
process.exitCode = await runCicdWorker({ repoRoot: process.cwd(), jobId });
|
||||
}
|
||||
@@ -1,398 +0,0 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { createWriteStream } from "node:fs";
|
||||
import { mkdir, open, readdir, readFile, stat, writeFile } from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
const stateRoot = ".state/hwlab-cicd/jobs";
|
||||
const knownKinds = new Set(["ci-publish", "dev-cd-apply"]);
|
||||
|
||||
function isoNow(now) {
|
||||
return (now ? now() : new Date()).toISOString();
|
||||
}
|
||||
|
||||
function compactTimestamp(value) {
|
||||
return value.replace(/[-:.]/gu, "").slice(0, 15);
|
||||
}
|
||||
|
||||
function makeJobId(kind, now) {
|
||||
return `${kind}-${compactTimestamp(isoNow(now))}-${randomBytes(3).toString("hex")}`;
|
||||
}
|
||||
|
||||
function jobDir(repoRoot, jobId) {
|
||||
return path.join(repoRoot, stateRoot, jobId);
|
||||
}
|
||||
|
||||
function jobPaths(repoRoot, jobId) {
|
||||
const dir = jobDir(repoRoot, jobId);
|
||||
return {
|
||||
dir,
|
||||
specPath: path.join(dir, "spec.json"),
|
||||
statePath: path.join(dir, "state.json"),
|
||||
stdoutPath: path.join(dir, "stdout.log"),
|
||||
stderrPath: path.join(dir, "stderr.log"),
|
||||
reportPath: path.join(dir, "report.json")
|
||||
};
|
||||
}
|
||||
|
||||
async function readJson(filePath) {
|
||||
return JSON.parse(await readFile(filePath, "utf8"));
|
||||
}
|
||||
|
||||
async function writeJsonFile(filePath, value) {
|
||||
await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
||||
}
|
||||
|
||||
function result(exitCode, payload, stream = "stdout") {
|
||||
return { exitCode, payload, stream };
|
||||
}
|
||||
|
||||
function error(code, message, details = {}) {
|
||||
return { ok: false, code, message, ...details };
|
||||
}
|
||||
|
||||
function followup(jobId) {
|
||||
return {
|
||||
status: `hwlab-cli cicd status ${jobId}`,
|
||||
logs: `hwlab-cli cicd logs ${jobId}`,
|
||||
report: `hwlab-cli cicd report ${jobId}`
|
||||
};
|
||||
}
|
||||
|
||||
function optionValue(options, name, fallback = null) {
|
||||
return options.get(name) ?? fallback;
|
||||
}
|
||||
|
||||
function positiveInteger(value, fallback) {
|
||||
if (value == null) return fallback;
|
||||
const parsed = Number.parseInt(value, 10);
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
||||
}
|
||||
|
||||
function buildJobSpec({ kind, flags, options, repoRoot, env, now }) {
|
||||
if (!knownKinds.has(kind)) {
|
||||
return { error: error("unsupported-cicd-kind", "Use --kind ci-publish or --kind dev-cd-apply.", { kind }) };
|
||||
}
|
||||
|
||||
const jobId = makeJobId(kind, now);
|
||||
const paths = jobPaths(repoRoot, jobId);
|
||||
const envPatch = {};
|
||||
const args = [];
|
||||
|
||||
if (kind === "ci-publish") {
|
||||
args.push("scripts/dev-artifact-publish.mjs", "--publish", "--report", paths.reportPath, "--quiet-build");
|
||||
const services = optionValue(options, "--services");
|
||||
if (services) args.push("--services", services);
|
||||
const concurrency = positiveInteger(optionValue(options, "--concurrency"), 4);
|
||||
args.push("--concurrency", String(concurrency));
|
||||
envPatch.HWLAB_CI_ARTIFACT_RUN_ID = optionValue(options, "--owner-task-id", jobId);
|
||||
envPatch.HWLAB_CI_ARTIFACT_RUN_OWNER = optionValue(options, "--owner", "hwlab-cli");
|
||||
} else {
|
||||
if (!flags.has("--confirm-dev") || !flags.has("--confirmed-non-production")) {
|
||||
return {
|
||||
error: error("confirmation-required", "DEV CD apply requires --confirm-dev --confirmed-non-production.", {
|
||||
requiredFlags: ["--confirm-dev", "--confirmed-non-production"]
|
||||
})
|
||||
};
|
||||
}
|
||||
args.push(
|
||||
"scripts/dev-cd-apply.mjs",
|
||||
"--apply",
|
||||
"--confirm-dev",
|
||||
"--confirmed-non-production",
|
||||
"--owner-task-id",
|
||||
optionValue(options, "--owner-task-id", jobId),
|
||||
"--report",
|
||||
paths.reportPath
|
||||
);
|
||||
if (flags.has("--break-stale-lock")) args.push("--break-stale-lock");
|
||||
const concurrency = optionValue(options, "--concurrency");
|
||||
if (concurrency) envPatch.HWLAB_DEV_CD_CONCURRENCY = String(positiveInteger(concurrency, 4));
|
||||
const registryManifestBaseUrl = optionValue(options, "--registry-manifest-base-url");
|
||||
if (registryManifestBaseUrl) args.push("--registry-manifest-base-url", registryManifestBaseUrl);
|
||||
}
|
||||
|
||||
if (optionValue(options, "--target-ref")) args.push("--target-ref", optionValue(options, "--target-ref"));
|
||||
if (optionValue(options, "--kubeconfig")) args.push("--kubeconfig", optionValue(options, "--kubeconfig"));
|
||||
|
||||
return {
|
||||
spec: {
|
||||
jobId,
|
||||
kind,
|
||||
cwd: repoRoot,
|
||||
node: process.execPath,
|
||||
command: process.execPath,
|
||||
args,
|
||||
envPatch,
|
||||
createdAt: isoNow(now),
|
||||
host: os.hostname(),
|
||||
paths
|
||||
},
|
||||
paths,
|
||||
env
|
||||
};
|
||||
}
|
||||
|
||||
async function defaultSpawnDetached({ repoRoot, jobId, env }) {
|
||||
const workerPath = path.join(repoRoot, "tools/hwlab-cli/lib/cicd-job-worker.mjs");
|
||||
const child = spawn(process.execPath, [workerPath, jobId], {
|
||||
cwd: repoRoot,
|
||||
env,
|
||||
detached: true,
|
||||
stdio: "ignore"
|
||||
});
|
||||
child.unref();
|
||||
return { pid: child.pid };
|
||||
}
|
||||
|
||||
async function submitJob(ctx) {
|
||||
const kind = optionValue(ctx.options, "--kind");
|
||||
const built = buildJobSpec({ ...ctx, kind });
|
||||
if (built.error) return result(1, built.error, "stderr");
|
||||
|
||||
await mkdir(built.paths.dir, { recursive: true });
|
||||
await writeJsonFile(built.paths.specPath, built.spec);
|
||||
const initialState = {
|
||||
ok: true,
|
||||
jobId: built.spec.jobId,
|
||||
kind: built.spec.kind,
|
||||
status: "submitted",
|
||||
nonBlocking: true,
|
||||
createdAt: built.spec.createdAt,
|
||||
updatedAt: built.spec.createdAt,
|
||||
cwd: built.spec.cwd,
|
||||
command: [path.basename(built.spec.command), ...built.spec.args].join(" "),
|
||||
statePath: built.paths.statePath,
|
||||
stdoutPath: built.paths.stdoutPath,
|
||||
stderrPath: built.paths.stderrPath,
|
||||
reportPath: built.paths.reportPath
|
||||
};
|
||||
await writeJsonFile(built.paths.statePath, initialState);
|
||||
|
||||
const launcher = ctx.spawnDetached ?? defaultSpawnDetached;
|
||||
const launched = await launcher({ repoRoot: ctx.repoRoot, jobId: built.spec.jobId, env: ctx.env, spec: built.spec });
|
||||
const submitted = {
|
||||
...initialState,
|
||||
pid: launched?.pid ?? null,
|
||||
followup: followup(built.spec.jobId)
|
||||
};
|
||||
await writeJsonFile(built.paths.statePath, submitted);
|
||||
return result(0, submitted);
|
||||
}
|
||||
|
||||
async function appendLog(filePath, line) {
|
||||
await writeFile(filePath, `${line}\n`, { flag: "a" });
|
||||
}
|
||||
|
||||
function reportSummary(report) {
|
||||
if (report?.artifactPublish) {
|
||||
return {
|
||||
kind: "ci-publish",
|
||||
status: report.artifactPublish.status ?? report.status ?? null,
|
||||
sourceCommitId: report.sourceCommitId ?? report.commitId ?? null,
|
||||
serviceCount: report.artifactPublish.serviceCount ?? null,
|
||||
requiredServiceCount: report.artifactPublish.requiredServiceCount ?? null,
|
||||
publishedCount: report.artifactPublish.publishedCount ?? null,
|
||||
timings: report.artifactPublish.timings ?? null,
|
||||
blockers: report.blockers ?? []
|
||||
};
|
||||
}
|
||||
if (report?.devCdApply) {
|
||||
return {
|
||||
kind: "dev-cd-apply",
|
||||
status: report.status ?? null,
|
||||
commitId: report.commitId ?? null,
|
||||
target: report.target ?? null,
|
||||
transaction: report.transaction
|
||||
? {
|
||||
transactionId: report.transaction.transactionId ?? null,
|
||||
ownerTaskId: report.transaction.ownerTaskId ?? null,
|
||||
phases: report.transaction.phases ?? [],
|
||||
release: report.transaction.release ?? null
|
||||
}
|
||||
: null,
|
||||
steps: report.devCdApply.steps?.map((step) => ({ id: step.id, status: step.status, reportPath: step.reportPath })) ?? [],
|
||||
blockers: report.blockers ?? []
|
||||
};
|
||||
}
|
||||
return {
|
||||
kind: "generic",
|
||||
status: report?.status ?? report?.ok ?? null,
|
||||
keys: report && typeof report === "object" ? Object.keys(report).slice(0, 20) : []
|
||||
};
|
||||
}
|
||||
|
||||
async function runJob(ctx) {
|
||||
const jobId = ctx.rest[0];
|
||||
if (!jobId) return result(1, error("missing-job-id", "usage: hwlab-cli cicd run-job <jobId>"), "stderr");
|
||||
const paths = jobPaths(ctx.repoRoot, jobId);
|
||||
const spec = await readJson(paths.specPath);
|
||||
const startedAt = isoNow(ctx.now);
|
||||
await writeJsonFile(paths.statePath, {
|
||||
ok: true,
|
||||
jobId,
|
||||
kind: spec.kind,
|
||||
status: "running",
|
||||
nonBlocking: true,
|
||||
createdAt: spec.createdAt,
|
||||
startedAt,
|
||||
updatedAt: startedAt,
|
||||
cwd: spec.cwd,
|
||||
pid: process.pid,
|
||||
command: [path.basename(spec.command), ...spec.args].join(" "),
|
||||
statePath: paths.statePath,
|
||||
stdoutPath: paths.stdoutPath,
|
||||
stderrPath: paths.stderrPath,
|
||||
reportPath: paths.reportPath
|
||||
});
|
||||
await appendLog(paths.stdoutPath, `[hwlab-cli] job ${jobId} started at ${startedAt}`);
|
||||
|
||||
const child = spawn(spec.command, spec.args, {
|
||||
cwd: spec.cwd,
|
||||
env: { ...process.env, ...spec.envPatch },
|
||||
stdio: ["ignore", "pipe", "pipe"]
|
||||
});
|
||||
const stdoutStream = createWriteStream(paths.stdoutPath, { flags: "a" });
|
||||
const stderrStream = createWriteStream(paths.stderrPath, { flags: "a" });
|
||||
child.stdout.pipe(stdoutStream);
|
||||
child.stderr.pipe(stderrStream);
|
||||
const exitCode = await new Promise((resolve) => {
|
||||
child.on("close", resolve);
|
||||
});
|
||||
stdoutStream.end();
|
||||
stderrStream.end();
|
||||
|
||||
const finishedAt = isoNow(ctx.now);
|
||||
const state = await readJson(paths.statePath);
|
||||
let summary = null;
|
||||
try {
|
||||
summary = reportSummary(await readJson(paths.reportPath));
|
||||
} catch {
|
||||
summary = null;
|
||||
}
|
||||
const finalState = {
|
||||
...state,
|
||||
status: exitCode === 0 ? "succeeded" : "failed",
|
||||
ok: exitCode === 0,
|
||||
exitCode,
|
||||
updatedAt: finishedAt,
|
||||
finishedAt,
|
||||
durationMs: Date.parse(finishedAt) - Date.parse(startedAt),
|
||||
reportSummary: summary,
|
||||
followup: followup(jobId)
|
||||
};
|
||||
await writeJsonFile(paths.statePath, finalState);
|
||||
return result(exitCode === 0 ? 0 : 2, finalState, exitCode === 0 ? "stdout" : "stderr");
|
||||
}
|
||||
|
||||
async function readState(repoRoot, jobId) {
|
||||
const paths = jobPaths(repoRoot, jobId);
|
||||
const state = await readJson(paths.statePath);
|
||||
return { paths, state };
|
||||
}
|
||||
|
||||
async function statusJob(ctx) {
|
||||
const jobId = ctx.rest[0];
|
||||
if (!jobId) return result(1, error("missing-job-id", "usage: hwlab-cli cicd status <jobId>"), "stderr");
|
||||
try {
|
||||
const { state } = await readState(ctx.repoRoot, jobId);
|
||||
return result(0, { ...state, followup: followup(jobId) });
|
||||
} catch (err) {
|
||||
return result(2, error("job-not-found", `No CI/CD job state found for ${jobId}.`, { detail: err.message }), "stderr");
|
||||
}
|
||||
}
|
||||
|
||||
async function tailFile(filePath, bytes) {
|
||||
try {
|
||||
const info = await stat(filePath);
|
||||
const length = Math.min(info.size, bytes);
|
||||
const handle = await open(filePath, "r");
|
||||
try {
|
||||
const buffer = Buffer.alloc(length);
|
||||
await handle.read(buffer, 0, length, info.size - length);
|
||||
return { path: filePath, totalBytes: info.size, returnedBytes: length, truncated: info.size > length, text: buffer.toString("utf8") };
|
||||
} finally {
|
||||
await handle.close();
|
||||
}
|
||||
} catch {
|
||||
return { path: filePath, totalBytes: 0, returnedBytes: 0, truncated: false, text: "" };
|
||||
}
|
||||
}
|
||||
|
||||
async function logsJob(ctx) {
|
||||
const jobId = ctx.rest[0];
|
||||
if (!jobId) return result(1, error("missing-job-id", "usage: hwlab-cli cicd logs <jobId>"), "stderr");
|
||||
const bytes = Math.min(positiveInteger(optionValue(ctx.options, "--tail-bytes"), 8000), 20000);
|
||||
try {
|
||||
const { paths, state } = await readState(ctx.repoRoot, jobId);
|
||||
return result(0, {
|
||||
ok: true,
|
||||
jobId,
|
||||
status: state.status,
|
||||
tailBytes: bytes,
|
||||
stdout: await tailFile(paths.stdoutPath, bytes),
|
||||
stderr: await tailFile(paths.stderrPath, bytes),
|
||||
followup: followup(jobId)
|
||||
});
|
||||
} catch (err) {
|
||||
return result(2, error("job-not-found", `No CI/CD job state found for ${jobId}.`, { detail: err.message }), "stderr");
|
||||
}
|
||||
}
|
||||
|
||||
async function reportJob(ctx) {
|
||||
const jobId = ctx.rest[0];
|
||||
if (!jobId) return result(1, error("missing-job-id", "usage: hwlab-cli cicd report <jobId>"), "stderr");
|
||||
try {
|
||||
const { paths, state } = await readState(ctx.repoRoot, jobId);
|
||||
const report = await readJson(paths.reportPath);
|
||||
return result(0, {
|
||||
ok: true,
|
||||
jobId,
|
||||
status: state.status,
|
||||
reportPath: paths.reportPath,
|
||||
summary: reportSummary(report),
|
||||
report: ctx.flags.has("--full") ? report : undefined,
|
||||
followup: followup(jobId)
|
||||
});
|
||||
} catch (err) {
|
||||
return result(2, error("report-not-ready", `No report is available for ${jobId}.`, { detail: err.message }), "stderr");
|
||||
}
|
||||
}
|
||||
|
||||
async function listJobs(ctx) {
|
||||
const root = path.join(ctx.repoRoot, stateRoot);
|
||||
const limit = Math.min(positiveInteger(optionValue(ctx.options, "--limit"), 20), 100);
|
||||
try {
|
||||
const names = (await readdir(root)).sort().reverse().slice(0, limit);
|
||||
const jobs = [];
|
||||
for (const name of names) {
|
||||
try {
|
||||
jobs.push((await readState(ctx.repoRoot, name)).state);
|
||||
} catch {
|
||||
// Ignore incomplete job directories; logs/report still remain on disk.
|
||||
}
|
||||
}
|
||||
return result(0, { ok: true, command: "hwlab-cli cicd jobs", count: jobs.length, jobs });
|
||||
} catch {
|
||||
return result(0, { ok: true, command: "hwlab-cli cicd jobs", count: 0, jobs: [] });
|
||||
}
|
||||
}
|
||||
|
||||
export async function runCicdCommand(ctx) {
|
||||
if (ctx.subcommand === "submit") return submitJob(ctx);
|
||||
if (ctx.subcommand === "status") return statusJob(ctx);
|
||||
if (ctx.subcommand === "logs") return logsJob(ctx);
|
||||
if (ctx.subcommand === "report") return reportJob(ctx);
|
||||
if (ctx.subcommand === "jobs") return listJobs(ctx);
|
||||
return result(1, error("usage", "usage: hwlab-cli cicd submit --kind <ci-publish|dev-cd-apply> | status|logs|report <jobId> | jobs"), "stderr");
|
||||
}
|
||||
|
||||
export async function runCicdWorker({ repoRoot = process.cwd(), jobId, now }) {
|
||||
const outcome = await runJob({
|
||||
repoRoot,
|
||||
rest: [jobId],
|
||||
now
|
||||
});
|
||||
return outcome.exitCode;
|
||||
}
|
||||
+10
-19
@@ -1,6 +1,5 @@
|
||||
import { DEV_ENDPOINT, loadMvpGateSummary } from "../../../internal/mvp-gate/summary.mjs";
|
||||
import { runM3IoSkillCommand } from "../../../skills/hwlab-agent-runtime/scripts/src/m3-io-skill-client.mjs";
|
||||
import { runCicdCommand } from "./cicd-jobs.mjs";
|
||||
|
||||
function formatJson(value) {
|
||||
return JSON.stringify(value, null, 2);
|
||||
@@ -174,12 +173,7 @@ function writeHelp(stdout) {
|
||||
"m3 status --api-base-url URL",
|
||||
"m3 io --action do.write --value true --approved --api-base-url URL",
|
||||
"m3 io --action di.read --api-base-url URL",
|
||||
"cicd submit --kind ci-publish --concurrency 4",
|
||||
"cicd submit --kind dev-cd-apply --confirm-dev --confirmed-non-production --concurrency 4",
|
||||
"cicd status JOB_ID",
|
||||
"cicd logs JOB_ID --tail-bytes 8000",
|
||||
"cicd report JOB_ID",
|
||||
"cicd jobs --limit 20",
|
||||
"G14 CI/CD is managed by native Tekton + GitOps; hwlab-cli cicd is removed",
|
||||
"test e2e --env dev --mvp",
|
||||
"test e2e --env dev --mvp --dry-run",
|
||||
"test e2e --env dev --mvp --live --confirm-dev --confirmed-non-production"
|
||||
@@ -202,18 +196,15 @@ export async function runCli(argv, io) {
|
||||
const { flags, options } = parseArgs(rest);
|
||||
|
||||
if (command === "cicd") {
|
||||
const outcome = await runCicdCommand({
|
||||
subcommand,
|
||||
rest,
|
||||
flags,
|
||||
options,
|
||||
repoRoot,
|
||||
env,
|
||||
now: io.now,
|
||||
spawnDetached: io.spawnDetached
|
||||
});
|
||||
writeJson(outcome.stream === "stderr" ? stderr : stdout, outcome.payload);
|
||||
return outcome.exitCode;
|
||||
writeJson(stderr, makeError("legacy-cicd-removed", "hwlab-cli cicd was removed from the active G14 release path. Use G14 k3s Tekton/GitOps via tran G14:k3s and scripts/g14-gitops-render.mjs.", {
|
||||
subcommand: subcommand ?? null,
|
||||
replacement: {
|
||||
observePipelineRuns: "tran G14:k3s kubectl get pipelineruns -n hwlab-ci",
|
||||
observeArgo: "tran G14:k3s kubectl -n argocd get application hwlab-g14-dev hwlab-g14-prod",
|
||||
renderSource: "node scripts/g14-gitops-render.mjs --check"
|
||||
}
|
||||
}));
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (command === "m3" && ["io", "status"].includes(subcommand)) {
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
@@ -34,80 +31,14 @@ async function captureCli(args, options = {}) {
|
||||
return { exitCode, stdout, stderr };
|
||||
}
|
||||
|
||||
test("repo hwlab-cli cicd submit is non-blocking and records follow-up commands", async () => {
|
||||
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-cicd-"));
|
||||
const launched = [];
|
||||
const result = await captureCli(
|
||||
["cicd", "submit", "--kind", "ci-publish", "--services", "hwlab-cloud-web", "--concurrency", "2"],
|
||||
{
|
||||
cwd,
|
||||
spawnDetached: async (job) => {
|
||||
launched.push(job);
|
||||
return { pid: 123456 };
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
assert.equal(result.exitCode, 0, result.stderr);
|
||||
const body = JSON.parse(result.stdout);
|
||||
assert.equal(body.ok, true);
|
||||
assert.equal(body.nonBlocking, true);
|
||||
assert.equal(body.kind, "ci-publish");
|
||||
assert.match(body.jobId, /^ci-publish-/u);
|
||||
assert.equal(body.pid, 123456);
|
||||
assert.equal(body.followup.status, `hwlab-cli cicd status ${body.jobId}`);
|
||||
assert.equal(launched.length, 1);
|
||||
|
||||
const spec = JSON.parse(await readFile(path.join(cwd, ".state", "hwlab-cicd", "jobs", body.jobId, "spec.json"), "utf8"));
|
||||
assert.deepEqual(spec.args.slice(0, 4), ["scripts/dev-artifact-publish.mjs", "--publish", "--report", body.reportPath]);
|
||||
assert.ok(spec.args.includes("--quiet-build"));
|
||||
assert.equal(spec.envPatch.HWLAB_CI_ARTIFACT_RUN_ID, body.jobId);
|
||||
});
|
||||
|
||||
test("repo hwlab-cli cicd status/logs/report disclose bounded job state", async () => {
|
||||
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-cicd-"));
|
||||
const submit = await captureCli(["cicd", "submit", "--kind", "ci-publish"], {
|
||||
cwd,
|
||||
spawnDetached: async () => ({ pid: 123457 })
|
||||
});
|
||||
const submitted = JSON.parse(submit.stdout);
|
||||
const jobDir = path.join(cwd, ".state", "hwlab-cicd", "jobs", submitted.jobId);
|
||||
await writeFile(path.join(jobDir, "stdout.log"), "build line 1\nbuild line 2\n", "utf8");
|
||||
await writeFile(path.join(jobDir, "stderr.log"), "warning line\n", "utf8");
|
||||
await writeFile(
|
||||
path.join(jobDir, "report.json"),
|
||||
`${JSON.stringify({
|
||||
sourceCommitId: "abc123",
|
||||
artifactPublish: {
|
||||
status: "published",
|
||||
serviceCount: 1,
|
||||
requiredServiceCount: 1,
|
||||
publishedCount: 1,
|
||||
timings: { durationMs: 42 }
|
||||
},
|
||||
blockers: []
|
||||
})}\n`,
|
||||
"utf8"
|
||||
);
|
||||
|
||||
const status = await captureCli(["cicd", "status", submitted.jobId], { cwd });
|
||||
assert.equal(status.exitCode, 0, status.stderr);
|
||||
assert.equal(JSON.parse(status.stdout).status, "submitted");
|
||||
|
||||
const logs = await captureCli(["cicd", "logs", submitted.jobId, "--tail-bytes", "12"], { cwd });
|
||||
assert.equal(logs.exitCode, 0, logs.stderr);
|
||||
const logBody = JSON.parse(logs.stdout);
|
||||
assert.equal(logBody.stdout.returnedBytes, 12);
|
||||
assert.equal(logBody.stdout.truncated, true);
|
||||
assert.ok(logBody.stdout.text.includes("line 2"));
|
||||
|
||||
const report = await captureCli(["cicd", "report", submitted.jobId], { cwd });
|
||||
assert.equal(report.exitCode, 0, report.stderr);
|
||||
const reportBody = JSON.parse(report.stdout);
|
||||
assert.equal(reportBody.summary.kind, "ci-publish");
|
||||
assert.equal(reportBody.summary.status, "published");
|
||||
assert.equal(reportBody.summary.publishedCount, 1);
|
||||
assert.equal(Object.hasOwn(reportBody, "report"), false);
|
||||
test("repo hwlab-cli cicd is removed from the active G14 release path", async () => {
|
||||
const result = await captureCli(["cicd", "submit", "--kind", "ci-publish"]);
|
||||
assert.equal(result.exitCode, 2);
|
||||
const body = JSON.parse(result.stderr);
|
||||
assert.equal(body.ok, false);
|
||||
assert.equal(body.code, "legacy-cicd-removed");
|
||||
assert.match(body.message, /G14 release path/u);
|
||||
assert.match(body.replacement.observePipelineRuns, /tran G14:k3s/u);
|
||||
});
|
||||
|
||||
test("repo hwlab-cli m3 status uses Skill CLI and calls only HWLAB API /v1/m3/status", async () => {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"hwlab-cli": "./bin/hwlab-cli.mjs"
|
||||
},
|
||||
"scripts": {
|
||||
"check": "node --check bin/hwlab-cli.mjs && node --check lib/cli.mjs && node --check lib/cicd-jobs.mjs && node --check lib/cicd-job-worker.mjs && node --test lib/cli.test.mjs && node ../../scripts/l6-cli-web-smoke.mjs",
|
||||
"check": "node --check bin/hwlab-cli.mjs && node --check lib/cli.mjs && node --test lib/cli.test.mjs && node ../../scripts/l6-cli-web-smoke.mjs",
|
||||
"health": "node bin/hwlab-cli.mjs health"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user