Add explicit non-interactive public CLI mode
This commit is contained in:
parent
e6c2a1a981
commit
afd7456423
6 changed files with 226 additions and 5 deletions
|
|
@ -35,6 +35,33 @@ function runDisasmer(args) {
|
|||
}
|
||||
|
||||
async function main() {
|
||||
const nonInteractive = await runDisasmer([
|
||||
"run",
|
||||
"build",
|
||||
"--project",
|
||||
project,
|
||||
"--non-interactive",
|
||||
"--json",
|
||||
]);
|
||||
assert.strictEqual(nonInteractive.signal, null, nonInteractive.stderr);
|
||||
assert.strictEqual(nonInteractive.code, 20, nonInteractive.stderr);
|
||||
assert.doesNotMatch(nonInteractive.stderr, /Opening Disasmer browser login/);
|
||||
const nonInteractiveReport = JSON.parse(nonInteractive.stdout);
|
||||
assert.strictEqual(nonInteractiveReport.status, "authentication_required");
|
||||
assert.strictEqual(nonInteractiveReport.non_interactive, true);
|
||||
assert.strictEqual(nonInteractiveReport.browser_opened, false);
|
||||
assert.strictEqual(nonInteractiveReport.machine_error.category, "authentication");
|
||||
assert.strictEqual(nonInteractiveReport.machine_error.stable_exit_code, 20);
|
||||
assert.strictEqual(
|
||||
nonInteractiveReport.machine_error.process_exit_code_applied,
|
||||
true
|
||||
);
|
||||
assert(
|
||||
nonInteractiveReport.machine_error.next_actions.includes(
|
||||
"pass --local to run against local services"
|
||||
)
|
||||
);
|
||||
|
||||
let request = "";
|
||||
const server = net.createServer((socket) => {
|
||||
socket.setEncoding("utf8");
|
||||
|
|
|
|||
|
|
@ -141,6 +141,9 @@ for (const [name, pattern] of [
|
|||
["top-level version metadata", /#\[command\([\s\S]*name = "disasmer"[\s\S]*version[\s\S]*arg_required_else_help = true[\s\S]*\)\]/],
|
||||
["top-level primary workflow help", /after_help = "Primary workflow:[\s\S]*disasmer login --browser[\s\S]*disasmer node attach --worker[\s\S]*Disasmer: Launch Virtual Process[\s\S]*Hosted account creation happens in the browser login flow\."/],
|
||||
["top-level logout command", /enum Commands[\s\S]*Logout\(AuthLogoutArgs\)[\s\S]*Auth \{/],
|
||||
["login non-interactive flag", /struct LoginArgs[\s\S]*non_interactive: bool/],
|
||||
["run non-interactive flag", /struct RunArgs[\s\S]*non_interactive: bool/],
|
||||
["non-interactive auth report", /fn non_interactive_auth_machine_error[\s\S]*browser_opened[\s\S]*false/],
|
||||
["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\)/],
|
||||
|
|
@ -166,6 +169,8 @@ for (const [name, pattern] of [
|
|||
for (const [name, pattern] of [
|
||||
["CLI parse coverage", /fn cli_first_mvp_command_surface_parses\(\)/],
|
||||
["CLI primary workflow help coverage", /fn top_level_help_exposes_primary_workflow_without_auth\(\)/],
|
||||
["CLI non-interactive run auth coverage", /fn non_interactive_run_without_session_requires_explicit_auth_or_local\(\)/],
|
||||
["CLI non-interactive browser login coverage", /fn browser_login_non_interactive_fails_before_opening_browser\(\)/],
|
||||
["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\(\)/],
|
||||
|
|
|
|||
|
|
@ -18,6 +18,21 @@ function disasmer(args) {
|
|||
);
|
||||
}
|
||||
|
||||
function disasmerRaw(args, env = {}) {
|
||||
return cp.spawnSync(
|
||||
"cargo",
|
||||
["run", "-q", "-p", "disasmer-cli", "--bin", "disasmer", "--", ...args],
|
||||
{
|
||||
cwd: repo,
|
||||
encoding: "utf8",
|
||||
env: {
|
||||
...process.env,
|
||||
...env,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const device = disasmer(["login", "--coordinator", coordinator, "--json"]);
|
||||
assert.strictEqual(device.coordinator, coordinator);
|
||||
assert(device.human_flow.Device, "default human login should use device flow");
|
||||
|
|
@ -44,4 +59,25 @@ assert.match(
|
|||
assert.match(browser.human_flow.Browser.callback_path, /^http:\/\/127\.0\.0\.1:\d+\/callback$/);
|
||||
assert.match(browser.human_flow.Browser.state, /^sha256:[a-f0-9]{64}$/);
|
||||
|
||||
const nonInteractiveBrowser = disasmerRaw(
|
||||
["login", "--browser", "--non-interactive", "--coordinator", coordinator, "--json"],
|
||||
{
|
||||
DISASMER_BROWSER_OPEN_COMMAND:
|
||||
"node -e 'require(\"fs\").writeFileSync(\"/tmp/disasmer-browser-should-not-open\", \"opened\")'",
|
||||
}
|
||||
);
|
||||
assert.strictEqual(nonInteractiveBrowser.status, 20, nonInteractiveBrowser.stderr);
|
||||
assert.doesNotMatch(nonInteractiveBrowser.stderr, /Opening Disasmer browser login/);
|
||||
const nonInteractiveReport = JSON.parse(nonInteractiveBrowser.stdout);
|
||||
assert.strictEqual(nonInteractiveReport.status, "authentication_required");
|
||||
assert.strictEqual(nonInteractiveReport.non_interactive, true);
|
||||
assert.strictEqual(nonInteractiveReport.browser_opened, false);
|
||||
assert.strictEqual(nonInteractiveReport.machine_error.category, "authentication");
|
||||
assert.strictEqual(nonInteractiveReport.machine_error.stable_exit_code, 20);
|
||||
assert(
|
||||
nonInteractiveReport.machine_error.next_actions.includes(
|
||||
"rerun without --non-interactive to open the browser"
|
||||
)
|
||||
);
|
||||
|
||||
console.log("CLI login smoke passed");
|
||||
|
|
|
|||
|
|
@ -5,7 +5,15 @@ const fs = require("fs");
|
|||
const path = require("path");
|
||||
|
||||
const repo = path.resolve(__dirname, "..");
|
||||
const readme = fs.readFileSync(path.join(repo, "README.md"), "utf8");
|
||||
const readmePath = path.join(repo, "README.md");
|
||||
if (!fs.existsSync(readmePath)) {
|
||||
if (fs.existsSync(path.join(repo, "DISASMER_PUBLIC_TREE.json"))) {
|
||||
console.log("Docs smoke skipped: root markdown is filtered from this public tree");
|
||||
process.exit(0);
|
||||
}
|
||||
throw new Error("README.md is missing");
|
||||
}
|
||||
const readme = fs.readFileSync(readmePath, "utf8");
|
||||
const userFacingDocs = [
|
||||
"README.md",
|
||||
"MVP.md",
|
||||
|
|
@ -73,7 +81,7 @@ const requiredReadmePatterns = [
|
|||
["hosted community limit", /community tier does not provide arbitrary hosted native commands or hosted containers/],
|
||||
["browser login", /disasmer login --browser/],
|
||||
["public-key agents", /disasmer agent enroll --public-key/],
|
||||
["noninteractive agent CLI", /DISASMER_AGENT_PUBLIC_KEY=<agent-public-key> disasmer run build/],
|
||||
["noninteractive agent CLI", /DISASMER_AGENT_PUBLIC_KEY=<agent-public-key> disasmer run --non-interactive build/],
|
||||
["agent key lifecycle", /register, list, rotate, and revoke an agent key/],
|
||||
["capability auto-detect", /auto-detects OS, architecture/],
|
||||
["capability override", /--cap <name>/],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue