Skip to main content

flowey_lib_common/
download_gh_cli.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Download a copy of the GitHub CLI.
5//!
6//! NOTE: this node will _not_ set up any form of authentication for the
7//! downloaded CLI binary!
8
9use crate::cache::CacheHit;
10use flowey::node::prelude::*;
11
12flowey_config! {
13    /// Config for the download_gh_cli node.
14    pub struct Config {
15        /// Version of `gh` to download (e.g: 2.52.0)
16        pub version: Option<String>,
17    }
18}
19
20flowey_request! {
21    pub enum Request {
22        /// Get a path to downloaded `gh`
23        Get(WriteVar<PathBuf>),
24    }
25}
26
27new_flow_node_with_config!(struct Node);
28
29impl FlowNodeWithConfig for Node {
30    type Request = Request;
31    type Config = Config;
32
33    fn imports(ctx: &mut ImportCtx<'_>) {
34        ctx.import::<crate::install_dist_pkg::Node>();
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 install_reqs = Vec::new();
44
45        for req in requests {
46            match req {
47                Request::Get(v) => install_reqs.push(v),
48            }
49        }
50
51        let version = config
52            .version
53            .ok_or(anyhow::anyhow!("missing config: version"))?;
54        let install_reqs = install_reqs;
55
56        // -- end of req processing -- //
57
58        if install_reqs.is_empty() {
59            return Ok(());
60        }
61
62        let gh_bin = ctx.platform().binary("gh");
63
64        let gh_arch = match ctx.arch() {
65            FlowArch::X86_64 => "amd64",
66            FlowArch::Aarch64 => "arm64",
67            arch => anyhow::bail!("unsupported architecture {arch}"),
68        };
69
70        let cache_dir = ctx.emit_rust_stepv("create gh cache dir", |_| {
71            |_| Ok(std::env::current_dir()?.absolute()?)
72        });
73
74        let cache_key = ReadVar::from_static(format!("gh-cli-{version}"));
75        let hitvar = ctx.reqv(|hitvar| crate::cache::Request {
76            label: "gh-cli".into(),
77            dir: cache_dir.clone(),
78            key: cache_key,
79            restore_keys: None,
80            hitvar,
81        });
82
83        ctx.emit_rust_step("installing gh", |ctx| {
84            let cache_dir = cache_dir.claim(ctx);
85            let hitvar = hitvar.claim(ctx);
86            let install_reqs = install_reqs.claim(ctx);
87            move |rt| {
88                let cache_dir = rt.read(cache_dir);
89
90                let cached = if matches!(rt.read(hitvar), CacheHit::Hit) {
91                    let cached_bin = cache_dir.join(&gh_bin);
92                    assert!(cached_bin.exists());
93                    Some(cached_bin)
94                } else {
95                    None
96                };
97
98                let path_to_gh = if let Some(cached) = cached {
99                    cached
100                } else {
101                    match rt.platform() {
102                        FlowPlatform::Windows => {
103                            flowey::shell_cmd!(rt, "curl --fail -L https://github.com/cli/cli/releases/download/v{version}/gh_{version}_windows_{gh_arch}.zip -o gh.zip").run()?;
104                            flowey::shell_cmd!(rt, "tar -xf gh.zip").run()?;
105                        },
106                        FlowPlatform::Linux(_) => {
107                            flowey::shell_cmd!(rt, "curl --fail -L https://github.com/cli/cli/releases/download/v{version}/gh_{version}_linux_{gh_arch}.tar.gz -o gh.tar.gz").run()?;
108                            flowey::shell_cmd!(rt, "tar -xf gh.tar.gz --strip-components=1").run()?;
109                        },
110                        FlowPlatform::MacOs => {
111                            flowey::shell_cmd!(rt, "curl --fail -L https://github.com/cli/cli/releases/download/v{version}/gh_{version}_macOS_{gh_arch}.zip -o gh.zip").run()?;
112                            flowey::shell_cmd!(rt, "tar -xf gh.zip --strip-components=1").run()?;
113                        }
114                        platform => anyhow::bail!("unsupported platform {platform}"),
115                    };
116
117                    // move the unzipped bin into the cache dir
118                    let final_bin = cache_dir.join(&gh_bin);
119                    fs_err::rename(format!("bin/{gh_bin}"), &final_bin)?;
120
121                    final_bin.absolute()?
122                };
123
124                for var in install_reqs {
125                    rt.write(var, &path_to_gh)
126                }
127
128                Ok(())
129            }
130        });
131
132        Ok(())
133    }
134}