diff options
author | Francois Kritzinger <francois@codesynthesis.com> | 2024-10-24 09:25:59 +0200 |
---|---|---|
committer | Francois Kritzinger <francois@codesynthesis.com> | 2024-12-10 16:34:15 +0200 |
commit | 656b96fde2f28d4e6866180174c4e8481a358624 (patch) | |
tree | 8d2bece19568909dd0d0d89099250136e949d2ae /mod | |
parent | 630834173bba497c9f21eb0459ba5cb7264346ee (diff) |
Handle check suite rebuilds (all checks) on a commit and/or PR
Diffstat (limited to 'mod')
-rw-r--r-- | mod/ci-common.cxx | 26 | ||||
-rw-r--r-- | mod/ci-common.hxx | 10 | ||||
-rw-r--r-- | mod/mod-ci-github-gh.cxx | 69 | ||||
-rw-r--r-- | mod/mod-ci-github-gh.hxx | 18 | ||||
-rw-r--r-- | mod/mod-ci-github-service-data.cxx | 20 | ||||
-rw-r--r-- | mod/mod-ci-github-service-data.hxx | 17 | ||||
-rw-r--r-- | mod/mod-ci-github.cxx | 83 |
7 files changed, 192 insertions, 51 deletions
diff --git a/mod/ci-common.cxx b/mod/ci-common.cxx index 11d55af..5b039ef 100644 --- a/mod/ci-common.cxx +++ b/mod/ci-common.cxx @@ -856,4 +856,30 @@ namespace brep return s; } + + optional<tenant_service> ci_start:: + find (odb::core::database& db, + const string& type, + const string& id) const + { + using namespace odb::core; + + assert (!transaction::has_current ()); + + transaction tr (db.begin ()); + + using query = query<build_tenant>; + + shared_ptr<build_tenant> t ( + db.query_one<build_tenant> (query::service.id == id && + query::service.type == type)); + + tr.commit (); + + optional<tenant_service> r; + if (t != nullptr) + r = move (t->service); + + return r; + } } diff --git a/mod/ci-common.hxx b/mod/ci-common.hxx index d155398..1703ae0 100644 --- a/mod/ci-common.hxx +++ b/mod/ci-common.hxx @@ -205,6 +205,16 @@ namespace brep optional<build_state> rebuild (odb::core::database&, const build_id&) const; + // Find the tenant given the tenant service type and id and return the + // associated data or nullopt if there is no such tenant. + // + // Note: should be called out of the database transaction. + // + optional<tenant_service> + find (odb::core::database&, + const string& type, + const string& id) const; + // Helpers. // diff --git a/mod/mod-ci-github-gh.cxx b/mod/mod-ci-github-gh.cxx index 4ad8d32..dcea563 100644 --- a/mod/mod-ci-github-gh.cxx +++ b/mod/mod-ci-github-gh.cxx @@ -129,9 +129,14 @@ namespace brep return p.name () == s ? (v = true) : false; }; - if (c (ni, "node_id")) node_id = p.next_expect_string (); - else if (c (hb, "head_branch")) head_branch = p.next_expect_string (); - else if (c (hs, "head_sha")) head_sha = p.next_expect_string (); + if (c (ni, "node_id")) node_id = p.next_expect_string (); + else if (c (hb, "head_branch")) + { + string* v (p.next_expect_string_null ()); + if (v != nullptr) + head_branch = *v; + } + else if (c (hs, "head_sha")) head_sha = p.next_expect_string (); else p.next_expect_value_skip (); } @@ -144,7 +149,7 @@ namespace brep operator<< (ostream& os, const gh_check_suite& cs) { os << "node_id: " << cs.node_id - << ", head_branch: " << cs.head_branch + << ", head_branch: " << (cs.head_branch ? *cs.head_branch : "null") << ", head_sha: " << cs.head_sha; return os; @@ -208,37 +213,61 @@ namespace brep { p.next_expect (event::begin_object); - bool l (false), r (false), s (false); + bool r (false), s (false), rp (false), fn (false); while (p.next_expect (event::name, event::end_object)) { - if (c (l, "label")) base_label = p.next_expect_string (); - else if (c (r, "ref")) base_ref = p.next_expect_string (); + if (c (r, "ref")) base_ref = p.next_expect_string (); else if (c (s, "sha")) base_sha = p.next_expect_string (); + else if (c (rp, "repo")) + { + p.next_expect (event::begin_object); + + while (p.next_expect (event::name, event::end_object)) + { + if (c (fn, "full_name")) + base_path = p.next_expect_string (); + else + p.next_expect_value_skip (); + } + } else p.next_expect_value_skip (); } - if (!l) missing_member (p, "gh_pull_request.base", "label"); - if (!r) missing_member (p, "gh_pull_request.base", "ref"); - if (!s) missing_member (p, "gh_pull_request.base", "sha"); + if (!r) missing_member (p, "gh_pull_request.base", "ref"); + if (!s) missing_member (p, "gh_pull_request.base", "sha"); + if (!rp) missing_member (p, "gh_pull_request.base", "repo"); + if (!fn) missing_member (p, "gh_pull_request.base.repo", "full_name"); } else if (c (hd, "head")) { p.next_expect (event::begin_object); - bool l (false), r (false), s (false); + bool r (false), s (false), rp (false), fn (false); while (p.next_expect (event::name, event::end_object)) { - if (c (l, "label")) head_label = p.next_expect_string (); - else if (c (r, "ref")) head_ref = p.next_expect_string (); + if (c (r, "ref")) head_ref = p.next_expect_string (); else if (c (s, "sha")) head_sha = p.next_expect_string (); + else if (c (rp, "repo")) + { + p.next_expect (event::begin_object); + + while (p.next_expect (event::name, event::end_object)) + { + if (c (fn, "full_name")) + head_path = p.next_expect_string (); + else + p.next_expect_value_skip (); + } + } else p.next_expect_value_skip (); } - if (!l) missing_member (p, "gh_pull_request.head", "label"); - if (!r) missing_member (p, "gh_pull_request.head", "ref"); - if (!s) missing_member (p, "gh_pull_request.head", "sha"); + if (!r) missing_member (p, "gh_pull_request.head", "ref"); + if (!s) missing_member (p, "gh_pull_request.head", "sha"); + if (!rp) missing_member (p, "gh_pull_request.head", "repo"); + if (!fn) missing_member (p, "gh_pull_request.head.repo", "full_name"); } else p.next_expect_value_skip (); } @@ -265,12 +294,12 @@ namespace brep : "null") << ", merge_commit_sha:" << pr.merge_commit_sha << ", base: { " - << "label: " << pr.base_label + << "path: " << pr.base_path << ", ref: " << pr.base_ref << ", sha: " << pr.base_sha << " }" << ", head: { " - << "label: " << pr.head_label + << "path: " << pr.head_path << ", ref: " << pr.head_ref << ", sha: " << pr.head_sha << " }"; @@ -298,7 +327,7 @@ namespace brep if (c (ni, "node_id")) node_id = p.next_expect_string (); else if (c (nm, "name")) name = p.next_expect_string (); - else if (c (fn, "full_name")) full_name = p.next_expect_string (); + else if (c (fn, "full_name")) path = p.next_expect_string (); else if (c (db, "default_branch")) default_branch = p.next_expect_string (); else if (c (cu, "clone_url")) clone_url = p.next_expect_string (); else p.next_expect_value_skip (); @@ -316,7 +345,7 @@ namespace brep { os << "node_id: " << rep.node_id << ", name: " << rep.name - << ", full_name: " << rep.full_name + << ", path: " << rep.path << ", default_branch: " << rep.default_branch << ", clone_url: " << rep.clone_url; diff --git a/mod/mod-ci-github-gh.hxx b/mod/mod-ci-github-gh.hxx index 2b77aeb..58714be 100644 --- a/mod/mod-ci-github-gh.hxx +++ b/mod/mod-ci-github-gh.hxx @@ -21,6 +21,8 @@ namespace butl namespace brep { + // @@@ Check if any data members are unused (once the dust settles). + using build_queued_hints = tenant_service_build_queued::build_queued_hints; // GitHub request/response types (all start with gh_). @@ -45,7 +47,7 @@ namespace brep struct gh_check_suite { string node_id; - string head_branch; + optional<string> head_branch; string head_sha; explicit @@ -81,14 +83,12 @@ namespace brep optional<bool> mergeable; string merge_commit_sha; - // @@ TODO Remove label if unused. - string base_label; // Name distinguishing the base from the head. - string base_ref; - string base_sha; + string base_path; // Repository path (<org>/<repo>) under github.com. + string base_ref; // @@ TODO Remove if remains unused. + string base_sha; // @@ TODO Remove if remains unused. - // @@ TODO Remove label if unused. - string head_label; // Name distinguishing the head from the base. - string head_ref; + string head_path; + string head_ref; // @@ TODO Remove if remains unused. string head_sha; explicit @@ -124,7 +124,7 @@ namespace brep { string node_id; string name; - string full_name; + string path; // Repository path (<org>/<repo>) under github.com. string default_branch; string clone_url; diff --git a/mod/mod-ci-github-service-data.cxx b/mod/mod-ci-github-service-data.cxx index 3812bf7..f93bc05 100644 --- a/mod/mod-ci-github-service-data.cxx +++ b/mod/mod-ci-github-service-data.cxx @@ -102,15 +102,18 @@ namespace brep timestamp iat_ea, uint64_t iid, string rid, - string hs, - bool rr) - : kind (local), pre_check (false), re_request (rr), + kind_type k, + bool rr, + bool pc, + string cs, + string rs) + : kind (k), pre_check (pc), re_request (rr), warning_success (ws), installation_access (move (iat_tok), iat_ea), installation_id (iid), repository_node_id (move (rid)), - check_sha (hs), - report_sha (move (hs)) + check_sha (move (cs)), + report_sha (move (rs)) { } @@ -122,16 +125,21 @@ namespace brep timestamp iat_ea, uint64_t iid, string rid, + kind_type k, + bool rr, + bool pc, + string cs, string rs, string rcu, uint32_t prn) - : kind (local), pre_check (true), re_request (false), + : kind (k), pre_check (pc), re_request (rr), warning_success (ws), installation_access (move (iat_tok), iat_ea), installation_id (iid), repository_node_id (move (rid)), repository_clone_url (move (rcu)), pr_number (prn), + check_sha (move (cs)), report_sha (move (rs)) { } diff --git a/mod/mod-ci-github-service-data.hxx b/mod/mod-ci-github-service-data.hxx index a6cb742..ae1506d 100644 --- a/mod/mod-ci-github-service-data.hxx +++ b/mod/mod-ci-github-service-data.hxx @@ -11,7 +11,7 @@ namespace brep { - // @@@ Check is any data members are unused. + // @@@ Check if any data members are unused (once the dust settles). // Service data associated with the tenant (corresponds to GH check suite). // @@ -76,7 +76,7 @@ namespace brep // // @@ TODO Serialize these fields. // - enum {local, remote /*, queue */} kind; + enum kind_type {local, remote /*, queue */} kind; bool pre_check; bool re_request; // Re-requested (rebuild). @@ -102,6 +102,8 @@ namespace brep // The GitHub ID of the synthetic PR merge check run or absent if it // hasn't been created yet. // + // @@ TODO Remove once merge check run code has been removed. + // optional<string> merge_node_id; // The commit ID the branch push or pull request (and its check runs) are @@ -147,8 +149,11 @@ namespace brep timestamp iat_expires_at, uint64_t installation_id, string repository_node_id, - string head_sha, - bool re_request); + kind_type kind, + bool pre_check, + bool re_request, + string check_sha, + string report_sha); // The pull_request constructor. // @@ -157,6 +162,10 @@ namespace brep timestamp iat_expires_at, uint64_t installation_id, string repository_node_id, + kind_type kind, + bool pre_check, + bool re_request, + string check_sha, string report_sha, string repository_clone_url, uint32_t pr_number); diff --git a/mod/mod-ci-github.cxx b/mod/mod-ci-github.cxx index 6289d8b..f7480b6 100644 --- a/mod/mod-ci-github.cxx +++ b/mod/mod-ci-github.cxx @@ -473,15 +473,61 @@ namespace brep // string sid (cs.repository.node_id + ":" + cs.check_suite.head_sha); - // @@ Let's pass the "state" arguments explicitly in both constructors. + // If the user requests a rebuild of the (entire) PR, then this manifests + // as the check_suite rather than pull_request event. Specifically: // + // - For a local PR, this event is shared with the branch push and all we + // need to do is restart the CI for the head commit. + // + // - For a remote PR, this event will have no gh_check_suite::head_branch. + // In this case we need to load the existing service data for this head + // commit, extract the test merge commit, and restart the CI for that. + // + string check_sha; + service_data::kind_type kind; + + bool re_requested (cs.action == "rerequested"); + if (re_requested && !cs.check_suite.head_branch) + { + kind = service_data::remote; + + if (optional<tenant_service> ts = find (*build_db_, "ci-github", sid)) + { + try + { + service_data sd (*ts->data); + check_sha = move (sd.check_sha); // Test merge commit. + } + catch (const invalid_argument& e) + { + fail << "failed to parse service data: " << e; + } + } + else + { + error << "check suite for remote pull request " + << cs.check_suite.node_id + << ": re-requested but tenant_service with id " << sid + << " did not exist"; + return true; + } + } + else + { + // Branch push or local PR rebuild. + // + kind = service_data::local; + check_sha = cs.check_suite.head_sha; + } + service_data sd (warning_success, iat->token, iat->expires_at, cs.installation.id, move (cs.repository.node_id), - move (cs.check_suite.head_sha), - cs.action == "rerequested" /* re_request */); + kind, false /* pre_check */, re_requested, + move (check_sha), + move (cs.check_suite.head_sha) /* report_sha */); // If this check suite is being re-run, replace the existing CI request if // it exists; otherwise create a new one, doing nothing if a request @@ -490,8 +536,9 @@ namespace brep // Note that GitHub UI does not allow re-running the entire check suite // until all the check runs are completed. // - duplicate_tenant_mode dtm (sd.re_request ? duplicate_tenant_mode::replace - : duplicate_tenant_mode::ignore); + duplicate_tenant_mode dtm (re_requested + ? duplicate_tenant_mode::replace + : duplicate_tenant_mode::ignore); // Create an unloaded CI request. // @@ -526,6 +573,7 @@ namespace brep error << "check suite " << cs.check_suite.node_id << ": re-requested but tenant_service with id " << sid << " did not exist"; + return true; } return true; @@ -698,26 +746,37 @@ namespace brep // job might actually still be relevant (in both local and remote PR // cases). - // @@ TMP We can already determine whether the PR is local or remote so we - // could pass `kind` to the service_data constructor. Let's do. + // @@ TODO Serialize the new service_data fields! + // - // @@ TODO: what happens when the entire PR build is re-requested? + // Distinguish between local and remote PRs by comparing the head and base + // repositories' paths. + // + service_data::kind_type kind ( + pr.pull_request.head_path == pr.pull_request.base_path + ? service_data::local + : service_data::remote); - // @@ TODO Serialize the new service_data fields! + // Note: for remote PRs the check_sha will be set later, in + // build_unloaded_pre_check(), to test merge commit id. // - // Note that check_sha will be set later, in build_unloaded_pre_check(). + string check_sha (kind == service_data::local + ? pr.pull_request.head_sha + : ""); + + // Note that PR rebuilds (re-requested) are handled by check_suite(). // service_data sd (warning_success, move (iat->token), iat->expires_at, pr.installation.id, move (pr.repository.node_id), + kind, true /* pre_check */, false /* re_request */, + move (check_sha), move (pr.pull_request.head_sha) /* report_sha */, move (pr.repository.clone_url), pr.pull_request.number); - assert (sd.pre_check); // @@ Get rid (once ctor changed). - // Create an unloaded CI request for the pre-check phase (during which we // wait for the PR's merge commit and behindness to become available). // |