Allow setting entrypoint from CLI
This commit is contained in:
@@ -62,6 +62,10 @@ pub struct Args {
|
||||
#[arg(long = "bwrap-arg", value_name = "ARG", action = clap::ArgAction::Append)]
|
||||
pub bwrap_args: Vec<String>,
|
||||
|
||||
/// Run this binary with the trailing args as its arguments
|
||||
#[arg(long, value_name = "CMD")]
|
||||
pub entrypoint: Option<String>,
|
||||
|
||||
/// Command and arguments to run inside the sandbox
|
||||
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||
pub command_and_args: Vec<OsString>,
|
||||
|
||||
@@ -21,7 +21,8 @@ pub fn build(args: Args, file_config: Option<FileConfig>) -> Result<SandboxConfi
|
||||
globals.validate_paths()?;
|
||||
profile.validate_paths()?;
|
||||
|
||||
let (command, command_args) = resolve_command(args.command_and_args, &profile, &globals)?;
|
||||
let (command, command_args) =
|
||||
resolve_command(args.entrypoint, args.command_and_args, &profile, &globals)?;
|
||||
let command = resolve_binary(&command)
|
||||
.ok_or_else(|| SandboxError::CommandNotFound(PathBuf::from(&command)))?;
|
||||
|
||||
@@ -124,11 +125,15 @@ fn merge_vecs<T: Clone>(cli: Vec<T>, profile: &[T], globals: &[T]) -> Vec<T> {
|
||||
}
|
||||
|
||||
fn resolve_command(
|
||||
cli_entrypoint: Option<String>,
|
||||
mut passthrough_args: Vec<OsString>,
|
||||
profile: &Options,
|
||||
globals: &Options,
|
||||
) -> Result<(OsString, Vec<OsString>), SandboxError> {
|
||||
let entrypoint = profile.entrypoint.clone().or(globals.entrypoint.clone());
|
||||
let entrypoint = cli_entrypoint
|
||||
.map(CommandValue::Simple)
|
||||
.or_else(|| profile.entrypoint.clone())
|
||||
.or_else(|| globals.entrypoint.clone());
|
||||
let command = profile.command.clone().or(globals.command.clone());
|
||||
|
||||
if let Some(ep) = entrypoint {
|
||||
@@ -568,6 +573,67 @@ mod tests {
|
||||
assert_eq!(config.command_args, vec![OsString::from("--flag")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_cli_entrypoint_overrides_profile() {
|
||||
let file_config = FileConfig {
|
||||
profile: HashMap::from([(
|
||||
"test".into(),
|
||||
Options {
|
||||
entrypoint: Some(CommandValue::Simple("/usr/bin/false".into())),
|
||||
..Options::default()
|
||||
},
|
||||
)]),
|
||||
..FileConfig::default()
|
||||
};
|
||||
let args = Args {
|
||||
profile: Some("test".into()),
|
||||
entrypoint: Some("/usr/bin/true".into()),
|
||||
..Args::default()
|
||||
};
|
||||
let config = build(args, Some(file_config)).unwrap();
|
||||
assert_eq!(config.command, PathBuf::from("/usr/bin/true"));
|
||||
assert!(config.command_args.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_cli_entrypoint_combines_with_passthrough_args() {
|
||||
let args = Args {
|
||||
entrypoint: Some("/usr/bin/true".into()),
|
||||
command_and_args: vec!["--flag".into(), "value".into()],
|
||||
..Args::default()
|
||||
};
|
||||
let config = build(args, None).unwrap();
|
||||
assert_eq!(config.command, PathBuf::from("/usr/bin/true"));
|
||||
assert_eq!(
|
||||
config.command_args,
|
||||
vec![OsString::from("--flag"), OsString::from("value")]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_cli_entrypoint_falls_back_to_config_command_defaults() {
|
||||
let file_config = FileConfig {
|
||||
options: Options {
|
||||
command: Some(CommandValue::WithArgs(vec![
|
||||
"--default".into(),
|
||||
"args".into(),
|
||||
])),
|
||||
..Options::default()
|
||||
},
|
||||
..FileConfig::default()
|
||||
};
|
||||
let args = Args {
|
||||
entrypoint: Some("/usr/bin/true".into()),
|
||||
..Args::default()
|
||||
};
|
||||
let config = build(args, Some(file_config)).unwrap();
|
||||
assert_eq!(config.command, PathBuf::from("/usr/bin/true"));
|
||||
assert_eq!(
|
||||
config.command_args,
|
||||
vec![OsString::from("--default"), OsString::from("args")]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_cli_command_overrides_config() {
|
||||
let file_config = FileConfig {
|
||||
@@ -754,6 +820,7 @@ mod tests {
|
||||
#[test]
|
||||
fn entrypoint_with_passthrough_args() {
|
||||
let (cmd, args) = resolve_command(
|
||||
None,
|
||||
vec!["--verbose".into(), "--model".into(), "opus".into()],
|
||||
&Options {
|
||||
entrypoint: Some(CommandValue::WithArgs(vec![
|
||||
@@ -784,6 +851,7 @@ mod tests {
|
||||
#[test]
|
||||
fn entrypoint_without_passthrough_uses_command_defaults() {
|
||||
let (cmd, args) = resolve_command(
|
||||
None,
|
||||
vec![],
|
||||
&Options {
|
||||
entrypoint: Some(CommandValue::Simple("claude".into())),
|
||||
@@ -809,6 +877,7 @@ mod tests {
|
||||
#[test]
|
||||
fn entrypoint_without_passthrough_or_command() {
|
||||
let (cmd, args) = resolve_command(
|
||||
None,
|
||||
vec![],
|
||||
&Options {
|
||||
entrypoint: Some(CommandValue::WithArgs(vec![
|
||||
@@ -827,6 +896,7 @@ mod tests {
|
||||
#[test]
|
||||
fn profile_entrypoint_overrides_global() {
|
||||
let (cmd, _) = resolve_command(
|
||||
None,
|
||||
vec![],
|
||||
&Options {
|
||||
entrypoint: Some(CommandValue::Simple("claude".into())),
|
||||
@@ -844,6 +914,7 @@ mod tests {
|
||||
#[test]
|
||||
fn global_entrypoint_with_profile_command() {
|
||||
let (cmd, args) = resolve_command(
|
||||
None,
|
||||
vec![],
|
||||
&Options {
|
||||
command: Some(CommandValue::WithArgs(vec![
|
||||
@@ -869,7 +940,7 @@ mod tests {
|
||||
}
|
||||
#[test]
|
||||
fn no_command_errors() {
|
||||
let result = resolve_command(vec![], &Options::default(), &Options::default());
|
||||
let result = resolve_command(None, vec![], &Options::default(), &Options::default());
|
||||
assert!(matches!(result, Err(SandboxError::NoCommand)));
|
||||
}
|
||||
|
||||
|
||||
@@ -755,6 +755,40 @@ fn config_entrypoint_alone_without_command_or_passthrough() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_entrypoint_appends_passthrough_args() {
|
||||
let output = sandbox(&["--entrypoint", "bash"])
|
||||
.args(["--", "-c", "echo cli-entrypoint-works"])
|
||||
.output()
|
||||
.expect("failed to execute");
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
assert_eq!(
|
||||
stdout, "cli-entrypoint-works",
|
||||
"expected --entrypoint to receive trailing args, got: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_entrypoint_overrides_config_entrypoint() {
|
||||
let cfg = ConfigFile::new(
|
||||
r#"
|
||||
entrypoint = ["/bin/false"]
|
||||
"#,
|
||||
);
|
||||
|
||||
let output = sandbox_withconfig(&["--config", &cfg, "--entrypoint", "bash"])
|
||||
.args(["--", "-c", "echo override-works"])
|
||||
.output()
|
||||
.expect("failed to execute");
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
assert_eq!(
|
||||
stdout, "override-works",
|
||||
"expected CLI --entrypoint to override config entrypoint, got: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_command_alone_without_passthrough() {
|
||||
let cfg = ConfigFile::new(
|
||||
|
||||
Reference in New Issue
Block a user