Sync public tree to b67d2a6

This commit is contained in:
Michel Paulissen 2026-07-04 17:37:07 +02:00
parent 21f21fa8b6
commit 1d72f20cce
7 changed files with 466 additions and 6 deletions

View file

@ -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/**",

View file

@ -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,

View file

@ -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

View file

@ -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",

View 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);
});

View file

@ -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/],

View file

@ -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,