flowey_lib_common/
install_nuget_azure_credential_provider.rs1use crate::download_nuget_exe::NugetInstallPlatform;
7use flowey::node::prelude::*;
8
9flowey_request! {
10 pub enum Request {
11 EnsureAuth(WriteVar<SideEffect>),
12 LocalOnlyAutoInstall(bool),
13 LocalOnlySkipAuthCheck(bool),
14 }
15}
16
17new_flow_node!(struct Node);
18
19impl FlowNode for Node {
20 type Request = Request;
21
22 fn imports(ctx: &mut ImportCtx<'_>) {
23 ctx.import::<crate::ado_task_nuget_authenticate::Node>();
24 ctx.import::<super::download_nuget_exe::Node>();
25 }
26
27 fn emit(requests: Vec<Self::Request>, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
28 let mut ensure_auth = Vec::new();
29 let mut auto_install = None;
30 let mut skip_auth_check = None;
31
32 for req in requests {
33 match req {
34 Request::EnsureAuth(v) => ensure_auth.push(v),
35 Request::LocalOnlyAutoInstall(v) => {
36 same_across_all_reqs("LocalOnlyAutoInstall", &mut auto_install, v)?;
37 }
38 Request::LocalOnlySkipAuthCheck(v) => {
39 same_across_all_reqs("LocalOnlySkipAuthCheck", &mut skip_auth_check, v)?;
40 }
41 }
42 }
43
44 if ensure_auth.is_empty() {
45 return Ok(());
46 }
47
48 if matches!(ctx.backend(), FlowBackend::Ado) {
49 if auto_install.is_some() {
50 anyhow::bail!("can only use `LocalOnlyAutoInstall` when using the Local backend");
51 }
52
53 if skip_auth_check.is_some() {
54 anyhow::bail!("can only use `LocalOnlySkipAuthCheck` when using the Local backend");
55 }
56
57 for v in ensure_auth {
61 ctx.req(crate::ado_task_nuget_authenticate::Request::EnsureAuth(v));
62 }
63 } else if matches!(ctx.backend(), FlowBackend::Local) {
64 let auto_install = auto_install.ok_or(anyhow::anyhow!(
65 "Missing essential request: LocalOnlyAutoInstall",
66 ))?;
67 let skip_auth_check = skip_auth_check.ok_or(anyhow::anyhow!(
68 "Missing essential request: LocalOnlySkipAuthCheck",
69 ))?;
70
71 let nuget_config_platform =
74 ctx.reqv(super::download_nuget_exe::Request::NugetInstallPlatform);
75
76 if auto_install {
77 ctx.emit_rust_step(
78 "Install Azure Artifacts Credential Provider",
79 move |ctx|{
80 let nuget_config_platform = nuget_config_platform.claim(ctx);
81 ensure_auth.claim(ctx);
82
83 move |rt| {
84 let nuget_config_platform = rt.read(nuget_config_platform);
85 if check_if_aacp_installed(rt, &nuget_config_platform).is_ok() {
86 return Ok(())
87 }
88
89 let sh = xshell::Shell::new()?;
90
91 if matches!(nuget_config_platform, NugetInstallPlatform::Windows) {
92 let install_aacp_cmd = r#""& { $(irm https://aka.ms/install-artifacts-credprovider.ps1) } -AddNetfx""#;
93 xshell::cmd!(sh, "powershell.exe iex {install_aacp_cmd}").run()?;
94 } else {
95 log::warn!("automatic Azure Artifacts Credential Provider installation is not supported yet for this platform!");
96 log::warn!("follow the guide, and press <enter> to continue");
97 let _ = std::io::stdin().read_line(&mut String::new());
98 }
99
100 check_if_aacp_installed(rt, &nuget_config_platform)?;
101
102 Ok(())
103 }
104 }
105 );
106 } else {
107 ctx.emit_rust_step(
108 "Check if Azure Artifacts Credential Provider is installed",
109 move |ctx| {
110 let nuget_config_platform = nuget_config_platform.claim(ctx);
111 ensure_auth.claim(ctx);
112
113 move |rt| {
114 let nuget_config_platform = rt.read(nuget_config_platform);
115 if let Err(e) = check_if_aacp_installed(rt, &nuget_config_platform) {
116 if skip_auth_check {
117 log::warn!("{}", e);
118 log::warn!("user passed --skip-auth-check, so assuming they know what they're doing...");
119 } else {
120 return Err(e)
121 }
122 }
123
124 Ok(())
125 }
126 },
127 );
128 }
129 } else {
130 anyhow::bail!("unsupported backend")
131 }
132
133 Ok(())
134 }
135}
136
137fn check_if_aacp_installed(
138 rt: &mut RustRuntimeServices<'_>,
139 nuget_config_platform: &NugetInstallPlatform,
140) -> anyhow::Result<()> {
141 let sh = xshell::Shell::new()?;
142
143 let profile: PathBuf = if matches!(nuget_config_platform, NugetInstallPlatform::Windows) {
144 let path = xshell::cmd!(sh, "cmd.exe /c echo %UserProfile%")
145 .ignore_status()
146 .read()?;
147
148 if crate::_util::running_in_wsl(rt) {
149 crate::_util::wslpath::win_to_linux(path)
150 } else {
151 path.into()
152 }
153 } else {
154 dirs::home_dir().unwrap_or_default()
155 };
156
157 for kind in ["netfx", "netcore"] {
158 let path = profile
159 .join(".nuget")
160 .join("plugins")
161 .join(kind)
162 .join("CredentialProvider.Microsoft");
163 if path.exists() {
164 log::info!("found it!");
165 return Ok(());
166 }
167 }
168
169 anyhow::bail!("Azure Artifacts Credential Provider was not detected!")
170}