mj_controller/targets/
bootstrap.rs1use super::*;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum ExecutionBoundary<'a> {
5 Direct,
6 Container {
7 engine: &'a str,
8 container_id: &'a str,
9 },
10 Ssh(&'a SshTarget),
11 SshContainer {
12 engine: &'a str,
13 ssh: &'a SshTarget,
14 container_id: &'a str,
15 },
16}
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct HarnessProbe<'a> {
20 pub executable: &'a str,
21 pub version_args: &'a [&'a str],
22 pub bridge_executable: Option<&'a str>,
23}
24
25pub fn bootstrap_probe_plan(
29 boundary: ExecutionBoundary<'_>,
30 harness: HarnessProbe<'_>,
31) -> Result<CommandPlan> {
32 validate_executable(harness.executable)?;
33 let mut commands = vec![
34 at_boundary(
35 boundary,
36 std::iter::once(harness.executable)
37 .chain(harness.version_args.iter().copied())
38 .map(str::to_owned)
39 .collect(),
40 )
41 .purpose("probe harness version"),
42 ];
43 if let Some(bridge) = harness.bridge_executable {
44 validate_executable(bridge)?;
45 commands.push(
46 at_boundary(boundary, vec![bridge.to_owned(), "--version".to_owned()])
47 .purpose("probe ACP bridge version"),
48 );
49 }
50 commands.push(
51 at_boundary(boundary, vec!["git".to_owned(), "--version".to_owned()]).purpose("probe Git"),
52 );
53 Ok(CommandPlan {
54 description: "probe reusable target tools".to_owned(),
55 commands,
56 })
57}
58
59pub fn install_git_plan(boundary: ExecutionBoundary<'_>) -> CommandPlan {
62 let managed_container = matches!(
63 boundary,
64 ExecutionBoundary::Container { .. } | ExecutionBoundary::SshContainer { .. }
65 );
66 let script = if managed_container {
67 "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'"
68 } else {
69 "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"
70 };
71 CommandPlan {
72 description: "install missing Git".to_owned(),
73 commands: vec![
74 at_boundary(
75 boundary,
76 vec!["sh".to_owned(), "-c".to_owned(), script.to_owned()],
77 )
78 .purpose("install Git")
79 .stage(ProvisionStage::Cloning),
80 ],
81 }
82}
83
84pub(super) const BUNDLE_REPOSITORIES_PARALLEL_GROUP: u32 = 1;
88
89pub(super) fn clone_commands(
90 bundle: &ProjectBundleSpec,
91 workspace: &str,
92 wrap: impl Fn(Vec<String>) -> CommandSpec,
93) -> Vec<CommandSpec> {
94 let mut commands = vec![
95 wrap(vec![
96 "mkdir".to_owned(),
97 "-p".to_owned(),
98 workspace.to_owned(),
99 ])
100 .purpose("create bundle workspace")
101 .stage(ProvisionStage::Cloning),
102 ];
103 for repository in &bundle.repositories {
104 let destination = format!("{workspace}/{}", repository.destination);
105 let url = repository
106 .url
107 .as_ref()
108 .expect("validated network repository");
109 let mut args = vec!["git".to_owned(), "clone".to_owned()];
110 for push_url in &repository.push_urls {
111 args.extend([
112 "--config".into(),
113 format!("remote.origin.pushurl={push_url}"),
114 ]);
115 }
116 if let Some(reference) = &repository.reference {
117 args.extend(["--reference-if-able".to_owned(), reference.clone()]);
118 }
119 args.push("--".to_owned());
120 args.push(url.clone());
121 args.push(destination);
122 commands.push(
123 wrap(args)
124 .purpose(format!("clone {}", repository.destination))
125 .stage(ProvisionStage::Cloning)
126 .parallel_group(BUNDLE_REPOSITORIES_PARALLEL_GROUP),
127 );
128 }
129 commands
130}