Skip to main content

flowey_lib_common/
download_cargo_nextest.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Download a copy of `cargo-nextest`.
5
6use crate::cache::CacheHit;
7use flowey::node::prelude::*;
8use std::collections::BTreeMap;
9
10flowey_config! {
11    /// Config for the download_cargo_nextest node.
12    pub struct Config {
13        /// Version of `cargo nextest` to install (e.g: "0.9.57")
14        pub version: Option<String>,
15    }
16}
17
18flowey_request! {
19    pub enum Request {
20        /// Download `cargo-nextest` as a standalone binary, without requiring Rust
21        /// to be installed.
22        ///
23        /// Useful when running archived nextest tests in a separate job.
24        Get(target_lexicon::Triple, WriteVar<PathBuf>),
25    }
26}
27
28new_flow_node_with_config!(struct Node);
29
30impl FlowNodeWithConfig for Node {
31    type Request = Request;
32    type Config = Config;
33
34    fn imports(ctx: &mut ImportCtx<'_>) {
35        ctx.import::<crate::cache::Node>();
36    }
37
38    fn emit(
39        config: Config,
40        requests: Vec<Self::Request>,
41        ctx: &mut NodeCtx<'_>,
42    ) -> anyhow::Result<()> {
43        let mut reqs: BTreeMap<String, Vec<WriteVar<PathBuf>>> = BTreeMap::new();
44
45        for req in requests {
46            match req {
47                Request::Get(target, path) => {
48                    reqs.entry(target.to_string()).or_default().push(path)
49                }
50            }
51        }
52
53        let version = config
54            .version
55            .ok_or(anyhow::anyhow!("missing config: version"))?;
56        let reqs = reqs;
57
58        // -- end of req processing -- //
59
60        if reqs.is_empty() {
61            return Ok(());
62        }
63
64        let cache_dir = ctx.emit_rust_stepv("create cargo-nextest cache dir", |_| {
65            |_| Ok(std::env::current_dir()?.absolute()?)
66        });
67
68        for (target, paths) in reqs {
69            let (cache_key, cache_dir) = {
70                let version = version.clone();
71                let cache_key = format!("cargo-nextest-{version}-{target}");
72                let cache_dir = cache_dir.map(ctx, {
73                    let k = cache_key.clone();
74                    |p| p.join(k)
75                });
76                (ReadVar::from_static(cache_key), cache_dir)
77            };
78
79            let hitvar = ctx.reqv(|v| {
80                crate::cache::Request {
81                    label: "cargo-nextest".into(),
82                    dir: cache_dir.clone(),
83                    key: cache_key,
84                    restore_keys: None, // we want an exact hit
85                    hitvar: v,
86                }
87            });
88
89            let version = version.clone();
90            ctx.emit_rust_step("downloading cargo-nextest", |ctx| {
91                let paths = paths.claim(ctx);
92                let cache_dir = cache_dir.claim(ctx);
93                let hitvar = hitvar.claim(ctx);
94
95                move |rt| {
96                    let cache_dir = rt.read(cache_dir);
97
98                    let cargo_nextest_bin = if target.contains("windows") {
99                        "cargo-nextest.exe"
100                    } else {
101                        "cargo-nextest"
102                    };
103                    let cached_bin_path = cache_dir.join(cargo_nextest_bin);
104
105                    if !matches!(rt.read(hitvar), CacheHit::Hit) {
106                        download_cargo_nextest(rt, version, target)?;
107
108                        // move the downloaded bin into the cache dir
109                        fs_err::create_dir_all(&cache_dir)?;
110                        fs_err::rename(cargo_nextest_bin, &cached_bin_path)?;
111                    }
112
113                    let cached_bin_path = cached_bin_path.absolute()?;
114                    log::info!("downloaded to {}", cached_bin_path.to_string_lossy());
115                    assert!(cached_bin_path.exists());
116                    for path in paths {
117                        rt.write(path, &cached_bin_path);
118                    }
119
120                    Ok(())
121                }
122            });
123        }
124
125        Ok(())
126    }
127}
128
129/// downloads and extracts nextest to the current dir.
130/// split out to make rustfmt happy.
131fn download_cargo_nextest(
132    rt: &mut RustRuntimeServices<'_>,
133    version: String,
134    target: String,
135) -> anyhow::Result<()> {
136    let nextest_archive = "nextest.tar.gz";
137    flowey::shell_cmd!(
138        rt,
139        "curl --fail -L https://get.nexte.st/{version}/{target}.tar.gz -o {nextest_archive}"
140    )
141    .run()?;
142    flowey::shell_cmd!(rt, "tar -xf {nextest_archive}").run()?;
143
144    Ok(())
145}