ambient_ci/action_impl/
rustup.rs1use std::{path::PathBuf, process::Command};
2
3use clingwrap::runner::{CommandError, CommandRunner};
4use serde::{Deserialize, Serialize};
5
6use crate::{
7 action::{ActionError, Context},
8 action_impl::ActionImpl,
9 runlog::RunLogSource,
10 util::{mkdir, UtilError},
11};
12
13pub const RUSTUP_DIR: &str = "rustup";
15
16const DEFAULT_CHANNEL: &str = "stable";
17
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
21pub struct Rustup {
22 channel: String,
23 target: Option<String>,
24 components: Option<Vec<String>>,
25 profile: Option<String>,
26}
27
28impl Rustup {
29 pub fn new(
31 channel: &Option<String>,
32 target: &Option<String>,
33 components: &Option<Vec<String>>,
34 profile: &Option<String>,
35 ) -> Self {
36 Self {
37 channel: channel.clone().unwrap_or(DEFAULT_CHANNEL.to_string()),
38 target: target.to_owned(),
39 components: components.to_owned(),
40 profile: profile.to_owned(),
41 }
42 }
43}
44
45impl ActionImpl for Rustup {
46 fn execute(&self, context: &mut Context) -> Result<(), ActionError> {
47 context
48 .runlog()
49 .debug(RunLogSource::PrePlan, format!("rustup {self:?}"));
50
51 let dir = context.deps_dir().join(RUSTUP_DIR);
52 if !dir.exists() {
53 mkdir(&dir).map_err(|err| RustupError::RustupDir(dir.clone(), err))?;
54 }
55
56 context.set_plan_env("RUSTUP_HOME", format!("/ci/deps/{RUSTUP_DIR}"));
57
58 let mut cmd = Command::new("rustup");
59 cmd.env("RUSTUP_HOME", dir.as_os_str());
60 cmd.arg("install");
61 cmd.arg(&self.channel);
62 if let Some(v) = &self.target {
63 cmd.arg("--target");
64 cmd.arg(v);
65 }
66 if let Some(v) = &self.profile {
67 cmd.arg("--profile");
68 cmd.arg(v);
69 }
70 if let Some(list) = &self.components {
71 cmd.arg("--component");
72 let mut v = String::new();
73 for c in list {
74 if !v.is_empty() {
75 v.push(',');
76 }
77 v.push_str(c);
78 }
79 cmd.arg(v);
80 }
81
82 context.runlog().start_program(RunLogSource::PrePlan, &cmd);
83 let mut runner = CommandRunner::new(cmd);
84 runner.capture_stdout();
85 runner.capture_stderr();
86 match runner.execute() {
87 Ok(output) => {
88 context
89 .runlog()
90 .program_succeeded(RunLogSource::PrePlan, &output);
91 Ok(())
92 }
93 Err(err) => {
94 context.runlog().program_failed(RunLogSource::PrePlan, &err);
95 Err(RustupError::Rustup(err).into())
96 }
97 }
98 }
99}
100
101#[derive(Debug, thiserror::Error)]
103pub enum RustupError {
104 #[error("failed to run rustup")]
106 Rustup(#[source] CommandError),
107
108 #[error("failed to create rustup directory {0}")]
110 RustupDir(PathBuf, #[source] UtilError),
111}
112
113impl From<RustupError> for ActionError {
114 fn from(value: RustupError) -> Self {
115 Self::Rustup(value)
116 }
117}