aboutsummaryrefslogtreecommitdiff
path: root/mod
diff options
context:
space:
mode:
authorFrancois Kritzinger <francois@codesynthesis.com>2024-05-24 09:38:04 +0200
committerFrancois Kritzinger <francois@codesynthesis.com>2024-06-05 09:12:46 +0200
commit034186327acaeac9f0cb4b445752ddd612515341 (patch)
treef3d89dcc34b453ee7188e833d04a65f9a62cf365 /mod
parentfb02409aea7168048d9afbc374c4db81c7f1aea7 (diff)
Fetch pull request mergeability
Diffstat (limited to 'mod')
-rw-r--r--mod/mod-ci-github-gq.cxx142
-rw-r--r--mod/mod-ci-github-gq.hxx22
2 files changed, 164 insertions, 0 deletions
diff --git a/mod/mod-ci-github-gq.cxx b/mod/mod-ci-github-gq.cxx
index 954f5a8..54822ae 100644
--- a/mod/mod-ci-github-gq.cxx
+++ b/mod/mod-ci-github-gq.cxx
@@ -557,6 +557,148 @@ namespace brep
return r;
}
+ // Serialize a GraphQL query that fetches a pull request from GitHub.
+ //
+ static string
+ gq_query_pr_mergeability (const string& nid)
+ {
+ ostringstream os;
+
+ os << "query {" << '\n'
+ << " node(id:" << gq_str (nid) << ") {" << '\n'
+ << " ... on PullRequest {" << '\n'
+ << " mergeable potentialMergeCommit { oid }" << '\n'
+ << " }" << '\n'
+ << " }" << '\n'
+ << "}" << '\n';
+
+ return os.str ();
+ }
+
+ pair<optional<gq_pr_mergeability>, bool>
+ gq_pull_request_mergeable (const basic_mark& error,
+ const string& iat,
+ const string& nid)
+ {
+ string rq (gq_serialize_request (gq_query_pr_mergeability (nid)));
+
+ try
+ {
+ // Response parser.
+ //
+ struct resp
+ {
+ // True if the pull request was found (i.e., the node ID was valid).
+ //
+ bool found = false;
+
+ // The response value. Absent if the merge commit is still being
+ // generated.
+ //
+ optional<gq_pr_mergeability> value;
+
+ resp (json::parser& p)
+ {
+ using event = json::event;
+
+ gq_parse_response (p, [this] (json::parser& p)
+ {
+ p.next_expect (event::begin_object);
+
+ if (p.next_expect_member_object_null ("node"))
+ {
+ found = true;
+
+ string ma (p.next_expect_member_string ("mergeable"));
+
+ if (ma == "MERGEABLE")
+ {
+ p.next_expect_member_object ("potentialMergeCommit");
+ string oid (p.next_expect_member_string ("oid"));
+ p.next_expect (event::end_object);
+
+ value = {true, move (oid)};
+ }
+ else if (ma == "CONFLICTING")
+ {
+ value = {false, ""};
+ }
+ else if (ma == "UNKNOWN")
+ {
+ // Still being generated; leave value absent.
+ }
+
+ p.next_expect (event::end_object); // node
+ }
+
+ p.next_expect (event::end_object);
+ });
+ }
+
+ resp () = default;
+ } rs;
+
+ uint16_t sc (github_post (rs,
+ "graphql", // API Endpoint.
+ strings {"Authorization: Bearer " + iat},
+ move (rq)));
+
+ if (sc == 200)
+ {
+ if (!rs.found)
+ error << "pull request '" << nid << "' not found";
+
+ return {move (rs.value), rs.found};
+ }
+ else
+ error << "failed to fetch pull request: error HTTP response status "
+ << sc;
+ }
+ catch (const json::invalid_json_input& e)
+ {
+ // Note: e.name is the GitHub API endpoint.
+ //
+ error << "malformed JSON in response from " << e.name << ", line: "
+ << e.line << ", column: " << e.column << ", byte offset: "
+ << e.position << ", error: " << e;
+ }
+ catch (const invalid_argument& e)
+ {
+ error << "malformed header(s) in response: " << e;
+ }
+ catch (const system_error& e)
+ {
+ error << "unable to fetch pull request (errno=" << e.code () << "): "
+ << e.what ();
+ }
+ catch (const runtime_error& e) // From response type's parsing constructor.
+ {
+ // GitHub response contained error(s) (could be ours or theirs at this
+ // point).
+ //
+ error << "unable to fetch pull request: " << e;
+ }
+
+ return {nullopt, false};
+ }
+
+ // bool
+ // gq_fetch_branch_open_pull_requests ()
+ // {
+ // // query {
+ // // repository(owner:"francoisk" name:"libb2")
+ // // {
+ // // pullRequests (last:100 states:OPEN baseRefName:"master") {
+ // // edges {
+ // // node {
+ // // id
+ // // }
+ // // }
+ // // }
+ // // }
+ // // }
+ // }
+
// GraphQL serialization functions.
//
// The GraphQL spec:
diff --git a/mod/mod-ci-github-gq.hxx b/mod/mod-ci-github-gq.hxx
index 8f7a9ca..7a15b5e 100644
--- a/mod/mod-ci-github-gq.hxx
+++ b/mod/mod-ci-github-gq.hxx
@@ -78,6 +78,28 @@ namespace brep
const string& details_url,
build_state,
optional<gq_built_result> = nullopt);
+
+ // Fetch a pull request's mergeability from GitHub and return it in first,
+ // or absent if the merge commit is still being generated.
+ //
+ // Return false in second and issue diagnostics if the request failed.
+ //
+ struct gq_pr_mergeability
+ {
+ // True if the pull request is auto-mergeable; false if it would create
+ // conflicts.
+ //
+ bool mergeable;
+
+ // The ID of the test merge commit. Empty if mergeable is false.
+ //
+ string merge_commit_id;
+ };
+
+ pair<optional<gq_pr_mergeability>, bool>
+ gq_pull_request_mergeable (const basic_mark& error,
+ const string& installation_access_token,
+ const string& node_id);
}
#endif // MOD_MOD_CI_GITHUB_GQ_HXX