Skip to main content

cargo_dist/backend/ci/
mod.rs

1//! Support for generating CI scripts for running dist
2
3use cargo_dist_schema::{
4    target_lexicon::{OperatingSystem, Triple},
5    DashScript, GhaRunStep, PowershellScript,
6};
7use semver::Version;
8use serde::Serialize;
9
10use crate::config::v0::CargoDistUrlOverrideRef;
11
12use self::github::GithubCiInfo;
13
14pub mod github;
15
16/// The current version of dist
17const SELF_DIST_VERSION: &str = env!("CARGO_PKG_VERSION");
18const BASE_DIST_FETCH_URL: &str = "https://github.com/axodotdev/cargo-dist/releases/download";
19
20// NOTE: This is hard-coded to download latest.
21const BASE_CARGO_AUDITABLE_FETCH_LATEST_URL: &str =
22    "https://github.com/rust-secure-code/cargo-auditable/releases/latest/download";
23
24const BASE_CARGO_CYCLONEDX_FETCH_URL: &str =
25    "https://github.com/CycloneDX/cyclonedx-rust-cargo/releases/download";
26
27// NOTE: This is hard-coded to a specific version because both cargo-cyclonedx
28//       and cyclonedx-bom are released on the same repo.
29//       This means the "latest" release is sometimes NOT actually cargo-cyclonedx!
30const CARGO_CYCLONEDX_VERSION: &str = "0.5.5";
31
32const BASE_OMNIBOR_FETCH_URL: &str = "https://github.com/omnibor/omnibor-rs/releases/download";
33
34// NOTE: This is hard-coded to a specific version because omnibor-cli,
35//       omnibor-rs, and gitoid are released on the same repo.
36//       This means the "latest" release is sometimes NOT actually omnibor-cli!
37const OMNIBOR_VERSION: &str = "0.7.0";
38
39/// Info about all the enabled CI backends
40#[derive(Debug, Default)]
41pub struct CiInfo {
42    /// Github CI
43    pub github: Option<GithubCiInfo>,
44}
45
46/// Gives us the full information re: the version of dist we're supposed
47/// to install/run in CI
48struct DistInstallSettings<'a> {
49    version: &'a Version,
50    url_override: Option<&'a CargoDistUrlOverrideRef>,
51}
52
53/// Generates github steps to install a tool
54pub trait InstallStrategy {
55    /// Return a sh/dash script
56    fn dash(&self) -> GhaRunStep;
57
58    /// Return a powershell script
59    fn powershell(&self) -> GhaRunStep;
60
61    /// Return the right install method for a given set of targets
62    fn for_triple(&self, triple: &Triple) -> GhaRunStep {
63        match triple.operating_system {
64            OperatingSystem::Linux | OperatingSystem::Darwin(_) => self.dash(),
65            OperatingSystem::Windows => self.powershell(),
66            _ => panic!("unsupported host triple {triple}"),
67        }
68    }
69}
70
71/// The strategy used to install dist in CI
72#[derive(Debug, Clone, Serialize)]
73pub enum DistInstallStrategy {
74    /// Download an installer and run it
75    Installer {
76        /// the prefix of the installer url, e.g.
77        installer_url: String,
78        /// the installer name, without `.sh` or `.ps1`
79        installer_name: String,
80    },
81    /// Run `cargo install --git` (slow!)
82    GitBranch {
83        /// the branch to install from — from <https://github.com/axodotdev/cargo-dist>
84        branch: String,
85    },
86}
87
88impl DistInstallSettings<'_> {
89    fn install_strategy(&self) -> DistInstallStrategy {
90        if let Some(branch) = self.version.pre.strip_prefix("github-") {
91            return DistInstallStrategy::GitBranch {
92                branch: branch.to_owned(),
93            };
94        }
95
96        if let Some(url) = self.url_override.as_ref() {
97            return DistInstallStrategy::Installer {
98                installer_url: url.as_str().to_owned(),
99                installer_name: "cargo-dist-installer".to_owned(),
100            };
101        }
102
103        let version = self.version;
104        let format = cargo_dist_schema::format_of_version(version);
105        let installer_name = if format.unsupported() {
106            // FIXME: we should probably do this check way higher up and produce a proper err...
107            panic!("requested dist v{version}, which is not supported by the this copy of dist ({SELF_DIST_VERSION})");
108        } else if format.artifact_names_contain_versions() {
109            format!("cargo-dist-v{version}-installer")
110        } else {
111            "cargo-dist-installer".to_owned()
112        };
113
114        DistInstallStrategy::Installer {
115            // FIXME: it would be nice if these values were somehow using all the machinery
116            // to compute these values for packages we build *BUT* it's messy and not that important
117            installer_url: format!("{BASE_DIST_FETCH_URL}/v{version}"),
118            installer_name,
119        }
120    }
121}
122
123impl InstallStrategy for DistInstallStrategy {
124    /// Returns a bit of sh/dash to install the requested version of dist
125    fn dash(&self) -> GhaRunStep {
126        DashScript::new(match self {
127            DistInstallStrategy::Installer { installer_url, installer_name } => format!(
128                "curl --proto '=https' --tlsv1.2 -LsSf {installer_url}/{installer_name}.sh | sh"
129            ),
130            DistInstallStrategy::GitBranch { branch } => format!(
131                "cargo install --git https://github.com/axodotdev/cargo-dist/ --branch={branch} --locked cargo-dist"
132            ),
133        }).into()
134    }
135
136    /// Returns a bit of powershell to install the requested version of dist
137    fn powershell(&self) -> GhaRunStep {
138        PowershellScript::new(match self {
139            DistInstallStrategy::Installer { installer_url, installer_name } => format!(
140                "irm {installer_url}/{installer_name}.ps1 | iex"
141            ),
142            DistInstallStrategy::GitBranch { branch } => format!(
143                "cargo install --git https://github.com/axodotdev/cargo-dist/ --branch={branch} --locked cargo-dist"
144            ),
145        }).into()
146    }
147}
148
149struct CargoAuditableInstallStrategy;
150
151impl InstallStrategy for CargoAuditableInstallStrategy {
152    /// Return a sh/dash script
153    fn dash(&self) -> GhaRunStep {
154        let installer_url =
155            format!("{BASE_CARGO_AUDITABLE_FETCH_LATEST_URL}/cargo-auditable-installer.sh");
156        DashScript::new(format!(
157            "curl --proto '=https' --tlsv1.2 -LsSf {installer_url} | sh"
158        ))
159        .into()
160    }
161
162    /// Return a powershell script
163    fn powershell(&self) -> GhaRunStep {
164        let installer_url =
165            format!("{BASE_CARGO_AUDITABLE_FETCH_LATEST_URL}/cargo-auditable-installer.ps1");
166        PowershellScript::new(format!(r#"powershell -c "irm {installer_url} | iex""#)).into()
167    }
168}
169
170struct CargoCyclonedxInstallStrategy;
171
172impl InstallStrategy for CargoCyclonedxInstallStrategy {
173    /// Return an sh/dash script to install cargo-cyclonedx
174    fn dash(&self) -> GhaRunStep {
175        let installer_url =
176            format!("{BASE_CARGO_CYCLONEDX_FETCH_URL}/cargo-cyclonedx-{CARGO_CYCLONEDX_VERSION}/cargo-cyclonedx-installer.sh");
177        DashScript::new(format!(
178            "curl --proto '=https' --tlsv1.2 -LsSf {installer_url} | sh"
179        ))
180        .into()
181    }
182
183    /// Return a powershell script to install cargo-cyclonedx.
184    /// This probably isn't being used.
185    fn powershell(&self) -> GhaRunStep {
186        let installer_url =
187            format!("{BASE_CARGO_CYCLONEDX_FETCH_URL}/cargo-cyclonedx-{CARGO_CYCLONEDX_VERSION}/cargo-cyclonedx-installer.ps1");
188        PowershellScript::new(format!(r#"powershell -c "irm {installer_url} | iex""#)).into()
189    }
190}
191
192struct OmniborInstallStrategy;
193
194impl InstallStrategy for OmniborInstallStrategy {
195    /// Return an sh/dash script to install cargo-cyclonedx
196    fn dash(&self) -> GhaRunStep {
197        let installer_url = format!(
198            "{BASE_OMNIBOR_FETCH_URL}/omnibor-cli-v{OMNIBOR_VERSION}/omnibor-cli-installer.sh"
199        );
200        DashScript::new(format!(
201            "curl --proto '=https' --tlsv1.2 -LsSf {installer_url} | sh"
202        ))
203        .into()
204    }
205
206    /// Return a powershell script to install cargo-cyclonedx.
207    /// This probably isn't being used.
208    fn powershell(&self) -> GhaRunStep {
209        let installer_url = format!(
210            "{BASE_OMNIBOR_FETCH_URL}/omnibor-cli-v{OMNIBOR_VERSION}/omnibor-cli-installer.ps1"
211        );
212        PowershellScript::new(format!(r#"powershell -c "irm {installer_url} | iex""#)).into()
213    }
214}