flowey_lib_hvlite/
install_git_credential_manager.rs1use flowey::node::prelude::*;
7use flowey_lib_common::_util::wslpath;
8
9flowey_config! {
10 pub struct Config {
12 pub auto_configure: Option<bool>,
14 pub use_native_linux_on_wsl2: Option<bool>,
16 }
17}
18
19flowey_request! {
20 pub enum Request {
21 EnsureConfigured(WriteVar<SideEffect>),
23 }
24}
25
26new_flow_node_with_config!(struct Node);
27
28impl FlowNodeWithConfig for Node {
29 type Request = Request;
30 type Config = Config;
31
32 fn imports(dep: &mut ImportCtx<'_>) {
33 dep.import::<flowey_lib_common::install_git::Node>();
34 dep.import::<flowey_lib_common::check_needs_relaunch::Node>();
35 }
36
37 fn emit(
38 config: Config,
39 requests: Vec<Self::Request>,
40 ctx: &mut NodeCtx<'_>,
41 ) -> anyhow::Result<()> {
42 if !matches!(ctx.backend(), FlowBackend::Local) {
43 anyhow::bail!("only supported on the local backend at this time");
44 }
45
46 let mut ensure_configured = Vec::new();
47
48 for req in requests {
49 match req {
50 Request::EnsureConfigured(v) => ensure_configured.push(v),
51 }
52 }
53
54 let use_native_linux_on_wsl2 = config.use_native_linux_on_wsl2.unwrap_or(false);
55 let auto_configure = config.auto_configure.unwrap_or(false);
56
57 let git_ensure_installed =
60 ctx.reqv(flowey_lib_common::install_git::Request::EnsureInstalled);
61
62 let (read_env, write_env) = ctx.new_var();
63 ctx.req(flowey_lib_common::check_needs_relaunch::Params {
64 check: read_env,
65 done: ensure_configured,
66 });
67
68 ctx.emit_rust_step("configure git credential manager", move |ctx| {
69 git_ensure_installed.clone().claim(ctx);
70 let write_env = write_env.claim(ctx);
71
72 move |rt: &mut RustRuntimeServices<'_>| {
73 let mut env_to_write = None;
74 let existing_credman = flowey::shell_cmd!(rt, "git config --global credential.helper").ignore_status().read()?;
75 log::info!("existing credentials helper: {existing_credman}");
76
77 if !existing_credman.is_empty() {
78 if existing_credman.contains("git-credential-manager")
79 || existing_credman.contains("credential-manager-core")
80 || existing_credman.contains("manager")
81 {
82 log::info!("existing credentials helper matches a known-good credential helper.");
83 rt.write(write_env, &None);
84 return Ok(())
85 } else {
86 log::warn!("existing credentials helper isn't any of the known-good credential helpers.");
87 log::warn!("assume the user knows what they're doing?");
88 rt.write(write_env, &None);
89 return Ok(())
90 }
91 }
92
93 if existing_credman.is_empty() && !auto_configure {
95 log::info!("Could not detect an existing Git Credential helper.");
96 log::info!("Press <y> to automatically configure an appropriate --global configuration helper, or any other key to abort the run.");
97 let mut input = String::new();
98 std::io::stdin().read_line(&mut input)?;
99 if input.trim() != "y" {
100 anyhow::bail!("aborting...")
101 }
102 }
103
104 if flowey_lib_common::_util::running_in_wsl(rt) && !use_native_linux_on_wsl2 {
105 let windows_user_profile_path_windows = flowey::shell_cmd!(rt, "cmd.exe /c echo %UserProfile%").read().map_err(|_| anyhow::anyhow!("Unable to run cmd.exe, please restart WSL by running `wsl --shutdown` in powershell and try again."))?;
106 let windows_user_profile_path = wslpath::win_to_linux(rt, windows_user_profile_path_windows)?;
107 let gcm_path_opt_1 = windows_user_profile_path.join("AppData/Local/Programs/Git Credential Manager/git-credential-manager.exe");
108 let gcm_path_opt_2 = wslpath::win_to_linux(rt, r#"C:\Program Files\Git\ucrt64\libexec\git-core\git-credential-manager.exe"#)?;
109 let gcm_path_opt_3 = wslpath::win_to_linux(rt, r#"C:\Program Files\Git\mingw64\bin\git-credential-manager.exe"#)?;
110 let gcm_path_opt_4 = wslpath::win_to_linux(rt, r#"C:\Program Files\Git\mingw64\libexec\git-core\git-credential-manager.exe"#)?;
111 let gcm_path_opt_5 = wslpath::win_to_linux(rt, r#"C:\Program Files (x86)\Git Credential Manager\git-credential-manager.exe"#)?;
112
113 let gcm_path = if rt.sh.path_exists(&gcm_path_opt_1) {
114 &gcm_path_opt_1
115 } else if rt.sh.path_exists(&gcm_path_opt_2) {
116 &gcm_path_opt_2
117 } else if rt.sh.path_exists(&gcm_path_opt_3) {
118 &gcm_path_opt_3
119 } else if rt.sh.path_exists(&gcm_path_opt_4) {
120 &gcm_path_opt_4
121 } else if rt.sh.path_exists(&gcm_path_opt_5) {
122 &gcm_path_opt_5
123 } else {
124 anyhow::bail!("Git Credential Manager not found, please install it manually.");
125 };
126
127 if gcm_path == &gcm_path_opt_1 || gcm_path == &gcm_path_opt_5 {
128 let mut wslenv = rt.sh.var("WSLENV")?;
129 if !wslenv.contains("GIT_EXEC_PATH/wp") {
130 log::info!("Standalone Git Credential Manager has been detected.");
131 log::info!("Please run the following from an administrator command prompt to configure it:");
132 log::info!("SETX WSLENV %WSLENV%:GIT_EXEC_PATH/wp");
133 log::info!("This command shares `GIT_EXEC_PATH`, an environment variable which determines where Git looks for its sub-programs,");
134 log::info!(" with WSL processes spawned from Win32 and vice versa. `/wp` are flags specifying how `GIT_EXEC_PATH` gets translated.");
135 log::info!("Please refer to <https://devblogs.microsoft.com/commandline/share-environment-vars-between-wsl-and-windows/>");
136 log::info!("and <https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables> for more details.");
137
138 let do_config = if !auto_configure {
139 wslenv.push_str(":GIT_EXEC_PATH/wp");
140
141 log::info!("Please press <y> to automatically configure it, or any other key to continue after manual configuration.");
142 let mut input = String::new();
143 std::io::stdin().read_line(&mut input)?;
144 input.trim() == "y"
145 } else {
146 true
147 };
148
149 if do_config {
150 flowey::shell_cmd!(rt, "setx.exe WSLENV {wslenv}").run()?;
151 }
152
153 env_to_write = Some(flowey_lib_common::check_needs_relaunch::BinOrEnv::Env("WSLENV".to_string(), "GIT_EXEC_PATH/wp".to_string()));
154 }
155 }
156
157 let gcm_path_str = gcm_path
159 .to_str()
160 .context("Git Credential Manager path is not valid UTF-8")?
161 .replace(' ', "\\ ");
162 flowey::shell_cmd!(rt, "git config --global credential.helper {gcm_path_str}").run()?;
163 flowey::shell_cmd!(rt, "git config --global credential.https://dev.azure.com.useHttpPath true").run()?;
164 } else if matches!(rt.platform(), FlowPlatform::Windows) {
165 flowey::shell_cmd!(rt, "git config --global credential.helper manager").run()?;
166 } else {
167 anyhow::bail!("git credential manager configuration only supported for windows/wsl2 at this time")
168 }
169
170 rt.write(write_env, &env_to_write);
171 Ok(())
172 }
173 });
174
175 Ok(())
176 }
177}