aboutsummaryrefslogtreecommitdiff
path: root/mod
diff options
context:
space:
mode:
authorFrancois Kritzinger <francois@codesynthesis.com>2024-10-24 09:25:59 +0200
committerFrancois Kritzinger <francois@codesynthesis.com>2024-12-10 16:34:15 +0200
commit656b96fde2f28d4e6866180174c4e8481a358624 (patch)
tree8d2bece19568909dd0d0d89099250136e949d2ae /mod
parent630834173bba497c9f21eb0459ba5cb7264346ee (diff)
Handle check suite rebuilds (all checks) on a commit and/or PR
Diffstat (limited to 'mod')
-rw-r--r--mod/ci-common.cxx26
-rw-r--r--mod/ci-common.hxx10
-rw-r--r--mod/mod-ci-github-gh.cxx69
-rw-r--r--mod/mod-ci-github-gh.hxx18
-rw-r--r--mod/mod-ci-github-service-data.cxx20
-rw-r--r--mod/mod-ci-github-service-data.hxx17
-rw-r--r--mod/mod-ci-github.cxx83
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).
//