Public dry run dryrun-f1e9b7b784b4
Source commit: f1e9b7b784b4a4dd28799719681535e2a4ef44c9
This commit is contained in:
parent
20c72e6066
commit
8c0c336ae9
5 changed files with 1829 additions and 20 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"kind": "disasmer-filtered-public-tree",
|
||||
"source_commit": "1714a9eedd5b1e30c6c9aceb7a999f2f695ba83c",
|
||||
"release_name": "dryrun-1714a9eedd5b",
|
||||
"source_commit": "f1e9b7b784b4a4dd28799719681535e2a4ef44c9",
|
||||
"release_name": "dryrun-f1e9b7b784b4",
|
||||
"filtered_out": [
|
||||
"private/**",
|
||||
"experiments/**",
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -305,7 +305,7 @@ fn launch_threads(entry: &str) -> BTreeMap<i64, VirtualThread> {
|
|||
thread(MAIN_THREAD, "main", &format!("{entry} virtual process"), 12),
|
||||
thread(LINUX_THREAD, "compile-linux", "compile linux", 42),
|
||||
thread(WINDOWS_THREAD, "compile-windows", "compile windows", 52),
|
||||
thread(PACKAGE_THREAD, "package", "package artifacts", 64),
|
||||
thread(PACKAGE_THREAD, "package-release", "package artifacts", 64),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|thread| (thread.id, thread))
|
||||
|
|
@ -1428,6 +1428,7 @@ fn parse_let_binding_name(line: &str) -> Option<String> {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
enum SourceLocalRuntimeValue {
|
||||
SourceSnapshot(String),
|
||||
TaskHandle {
|
||||
task: TaskId,
|
||||
thread_id: i64,
|
||||
|
|
@ -1441,6 +1442,9 @@ enum SourceLocalRuntimeValue {
|
|||
impl SourceLocalRuntimeValue {
|
||||
fn display(&self, _state: &AdapterState) -> String {
|
||||
match self {
|
||||
SourceLocalRuntimeValue::SourceSnapshot(digest) => {
|
||||
format!("SourceSnapshot {{ digest = \"{digest}\" }}")
|
||||
}
|
||||
SourceLocalRuntimeValue::TaskHandle {
|
||||
task,
|
||||
thread_id,
|
||||
|
|
@ -1462,7 +1466,18 @@ fn infer_disasmer_source_local_value(
|
|||
statement: &str,
|
||||
runtime_values: &BTreeMap<String, SourceLocalRuntimeValue>,
|
||||
) -> Option<SourceLocalRuntimeValue> {
|
||||
if let Some(task_function) = extract_call_argument(statement, "disasmer::spawn::task(") {
|
||||
if statement.contains("prepare_source_snapshot()") {
|
||||
return Some(SourceLocalRuntimeValue::SourceSnapshot(
|
||||
source_snapshot_digest_from_project(state).unwrap_or_else(|| {
|
||||
format!(
|
||||
"source://local-checkout/{}",
|
||||
project_snapshot_suffix(&state.project)
|
||||
)
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(task_function) = extract_spawn_task_function(statement) {
|
||||
let task_name = extract_string_argument(statement, ".name(");
|
||||
let spawned_thread =
|
||||
thread_for_spawn_statement(state, &task_function, task_name.as_deref())?;
|
||||
|
|
@ -1498,12 +1513,83 @@ fn infer_disasmer_source_local_value(
|
|||
None
|
||||
}
|
||||
|
||||
fn extract_call_argument(statement: &str, marker: &str) -> Option<String> {
|
||||
fn extract_spawn_task_function(statement: &str) -> Option<String> {
|
||||
if let Some(args) = extract_call_arguments(statement, "disasmer::spawn::task(") {
|
||||
return args.first().cloned();
|
||||
}
|
||||
let args = extract_call_arguments(statement, "disasmer::spawn::task_with_arg(")?;
|
||||
args.get(1).cloned()
|
||||
}
|
||||
|
||||
fn source_snapshot_digest_from_project(state: &AdapterState) -> Option<String> {
|
||||
let source =
|
||||
fs::read_to_string(resolve_source_path(&state.project, &state.source_path)).ok()?;
|
||||
let digest_marker = source.find("digest:")?;
|
||||
let after_digest = &source[digest_marker..];
|
||||
let first_quote = after_digest.find('"')? + 1;
|
||||
let rest = &after_digest[first_quote..];
|
||||
let end_quote = rest.find('"')?;
|
||||
Some(rest[..end_quote].to_owned())
|
||||
}
|
||||
|
||||
fn extract_call_arguments(statement: &str, marker: &str) -> Option<Vec<String>> {
|
||||
let start = statement.find(marker)? + marker.len();
|
||||
let rest = &statement[start..];
|
||||
let end = rest.find(')')?;
|
||||
let value = rest[..end].trim();
|
||||
(!value.is_empty()).then_some(value.to_owned())
|
||||
let mut args = Vec::new();
|
||||
let mut current = String::new();
|
||||
let mut depth = 0_i32;
|
||||
let mut in_string = false;
|
||||
let mut escaped = false;
|
||||
|
||||
for ch in rest.chars() {
|
||||
if in_string {
|
||||
current.push(ch);
|
||||
if escaped {
|
||||
escaped = false;
|
||||
} else if ch == '\\' {
|
||||
escaped = true;
|
||||
} else if ch == '"' {
|
||||
in_string = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
match ch {
|
||||
'"' => {
|
||||
in_string = true;
|
||||
current.push(ch);
|
||||
}
|
||||
'(' | '[' | '{' => {
|
||||
depth += 1;
|
||||
current.push(ch);
|
||||
}
|
||||
')' => {
|
||||
if depth == 0 {
|
||||
let value = current.trim();
|
||||
if !value.is_empty() {
|
||||
args.push(value.to_owned());
|
||||
}
|
||||
return (!args.is_empty()).then_some(args);
|
||||
}
|
||||
depth -= 1;
|
||||
current.push(ch);
|
||||
}
|
||||
']' | '}' => {
|
||||
depth -= 1;
|
||||
current.push(ch);
|
||||
}
|
||||
',' if depth == 0 => {
|
||||
let value = current.trim();
|
||||
if !value.is_empty() {
|
||||
args.push(value.to_owned());
|
||||
}
|
||||
current.clear();
|
||||
}
|
||||
_ => current.push(ch),
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn extract_string_argument(statement: &str, marker: &str) -> Option<String> {
|
||||
|
|
@ -1518,9 +1604,15 @@ fn extract_env_name(statement: &str) -> Option<String> {
|
|||
if statement.contains("windows_env") || statement.contains("env!(\"windows\")") {
|
||||
return Some("windows".to_owned());
|
||||
}
|
||||
if statement.contains("linux_command_env") || statement.contains("env!(\"linux-command\")") {
|
||||
return Some("linux-command".to_owned());
|
||||
}
|
||||
if statement.contains("linux_env") || statement.contains("env!(\"linux\")") {
|
||||
return Some("linux".to_owned());
|
||||
}
|
||||
if statement.contains("coordinator_env") || statement.contains("env!(\"coordinator\")") {
|
||||
return Some("coordinator".to_owned());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
|
@ -1552,25 +1644,27 @@ fn thread_for_spawn_statement<'a>(
|
|||
}
|
||||
}
|
||||
let normalized = task_function.replace('_', "-");
|
||||
state
|
||||
.threads
|
||||
.values()
|
||||
.find(|thread| thread.task.as_str() == normalized)
|
||||
state.threads.values().find(|thread| {
|
||||
thread.task.as_str() == normalized
|
||||
|| (thread.id == PACKAGE_THREAD && normalized == "package-release")
|
||||
})
|
||||
}
|
||||
|
||||
fn task_environment(thread: &VirtualThread) -> &'static str {
|
||||
match thread.id {
|
||||
WINDOWS_THREAD => "windows",
|
||||
MAIN_THREAD => "coordinator",
|
||||
PACKAGE_THREAD => "coordinator",
|
||||
_ => "linux",
|
||||
}
|
||||
}
|
||||
|
||||
fn task_arguments_value(thread: &VirtualThread) -> String {
|
||||
if thread.task.as_str() == "task_add_one" {
|
||||
"[input: i32]".to_owned()
|
||||
} else {
|
||||
"[]".to_owned()
|
||||
match thread.task.as_str() {
|
||||
"compile-linux" => "[source: SourceSnapshot]".to_owned(),
|
||||
"package-release" => "[inputs: Vec<Artifact>]".to_owned(),
|
||||
"task_add_one" => "[input: i32]".to_owned(),
|
||||
_ => "[]".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2224,6 +2318,7 @@ fn read_message<R: BufRead>(reader: &mut R) -> Result<Option<Value>> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs;
|
||||
use std::io::Cursor;
|
||||
|
||||
use serde_json::json;
|
||||
|
|
@ -2349,7 +2444,7 @@ mod tests {
|
|||
let error = freeze_all(&mut state, LINUX_THREAD, None).unwrap_err();
|
||||
|
||||
assert_eq!(error.thread_id, PACKAGE_THREAD);
|
||||
assert_eq!(error.task, TaskId::from("package"));
|
||||
assert_eq!(error.task, TaskId::from("package-release"));
|
||||
assert!(error.message().contains("could not freeze"));
|
||||
assert_eq!(state.epoch, 0);
|
||||
assert!(state
|
||||
|
|
@ -2541,6 +2636,119 @@ mod tests {
|
|||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn source_locals_infer_task_with_arg_values_from_runtime_state() {
|
||||
let project =
|
||||
std::env::temp_dir().join(format!("disasmer-dap-task-with-arg-{}", std::process::id()));
|
||||
let src = project.join("src");
|
||||
fs::create_dir_all(&src).unwrap();
|
||||
fs::write(
|
||||
src.join("main.rs"),
|
||||
r#"use disasmer::{Artifact, EnvRef, SourceSnapshot};
|
||||
|
||||
async fn build_release() -> Result<(), disasmer::TaskArgError> {
|
||||
let source = prepare_source_snapshot();
|
||||
|
||||
let compile = disasmer::spawn::task_with_arg(source.clone(), compile_linux)
|
||||
.name("compile linux")
|
||||
.env(linux_command_env())
|
||||
.start()
|
||||
.await?;
|
||||
|
||||
let compile_thread = compile.virtual_thread_id();
|
||||
let linux_artifact = compile.join().await;
|
||||
|
||||
let package = disasmer::spawn::task_with_arg(vec![linux_artifact.clone()], package_release)
|
||||
.name("package release")
|
||||
.env(coordinator_env())
|
||||
.start()
|
||||
.await?;
|
||||
|
||||
let package_thread = package.virtual_thread_id();
|
||||
let release_artifact = package.join().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_source_snapshot() -> SourceSnapshot {
|
||||
SourceSnapshot {
|
||||
digest: "source://coordinator/quick-test-checkout".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn linux_command_env() -> EnvRef {
|
||||
disasmer::env!("linux-command")
|
||||
}
|
||||
|
||||
fn coordinator_env() -> EnvRef {
|
||||
disasmer::env!("coordinator")
|
||||
}
|
||||
|
||||
fn compile_linux(source: SourceSnapshot) -> Artifact {
|
||||
Artifact { id: source.digest }
|
||||
}
|
||||
|
||||
fn package_release(inputs: Vec<Artifact>) -> Artifact {
|
||||
inputs.into_iter().next().unwrap()
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut state = AdapterState::default();
|
||||
state.project = project.to_string_lossy().into_owned();
|
||||
state.source_path = "src/main.rs".to_owned();
|
||||
state.threads.get_mut(&MAIN_THREAD).unwrap().line = 23;
|
||||
let thread = state.threads[&MAIN_THREAD].clone();
|
||||
|
||||
let locals = variables_response(&state, thread.locals_ref);
|
||||
let locals = locals["variables"].as_array().unwrap();
|
||||
|
||||
assert!(locals.iter().any(|variable| {
|
||||
variable["name"] == "source"
|
||||
&& variable["value"]
|
||||
.as_str()
|
||||
.is_some_and(|value| value.contains("source://coordinator/quick-test-checkout"))
|
||||
}));
|
||||
assert!(locals.iter().any(|variable| {
|
||||
variable["name"] == "compile"
|
||||
&& variable["value"].as_str().is_some_and(|value| {
|
||||
value.contains("TaskHandle")
|
||||
&& value.contains("compile-linux")
|
||||
&& value.contains("virtual_thread_id = 2")
|
||||
&& value.contains("linux-command")
|
||||
})
|
||||
}));
|
||||
assert!(locals
|
||||
.iter()
|
||||
.any(|variable| variable["name"] == "compile_thread" && variable["value"] == "2"));
|
||||
assert!(locals.iter().any(|variable| {
|
||||
variable["name"] == "linux_artifact"
|
||||
&& variable["value"]
|
||||
.as_str()
|
||||
.is_some_and(|value| value.contains("Artifact"))
|
||||
}));
|
||||
assert!(locals.iter().any(|variable| {
|
||||
variable["name"] == "package"
|
||||
&& variable["value"].as_str().is_some_and(|value| {
|
||||
value.contains("TaskHandle")
|
||||
&& value.contains("package-release")
|
||||
&& value.contains("virtual_thread_id = 4")
|
||||
&& value.contains("coordinator")
|
||||
})
|
||||
}));
|
||||
assert!(locals
|
||||
.iter()
|
||||
.any(|variable| variable["name"] == "package_thread" && variable["value"] == "4"));
|
||||
assert!(locals.iter().any(|variable| {
|
||||
variable["name"] == "release_artifact"
|
||||
&& variable["value"]
|
||||
.as_str()
|
||||
.is_some_and(|value| value.contains("Artifact"))
|
||||
}));
|
||||
|
||||
let _ = fs::remove_dir_all(project);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wasm_frame_locals_expose_wasmtime_runtime_values() {
|
||||
let mut state = AdapterState::default();
|
||||
|
|
|
|||
109
scripts/cli-first-contract-smoke.js
Normal file
109
scripts/cli-first-contract-smoke.js
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const assert = require("assert");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const repo = path.resolve(__dirname, "..");
|
||||
|
||||
function read(relativePath) {
|
||||
const fullPath = path.join(repo, relativePath);
|
||||
if (!fs.existsSync(fullPath)) {
|
||||
if (fs.existsSync(path.join(repo, "DISASMER_PUBLIC_TREE.json"))) {
|
||||
console.log(
|
||||
`CLI-first contract smoke skipped: ${relativePath} is filtered from this public tree`
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
throw new Error(`${relativePath} is missing`);
|
||||
}
|
||||
return fs.readFileSync(fullPath, "utf8");
|
||||
}
|
||||
|
||||
function criterionLines(source) {
|
||||
return source
|
||||
.split(/\r?\n/)
|
||||
.filter((line) => /^- \[[ x]\] \*\*/.test(line));
|
||||
}
|
||||
|
||||
function expect(source, name, pattern) {
|
||||
assert.match(source, pattern, `missing CLI-first contract evidence: ${name}`);
|
||||
}
|
||||
|
||||
const criteria = read("cli_acceptance_criteria.md");
|
||||
const cli = read("crates/disasmer-cli/src/main.rs");
|
||||
|
||||
expect(criteria, "header", /^# Disasmer CLI-First MVP Acceptance Criteria/m);
|
||||
expect(
|
||||
criteria,
|
||||
"addendum status",
|
||||
/\*\*Status:\*\* CLI-first addendum to `acceptance_criteria\.md` and `acceptance_criteria_phase2\.md`/
|
||||
);
|
||||
expect(
|
||||
criteria,
|
||||
"website exception",
|
||||
/hosted account creation as the only intentional private website exception/
|
||||
);
|
||||
expect(
|
||||
criteria,
|
||||
"no duplicate work note",
|
||||
/does not automatically mean new product code, a new feature, or even actual implementation work is required/
|
||||
);
|
||||
expect(
|
||||
criteria,
|
||||
"future hosted business non-goal",
|
||||
/billing, full support tooling, broad moderation consoles, and durable account\/business-process management are intentionally outside this CLI-first MVP slice/
|
||||
);
|
||||
|
||||
const lines = criterionLines(criteria);
|
||||
assert(lines.length > 0, "CLI-first criteria must contain criteria lines");
|
||||
for (const line of lines) {
|
||||
assert.match(
|
||||
line,
|
||||
/^- \[[ x]\] \*\*(Passed|Partial|Open)(?: \([^)]+\))?:\*\*/,
|
||||
`CLI-first criterion lacks an explicit status prefix: ${line}`
|
||||
);
|
||||
}
|
||||
|
||||
const openCriteria = lines.filter((line) => /\*\*Open(?::| \()/.test(line));
|
||||
assert.deepStrictEqual(
|
||||
openCriteria,
|
||||
[
|
||||
"- [ ] **Open:** Every criterion in this document is validated end-to-end or with integration-style coverage, preferably against a realistic NixOS deployment that can pretend to be live hosted infrastructure.",
|
||||
],
|
||||
"only the full CLI-first verification gate should remain Open while command/API facts are Partial"
|
||||
);
|
||||
|
||||
for (const [name, pattern] of [
|
||||
["top-level version metadata", /#\[command\(name = "disasmer", version, arg_required_else_help = true\)\]/],
|
||||
["doctor command", /Doctor\(DoctorArgs\)/],
|
||||
["auth status command", /enum AuthCommands[\s\S]*Status\(AuthStatusArgs\)/],
|
||||
["auth logout command", /enum AuthCommands[\s\S]*Logout\(AuthLogoutArgs\)/],
|
||||
["key lifecycle commands", /enum KeyCommands[\s\S]*Add\(KeyAddArgs\)[\s\S]*List\(KeyListArgs\)[\s\S]*Revoke\(KeyRevokeArgs\)/],
|
||||
["project commands", /enum ProjectCommands[\s\S]*Init\(ProjectInitArgs\)[\s\S]*Status\(ProjectStatusArgs\)[\s\S]*List\(ProjectListArgs\)[\s\S]*Select\(ProjectSelectArgs\)/],
|
||||
["inspect command", /Inspect\(BundleInspectArgs\)/],
|
||||
["build command", /Build\(BuildArgs\)/],
|
||||
["node lifecycle commands", /enum NodeCommands[\s\S]*Attach\(AttachArgs\)[\s\S]*Enroll\(NodeEnrollArgs\)[\s\S]*List\(NodeListArgs\)[\s\S]*Status\(NodeStatusArgs\)[\s\S]*Revoke\(NodeRevokeArgs\)/],
|
||||
["process commands", /enum ProcessCommands[\s\S]*Status\(ProcessStatusArgs\)[\s\S]*Restart\(ProcessRestartArgs\)[\s\S]*Cancel\(ProcessCancelArgs\)/],
|
||||
["task list command", /enum TaskCommands[\s\S]*List\(TaskListArgs\)/],
|
||||
["logs command", /Logs\(LogsArgs\)/],
|
||||
["artifact commands", /enum ArtifactCommands[\s\S]*List\(ArtifactListArgs\)[\s\S]*Download\(ArtifactDownloadArgs\)[\s\S]*Export\(ArtifactExportArgs\)/],
|
||||
["DAP command", /Dap\(DapArgs\)/],
|
||||
["debug attach command", /enum DebugCommands[\s\S]*Attach\(DebugAttachArgs\)/],
|
||||
["quota command", /enum QuotaCommands[\s\S]*Status\(QuotaStatusArgs\)/],
|
||||
["admin commands", /enum AdminCommands[\s\S]*Status\(AdminStatusArgs\)[\s\S]*Bootstrap\(AdminBootstrapArgs\)[\s\S]*RevokeNode\(NodeRevokeArgs\)[\s\S]*StopProcess\(ProcessCancelArgs\)[\s\S]*SuspendTenant\(AdminSuspendTenantArgs\)/],
|
||||
]) {
|
||||
expect(cli, name, pattern);
|
||||
}
|
||||
|
||||
for (const [name, pattern] of [
|
||||
["CLI parse coverage", /fn cli_first_mvp_command_surface_parses\(\)/],
|
||||
["CLI version coverage", /fn top_level_version_is_available\(\)/],
|
||||
["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\(\)/],
|
||||
]) {
|
||||
expect(cli, name, pattern);
|
||||
}
|
||||
|
||||
console.log("CLI-first contract smoke passed");
|
||||
93
scripts/website-inventory-contract-smoke.js
Normal file
93
scripts/website-inventory-contract-smoke.js
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const assert = require("assert");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const repo = path.resolve(__dirname, "..");
|
||||
const inventoryPath = path.join(repo, "website_mvp_inventory.md");
|
||||
if (!fs.existsSync(inventoryPath)) {
|
||||
if (fs.existsSync(path.join(repo, "DISASMER_PUBLIC_TREE.json"))) {
|
||||
console.log(
|
||||
"Website inventory contract smoke skipped: website_mvp_inventory.md is filtered from this public tree"
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
throw new Error("website_mvp_inventory.md is missing");
|
||||
}
|
||||
const source = fs.readFileSync(inventoryPath, "utf8");
|
||||
|
||||
function expect(name, pattern) {
|
||||
assert.match(source, pattern, `missing website inventory evidence: ${name}`);
|
||||
}
|
||||
|
||||
function criterionLines() {
|
||||
return source
|
||||
.split(/\r?\n/)
|
||||
.filter((line) => /^- \[[ x]\] \*\*/.test(line));
|
||||
}
|
||||
|
||||
expect("header", /^# Disasmer Minimal Website Inventory/m);
|
||||
expect(
|
||||
"acceptance-style status",
|
||||
/\*\*Status:\*\* working MVP website inventory aligned with `acceptance_criteria\.md`, `acceptance_criteria_phase2\.md`, and `cli_acceptance_criteria\.md`/
|
||||
);
|
||||
expect(
|
||||
"hosted signup exception",
|
||||
/except hosted Authentik account creation/
|
||||
);
|
||||
expect(
|
||||
"no duplicate work note",
|
||||
/does not automatically mean new product code, a new feature, or even actual implementation work is required/
|
||||
);
|
||||
expect(
|
||||
"barebones no CSS",
|
||||
/barebones functional HTML with no CSS/
|
||||
);
|
||||
expect(
|
||||
"future hosted business scope",
|
||||
/Billing, checkout, hosted support tooling, a full hosted admin console, broad moderation workflows/
|
||||
);
|
||||
expect(
|
||||
"billing is not MVP website work",
|
||||
/Do not build billing\/upgrade flows into the minimal website for this MVP/
|
||||
);
|
||||
assert.doesNotMatch(source, /community-tier/, "use `community tier`, not `community-tier`");
|
||||
|
||||
const lines = criterionLines();
|
||||
assert(lines.length > 0, "website inventory must contain status-prefixed checklist items");
|
||||
for (const line of lines) {
|
||||
assert.match(
|
||||
line,
|
||||
/^- \[[ x]\] \*\*(Passed|Partial|Open)(?: \([^)]+\))?:\*\*/,
|
||||
`website inventory item lacks an explicit status prefix: ${line}`
|
||||
);
|
||||
}
|
||||
|
||||
for (const [name, pattern] of [
|
||||
["CLI login command", /`disasmer login \[--browser\] \[--coordinator <url>\]`/],
|
||||
["CLI auth status command", /`disasmer auth status`/],
|
||||
["CLI key command", /`disasmer key add --public-key <pubkey>`/],
|
||||
["CLI project init command", /`disasmer project init`/],
|
||||
["CLI node enroll command", /`disasmer node enroll --coordinator <url> --ttl-seconds <seconds>`/],
|
||||
["CLI process status command", /`disasmer process status`/],
|
||||
["CLI task list command", /`disasmer task list`/],
|
||||
["CLI DAP command", /`disasmer dap`/],
|
||||
["CLI quota command", /`disasmer quota status`/],
|
||||
["CLI admin status command", /`disasmer admin status`/],
|
||||
]) {
|
||||
expect(name, pattern);
|
||||
}
|
||||
|
||||
for (const [name, pattern] of [
|
||||
["website default project is open", /- \[ \] \*\*Open:\*\* land in their default project\./],
|
||||
["website node attach is open", /- \[ \] \*\*Open:\*\* copy a node attach command\./],
|
||||
["website process view is open", /- \[ \] \*\*Open:\*\* see the single current virtual process\./],
|
||||
["website logs are open", /- \[ \] \*\*Open:\*\* see recent bounded logs\./],
|
||||
["website artifact download is open", /- \[ \] \*\*Open:\*\* securely download an available best-effort retained artifact\./],
|
||||
["website keys are partial through CLI", /- \[ \] \*\*Partial:\*\* manage\/revoke public keys or know the CLI command to do so\./],
|
||||
]) {
|
||||
expect(name, pattern);
|
||||
}
|
||||
|
||||
console.log("Website inventory contract smoke passed");
|
||||
Loading…
Add table
Add a link
Reference in a new issue