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 if matches!(nuget_config_platform, NugetInstallPlatform::Windows) {
90 let install_aacp_cmd = r#""& { $(irm https://aka.ms/install-artifacts-credprovider.ps1) } -AddNetfx""#;
91 flowey::shell_cmd!(rt, "powershell.exe iex {install_aacp_cmd}").run()?;
92 } else {
93 log::warn!("automatic Azure Artifacts Credential Provider installation is not supported yet for this platform!");
94 log::warn!("follow the guide, and press <enter> to continue");
95 let _ = std::io::stdin().read_line(&mut String::new());
96 }
97
98 check_if_aacp_installed(rt, &nuget_config_platform)?;
99
100 Ok(())
101 }
102 }
103 );
104 } else {
105 ctx.emit_rust_step(
106 "Check if Azure Artifacts Credential Provider is installed",
107 move |ctx| {
108 let nuget_config_platform = nuget_config_platform.claim(ctx);
109 ensure_auth.claim(ctx);
110
111 move |rt| {
112 let nuget_config_platform = rt.read(nuget_config_platform);
113 if let Err(e) = check_if_aacp_installed(rt, &nuget_config_platform) {
114 if skip_auth_check {
115 log::warn!("{}", e);
116 log::warn!("user passed --skip-auth-check, so assuming they know what they're doing...");
117 } else {
118 return Err(e)
119 }
120 }
121
122 Ok(())
123 }
124 },
125 );
126 }
127 } else {
128 anyhow::bail!("unsupported backend")
129 }
130
131 Ok(())
132 }
133}
134
135fn check_if_aacp_installed(
136 rt: &mut RustRuntimeServices<'_>,
137 nuget_config_platform: &NugetInstallPlatform,
138) -> anyhow::Result<()> {
139 let profile: PathBuf = if matches!(nuget_config_platform, NugetInstallPlatform::Windows) {
140 let path = flowey::shell_cmd!(rt, "cmd.exe /c echo %UserProfile%")
141 .ignore_status()
142 .read()?;
143
144 if crate::_util::running_in_wsl(rt) {
145 crate::_util::wslpath::win_to_linux(rt, path)
146 } else {
147 path.into()
148 }
149 } else {
150 dirs::home_dir().unwrap_or_default()
151 };
152
153 for kind in ["netfx", "netcore"] {
154 let path = profile
155 .join(".nuget")
156 .join("plugins")
157 .join(kind)
158 .join("CredentialProvider.Microsoft");
159 if path.exists() {
160 log::info!("found it!");
161 return Ok(());
162 }
163 }
164
165 anyhow::bail!("Azure Artifacts Credential Provider was not detected!")
166}