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 repo_path = rt.read(repo_path);
94 let gh_cli = rt.read(gh_cli);
95 let gh_run_status = match gh_run_status {
96 GhRunStatus::Completed => "completed",
97 GhRunStatus::Success => "success",
98 };
99
100 rt.sh.change_dir(repo_path);
101
102 let handle_output = |output: Result<String, xshell::Error>, error_msg: &str| -> Option<String> {
103 match output {
104 Ok(output) if output.trim().is_empty() => None,
105 Ok(output) => Some(output.trim().to_string()),
106 Err(e) => {
107 println!("{}: {}", error_msg, e);
108 None
109 }
110 }
111 };
112
113 let get_action_id_for_commit = |commit: &str| -> Option<String> {
115 let output = flowey::shell_cmd!(
116 rt,
117 "{gh_cli} run list
118 --commit {commit}
119 -w {pipeline_name}
120 -s {gh_run_status}
121 -L 1
122 --json databaseId
123 --jq .[].databaseId"
124 )
125 .read();
126
127 handle_output(output, &format!("Failed to get action id for commit {}", commit))
128 };
129
130 let verify_job_exists = |action_id: &str, job_name: &str| -> Option<String> {
132 let select = format!(".jobs[] | select(.name == \"{job_name}\" and .conclusion == \"success\") | .url");
135 let output = flowey::shell_cmd!(
136 rt,
137 "{gh_cli} run view {action_id}
138 --json jobs
139 --jq={select}"
140 )
141 .read();
142
143 handle_output(output, &format!("Failed to get job {} for action id {}", job_name, action_id))
144 };
145
146 let get_action_id = |commit: String| -> Option<String> {
148 let action_id = get_action_id_for_commit(&commit)?;
149
150 if let Some(job_name) = &gh_run_job_name {
152 verify_job_exists(&action_id, job_name)?;
153 }
154
155 Some(action_id)
156 };
157
158 let mut action_id = get_action_id(github_commit_hash.clone());
159 let mut loop_count = 0;
160
161 while action_id.is_none() {
164 println!(
165 "Unable to get action id for commit {}, trying again",
166 github_commit_hash
167 );
168
169 if loop_count > 4 {
170 anyhow::bail!("Failed to get action id after 5 attempts");
171 }
172
173 github_commit_hash =
174 flowey::shell_cmd!(rt, "git rev-parse {github_commit_hash}^").read()?;
175 action_id = get_action_id(github_commit_hash.clone());
176
177 loop_count += 1;
178 }
179
180 let id = action_id.context("failed to get action id")?;
182
183 println!("Got action id {id}, commit {github_commit_hash}");
184 rt.write(
185 gh_workflow,
186 &GithubWorkflow {
187 id,
188 commit: github_commit_hash,
189 },
190 );
191
192 Ok(())
193 }
194 });
195 }
196
197 Ok(())
198 }
199}