mirror of
https://github.com/ModernRelay/omnigraph.git
synced 2026-06-12 01:45:14 +02:00
feat(cli): init stops scaffolding omnigraph.yaml; cluster init replaces it (RFC-008 stage 3)
omnigraph init no longer writes a legacy config into cwd (the source of the earlier test-pollution bug, and a scaffold for a deprecated file); the scaffolder is deleted. omnigraph cluster init scaffolds the replacement: a minimal valid cluster.yaml (version: 1, optional metadata.name / storage:, a commented graphs example), refusing to overwrite. The scaffold validates clean via cluster validate in the e2e. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
cd1f175396
commit
5ba9656666
5 changed files with 103 additions and 65 deletions
|
|
@ -345,6 +345,23 @@ pub(crate) enum Command {
|
|||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub(crate) enum ClusterCommand {
|
||||
/// Scaffold a minimal cluster.yaml in the config directory (refuses
|
||||
/// if one exists). The cluster checkout replaces the legacy
|
||||
/// omnigraph.yaml scaffold (RFC-008 stage 3).
|
||||
Init {
|
||||
/// Directory to scaffold into (default: .)
|
||||
#[arg(long, default_value = ".")]
|
||||
config: PathBuf,
|
||||
/// Optional deployment label (metadata.name)
|
||||
#[arg(long)]
|
||||
name: Option<String>,
|
||||
/// Optional storage root URI (s3://bucket/prefix); omit for the
|
||||
/// config-dir layout
|
||||
#[arg(long)]
|
||||
storage: Option<String>,
|
||||
#[arg(long)]
|
||||
json: bool,
|
||||
},
|
||||
/// Validate cluster.yaml and referenced schemas, queries, and policy files.
|
||||
Validate {
|
||||
/// Cluster config directory containing cluster.yaml.
|
||||
|
|
|
|||
|
|
@ -677,69 +677,6 @@ pub(crate) fn normalize_legacy_alias_uri(
|
|||
(Some(candidate), alias_args)
|
||||
}
|
||||
|
||||
pub(crate) fn scaffold_config_if_missing(uri: &str) -> Result<()> {
|
||||
let path = inferred_config_path(uri)?;
|
||||
if path.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fs::write(
|
||||
path,
|
||||
format!(
|
||||
"\
|
||||
project:
|
||||
name: Omnigraph Project
|
||||
|
||||
graphs:
|
||||
local:
|
||||
uri: {}
|
||||
# bearer_token_env: OMNIGRAPH_BEARER_TOKEN
|
||||
|
||||
server:
|
||||
graph: local
|
||||
bind: 127.0.0.1:8080
|
||||
|
||||
cli:
|
||||
graph: local
|
||||
branch: main
|
||||
output_format: table
|
||||
table_max_column_width: 80
|
||||
table_cell_layout: truncate
|
||||
|
||||
query:
|
||||
roots:
|
||||
- queries
|
||||
- .
|
||||
|
||||
aliases:
|
||||
# owner:
|
||||
# command: read
|
||||
# query: context.gq
|
||||
# name: decision_owner
|
||||
# args: [slug]
|
||||
# graph: local
|
||||
# branch: main
|
||||
# format: kv
|
||||
#
|
||||
# attach_trace:
|
||||
# command: change
|
||||
# query: mutations.gq
|
||||
# name: attach_trace
|
||||
# args: [decision_slug, trace_slug]
|
||||
# graph: local
|
||||
# branch: main
|
||||
|
||||
# auth:
|
||||
# env_file: ./.env.omni
|
||||
#
|
||||
# policy:
|
||||
# file: ./policy.yaml
|
||||
",
|
||||
yaml_string(uri),
|
||||
),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn inferred_config_path(uri: &str) -> Result<PathBuf> {
|
||||
if uri.contains("://") {
|
||||
|
|
|
|||
|
|
@ -153,7 +153,6 @@ async fn main() -> Result<()> {
|
|||
omnigraph::db::InitOptions { force },
|
||||
)
|
||||
.await?;
|
||||
scaffold_config_if_missing(&uri)?;
|
||||
println!("initialized {}", uri);
|
||||
}
|
||||
Command::Load {
|
||||
|
|
@ -1218,6 +1217,43 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
}
|
||||
Command::Cluster { command } => match command {
|
||||
ClusterCommand::Init {
|
||||
config,
|
||||
name,
|
||||
storage,
|
||||
json,
|
||||
} => {
|
||||
let target = config.join("cluster.yaml");
|
||||
if target.exists() {
|
||||
bail!(
|
||||
"'{}' already exists — cluster init refuses to overwrite",
|
||||
target.display()
|
||||
);
|
||||
}
|
||||
std::fs::create_dir_all(&config)?;
|
||||
let mut scaffold = String::from("version: 1\n");
|
||||
if let Some(name) = &name {
|
||||
scaffold.push_str(&format!("metadata:\n name: {name}\n"));
|
||||
}
|
||||
match &storage {
|
||||
Some(root) => scaffold.push_str(&format!("storage: {root}\n")),
|
||||
None => scaffold.push_str(
|
||||
"# storage: s3://bucket/prefix # omit: this folder is the storage root\n",
|
||||
),
|
||||
}
|
||||
scaffold.push_str(
|
||||
"graphs: {}\n# graphs:\n# knowledge:\n# schema: knowledge.pg\n# queries: queries/\n",
|
||||
);
|
||||
std::fs::write(&target, scaffold)?;
|
||||
if json {
|
||||
print_json(&serde_json::json!({ "created": target.display().to_string() }))?;
|
||||
} else {
|
||||
println!(
|
||||
"created {} — declare graphs, then `omnigraph cluster import` and `apply`",
|
||||
target.display()
|
||||
);
|
||||
}
|
||||
}
|
||||
ClusterCommand::Validate { config, json } => {
|
||||
let output = validate_config_dir(config);
|
||||
finish_cluster_validate(&output, json)?;
|
||||
|
|
|
|||
|
|
@ -949,3 +949,50 @@ graphs:
|
|||
let leaked = b.to_string();
|
||||
assert!(!leaked.contains("phantom") && !leaked.contains("9999"), "{leaked}");
|
||||
}
|
||||
|
||||
/// RFC-008 stage 3: `cluster init` scaffolds a minimal valid cluster.yaml
|
||||
/// (the replacement for the retired omnigraph.yaml scaffold) and refuses
|
||||
/// to overwrite.
|
||||
#[test]
|
||||
fn cluster_init_scaffolds_minimal_valid_config_and_refuses_overwrite() {
|
||||
let temp = tempdir().unwrap();
|
||||
let output = cli()
|
||||
.arg("cluster")
|
||||
.arg("init")
|
||||
.arg("--config")
|
||||
.arg(temp.path())
|
||||
.arg("--name")
|
||||
.arg("brain")
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
let text = fs::read_to_string(temp.path().join("cluster.yaml")).unwrap();
|
||||
assert!(text.contains("version: 1") && text.contains("name: brain"), "{text}");
|
||||
|
||||
// The scaffold validates clean.
|
||||
let output = cli()
|
||||
.arg("cluster")
|
||||
.arg("validate")
|
||||
.arg("--config")
|
||||
.arg(temp.path())
|
||||
.arg("--json")
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
let payload: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
|
||||
assert_eq!(payload["ok"], true, "{payload}");
|
||||
|
||||
// Refuses a second run.
|
||||
let output = cli()
|
||||
.arg("cluster")
|
||||
.arg("init")
|
||||
.arg("--config")
|
||||
.arg(temp.path())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(!output.status.success());
|
||||
assert!(
|
||||
String::from_utf8_lossy(&output.stderr).contains("refuses to overwrite"),
|
||||
"{output:?}"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ fn init_creates_graph_successfully_on_missing_local_directory() {
|
|||
assert!(stdout.contains("initialized"));
|
||||
assert!(graph.join("_schema.pg").exists());
|
||||
assert!(graph.join("__manifest").exists());
|
||||
assert!(temp.path().join("omnigraph.yaml").exists());
|
||||
// RFC-008 stage 3: init no longer scaffolds the legacy config file.
|
||||
assert!(!temp.path().join("omnigraph.yaml").exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue