Public dry run dryrun-0b6a82183be2

Source commit: 0b6a82183be25780b308c75442a9b5f602f727d4
This commit is contained in:
Michel Paulissen 2026-07-03 19:11:24 +02:00
parent 8c0c336ae9
commit 5386bb851f
16 changed files with 608 additions and 95 deletions

View file

@ -37,6 +37,7 @@ cargo fmt --all --check
cargo test --workspace
cargo build --workspace --bins
node scripts/docs-smoke.js
node scripts/cli-output-mode-smoke.js
node scripts/cli-login-smoke.js
node scripts/cli-browser-login-flow-smoke.js
node scripts/cli-install-smoke.js

View file

@ -32,6 +32,7 @@ function expect(source, name, pattern) {
const criteria = read("cli_acceptance_criteria.md");
const cli = read("crates/disasmer-cli/src/main.rs");
const outputModeSmoke = read("scripts/cli-output-mode-smoke.js");
expect(criteria, "header", /^# Disasmer CLI-First MVP Acceptance Criteria/m);
expect(
@ -76,6 +77,8 @@ assert.deepStrictEqual(
for (const [name, pattern] of [
["top-level version metadata", /#\[command\(name = "disasmer", version, arg_required_else_help = true\)\]/],
["human report renderer", /fn human_report\(value: &Value\) -> String/],
["shared report emitter", /fn emit_report<T: Serialize>\(report: &T, json_output: bool\) -> Result<\(\)>/],
["doctor command", /Doctor\(DoctorArgs\)/],
["auth status command", /enum AuthCommands[\s\S]*Status\(AuthStatusArgs\)/],
["auth logout command", /enum AuthCommands[\s\S]*Logout\(AuthLogoutArgs\)/],
@ -99,6 +102,8 @@ for (const [name, pattern] of [
for (const [name, pattern] of [
["CLI parse coverage", /fn cli_first_mvp_command_surface_parses\(\)/],
["CLI version coverage", /fn top_level_version_is_available\(\)/],
["CLI JSON parse coverage", /fn cli_first_json_mode_parses_for_primary_commands\(\)/],
["CLI human output coverage", /fn human_report_is_text_not_json\(\)/],
["project local config coverage", /fn project_init_select_and_status_use_local_project_config\(\)/],
["build no full repo upload coverage", /fn build_command_reuses_bundle_inspection_without_full_repo_upload\(\)/],
["safe coordinator-required plans", /fn node_enroll_and_process_commands_have_safe_plan_without_coordinator\(\)/],
@ -106,4 +111,26 @@ for (const [name, pattern] of [
expect(cli, name, pattern);
}
for (const [name, pattern] of [
["agent --json flag", /struct AgentEnrollArgs[\s\S]*#\[arg\(long\)\]\s*json: bool/],
["bundle inspect --json flag", /struct BundleInspectArgs[\s\S]*#\[arg\(long\)\]\s*json: bool/],
["build --json flag", /struct BuildArgs[\s\S]*#\[arg\(long\)\]\s*json: bool/],
["run --json flag", /struct RunArgs[\s\S]*#\[arg\(long\)\]\s*json: bool/],
["node attach --json flag", /struct AttachArgs[\s\S]*#\[arg\(long\)\]\s*json: bool/],
["DAP plan --json flag", /struct DapArgs[\s\S]*#\[arg\(long\)\]\s*json: bool/],
["shared scope --json flag", /struct CliScopeArgs[\s\S]*#\[arg\(long\)\]\s*json: bool/],
]) {
expect(cli, name, pattern);
}
for (const [name, pattern] of [
["human default assertion", /default output should be human-readable text, not JSON/],
["login JSON mode", /\["login", "--coordinator", "https:\/\/coord\.example\.test", "--json"\]/],
["doctor human mode", /\["doctor"\]/],
["bundle inspect JSON mode", /\["bundle", "inspect", "--project", project, "--json"\]/],
["auth expiry posture", /token_expiry_posture[\s\S]*expires_at/],
]) {
expect(outputModeSmoke, name, pattern);
}
console.log("CLI-first contract smoke passed");

View file

@ -42,7 +42,7 @@ try {
const inspection = JSON.parse(
cp.execFileSync(
installedBin,
["bundle", "inspect", "--project", project],
["bundle", "inspect", "--project", project, "--json"],
{ cwd: repo, encoding: "utf8" }
)
);

View file

@ -115,7 +115,7 @@ function runCli(args, env = {}) {
assert.strictEqual((await send(addr, { type: "ping" })).type, "pong");
const { pid: cliPid, report } = await runCli(
["run", "--coordinator", `${addr.host}:${addr.port}`, "--project", project],
["run", "--coordinator", `${addr.host}:${addr.port}`, "--project", project, "--json"],
{ DISASMER_AGENT_PUBLIC_KEY: agentPublicKey }
);
assert(Number.isInteger(cliPid));
@ -164,7 +164,8 @@ function runCli(args, env = {}) {
"run",
"--local",
"--project",
project
project,
"--json",
]);
assert(Number.isInteger(autoCliPid));
assert.strictEqual(autoReport.plan.entry, "build");

View file

@ -18,7 +18,7 @@ function disasmer(args) {
);
}
const device = disasmer(["login", "--coordinator", coordinator]);
const device = disasmer(["login", "--coordinator", coordinator, "--json"]);
assert.strictEqual(device.coordinator, coordinator);
assert(device.human_flow.Device, "default human login should use device flow");
assert.strictEqual(device.human_flow.Device.verification_url, `${coordinator}/auth/device`);
@ -27,14 +27,14 @@ assert.match(device.human_flow.Device.device_code, /^sha256:[a-f0-9]{64}$/);
assert.strictEqual(device.human_flow.Device.expires_in_seconds, 900);
assert.strictEqual(device.human_flow.Device.yields_long_lived_secret_directly, false);
const defaultDevice = disasmer(["login"]);
const defaultDevice = disasmer(["login", "--json"]);
assert.strictEqual(defaultDevice.coordinator, defaultOperatorEndpoint);
assert.strictEqual(
defaultDevice.human_flow.Device.verification_url,
`${defaultOperatorEndpoint}/auth/device`
);
const browser = disasmer(["login", "--browser", "--plan", "--coordinator", coordinator]);
const browser = disasmer(["login", "--browser", "--plan", "--coordinator", coordinator, "--json"]);
assert.strictEqual(browser.coordinator, coordinator);
assert(browser.human_flow.Browser, "browser login should be available for human users");
assert.match(

View file

@ -0,0 +1,77 @@
#!/usr/bin/env node
const assert = require("assert");
const cp = require("child_process");
const path = require("path");
const repo = path.resolve(__dirname, "..");
const project = path.join(repo, "examples/launch-build-demo");
function disasmer(args, env = {}) {
return cp.execFileSync(
"cargo",
["run", "-q", "-p", "disasmer-cli", "--bin", "disasmer", "--", ...args],
{
cwd: repo,
encoding: "utf8",
env: {
...process.env,
...env,
},
}
);
}
function json(args, env) {
return JSON.parse(disasmer(args, env));
}
function assertHuman(name, output, requiredPatterns) {
assert(
!output.trimStart().startsWith("{"),
`${name} default output should be human-readable text, not JSON`
);
for (const pattern of requiredPatterns) {
assert.match(output, pattern, `${name} human output missing ${pattern}`);
}
}
const loginHuman = disasmer(["login", "--coordinator", "https://coord.example.test"]);
assertHuman("login", loginHuman, [
/Disasmer login/,
/flow: device/,
/verify: https:\/\/coord\.example\.test\/auth\/device/,
]);
const loginJson = json(["login", "--coordinator", "https://coord.example.test", "--json"]);
assert.strictEqual(loginJson.coordinator, "https://coord.example.test");
assert(loginJson.human_flow.Device);
const doctorHuman = disasmer(["doctor"]);
assertHuman("doctor", doctorHuman, [
/Disasmer doctor/,
/dependencies:/,
/auth:/,
/node capabilities:/,
]);
const authJson = json(["auth", "status", "--json"], {
DISASMER_TOKEN: "token",
DISASMER_TOKEN_EXPIRES_AT: "2026-07-04T00:00:00Z",
});
assert.strictEqual(authJson.session.kind, "human");
assert.strictEqual(authJson.session.token_expiry_posture, "expires_at");
assert.strictEqual(authJson.session.expires_at, "2026-07-04T00:00:00Z");
const inspectHuman = disasmer(["bundle", "inspect", "--project", project]);
assertHuman("bundle inspect", inspectHuman, [
/Disasmer bundle inspect/,
/bundle: sha256:/,
/environments:/,
]);
const inspectJson = json(["bundle", "inspect", "--project", project, "--json"]);
assert.strictEqual(inspectJson.project, project);
assert.match(inspectJson.metadata.identity, /^sha256:/);
console.log("CLI output mode smoke passed");

View file

@ -158,6 +158,10 @@ for (const script of [publicAcceptance, publicSplit]) {
script.includes("node scripts/cli-install-smoke.js"),
"public acceptance gates must run cli-install-smoke.js"
);
assert(
script.includes("node scripts/cli-output-mode-smoke.js"),
"public acceptance gates must run cli-output-mode-smoke.js"
);
assert(
script.includes("node scripts/cli-login-smoke.js"),
"public acceptance gates must run cli-login-smoke.js"

View file

@ -36,7 +36,8 @@ const inspection = JSON.parse(
"bundle",
"inspect",
"--project",
project
project,
"--json"
],
{ cwd: repo, encoding: "utf8" }
)

View file

@ -76,6 +76,7 @@ function runAttach(addr, grant) {
grant,
"--cap",
"quic-direct",
"--json",
],
{ cwd: repo }
);

View file

@ -883,7 +883,7 @@ async function main() {
const disasmerNode = executable(installDir, "disasmer-node");
const disasmerDap = executable(installDir, "disasmer-debug-dap");
const defaultLoginPlan = runJson(disasmer, ["login"], { cwd: checkout });
const defaultLoginPlan = runJson(disasmer, ["login", "--json"], { cwd: checkout });
assert.strictEqual(defaultLoginPlan.coordinator, serviceEndpoint);
const suffix = String(Date.now());
@ -902,6 +902,7 @@ async function main() {
[
"login",
"--browser",
"--json",
"--complete-browser-code",
oidcCode,
"--oidc-issuer-url",
@ -959,6 +960,7 @@ async function main() {
`${cliNode}-public-key`,
"--enrollment-grant",
cliGrant.grant.grant_id,
"--json",
],
{ cwd: checkout }
);

View file

@ -46,6 +46,7 @@ if [[ -n "${DISASMER_FORGEJO_TOKEN:-}" ]]; then
(cd "$tmp_dir" && node scripts/publish-public-release-dryrun.js)
fi
(cd "$tmp_dir" && node scripts/docs-smoke.js)
(cd "$tmp_dir" && node scripts/cli-output-mode-smoke.js)
(cd "$tmp_dir" && node scripts/cli-login-smoke.js)
(cd "$tmp_dir" && node scripts/cli-browser-login-flow-smoke.js)
(cd "$tmp_dir" && node scripts/cli-install-smoke.js)

View file

@ -61,6 +61,7 @@ assert.deepStrictEqual(inspectCommand.args.slice(0, 8), [
assert(inspectCommand.args.includes("inspect"));
assert(inspectCommand.args.includes("--project"));
assert(inspectCommand.args.includes(root));
assert(inspectCommand.args.includes("--json"));
const refreshed = extension.refreshBundleBeforeLaunch(root, "/repo", (command, args, options) => {
assert.strictEqual(command, "cargo");

View file

@ -169,7 +169,8 @@ function runWindowsAttach(addr, grant) {
"--enrollment-grant",
grant,
"--cap",
"windows-command-dev"
"windows-command-dev",
"--json"
],
{ cwd: repo }
);