mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-08 03:10:26 +00:00
Add SSH sandbox provider (arc-ssh) for user-provided hosts
Adds a generic SSH sandbox that connects to any user-provided host via openssh, without VM lifecycle management. Supports git clone with GitHub App credentials, configurable working directory, and arc cp reconnection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2ce89e5e5e
commit
e0b3c4cde4
12 changed files with 1641 additions and 1 deletions
19
Cargo.lock
generated
19
Cargo.lock
generated
|
|
@ -409,6 +409,24 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arc-ssh"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"arc-agent",
|
||||
"arc-github",
|
||||
"async-trait",
|
||||
"base64",
|
||||
"openssh",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shlex",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arc-types"
|
||||
version = "0.2.0"
|
||||
|
|
@ -463,6 +481,7 @@ dependencies = [
|
|||
"arc-github",
|
||||
"arc-llm",
|
||||
"arc-mcp",
|
||||
"arc-ssh",
|
||||
"arc-util",
|
||||
"assert_cmd",
|
||||
"async-trait",
|
||||
|
|
|
|||
|
|
@ -4080,6 +4080,8 @@ components:
|
|||
$ref: "#/components/schemas/DaytonaConfiguration"
|
||||
exe:
|
||||
$ref: "#/components/schemas/ExeConfiguration"
|
||||
ssh:
|
||||
$ref: "#/components/schemas/SshConfiguration"
|
||||
local:
|
||||
$ref: "#/components/schemas/LocalSandboxConfiguration"
|
||||
env:
|
||||
|
|
@ -4106,6 +4108,23 @@ components:
|
|||
type: string
|
||||
description: VM image to use for the exe.dev sandbox.
|
||||
|
||||
SshConfiguration:
|
||||
description: SSH sandbox configuration for user-provided hosts.
|
||||
type: object
|
||||
required:
|
||||
- destination
|
||||
- working_directory
|
||||
properties:
|
||||
destination:
|
||||
type: string
|
||||
description: SSH destination (e.g. user@host or an SSH alias).
|
||||
working_directory:
|
||||
type: string
|
||||
description: Remote working directory.
|
||||
config_file:
|
||||
type: string
|
||||
description: Optional path to a custom SSH config file.
|
||||
|
||||
DaytonaConfiguration:
|
||||
description: Daytona-specific sandbox settings.
|
||||
type: object
|
||||
|
|
|
|||
|
|
@ -1333,6 +1333,7 @@ mod runs {
|
|||
network: Some(arc_workflows::daytona_sandbox::DaytonaNetwork::Block),
|
||||
}),
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
vars: Some(std::collections::HashMap::from([
|
||||
|
|
@ -1490,6 +1491,7 @@ mod workflows {
|
|||
network: None,
|
||||
}),
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
vars: Some(std::collections::HashMap::from([
|
||||
|
|
@ -1561,6 +1563,7 @@ mod workflows {
|
|||
network: None,
|
||||
}),
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
vars: Some(std::collections::HashMap::from([
|
||||
|
|
@ -1643,6 +1646,7 @@ mod workflows {
|
|||
network: None,
|
||||
}),
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
vars: Some(std::collections::HashMap::from([
|
||||
|
|
@ -1716,6 +1720,7 @@ mod workflows {
|
|||
network: None,
|
||||
}),
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
vars: Some(std::collections::HashMap::from([
|
||||
|
|
@ -3279,6 +3284,7 @@ mod settings {
|
|||
network: Some(arc_workflows::daytona_sandbox::DaytonaNetwork::Block),
|
||||
}),
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
vars: None,
|
||||
|
|
|
|||
|
|
@ -305,6 +305,7 @@ fn fully_populated_server_config() -> ServerConfig {
|
|||
network: Some(DaytonaNetwork::Block),
|
||||
}),
|
||||
exe: Some(arc_exe::ExeConfig { image: None }),
|
||||
ssh: None,
|
||||
env: Some(Default::default()),
|
||||
}),
|
||||
vars: Some(Default::default()),
|
||||
|
|
|
|||
26
lib/crates/arc-ssh/Cargo.toml
Normal file
26
lib/crates/arc-ssh/Cargo.toml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
[package]
|
||||
name = "arc-ssh"
|
||||
edition.workspace = true
|
||||
version.workspace = true
|
||||
license.workspace = true
|
||||
description = "Generic SSH sandbox for Arc agent tool operations"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
arc-agent = { path = "../arc-agent" }
|
||||
arc-github = { path = "../arc-github" }
|
||||
async-trait.workspace = true
|
||||
tokio.workspace = true
|
||||
tokio-util.workspace = true
|
||||
openssh.workspace = true
|
||||
serde_json.workspace = true
|
||||
base64.workspace = true
|
||||
tracing.workspace = true
|
||||
serde.workspace = true
|
||||
shlex = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true, features = ["test-util", "macros"] }
|
||||
tempfile = "3"
|
||||
1331
lib/crates/arc-ssh/src/lib.rs
Normal file
1331
lib/crates/arc-ssh/src/lib.rs
Normal file
File diff suppressed because it is too large
Load diff
101
lib/crates/arc-ssh/src/openssh_runner.rs
Normal file
101
lib/crates/arc-ssh/src/openssh_runner.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
use async_trait::async_trait;
|
||||
use openssh::{KnownHosts, SessionBuilder};
|
||||
|
||||
use crate::{shell_quote, SshOutput, SshRunner};
|
||||
|
||||
/// Real SSH implementation using the `openssh` crate (multiplexed connections).
|
||||
pub struct OpensshRunner {
|
||||
session: openssh::Session,
|
||||
}
|
||||
|
||||
impl OpensshRunner {
|
||||
/// Connect to a host via SSH, using the user's SSH agent for authentication.
|
||||
/// Commands are executed through a shell (`sh -c`).
|
||||
pub async fn connect(destination: &str, config_file: Option<&str>) -> Result<Self, String> {
|
||||
let mut builder = SessionBuilder::default();
|
||||
builder.known_hosts_check(KnownHosts::Accept);
|
||||
if let Some(cfg) = config_file {
|
||||
builder.config_file(cfg);
|
||||
}
|
||||
let session = builder
|
||||
.connect(destination)
|
||||
.await
|
||||
.map_err(|e| format!("SSH connection to {destination} failed: {e}"))?;
|
||||
Ok(Self { session })
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl SshRunner for OpensshRunner {
|
||||
async fn run_command(&self, command: &str) -> Result<SshOutput, String> {
|
||||
let output = self
|
||||
.session
|
||||
.shell(command)
|
||||
.output()
|
||||
.await
|
||||
.map_err(|e| format!("SSH command failed: {e}"))?;
|
||||
|
||||
let exit_code = output.status.code().unwrap_or(-1);
|
||||
Ok(SshOutput {
|
||||
stdout: output.stdout,
|
||||
stderr: output.stderr,
|
||||
exit_code,
|
||||
})
|
||||
}
|
||||
|
||||
async fn run_command_with_timeout(
|
||||
&self,
|
||||
command: &str,
|
||||
timeout: std::time::Duration,
|
||||
) -> Result<SshOutput, String> {
|
||||
let mut child = self.session.shell(command);
|
||||
let fut = child.output();
|
||||
|
||||
match tokio::time::timeout(timeout, fut).await {
|
||||
Ok(Ok(output)) => {
|
||||
let exit_code = output.status.code().unwrap_or(-1);
|
||||
Ok(SshOutput {
|
||||
stdout: output.stdout,
|
||||
stderr: output.stderr,
|
||||
exit_code,
|
||||
})
|
||||
}
|
||||
Ok(Err(e)) => Err(format!("SSH command failed: {e}")),
|
||||
Err(_) => Err("Command timed out".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn upload_file(&self, path: &str, content: &[u8]) -> Result<(), String> {
|
||||
use base64::Engine;
|
||||
let encoded = base64::engine::general_purpose::STANDARD.encode(content);
|
||||
let cmd = format!("echo '{}' | base64 -d > {}", encoded, shell_quote(path),);
|
||||
let output = self
|
||||
.session
|
||||
.shell(&cmd)
|
||||
.output()
|
||||
.await
|
||||
.map_err(|e| format!("SSH upload failed: {e}"))?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(format!("Upload to {path} failed: {stderr}"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn download_file(&self, path: &str) -> Result<Vec<u8>, String> {
|
||||
let cmd = format!("cat {}", shell_quote(path));
|
||||
let output = self
|
||||
.session
|
||||
.shell(&cmd)
|
||||
.output()
|
||||
.await
|
||||
.map_err(|e| format!("SSH download failed: {e}"))?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(format!("Download of {path} failed: {stderr}"));
|
||||
}
|
||||
Ok(output.stdout)
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ dotenvy.workspace = true
|
|||
arc-agent = { path = "../arc-agent" }
|
||||
arc-devcontainer = { path = "../arc-devcontainer" }
|
||||
arc-exe = { path = "../arc-exe", optional = true }
|
||||
arc-ssh = { path = "../arc-ssh" }
|
||||
arc-mcp = { path = "../arc-mcp" }
|
||||
arc-github = { path = "../arc-github" }
|
||||
arc-util = { path = "../arc-util" }
|
||||
|
|
|
|||
|
|
@ -150,6 +150,26 @@ pub async fn reconnect(record: &SandboxRecord) -> Result<Box<dyn arc_agent::sand
|
|||
let sandbox = arc_exe::ExeSandbox::from_existing(Box::new(data_ssh));
|
||||
Ok(Box::new(sandbox))
|
||||
}
|
||||
"ssh" => {
|
||||
let destination = record
|
||||
.data_host
|
||||
.as_deref()
|
||||
.context("SSH sandbox record missing data_host (destination)")?;
|
||||
|
||||
let ssh = arc_ssh::OpensshRunner::connect(destination, None)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
anyhow::anyhow!("Failed to connect to SSH sandbox '{destination}': {e}")
|
||||
})?;
|
||||
|
||||
let config = arc_ssh::SshConfig {
|
||||
destination: destination.to_string(),
|
||||
working_directory: record.working_directory.clone(),
|
||||
config_file: None,
|
||||
};
|
||||
let sandbox = arc_ssh::SshSandbox::from_existing(Box::new(ssh), config);
|
||||
Ok(Box::new(sandbox))
|
||||
}
|
||||
other => bail!("Unknown sandbox provider: {other}"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ pub enum SandboxProvider {
|
|||
/// Run tools inside an exe.dev VM
|
||||
#[cfg(feature = "exedev")]
|
||||
Exe,
|
||||
/// Run tools on a user-provided SSH host
|
||||
Ssh,
|
||||
}
|
||||
|
||||
impl SandboxProvider {
|
||||
|
|
@ -46,6 +48,7 @@ impl SandboxProvider {
|
|||
Self::Daytona => true,
|
||||
#[cfg(feature = "exedev")]
|
||||
Self::Exe => true,
|
||||
Self::Ssh => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -59,6 +62,7 @@ impl fmt::Display for SandboxProvider {
|
|||
Self::Daytona => write!(f, "daytona"),
|
||||
#[cfg(feature = "exedev")]
|
||||
Self::Exe => write!(f, "exe"),
|
||||
Self::Ssh => write!(f, "ssh"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -73,6 +77,7 @@ impl FromStr for SandboxProvider {
|
|||
"daytona" => Ok(Self::Daytona),
|
||||
#[cfg(feature = "exedev")]
|
||||
"exe" => Ok(Self::Exe),
|
||||
"ssh" => Ok(Self::Ssh),
|
||||
other => Err(format!("unknown sandbox provider: {other}")),
|
||||
}
|
||||
}
|
||||
|
|
@ -309,6 +314,14 @@ mod tests {
|
|||
SandboxProvider::Exe
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
"ssh".parse::<SandboxProvider>().unwrap(),
|
||||
SandboxProvider::Ssh
|
||||
);
|
||||
assert_eq!(
|
||||
"SSH".parse::<SandboxProvider>().unwrap(),
|
||||
SandboxProvider::Ssh
|
||||
);
|
||||
assert!("invalid".parse::<SandboxProvider>().is_err());
|
||||
}
|
||||
|
||||
|
|
@ -319,6 +332,7 @@ mod tests {
|
|||
assert_eq!(SandboxProvider::Daytona.to_string(), "daytona");
|
||||
#[cfg(feature = "exedev")]
|
||||
assert_eq!(SandboxProvider::Exe.to_string(), "exe");
|
||||
assert_eq!(SandboxProvider::Ssh.to_string(), "ssh");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -225,6 +225,33 @@ fn resolve_exe_clone_params(cwd: &std::path::Path) -> Option<arc_exe::GitClonePa
|
|||
Some(arc_exe::GitCloneParams { url, branch })
|
||||
}
|
||||
|
||||
/// Resolve SSH sandbox config: TOML config > run defaults.
|
||||
fn resolve_ssh_config(
|
||||
run_cfg: Option<&WorkflowRunConfig>,
|
||||
run_defaults: &RunDefaults,
|
||||
) -> Option<arc_ssh::SshConfig> {
|
||||
run_cfg
|
||||
.and_then(|c| c.sandbox.as_ref())
|
||||
.and_then(|e| e.ssh.clone())
|
||||
.or_else(|| run_defaults.sandbox.as_ref().and_then(|s| s.ssh.clone()))
|
||||
}
|
||||
|
||||
/// Resolve SSH sandbox git clone parameters from the current repo.
|
||||
///
|
||||
/// Returns `None` if no git repo is detected. Credential resolution is
|
||||
/// handled by SshSandbox itself via its `github_app` field.
|
||||
fn resolve_ssh_clone_params(cwd: &std::path::Path) -> Option<arc_ssh::GitCloneParams> {
|
||||
let (detected_url, branch) = match crate::daytona_sandbox::detect_repo_info(cwd) {
|
||||
Ok(info) => info,
|
||||
Err(e) => {
|
||||
tracing::warn!("No git repo detected for SSH clone: {e}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let url = arc_github::ssh_url_to_https(&detected_url);
|
||||
Some(arc_ssh::GitCloneParams { url, branch })
|
||||
}
|
||||
|
||||
/// Resolve the fallback chain from config.
|
||||
///
|
||||
/// `apply_defaults` must be called on `run_cfg` before this — it merges
|
||||
|
|
@ -625,6 +652,7 @@ pub async fn run_command(
|
|||
let mut daytona_config = resolve_daytona_config(run_cfg.as_ref(), &run_defaults);
|
||||
#[cfg(feature = "exedev")]
|
||||
let exe_config = resolve_exe_config(run_cfg.as_ref(), &run_defaults);
|
||||
let ssh_config = resolve_ssh_config(run_cfg.as_ref(), &run_defaults);
|
||||
|
||||
// Resolve devcontainer if enabled
|
||||
let devcontainer_config = if run_cfg
|
||||
|
|
@ -762,6 +790,23 @@ pub async fn run_command(
|
|||
}));
|
||||
Arc::new(env)
|
||||
}
|
||||
SandboxProvider::Ssh => {
|
||||
let config = ssh_config
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow::anyhow!("--sandbox ssh requires [sandbox.ssh] config"))?;
|
||||
let clone_params = resolve_ssh_clone_params(&original_cwd);
|
||||
let mut env = arc_ssh::SshSandbox::new(
|
||||
config,
|
||||
clone_params,
|
||||
Some(run_id.clone()),
|
||||
github_app.clone(),
|
||||
);
|
||||
let emitter_cb = Arc::clone(&emitter);
|
||||
env.set_event_callback(Arc::new(move |event| {
|
||||
emitter_cb.emit(&crate::event::WorkflowRunEvent::Sandbox { event });
|
||||
}));
|
||||
Arc::new(env)
|
||||
}
|
||||
SandboxProvider::Local => {
|
||||
let mut env = LocalSandbox::new(cwd.clone());
|
||||
let emitter_cb = Arc::clone(&emitter);
|
||||
|
|
@ -836,6 +881,17 @@ pub async fn run_command(
|
|||
data_host,
|
||||
}
|
||||
}
|
||||
SandboxProvider::Ssh => {
|
||||
let data_host = ssh_config.as_ref().map(|c| c.destination.clone());
|
||||
crate::sandbox_record::SandboxRecord {
|
||||
provider: "ssh".to_string(),
|
||||
working_directory: sandbox.working_directory().to_string(),
|
||||
identifier: sandbox_info_opt,
|
||||
host_working_directory: None,
|
||||
container_mount_point: None,
|
||||
data_host,
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Err(e) = record.save(&run_dir.join("sandbox.json")) {
|
||||
tracing::warn!(error = %e, "Failed to save sandbox record");
|
||||
|
|
@ -895,7 +951,7 @@ pub async fn run_command(
|
|||
}
|
||||
Ok(None) => {
|
||||
eprintln!(
|
||||
"{} --ssh only works with --sandbox daytona or exe, skipping.",
|
||||
"{} --ssh only works with --sandbox daytona, exe, or ssh, skipping.",
|
||||
styles.yellow.apply_to("Warning:"),
|
||||
);
|
||||
}
|
||||
|
|
@ -1611,6 +1667,23 @@ async fn run_from_branch(
|
|||
}));
|
||||
(Arc::new(env), None)
|
||||
}
|
||||
SandboxProvider::Ssh => {
|
||||
let config = resolve_ssh_config(None, &run_defaults).ok_or_else(|| {
|
||||
anyhow::anyhow!("--sandbox ssh requires [sandbox.ssh] config")
|
||||
})?;
|
||||
let clone_params = resolve_ssh_clone_params(&original_cwd);
|
||||
let mut env = arc_ssh::SshSandbox::new(
|
||||
config,
|
||||
clone_params,
|
||||
Some(run_id.clone()),
|
||||
github_app.clone(),
|
||||
);
|
||||
let emitter_cb = Arc::clone(&emitter);
|
||||
env.set_event_callback(Arc::new(move |event| {
|
||||
emitter_cb.emit(&crate::event::WorkflowRunEvent::Sandbox { event });
|
||||
}));
|
||||
(Arc::new(env), None)
|
||||
}
|
||||
SandboxProvider::Daytona => {
|
||||
bail!("--run-branch resume is not yet supported with --sandbox daytona");
|
||||
}
|
||||
|
|
@ -1911,6 +1984,7 @@ async fn run_preflight(
|
|||
let daytona_config = resolve_daytona_config(run_cfg.as_ref(), run_defaults);
|
||||
#[cfg(feature = "exedev")]
|
||||
let exe_config = resolve_exe_config(run_cfg.as_ref(), run_defaults);
|
||||
let ssh_config = resolve_ssh_config(run_cfg.as_ref(), run_defaults);
|
||||
|
||||
let sandbox_result: Result<Arc<dyn Sandbox>, String> = match sandbox_provider {
|
||||
SandboxProvider::Docker => {
|
||||
|
|
@ -1947,6 +2021,14 @@ async fn run_preflight(
|
|||
}
|
||||
Err(e) => Err(format!("exe.dev SSH connection failed: {e}")),
|
||||
},
|
||||
SandboxProvider::Ssh => match ssh_config {
|
||||
Some(config) => {
|
||||
let clone_params = resolve_ssh_clone_params(&original_cwd);
|
||||
let env = arc_ssh::SshSandbox::new(config, clone_params, None, None);
|
||||
Ok(Arc::new(env) as Arc<dyn Sandbox>)
|
||||
}
|
||||
None => Err("SSH sandbox requires [sandbox.ssh] config".to_string()),
|
||||
},
|
||||
SandboxProvider::Local => {
|
||||
Ok(Arc::new(LocalSandbox::new(original_cwd.clone())) as Arc<dyn Sandbox>)
|
||||
}
|
||||
|
|
@ -2592,6 +2674,7 @@ mod tests {
|
|||
daytona: None,
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
vars: None,
|
||||
|
|
@ -2622,6 +2705,7 @@ mod tests {
|
|||
daytona: None,
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
vars: None,
|
||||
|
|
@ -2640,6 +2724,7 @@ mod tests {
|
|||
daytona: None,
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
..RunDefaults::default()
|
||||
|
|
@ -2658,6 +2743,7 @@ mod tests {
|
|||
daytona: None,
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
..RunDefaults::default()
|
||||
|
|
@ -2699,6 +2785,7 @@ mod tests {
|
|||
daytona: None,
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
vars: None,
|
||||
|
|
@ -2728,6 +2815,7 @@ mod tests {
|
|||
daytona: None,
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
..RunDefaults::default()
|
||||
|
|
@ -2757,6 +2845,7 @@ mod tests {
|
|||
daytona: None,
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
vars: None,
|
||||
|
|
@ -2777,6 +2866,7 @@ mod tests {
|
|||
daytona: None,
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
..RunDefaults::default()
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ pub struct SandboxConfig {
|
|||
pub daytona: Option<DaytonaConfig>,
|
||||
#[cfg(feature = "exedev")]
|
||||
pub exe: Option<arc_exe::ExeConfig>,
|
||||
pub ssh: Option<arc_ssh::SshConfig>,
|
||||
pub env: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
|
|
@ -1107,6 +1108,7 @@ preserve = true
|
|||
daytona: None,
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
..RunDefaults::default()
|
||||
|
|
@ -1137,6 +1139,7 @@ provider = "docker"
|
|||
daytona: None,
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
..RunDefaults::default()
|
||||
|
|
@ -1170,6 +1173,7 @@ provider = "daytona"
|
|||
}),
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
..RunDefaults::default()
|
||||
|
|
@ -1210,6 +1214,7 @@ auto_stop_interval = 60
|
|||
}),
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
..RunDefaults::default()
|
||||
|
|
@ -1249,6 +1254,7 @@ env = "from_task"
|
|||
}),
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
..RunDefaults::default()
|
||||
|
|
@ -1292,6 +1298,7 @@ cpu = 2
|
|||
}),
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
..RunDefaults::default()
|
||||
|
|
@ -1334,6 +1341,7 @@ auto_stop_interval = 60
|
|||
}),
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
..RunDefaults::default()
|
||||
|
|
@ -1559,6 +1567,7 @@ network = "block"
|
|||
}),
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
..RunDefaults::default()
|
||||
|
|
@ -1597,6 +1606,7 @@ auto_stop_interval = 60
|
|||
}),
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: None,
|
||||
}),
|
||||
..RunDefaults::default()
|
||||
|
|
@ -1807,6 +1817,7 @@ SHARED = "from_task"
|
|||
daytona: None,
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: Some(HashMap::from([
|
||||
("DEFAULT_KEY".into(), "default_val".into()),
|
||||
("SHARED".into(), "from_default".into()),
|
||||
|
|
@ -1843,6 +1854,7 @@ provider = "daytona"
|
|||
daytona: None,
|
||||
#[cfg(feature = "exedev")]
|
||||
exe: None,
|
||||
ssh: None,
|
||||
env: Some(HashMap::from([("KEY".into(), "val".into())])),
|
||||
}),
|
||||
..RunDefaults::default()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue