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                        if let Ok(path) = check_az_install(rt) {
124                            rt.write_all(get_az_cli, &path);
125                            return Ok(());
126                        }
127                        match rt.platform() {
128                            FlowPlatform::Windows => {
129                                let az_dir = rt.sh.current_dir().join("az");
130                                rt.sh.create_dir(&az_dir)?;
131                                rt.sh.change_dir(&az_dir);
132                                flowey::shell_cmd!(
133                                    rt,
134                                    "curl --fail -L https://aka.ms/installazurecliwindowszipx64 -o az.zip"
135                                )
136                                .run()?;
137                                flowey::shell_cmd!(rt, "tar -xf az.zip").run()?;
138                                rt.write_all(get_az_cli, &az_dir.join("bin\\az.cmd"));
139                            }
140                            FlowPlatform::Linux(_) => {
141                                flowey::shell_cmd!(
142                                    rt,
143                                    "curl --fail -sL https://aka.ms/InstallAzureCLIDeb -o InstallAzureCLIDeb.sh"
144                                )
145                                .run()?;
146                                flowey::shell_cmd!(rt, "chmod +x ./InstallAzureCLIDeb.sh").run()?;
147                                flowey::shell_cmd!(rt, "sudo ./InstallAzureCLIDeb.sh").run()?;
148                                let path = check_az_install(rt)?;
149                                rt.write_all(get_az_cli, &path);
150                            }
151                            platform => anyhow::bail!("unsupported platform {platform}"),
152                        };
153
154                        Ok(())
155                    }
156                })
157            }
158        };
159
160        Ok(())
161    }
162}