flowey_lib_common/
install_azure_cli.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! Globally install the Azure CLI (`az`)

use flowey::node::prelude::*;

flowey_request! {
    pub enum Request {
        /// Automatically install all required azure-cli tools and components.
        ///
        /// This must be set to true/false when running locally.
        AutoInstall(bool),
        /// Which version of azure-cli to install (e.g: 2.57.0)
        Version(String),
        /// Get a path to `az`
        GetAzureCli(WriteVar<PathBuf>),
    }
}

new_flow_node!(struct Node);

impl FlowNode for Node {
    type Request = Request;

    fn imports(_ctx: &mut ImportCtx<'_>) {}

    fn emit(requests: Vec<Self::Request>, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
        let mut auto_install = None;
        let mut version = None;
        let mut get_az_cli = Vec::new();

        for req in requests {
            match req {
                Request::AutoInstall(v) => {
                    same_across_all_reqs("AutoInstall", &mut auto_install, v)?
                }
                Request::Version(v) => same_across_all_reqs("Version", &mut version, v)?,
                Request::GetAzureCli(v) => get_az_cli.push(v),
            }
        }

        // don't require specifying a Version if no one requested az to
        // be installed
        if get_az_cli.is_empty() {
            return Ok(());
        }

        let auto_install = auto_install;
        let version = version.ok_or(anyhow::anyhow!("Missing essential request: Version"))?;
        let get_az_cli = get_az_cli;

        // -- end of req processing -- //

        let check_az_install = {
            |_rt: &RustRuntimeServices<'_>| -> anyhow::Result<PathBuf> {
                let Ok(path) = which::which("az") else {
                    anyhow::bail!("did not find `az` on $PATH");
                };

                // FUTURE: should also perform version checks...
                anyhow::Ok(path)
            }
        };

        match ctx.backend() {
            FlowBackend::Local => {
                let auto_install = auto_install
                    .ok_or(anyhow::anyhow!("Missing essential request: AutoInstall"))?;

                if auto_install {
                    ctx.emit_rust_step("installing azure-cli", |ctx| {
                        let get_az_cli = get_az_cli.claim(ctx);
                        move |rt| {
                            log::warn!("automatic azure-cli installation is not supported yet!");
                            log::warn!(
                                "follow the guide, and manually ensure you have azure-cli installed"
                            );
                            log::warn!("  ensure you have azure-cli version {version} installed");
                            log::warn!("press <enter> to continue");
                            let _ = std::io::stdin().read_line(&mut String::new());

                            let path = check_az_install(rt)?;
                            rt.write_all(get_az_cli, &path);
                            Ok(())
                        }
                    })
                } else {
                    ctx.emit_rust_step("detecting azure-cli install", |ctx| {
                        let get_az_cli = get_az_cli.claim(ctx);
                        move |rt| {
                            let path = check_az_install(rt)?;
                            rt.write_all(get_az_cli, &path);
                            Ok(())
                        }
                    })
                }
            }
            FlowBackend::Ado => {
                if !auto_install.unwrap_or(true) {
                    anyhow::bail!("AutoInstall must be `true` when running on ADO")
                }

                // FUTURE: don't assume that all ADO workers come with azure-cli
                // pre-installed.
                ctx.emit_rust_step("detecting azure-cli install", |ctx| {
                    let get_az_cli = get_az_cli.claim(ctx);
                    move |rt| {
                        let path = check_az_install(rt)?;
                        rt.write_all(get_az_cli, &path);
                        Ok(())
                    }
                })
            }
            FlowBackend::Github => {
                if !auto_install.unwrap_or(true) {
                    anyhow::bail!("AutoInstall must be `true` when running on Github Actions")
                }

                ctx.emit_rust_step("installing azure-cli", |ctx| {
                    let get_az_cli = get_az_cli.claim(ctx);
                    move |rt| {
                        let sh = xshell::Shell::new()?;
                        if let Ok(path) = check_az_install(rt) {
                            rt.write_all(get_az_cli, &path);
                            return Ok(());
                        }
                        match rt.platform() {
                            FlowPlatform::Windows => {
                                let az_dir = sh.current_dir().join("az");
                                sh.create_dir(&az_dir)?;
                                sh.change_dir(&az_dir);
                                xshell::cmd!(
                                    sh,
                                    "curl --fail -L https://aka.ms/installazurecliwindowszipx64 -o az.zip"
                                )
                                .run()?;
                                xshell::cmd!(sh, "tar -xf az.zip").run()?;
                                rt.write_all(get_az_cli, &az_dir.join("bin\\az.cmd"));
                            }
                            FlowPlatform::Linux(_) => {
                                xshell::cmd!(
                                    sh,
                                    "curl --fail -sL https://aka.ms/InstallAzureCLIDeb -o InstallAzureCLIDeb.sh"
                                )
                                .run()?;
                                xshell::cmd!(sh, "chmod +x ./InstallAzureCLIDeb.sh").run()?;
                                xshell::cmd!(sh, "sudo ./InstallAzureCLIDeb.sh").run()?;
                                let path = check_az_install(rt)?;
                                rt.write_all(get_az_cli, &path);
                            }
                            platform => anyhow::bail!("unsupported platform {platform}"),
                        };

                        Ok(())
                    }
                })
            }
        };

        Ok(())
    }
}