use super::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExecutionBoundary<'a> {
Direct,
Container {
engine: &'a str,
container_id: &'a str,
},
Ssh(&'a SshTarget),
SshContainer {
engine: &'a str,
ssh: &'a SshTarget,
container_id: &'a str,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HarnessProbe<'a> {
pub executable: &'a str,
pub version_args: &'a [&'a str],
pub bridge_executable: Option<&'a str>,
}
/// Compatibility is intentionally interpreted by the controller. A successful
/// probe permits an image-baked tool to be reused; a missing/incompatible tool
/// causes the controller to upload/install its release-owned copy.
pub fn bootstrap_probe_plan(
boundary: ExecutionBoundary<'_>,
harness: HarnessProbe<'_>,
) -> Result<CommandPlan> {
validate_executable(harness.executable)?;
let mut commands = vec![
at_boundary(
boundary,
std::iter::once(harness.executable)
.chain(harness.version_args.iter().copied())
.map(str::to_owned)
.collect(),
)
.purpose("probe harness version"),
];
if let Some(bridge) = harness.bridge_executable {
validate_executable(bridge)?;
commands.push(
at_boundary(boundary, vec![bridge.to_owned(), "--version".to_owned()])
.purpose("probe ACP bridge version"),
);
}
commands.push(
at_boundary(boundary, vec!["git".to_owned(), "--version".to_owned()]).purpose("probe Git"),
);
Ok(CommandPlan {
description: "probe reusable target tools".to_owned(),
commands,
})
}
/// Thin Linux Git bootstrap. Managed containers also receive GitHub CLI and
/// its HTTPS credential helper so an injected `GH_TOKEN` works before clone.
pub fn install_git_plan(boundary: ExecutionBoundary<'_>) -> CommandPlan {
let managed_container = matches!(
boundary,
ExecutionBoundary::Container { .. } | ExecutionBoundary::SshContainer { .. }
);
let script = if managed_container {
"set -eu; if ! command -v git >/dev/null 2>&1 || ! command -v gh >/dev/null 2>&1; then SUDO=''; if [ \"$(id -u)\" != 0 ]; then command -v sudo >/dev/null 2>&1 && sudo -n true || { echo 'Git and GitHub CLI installation requires root or passwordless sudo' >&2; exit 1; }; SUDO='sudo -n'; fi; if command -v apt-get >/dev/null 2>&1; then $SUDO apt-get update; $SUDO apt-get install -y git gh ca-certificates curl; elif command -v dnf >/dev/null 2>&1; then $SUDO dnf install -y git gh ca-certificates curl; elif command -v yum >/dev/null 2>&1; then $SUDO yum install -y git gh ca-certificates curl; elif command -v apk >/dev/null 2>&1; then $SUDO apk add --no-cache git github-cli ca-certificates curl; else echo 'Unsupported package manager; install Git and GitHub CLI in the image' >&2; exit 1; fi; fi; git config --global credential.https://github.com.helper '!gh auth git-credential'; git config --global credential.https://gist.github.com.helper '!gh auth git-credential'"
} else {
"set -eu; if command -v git >/dev/null 2>&1; then exit 0; fi; SUDO=''; if [ \"$(id -u)\" != 0 ]; then command -v sudo >/dev/null 2>&1 && sudo -n true || { echo 'Git installation requires root or passwordless sudo' >&2; exit 1; }; SUDO='sudo -n'; fi; if command -v apt-get >/dev/null 2>&1; then $SUDO apt-get update; $SUDO apt-get install -y git ca-certificates curl; elif command -v dnf >/dev/null 2>&1; then $SUDO dnf install -y git ca-certificates curl; elif command -v yum >/dev/null 2>&1; then $SUDO yum install -y git ca-certificates curl; elif command -v apk >/dev/null 2>&1; then $SUDO apk add --no-cache git ca-certificates curl; else echo 'Unsupported package manager; install Git manually' >&2; exit 1; fi"
};
CommandPlan {
description: "install missing Git".to_owned(),
commands: vec![
at_boundary(
boundary,
vec!["sh".to_owned(), "-c".to_owned(), script.to_owned()],
)
.purpose("install Git")
.stage(ProvisionStage::Cloning),
],
}
}
/// Shared [`CommandSpec::parallel_group`] marker for one bundle's per-repository
/// clone/init commands. Every `clone_commands` call builds its own
/// [`CommandPlan`], so a single fixed marker never mixes batches across plans.
pub(super) const BUNDLE_REPOSITORIES_PARALLEL_GROUP: u32 = 1;
pub(super) fn clone_commands(
bundle: &ProjectBundleSpec,
workspace: &str,
wrap: impl Fn(Vec<String>) -> CommandSpec,
) -> Vec<CommandSpec> {
let mut commands = vec![
wrap(vec![
"mkdir".to_owned(),
"-p".to_owned(),
workspace.to_owned(),
])
.purpose("create bundle workspace")
.stage(ProvisionStage::Cloning),
];
for repository in &bundle.repositories {
let destination = format!("{workspace}/{}", repository.destination);
let url = repository
.url
.as_ref()
.expect("validated network repository");
let mut args = vec!["git".to_owned(), "clone".to_owned()];
for push_url in &repository.push_urls {
args.extend([
"--config".into(),
format!("remote.origin.pushurl={push_url}"),
]);
}
if let Some(reference) = &repository.reference {
args.extend(["--reference-if-able".to_owned(), reference.clone()]);
}
args.push("--".to_owned());
args.push(url.clone());
args.push(destination);
commands.push(
wrap(args)
.purpose(format!("clone {}", repository.destination))
.stage(ProvisionStage::Cloning)
.parallel_group(BUNDLE_REPOSITORIES_PARALLEL_GROUP),
);
}
commands
}