feat: manage workspace backend local config
This commit is contained in:
@@ -10,9 +10,7 @@ use crate::server::{AuthConfig, ServerConfig};
|
||||
use crate::{Error, Result};
|
||||
|
||||
pub const WORKSPACE_BACKEND_CONFIG_RELATIVE_PATH: &str = ".yoi/workspace-backend.local.toml";
|
||||
pub const WORKSPACE_BACKEND_DEFAULT_CONFIG_RELATIVE_PATH: &str =
|
||||
".yoi/workspace-backend.default.toml";
|
||||
pub const WORKSPACE_BACKEND_DEFAULT_CONFIG_TEMPLATE: &str =
|
||||
pub const WORKSPACE_BACKEND_CONFIG_TEMPLATE: &str =
|
||||
include_str!("../../../resources/workspace-backend.default.toml");
|
||||
const DEFAULT_LISTEN: &str = "127.0.0.1:8787";
|
||||
const DEFAULT_FRONTEND_URL: &str = "http://127.0.0.1:5173";
|
||||
@@ -78,6 +76,61 @@ pub struct RemoteRuntimeConfigFile {
|
||||
pub token_ref: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ConfigDiff {
|
||||
pub differs: bool,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
impl ConfigDiff {
|
||||
fn new(default: &str, local: &str) -> Self {
|
||||
if default == local {
|
||||
return Self {
|
||||
differs: false,
|
||||
text: "workspace backend local config matches the packaged default\n".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
let mut text = String::from("--- packaged default\n+++ workspace local\n");
|
||||
let default_lines = default.lines().collect::<Vec<_>>();
|
||||
let local_lines = local.lines().collect::<Vec<_>>();
|
||||
let max = default_lines.len().max(local_lines.len());
|
||||
for index in 0..max {
|
||||
match (default_lines.get(index), local_lines.get(index)) {
|
||||
(Some(left), Some(right)) if left == right => {
|
||||
text.push(' ');
|
||||
text.push_str(left);
|
||||
text.push('\n');
|
||||
}
|
||||
(Some(left), Some(right)) => {
|
||||
text.push('-');
|
||||
text.push_str(left);
|
||||
text.push('\n');
|
||||
text.push('+');
|
||||
text.push_str(right);
|
||||
text.push('\n');
|
||||
}
|
||||
(Some(left), None) => {
|
||||
text.push('-');
|
||||
text.push_str(left);
|
||||
text.push('\n');
|
||||
}
|
||||
(None, Some(right)) => {
|
||||
text.push('+');
|
||||
text.push_str(right);
|
||||
text.push('\n');
|
||||
}
|
||||
(None, None) => {}
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
differs: true,
|
||||
text,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ResolvedWorkspaceBackendConfig {
|
||||
pub server: ServerConfig,
|
||||
@@ -92,14 +145,8 @@ impl WorkspaceBackendConfigFile {
|
||||
.join(WORKSPACE_BACKEND_CONFIG_RELATIVE_PATH)
|
||||
}
|
||||
|
||||
pub fn default_template_path_for_workspace(workspace_root: impl AsRef<Path>) -> PathBuf {
|
||||
workspace_root
|
||||
.as_ref()
|
||||
.join(WORKSPACE_BACKEND_DEFAULT_CONFIG_RELATIVE_PATH)
|
||||
}
|
||||
|
||||
pub fn ensure_default_template_for_workspace(workspace_root: impl AsRef<Path>) -> Result<()> {
|
||||
let path = Self::default_template_path_for_workspace(workspace_root);
|
||||
pub fn ensure_local_config_for_workspace(workspace_root: impl AsRef<Path>) -> Result<()> {
|
||||
let path = Self::path_for_workspace(workspace_root);
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
@@ -110,7 +157,7 @@ impl WorkspaceBackendConfigFile {
|
||||
{
|
||||
Ok(mut file) => {
|
||||
use std::io::Write;
|
||||
file.write_all(WORKSPACE_BACKEND_DEFAULT_CONFIG_TEMPLATE.as_bytes())?;
|
||||
file.write_all(WORKSPACE_BACKEND_CONFIG_TEMPLATE.as_bytes())?;
|
||||
file.sync_all()?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -119,6 +166,20 @@ impl WorkspaceBackendConfigFile {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_config_diff_for_workspace(workspace_root: impl AsRef<Path>) -> Result<ConfigDiff> {
|
||||
let workspace_root = workspace_root.as_ref();
|
||||
let path = Self::path_for_workspace(workspace_root);
|
||||
match fs::read_to_string(&path) {
|
||||
Ok(local) => Ok(ConfigDiff::new(WORKSPACE_BACKEND_CONFIG_TEMPLATE, &local)),
|
||||
Err(error) if error.kind() == io::ErrorKind::NotFound => Err(Error::Config(format!(
|
||||
"workspace backend local config `{}` does not exist; run `yoi workspace init --workspace {}` first",
|
||||
path.display(),
|
||||
workspace_root.display()
|
||||
))),
|
||||
Err(error) => Err(Error::Io(error)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_for_workspace(workspace_root: impl AsRef<Path>) -> Result<Self> {
|
||||
let path = Self::path_for_workspace(workspace_root);
|
||||
match fs::read_to_string(&path) {
|
||||
@@ -367,17 +428,39 @@ root = ".local-data"
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copies_default_template_without_overwriting() {
|
||||
fn copies_local_config_without_overwriting() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
WorkspaceBackendConfigFile::ensure_default_template_for_workspace(dir.path()).unwrap();
|
||||
let path = WorkspaceBackendConfigFile::default_template_path_for_workspace(dir.path());
|
||||
WorkspaceBackendConfigFile::ensure_local_config_for_workspace(dir.path()).unwrap();
|
||||
let path = WorkspaceBackendConfigFile::path_for_workspace(dir.path());
|
||||
let raw = fs::read_to_string(&path).unwrap();
|
||||
assert_eq!(raw, WORKSPACE_BACKEND_DEFAULT_CONFIG_TEMPLATE);
|
||||
assert_eq!(raw, WORKSPACE_BACKEND_CONFIG_TEMPLATE);
|
||||
WorkspaceBackendConfigFile::parse_str(&raw, &path).unwrap();
|
||||
|
||||
fs::write(&path, "# custom template\n").unwrap();
|
||||
WorkspaceBackendConfigFile::ensure_default_template_for_workspace(dir.path()).unwrap();
|
||||
assert_eq!(fs::read_to_string(&path).unwrap(), "# custom template\n");
|
||||
fs::write(&path, "# custom local config\n").unwrap();
|
||||
WorkspaceBackendConfigFile::ensure_local_config_for_workspace(dir.path()).unwrap();
|
||||
assert_eq!(
|
||||
fs::read_to_string(&path).unwrap(),
|
||||
"# custom local config\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_config_diff_reports_match_and_difference() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
WorkspaceBackendConfigFile::ensure_local_config_for_workspace(dir.path()).unwrap();
|
||||
let matched =
|
||||
WorkspaceBackendConfigFile::local_config_diff_for_workspace(dir.path()).unwrap();
|
||||
assert!(!matched.differs);
|
||||
|
||||
fs::write(
|
||||
WorkspaceBackendConfigFile::path_for_workspace(dir.path()),
|
||||
"[server]\nlisten = \"127.0.0.1:9999\"\n",
|
||||
)
|
||||
.unwrap();
|
||||
let diff = WorkspaceBackendConfigFile::local_config_diff_for_workspace(dir.path()).unwrap();
|
||||
assert!(diff.differs);
|
||||
assert!(diff.text.contains("+++ workspace local"));
|
||||
assert!(diff.text.contains("127.0.0.1:9999"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -15,8 +15,8 @@ pub mod server;
|
||||
pub mod store;
|
||||
|
||||
pub use config::{
|
||||
ResolvedWorkspaceBackendConfig, WORKSPACE_BACKEND_CONFIG_RELATIVE_PATH,
|
||||
WORKSPACE_BACKEND_DEFAULT_CONFIG_RELATIVE_PATH, WorkspaceBackendConfigFile,
|
||||
ConfigDiff, ResolvedWorkspaceBackendConfig, WORKSPACE_BACKEND_CONFIG_RELATIVE_PATH,
|
||||
WORKSPACE_BACKEND_CONFIG_TEMPLATE, WorkspaceBackendConfigFile,
|
||||
};
|
||||
pub use identity::{WORKSPACE_IDENTITY_RELATIVE_PATH, WorkspaceIdentity};
|
||||
pub use records::{
|
||||
|
||||
@@ -5,13 +5,16 @@ use std::sync::Arc;
|
||||
|
||||
use tokio::net::TcpListener;
|
||||
use yoi_workspace_server::{
|
||||
SqliteWorkspaceStore, WorkspaceBackendConfigFile, WorkspaceIdentity, serve,
|
||||
SqliteWorkspaceStore, WORKSPACE_BACKEND_CONFIG_TEMPLATE, WorkspaceBackendConfigFile,
|
||||
WorkspaceIdentity, serve,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Command {
|
||||
Serve(ServeOptions),
|
||||
Init(InitOptions),
|
||||
ConfigDefault,
|
||||
ConfigDiff(WorkspacePathOptions),
|
||||
Help,
|
||||
}
|
||||
|
||||
@@ -28,6 +31,11 @@ struct InitOptions {
|
||||
workspace: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct WorkspacePathOptions {
|
||||
workspace: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CliError(String);
|
||||
|
||||
@@ -55,6 +63,8 @@ async fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||
match parse_command(&args)? {
|
||||
Command::Serve(options) => run_serve(options).await,
|
||||
Command::Init(options) => run_init(options),
|
||||
Command::ConfigDefault => run_config_default(),
|
||||
Command::ConfigDiff(options) => run_config_diff(options),
|
||||
Command::Help => Ok(()),
|
||||
}
|
||||
}
|
||||
@@ -73,6 +83,7 @@ fn parse_command(args: &[String]) -> Result<Command, CliError> {
|
||||
}
|
||||
Ok(Command::Init(parse_init_options(rest)?))
|
||||
}
|
||||
"config" => parse_config_command(rest),
|
||||
"serve" => {
|
||||
if rest.iter().any(|arg| arg == "--help" || arg == "-h") {
|
||||
print_serve_help();
|
||||
@@ -85,14 +96,14 @@ fn parse_command(args: &[String]) -> Result<Command, CliError> {
|
||||
Ok(Command::Help)
|
||||
}
|
||||
other => Err(CliError(format!(
|
||||
"unknown command `{other}`; expected `init` or `serve`"
|
||||
"unknown command `{other}`; expected `init`, `config`, or `serve`"
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_init(options: InitOptions) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let identity = WorkspaceIdentity::load_or_init(&options.workspace)?;
|
||||
WorkspaceBackendConfigFile::ensure_default_template_for_workspace(&options.workspace)?;
|
||||
WorkspaceBackendConfigFile::ensure_local_config_for_workspace(&options.workspace)?;
|
||||
eprintln!(
|
||||
"yoi-workspace-server: initialized workspace `{}` ({})",
|
||||
options.workspace.display(),
|
||||
@@ -101,6 +112,17 @@ fn run_init(options: InitOptions) -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_config_default() -> Result<(), Box<dyn std::error::Error>> {
|
||||
print!("{WORKSPACE_BACKEND_CONFIG_TEMPLATE}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_config_diff(options: WorkspacePathOptions) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let diff = WorkspaceBackendConfigFile::local_config_diff_for_workspace(&options.workspace)?;
|
||||
print!("{}", diff.text);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_serve(options: ServeOptions) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let identity = WorkspaceIdentity::load_required(&options.workspace)?;
|
||||
let config_file = WorkspaceBackendConfigFile::load_for_workspace(&options.workspace)?;
|
||||
@@ -130,6 +152,65 @@ async fn run_serve(options: ServeOptions) -> Result<(), Box<dyn std::error::Erro
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_config_command(args: &[String]) -> Result<Command, CliError> {
|
||||
let Some((subcommand, rest)) = args.split_first() else {
|
||||
print_config_help();
|
||||
return Ok(Command::Help);
|
||||
};
|
||||
match subcommand.as_str() {
|
||||
"default" => {
|
||||
if rest.iter().any(|arg| arg == "--help" || arg == "-h") {
|
||||
print_config_help();
|
||||
return Ok(Command::Help);
|
||||
}
|
||||
if !rest.is_empty() {
|
||||
return Err(CliError(
|
||||
"config default does not accept options".to_string(),
|
||||
));
|
||||
}
|
||||
Ok(Command::ConfigDefault)
|
||||
}
|
||||
"diff" => {
|
||||
if rest.iter().any(|arg| arg == "--help" || arg == "-h") {
|
||||
print_config_help();
|
||||
return Ok(Command::Help);
|
||||
}
|
||||
Ok(Command::ConfigDiff(parse_workspace_path_options(rest)?))
|
||||
}
|
||||
"--help" | "-h" => {
|
||||
print_config_help();
|
||||
Ok(Command::Help)
|
||||
}
|
||||
other => Err(CliError(format!(
|
||||
"unknown config subcommand `{other}`; expected `default` or `diff`"
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_workspace_path_options(args: &[String]) -> Result<WorkspacePathOptions, CliError> {
|
||||
let mut workspace = std::env::current_dir()
|
||||
.map_err(|error| CliError(format!("failed to read current dir: {error}")))?;
|
||||
let mut iter = args.iter();
|
||||
while let Some(arg) = iter.next() {
|
||||
match arg.as_str() {
|
||||
"--workspace" => {
|
||||
let value = iter
|
||||
.next()
|
||||
.ok_or_else(|| CliError("--workspace requires a path".to_string()))?;
|
||||
workspace = PathBuf::from(value);
|
||||
}
|
||||
value if value.starts_with("--workspace=") => {
|
||||
workspace = PathBuf::from(value_after_equals(arg, "--workspace")?);
|
||||
}
|
||||
other => return Err(CliError(format!("unknown workspace option `{other}`"))),
|
||||
}
|
||||
}
|
||||
let workspace = workspace
|
||||
.canonicalize()
|
||||
.map_err(|error| CliError(format!("failed to canonicalize workspace: {error}")))?;
|
||||
Ok(WorkspacePathOptions { workspace })
|
||||
}
|
||||
|
||||
fn parse_init_options(args: &[String]) -> Result<InitOptions, CliError> {
|
||||
let mut workspace = std::env::current_dir()
|
||||
.map_err(|error| CliError(format!("failed to read current dir: {error}")))?;
|
||||
@@ -252,13 +333,19 @@ fn parse_listen(value: &str) -> Result<SocketAddr, CliError> {
|
||||
|
||||
fn print_help() {
|
||||
println!(
|
||||
"yoi-workspace-server\n\nUsage:\n yoi-workspace-server init [OPTIONS]\n yoi-workspace-server serve [OPTIONS]\n\nOptions:\n -h, --help Print help"
|
||||
"yoi-workspace-server\n\nUsage:\n yoi-workspace-server init [OPTIONS]\n yoi-workspace-server config <COMMAND> [OPTIONS]\n yoi-workspace-server serve [OPTIONS]\n\nOptions:\n -h, --help Print help"
|
||||
);
|
||||
}
|
||||
|
||||
fn print_init_help() {
|
||||
println!(
|
||||
"yoi-workspace-server init\n\nUsage:\n yoi-workspace-server init [OPTIONS]\n\nDescription:\n Initializes a Workspace identity and copies the default Backend config template. Does not create Backend data stores.\n\nOptions:\n --workspace <PATH> Workspace root to initialize (defaults to cwd)\n -h, --help Print help"
|
||||
"yoi-workspace-server init\n\nUsage:\n yoi-workspace-server init [OPTIONS]\n\nDescription:\n Initializes a Workspace identity and copies the packaged Backend config template to .yoi/workspace-backend.local.toml. Does not create Backend data stores.\n\nOptions:\n --workspace <PATH> Workspace root to initialize (defaults to cwd)\n -h, --help Print help"
|
||||
);
|
||||
}
|
||||
|
||||
fn print_config_help() {
|
||||
println!(
|
||||
"yoi-workspace-server config\n\nUsage:\n yoi-workspace-server config default\n yoi-workspace-server config diff [OPTIONS]\n\nDescription:\n Prints the packaged Workspace Backend config template or compares it with the workspace-local config.\n\nOptions for diff:\n --workspace <PATH> Workspace root (defaults to cwd)\n -h, --help Print help"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -272,7 +359,8 @@ fn print_serve_help() {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use yoi_workspace_server::{
|
||||
WORKSPACE_BACKEND_DEFAULT_CONFIG_RELATIVE_PATH, WORKSPACE_IDENTITY_RELATIVE_PATH,
|
||||
WORKSPACE_BACKEND_CONFIG_RELATIVE_PATH, WORKSPACE_BACKEND_CONFIG_TEMPLATE,
|
||||
WORKSPACE_IDENTITY_RELATIVE_PATH,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -284,7 +372,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn init_creates_identity_and_default_template_only() {
|
||||
fn init_creates_identity_and_local_config_only() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
run_init(InitOptions {
|
||||
workspace: temp.path().canonicalize().unwrap(),
|
||||
@@ -292,15 +380,16 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
assert!(temp.path().join(WORKSPACE_IDENTITY_RELATIVE_PATH).exists());
|
||||
assert!(
|
||||
temp.path()
|
||||
.join(WORKSPACE_BACKEND_DEFAULT_CONFIG_RELATIVE_PATH)
|
||||
.exists()
|
||||
let local_config_path = temp.path().join(WORKSPACE_BACKEND_CONFIG_RELATIVE_PATH);
|
||||
assert!(local_config_path.exists());
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(local_config_path).unwrap(),
|
||||
WORKSPACE_BACKEND_CONFIG_TEMPLATE
|
||||
);
|
||||
assert!(
|
||||
!temp
|
||||
.path()
|
||||
.join(".yoi/workspace-backend.local.toml")
|
||||
.join(".yoi/workspace-backend.default.toml")
|
||||
.exists()
|
||||
);
|
||||
assert!(!temp.path().join(".yoi/workspace.db").exists());
|
||||
|
||||
+25
-3
@@ -610,7 +610,8 @@ fn current_dir() -> Result<PathBuf, ParseError> {
|
||||
fn parse_workspace_args(args: &[String]) -> Result<Mode, ParseError> {
|
||||
let Some((subcommand, rest)) = args.split_first() else {
|
||||
return Err(ParseError(
|
||||
"yoi workspace requires `init` or `serve` (try `yoi workspace --help`)".to_string(),
|
||||
"yoi workspace requires `init`, `config`, or `serve` (try `yoi workspace --help`)"
|
||||
.to_string(),
|
||||
));
|
||||
};
|
||||
match subcommand.as_str() {
|
||||
@@ -623,6 +624,15 @@ fn parse_workspace_args(args: &[String]) -> Result<Mode, ParseError> {
|
||||
args: rest.to_vec(),
|
||||
})
|
||||
}
|
||||
"config" => {
|
||||
if rest.iter().any(|arg| arg == "--help" || arg == "-h") {
|
||||
return Ok(Mode::WorkspaceHelp);
|
||||
}
|
||||
Ok(Mode::WorkspaceServer {
|
||||
subcommand: "config".to_string(),
|
||||
args: rest.to_vec(),
|
||||
})
|
||||
}
|
||||
"serve" => {
|
||||
if rest.iter().any(|arg| arg == "--help" || arg == "-h") {
|
||||
return Ok(Mode::WorkspaceHelp);
|
||||
@@ -634,7 +644,7 @@ fn parse_workspace_args(args: &[String]) -> Result<Mode, ParseError> {
|
||||
}
|
||||
"--help" | "-h" => Ok(Mode::WorkspaceHelp),
|
||||
other => Err(ParseError(format!(
|
||||
"unknown yoi workspace subcommand `{other}`; expected `init` or `serve`"
|
||||
"unknown yoi workspace subcommand `{other}`; expected `init`, `config`, or `serve`"
|
||||
))),
|
||||
}
|
||||
}
|
||||
@@ -1015,13 +1025,14 @@ fn parse_session_id(value: &str) -> Result<SegmentId, ParseError> {
|
||||
fn print_help() {
|
||||
println!(
|
||||
"yoi\n\nUsage:\n yoi [OPTIONS]\n yoi resume [--workspace <PATH>] [--all]\n yoi panel [--workspace <PATH>]\n yoi keys\n yoi setup-model\n yoi worker [WORKER_OPTIONS]\n yoi worker delete <NAME> [--force] [--dry-run]\n yoi worker prune --older-than <DURATION> [--force] [--dry-run]\n yoi objective <COMMAND> [OPTIONS]\n yoi session analyze <SESSION_JSONL_PATH> --json\n yoi session prune --unreferenced [--older-than <DURATION>] [--force] [--dry-run]\n yoi ticket <COMMAND> [OPTIONS]\n yoi workspace init [OPTIONS]
|
||||
yoi workspace config <COMMAND> [OPTIONS]
|
||||
yoi workspace serve [OPTIONS]\n yoi plugin new <rust-component-tool|rust-component-service> <PATH> [--json]\n yoi plugin check <PATH_OR_PACKAGE> [--json]\n yoi plugin pack <PATH> [--output <FILE>] [--json]\n yoi plugin list [--workspace <PATH>] [--profile <REF>] [--json]\n yoi plugin show <REF> [--workspace <PATH>] [--profile <REF>] [--json]\n yoi mcp list [--workspace <PATH>] [--profile <REF>] [--json]\n yoi mcp show <SERVER> [--workspace <PATH>] [--profile <REF>] [--json]\n yoi mcp tools|resources|prompts [SERVER] [--workspace <PATH>] [--profile <REF>] [--json]\n yoi memory lint [OPTIONS]\n\nSurfaces:\n Console Single-Worker chat/client surface (default, --worker, yoi resume, Backend Runtime target)\n Dashboard Workspace cockpit/action surface (yoi panel)\n TUI Terminal UI implementation umbrella for Console and Dashboard\n\nOptions:\n --workspace <PATH> Runtime workspace root for default Console/--worker (defaults to cwd)\n --worker <NAME> Open the Worker Console by name (attach/restore/create)\n --socket <PATH> Attach a Worker Console to a specific socket with --worker\n --session <UUID> Resume a specific session segment in the Worker Console\n --profile <REF> Select a reusable Profile recipe\n -h, --help Print help\n"
|
||||
);
|
||||
}
|
||||
|
||||
fn print_workspace_help() {
|
||||
println!(
|
||||
"yoi workspace\n\nUsage:\n yoi workspace init [OPTIONS]\n yoi workspace serve [OPTIONS]\n\nDescription:\n Launches the separate yoi-workspace-server executable. The yoi binary does not link the workspace server crate.\n\nSubcommands:\n init Initialize .yoi/workspace.toml and the default Backend config template\n serve Serve an already initialized Workspace\n\nOptions forwarded to init/serve:\n --workspace <PATH> Workspace root (defaults to cwd)\n\nLegacy dev options forwarded to serve:\n --db <PATH> SQLite database path override\n --frontend <PATH> Static SPA build directory to serve\n --listen <ADDR> Listen address override\n -h, --help Print help\n\nEnvironment:\n YOI_WORKSPACE_SERVER_COMMAND Path to yoi-workspace-server executable override\n"
|
||||
"yoi workspace\n\nUsage:\n yoi workspace init [OPTIONS]\n yoi workspace config <COMMAND> [OPTIONS]\n yoi workspace serve [OPTIONS]\n\nDescription:\n Launches the separate yoi-workspace-server executable. The yoi binary does not link the workspace server crate.\n\nSubcommands:\n init Initialize .yoi/workspace.toml and .yoi/workspace-backend.local.toml\n config default Print the latest packaged Backend config template\n config diff Compare workspace local config with the packaged template\n serve Serve an already initialized Workspace\n\nOptions forwarded to init/config diff/serve:\n --workspace <PATH> Workspace root (defaults to cwd)\n\nLegacy dev options forwarded to serve:\n --db <PATH> SQLite database path override\n --frontend <PATH> Static SPA build directory to serve\n --listen <ADDR> Listen address override\n -h, --help Print help\n\nEnvironment:\n YOI_WORKSPACE_SERVER_COMMAND Path to yoi-workspace-server executable override\n"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1244,6 +1255,17 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_workspace_config_passthrough() {
|
||||
match parse_args_from(["workspace", "config", "diff", "--workspace", "/tmp/ws"]).unwrap() {
|
||||
Mode::WorkspaceServer { subcommand, args } => {
|
||||
assert_eq!(subcommand, "config");
|
||||
assert_eq!(args, vec!["diff", "--workspace", "/tmp/ws"]);
|
||||
}
|
||||
other => panic!("unexpected mode: {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_workspace_help() {
|
||||
assert!(matches!(
|
||||
|
||||
Reference in New Issue
Block a user