flowey_lib_common/
gh_workflow_id.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Gets the Github workflow id for a given commit hash
5
6use flowey::node::prelude::*;
7
8flowey_request! {
9    pub struct Request {
10        pub github_commit_hash: ReadVar<String>,
11        pub repo_path: ReadVar<PathBuf>,
12        pub pipeline_name: String,
13        pub gh_workflow: WriteVar<GithubWorkflow>,
14    }
15}
16
17#[derive(Serialize, Deserialize, Clone)]
18pub struct GithubWorkflow {
19    pub id: String,
20    pub commit: String,
21}
22
23new_simple_flow_node!(struct Node);
24
25impl SimpleFlowNode for Node {
26    type Request = Request;
27
28    fn imports(ctx: &mut ImportCtx<'_>) {
29        ctx.import::<crate::use_gh_cli::Node>();
30    }
31
32    fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
33        let Request {
34            repo_path,
35            github_commit_hash,
36            gh_workflow,
37            pipeline_name,
38        } = request;
39
40        let pipeline_name = pipeline_name.clone();
41
42        let gh_cli = ctx.reqv(crate::use_gh_cli::Request::Get);
43
44        ctx.emit_rust_step("get action id", |ctx| {
45            let gh_workflow = gh_workflow.claim(ctx);
46            let github_commit_hash = github_commit_hash.claim(ctx);
47            let repo_path = repo_path.claim(ctx);
48            let pipeline_name = pipeline_name.clone();
49            let gh_cli = gh_cli.claim(ctx);
50
51            move |rt| {
52                let github_commit_hash = rt.read(github_commit_hash);
53                let sh = xshell::Shell::new()?;
54                let repo_path = rt.read(repo_path);
55                let gh_cli = rt.read(gh_cli);
56
57                sh.change_dir(repo_path);
58
59                // Fetches the CI build workflow id for a given commit hash
60                let get_action_id = |commit: String| -> Option<String> {
61                    let output = xshell::cmd!(
62                        sh,
63                        "{gh_cli} run list
64                        --commit {commit}
65                        -w {pipeline_name}
66                        -s completed
67                        -L 1
68                        --json databaseId
69                        --jq .[].databaseId"
70                    )
71                    .read();
72
73                    match output {
74                        Ok(output) if output.trim().is_empty() => None,
75                        Ok(output) => Some(output),
76                        Err(e) => {
77                            println!("Failed to get action id for commit {}: {}", commit, e);
78                            None
79                        }
80                    }
81                };
82
83                let mut github_commit_hash = github_commit_hash.clone();
84                let mut action_id = get_action_id(github_commit_hash.clone());
85                let mut loop_count = 0;
86
87                // CI may not have finished the build for the merge base, so loop through commits
88                // until we find a finished build or fail after 5 attempts
89                while action_id.is_none() {
90                    println!(
91                        "Unable to get action id for commit {}, trying again",
92                        github_commit_hash
93                    );
94
95                    if loop_count > 4 {
96                        anyhow::bail!("Failed to get action id after 5 attempts");
97                    }
98
99                    github_commit_hash =
100                        xshell::cmd!(sh, "git rev-parse {github_commit_hash}^").read()?;
101                    action_id = get_action_id(github_commit_hash.clone());
102
103                    loop_count += 1;
104                }
105
106                // We have an action id or we would've bailed in the loop above
107                let id = action_id.context("failed to get action id")?;
108
109                println!("Got action id {id}, commit {github_commit_hash}");
110                rt.write(
111                    gh_workflow,
112                    &GithubWorkflow {
113                        id,
114                        commit: github_commit_hash,
115                    },
116                );
117
118                Ok(())
119            }
120        });
121
122        Ok(())
123    }
124}