flowey_lib_common/
use_gh_cli.rs1use flowey::node::prelude::*;
11use std::io::Write;
12
13#[derive(Serialize, Deserialize)]
14pub enum GhCliAuth<C = VarNotClaimed> {
15 LocalOnlyInteractive,
17 AuthToken(ReadVar<String, C>),
20}
21
22impl ClaimVar for GhCliAuth {
23 type Claimed = GhCliAuth<VarClaimed>;
24
25 fn claim(self, ctx: &mut StepCtx<'_>) -> Self::Claimed {
26 match self {
27 GhCliAuth::LocalOnlyInteractive => GhCliAuth::LocalOnlyInteractive,
28 GhCliAuth::AuthToken(v) => GhCliAuth::AuthToken(v.claim(ctx)),
29 }
30 }
31}
32
33flowey_request! {
34 pub enum Request {
35 WithAuth(GhCliAuth),
37 Get(WriteVar<PathBuf>),
39 }
40}
41
42new_flow_node!(struct Node);
43
44impl FlowNode for Node {
45 type Request = Request;
46
47 fn imports(ctx: &mut ImportCtx<'_>) {
48 ctx.import::<crate::download_gh_cli::Node>();
49 }
50
51 fn emit(requests: Vec<Self::Request>, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
52 let mut get_reqs = Vec::new();
53 let mut with_auth_interactive = false;
54 let mut with_auth_token = None;
55
56 for req in requests {
57 match req {
58 Request::WithAuth(v) => match v {
59 GhCliAuth::LocalOnlyInteractive => with_auth_interactive = true,
60 GhCliAuth::AuthToken(v) => {
61 same_across_all_reqs_backing_var("WithAuth", &mut with_auth_token, v)?
62 }
63 },
64 Request::Get(v) => get_reqs.push(v),
65 }
66 }
67
68 let get_reqs = get_reqs;
69 let auth = match (with_auth_interactive, with_auth_token) {
70 (true, None) => GhCliAuth::LocalOnlyInteractive,
71 (false, Some(v)) => GhCliAuth::AuthToken(v),
72 (true, Some(_)) => {
73 anyhow::bail!("`WithAuth` must be consistent across requests")
74 }
75 (false, None) => anyhow::bail!("Missing essential request: WithAuth"),
76 };
77
78 if get_reqs.is_empty() {
81 if let GhCliAuth::AuthToken(tok) = auth {
82 tok.claim_unused(ctx);
83 }
84 return Ok(());
85 }
86
87 if !matches!(ctx.backend(), FlowBackend::Local) {
88 if matches!(auth, GhCliAuth::LocalOnlyInteractive) {
89 anyhow::bail!("cannot use interactive auth on a non-local backend")
90 }
91 }
92
93 let gh_bin_path = ctx.reqv(crate::download_gh_cli::Request::Get);
94
95 ctx.emit_rust_step("setup gh cli", |ctx| {
96 let auth = auth.claim(ctx);
97 let get_reqs = get_reqs.claim(ctx);
98 let gh_bin_path = gh_bin_path.claim(ctx);
99 |rt| {
100 let sh = xshell::Shell::new()?;
101
102 let gh_bin_path = rt.read(gh_bin_path).display().to_string();
103 let gh_token = match auth {
104 GhCliAuth::LocalOnlyInteractive => String::new(),
105 GhCliAuth::AuthToken(tok) => rt.read(tok),
106 };
107 let gh_token = if !gh_token.is_empty() {
110 match rt.platform().kind() {
111 FlowPlatformKind::Windows => format!(r#"SET "GITHUB_TOKEN={gh_token}""#),
112 FlowPlatformKind::Unix => format!(r#"GITHUB_TOKEN="{gh_token}""#),
113 }
114 } else {
115 String::new()
116 };
117
118 let shim_txt = match rt.platform().kind() {
119 FlowPlatformKind::Windows => WINDOWS_SHIM_BAT.trim(),
120 FlowPlatformKind::Unix => UNIX_SHIM_SH.trim(),
121 }
122 .replace("{GITHUB_TOKEN}", &gh_token)
123 .replace("{GH_BIN_PATH}", &gh_bin_path);
124
125 let script_name = match rt.platform().kind() {
126 FlowPlatformKind::Windows => "shim.bat",
127 FlowPlatformKind::Unix => "shim.sh",
128 };
129 let path = {
130 let dst = std::env::current_dir()?.join(script_name);
131 let mut options = fs_err::OpenOptions::new();
132 #[cfg(unix)]
133 fs_err::os::unix::fs::OpenOptionsExt::mode(&mut options, 0o777); let mut file = options.create_new(true).write(true).open(&dst)?;
135 file.write_all(shim_txt.as_bytes())?;
136 dst.absolute()?
137 };
138 if !xshell::cmd!(sh, "{path} auth status")
139 .ignore_status()
140 .output()?
141 .status
142 .success()
143 {
144 if matches!(rt.backend(), FlowBackend::Local) {
145 xshell::cmd!(sh, "{path} auth login").run()?;
146 } else {
147 anyhow::bail!("unable to authenticate with github - is GhCliAuth valid?")
148 }
149 };
150
151 for var in get_reqs {
152 rt.write(var, &path);
153 }
154
155 Ok(())
156 }
157 });
158
159 Ok(())
160 }
161}
162
163const UNIX_SHIM_SH: &str = r#"
164#!/bin/sh
165{GITHUB_TOKEN} exec {GH_BIN_PATH} "$@"
166"#;
167
168const WINDOWS_SHIM_BAT: &str = r#"
169@ECHO OFF
170{GITHUB_TOKEN}
171{GH_BIN_PATH} %*
172"#;