mod capability;
mod console_dev;
mod console_installation;
mod console_operator;
mod delivery;
mod extraction;
mod ga;
mod host;
mod launchpad;
mod module;
mod operator;
mod service;
mod system;
mod system_sandbox;
use clap::{Args, Parser, Subcommand};
#[derive(Debug, Parser)]
#[command(
name = "lenso",
version,
about = "Scaffold and operate Lenso backend projects",
propagate_version = true
)]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Debug, Subcommand)]
enum Command {
Serve(ServeArgs),
App {
#[command(subcommand)]
command: AppCommand,
},
Dev {
#[command(subcommand)]
command: DevCommand,
},
Agent {
#[command(subcommand)]
command: AgentCommand,
},
Capability {
#[command(subcommand)]
command: CapabilityCommand,
},
Host {
#[command(subcommand)]
command: HostCommand,
},
Module {
#[command(subcommand)]
command: ModuleCommand,
},
Service {
#[command(subcommand)]
command: ServiceCommand,
},
Operator {
#[command(subcommand)]
command: OperatorCommand,
},
System {
#[command(subcommand)]
command: SystemCommand,
},
Console {
#[command(subcommand)]
command: ConsoleCommand,
},
Ga {
#[command(subcommand)]
command: GaCommand,
},
}
#[derive(Debug, Subcommand)]
enum GaCommand {
SupportCheck(GaSupportCheckArgs),
ManifestMigrate(GaManifestMigrateArgs),
ServiceUpgrade(GaServiceUpgradeArgs),
ContractRetire(GaContractRetireArgs),
FailureEvaluate(GaFailureEvaluateArgs),
}
#[derive(Debug, Args, Clone)]
struct GaSupportCheckArgs {
#[arg(long)]
manifest: std::path::PathBuf,
#[arg(long = "component", required = true)]
components: Vec<String>,
#[arg(long)]
state_version: String,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct GaManifestMigrateArgs {
#[arg(long)]
manifest: std::path::PathBuf,
#[arg(long)]
source: std::path::PathBuf,
#[arg(long)]
target_format: String,
#[arg(long = "identity-pointer")]
identity_pointers: Vec<String>,
#[arg(long)]
target: Option<std::path::PathBuf>,
#[arg(long)]
dry_run: bool,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct GaServiceUpgradeArgs {
#[arg(long)]
manifest: std::path::PathBuf,
#[arg(long)]
input: std::path::PathBuf,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct GaContractRetireArgs {
#[arg(long)]
input: std::path::PathBuf,
#[arg(long)]
approval: Option<std::path::PathBuf>,
#[arg(long)]
output: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct GaFailureEvaluateArgs {
#[arg(long)]
input: std::path::PathBuf,
#[arg(long)]
json: bool,
}
#[derive(Debug, Subcommand)]
enum AppCommand {
Create(AppCreateArgs),
List,
Inspect(AppInspectArgs),
Add(AppAddArgs),
Compose(AppComposeArgs),
Plan(AppPlanArgs),
Upgrade(AppUpgradeArgs),
Apply(AppApplyArgs),
Next(AppNextArgs),
Explain(AppExplainArgs),
Verify(AppVerifyArgs),
Diff(AppDiffArgs),
Repair(AppRepairArgs),
}
#[derive(Debug, Args, Clone)]
struct AppCreateArgs {
dir: std::path::PathBuf,
#[arg(long, default_value = "support-desk")]
blueprint: String,
#[arg(long)]
force: bool,
}
#[derive(Debug, Args, Clone)]
struct AppInspectArgs {
blueprint: String,
}
#[derive(Debug, Args, Clone)]
struct AppAddArgs {
addon: String,
}
#[derive(Debug, Args, Clone)]
struct AppPlanArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long = "addon")]
addons: Vec<String>,
#[arg(long = "pack")]
packs: Vec<std::path::PathBuf>,
#[arg(long)]
write_plan: bool,
}
#[derive(Debug, Args, Clone)]
struct AppComposeArgs {
dir: Option<std::path::PathBuf>,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long, default_value = "support-desk")]
blueprint: String,
#[arg(long = "addon")]
addons: Vec<String>,
#[arg(long = "pack")]
packs: Vec<std::path::PathBuf>,
#[arg(long)]
apply: bool,
#[arg(long)]
write_plan: bool,
#[arg(long)]
explain: bool,
}
#[derive(Debug, Args, Clone)]
struct AppUpgradeArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
check: bool,
#[arg(long)]
write_plan: bool,
}
#[derive(Debug, Args, Clone)]
struct AppApplyArgs {
plan: std::path::PathBuf,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
dry_run: bool,
}
#[derive(Debug, Args, Clone)]
struct AppNextArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
live: bool,
}
#[derive(Debug, Args, Clone)]
struct AppExplainArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct AppVerifyArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
write_proof: bool,
}
#[derive(Debug, Args, Clone)]
struct AppDiffArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct AppRepairArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
dry_run: bool,
}
#[derive(Debug, Subcommand)]
enum DevCommand {
Up(DevUpArgs),
Status(DevStatusArgs),
Doctor(DevDoctorArgs),
Stop,
}
#[derive(Debug, Args, Clone)]
struct DevUpArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
module_services_file: Option<std::path::PathBuf>,
#[arg(long)]
workspace_file: Option<std::path::PathBuf>,
#[arg(long)]
no_workspace: bool,
#[arg(long)]
skip_db: bool,
#[arg(long)]
skip_migrate: bool,
#[arg(long)]
separate_worker: bool,
}
#[derive(Debug, Args, Clone)]
struct DevStatusArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct DevDoctorArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
live: bool,
#[arg(long)]
write_state: bool,
}
#[derive(Debug, Subcommand)]
enum AgentCommand {
Context(AgentContextArgs),
Task(AgentTaskArgs),
}
#[derive(Debug, Args, Clone)]
struct AgentContextArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
output: Option<std::path::PathBuf>,
#[arg(long)]
from_app_plan: bool,
#[arg(long = "for-module")]
for_module: Option<String>,
#[arg(long = "for-capability")]
for_capability: Option<String>,
}
#[derive(Debug, Args, Clone)]
struct AgentTaskArgs {
task: String,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
output: Option<std::path::PathBuf>,
#[arg(long)]
from_app_plan: bool,
#[arg(long = "for-module")]
for_module: Option<String>,
#[arg(long = "for-capability")]
for_capability: Option<String>,
}
#[derive(Debug, Subcommand)]
enum CapabilityCommand {
Init(CapabilityInitArgs),
Check(CapabilityCheckArgs),
Inspect(CapabilityInspectArgs),
Library {
#[command(subcommand)]
command: CapabilityLibraryCommand,
},
Fit(CapabilityFitArgs),
}
#[derive(Debug, Subcommand)]
enum CapabilityLibraryCommand {
Init(CapabilityLibraryInitArgs),
Add(CapabilityLibraryAddArgs),
List(CapabilityLibraryListArgs),
Check(CapabilityLibraryCheckArgs),
}
#[derive(Debug, Args, Clone)]
struct CapabilityInitArgs {
name: String,
#[arg(long)]
dir: std::path::PathBuf,
#[arg(long, default_value = "ts")]
lang: String,
#[arg(long = "for-blueprint")]
for_blueprint: Vec<String>,
}
#[derive(Debug, Args, Clone)]
struct CapabilityCheckArgs {
path: std::path::PathBuf,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct CapabilityInspectArgs {
path: std::path::PathBuf,
}
#[derive(Debug, Args, Clone)]
struct CapabilityLibraryInitArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct CapabilityLibraryAddArgs {
path: std::path::PathBuf,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct CapabilityLibraryListArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct CapabilityLibraryCheckArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct CapabilityFitArgs {
pack: std::path::PathBuf,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Subcommand)]
enum SystemCommand {
Dev(SystemDevArgs),
Init(SystemInitArgs),
AddService(SystemAddServiceArgs),
AddModule(SystemAddModuleArgs),
Plan(SystemPlanArgs),
Diff(SystemDiffArgs),
Apply(SystemApplyArgs),
Doctor(SystemDoctorArgs),
Release {
#[command(subcommand)]
command: SystemReleaseCommand,
},
Runbook {
#[command(subcommand)]
command: SystemRunbookCommand,
},
Graph(SystemGraphArgs),
Check(SystemCheckArgs),
}
#[derive(Debug, Args, Clone)]
struct SystemDevArgs {
#[arg(long)]
system_file: Option<std::path::PathBuf>,
#[arg(long)]
sandbox_file: Option<std::path::PathBuf>,
#[arg(long, value_name = "SCENARIO_ID")]
scenario: Option<String>,
#[arg(long)]
dry_run: bool,
#[arg(long)]
cleanup: bool,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemInitArgs {
name: String,
#[arg(long = "env")]
environments: Vec<String>,
#[arg(long)]
system_file: Option<std::path::PathBuf>,
#[arg(long)]
force: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemAddServiceArgs {
name: String,
#[arg(long, default_value = "local")]
target: String,
#[arg(long = "module")]
modules: Vec<String>,
#[arg(long)]
cwd: Option<std::path::PathBuf>,
#[arg(long)]
lang: Option<String>,
#[arg(long)]
command: Option<String>,
#[arg(long)]
ready_url: Option<String>,
#[arg(long)]
manifest: Option<String>,
#[arg(long)]
system_file: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct SystemAddModuleArgs {
name: String,
#[arg(long = "to")]
install_to: Option<String>,
#[arg(long = "capability")]
capabilities: Vec<String>,
#[arg(long = "dependency")]
dependencies: Vec<String>,
#[arg(long)]
system_file: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct SystemPlanArgs {
#[arg(long)]
system_file: Option<std::path::PathBuf>,
#[arg(long)]
check: bool,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemDiffArgs {
#[arg(long)]
system_file: Option<std::path::PathBuf>,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
check: bool,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemApplyArgs {
#[arg(long)]
system_file: Option<std::path::PathBuf>,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
dry_run: bool,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemDoctorArgs {
#[arg(long)]
system_file: Option<std::path::PathBuf>,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Subcommand)]
enum SystemReleaseCommand {
Plan(SystemReleasePlanArgs),
Check(SystemReleaseCheckArgs),
Apply(SystemReleaseApplyArgs),
Promote(SystemReleasePromoteArgs),
Rollback(SystemReleaseRollbackArgs),
History(SystemReleaseHistoryArgs),
}
#[derive(Debug, Subcommand)]
enum SystemRunbookCommand {
Generate(SystemRunbookGenerateArgs),
Check(SystemRunbookCheckArgs),
Record(SystemRunbookRecordArgs),
History(SystemRunbookHistoryArgs),
Doctor(SystemRunbookDoctorArgs),
}
#[derive(Debug, Args, Clone)]
struct SystemReleasePlanArgs {
#[arg(long = "env")]
environment_name: String,
#[arg(long)]
system_file: Option<std::path::PathBuf>,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
output: Option<std::path::PathBuf>,
#[arg(long)]
allow_drift: bool,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemReleaseCheckArgs {
plan_file: std::path::PathBuf,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemReleaseApplyArgs {
plan_file: std::path::PathBuf,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
dry_run: bool,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemReleasePromoteArgs {
#[arg(long = "from")]
from_environment: String,
#[arg(long = "to")]
to_environment: String,
#[arg(long)]
system_file: Option<std::path::PathBuf>,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
output: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemReleaseRollbackArgs {
#[arg(long = "env")]
environment_name: String,
#[arg(long)]
system_file: Option<std::path::PathBuf>,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
output: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemReleaseHistoryArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemRunbookGenerateArgs {
release_plan_file: std::path::PathBuf,
#[arg(long)]
output: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemRunbookCheckArgs {
runbook_file: std::path::PathBuf,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemRunbookRecordArgs {
runbook_file: std::path::PathBuf,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemRunbookHistoryArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemRunbookDoctorArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemGraphArgs {
#[arg(long)]
system_file: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct SystemCheckArgs {
#[arg(long)]
system_file: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Subcommand)]
enum OperatorCommand {
ExportCrd(OperatorExportCrdArgs),
}
#[derive(Debug, Args, Clone)]
pub(crate) struct OperatorExportCrdArgs {
#[arg(long)]
output: std::path::PathBuf,
#[arg(long, default_value = "ghcr.io/lenso-dev/lenso-operator:latest")]
image: String,
#[arg(long, default_value = "lenso-system")]
namespace: String,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args)]
struct ServeArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
separate_worker: bool,
#[arg(long)]
skip_db: bool,
#[arg(long)]
skip_migrate: bool,
}
#[derive(Debug, Subcommand)]
enum HostCommand {
Init {
dir: String,
#[arg(long)]
name: Option<String>,
#[arg(long)]
force: bool,
},
}
#[derive(Debug, Args, Clone)]
struct ConsoleOperatorBootstrapArgs {
#[arg(long)]
console_root: Option<std::path::PathBuf>,
#[arg(long)]
console_url: Option<String>,
#[arg(long)]
env_file: Option<std::path::PathBuf>,
#[arg(long)]
user_id: Option<String>,
#[arg(long)]
identifier: Option<String>,
#[arg(long, conflicts_with = "password_stdin")]
password_file: Option<std::path::PathBuf>,
#[arg(long, conflicts_with = "password_file")]
password_stdin: bool,
#[arg(long = "scope")]
scopes: Vec<String>,
}
#[derive(Debug, Subcommand)]
enum ConsoleOperatorCommand {
Bootstrap(ConsoleOperatorBootstrapArgs),
}
#[derive(Debug, Subcommand)]
enum ConsoleRecoveryCommand {
Reconcile(ConsoleReconcileArgs),
Activate(ConsoleActivateArgs),
RecoverActivation(ConsoleRecoverActivationArgs),
}
#[derive(Debug, Subcommand)]
enum ConsoleCommand {
Install(ConsoleChangeArgs),
Upgrade(ConsoleChangeArgs),
Backup(ConsoleBackupArgs),
Restore(ConsoleRestoreArgs),
Recovery {
#[command(subcommand)]
command: ConsoleRecoveryCommand,
},
Doctor(ConsoleDoctorArgs),
Operator {
#[command(subcommand)]
command: ConsoleOperatorCommand,
},
Dev(ConsoleDevArgs),
Package {
#[command(subcommand)]
command: ConsolePackageCommand,
},
}
#[derive(Debug, Args, Clone)]
struct ConsoleBackupArgs {
#[arg(long, default_value = ".lenso-console")]
root: std::path::PathBuf,
#[arg(long)]
env_file: std::path::PathBuf,
#[arg(long)]
output: std::path::PathBuf,
#[arg(long)]
recipient: String,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ConsoleRestoreArgs {
#[arg(long, default_value = ".lenso-console")]
root: std::path::PathBuf,
#[arg(long)]
recovery_set: std::path::PathBuf,
#[arg(long)]
current_env_file: std::path::PathBuf,
#[arg(long)]
recovery_env_file: std::path::PathBuf,
#[arg(long)]
output: Option<std::path::PathBuf>,
#[arg(long)]
apply: bool,
#[arg(long, requires = "apply")]
approve_plan_digest: Option<String>,
#[arg(long, requires = "apply")]
identity_file: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct ConsoleReconcileArgs {
#[arg(long, default_value = ".lenso-console")]
root: std::path::PathBuf,
#[arg(long)]
env_file: std::path::PathBuf,
#[arg(long)]
evidence: std::path::PathBuf,
#[arg(long)]
output: Option<std::path::PathBuf>,
#[arg(long)]
apply: bool,
#[arg(long, requires = "apply")]
approve_plan_digest: Option<String>,
}
#[derive(Debug, Args, Clone)]
struct ConsoleActivateArgs {
#[arg(long, default_value = ".lenso-console")]
root: std::path::PathBuf,
#[arg(long)]
recovery_env_file: std::path::PathBuf,
#[arg(long)]
active_env_file: std::path::PathBuf,
#[arg(long)]
output: Option<std::path::PathBuf>,
#[arg(long)]
apply: bool,
#[arg(long, requires = "apply")]
approve_plan_digest: Option<String>,
#[arg(long, requires = "apply")]
approve_authority_transfer: bool,
}
#[derive(Debug, Args, Clone)]
struct ConsoleRecoverActivationArgs {
#[arg(long, default_value = ".lenso-console")]
root: std::path::PathBuf,
#[arg(long)]
recovery_env_file: std::path::PathBuf,
#[arg(long)]
active_env_file: std::path::PathBuf,
#[arg(long)]
output: Option<std::path::PathBuf>,
#[arg(long)]
apply: bool,
#[arg(long, requires = "apply")]
approve_plan_digest: Option<String>,
#[arg(long, requires = "apply")]
approve_authority_reset: bool,
}
#[derive(Debug, Args, Clone)]
struct ConsoleChangeArgs {
#[arg(long)]
manifest: std::path::PathBuf,
#[arg(long, default_value = ".lenso-console")]
root: std::path::PathBuf,
#[arg(long)]
env_file: Option<std::path::PathBuf>,
#[arg(long)]
output: Option<std::path::PathBuf>,
#[arg(long)]
apply: bool,
#[arg(long, requires = "apply")]
approve_plan_digest: Option<String>,
#[arg(long, requires = "apply")]
approve_irreversible: bool,
}
#[derive(Debug, Args, Clone)]
struct ConsoleDoctorArgs {
#[arg(long, default_value = ".lenso-console")]
root: std::path::PathBuf,
#[arg(long)]
live_url: Option<String>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ConsoleDevArgs {
#[arg(long = "package")]
package: Option<std::path::PathBuf>,
#[arg(long)]
host: Option<String>,
#[arg(long, default_value_t = 5174)]
port: u16,
#[arg(long)]
open: bool,
#[arg(long = "runtime-console-root")]
runtime_console_root: Option<std::path::PathBuf>,
}
#[derive(Debug, Subcommand)]
enum ModuleCommand {
Create(ModuleCreateArgs),
Dev(ModuleDevArgs),
Install(RemoteModuleInstallArgs),
Enable(RemoteModuleInstallArgs),
Add(RemoteModuleInstallArgs),
Update(ModuleUpdateArgs),
Uninstall(RemoteModuleUninstallArgs),
Disable(RemoteModuleUninstallArgs),
Doctor(ModuleDoctorArgs),
Extraction {
#[command(subcommand)]
command: ModuleExtractionCommand,
},
Release {
#[command(subcommand)]
command: ModuleReleaseCommand,
},
Service {
#[command(subcommand)]
command: ModuleServiceCommand,
},
Marketplace {
#[command(subcommand)]
command: ModuleMarketplaceCommand,
},
}
#[derive(Debug, Subcommand)]
enum ModuleExtractionCommand {
Readiness(ModuleExtractionReadinessArgs),
}
#[derive(Debug, Args, Clone)]
struct ModuleExtractionReadinessArgs {
module_name: String,
#[arg(long)]
module_manifest: Option<std::path::PathBuf>,
#[arg(long)]
system_file: Option<std::path::PathBuf>,
#[arg(long)]
evidence_file: Option<std::path::PathBuf>,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
modules_root: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ModuleDevArgs {
#[arg(long)]
console: bool,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
host: Option<String>,
#[arg(long, default_value_t = 5174)]
port: u16,
#[arg(long)]
open: bool,
#[arg(long = "runtime-console-root")]
runtime_console_root: Option<std::path::PathBuf>,
}
#[derive(Debug, Subcommand)]
enum ModuleReleaseCommand {
Inspect(ModuleReleaseInspectArgs),
Check(ModuleReleaseInspectArgs),
}
#[derive(Debug, Subcommand)]
enum ModuleMarketplaceCommand {
Install(RemoteModuleInstallArgs),
}
#[derive(Debug, Subcommand)]
enum ServiceCommand {
Create(ServiceCreateArgs),
Workspace {
#[command(subcommand)]
command: ServiceWorkspaceCommand,
},
Env {
#[command(subcommand)]
command: ServiceEnvCommand,
},
Deploy {
#[command(subcommand)]
command: ServiceDeployCommand,
},
Dev(ServiceDevArgs),
Package(ServicePackageArgs),
Install(ServiceInstallArgs),
Uninstall(RemoteModuleUninstallArgs),
Diff(ServiceDiffArgs),
UpgradePlan(ServiceDiffArgs),
Upgrade(ServiceUpgradeArgs),
Rollback(ServiceRollbackArgs),
Release {
#[command(subcommand)]
command: ServiceReleaseCommand,
},
Policy {
#[command(subcommand)]
command: ServicePolicyCommand,
},
Delivery {
#[command(subcommand)]
command: ServiceDeliveryCommand,
},
Doctor(ModuleDoctorArgs),
Check(ServiceCheckArgs),
Verify(ServiceCheckArgs),
List(ModuleServiceListArgs),
Export(ModuleServiceExportArgs),
Status(ModuleServiceStatusArgs),
Logs(ModuleServiceLogsArgs),
Start(ModuleServiceStartArgs),
Stop(ModuleServiceStopArgs),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub(crate) enum ServiceLanguage {
Rust,
Ts,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub(crate) enum ServiceDeploymentTargetArg {
Kubernetes,
Operator,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub(crate) enum ServiceDeploymentSourceArg {
Kubernetes,
Operator,
}
#[derive(Debug, Args, Clone)]
struct ServiceCreateArgs {
name: String,
#[arg(long, value_enum)]
lang: ServiceLanguage,
#[arg(long)]
output_dir: Option<std::path::PathBuf>,
#[arg(long, default_value_t = 4100)]
port: u16,
#[arg(long)]
workspace_file: Option<std::path::PathBuf>,
#[arg(long)]
no_workspace: bool,
#[arg(long)]
dry_run: bool,
}
#[derive(Debug, Subcommand)]
enum ServiceWorkspaceCommand {
Init(ServiceWorkspaceInitArgs),
Add(ServiceWorkspaceAddArgs),
List(ServiceWorkspaceListArgs),
Check(ServiceWorkspaceCheckArgs),
Export(ServiceWorkspaceExportArgs),
}
#[derive(Debug, Subcommand)]
enum ServiceEnvCommand {
List(ServiceEnvListArgs),
Add(ServiceEnvAddArgs),
Remove(ServiceEnvRemoveArgs),
Verify(ServiceEnvVerifyArgs),
}
#[derive(Debug, Subcommand)]
enum ServiceDeployCommand {
Export(ServiceDeployExportArgs),
Status(ServiceDeployStatusArgs),
Wait(ServiceDeployWaitArgs),
}
#[derive(Debug, Args, Clone)]
struct ServiceWorkspaceInitArgs {
#[arg(long)]
workspace_file: Option<std::path::PathBuf>,
#[arg(long)]
force: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceWorkspaceAddArgs {
name: String,
#[arg(long)]
cwd: std::path::PathBuf,
#[arg(long, value_enum)]
lang: ServiceLanguage,
#[arg(long)]
command: String,
#[arg(long)]
ready_url: String,
#[arg(long = "module")]
modules: Vec<String>,
#[arg(long, default_value = "lenso.service.json")]
manifest: String,
#[arg(long)]
workspace_file: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct ServiceWorkspaceListArgs {
#[arg(long)]
workspace_file: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceWorkspaceCheckArgs {
service_name: Option<String>,
#[arg(long)]
workspace_file: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceWorkspaceExportArgs {
#[arg(long)]
workspace_file: Option<std::path::PathBuf>,
#[arg(long)]
output: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct ServiceEnvListArgs {
#[arg(long = "service")]
service_name: Option<String>,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceEnvAddArgs {
name: String,
#[arg(long = "service")]
service_name: String,
#[arg(long, value_enum)]
target: ServiceDeploymentTargetArg,
#[arg(long)]
namespace: Option<String>,
#[arg(long)]
kube_context: Option<String>,
#[arg(long)]
image: Option<String>,
#[arg(long)]
public_base_url: Option<String>,
#[arg(long)]
manifest_reference: Option<String>,
#[arg(long)]
release_track: Option<String>,
#[arg(long)]
replicas: Option<u32>,
#[arg(long)]
port: Option<u16>,
#[arg(long)]
ingress_host: Option<String>,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceEnvRemoveArgs {
name: String,
#[arg(long = "service")]
service_name: String,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
dry_run: bool,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceEnvVerifyArgs {
name: String,
#[arg(long = "service")]
service_name: String,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceDeployExportArgs {
service_name: String,
#[arg(long = "env")]
environment_name: String,
#[arg(long, value_enum, default_value_t = ServiceDeploymentTargetArg::Kubernetes)]
target: ServiceDeploymentTargetArg,
#[arg(long)]
output_dir: std::path::PathBuf,
#[arg(long)]
image: Option<String>,
#[arg(long)]
namespace: Option<String>,
#[arg(long)]
ingress_host: Option<String>,
#[arg(long)]
port: Option<u16>,
#[arg(long)]
replicas: Option<u32>,
#[arg(long)]
hpa: bool,
#[arg(long)]
pdb: bool,
#[arg(long)]
network_policy: bool,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceDeployStatusArgs {
service_name: String,
#[arg(long = "env")]
environment_name: String,
#[arg(long)]
from_file: Option<std::path::PathBuf>,
#[arg(long, value_enum, default_value_t = ServiceDeploymentSourceArg::Kubernetes)]
source: ServiceDeploymentSourceArg,
#[arg(long)]
write_state: bool,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceDeployWaitArgs {
service_name: String,
#[arg(long = "env")]
environment_name: String,
#[arg(long)]
from_file: Option<std::path::PathBuf>,
#[arg(long, value_enum, default_value_t = ServiceDeploymentSourceArg::Kubernetes)]
source: ServiceDeploymentSourceArg,
#[arg(long, default_value_t = 120)]
timeout_seconds: u64,
#[arg(long, default_value_t = 5)]
interval_seconds: u64,
#[arg(long)]
write_state: bool,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceDevArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
module_services_file: Option<std::path::PathBuf>,
#[arg(long)]
workspace_file: Option<std::path::PathBuf>,
#[arg(long)]
no_workspace: bool,
#[arg(long)]
skip_db: bool,
#[arg(long)]
skip_migrate: bool,
#[arg(long)]
separate_worker: bool,
}
#[derive(Debug, Args, Clone)]
struct ServicePackageArgs {
#[arg(default_value = ".")]
service_dir: std::path::PathBuf,
#[arg(long, default_value = "lenso.service.json")]
manifest: String,
#[arg(long, default_value = "dist/lenso-service")]
output_dir: std::path::PathBuf,
#[arg(long)]
check: bool,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceCheckArgs {
manifest_reference: Option<String>,
#[arg(long)]
serve_command: Option<String>,
#[arg(long)]
cwd: Option<std::path::PathBuf>,
#[arg(long)]
ready_url: Option<String>,
#[arg(long)]
manifest_url: Option<String>,
#[arg(long)]
operation: Option<String>,
#[arg(long)]
sample_input: Option<std::path::PathBuf>,
#[arg(long, default_value_t = 10_000)]
ready_timeout_ms: u64,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
env_file: Option<std::path::PathBuf>,
#[arg(long)]
module_services_file: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceDiffArgs {
service_name: String,
manifest_reference: String,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceUpgradeArgs {
service_name: String,
manifest_reference: String,
#[arg(long)]
base_url: Option<String>,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
env_file: Option<std::path::PathBuf>,
#[arg(long)]
module_services_file: Option<std::path::PathBuf>,
#[arg(long)]
dry_run: bool,
#[arg(long)]
allow_incompatible: bool,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceRollbackArgs {
service_name: String,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
env_file: Option<std::path::PathBuf>,
#[arg(long)]
module_services_file: Option<std::path::PathBuf>,
#[arg(long)]
dry_run: bool,
}
#[derive(Debug, Subcommand)]
enum ServiceReleaseCommand {
Plan(ServiceReleasePlanArgs),
Check(ServiceReleaseCheckArgs),
Apply(ServiceReleaseApplyArgs),
Promote(ServiceReleasePromoteArgs),
Rollback(ServiceReleaseRollbackArgs),
}
#[derive(Debug, Subcommand)]
enum ServicePolicyCommand {
Check(ServicePolicyCheckArgs),
}
#[derive(Debug, Subcommand)]
enum ServiceDeliveryCommand {
Assemble(ServiceDeliveryAssembleArgs),
Check(ServiceDeliveryArtifactArgs),
Diff(ServiceDeliveryDiffArgs),
Policy(ServiceDeliveryArtifactArgs),
CanIDeploy(ServiceDeliveryArtifactArgs),
DeploymentPlan(ServiceDeliveryArtifactArgs),
OperatorExport(ServiceDeliveryOperatorExportArgs),
PromotionApply(ServiceDeliveryPromotionApplyArgs),
}
#[derive(Debug, Args, Clone)]
struct ServiceDeliveryAssembleArgs {
input: std::path::PathBuf,
#[arg(long)]
output: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct ServiceDeliveryArtifactArgs {
artifact: std::path::PathBuf,
#[arg(long)]
output: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct ServiceDeliveryDiffArgs {
from: std::path::PathBuf,
to: std::path::PathBuf,
#[arg(long)]
output: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct ServiceDeliveryOperatorExportArgs {
deployment_plan: std::path::PathBuf,
#[arg(long)]
previous: Option<std::path::PathBuf>,
#[arg(long)]
output: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct ServiceDeliveryPromotionApplyArgs {
promotion_plan: std::path::PathBuf,
approval: std::path::PathBuf,
protected_evidence: std::path::PathBuf,
environment_verification: std::path::PathBuf,
source_observation: std::path::PathBuf,
source_gateway_observation: std::path::PathBuf,
target_observation: std::path::PathBuf,
operator_export: std::path::PathBuf,
#[arg(long)]
output: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct ServiceReleasePlanArgs {
service_name: String,
manifest_reference: String,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long = "env")]
environment_name: Option<String>,
#[arg(long)]
output: Option<std::path::PathBuf>,
#[arg(long)]
fail_on: Option<String>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceReleaseCheckArgs {
plan_file: std::path::PathBuf,
#[arg(long = "env")]
environment_name: Option<String>,
#[arg(long)]
fail_on: Option<String>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceReleaseApplyArgs {
plan_file: std::path::PathBuf,
#[arg(long = "env")]
environment_name: Option<String>,
#[arg(long)]
base_url: Option<String>,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
env_file: Option<std::path::PathBuf>,
#[arg(long)]
module_services_file: Option<std::path::PathBuf>,
#[arg(long)]
dry_run: bool,
#[arg(long)]
allow_incompatible: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceReleasePromoteArgs {
service_name: String,
#[arg(long = "from")]
from_environment: String,
#[arg(long = "to")]
to_environment: String,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
output: Option<std::path::PathBuf>,
#[arg(long)]
fail_on: Option<String>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceReleaseRollbackArgs {
service_name: String,
#[arg(long = "env")]
environment_name: String,
#[arg(long = "to")]
release_id: Option<String>,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
output: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ServicePolicyCheckArgs {
plan_file: std::path::PathBuf,
#[arg(long)]
fail_on: Option<String>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Subcommand)]
enum ModuleServiceCommand {
List(ModuleServiceListArgs),
Export(ModuleServiceExportArgs),
Status(ModuleServiceStatusArgs),
Logs(ModuleServiceLogsArgs),
Start(ModuleServiceStartArgs),
Stop(ModuleServiceStopArgs),
}
#[derive(Debug, Args, Clone)]
struct ModuleServiceListArgs {
module_name: Option<String>,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
module_services_file: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ModuleServiceExportArgs {
#[arg(long = "module")]
module_name: String,
#[arg(long, default_value = "compose")]
format: String,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
module_services_file: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct ModuleServiceStatusArgs {
module_name: String,
service_name: String,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
module_services_file: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ModuleServiceLogsArgs {
module_name: String,
service_name: String,
#[arg(long, default_value_t = 100)]
tail: usize,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
module_services_file: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct ModuleServiceStartArgs {
module_name: String,
service_name: String,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
module_services_file: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct ModuleServiceStopArgs {
module_name: String,
service_name: String,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
module_services_file: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct RemoteModuleInstallArgs {
manifest_reference: String,
#[arg(long, default_value = "remote")]
source: String,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
env_file: Option<std::path::PathBuf>,
#[arg(long)]
module_services_file: Option<std::path::PathBuf>,
#[arg(long)]
base_url: Option<String>,
#[arg(long)]
catalog_url: Option<String>,
#[arg(long = "profile", alias = "with", value_delimiter = ',')]
install_profiles: Vec<String>,
#[arg(long = "no-console-extension", alias = "no-console-plan", action = clap::ArgAction::SetFalse, default_value_t = true)]
console_plan: bool,
#[arg(long)]
run_install_commands: bool,
#[arg(long)]
dry_run: bool,
#[arg(long)]
allow_incompatible: bool,
}
#[derive(Debug, Args, Clone)]
struct ServiceInstallArgs {
#[command(flatten)]
install: RemoteModuleInstallArgs,
#[arg(long)]
workspace_file: Option<std::path::PathBuf>,
}
#[derive(Debug, Args, Clone)]
struct ModuleReleaseInspectArgs {
release_reference: String,
#[arg(long)]
base_url: Option<String>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct RemoteModuleUninstallArgs {
module_name: String,
#[arg(long)]
source: Option<String>,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
env_file: Option<std::path::PathBuf>,
#[arg(long)]
module_services_file: Option<std::path::PathBuf>,
#[arg(long)]
dry_run: bool,
}
#[derive(Debug, Args, Clone)]
struct ModuleUpdateArgs {
module_name: String,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
env_file: Option<std::path::PathBuf>,
#[arg(long)]
module_services_file: Option<std::path::PathBuf>,
#[arg(long)]
base_url: Option<String>,
#[arg(long = "profile", alias = "with", value_delimiter = ',')]
install_profiles: Vec<String>,
#[arg(long = "no-console-extension", alias = "no-console-plan", action = clap::ArgAction::SetFalse, default_value_t = true)]
console_plan: bool,
#[arg(long)]
run_install_commands: bool,
#[arg(long)]
dry_run: bool,
#[arg(long)]
allow_incompatible: bool,
}
#[derive(Debug, Args, Clone)]
struct ModuleDoctorArgs {
module_name: Option<String>,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
env_file: Option<std::path::PathBuf>,
#[arg(long)]
module_services_file: Option<std::path::PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Debug, Subcommand)]
enum ConsolePackageCommand {
Create(ConsolePackageCreateArgs),
ApplyPlan(ConsolePackageApplyPlanArgs),
}
#[derive(Debug, Args, Clone)]
struct ModuleCreateArgs {
module_id: String,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
output_dir: Option<std::path::PathBuf>,
#[arg(long)]
runtime_console_root: Option<std::path::PathBuf>,
#[arg(long)]
area: Option<String>,
#[arg(long)]
label: Option<String>,
#[arg(long)]
route: Option<String>,
#[arg(long)]
capability: Option<String>,
#[arg(long)]
icon: Option<String>,
#[arg(long)]
source: Option<String>,
#[arg(long)]
remote: bool,
#[arg(long)]
with_console: bool,
#[arg(long)]
package_slug: Option<String>,
#[arg(long)]
package_scope: Option<String>,
#[arg(long)]
package_name: Option<String>,
#[arg(long)]
surface_name: Option<String>,
#[arg(long)]
package_root: Option<String>,
#[arg(long)]
dry_run: bool,
}
#[derive(Debug, Args, Clone)]
struct ConsolePackageCreateArgs {
module_id: String,
#[arg(long)]
runtime_console_root: Option<std::path::PathBuf>,
#[arg(long)]
area: Option<String>,
#[arg(long)]
label: Option<String>,
#[arg(long)]
route: Option<String>,
#[arg(long)]
capability: Option<String>,
#[arg(long)]
icon: Option<String>,
#[arg(long)]
source: Option<String>,
#[arg(long)]
package_slug: Option<String>,
#[arg(long)]
package_scope: Option<String>,
#[arg(long)]
package_name: Option<String>,
#[arg(long)]
surface_name: Option<String>,
#[arg(long)]
dry_run: bool,
}
#[derive(Debug, Args, Clone)]
struct ConsolePackageApplyPlanArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long)]
runtime_console_root: Option<std::path::PathBuf>,
#[arg(long)]
install_plan_file: Option<std::path::PathBuf>,
#[arg(long)]
dependency_version: Option<String>,
#[arg(long)]
dry_run: bool,
}
impl From<&RemoteModuleInstallArgs> for module::RemoteModuleInstallOptions {
fn from(args: &RemoteModuleInstallArgs) -> Self {
Self {
allow_incompatible: args.allow_incompatible,
base_url: args.base_url.clone(),
catalog_url: args.catalog_url.clone(),
console_plan: args.console_plan,
dry_run: args.dry_run,
env_file: args.env_file.clone(),
install_profiles: args.install_profiles.clone(),
module_services_file: args.module_services_file.clone(),
repo_root: args.repo_root.clone(),
run_install_commands: args.run_install_commands,
source: args.source.clone(),
}
}
}
impl From<&ModuleReleaseInspectArgs> for module::ModuleReleaseInspectOptions {
fn from(args: &ModuleReleaseInspectArgs) -> Self {
Self {
base_url: args.base_url.clone(),
check: false,
json: args.json,
}
}
}
impl From<&RemoteModuleUninstallArgs> for module::RemoteModuleUninstallOptions {
fn from(args: &RemoteModuleUninstallArgs) -> Self {
Self {
dry_run: args.dry_run,
env_file: args.env_file.clone(),
module_services_file: args.module_services_file.clone(),
repo_root: args.repo_root.clone(),
source: args.source.clone(),
}
}
}
impl From<&ModuleUpdateArgs> for module::ModuleUpdateOptions {
fn from(args: &ModuleUpdateArgs) -> Self {
Self {
allow_incompatible: args.allow_incompatible,
base_url: args.base_url.clone(),
console_plan: args.console_plan,
dry_run: args.dry_run,
env_file: args.env_file.clone(),
install_profiles: args.install_profiles.clone(),
module_services_file: args.module_services_file.clone(),
repo_root: args.repo_root.clone(),
run_install_commands: args.run_install_commands,
}
}
}
impl From<&ModuleDoctorArgs> for module::ModuleDoctorOptions {
fn from(args: &ModuleDoctorArgs) -> Self {
Self {
env_file: args.env_file.clone(),
json: args.json,
module_name: args.module_name.clone(),
module_services_file: args.module_services_file.clone(),
repo_root: args.repo_root.clone(),
}
}
}
impl From<&ServiceCheckArgs> for module::ModuleDoctorOptions {
fn from(args: &ServiceCheckArgs) -> Self {
Self {
env_file: args.env_file.clone(),
json: args.json,
module_name: args.manifest_reference.clone(),
module_services_file: args.module_services_file.clone(),
repo_root: args.repo_root.clone(),
}
}
}
impl From<&ServiceDiffArgs> for module::ServiceDiffOptions {
fn from(args: &ServiceDiffArgs) -> Self {
Self {
json: args.json,
manifest_reference: args.manifest_reference.clone(),
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
}
}
}
impl From<&ServiceUpgradeArgs> for module::ServiceUpgradeOptions {
fn from(args: &ServiceUpgradeArgs) -> Self {
Self {
allow_incompatible: args.allow_incompatible,
base_url: args.base_url.clone(),
dry_run: args.dry_run,
env_file: args.env_file.clone(),
json: args.json,
manifest_reference: args.manifest_reference.clone(),
module_services_file: args.module_services_file.clone(),
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
}
}
}
impl From<&ServiceRollbackArgs> for module::ServiceRollbackOptions {
fn from(args: &ServiceRollbackArgs) -> Self {
Self {
dry_run: args.dry_run,
env_file: args.env_file.clone(),
module_services_file: args.module_services_file.clone(),
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
}
}
}
impl From<&ServiceEnvListArgs> for module::ServiceEnvListOptions {
fn from(args: &ServiceEnvListArgs) -> Self {
Self {
json: args.json,
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
}
}
}
impl From<&ServiceEnvAddArgs> for module::ServiceEnvAddOptions {
fn from(args: &ServiceEnvAddArgs) -> Self {
Self {
environment_name: args.name.clone(),
image: args.image.clone(),
ingress_host: args.ingress_host.clone(),
json: args.json,
kube_context: args.kube_context.clone(),
manifest_reference: args.manifest_reference.clone(),
namespace: args.namespace.clone(),
port: args.port,
public_base_url: args.public_base_url.clone(),
release_track: args.release_track.clone(),
replicas: args.replicas,
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
target: service_deployment_target_arg(args.target).to_owned(),
}
}
}
impl From<&ServiceEnvRemoveArgs> for module::ServiceEnvRemoveOptions {
fn from(args: &ServiceEnvRemoveArgs) -> Self {
Self {
dry_run: args.dry_run,
environment_name: args.name.clone(),
json: args.json,
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
}
}
}
impl From<&ServiceEnvVerifyArgs> for module::ServiceEnvVerifyOptions {
fn from(args: &ServiceEnvVerifyArgs) -> Self {
Self {
environment_name: args.name.clone(),
json: args.json,
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
}
}
}
impl From<&ServiceDeployExportArgs> for module::ServiceDeployExportOptions {
fn from(args: &ServiceDeployExportArgs) -> Self {
Self {
environment_name: args.environment_name.clone(),
image: args.image.clone(),
ingress_host: args.ingress_host.clone(),
json: args.json,
namespace: args.namespace.clone(),
output_dir: args.output_dir.clone(),
hpa: args.hpa,
port: args.port,
pdb: args.pdb,
network_policy: args.network_policy,
replicas: args.replicas,
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
target: service_deployment_target_arg(args.target).to_owned(),
}
}
}
impl From<&ServiceDeployStatusArgs> for module::ServiceDeployStatusOptions {
fn from(args: &ServiceDeployStatusArgs) -> Self {
Self {
environment_name: args.environment_name.clone(),
from_file: args.from_file.clone(),
json: args.json,
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
source: service_deployment_source_arg(args.source).to_owned(),
write_state: args.write_state,
}
}
}
impl From<&ServiceDeployWaitArgs> for module::ServiceDeployWaitOptions {
fn from(args: &ServiceDeployWaitArgs) -> Self {
Self {
environment_name: args.environment_name.clone(),
from_file: args.from_file.clone(),
interval_seconds: args.interval_seconds,
json: args.json,
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
source: service_deployment_source_arg(args.source).to_owned(),
timeout_seconds: args.timeout_seconds,
write_state: args.write_state,
}
}
}
impl From<&ServiceReleasePlanArgs> for module::ServiceReleasePlanOptions {
fn from(args: &ServiceReleasePlanArgs) -> Self {
Self {
environment_name: args.environment_name.clone(),
fail_on: args.fail_on.clone(),
json: args.json,
manifest_reference: args.manifest_reference.clone(),
output: args.output.clone(),
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
}
}
}
const fn service_deployment_target_arg(target: ServiceDeploymentTargetArg) -> &'static str {
match target {
ServiceDeploymentTargetArg::Kubernetes => "kubernetes",
ServiceDeploymentTargetArg::Operator => "operator",
}
}
const fn service_deployment_source_arg(source: ServiceDeploymentSourceArg) -> &'static str {
match source {
ServiceDeploymentSourceArg::Kubernetes => "kubernetes",
ServiceDeploymentSourceArg::Operator => "operator",
}
}
impl From<&ServiceReleaseCheckArgs> for module::ServiceReleaseCheckOptions {
fn from(args: &ServiceReleaseCheckArgs) -> Self {
Self {
environment_name: args.environment_name.clone(),
fail_on: args.fail_on.clone(),
json: args.json,
plan_file: args.plan_file.clone(),
}
}
}
impl From<&ServiceReleaseApplyArgs> for module::ServiceReleaseApplyOptions {
fn from(args: &ServiceReleaseApplyArgs) -> Self {
Self {
allow_incompatible: args.allow_incompatible,
base_url: args.base_url.clone(),
dry_run: args.dry_run,
environment_name: args.environment_name.clone(),
env_file: args.env_file.clone(),
module_services_file: args.module_services_file.clone(),
plan_file: args.plan_file.clone(),
repo_root: args.repo_root.clone(),
}
}
}
impl From<&ServiceReleasePromoteArgs> for module::ServiceReleasePromoteOptions {
fn from(args: &ServiceReleasePromoteArgs) -> Self {
Self {
fail_on: args.fail_on.clone(),
from_environment: args.from_environment.clone(),
json: args.json,
output: args.output.clone(),
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
to_environment: args.to_environment.clone(),
}
}
}
impl From<&ServiceReleaseRollbackArgs> for module::ServiceReleaseRollbackPlanOptions {
fn from(args: &ServiceReleaseRollbackArgs) -> Self {
Self {
environment_name: args.environment_name.clone(),
json: args.json,
output: args.output.clone(),
release_id: args.release_id.clone(),
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
}
}
}
impl From<&ServicePolicyCheckArgs> for module::ServiceReleaseCheckOptions {
fn from(args: &ServicePolicyCheckArgs) -> Self {
Self {
environment_name: None,
fail_on: args.fail_on.clone(),
json: args.json,
plan_file: args.plan_file.clone(),
}
}
}
impl From<&ModuleServiceListArgs> for module::ModuleServiceListOptions {
fn from(args: &ModuleServiceListArgs) -> Self {
Self {
json: args.json,
module_name: args.module_name.clone(),
module_services_file: args.module_services_file.clone(),
repo_root: args.repo_root.clone(),
}
}
}
impl From<&ModuleServiceExportArgs> for module::ModuleServiceExportOptions {
fn from(args: &ModuleServiceExportArgs) -> Self {
Self {
format: args.format.clone(),
module_name: args.module_name.clone(),
module_services_file: args.module_services_file.clone(),
repo_root: args.repo_root.clone(),
}
}
}
impl From<&ModuleServiceStatusArgs> for module::ModuleServiceStatusOptions {
fn from(args: &ModuleServiceStatusArgs) -> Self {
Self {
json: args.json,
module_name: args.module_name.clone(),
module_services_file: args.module_services_file.clone(),
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
}
}
}
impl From<&ModuleServiceLogsArgs> for module::ModuleServiceLogsOptions {
fn from(args: &ModuleServiceLogsArgs) -> Self {
Self {
module_name: args.module_name.clone(),
module_services_file: args.module_services_file.clone(),
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
tail: args.tail,
}
}
}
impl From<&ModuleServiceStartArgs> for module::ModuleServiceStartOptions {
fn from(args: &ModuleServiceStartArgs) -> Self {
Self {
module_name: args.module_name.clone(),
module_services_file: args.module_services_file.clone(),
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
}
}
}
impl From<&ModuleServiceStopArgs> for module::ModuleServiceStopOptions {
fn from(args: &ModuleServiceStopArgs) -> Self {
Self {
module_name: args.module_name.clone(),
module_services_file: args.module_services_file.clone(),
repo_root: args.repo_root.clone(),
service_name: args.service_name.clone(),
}
}
}
impl From<&ConsoleOperatorBootstrapArgs> for console_operator::BootstrapOperatorOptions {
fn from(args: &ConsoleOperatorBootstrapArgs) -> Self {
Self {
console_root: args.console_root.clone(),
console_url: args.console_url.clone(),
env_file: args.env_file.clone(),
identifier: args.identifier.clone(),
password_file: args.password_file.clone(),
password_stdin: args.password_stdin,
scopes: args.scopes.clone(),
user_id: args.user_id.clone(),
}
}
}
impl From<&ConsoleChangeArgs> for console_installation::ChangeOptions {
fn from(args: &ConsoleChangeArgs) -> Self {
Self {
manifest: args.manifest.clone(),
root: args.root.clone(),
env_file: args.env_file.clone(),
output: args.output.clone(),
apply: args.apply,
approve_plan_digest: args.approve_plan_digest.clone(),
approve_irreversible: args.approve_irreversible,
}
}
}
impl From<&ConsoleDoctorArgs> for console_installation::DoctorOptions {
fn from(args: &ConsoleDoctorArgs) -> Self {
Self {
root: args.root.clone(),
live_url: args.live_url.clone(),
json: args.json,
}
}
}
impl From<&ConsoleBackupArgs> for console_installation::BackupOptions {
fn from(args: &ConsoleBackupArgs) -> Self {
Self {
root: args.root.clone(),
env_file: args.env_file.clone(),
output: args.output.clone(),
recipient: args.recipient.clone(),
json: args.json,
}
}
}
impl From<&ConsoleRestoreArgs> for console_installation::RestoreOptions {
fn from(args: &ConsoleRestoreArgs) -> Self {
Self {
root: args.root.clone(),
recovery_set: args.recovery_set.clone(),
current_env_file: args.current_env_file.clone(),
recovery_env_file: args.recovery_env_file.clone(),
output: args.output.clone(),
apply: args.apply,
approve_plan_digest: args.approve_plan_digest.clone(),
identity_file: args.identity_file.clone(),
}
}
}
impl From<&ConsoleReconcileArgs> for console_installation::ReconcileOptions {
fn from(args: &ConsoleReconcileArgs) -> Self {
Self {
root: args.root.clone(),
env_file: args.env_file.clone(),
evidence: args.evidence.clone(),
output: args.output.clone(),
apply: args.apply,
approve_plan_digest: args.approve_plan_digest.clone(),
}
}
}
impl From<&ConsoleActivateArgs> for console_installation::ActivateOptions {
fn from(args: &ConsoleActivateArgs) -> Self {
Self {
root: args.root.clone(),
recovery_env_file: args.recovery_env_file.clone(),
active_env_file: args.active_env_file.clone(),
output: args.output.clone(),
apply: args.apply,
approve_plan_digest: args.approve_plan_digest.clone(),
approve_authority_transfer: args.approve_authority_transfer,
}
}
}
impl From<&ConsoleRecoverActivationArgs> for console_installation::RecoverActivationOptions {
fn from(args: &ConsoleRecoverActivationArgs) -> Self {
Self {
root: args.root.clone(),
recovery_env_file: args.recovery_env_file.clone(),
active_env_file: args.active_env_file.clone(),
output: args.output.clone(),
apply: args.apply,
approve_plan_digest: args.approve_plan_digest.clone(),
approve_authority_reset: args.approve_authority_reset,
}
}
}
impl From<&ModuleCreateArgs> for module::ModuleCreateOptions {
fn from(args: &ModuleCreateArgs) -> Self {
Self {
area: args.area.clone(),
capability: args.capability.clone(),
dry_run: args.dry_run,
icon: args.icon.clone(),
label: args.label.clone(),
module_id: args.module_id.clone(),
output_dir: args.output_dir.clone(),
package_name: args.package_name.clone(),
package_root: args.package_root.clone(),
package_scope: args.package_scope.clone(),
package_slug: args.package_slug.clone(),
remote: args.remote,
repo_root: args.repo_root.clone(),
route: args.route.clone(),
runtime_console_root: args.runtime_console_root.clone(),
source: args.source.clone(),
surface_name: args.surface_name.clone(),
with_console: args.with_console,
}
}
}
impl From<&ConsolePackageCreateArgs> for module::ConsolePackageCreateOptions {
fn from(args: &ConsolePackageCreateArgs) -> Self {
Self {
area: args.area.clone(),
capability: args.capability.clone(),
dry_run: args.dry_run,
icon: args.icon.clone(),
label: args.label.clone(),
module_id: args.module_id.clone(),
package_name: args.package_name.clone(),
package_scope: args.package_scope.clone(),
package_slug: args.package_slug.clone(),
route: args.route.clone(),
runtime_console_root: args.runtime_console_root.clone(),
source: args.source.clone(),
surface_name: args.surface_name.clone(),
}
}
}
impl From<&ConsolePackageApplyPlanArgs> for module::ConsolePackageApplyPlanOptions {
fn from(args: &ConsolePackageApplyPlanArgs) -> Self {
Self {
dependency_version: args.dependency_version.clone(),
dry_run: args.dry_run,
install_plan_file: args.install_plan_file.clone(),
log_next_steps: true,
repo_root: args.repo_root.clone(),
runtime_console_root: args.runtime_console_root.clone(),
}
}
}
fn looks_like_manifest_reference(reference: &str) -> bool {
reference.starts_with("http://")
|| reference.starts_with("https://")
|| reference.ends_with(".json")
|| reference.contains("/manifest")
}
fn service_check_uses_manifest(args: &ServiceCheckArgs) -> bool {
args.serve_command.is_some()
|| args.operation.is_some()
|| args.sample_input.is_some()
|| args
.manifest_reference
.as_deref()
.is_some_and(looks_like_manifest_reference)
}
fn service_verify_uses_manifest(args: &ServiceCheckArgs) -> bool {
args.manifest_reference.is_none() || service_check_uses_manifest(args)
}
async fn run_service_check_or_doctor(
args: &ServiceCheckArgs,
default_to_manifest: bool,
) -> anyhow::Result<()> {
let uses_manifest = if default_to_manifest {
service_verify_uses_manifest(args)
} else {
service_check_uses_manifest(args)
};
if uses_manifest {
module::check_service_manifest_reference(
args.manifest_reference
.as_deref()
.unwrap_or("./lenso.service.json"),
module::ServiceManifestCheckOptions {
cwd: args.cwd.clone(),
env_file: args.env_file.clone(),
json: args.json,
manifest_url: args.manifest_url.clone(),
operation: args.operation.clone(),
ready_timeout_ms: args.ready_timeout_ms,
ready_url: args.ready_url.clone(),
repo_root: args.repo_root.clone(),
sample_input: args.sample_input.clone(),
serve_command: args.serve_command.clone(),
},
)
.await?;
} else {
module::doctor_module(args.into()).await?;
}
Ok(())
}
fn warn_module_install_manifest_reference(reference: &str) {
if looks_like_manifest_reference(reference) {
eprintln!(
"warning: `lenso module install <manifest>` is deprecated for service manifests; use `lenso service install <manifest>` or `lenso module install <module-name>`."
);
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
match cli.command {
Command::Serve(args) => {
host::serve(
args.repo_root.as_deref(),
args.skip_db,
args.skip_migrate,
args.separate_worker,
)
.await?;
}
Command::App { command } => match command {
AppCommand::Create(args) => {
launchpad::create_app(launchpad::AppCreateOptions {
blueprint: args.blueprint,
dir: args.dir,
force: args.force,
})?;
}
AppCommand::List => {
launchpad::list_blueprints();
}
AppCommand::Inspect(args) => {
launchpad::inspect_blueprint(&args.blueprint)?;
}
AppCommand::Add(args) => {
launchpad::add_app_addon(launchpad::AppAddOptions { addon: args.addon })?;
}
AppCommand::Compose(args) => {
launchpad::app_compose(launchpad::AppComposeOptions {
addons: args.addons,
apply: args.apply,
blueprint: args.blueprint,
dir: args.dir,
explain: args.explain,
packs: args.packs,
repo_root: args.repo_root,
write_plan: args.write_plan,
})?;
}
AppCommand::Plan(args) => {
launchpad::app_plan(launchpad::AppPlanOptions {
addons: args.addons,
packs: args.packs,
repo_root: args.repo_root,
write_plan: args.write_plan,
})?;
}
AppCommand::Upgrade(args) => {
launchpad::app_upgrade(launchpad::AppUpgradeOptions {
check: args.check,
repo_root: args.repo_root,
write_plan: args.write_plan,
})?;
}
AppCommand::Apply(args) => {
launchpad::app_apply(launchpad::AppApplyOptions {
dry_run: args.dry_run,
plan: args.plan,
repo_root: args.repo_root,
})?;
}
AppCommand::Next(args) => {
launchpad::app_next(launchpad::AppNextOptions {
live: args.live,
repo_root: args.repo_root,
})?;
}
AppCommand::Explain(args) => {
launchpad::app_explain(launchpad::AppExplainOptions {
repo_root: args.repo_root,
})?;
}
AppCommand::Verify(args) => {
launchpad::app_verify(launchpad::AppVerifyOptions {
repo_root: args.repo_root,
write_proof: args.write_proof,
})?;
}
AppCommand::Diff(args) => {
launchpad::app_diff(launchpad::AppDiffOptions {
repo_root: args.repo_root,
})?;
}
AppCommand::Repair(args) => {
launchpad::app_repair(launchpad::AppRepairOptions {
dry_run: args.dry_run,
repo_root: args.repo_root,
})?;
}
},
Command::Dev { command } => match command {
DevCommand::Up(args) => {
service::dev_service(service::ServiceDevOptions {
module_services_file: args.module_services_file,
no_workspace: args.no_workspace,
repo_root: args.repo_root,
separate_worker: args.separate_worker,
skip_db: args.skip_db,
skip_migrate: args.skip_migrate,
workspace_file: args.workspace_file,
})
.await?;
}
DevCommand::Status(args) => {
launchpad::dev_status(launchpad::DevStatusOptions {
repo_root: args.repo_root,
})?;
}
DevCommand::Doctor(args) => {
launchpad::dev_doctor(launchpad::DevDoctorOptions {
live: args.live,
repo_root: args.repo_root,
write_state: args.write_state,
})
.await?;
}
DevCommand::Stop => {
launchpad::dev_stop();
}
},
Command::Agent { command } => match command {
AgentCommand::Context(args) => {
launchpad::agent_context(launchpad::AgentContextOptions {
for_capability: args.for_capability,
for_module: args.for_module,
from_app_plan: args.from_app_plan,
output: args.output,
repo_root: args.repo_root,
task: None,
})?;
}
AgentCommand::Task(args) => {
launchpad::agent_context(launchpad::AgentContextOptions {
for_capability: args.for_capability,
for_module: args.for_module,
from_app_plan: args.from_app_plan,
output: args.output,
repo_root: args.repo_root,
task: Some(args.task),
})?;
}
},
Command::Capability { command } => match command {
CapabilityCommand::Init(args) => {
capability::init(capability::InitOptions {
blueprints: args.for_blueprint,
dir: args.dir,
lang: args.lang,
name: args.name,
})?;
}
CapabilityCommand::Check(args) => {
capability::check(capability::CheckOptions {
json: args.json,
path: args.path,
})?;
}
CapabilityCommand::Inspect(args) => {
capability::inspect(capability::InspectOptions { path: args.path })?;
}
CapabilityCommand::Library { command } => match command {
CapabilityLibraryCommand::Init(args) => {
capability::library_init(capability::LibraryInitOptions {
repo_root: args.repo_root,
})?;
}
CapabilityLibraryCommand::Add(args) => {
capability::library_add(capability::LibraryAddOptions {
path: args.path,
repo_root: args.repo_root,
})?;
}
CapabilityLibraryCommand::List(args) => {
capability::library_list(capability::LibraryListOptions {
json: args.json,
repo_root: args.repo_root,
})?;
}
CapabilityLibraryCommand::Check(args) => {
capability::library_check(capability::LibraryCheckOptions {
json: args.json,
repo_root: args.repo_root,
})?;
}
},
CapabilityCommand::Fit(args) => {
launchpad::capability_fit(launchpad::CapabilityFitOptions {
json: args.json,
pack: args.pack,
repo_root: args.repo_root,
})?;
}
},
Command::Host { command } => match command {
HostCommand::Init { dir, name, force } => host::init(&dir, name.as_deref(), force)?,
},
Command::Console { command } => match command {
ConsoleCommand::Install(args) => console_installation::install((&args).into())?,
ConsoleCommand::Upgrade(args) => console_installation::upgrade((&args).into())?,
ConsoleCommand::Backup(args) => console_installation::backup((&args).into())?,
ConsoleCommand::Restore(args) => console_installation::restore((&args).into())?,
ConsoleCommand::Recovery { command } => match command {
ConsoleRecoveryCommand::Reconcile(args) => {
console_installation::reconcile((&args).into())?;
}
ConsoleRecoveryCommand::Activate(args) => {
console_installation::activate((&args).into())?;
}
ConsoleRecoveryCommand::RecoverActivation(args) => {
console_installation::recover_activation((&args).into())?;
}
},
ConsoleCommand::Doctor(args) => console_installation::doctor((&args).into()).await?,
ConsoleCommand::Operator { command } => match command {
ConsoleOperatorCommand::Bootstrap(args) => {
console_operator::bootstrap_operator((&args).into()).await?;
}
},
ConsoleCommand::Dev(args) => {
console_dev::run_console_dev(console_dev::ConsoleDevOptions {
cwd: None,
host: args.host,
open: args.open,
package: args.package,
port: args.port,
runtime_console_root: args.runtime_console_root,
})?;
}
ConsoleCommand::Package { command } => match command {
ConsolePackageCommand::Create(args) => {
module::create_console_package((&args).into()).await?;
}
ConsolePackageCommand::ApplyPlan(args) => {
module::apply_console_package_install_plan((&args).into()).await?;
}
},
},
Command::Ga { command } => match command {
GaCommand::SupportCheck(args) => ga::support_check(
&args.manifest,
&args.components,
&args.state_version,
args.json,
)?,
GaCommand::ManifestMigrate(args) => ga::manifest_migrate(
&args.manifest,
&args.source,
&args.target_format,
&args.identity_pointers,
args.target.as_deref(),
args.dry_run,
args.json,
)?,
GaCommand::ServiceUpgrade(args) => {
ga::service_upgrade(&args.manifest, &args.input, args.json)?;
}
GaCommand::ContractRetire(args) => ga::contract_retire(
&args.input,
args.approval.as_deref(),
args.output.as_deref(),
args.json,
)?,
GaCommand::FailureEvaluate(args) => ga::failure_evaluate(&args.input, args.json)?,
},
Command::Operator { command } => match command {
OperatorCommand::ExportCrd(args) => {
operator::export_crd_bundle((&args).into())?;
}
},
Command::System { command } => match command {
SystemCommand::Dev(args) => {
system_sandbox::dev_system(system_sandbox::SystemDevOptions {
cleanup: args.cleanup,
dry_run: args.dry_run,
json: args.json,
sandbox_file: args.sandbox_file,
scenario: args.scenario,
system_file: args.system_file,
})
.await?;
}
SystemCommand::Init(args) => {
system::init_system(system::SystemInitOptions {
environments: args.environments,
force: args.force,
name: args.name,
system_file: args.system_file,
})?;
}
SystemCommand::AddService(args) => {
system::add_system_service(system::SystemAddServiceOptions {
command: args.command,
cwd: args.cwd,
lang: args.lang,
manifest: args.manifest,
modules: args.modules,
name: args.name,
ready_url: args.ready_url,
system_file: args.system_file,
target: args.target,
})?;
}
SystemCommand::AddModule(args) => {
system::add_system_module(system::SystemAddModuleOptions {
capabilities: args.capabilities,
dependencies: args.dependencies,
install_to: args.install_to,
name: args.name,
system_file: args.system_file,
})?;
}
SystemCommand::Plan(args) => {
system::plan_system(system::SystemPlanOptions {
check: args.check,
json: args.json,
system_file: args.system_file,
})?;
}
SystemCommand::Diff(args) => {
system::diff_system(system::SystemDiffOptions {
check: args.check,
json: args.json,
repo_root: args.repo_root,
system_file: args.system_file,
})?;
}
SystemCommand::Apply(args) => {
system::apply_system(system::SystemApplyOptions {
dry_run: args.dry_run,
json: args.json,
repo_root: args.repo_root,
system_file: args.system_file,
})?;
}
SystemCommand::Doctor(args) => {
system::doctor_system(system::SystemDoctorOptions {
json: args.json,
repo_root: args.repo_root,
system_file: args.system_file,
})?;
}
SystemCommand::Release { command } => match command {
SystemReleaseCommand::Plan(args) => {
system::plan_system_release(system::SystemReleasePlanOptions {
allow_drift: args.allow_drift,
environment_name: args.environment_name,
json: args.json,
output: args.output,
repo_root: args.repo_root,
source_environment: None,
system_file: args.system_file,
})?;
}
SystemReleaseCommand::Check(args) => {
system::check_system_release(system::SystemReleaseCheckOptions {
json: args.json,
plan_file: args.plan_file,
})?;
}
SystemReleaseCommand::Apply(args) => {
system::apply_system_release(system::SystemReleaseApplyOptions {
dry_run: args.dry_run,
json: args.json,
plan_file: args.plan_file,
repo_root: args.repo_root,
})?;
}
SystemReleaseCommand::Promote(args) => {
system::promote_system_release(system::SystemReleasePromoteOptions {
from_environment: args.from_environment,
json: args.json,
output: args.output,
repo_root: args.repo_root,
system_file: args.system_file,
to_environment: args.to_environment,
})?;
}
SystemReleaseCommand::Rollback(args) => {
system::rollback_system_release(system::SystemReleaseRollbackOptions {
environment_name: args.environment_name,
json: args.json,
output: args.output,
repo_root: args.repo_root,
system_file: args.system_file,
})?;
}
SystemReleaseCommand::History(args) => {
system::history_system_release(system::SystemReleaseHistoryOptions {
json: args.json,
repo_root: args.repo_root,
})?;
}
},
SystemCommand::Runbook { command } => match command {
SystemRunbookCommand::Generate(args) => {
system::generate_system_runbook(system::SystemRunbookGenerateOptions {
json: args.json,
output: args.output,
release_plan_file: args.release_plan_file,
})?;
}
SystemRunbookCommand::Check(args) => {
system::check_system_runbook(system::SystemRunbookCheckOptions {
json: args.json,
runbook_file: args.runbook_file,
})?;
}
SystemRunbookCommand::Record(args) => {
system::record_system_runbook(system::SystemRunbookRecordOptions {
json: args.json,
repo_root: args.repo_root,
runbook_file: args.runbook_file,
})?;
}
SystemRunbookCommand::History(args) => {
system::history_system_runbook(system::SystemRunbookHistoryOptions {
json: args.json,
repo_root: args.repo_root,
})?;
}
SystemRunbookCommand::Doctor(args) => {
system::doctor_system_runbook(system::SystemRunbookDoctorOptions {
json: args.json,
repo_root: args.repo_root,
})?;
}
},
SystemCommand::Graph(args) => {
system::graph_system(system::SystemGraphOptions {
json: args.json,
system_file: args.system_file,
})?;
}
SystemCommand::Check(args) => {
system::plan_system(system::SystemPlanOptions {
check: true,
json: args.json,
system_file: args.system_file,
})?;
}
},
Command::Module { command } => match command {
ModuleCommand::Create(args) => {
module::create_module((&args).into()).await?;
}
ModuleCommand::Dev(args) => {
if !args.console {
anyhow::bail!("`lenso module dev` currently requires --console");
}
console_dev::run_console_dev(console_dev::ConsoleDevOptions {
cwd: args.repo_root,
host: args.host,
open: args.open,
package: None,
port: args.port,
runtime_console_root: args.runtime_console_root,
})?;
}
ModuleCommand::Install(args) => {
warn_module_install_manifest_reference(&args.manifest_reference);
module::install_module(&args.manifest_reference, (&args).into()).await?;
}
ModuleCommand::Enable(args) => {
module::install_module(&args.manifest_reference, (&args).into()).await?;
}
ModuleCommand::Add(args) => {
module::install_module(&args.manifest_reference, (&args).into()).await?;
}
ModuleCommand::Update(args) => {
module::update_module(&args.module_name, (&args).into()).await?;
}
ModuleCommand::Uninstall(args) => {
module::uninstall_module(&args.module_name, (&args).into()).await?;
}
ModuleCommand::Disable(args) => {
module::uninstall_module(&args.module_name, (&args).into()).await?;
}
ModuleCommand::Doctor(args) => {
module::doctor_module((&args).into()).await?;
}
ModuleCommand::Extraction { command } => match command {
ModuleExtractionCommand::Readiness(args) => {
extraction::report_module_extraction_readiness(
extraction::ModuleExtractionReadinessOptions {
evidence_file: args.evidence_file,
json: args.json,
module_manifest: args.module_manifest,
module_name: args.module_name,
modules_root: args.modules_root,
repo_root: args.repo_root,
system_file: args.system_file,
},
)?;
}
},
ModuleCommand::Release { command } => match command {
ModuleReleaseCommand::Inspect(args) => {
module::inspect_module_release(&args.release_reference, (&args).into()).await?;
}
ModuleReleaseCommand::Check(args) => {
let mut options: module::ModuleReleaseInspectOptions = (&args).into();
options.check = true;
module::inspect_module_release(&args.release_reference, options).await?;
}
},
ModuleCommand::Service { command } => match command {
ModuleServiceCommand::List(args) => {
module::list_module_services((&args).into()).await?;
}
ModuleServiceCommand::Export(args) => {
module::export_module_services((&args).into()).await?;
}
ModuleServiceCommand::Status(args) => {
module::status_module_service((&args).into()).await?;
}
ModuleServiceCommand::Logs(args) => {
module::logs_module_service((&args).into()).await?;
}
ModuleServiceCommand::Start(args) => {
module::start_module_service((&args).into()).await?;
}
ModuleServiceCommand::Stop(args) => {
module::stop_module_service((&args).into()).await?;
}
},
ModuleCommand::Marketplace { command } => match command {
ModuleMarketplaceCommand::Install(args) => {
module::install_module(&args.manifest_reference, (&args).into()).await?;
}
},
},
Command::Service { command } => match command {
ServiceCommand::Create(args) => {
service::create_service((&args).into())?;
}
ServiceCommand::Workspace { command } => match command {
ServiceWorkspaceCommand::Init(args) => {
service::init_service_workspace(service::ServiceWorkspaceInitOptions {
force: args.force,
workspace_file: args.workspace_file,
})?;
}
ServiceWorkspaceCommand::Add(args) => {
service::add_service_workspace_entry(service::ServiceWorkspaceAddOptions {
command: args.command,
cwd: args.cwd,
lang: args.lang,
manifest: args.manifest,
modules: args.modules,
name: args.name,
ready_url: args.ready_url,
workspace_file: args.workspace_file,
})?;
}
ServiceWorkspaceCommand::List(args) => {
service::list_service_workspace(service::ServiceWorkspaceListOptions {
json: args.json,
workspace_file: args.workspace_file,
})?;
}
ServiceWorkspaceCommand::Check(args) => {
service::check_service_workspace(service::ServiceWorkspaceCheckOptions {
json: args.json,
service_name: args.service_name,
workspace_file: args.workspace_file,
})
.await?;
}
ServiceWorkspaceCommand::Export(args) => {
service::export_service_workspace(service::ServiceWorkspaceExportOptions {
output: args.output,
workspace_file: args.workspace_file,
})?;
}
},
ServiceCommand::Env { command } => match command {
ServiceEnvCommand::List(args) => {
module::list_service_environments((&args).into())?;
}
ServiceEnvCommand::Add(args) => {
module::add_service_environment((&args).into())?;
}
ServiceEnvCommand::Remove(args) => {
module::remove_service_environment((&args).into())?;
}
ServiceEnvCommand::Verify(args) => {
module::verify_service_environment((&args).into())?;
}
},
ServiceCommand::Deploy { command } => match command {
ServiceDeployCommand::Export(args) => {
module::export_service_deployment((&args).into())?;
}
ServiceDeployCommand::Status(args) => {
module::status_service_deployment((&args).into())?;
}
ServiceDeployCommand::Wait(args) => {
module::wait_service_deployment((&args).into())?;
}
},
ServiceCommand::Dev(args) => {
service::dev_service((&args).into()).await?;
}
ServiceCommand::Package(args) => {
service::package_service((&args).into()).await?;
}
ServiceCommand::Install(args) => {
let mut options: module::RemoteModuleInstallOptions = (&args.install).into();
let mut manifest_reference = args.install.manifest_reference.clone();
if let Some(resolved) = service::resolve_workspace_install_reference(
&manifest_reference,
args.install.repo_root.as_deref(),
args.workspace_file.as_deref(),
)? {
manifest_reference = resolved.manifest_reference;
if options.base_url.is_none() {
options.base_url = resolved.base_url;
}
}
if options.base_url.is_none() {
options.base_url = service::infer_workspace_base_url_for_manifest(
&manifest_reference,
args.install.repo_root.as_deref(),
args.workspace_file.as_deref(),
)?;
}
module::install_module(&manifest_reference, options).await?;
}
ServiceCommand::Uninstall(args) => {
module::uninstall_remote_module(&args.module_name, (&args).into()).await?;
}
ServiceCommand::Diff(args) => {
module::diff_service((&args).into()).await?;
}
ServiceCommand::UpgradePlan(args) => {
module::diff_service((&args).into()).await?;
}
ServiceCommand::Upgrade(args) => {
module::upgrade_service((&args).into()).await?;
}
ServiceCommand::Rollback(args) => {
module::rollback_service((&args).into()).await?;
}
ServiceCommand::Release { command } => match command {
ServiceReleaseCommand::Plan(args) => {
module::plan_service_release((&args).into()).await?;
}
ServiceReleaseCommand::Check(args) => {
module::check_service_release_plan((&args).into())?;
}
ServiceReleaseCommand::Apply(args) => {
module::apply_service_release_plan((&args).into()).await?;
}
ServiceReleaseCommand::Promote(args) => {
module::promote_service_release((&args).into()).await?;
}
ServiceReleaseCommand::Rollback(args) => {
module::plan_service_release_rollback((&args).into()).await?;
}
},
ServiceCommand::Policy { command } => match command {
ServicePolicyCommand::Check(args) => {
module::policy_check_service_release_plan((&args).into())?;
}
},
ServiceCommand::Delivery { command } => match command {
ServiceDeliveryCommand::Assemble(args) => {
delivery::assemble_release(&args.input, args.output.as_deref())?;
}
ServiceDeliveryCommand::Check(args) => {
delivery::check_release(&args.artifact, args.output.as_deref())?;
}
ServiceDeliveryCommand::Diff(args) => {
delivery::diff_releases(&args.from, &args.to, args.output.as_deref())?;
}
ServiceDeliveryCommand::Policy(args) => {
delivery::check_policy_evidence(&args.artifact, args.output.as_deref())?;
}
ServiceDeliveryCommand::CanIDeploy(args) => {
delivery::can_i_deploy(&args.artifact, args.output.as_deref())?;
}
ServiceDeliveryCommand::DeploymentPlan(args) => {
delivery::check_deployment_plan(&args.artifact, args.output.as_deref())?;
}
ServiceDeliveryCommand::OperatorExport(args) => {
delivery::export_operator_resource(
&args.deployment_plan,
args.previous.as_deref(),
args.output.as_deref(),
)?;
}
ServiceDeliveryCommand::PromotionApply(args) => {
delivery::authorize_promotion_apply(
&args.promotion_plan,
&args.approval,
&args.protected_evidence,
&args.environment_verification,
&args.source_observation,
&args.source_gateway_observation,
&args.target_observation,
&args.operator_export,
args.output.as_deref(),
)?;
}
},
ServiceCommand::Doctor(args) => {
module::doctor_module((&args).into()).await?;
}
ServiceCommand::Check(args) => {
run_service_check_or_doctor(&args, false).await?;
}
ServiceCommand::Verify(args) => {
run_service_check_or_doctor(&args, true).await?;
}
ServiceCommand::List(args) => {
module::list_module_services((&args).into()).await?;
}
ServiceCommand::Export(args) => {
module::export_module_services((&args).into()).await?;
}
ServiceCommand::Status(args) => {
module::status_module_service((&args).into()).await?;
}
ServiceCommand::Logs(args) => {
module::logs_module_service((&args).into()).await?;
}
ServiceCommand::Start(args) => {
module::start_module_service((&args).into()).await?;
}
ServiceCommand::Stop(args) => {
module::stop_module_service((&args).into()).await?;
}
},
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_service_create_ts() {
let cli = Cli::parse_from([
"lenso",
"service",
"create",
"support-suite-provider",
"--lang",
"ts",
"--port",
"4110",
"--workspace-file",
"lenso.workspace.json",
]);
let Command::Service {
command: ServiceCommand::Create(args),
} = cli.command
else {
panic!("expected service create");
};
assert_eq!(args.name, "support-suite-provider");
assert_eq!(args.lang, ServiceLanguage::Ts);
assert_eq!(args.port, 4110);
assert_eq!(
args.workspace_file.as_deref(),
Some(std::path::Path::new("lenso.workspace.json"))
);
}
#[test]
fn parses_app_command_create_support_desk() {
let cli = Cli::parse_from([
"lenso",
"app",
"create",
"support-desk",
"--blueprint",
"support-desk",
]);
let Command::App {
command: AppCommand::Create(args),
} = cli.command
else {
panic!("expected app create");
};
assert_eq!(args.dir, std::path::PathBuf::from("support-desk"));
assert_eq!(args.blueprint, "support-desk");
}
#[test]
fn parses_app_list() {
let cli = Cli::parse_from(["lenso", "app", "list"]);
let Command::App {
command: AppCommand::List,
} = cli.command
else {
panic!("expected app list");
};
}
#[test]
fn parses_app_inspect() {
let cli = Cli::parse_from(["lenso", "app", "inspect", "support-desk"]);
let Command::App {
command: AppCommand::Inspect(args),
} = cli.command
else {
panic!("expected app inspect");
};
assert_eq!(args.blueprint, "support-desk");
}
#[test]
fn parses_app_add() {
let cli = Cli::parse_from(["lenso", "app", "add", "support-sla"]);
let Command::App {
command: AppCommand::Add(args),
} = cli.command
else {
panic!("expected app add");
};
assert_eq!(args.addon, "support-sla");
}
#[test]
fn parses_app_plan_write_plan() {
let cli = Cli::parse_from([
"lenso",
"app",
"plan",
"--addon",
"support-sla",
"--pack",
"./capabilities/support-sla",
"--addon",
"customer-profile",
"--write-plan",
]);
let Command::App {
command: AppCommand::Plan(args),
} = cli.command
else {
panic!("expected app plan");
};
assert!(args.write_plan);
assert_eq!(args.addons, vec!["support-sla", "customer-profile"]);
assert_eq!(
args.packs,
vec![std::path::PathBuf::from("./capabilities/support-sla")]
);
}
#[test]
fn parses_app_compose_new_app_with_two_addons() {
let cli = Cli::parse_from([
"lenso",
"app",
"compose",
"./acme-support",
"--blueprint",
"support-desk",
"--addon",
"support-sla",
"--addon",
"customer-profile",
"--apply",
]);
let Command::App {
command: AppCommand::Compose(args),
} = cli.command
else {
panic!("expected app compose");
};
assert_eq!(args.dir, Some(std::path::PathBuf::from("./acme-support")));
assert_eq!(args.addons, vec!["support-sla", "customer-profile"]);
assert!(args.apply);
}
#[test]
fn parses_app_compose_pack() {
let cli = Cli::parse_from([
"lenso",
"app",
"compose",
"./acme-support",
"--blueprint",
"support-desk",
"--pack",
"./capabilities/support-sla",
"--write-plan",
]);
let Command::App {
command: AppCommand::Compose(args),
} = cli.command
else {
panic!("expected app compose");
};
assert_eq!(
args.packs,
vec![std::path::PathBuf::from("./capabilities/support-sla")]
);
assert!(args.write_plan);
}
#[test]
fn parses_app_next_live() {
let cli = Cli::parse_from(["lenso", "app", "next", "--live"]);
let Command::App {
command: AppCommand::Next(args),
} = cli.command
else {
panic!("expected app next");
};
assert!(args.live);
}
#[test]
fn parses_app_explain_repo_root() {
let cli = Cli::parse_from(["lenso", "app", "explain", "--repo-root", "./acme"]);
let Command::App {
command: AppCommand::Explain(args),
} = cli.command
else {
panic!("expected app explain");
};
assert_eq!(args.repo_root, Some(std::path::PathBuf::from("./acme")));
}
#[test]
fn parses_app_upgrade_check() {
let cli = Cli::parse_from(["lenso", "app", "upgrade", "--check"]);
let Command::App {
command: AppCommand::Upgrade(args),
} = cli.command
else {
panic!("expected app upgrade");
};
assert!(args.check);
}
#[test]
fn parses_app_apply_dry_run() {
let cli = Cli::parse_from([
"lenso",
"app",
"apply",
".lenso/app-change-plan.json",
"--dry-run",
]);
let Command::App {
command: AppCommand::Apply(args),
} = cli.command
else {
panic!("expected app apply");
};
assert!(args.dry_run);
assert_eq!(
args.plan,
std::path::PathBuf::from(".lenso/app-change-plan.json")
);
}
#[test]
fn parses_agent_task_from_app_plan_for_module() {
let cli = Cli::parse_from([
"lenso",
"agent",
"task",
"--from-app-plan",
"--for-module",
"support-ticket",
"add private notes",
]);
let Command::Agent {
command: AgentCommand::Task(args),
} = cli.command
else {
panic!("expected agent task");
};
assert!(args.from_app_plan);
assert_eq!(args.for_module, Some("support-ticket".to_owned()));
}
#[test]
fn parses_agent_task_for_capability() {
let cli = Cli::parse_from([
"lenso",
"agent",
"task",
"--for-capability",
"support-sla",
"add enterprise escalation",
]);
let Command::Agent {
command: AgentCommand::Task(args),
} = cli.command
else {
panic!("expected agent task");
};
assert_eq!(args.for_capability.as_deref(), Some("support-sla"));
}
#[test]
fn parses_app_verify() {
let cli = Cli::parse_from(["lenso", "app", "verify", "--write-proof"]);
let Command::App {
command: AppCommand::Verify(args),
} = cli.command
else {
panic!("expected app verify");
};
assert!(args.write_proof);
}
#[test]
fn parses_app_diff() {
let cli = Cli::parse_from(["lenso", "app", "diff"]);
let Command::App {
command: AppCommand::Diff(args),
} = cli.command
else {
panic!("expected app diff");
};
assert!(args.repo_root.is_none());
}
#[test]
fn parses_app_repair_dry_run() {
let cli = Cli::parse_from(["lenso", "app", "repair", "--dry-run"]);
let Command::App {
command: AppCommand::Repair(args),
} = cli.command
else {
panic!("expected app repair");
};
assert!(args.dry_run);
}
#[test]
fn parses_dev_command_status() {
let cli = Cli::parse_from(["lenso", "dev", "status", "--repo-root", "support-desk"]);
let Command::Dev {
command: DevCommand::Status(args),
} = cli.command
else {
panic!("expected dev status");
};
assert_eq!(
args.repo_root.as_deref(),
Some(std::path::Path::new("support-desk"))
);
}
#[test]
fn parses_dev_doctor() {
let cli = Cli::parse_from(["lenso", "dev", "doctor", "--live", "--write-state"]);
let Command::Dev {
command: DevCommand::Doctor(args),
} = cli.command
else {
panic!("expected dev doctor");
};
assert!(args.live);
assert!(args.write_state);
}
#[test]
fn parses_console_dev() {
let cli = Cli::parse_from([
"lenso",
"console",
"dev",
"--package",
"packages/auth-console",
"--host",
"http://127.0.0.1:3000",
"--port",
"5175",
"--open",
"--runtime-console-root",
"../lenso-runtime-console",
]);
let Command::Console {
command: ConsoleCommand::Dev(args),
} = cli.command
else {
panic!("expected console dev");
};
assert_eq!(
args.package.as_deref(),
Some(std::path::Path::new("packages/auth-console"))
);
assert_eq!(args.host.as_deref(), Some("http://127.0.0.1:3000"));
assert_eq!(args.port, 5175);
assert!(args.open);
assert_eq!(
args.runtime_console_root.as_deref(),
Some(std::path::Path::new("../lenso-runtime-console"))
);
}
#[test]
fn parses_console_operator_bootstrap_without_legacy_alias() {
let cli = Cli::parse_from([
"lenso",
"console",
"operator",
"bootstrap",
"--console-root",
"../lenso-console",
"--identifier",
"admin@example.com",
"--console-url",
"http://127.0.0.1:3030",
"--password-file",
"./operator-password",
"--scope",
"runtime.stories.read",
]);
let Command::Console {
command:
ConsoleCommand::Operator {
command: ConsoleOperatorCommand::Bootstrap(args),
},
} = cli.command
else {
panic!("expected Console operator bootstrap");
};
assert_eq!(
args.console_root.as_deref(),
Some(std::path::Path::new("../lenso-console"))
);
assert_eq!(args.identifier.as_deref(), Some("admin@example.com"));
assert_eq!(
args.password_file.as_deref(),
Some(std::path::Path::new("./operator-password"))
);
assert_eq!(args.console_url.as_deref(), Some("http://127.0.0.1:3030"));
assert_eq!(args.scopes, ["runtime.stories.read"]);
assert!(
Cli::try_parse_from([
"lenso",
"console",
"bootstrap-admin",
"--identifier",
"admin@example.com",
])
.is_err()
);
assert!(Cli::try_parse_from(["lenso", "console", "update"]).is_err());
}
#[test]
fn parses_console_installation_authority_commands() {
let install = Cli::parse_from([
"lenso",
"console",
"install",
"--manifest",
"release.json",
"--root",
"/srv/lenso-console",
"--output",
"plan.json",
]);
let Command::Console {
command: ConsoleCommand::Install(args),
} = install.command
else {
panic!("expected Console install");
};
assert_eq!(args.manifest, std::path::Path::new("release.json"));
assert_eq!(args.root, std::path::Path::new("/srv/lenso-console"));
assert_eq!(
args.output.as_deref(),
Some(std::path::Path::new("plan.json"))
);
assert!(!args.apply);
let upgrade = Cli::parse_from([
"lenso",
"console",
"upgrade",
"--manifest",
"release.json",
"--env-file",
"console.env",
"--apply",
"--approve-plan-digest",
"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"--approve-irreversible",
]);
let Command::Console {
command: ConsoleCommand::Upgrade(args),
} = upgrade.command
else {
panic!("expected Console upgrade");
};
assert!(args.apply);
assert!(args.approve_irreversible);
assert_eq!(
args.env_file.as_deref(),
Some(std::path::Path::new("console.env"))
);
let doctor = Cli::parse_from([
"lenso",
"console",
"doctor",
"--root",
"/srv/lenso-console",
"--live-url",
"https://console.example.com",
"--json",
]);
let Command::Console {
command: ConsoleCommand::Doctor(args),
} = doctor.command
else {
panic!("expected Console doctor");
};
assert_eq!(
args.live_url.as_deref(),
Some("https://console.example.com")
);
assert!(args.json);
let restore = Cli::parse_from([
"lenso",
"console",
"restore",
"--root",
"/srv/lenso-console",
"--recovery-set",
"recovery-set",
"--current-env-file",
"current.env",
"--recovery-env-file",
"recovery.env",
"--apply",
"--approve-plan-digest",
"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"--identity-file",
"age-key.txt",
]);
let Command::Console {
command: ConsoleCommand::Restore(args),
} = restore.command
else {
panic!("expected Console restore");
};
assert!(args.apply);
assert_eq!(args.recovery_set, std::path::Path::new("recovery-set"));
assert_eq!(
args.identity_file.as_deref(),
Some(std::path::Path::new("age-key.txt"))
);
let reconcile = Cli::parse_from([
"lenso",
"console",
"recovery",
"reconcile",
"--root",
"/srv/lenso-console",
"--env-file",
"recovery.env",
"--evidence",
"reconciliation-input.json",
"--apply",
"--approve-plan-digest",
"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
]);
let Command::Console {
command:
ConsoleCommand::Recovery {
command: ConsoleRecoveryCommand::Reconcile(args),
},
} = reconcile.command
else {
panic!("expected Console recovery reconciliation");
};
assert!(args.apply);
assert_eq!(
args.evidence,
std::path::Path::new("reconciliation-input.json")
);
let activate = Cli::parse_from([
"lenso",
"console",
"recovery",
"activate",
"--root",
"/srv/lenso-console",
"--recovery-env-file",
"recovery.env",
"--active-env-file",
"active.env",
"--apply",
"--approve-plan-digest",
"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"--approve-authority-transfer",
]);
let Command::Console {
command:
ConsoleCommand::Recovery {
command: ConsoleRecoveryCommand::Activate(args),
},
} = activate.command
else {
panic!("expected Console recovery activation");
};
assert!(args.apply);
assert!(args.approve_authority_transfer);
assert_eq!(args.active_env_file, std::path::Path::new("active.env"));
let recover_activation = Cli::parse_from([
"lenso",
"console",
"recovery",
"recover-activation",
"--root",
"/srv/lenso-console",
"--recovery-env-file",
"recovery.env",
"--active-env-file",
"active.env",
"--apply",
"--approve-plan-digest",
"sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
"--approve-authority-reset",
]);
let Command::Console {
command:
ConsoleCommand::Recovery {
command: ConsoleRecoveryCommand::RecoverActivation(args),
},
} = recover_activation.command
else {
panic!("expected Console activation recovery");
};
assert!(args.apply);
assert!(args.approve_authority_reset);
assert_eq!(args.recovery_env_file, std::path::Path::new("recovery.env"));
let backup = Cli::parse_from([
"lenso",
"console",
"backup",
"--root",
"/srv/lenso-console",
"--env-file",
"console.env",
"--output",
"recovery-set",
"--recipient",
"age1example",
"--json",
]);
let Command::Console {
command: ConsoleCommand::Backup(args),
} = backup.command
else {
panic!("expected Console backup");
};
assert_eq!(args.output, std::path::Path::new("recovery-set"));
assert_eq!(args.recipient, "age1example");
assert!(args.json);
assert!(
Cli::try_parse_from([
"lenso",
"console",
"install",
"--manifest",
"release.json",
"--approve-plan-digest",
"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
])
.is_err()
);
}
#[test]
fn parses_module_dev_console() {
let cli = Cli::parse_from([
"lenso",
"module",
"dev",
"--console",
"--repo-root",
"./module-repo",
"--host",
"http://127.0.0.1:3000",
"--port",
"5176",
"--open",
"--runtime-console-root",
"../lenso-runtime-console",
]);
let Command::Module {
command: ModuleCommand::Dev(args),
} = cli.command
else {
panic!("expected module dev");
};
assert!(args.console);
assert_eq!(
args.repo_root.as_deref(),
Some(std::path::Path::new("./module-repo"))
);
assert_eq!(args.host.as_deref(), Some("http://127.0.0.1:3000"));
assert_eq!(args.port, 5176);
assert!(args.open);
assert_eq!(
args.runtime_console_root.as_deref(),
Some(std::path::Path::new("../lenso-runtime-console"))
);
}
#[test]
fn parses_agent_command_context() {
let cli = Cli::parse_from(["lenso", "agent", "context", "--output", "AGENT_CONTEXT.md"]);
let Command::Agent {
command: AgentCommand::Context(args),
} = cli.command
else {
panic!("expected agent context");
};
assert_eq!(
args.output.as_deref(),
Some(std::path::Path::new("AGENT_CONTEXT.md"))
);
}
#[test]
fn parses_capability_init_ts() {
let cli = Cli::parse_from([
"lenso",
"capability",
"init",
"support-sla",
"--dir",
"./capabilities/support-sla",
"--lang",
"ts",
"--for-blueprint",
"support-desk",
]);
let Command::Capability { command } = cli.command else {
panic!("expected capability command");
};
let CapabilityCommand::Init(args) = command else {
panic!("expected capability init");
};
assert_eq!(args.name, "support-sla");
assert_eq!(args.lang, "ts");
assert_eq!(args.for_blueprint, vec!["support-desk"]);
}
#[test]
fn parses_capability_check_json() {
let cli = Cli::parse_from([
"lenso",
"capability",
"check",
"./capabilities/support-sla",
"--json",
]);
let Command::Capability { command } = cli.command else {
panic!("expected capability command");
};
let CapabilityCommand::Check(args) = command else {
panic!("expected capability check");
};
assert_eq!(
args.path,
std::path::PathBuf::from("./capabilities/support-sla")
);
assert!(args.json);
}
#[test]
fn parses_capability_library_add() {
let cli = Cli::parse_from([
"lenso",
"capability",
"library",
"add",
"./capabilities/support-sla",
"--repo-root",
"./acme-support",
]);
let Command::Capability { command } = cli.command else {
panic!("expected capability command");
};
let CapabilityCommand::Library {
command: CapabilityLibraryCommand::Add(args),
} = command
else {
panic!("expected capability library add");
};
assert_eq!(
args.path,
std::path::PathBuf::from("./capabilities/support-sla")
);
assert_eq!(
args.repo_root.as_deref(),
Some(std::path::Path::new("./acme-support"))
);
}
#[test]
fn parses_capability_library_list_json() {
let cli = Cli::parse_from(["lenso", "capability", "library", "list", "--json"]);
let Command::Capability { command } = cli.command else {
panic!("expected capability command");
};
let CapabilityCommand::Library {
command: CapabilityLibraryCommand::List(args),
} = command
else {
panic!("expected capability library list");
};
assert!(args.json);
}
#[test]
fn parses_capability_fit_json() {
let cli = Cli::parse_from([
"lenso",
"capability",
"fit",
"support-sla",
"--repo-root",
".",
"--json",
]);
let Command::Capability { command } = cli.command else {
panic!("expected capability command");
};
let CapabilityCommand::Fit(args) = command else {
panic!("expected capability fit");
};
assert_eq!(args.pack, std::path::PathBuf::from("support-sla"));
assert!(args.json);
}
#[test]
fn parses_service_create_rust() {
let cli = Cli::parse_from([
"lenso",
"service",
"create",
"rust-audit-service",
"--lang",
"rust",
]);
let Command::Service {
command: ServiceCommand::Create(args),
} = cli.command
else {
panic!("expected service create");
};
assert_eq!(args.name, "rust-audit-service");
assert_eq!(args.lang, ServiceLanguage::Rust);
}
#[test]
fn parses_service_dev() {
let cli = Cli::parse_from([
"lenso",
"service",
"dev",
"--skip-db",
"--workspace-file",
"services.json",
]);
let Command::Service {
command: ServiceCommand::Dev(args),
} = cli.command
else {
panic!("expected service dev");
};
assert!(args.skip_db);
assert_eq!(
args.workspace_file.as_deref(),
Some(std::path::Path::new("services.json"))
);
}
#[test]
fn parses_service_workspace_add() {
let cli = Cli::parse_from([
"lenso",
"service",
"workspace",
"add",
"support-suite-provider",
"--cwd",
"services/support-suite-provider",
"--lang",
"ts",
"--command",
"pnpm start",
"--ready-url",
"http://127.0.0.1:4110/lenso/service/v1/status",
"--module",
"support-ticket",
]);
let Command::Service {
command:
ServiceCommand::Workspace {
command: ServiceWorkspaceCommand::Add(args),
},
} = cli.command
else {
panic!("expected service workspace add");
};
assert_eq!(args.name, "support-suite-provider");
assert_eq!(args.lang, ServiceLanguage::Ts);
assert_eq!(args.modules, ["support-ticket"]);
}
#[test]
fn parses_service_workspace_check() {
let cli = Cli::parse_from([
"lenso",
"service",
"workspace",
"check",
"support-suite-provider",
"--workspace-file",
".lenso/services.json",
"--json",
]);
let Command::Service {
command:
ServiceCommand::Workspace {
command: ServiceWorkspaceCommand::Check(args),
},
} = cli.command
else {
panic!("expected service workspace check");
};
assert_eq!(args.service_name.as_deref(), Some("support-suite-provider"));
assert_eq!(
args.workspace_file.as_deref(),
Some(std::path::Path::new(".lenso/services.json"))
);
assert!(args.json);
}
#[test]
fn parses_service_workspace_export() {
let cli = Cli::parse_from([
"lenso",
"service",
"workspace",
"export",
"--workspace-file",
"lenso.workspace.json",
"--output",
".lenso/module-services.json",
]);
let Command::Service {
command:
ServiceCommand::Workspace {
command: ServiceWorkspaceCommand::Export(args),
},
} = cli.command
else {
panic!("expected service workspace export");
};
assert_eq!(
args.workspace_file.as_deref(),
Some(std::path::Path::new("lenso.workspace.json"))
);
assert_eq!(
args.output.as_deref(),
Some(std::path::Path::new(".lenso/module-services.json"))
);
}
#[test]
fn parses_service_env_add() {
let cli = Cli::parse_from([
"lenso",
"service",
"env",
"add",
"staging",
"--service",
"support-suite-provider",
"--target",
"kubernetes",
"--namespace",
"lenso-staging",
"--image",
"ghcr.io/acme/support-suite-provider:0.4.0",
"--public-base-url",
"https://support-staging.example.com",
"--replicas",
"2",
"--port",
"4110",
]);
let Command::Service {
command:
ServiceCommand::Env {
command: ServiceEnvCommand::Add(args),
},
} = cli.command
else {
panic!("expected service env add");
};
assert_eq!(args.name, "staging");
assert_eq!(args.service_name, "support-suite-provider");
assert_eq!(args.target, ServiceDeploymentTargetArg::Kubernetes);
assert_eq!(args.namespace.as_deref(), Some("lenso-staging"));
assert_eq!(args.replicas, Some(2));
assert_eq!(args.port, Some(4110));
}
#[test]
fn parses_service_deploy_commands() {
let export = Cli::parse_from([
"lenso",
"service",
"deploy",
"export",
"support-suite-provider",
"--env",
"staging",
"--target",
"kubernetes",
"--output-dir",
"dist/kubernetes/staging",
"--image",
"ghcr.io/acme/support-suite-provider:0.4.0",
]);
let Command::Service {
command:
ServiceCommand::Deploy {
command: ServiceDeployCommand::Export(export_args),
},
} = export.command
else {
panic!("expected service deploy export");
};
assert_eq!(export_args.service_name, "support-suite-provider");
assert_eq!(export_args.environment_name, "staging");
assert_eq!(export_args.target, ServiceDeploymentTargetArg::Kubernetes);
let status = Cli::parse_from([
"lenso",
"service",
"deploy",
"status",
"support-suite-provider",
"--env",
"staging",
"--from-file",
"deployment.json",
"--write-state",
]);
let Command::Service {
command:
ServiceCommand::Deploy {
command: ServiceDeployCommand::Status(status_args),
},
} = status.command
else {
panic!("expected service deploy status");
};
assert_eq!(status_args.environment_name, "staging");
assert!(status_args.write_state);
let wait = Cli::parse_from([
"lenso",
"service",
"deploy",
"wait",
"support-suite-provider",
"--env",
"staging",
"--source",
"kubernetes",
"--timeout-seconds",
"30",
"--interval-seconds",
"2",
"--write-state",
]);
let Command::Service {
command:
ServiceCommand::Deploy {
command: ServiceDeployCommand::Wait(wait_args),
},
} = wait.command
else {
panic!("expected service deploy wait");
};
assert_eq!(wait_args.environment_name, "staging");
assert_eq!(wait_args.timeout_seconds, 30);
assert_eq!(wait_args.interval_seconds, 2);
assert!(wait_args.write_state);
}
#[test]
fn parses_operator_export_crd() {
let cli = Cli::parse_from([
"lenso",
"operator",
"export-crd",
"--output",
"dist/lenso-operator/crds",
"--namespace",
"lenso-system",
]);
let Command::Operator {
command: OperatorCommand::ExportCrd(args),
} = cli.command
else {
panic!("expected operator export-crd");
};
assert_eq!(
args.output,
std::path::PathBuf::from("dist/lenso-operator/crds")
);
assert_eq!(args.namespace, "lenso-system");
}
#[test]
fn parses_service_deploy_operator_target_and_source() {
let export = Cli::parse_from([
"lenso",
"service",
"deploy",
"export",
"support-suite-provider",
"--env",
"staging",
"--target",
"operator",
"--output-dir",
"dist/operator/staging",
]);
let Command::Service {
command:
ServiceCommand::Deploy {
command: ServiceDeployCommand::Export(export_args),
},
} = export.command
else {
panic!("expected service deploy export");
};
assert_eq!(export_args.target, ServiceDeploymentTargetArg::Operator);
let status = Cli::parse_from([
"lenso",
"service",
"deploy",
"status",
"support-suite-provider",
"--env",
"staging",
"--source",
"operator",
]);
let Command::Service {
command:
ServiceCommand::Deploy {
command: ServiceDeployCommand::Status(status_args),
},
} = status.command
else {
panic!("expected service deploy status");
};
assert_eq!(status_args.source, ServiceDeploymentSourceArg::Operator);
}
#[test]
fn parses_service_install_workspace_file() {
let cli = Cli::parse_from([
"lenso",
"service",
"install",
"./services/support-suite-provider/lenso.service.json",
"--workspace-file",
".lenso/services.json",
]);
let Command::Service {
command: ServiceCommand::Install(args),
} = cli.command
else {
panic!("expected service install");
};
assert_eq!(
args.workspace_file.as_deref(),
Some(std::path::Path::new(".lenso/services.json"))
);
}
#[test]
fn parses_module_enable_disable_aliases() {
let cli = Cli::parse_from(["lenso", "module", "enable", "support-ticket"]);
let Command::Module {
command: ModuleCommand::Enable(enable_args),
} = cli.command
else {
panic!("expected module enable");
};
assert_eq!(enable_args.manifest_reference, "support-ticket");
let cli = Cli::parse_from(["lenso", "module", "disable", "support-ticket"]);
let Command::Module {
command: ModuleCommand::Disable(disable_args),
} = cli.command
else {
panic!("expected module disable");
};
assert_eq!(disable_args.module_name, "support-ticket");
}
#[test]
fn parses_service_package() {
let cli = Cli::parse_from([
"lenso",
"service",
"package",
"../services/support-suite-provider",
"--manifest",
"service.json",
"--output-dir",
"../dist/services",
"--check",
"--json",
]);
let Command::Service {
command: ServiceCommand::Package(args),
} = cli.command
else {
panic!("expected service package");
};
assert_eq!(
args.service_dir.as_path(),
std::path::Path::new("../services/support-suite-provider")
);
assert_eq!(args.manifest, "service.json");
assert_eq!(
args.output_dir.as_path(),
std::path::Path::new("../dist/services")
);
assert!(args.check);
assert!(args.json);
}
#[test]
fn parses_service_check_manifest_reference() {
let cli = Cli::parse_from([
"lenso",
"service",
"check",
"./lenso.service.json",
"--json",
"--serve-command",
"pnpm start",
]);
let Command::Service {
command: ServiceCommand::Check(args),
} = cli.command
else {
panic!("expected service check");
};
assert_eq!(
args.manifest_reference.as_deref(),
Some("./lenso.service.json")
);
assert!(args.json);
assert_eq!(args.serve_command.as_deref(), Some("pnpm start"));
}
#[test]
fn parses_service_verify_manifest_reference() {
let cli = Cli::parse_from([
"lenso",
"service",
"verify",
"./lenso.service.json",
"--json",
"--serve-command",
"pnpm start",
]);
let Command::Service {
command: ServiceCommand::Verify(args),
} = cli.command
else {
panic!("expected service verify");
};
assert_eq!(
args.manifest_reference.as_deref(),
Some("./lenso.service.json")
);
assert!(args.json);
assert_eq!(args.serve_command.as_deref(), Some("pnpm start"));
assert!(service_verify_uses_manifest(&args));
}
#[test]
fn parses_service_check_operation_filter_and_sample_input() {
let cli = Cli::parse_from([
"lenso",
"service",
"check",
"./lenso.service.json",
"--operation",
"support-ticket/http/GET:/tickets",
"--sample-input",
"fixtures/probe.json",
]);
let Command::Service {
command: ServiceCommand::Check(args),
} = cli.command
else {
panic!("expected service check");
};
assert_eq!(
args.operation.as_deref(),
Some("support-ticket/http/GET:/tickets")
);
assert_eq!(
args.sample_input.as_deref(),
Some(std::path::Path::new("fixtures/probe.json"))
);
}
#[test]
fn service_check_operation_options_use_manifest_check_mode() {
let cli = Cli::parse_from(["lenso", "service", "check", "--operation", "missing"]);
let Command::Service {
command: ServiceCommand::Check(args),
} = cli.command
else {
panic!("expected service check");
};
assert!(service_check_uses_manifest(&args));
assert_eq!(args.manifest_reference.as_deref(), None);
let cli = Cli::parse_from([
"lenso",
"service",
"check",
"--sample-input",
"fixtures/probe.json",
]);
let Command::Service {
command: ServiceCommand::Check(args),
} = cli.command
else {
panic!("expected service check");
};
assert!(service_check_uses_manifest(&args));
assert_eq!(args.manifest_reference.as_deref(), None);
}
#[test]
fn service_verify_defaults_to_manifest_but_accepts_provider_name() {
let cli = Cli::parse_from(["lenso", "service", "verify"]);
let Command::Service {
command: ServiceCommand::Verify(args),
} = cli.command
else {
panic!("expected service verify");
};
assert!(service_verify_uses_manifest(&args));
let cli = Cli::parse_from(["lenso", "service", "verify", "support-ticket"]);
let Command::Service {
command: ServiceCommand::Verify(args),
} = cli.command
else {
panic!("expected service verify");
};
assert!(!service_verify_uses_manifest(&args));
}
#[test]
fn parses_service_delivery_lifecycle_commands() {
let diff = Cli::parse_from([
"lenso",
"service",
"diff",
"support-suite-provider",
"./lenso.service.json",
]);
let Command::Service {
command: ServiceCommand::Diff(diff_args),
} = diff.command
else {
panic!("expected service diff");
};
assert_eq!(diff_args.service_name, "support-suite-provider");
let upgrade_plan = Cli::parse_from([
"lenso",
"service",
"upgrade-plan",
"support-suite-provider",
"./lenso.service.json",
"--json",
]);
let Command::Service {
command: ServiceCommand::UpgradePlan(upgrade_plan_args),
} = upgrade_plan.command
else {
panic!("expected service upgrade-plan");
};
assert_eq!(upgrade_plan_args.service_name, "support-suite-provider");
assert!(upgrade_plan_args.json);
let upgrade = Cli::parse_from([
"lenso",
"service",
"upgrade",
"support-suite-provider",
"./lenso.service.json",
"--dry-run",
]);
let Command::Service {
command: ServiceCommand::Upgrade(upgrade_args),
} = upgrade.command
else {
panic!("expected service upgrade");
};
assert!(upgrade_args.dry_run);
let rollback = Cli::parse_from([
"lenso",
"service",
"rollback",
"support-suite-provider",
"--dry-run",
]);
let Command::Service {
command: ServiceCommand::Rollback(rollback_args),
} = rollback.command
else {
panic!("expected service rollback");
};
assert!(rollback_args.dry_run);
}
#[test]
fn parses_service_release_and_policy_commands() {
let plan = Cli::parse_from([
"lenso",
"service",
"release",
"plan",
"support-suite-provider",
"./lenso.service-package.json",
"--output",
".lenso/releases/support.plan.json",
"--env",
"staging",
"--fail-on",
"breaking",
"--json",
]);
let Command::Service {
command:
ServiceCommand::Release {
command: ServiceReleaseCommand::Plan(plan_args),
},
} = plan.command
else {
panic!("expected service release plan");
};
assert_eq!(plan_args.service_name, "support-suite-provider");
assert_eq!(plan_args.environment_name.as_deref(), Some("staging"));
assert_eq!(plan_args.fail_on.as_deref(), Some("breaking"));
assert!(plan_args.json);
let check = Cli::parse_from([
"lenso",
"service",
"release",
"check",
".lenso/releases/support.plan.json",
"--fail-on",
"needs_attention",
]);
let Command::Service {
command:
ServiceCommand::Release {
command: ServiceReleaseCommand::Check(check_args),
},
} = check.command
else {
panic!("expected service release check");
};
assert_eq!(check_args.fail_on.as_deref(), Some("needs_attention"));
let apply = Cli::parse_from([
"lenso",
"service",
"release",
"apply",
".lenso/releases/support.plan.json",
"--env",
"staging",
"--dry-run",
]);
let Command::Service {
command:
ServiceCommand::Release {
command: ServiceReleaseCommand::Apply(apply_args),
},
} = apply.command
else {
panic!("expected service release apply");
};
assert!(apply_args.dry_run);
assert_eq!(apply_args.environment_name.as_deref(), Some("staging"));
let promote = Cli::parse_from([
"lenso",
"service",
"release",
"promote",
"support-suite-provider",
"--from",
"staging",
"--to",
"prod",
"--output",
".lenso/releases/support.prod.plan.json",
]);
let Command::Service {
command:
ServiceCommand::Release {
command: ServiceReleaseCommand::Promote(promote_args),
},
} = promote.command
else {
panic!("expected service release promote");
};
assert_eq!(promote_args.from_environment, "staging");
assert_eq!(promote_args.to_environment, "prod");
let rollback = Cli::parse_from([
"lenso",
"service",
"release",
"rollback",
"support-suite-provider",
"--env",
"prod",
"--to",
"rel_1",
]);
let Command::Service {
command:
ServiceCommand::Release {
command: ServiceReleaseCommand::Rollback(rollback_args),
},
} = rollback.command
else {
panic!("expected service release rollback");
};
assert_eq!(rollback_args.environment_name, "prod");
assert_eq!(rollback_args.release_id.as_deref(), Some("rel_1"));
let policy = Cli::parse_from([
"lenso",
"service",
"policy",
"check",
".lenso/releases/support.plan.json",
"--json",
]);
let Command::Service {
command:
ServiceCommand::Policy {
command: ServicePolicyCommand::Check(policy_args),
},
} = policy.command
else {
panic!("expected service policy check");
};
assert!(policy_args.json);
}
#[test]
fn parses_autonomous_service_delivery_commands() {
let assemble = Cli::parse_from([
"lenso",
"service",
"delivery",
"assemble",
"release-input.json",
"--output",
"release.json",
]);
let Command::Service {
command:
ServiceCommand::Delivery {
command: ServiceDeliveryCommand::Assemble(args),
},
} = assemble.command
else {
panic!("expected service delivery assemble");
};
assert_eq!(args.input, std::path::PathBuf::from("release-input.json"));
assert_eq!(args.output, Some(std::path::PathBuf::from("release.json")));
let export = Cli::parse_from([
"lenso",
"service",
"delivery",
"operator-export",
"production.deployment-plan.json",
"--previous",
"previous-export.json",
]);
let Command::Service {
command:
ServiceCommand::Delivery {
command: ServiceDeliveryCommand::OperatorExport(args),
},
} = export.command
else {
panic!("expected service delivery operator export");
};
assert_eq!(
args.deployment_plan,
std::path::PathBuf::from("production.deployment-plan.json")
);
assert_eq!(
args.previous,
Some(std::path::PathBuf::from("previous-export.json"))
);
let promotion_apply = Cli::parse_from([
"lenso",
"service",
"delivery",
"promotion-apply",
"promotion-plan.json",
"approval.json",
"protected-evidence.json",
"environment-verification.json",
"source-observation.json",
"source-gateway-observation.json",
"target-observation.json",
"operator-export.json",
"--output",
"authorization.json",
]);
let Command::Service {
command:
ServiceCommand::Delivery {
command: ServiceDeliveryCommand::PromotionApply(args),
},
} = promotion_apply.command
else {
panic!("expected service delivery promotion apply");
};
assert_eq!(
args.promotion_plan,
std::path::PathBuf::from("promotion-plan.json")
);
assert_eq!(
args.source_observation,
std::path::PathBuf::from("source-observation.json")
);
assert_eq!(
args.source_gateway_observation,
std::path::PathBuf::from("source-gateway-observation.json")
);
assert_eq!(
args.target_observation,
std::path::PathBuf::from("target-observation.json")
);
assert_eq!(
args.output,
Some(std::path::PathBuf::from("authorization.json"))
);
}
#[test]
fn parses_service_logs() {
let cli = Cli::parse_from([
"lenso",
"service",
"logs",
"support-ticket",
"api",
"--tail",
"100",
]);
let Command::Service {
command: ServiceCommand::Logs(args),
} = cli.command
else {
panic!("expected service logs");
};
assert_eq!(args.module_name, "support-ticket");
assert_eq!(args.service_name, "api");
assert_eq!(args.tail, 100);
}
#[test]
fn parses_system_dev_dry_run_and_cleanup_surfaces() {
let cli = Cli::parse_from([
"lenso",
"system",
"dev",
"--system-file",
"system.json",
"--sandbox-file",
"sandbox.json",
"--scenario",
"deadline-timeout",
"--dry-run",
"--json",
]);
let Command::System {
command: SystemCommand::Dev(args),
} = cli.command
else {
panic!("expected system dev");
};
assert_eq!(
args.system_file.as_deref(),
Some(std::path::Path::new("system.json"))
);
assert_eq!(
args.sandbox_file.as_deref(),
Some(std::path::Path::new("sandbox.json"))
);
assert_eq!(args.scenario.as_deref(), Some("deadline-timeout"));
assert!(args.dry_run);
assert!(args.json);
assert!(!args.cleanup);
let cleanup = Cli::parse_from(["lenso", "system", "dev", "--cleanup"]);
let Command::System {
command: SystemCommand::Dev(args),
} = cleanup.command
else {
panic!("expected system dev cleanup");
};
assert!(args.cleanup);
}
}