flowey_lib_common/
install_azure_cli.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Globally install the Azure CLI (`az`)
5
6use flowey::node::prelude::*;
7
8flowey_request! {
9    pub enum Request {
10        /// Automatically install all required azure-cli tools and components.
11        ///
12        /// This must be set to true/false when running locally.
13        AutoInstall(bool),
14        /// Which version of azure-cli to install (e.g: 2.57.0)
15        Version(String),
16        /// Get a path to `az`
17        GetAzureCli(WriteVar<PathBuf>),
18    }
19}
20
21new_flow_node!(struct Node);
22
23impl FlowNode for Node {
24    type Request = Request;
25
26    fn imports(_ctx: &mut ImportCtx<'_>) {}
27
28    fn emit(requests: Vec<Self::Request>, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
29        let mut auto_install = None;
30        let mut version = None;
31        let mut get_az_cli = Vec::new();
32
33        for req in requests {
34            match req {
35                Request::AutoInstall(v) => {
36                    same_across_all_reqs("AutoInstall", &mut auto_install, v)?
37                }
38                Request::Version(v) => same_across_all_reqs("Version", &mut version, v)?,
39                Request::GetAzureCli(v) => get_az_cli.push(v),
40            }
41        }
42
43        // don't require specifying a Version if no one requested az to
44        // be installed
45        if get_az_cli.is_empty() {
46            return Ok(());
47        }
48
49        let auto_install = auto_install;
50        let version = version.ok_or(anyhow::anyhow!("Missing essential request: Version"))?;
51        let get_az_cli = get_az_cli;
52
53        // -- end of req processing -- //
54
55        let check_az_install = {
56            |_rt: &RustRuntimeServices<'_>| -> anyhow::Result<PathBuf> {
57                let Ok(path) = which::which("az") else {
58                    anyhow::bail!("did not find `az` on $PATH");
59                };
60
61                // FUTURE: should also perform version checks...
62                anyhow::Ok(path)
63            }
64        };
65
66        match ctx.backend() {
67            FlowBackend::Local => {
68                let auto_install = auto_install
69                    .ok_or(anyhow::anyhow!("Missing essential request: AutoInstall"))?;
70
71                if auto_install {
72                    ctx.emit_rust_step("installing azure-cli", |ctx| {
73                        let get_az_cli = get_az_cli.claim(ctx);
74                        move |rt| {
75                            log::warn!("automatic azure-cli installation is not supported yet!");
76                            log::warn!(
77                                "follow the guide, and manually ensure you have azure-cli installed"
78                            );
79                            log::warn!("  ensure you have azure-cli version {version} installed");
80                            log::warn!("press <enter> to continue");
81                            let _ = std::io::stdin().read_line(&mut String::new());
82
83                            let path = check_az_install(rt)?;
84                            rt.write_all(get_az_cli, &path);
85                            Ok(())
86                        }
87                    })
88                } else {
89                    ctx.emit_rust_step("detecting azure-cli install", |ctx| {
90                        let get_az_cli = get_az_cli.claim(ctx);
91                        move |rt| {
92                            let path = check_az_install(rt)?;
93                            rt.write_all(get_az_cli, &path);
94                            Ok(())
95                        }
96                    })
97                }
98            }
99            FlowBackend::Ado => {
100                if !auto_install.unwrap_or(true) {
101                    anyhow::bail!("AutoInstall must be `true` when running on ADO")
102                }
103
104                // FUTURE: don't assume that all ADO workers come with azure-cli
105                // pre-installed.
106                ctx.emit_rust_step("detecting azure-cli install", |ctx| {
107                    let get_az_cli = get_az_cli.claim(ctx);
108                    move |rt| {
109                        let path = check_az_install(rt)?;
110                        rt.write_all(get_az_cli, &path);
111                        Ok(())
112                    }
113                })
114            }
115            FlowBackend::Github => {
116                if !auto_install.unwrap_or(true) {
117                    anyhow::bail!("AutoInstall must be `true` when running on Github Actions")
118                }
119
120                ctx.emit_rust_step("installing azure-cli", |ctx| {
121                    let get_az_cli = get_az_cli.claim(ctx);
122                    move |rt| {
123                        let sh = xshell::Shell::new()?;
124                        if let Ok(path) = check_az_install(rt) {
125                            rt.write_all(get_az_cli, &path);
126                            return Ok(());
127                        }
128                        match rt.platform() {
129                            FlowPlatform::Windows => {
130                                let az_dir = sh.current_dir().join("az");
131                                sh.create_dir(&az_dir)?;
132                                sh.change_dir(&az_dir);
133                                xshell::cmd!(
134                                    sh,
135                                    "curl --fail -L https://aka.ms/installazurecliwindowszipx64 -o az.zip"
136                                )
137                                .run()?;
138                                xshell::cmd!(sh, "tar -xf az.zip").run()?;
139                                rt.write_all(get_az_cli, &az_dir.join("bin\\az.cmd"));
140                            }
141                            FlowPlatform::Linux(_) => {
142                                xshell::cmd!(
143                                    sh,
144                                    "curl --fail -sL https://aka.ms/InstallAzureCLIDeb -o InstallAzureCLIDeb.sh"
145                                )
146                                .run()?;
147                                xshell::cmd!(sh, "chmod +x ./InstallAzureCLIDeb.sh").run()?;
148                                xshell::cmd!(sh, "sudo ./InstallAzureCLIDeb.sh").run()?;
149                                let path = check_az_install(rt)?;
150                                rt.write_all(get_az_cli, &path);
151                            }
152                            platform => anyhow::bail!("unsupported platform {platform}"),
153                        };
154
155                        Ok(())
156                    }
157                })
158            }
159        };
160
161        Ok(())
162    }
163}