flowey_lib_common/
install_git.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Globally install git and ensure it is available on the user's $PATH
5
6use flowey::node::prelude::*;
7
8new_flow_node!(struct Node);
9
10flowey_request! {
11    pub enum Request {
12        /// Ensure that Git was installed and is available on $PATH
13        EnsureInstalled(WriteVar<SideEffect>),
14
15        /// Automatically install Git
16        LocalOnlyAutoInstall(bool),
17    }
18}
19
20impl FlowNode for Node {
21    type Request = Request;
22
23    fn imports(dep: &mut ImportCtx<'_>) {
24        dep.import::<crate::check_needs_relaunch::Node>();
25        dep.import::<crate::install_dist_pkg::Node>();
26    }
27
28    fn emit(requests: Vec<Self::Request>, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
29        let mut ensure_installed = Vec::new();
30        let mut auto_install = None;
31
32        for req in requests {
33            match req {
34                Request::EnsureInstalled(v) => ensure_installed.push(v),
35                Request::LocalOnlyAutoInstall(v) => {
36                    same_across_all_reqs("LocalOnlyAutoInstall", &mut auto_install, v)?
37                }
38            }
39        }
40
41        let ensure_installed = ensure_installed;
42        let auto_install = auto_install.ok_or(anyhow::anyhow!(
43            "Missing essential request: LocalOnlyAutoInstall",
44        ))?;
45
46        // -- end of req processing -- //
47
48        if ensure_installed.is_empty() {
49            return Ok(());
50        }
51
52        if auto_install {
53            let (read_bin, write_bin) = ctx.new_var();
54            ctx.req(crate::check_needs_relaunch::Params {
55                check: read_bin,
56                done: ensure_installed,
57            });
58
59            let git_installed = ctx.reqv(|v| crate::install_dist_pkg::Request::Install {
60                package_names: vec!["git".into()],
61                done: v,
62            });
63
64            ctx.emit_rust_step("install git", |ctx| {
65                let write_bin = write_bin.claim(ctx);
66                git_installed.claim(ctx);
67
68                |rt: &mut RustRuntimeServices<'_>| {
69                    match rt.platform() {
70                        FlowPlatform::Linux(_) | FlowPlatform::MacOs => {
71                            rt.write(write_bin, &Some(crate::check_needs_relaunch::BinOrEnv::Bin("git".to_string())));
72                            Ok(())
73                        },
74                        FlowPlatform::Windows => {
75                            if which::which("git.exe").is_err() {
76                                let sh = xshell::Shell::new()?;
77                                xshell::cmd!(sh, "powershell.exe winget install --id Microsoft.Git --accept-source-agreements").run()?;
78                            }
79
80                            rt.write(write_bin, &Some(crate::check_needs_relaunch::BinOrEnv::Bin("git".to_string())));
81                            Ok(())
82                        },
83                        platform => anyhow::bail!("unsupported platform {platform}"),
84                    }
85                }
86            });
87        } else {
88            ctx.emit_rust_step("ensure git is installed", |ctx| {
89                ensure_installed.claim(ctx);
90                |_rt| {
91                    if which::which("git").is_err() {
92                        anyhow::bail!("Please install git to continue setup.");
93                    }
94
95                    Ok(())
96                }
97            });
98        }
99
100        Ok(())
101    }
102}