diff --git a/util/ci-wait-shotgun.py b/util/ci-wait-shotgun.py new file mode 100755 index 0000000000..ae126e615f --- /dev/null +++ b/util/ci-wait-shotgun.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +import os +import sys +import time + +import gitlab + + +def init_gitlab_project(): + if os.getenv("CI_SERVER_URL", None) is not None: + url = os.getenv("CI_SERVER_URL") + job_token = os.environ["CI_JOB_TOKEN"] + gl = gitlab.Gitlab(url, job_token=job_token) + else: # running locally on dev machine + gl = gitlab.Gitlab.from_config() + return gl.projects.get("isc-projects/bind9-shotgun-ci") + + +def parse_parent_pipeline_id(): + if len(sys.argv) != 2: + raise RuntimeError("usage: util/ci-wait-shotgun.py PIPELINE_ID") + try: + return int(sys.argv[1]) + except ValueError as exc: + raise RuntimeError("error: PIPELINE_ID must be a number") from exc + + +def wait_until(callback, timeout=1800, retry=10): + start = time.time() + while time.time() - start < timeout: + if callback(): + return + time.sleep(retry) + raise RuntimeError(f"error: timed out after {timeout}s") + + +def get_child_pipeline_id(project, pipeline_id): + pipeline = project.pipelines.get(pipeline_id) + + def pipeline_finished(): + pipeline.refresh() + return pipeline.finished_at is not None + + wait_until(pipeline_finished, timeout=3000) + + bridges = pipeline.bridges.list() + if len(bridges) != 1: + raise RuntimeError("error: expected exactly one child pipeline") + return bridges[0].downstream_pipeline["id"] + + +def get_postproc_job(project, pipeline_id): + pipeline = project.pipelines.get(pipeline_id) + postproc_job = None + + def job_finished(): + nonlocal postproc_job + for job in pipeline.jobs.list(get_all=True): + if job.name == "postproc": + postproc_job = job + if postproc_job is None: + raise RuntimeError("error: failed to find 'postproc' job in child pipeline") + return postproc_job.finished_at is not None + + wait_until(job_finished) + return postproc_job + + +def evaluate_postproc_job(job): + if job.status != "success": + raise RuntimeError("error: 'postproc' job didn't succeed") + index_url = ( + "https://isc-projects.gitlab-pages.isc.org/-/" + f"bind9-shotgun-ci/-/jobs/{job.id}/artifacts/index.html" + ) + print(f"Result ready for manual inspection: {index_url}") + + +def main(): + project = init_gitlab_project() + parent_pipeline_id = parse_parent_pipeline_id() + child_pipeline_id = get_child_pipeline_id(project, parent_pipeline_id) + postproc_job = get_postproc_job(project, child_pipeline_id) + evaluate_postproc_job(postproc_job) + + +if __name__ == "__main__": + try: + main() + except RuntimeError as err: + print(err) + sys.exit(1)