-
Notifications
You must be signed in to change notification settings - Fork 60
[subtask] [Subtask 2/5] CLI Integration: Add --profile and --manifest flags #540
Description
Parent Issue: #307
Objective
Add CLI flags and configuration to enable headless deployment mode and pass the manifest path to the runtime.
Context
This builds on the foundation types from Subtask 1/5. It exposes headless mode to users through command-line arguments and wires the configuration through to the LifecycleManager initialization.
Implementation Details
1. Add CLI Flags to Serve Command
File: src/commands.rs
Add new fields to the Serve struct (around line 64):
/// Deployment profile: interactive (default) or headless
#[arg(long, value_enum)]
#[serde(skip_serializing_if = "Option::is_none")]
pub profile: Option(DeploymentProfile),
/// Path to provisioning manifest (YAML or TOML). Required when --profile=headless
#[arg(long)]
#[serde(skip_serializing_if = "Option::is_none")]
pub manifest: Option(PathBuf),Import the type at the top:
use wassette::DeploymentProfile;2. Add Validation Logic
File: src/serve.rs (or wherever the serve command is handled)
Add validation when processing the Serve command:
// Validate headless mode requirements
if let Some(DeploymentProfile::Headless) = serve_args.profile {
if serve_args.manifest.is_none() {
anyhow::bail!(
"Headless deployment profile requires --manifest flag with path to provisioning manifest"
);
}
}
// Validate manifest exists if provided
if let Some(ref manifest_path) = serve_args.manifest {
if !manifest_path.exists() {
anyhow::bail!(
"Manifest file not found: {}",
manifest_path.display()
);
}
}3. Wire Profile to LifecycleManager
File: Look for where LifecycleBuilder is constructed (likely in src/main.rs or src/serve.rs)
Update the builder chain:
let mut builder = LifecycleBuilder::new(component_dir);
// Set profile if provided, otherwise default to Interactive
if let Some(profile) = serve_args.profile {
builder = builder.with_profile(profile);
}
// Additional existing builder calls...
let lifecycle_manager = builder.build().await?;4. Store Manifest Path in Configuration
File: crates/wassette/src/config.rs
Add manifest path to LifecycleConfig:
manifest_path: Option(PathBuf),Add accessor:
pub fn manifest_path(&self) -> Option<&Path> {
self.manifest_path.as_deref()
}Update LifecycleBuilder:
manifest_path: Option(PathBuf),Add builder method:
pub fn with_manifest(mut self, manifest_path: PathBuf) -> Self {
self.manifest_path = Some(manifest_path);
self
}Update initialization and build() to propagate the field.
5. Update CLI Documentation
File: docs/reference/cli.md (if it exists, otherwise note for future docs update)
Document new flags:
### `wassette serve --profile (PROFILE)`
Set the deployment profile:
- `interactive` (default): Permits runtime component loading and permission grants
- `headless`: Declarative-only mode for unattended deployments
### `wassette serve --manifest (PATH)`
Path to provisioning manifest (YAML or TOML). Required when using `--profile headless`.
Example:
```bash
wassette serve --profile headless --manifest ./deployment.yaml
## Acceptance Criteria
- [ ] `--profile` flag accepts "interactive" or "headless" values
- [ ] `--manifest` flag accepts a file path
- [ ] Validation error occurs if `--profile headless` is used without `--manifest`
- [ ] Validation error occurs if manifest path doesn't exist
- [ ] Profile is correctly passed to `LifecycleBuilder`
- [ ] Manifest path is stored in `LifecycleConfig`
- [ ] `cargo build` succeeds
- [ ] `cargo clippy` passes with no warnings
- [ ] Help text (`wassette serve --help`) shows new flags clearly
## Testing Strategy
### Manual Testing
```bash
# Should work: default interactive mode
cargo run -- serve --stdio
# Should work: explicit interactive mode
cargo run -- serve --stdio --profile interactive
# Should fail: headless without manifest
cargo run -- serve --stdio --profile headless
# Should fail: manifest doesn't exist
cargo run -- serve --stdio --profile headless --manifest /nonexistent/file.yaml
# Should work: headless with manifest (after creating example manifest)
echo "version: 1
components: []" > /tmp/test-manifest.yaml
cargo run -- serve --stdio --profile headless --manifest /tmp/test-manifest.yaml
Unit Tests
Add to src/commands.rs or appropriate test module:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deployment_profile_cli_parsing() {
// Test that clap can parse the enum values
let args = Serve::try_parse_from(&[
"serve",
"--profile", "headless",
"--manifest", "/tmp/manifest.yaml"
]);
assert!(args.is_ok());
}
}Dependencies
- Subtask 1/5: Requires
DeploymentProfileenum
References
- Parent issue Proposal: Headless Deployment Mode for Wassette #307 section "Proposed Architecture > Deployment Profiles"
- Existing CLI patterns in
src/commands.rs - clap documentation for enum parsing
Related to Proposal: Headless Deployment Mode for Wassette #307