flowey_lib_common/
gh_workflow_id.rs1use flowey::node::prelude::*;
7
8#[derive(Serialize, Deserialize)]
9pub enum GhRunStatus {
10 Completed,
11 Success,
12}
13
14#[derive(Serialize, Deserialize, Clone)]
15pub struct GithubWorkflow {
16 pub id: String,
17 pub commit: String,
18}
19
20#[derive(Serialize, Deserialize)]
22pub struct WorkflowQueryParams {
23 pub github_commit_hash: ReadVar<String>,
24 pub repo_path: ReadVar<PathBuf>,
25 pub pipeline_name: String,
26 pub gh_workflow: WriteVar<GithubWorkflow>,
27}
28
29#[derive(Serialize, Deserialize)]
31pub struct BasicQuery {
32 #[serde(flatten)]
33 pub params: WorkflowQueryParams,
34}
35
36#[derive(Serialize, Deserialize)]
38pub struct QueryWithStatusAndJob {
39 #[serde(flatten)]
40 pub params: WorkflowQueryParams,
41 pub gh_run_status: GhRunStatus,
42 pub gh_run_job_name: String,
43}
44
45flowey_request! {
46 pub enum Request {
47 Basic(BasicQuery),
49 WithStatusAndJob(QueryWithStatusAndJob),
51 }
52}
53
54new_flow_node!(struct Node);
55
56impl FlowNode for Node {
57 type Request = Request;
58
59 fn imports(ctx: &mut ImportCtx<'_>) {
60 ctx.import::<crate::use_gh_cli::Node>();
61 }
62
63 fn emit(requests: Vec<Self::Request>, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
64 for request in requests {
65 let (params, gh_run_status, gh_run_job_name) = match request {
66 Request::Basic(BasicQuery { params }) => (params, GhRunStatus::Success, None),
67 Request::WithStatusAndJob(QueryWithStatusAndJob {
68 params,
69 gh_run_status,
70 gh_run_job_name,
71 }) => (params, gh_run_status, Some(gh_run_job_name)),
72 };
73
74 let WorkflowQueryParams {
75 github_commit_hash,
76 repo_path,
77 pipeline_name,
78 gh_workflow,
79 } = params;
80
81 let pipeline_name = pipeline_name.clone();
82 let gh_cli = ctx.reqv(crate::use_gh_cli::Request::Get);
83
84 ctx.emit_rust_step("get action id by commit", |ctx| {
85 let gh_workflow = gh_workflow.claim(ctx);
86 let github_commit_hash = github_commit_hash.claim(ctx);
87 let repo_path = repo_path.claim(ctx);
88 let pipeline_name = pipeline_name.clone();
89 let gh_cli = gh_cli.claim(ctx);
90
91 move |rt| {
92 let mut github_commit_hash = rt.read(github_commit_hash);
93 let sh = xshell::Shell::new()?;
94 let repo_path = rt.read(repo_path);
95 let gh_cli = rt.read(gh_cli);
96 let gh_run_status = match gh_run_status {
97 GhRunStatus::Completed => "completed",
98 GhRunStatus::Success => "success",
99 };
100
101 sh.change_dir(repo_path);
102
103 let handle_output = |output: Result<String, xshell::Error>, error_msg: &str| -> Option<String> {
104 match output {
105 Ok(output) if output.trim().is_empty() => None,
106 Ok(output) => Some(output.trim().to_string()),
107 Err(e) => {
108 println!("{}: {}", error_msg, e);
109 None
110 }
111 }
112 };
113
114 let get_action_id_for_commit = |commit: &str| -> Option<String> {
116 let output = xshell::cmd!(
117 sh,
118 "{gh_cli} run list
119 --commit {commit}
120 -w {pipeline_name}
121 -s {gh_run_status}
122 -L 1
123 --json databaseId
124 --jq .[].databaseId"
125 )
126 .read();
127
128 handle_output(output, &format!("Failed to get action id for commit {}", commit))
129 };
130
131 let verify_job_exists = |action_id: &str, job_name: &str| -> Option<String> {
133 let select = format!(".jobs[] | select(.name == \"{job_name}\" and .conclusion == \"success\") | .url");
136 let output = xshell::cmd!(
137 sh,
138 "{gh_cli} run view {action_id}
139 --json jobs
140 --jq={select}"
141 )
142 .read();
143
144 handle_output(output, &format!("Failed to get job {} for action id {}", job_name, action_id))
145 };
146
147 let get_action_id = |commit: String| -> Option<String> {
149 let action_id = get_action_id_for_commit(&commit)?;
150
151 if let Some(job_name) = &gh_run_job_name {
153 verify_job_exists(&action_id, job_name)?;
154 }
155
156 Some(action_id)
157 };
158
159 let mut action_id = get_action_id(github_commit_hash.clone());
160 let mut loop_count = 0;
161
162 while action_id.is_none() {
165 println!(
166 "Unable to get action id for commit {}, trying again",
167 github_commit_hash
168 );
169
170 if loop_count > 4 {
171 anyhow::bail!("Failed to get action id after 5 attempts");
172 }
173
174 github_commit_hash =
175 xshell::cmd!(sh, "git rev-parse {github_commit_hash}^").read()?;
176 action_id = get_action_id(github_commit_hash.clone());
177
178 loop_count += 1;
179 }
180
181 let id = action_id.context("failed to get action id")?;
183
184 println!("Got action id {id}, commit {github_commit_hash}");
185 rt.write(
186 gh_workflow,
187 &GithubWorkflow {
188 id,
189 commit: github_commit_hash,
190 },
191 );
192
193 Ok(())
194 }
195 });
196 }
197
198 Ok(())
199 }
200}