Sync public tree to b67d2a6
This commit is contained in:
parent
21f21fa8b6
commit
1d72f20cce
7 changed files with 466 additions and 6 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"kind": "disasmer-filtered-public-tree",
|
"kind": "disasmer-filtered-public-tree",
|
||||||
"source_commit": "0bdcbac7736ebc2237e80aaa837d7b0d6286252e",
|
"source_commit": "b67d2a61d1b2a7600593024b4943e155443eb1d7",
|
||||||
"release_name": "dryrun-0bdcbac7736e",
|
"release_name": "dryrun-b67d2a61d1b2",
|
||||||
"filtered_out": [
|
"filtered_out": [
|
||||||
"private/**",
|
"private/**",
|
||||||
"experiments/**",
|
"experiments/**",
|
||||||
|
|
|
||||||
|
|
@ -5722,9 +5722,40 @@ fn coordinator_run_report(plan: RunPlan) -> Result<Value> {
|
||||||
.get("status")
|
.get("status")
|
||||||
.and_then(Value::as_str)
|
.and_then(Value::as_str)
|
||||||
.unwrap_or("coordinator_response");
|
.unwrap_or("coordinator_response");
|
||||||
|
let task = plan.entry.clone();
|
||||||
|
let artifact_path = format!("/vfs/artifacts/{task}-output.txt");
|
||||||
|
let launch_task_response = if status == "started" {
|
||||||
|
let manifest_path = plan.project.join("Cargo.toml");
|
||||||
|
session.request_allow_error(json!({
|
||||||
|
"type": "launch_task",
|
||||||
|
"tenant": tenant.clone(),
|
||||||
|
"project": project.clone(),
|
||||||
|
"actor_user": user.clone(),
|
||||||
|
"process": process.clone(),
|
||||||
|
"task": task.clone(),
|
||||||
|
"environment": null,
|
||||||
|
"environment_digest": null,
|
||||||
|
"required_capabilities": ["Command"],
|
||||||
|
"dependency_cache": null,
|
||||||
|
"source_snapshot": null,
|
||||||
|
"required_artifacts": [],
|
||||||
|
"quota_available": true,
|
||||||
|
"policy_allowed": true,
|
||||||
|
"command": "cargo",
|
||||||
|
"command_args": [
|
||||||
|
"test",
|
||||||
|
"--quiet",
|
||||||
|
"--manifest-path",
|
||||||
|
manifest_path.to_string_lossy()
|
||||||
|
],
|
||||||
|
"artifact_path": artifact_path.clone(),
|
||||||
|
}))?
|
||||||
|
} else {
|
||||||
|
Value::Null
|
||||||
|
};
|
||||||
Ok(json!({
|
Ok(json!({
|
||||||
"command": "run",
|
"command": "run",
|
||||||
"status": status,
|
"status": if launch_task_response.get("type").and_then(Value::as_str) == Some("task_launched") { "task_launched" } else { status },
|
||||||
"project_root": plan.project,
|
"project_root": plan.project,
|
||||||
"entry": plan.entry,
|
"entry": plan.entry,
|
||||||
"tenant": tenant,
|
"tenant": tenant,
|
||||||
|
|
@ -5734,6 +5765,9 @@ fn coordinator_run_report(plan: RunPlan) -> Result<Value> {
|
||||||
"coordinator": coordinator,
|
"coordinator": coordinator,
|
||||||
"process": process,
|
"process": process,
|
||||||
"run_start": run_start,
|
"run_start": run_start,
|
||||||
|
"task": task,
|
||||||
|
"worker_placement_requested": launch_task_response.is_object(),
|
||||||
|
"task_launch": launch_task_response,
|
||||||
"coordinator_response": response,
|
"coordinator_response": response,
|
||||||
"coordinator_session_requests": session.requests(),
|
"coordinator_session_requests": session.requests(),
|
||||||
"private_website_required": false,
|
"private_website_required": false,
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,9 @@ fi
|
||||||
node scripts/public-release-dryrun-contract-smoke.js
|
node scripts/public-release-dryrun-contract-smoke.js
|
||||||
node scripts/public-browser-login-contract-smoke.js
|
node scripts/public-browser-login-contract-smoke.js
|
||||||
node scripts/self-hosted-coordinator-smoke.js
|
node scripts/self-hosted-coordinator-smoke.js
|
||||||
|
if [[ "${DISASMER_CLI_HAPPY_PATH_LIVE:-}" == "1" ]]; then
|
||||||
|
node scripts/cli-happy-path-live-smoke.js
|
||||||
|
fi
|
||||||
node scripts/public-local-demo-matrix-smoke.js
|
node scripts/public-local-demo-matrix-smoke.js
|
||||||
scripts/release-source-scan.sh
|
scripts/release-source-scan.sh
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ const errorExitSmoke = read("scripts/cli-error-exit-smoke.js");
|
||||||
const browserLoginFlowSmoke = read("scripts/cli-browser-login-flow-smoke.js");
|
const browserLoginFlowSmoke = read("scripts/cli-browser-login-flow-smoke.js");
|
||||||
const nodeAttachSmoke = read("scripts/node-attach-smoke.js");
|
const nodeAttachSmoke = read("scripts/node-attach-smoke.js");
|
||||||
const dapSmoke = read("scripts/dap-smoke.js");
|
const dapSmoke = read("scripts/dap-smoke.js");
|
||||||
|
const cliHappyPathSmoke = read("scripts/cli-happy-path-live-smoke.js");
|
||||||
|
|
||||||
expect(criteria, "header", /^# Disasmer CLI-First MVP Acceptance Criteria/m);
|
expect(criteria, "header", /^# Disasmer CLI-First MVP Acceptance Criteria/m);
|
||||||
expect(
|
expect(
|
||||||
|
|
@ -123,8 +124,31 @@ assert.deepStrictEqual(
|
||||||
expect(
|
expect(
|
||||||
criteria,
|
criteria,
|
||||||
"CLI-first non-e2e gate wording",
|
"CLI-first non-e2e gate wording",
|
||||||
/scripts\/acceptance-cli-first\.sh[\s\S]*final public-release e2e remains intentionally excluded/
|
/scripts\/acceptance-cli-first\.sh[\s\S]*scripts\/cli-happy-path-live-smoke\.js[\s\S]*live default operator/
|
||||||
);
|
);
|
||||||
|
expect(
|
||||||
|
cliFirstAcceptance,
|
||||||
|
"CLI-first gate can run live happy path when explicitly enabled",
|
||||||
|
/DISASMER_CLI_HAPPY_PATH_LIVE[\s\S]*cli-happy-path-live-smoke\.js/
|
||||||
|
);
|
||||||
|
for (const [name, pattern] of [
|
||||||
|
["happy path uses released binaries", /disasmer-public-binaries-/],
|
||||||
|
["happy path completes browser login", /--complete-browser-code/],
|
||||||
|
["happy path initializes project", /"project"[\s\S]*"init"/],
|
||||||
|
["happy path inspects project", /"inspect"[\s\S]*"--project"/],
|
||||||
|
["happy path enrolls node", /"node"[\s\S]*"enroll"/],
|
||||||
|
["happy path attaches node", /"node"[\s\S]*"attach"/],
|
||||||
|
["happy path starts worker", /--worker/],
|
||||||
|
["happy path runs build", /"run"[\s\S]*"build"/],
|
||||||
|
["happy path checks logs", /"logs"/],
|
||||||
|
["happy path checks artifacts", /"artifact"[\s\S]*"list"/],
|
||||||
|
["happy path downloads artifact", /"artifact"[\s\S]*"download"/],
|
||||||
|
["happy path restarts process", /"process"[\s\S]*"restart"/],
|
||||||
|
["happy path cancels process", /"process"[\s\S]*"cancel"/],
|
||||||
|
["happy path writes evidence", /cli-happy-path-live\.json/],
|
||||||
|
]) {
|
||||||
|
expect(cliHappyPathSmoke, name, pattern);
|
||||||
|
}
|
||||||
expect(
|
expect(
|
||||||
criteria,
|
criteria,
|
||||||
"artifact export explicit local byte write",
|
"artifact export explicit local byte write",
|
||||||
|
|
|
||||||
397
scripts/cli-happy-path-live-smoke.js
Normal file
397
scripts/cli-happy-path-live-smoke.js
Normal file
|
|
@ -0,0 +1,397 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const assert = require("assert");
|
||||||
|
const cp = require("child_process");
|
||||||
|
const fs = require("fs");
|
||||||
|
const os = require("os");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const repo = path.resolve(__dirname, "..");
|
||||||
|
const releaseRoot = path.resolve(
|
||||||
|
process.env.DISASMER_PUBLIC_RELEASE_DIR ||
|
||||||
|
path.join(repo, "target/public-release-dryrun")
|
||||||
|
);
|
||||||
|
const acceptanceRoot = path.join(repo, "target/acceptance");
|
||||||
|
const manifestPath = path.join(releaseRoot, "public-release-manifest.json");
|
||||||
|
const reportPath = path.join(acceptanceRoot, "cli-happy-path-live.json");
|
||||||
|
const serviceEndpoint = "https://disasmer.michelpaulissen.com:9443";
|
||||||
|
const serviceAddr =
|
||||||
|
process.env.DISASMER_PUBLIC_RELEASE_DRYRUN_SERVICE_ADDR ||
|
||||||
|
"disasmer.michelpaulissen.com:9443";
|
||||||
|
const oidcIssuer = process.env.DISASMER_PUBLIC_RELEASE_DRYRUN_OIDC_ISSUER_URL;
|
||||||
|
const oidcCode = process.env.DISASMER_PUBLIC_RELEASE_DRYRUN_OIDC_CODE;
|
||||||
|
const enabled = process.env.DISASMER_CLI_HAPPY_PATH_LIVE === "1";
|
||||||
|
const commands = [];
|
||||||
|
|
||||||
|
function requireEnabled() {
|
||||||
|
if (!enabled) {
|
||||||
|
throw new Error(
|
||||||
|
"DISASMER_CLI_HAPPY_PATH_LIVE=1 is required because this runs the live CLI happy path"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!oidcIssuer || !oidcCode) {
|
||||||
|
throw new Error(
|
||||||
|
"DISASMER_PUBLIC_RELEASE_DRYRUN_OIDC_ISSUER_URL and DISASMER_PUBLIC_RELEASE_DRYRUN_OIDC_CODE are required"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readJson(file) {
|
||||||
|
return JSON.parse(fs.readFileSync(file, "utf8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureDir(dir) {
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
function nonInteractiveGitEnv(extra = {}) {
|
||||||
|
return {
|
||||||
|
...process.env,
|
||||||
|
GIT_TERMINAL_PROMPT: "0",
|
||||||
|
GIT_ASKPASS: process.env.GIT_ASKPASS || "/bin/false",
|
||||||
|
SSH_ASKPASS: process.env.SSH_ASKPASS || "/bin/false",
|
||||||
|
GIT_SSH_COMMAND:
|
||||||
|
process.env.GIT_SSH_COMMAND ||
|
||||||
|
"ssh -o BatchMode=yes -o NumberOfPasswordPrompts=0",
|
||||||
|
...extra,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function commandEnv(command, env) {
|
||||||
|
if (command !== "git") return env;
|
||||||
|
return nonInteractiveGitEnv(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(command, args, options = {}) {
|
||||||
|
commands.push([command, ...args].join(" "));
|
||||||
|
return cp.execFileSync(command, args, {
|
||||||
|
cwd: repo,
|
||||||
|
encoding: "utf8",
|
||||||
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
|
timeout: 15 * 60 * 1000,
|
||||||
|
...options,
|
||||||
|
env: commandEnv(command, options.env),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function runJson(command, args, options = {}) {
|
||||||
|
const output = run(command, args, options);
|
||||||
|
const line = output
|
||||||
|
.trim()
|
||||||
|
.split(/\r?\n/)
|
||||||
|
.filter(Boolean)
|
||||||
|
.at(-1);
|
||||||
|
return JSON.parse(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
function executable(root, name) {
|
||||||
|
return path.join(root, "bin", process.platform === "win32" ? `${name}.exe` : name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function binaryArchive(manifest) {
|
||||||
|
const platform = `${os.platform()}-${os.arch()}`;
|
||||||
|
const asset = manifest.assets.find(
|
||||||
|
(candidate) =>
|
||||||
|
candidate.name.startsWith("disasmer-public-binaries-") &&
|
||||||
|
candidate.name.includes(platform)
|
||||||
|
);
|
||||||
|
assert(asset, `missing released binary archive for ${platform}`);
|
||||||
|
assert(fs.existsSync(asset.file), `missing released binary archive ${asset.file}`);
|
||||||
|
return asset.file;
|
||||||
|
}
|
||||||
|
|
||||||
|
function publicRepoUrl(manifest) {
|
||||||
|
const url =
|
||||||
|
process.env.DISASMER_PUBLIC_REPO_URL ||
|
||||||
|
process.env.DISASMER_PUBLIC_REPO_REMOTE ||
|
||||||
|
manifest.public_repo_url ||
|
||||||
|
manifest.public_repo_remote;
|
||||||
|
assert(url, "public repository URL is required");
|
||||||
|
assert(url.includes("git.michelpaulissen.com"));
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForJsonLine(child, label) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let stdout = "";
|
||||||
|
let stderr = "";
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
reject(new Error(`${label} did not produce a JSON line\n${stderr}`));
|
||||||
|
}, 120000);
|
||||||
|
child.stdout.on("data", (chunk) => {
|
||||||
|
stdout += chunk.toString();
|
||||||
|
for (const line of stdout.split(/\r?\n/)) {
|
||||||
|
if (!line.trim()) continue;
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(line);
|
||||||
|
clearTimeout(timer);
|
||||||
|
resolve(parsed);
|
||||||
|
return;
|
||||||
|
} catch (_) {
|
||||||
|
// keep waiting
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
child.stderr.on("data", (chunk) => {
|
||||||
|
stderr += chunk.toString();
|
||||||
|
});
|
||||||
|
child.once("error", (error) => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
child.once("exit", (code) => {
|
||||||
|
if (code !== 0) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
reject(new Error(`${label} exited with ${code}\n${stderr}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stopChild(child) {
|
||||||
|
if (!child || child.exitCode !== null) return;
|
||||||
|
child.kill("SIGTERM");
|
||||||
|
await new Promise((resolve) => child.once("exit", resolve));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
requireEnabled();
|
||||||
|
const manifest = readJson(manifestPath);
|
||||||
|
assert.strictEqual(manifest.kind, "disasmer-public-release-dryrun");
|
||||||
|
assert.strictEqual(manifest.default_operator_endpoint, serviceEndpoint);
|
||||||
|
assert.strictEqual(serviceAddr, "disasmer.michelpaulissen.com:9443");
|
||||||
|
|
||||||
|
const workRoot = fs.mkdtempSync(path.join(os.tmpdir(), "disasmer-cli-happy-"));
|
||||||
|
const installDir = path.join(workRoot, "install");
|
||||||
|
const checkout = path.join(workRoot, "public-repo");
|
||||||
|
ensureDir(installDir);
|
||||||
|
run("tar", ["-xzf", binaryArchive(manifest), "-C", installDir]);
|
||||||
|
run("git", ["clone", "--depth", "1", publicRepoUrl(manifest), checkout]);
|
||||||
|
|
||||||
|
const disasmer = executable(installDir, "disasmer");
|
||||||
|
const disasmerNode = executable(installDir, "disasmer-node");
|
||||||
|
const projectDir = path.join(checkout, "examples/launch-build-demo");
|
||||||
|
const suffix = String(Date.now());
|
||||||
|
const tenant = `cli-${suffix}`;
|
||||||
|
const project = `project-${suffix}`;
|
||||||
|
const user = "user";
|
||||||
|
const cliNode = `cli-node-${suffix}`;
|
||||||
|
const workerNode = `worker-${suffix}`;
|
||||||
|
const scope = [
|
||||||
|
"--coordinator",
|
||||||
|
serviceEndpoint,
|
||||||
|
"--tenant",
|
||||||
|
tenant,
|
||||||
|
"--project-id",
|
||||||
|
project,
|
||||||
|
"--user",
|
||||||
|
user,
|
||||||
|
"--json",
|
||||||
|
];
|
||||||
|
|
||||||
|
const login = runJson(
|
||||||
|
disasmer,
|
||||||
|
[
|
||||||
|
"login",
|
||||||
|
"--browser",
|
||||||
|
"--json",
|
||||||
|
"--complete-browser-code",
|
||||||
|
oidcCode,
|
||||||
|
"--oidc-issuer-url",
|
||||||
|
oidcIssuer,
|
||||||
|
"--tenant",
|
||||||
|
tenant,
|
||||||
|
"--project-id",
|
||||||
|
project,
|
||||||
|
"--user",
|
||||||
|
user,
|
||||||
|
],
|
||||||
|
{ cwd: projectDir }
|
||||||
|
);
|
||||||
|
assert.strictEqual(login.plan.coordinator, serviceEndpoint);
|
||||||
|
assert.strictEqual(login.boundary.scoped_cli_session_received, true);
|
||||||
|
|
||||||
|
const projectInit = runJson(
|
||||||
|
disasmer,
|
||||||
|
[
|
||||||
|
"project",
|
||||||
|
"init",
|
||||||
|
...scope,
|
||||||
|
"--new-project",
|
||||||
|
project,
|
||||||
|
"--name",
|
||||||
|
"CLI Happy Path",
|
||||||
|
"--yes",
|
||||||
|
],
|
||||||
|
{ cwd: projectDir }
|
||||||
|
);
|
||||||
|
assert.strictEqual(projectInit.command, "project init");
|
||||||
|
assert.strictEqual(projectInit.project_config_written, true);
|
||||||
|
assert.strictEqual(projectInit.coordinator_response.type, "project");
|
||||||
|
|
||||||
|
const projectList = runJson(disasmer, ["project", "list", ...scope], {
|
||||||
|
cwd: projectDir,
|
||||||
|
});
|
||||||
|
assert(projectList.project_count >= 1);
|
||||||
|
const projectSelect = runJson(
|
||||||
|
disasmer,
|
||||||
|
["project", "select", ...scope, project],
|
||||||
|
{ cwd: projectDir }
|
||||||
|
);
|
||||||
|
assert.strictEqual(projectSelect.command, "project select");
|
||||||
|
|
||||||
|
const inspection = runJson(disasmer, ["inspect", "--project", ".", "--json"], {
|
||||||
|
cwd: projectDir,
|
||||||
|
});
|
||||||
|
assert(
|
||||||
|
inspection.discovered_environments.some((environment) => environment.name === "linux")
|
||||||
|
);
|
||||||
|
assert(inspection.source_providers);
|
||||||
|
|
||||||
|
const cliGrant = runJson(
|
||||||
|
disasmer,
|
||||||
|
["node", "enroll", ...scope, "--grant", `cli-grant-${suffix}`],
|
||||||
|
{ cwd: projectDir }
|
||||||
|
);
|
||||||
|
assert.strictEqual(cliGrant.command, "node enroll");
|
||||||
|
const attach = runJson(
|
||||||
|
disasmer,
|
||||||
|
[
|
||||||
|
"node",
|
||||||
|
"attach",
|
||||||
|
"--coordinator",
|
||||||
|
serviceEndpoint,
|
||||||
|
"--tenant",
|
||||||
|
tenant,
|
||||||
|
"--project-id",
|
||||||
|
project,
|
||||||
|
"--node",
|
||||||
|
cliNode,
|
||||||
|
"--public-key",
|
||||||
|
`${cliNode}-public-key`,
|
||||||
|
"--enrollment-grant",
|
||||||
|
cliGrant.enrollment_grant.grant,
|
||||||
|
"--json",
|
||||||
|
],
|
||||||
|
{ cwd: projectDir }
|
||||||
|
);
|
||||||
|
assert.strictEqual(attach.command, "node attach");
|
||||||
|
assert.strictEqual(attach.boundary.used_enrollment_exchange, true);
|
||||||
|
|
||||||
|
const workerGrant = runJson(
|
||||||
|
disasmer,
|
||||||
|
["node", "enroll", ...scope, "--grant", `worker-grant-${suffix}`],
|
||||||
|
{ cwd: projectDir }
|
||||||
|
);
|
||||||
|
const workerArgs = [
|
||||||
|
"--coordinator",
|
||||||
|
serviceEndpoint,
|
||||||
|
"--tenant",
|
||||||
|
tenant,
|
||||||
|
"--project-id",
|
||||||
|
project,
|
||||||
|
"--node",
|
||||||
|
workerNode,
|
||||||
|
"--public-key",
|
||||||
|
`${workerNode}-public-key`,
|
||||||
|
"--enrollment-grant",
|
||||||
|
workerGrant.enrollment_grant.grant,
|
||||||
|
"--worker",
|
||||||
|
"--assignment-poll-ms",
|
||||||
|
"500",
|
||||||
|
"--emit-ready",
|
||||||
|
];
|
||||||
|
commands.push([disasmerNode, ...workerArgs].join(" "));
|
||||||
|
const worker = cp.spawn(disasmerNode, workerArgs, { cwd: projectDir });
|
||||||
|
try {
|
||||||
|
const ready = await waitForJsonLine(worker, "CLI happy-path worker ready");
|
||||||
|
assert.strictEqual(ready.node_status, "ready");
|
||||||
|
|
||||||
|
const runReport = runJson(disasmer, ["run", "build", "--project", ".", "--json"], {
|
||||||
|
cwd: projectDir,
|
||||||
|
});
|
||||||
|
assert.strictEqual(runReport.command, "run");
|
||||||
|
assert.strictEqual(runReport.status, "task_launched");
|
||||||
|
assert.strictEqual(runReport.task_launch.type, "task_launched");
|
||||||
|
assert.strictEqual(runReport.task_launch.placement.node, workerNode);
|
||||||
|
|
||||||
|
const completion = await waitForJsonLine(worker, "CLI happy-path worker completion");
|
||||||
|
assert.strictEqual(completion.node_status, "completed");
|
||||||
|
|
||||||
|
const processStatus = runJson(
|
||||||
|
disasmer,
|
||||||
|
["process", "status", ...scope, "--process", "vp-current"],
|
||||||
|
{ cwd: projectDir }
|
||||||
|
);
|
||||||
|
assert.strictEqual(processStatus.command, "process status");
|
||||||
|
assert(processStatus.current_task_count >= 1);
|
||||||
|
|
||||||
|
const logs = runJson(disasmer, ["logs", ...scope, "--process", "vp-current"], {
|
||||||
|
cwd: projectDir,
|
||||||
|
});
|
||||||
|
assert(logs.log_entries.length >= 1);
|
||||||
|
|
||||||
|
const artifacts = runJson(
|
||||||
|
disasmer,
|
||||||
|
["artifact", "list", ...scope, "--process", "vp-current"],
|
||||||
|
{ cwd: projectDir }
|
||||||
|
);
|
||||||
|
assert(artifacts.artifacts.length >= 1);
|
||||||
|
const artifact = artifacts.artifacts[0].artifact;
|
||||||
|
|
||||||
|
const download = runJson(disasmer, ["artifact", "download", ...scope, artifact], {
|
||||||
|
cwd: projectDir,
|
||||||
|
});
|
||||||
|
assert.strictEqual(download.download_session.link_issued, true);
|
||||||
|
|
||||||
|
const restart = runJson(
|
||||||
|
disasmer,
|
||||||
|
["process", "restart", ...scope, "--process", "vp-current", "--yes"],
|
||||||
|
{ cwd: projectDir }
|
||||||
|
);
|
||||||
|
assert.strictEqual(restart.restart_request.accepted, true);
|
||||||
|
|
||||||
|
const cancel = runJson(
|
||||||
|
disasmer,
|
||||||
|
["process", "cancel", ...scope, "--process", "vp-current", "--yes"],
|
||||||
|
{ cwd: projectDir }
|
||||||
|
);
|
||||||
|
assert.strictEqual(cancel.cancel_request.accepted, true);
|
||||||
|
|
||||||
|
const report = {
|
||||||
|
kind: "disasmer-cli-happy-path-live",
|
||||||
|
release_name: manifest.release_name,
|
||||||
|
source_commit: manifest.source_commit,
|
||||||
|
service_addr: serviceAddr,
|
||||||
|
default_operator_endpoint: serviceEndpoint,
|
||||||
|
public_repository_url: publicRepoUrl(manifest),
|
||||||
|
tenant,
|
||||||
|
project,
|
||||||
|
cli_node: cliNode,
|
||||||
|
worker_node: workerNode,
|
||||||
|
signed_in: true,
|
||||||
|
cli_installed_and_login_ran: true,
|
||||||
|
project_initialized_or_selected: true,
|
||||||
|
inspect_discovered_linux: true,
|
||||||
|
node_attached_with_cli: true,
|
||||||
|
run_launched_task: true,
|
||||||
|
work_ran_on_attached_node: true,
|
||||||
|
status_and_logs_seen: true,
|
||||||
|
artifact_download_requested: true,
|
||||||
|
process_restart_requested: true,
|
||||||
|
process_cancel_requested: true,
|
||||||
|
commands,
|
||||||
|
acceptance_result: "passed",
|
||||||
|
};
|
||||||
|
ensureDir(path.dirname(reportPath));
|
||||||
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
||||||
|
console.log(`CLI happy-path live smoke passed: ${reportPath}`);
|
||||||
|
} finally {
|
||||||
|
await stopChild(worker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((error) => {
|
||||||
|
console.error(error.stack || error.message);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
@ -203,6 +203,7 @@ for (const [name, pattern] of [
|
||||||
["final evidence verifies Forgejo host", /forgejoHost = "git\.michelpaulissen\.com"/],
|
["final evidence verifies Forgejo host", /forgejoHost = "git\.michelpaulissen\.com"/],
|
||||||
["final evidence verifies default operator", /serviceEndpoint = "https:\/\/disasmer\.michelpaulissen\.com:9443"/],
|
["final evidence verifies default operator", /serviceEndpoint = "https:\/\/disasmer\.michelpaulissen\.com:9443"/],
|
||||||
["final evidence accepts external public tree push", /function publicTreeAlreadyPushed\(manifest\)[\s\S]*DISASMER_PUBLIC_TREE_ALREADY_PUSHED/],
|
["final evidence accepts external public tree push", /function publicTreeAlreadyPushed\(manifest\)[\s\S]*DISASMER_PUBLIC_TREE_ALREADY_PUSHED/],
|
||||||
|
["final evidence records resolved public repo", /resolvedPublicRepoRemote[\s\S]*public_repository_url: resolvedPublicRepoRemote/],
|
||||||
["final evidence requires private hosted coordinator", /operator_implementation[\s\S]*"private-hosted-coordinator"/],
|
["final evidence requires private hosted coordinator", /operator_implementation[\s\S]*"private-hosted-coordinator"/],
|
||||||
["final evidence requires service private coordinator marker", /service\.operator_implementation[\s\S]*"private-hosted-coordinator"/],
|
["final evidence requires service private coordinator marker", /service\.operator_implementation[\s\S]*"private-hosted-coordinator"/],
|
||||||
["final evidence requires current service smoke source", /service\.source_commit[\s\S]*manifest\.source_commit/],
|
["final evidence requires current service smoke source", /service\.source_commit[\s\S]*manifest\.source_commit/],
|
||||||
|
|
|
||||||
|
|
@ -161,8 +161,9 @@ assert.strictEqual(
|
||||||
true,
|
true,
|
||||||
"filtered public tree must be pushed to Forgejo"
|
"filtered public tree must be pushed to Forgejo"
|
||||||
);
|
);
|
||||||
|
const resolvedPublicRepoRemote = publicRepoRemote(manifest);
|
||||||
assertIncludes(
|
assertIncludes(
|
||||||
publicRepoRemote(manifest),
|
resolvedPublicRepoRemote,
|
||||||
forgejoHost,
|
forgejoHost,
|
||||||
"manifest public repository must point at Forgejo"
|
"manifest public repository must point at Forgejo"
|
||||||
);
|
);
|
||||||
|
|
@ -412,7 +413,7 @@ const finalReport = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
forgejo_host: forgejoHost,
|
forgejo_host: forgejoHost,
|
||||||
public_repository_url: manifest.public_repo_url,
|
public_repository_url: resolvedPublicRepoRemote,
|
||||||
forgejo_release: {
|
forgejo_release: {
|
||||||
owner: forgejoRelease.owner,
|
owner: forgejoRelease.owner,
|
||||||
repo: forgejoRelease.repo,
|
repo: forgejoRelease.repo,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue