mirror of
https://github.com/kata-containers/kata-containers.git
synced 2026-07-25 22:49:59 +00:00
ci.gatekeeper: Recognize linked ci-devel runs
Gatekeeper requires ok-to-test before checking required jobs, even when a maintainer has dispatched ci-devel for the pull request tip. Recognize the first linked ci-devel run only when its workflow, event, and commit match. Use that run as alternate authorization and link it from the gatekeeper step summary. Invalid links retain the regular path. Generated-By: GitHub Copilot Signed-off-by: Aurélien Bombo <abombo@microsoft.com>
This commit is contained in:
@@ -38,6 +38,11 @@ if os.environ.get("GITHUB_TOKEN"):
|
||||
_GH_HEADERS["Authorization"] = f"Bearer {os.environ['GITHUB_TOKEN']}"
|
||||
_GH_API_URL = f"https://api.github.com/repos/{os.environ['GITHUB_REPOSITORY']}"
|
||||
_GH_RUNS_URL = f"{_GH_API_URL}/actions/runs"
|
||||
_GH_WEB_URL = f"https://github.com/{os.environ['GITHUB_REPOSITORY']}"
|
||||
_GH_RUN_URL_RE = re.compile(
|
||||
rf"{re.escape(_GH_WEB_URL)}/actions/runs/(\d+)"
|
||||
)
|
||||
_CI_DEVEL_WORKFLOW_PATH = ".github/workflows/ci-devel.yaml"
|
||||
_GH_SUMMARY_URL = (
|
||||
f"{os.environ.get('GITHUB_SERVER_URL')}/"
|
||||
f"{os.environ.get('GITHUB_REPOSITORY')}/actions/runs/"
|
||||
@@ -59,6 +64,7 @@ class Checker:
|
||||
def __init__(self):
|
||||
self.latest_commit_sha = os.getenv("COMMIT_HASH")
|
||||
self.pr_number = os.getenv("GH_PR_NUMBER")
|
||||
self.ci_devel_run = None
|
||||
required_labels = os.getenv("REQUIRED_LABELS")
|
||||
if required_labels:
|
||||
self.required_labels = set(required_labels.split(";"))
|
||||
@@ -183,6 +189,14 @@ class Checker:
|
||||
failing = []
|
||||
running = []
|
||||
|
||||
if self.ci_devel_run:
|
||||
run_id = self.ci_devel_run["id"]
|
||||
run_url = self.ci_devel_run.get(
|
||||
"html_url", f"{_GH_WEB_URL}/actions/runs/{run_id}"
|
||||
)
|
||||
lines.append("## ci-devel run\n")
|
||||
lines.append(f"[Scanned ci-devel run {run_id}]({run_url})\n")
|
||||
|
||||
for name, job in self.results.items():
|
||||
status = self._job_status(job)
|
||||
url = job.get("html_url", "")
|
||||
@@ -256,16 +270,21 @@ class Checker:
|
||||
return self.paginated_fetch(
|
||||
url, "jobs", f"get_jobs_for_workflow_run__{run_id}")
|
||||
|
||||
def check_workflow_runs_status(self, attempt):
|
||||
def get_workflow_runs(self, attempt):
|
||||
"""Get workflow runs for the commit being checked"""
|
||||
return self.paginated_fetch(
|
||||
_GH_RUNS_URL, "workflow_runs",
|
||||
f"check_workflow_runs_status_{attempt}",
|
||||
{"head_sha": self.latest_commit_sha})
|
||||
|
||||
def check_workflow_runs_status(self, attempt, workflow_runs=None):
|
||||
"""
|
||||
Checks if all required jobs passed
|
||||
|
||||
:returns: 0 - all passing; 1 - any failure; 127 some jobs running
|
||||
"""
|
||||
workflow_runs = self.paginated_fetch(
|
||||
_GH_RUNS_URL, "workflow_runs",
|
||||
f"check_workflow_runs_status_{attempt}",
|
||||
{"head_sha": self.latest_commit_sha})
|
||||
if workflow_runs is None:
|
||||
workflow_runs = self.get_workflow_runs(attempt)
|
||||
for run in workflow_runs:
|
||||
jobs = self.get_jobs_for_workflow_run(run["id"])
|
||||
for job in jobs:
|
||||
@@ -274,7 +293,7 @@ class Checker:
|
||||
self.write_step_summary()
|
||||
return self.status()
|
||||
|
||||
def wait_for_required_tests(self):
|
||||
def wait_for_required_tests(self, workflow_runs=None):
|
||||
"""
|
||||
Wait for all required tests to pass or failfast
|
||||
|
||||
@@ -283,7 +302,8 @@ class Checker:
|
||||
i = 0
|
||||
while True:
|
||||
i += 1
|
||||
ret = self.check_workflow_runs_status(i)
|
||||
ret = self.check_workflow_runs_status(i, workflow_runs)
|
||||
workflow_runs = None
|
||||
if ret == RUNNING:
|
||||
running_jobs = len([name
|
||||
for name, job in self.results.items()
|
||||
@@ -293,7 +313,41 @@ class Checker:
|
||||
continue
|
||||
return ret
|
||||
|
||||
def check_required_labels(self):
|
||||
def get_pull_request(self):
|
||||
"""Get pull request metadata from its issue endpoint"""
|
||||
return self.fetch_json_from_url(
|
||||
f"{_GH_API_URL}/issues/{self.pr_number}",
|
||||
f"get_pull_request__{self.pr_number}")
|
||||
|
||||
@staticmethod
|
||||
def get_linked_run_id(body):
|
||||
"""Get the first same-repository Actions run linked in a PR body"""
|
||||
match = _GH_RUN_URL_RE.search(body or "")
|
||||
return int(match.group(1)) if match else None
|
||||
|
||||
def find_ci_devel_run(self, pull_request, workflow_runs):
|
||||
"""Find a linked ci-devel run for the commit being checked"""
|
||||
run_id = self.get_linked_run_id(pull_request.get("body"))
|
||||
if run_id is None:
|
||||
return None
|
||||
|
||||
for run in workflow_runs:
|
||||
if run["id"] != run_id:
|
||||
continue
|
||||
if (run.get("path") == _CI_DEVEL_WORKFLOW_PATH and
|
||||
run.get("event") == "workflow_dispatch" and
|
||||
run.get("head_sha") == self.latest_commit_sha):
|
||||
print(f"Using linked ci-devel run {run['html_url']}",
|
||||
file=sys.stderr)
|
||||
return run
|
||||
break
|
||||
|
||||
print(f"Linked Actions run {run_id} is not a ci-devel run for "
|
||||
f"{self.latest_commit_sha}; using regular workflow runs.",
|
||||
file=sys.stderr)
|
||||
return None
|
||||
|
||||
def check_required_labels(self, pull_request=None):
|
||||
"""
|
||||
Check if all expected labels are present
|
||||
|
||||
@@ -308,14 +362,10 @@ class Checker:
|
||||
f"required-labels-check ({self.required_labels})")
|
||||
return True
|
||||
|
||||
response = requests.get(
|
||||
f"{_GH_API_URL}/issues/{self.pr_number}",
|
||||
headers=_GH_HEADERS,
|
||||
timeout=60
|
||||
)
|
||||
response.raise_for_status()
|
||||
labels = set(_["name"] for _ in response.json()["labels"])
|
||||
if self.required_labels.issubset(labels):
|
||||
if pull_request is None:
|
||||
pull_request = self.get_pull_request()
|
||||
labels = set(_["name"] for _ in pull_request["labels"])
|
||||
if self.required_labels.issubset(labels) or self.ci_devel_run:
|
||||
return True
|
||||
print(f"To run all required tests the PR{self.pr_number} must have "
|
||||
f"{', '.join(self.required_labels.difference(labels))} labels "
|
||||
@@ -330,9 +380,17 @@ class Checker:
|
||||
"""
|
||||
print(f"Gatekeeper for project={os.environ['GITHUB_REPOSITORY']} and "
|
||||
f"SHA={self.latest_commit_sha} PR={self.pr_number}")
|
||||
if not self.check_required_labels():
|
||||
pull_request = None
|
||||
workflow_runs = None
|
||||
if self.pr_number:
|
||||
pull_request = self.get_pull_request()
|
||||
if self.get_linked_run_id(pull_request.get("body")) is not None:
|
||||
workflow_runs = self.get_workflow_runs(1)
|
||||
self.ci_devel_run = self.find_ci_devel_run(
|
||||
pull_request, workflow_runs)
|
||||
if not self.check_required_labels(pull_request):
|
||||
sys.exit(2)
|
||||
sys.exit(self.wait_for_required_tests())
|
||||
sys.exit(self.wait_for_required_tests(workflow_runs))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user