#!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0-or-later # Copyright 2022 sysmocom - s.f.m.c. GmbH import argparse import io import json import re import urllib.request jenkins_url = "https://jenkins.osmocom.org" re_start_build = re.compile("Starting building: gerrit-[a-zA-Z-_0-9]* #[0-9]*") re_result = re.compile("^pipeline_([a-zA-Z-_0-9:]*): (SUCCESS|FAILED)$") re_job_type = re.compile("JOB_TYPE=([a-zA-Z-_0-9]*),") re_distro = re.compile("Building binary packages for distro: '([a-zA-Z0-9:].*)'") def parse_args(): parser = argparse.ArgumentParser( description="Prepare a comment to be submitted to gerrit. Depending on" " the comment type, (start) either a link to the pipeline," " or (result) a summary of failed / successful builds from" " the pipeline we run for patches submitted to gerrit.") parser.add_argument("build_url", help="$BUILD_URL of the pipeline job, e.g." " https://jenkins.osmocom.org/jenkins/job/gerrit-osmo-bsc-nat/17/") parser.add_argument("-o", "--output", help="output json file") parser.add_argument("-t", "--type", help="comment type", choices=["start", "result"], required=True) parser.add_argument("-n", "--notify-on-success", action="store_true", help="always indicate in json that the owner should be" " notified via mail, not only on failure") return parser.parse_args() def stage_binpkgs_from_url(job_url): """ Multiple gerrit-binpkgs jobs may be started to build binary packages for multiple distributions. It is not clear from the job name / URL of a job for which distro it is building, so read it from the log output. :returns: a distro like "debian:12" """ global re_distro url = f"{job_url}/consoleText" with urllib.request.urlopen(url) as response: content = response.read().decode("utf-8") match = re_distro.search(content) assert match, f"couldn't find distro name in log: {url}" return match.group(1) def stage_from_job_name(job_name, job_url): if job_name == "gerrit-verifications-comment": # The job that runs this script. Don't include it in the summary. return None if job_name == "gerrit-lint": return "lint" if job_name == "gerrit-binpkgs": return stage_binpkgs_from_url(job_url) if job_name == "gerrit-pipeline-endianness": return "endianness" if job_name.endswith("-build"): return "build" assert False, f"couldn't figure out stage from job_name: {job_name}" def parse_pipeline(build_url): """ Parse started jobs and result from the pipeline log. :returns: a dict that looks like: {"build": {"name": "gerrit-osmo-bsc-nat-build", id=7, "passed": True, "url": "https://..."}, "lint": {...}, "deb": {...}, "rpm: {...}} """ global re_start_build global re_result global jenkins_url ret = {} url = f"{build_url}/consoleText" with urllib.request.urlopen(url) as response: for line in io.TextIOWrapper(response, encoding='utf-8'): # Parse start build lines for match in re_start_build.findall(line): job_name = match.split(" ")[2] job_id = int(match.split(" ")[3].replace("#", "")) job_url = f"{jenkins_url}/jenkins/job/{job_name}/{job_id}" stage = stage_from_job_name(job_name, job_url) if stage: ret[stage] = {"url": job_url, "name": job_name, "id": job_id} # Parse result lines match = re_result.match(line) if match: stage = match.group(1) if stage.startswith("comment_"): # Jobs that run this script, not relevant for summary continue assert stage in ret, f"found result for stage {stage}, but" \ " didn't find where it was started. The" \ " re_start_build regex probably needs to be adjusted" \ " to match the related gerrit-*-build job.\n\n" \ f"ret: {ret}" ret[stage]["passed"] = (match.group(2) == "SUCCESS") return ret def parse_build_matrix(job): """ Parse started jobs and result from the matrix of the build job. Usually it is only one job, but for some projects we build for multiple arches (x86_64, arm) or build multiple times with different configure flags. :param job: "build" dict from parse_pipeline() :returns: a list of jobs in the matrix, looks like: [{"passed": True, "url": "https://..."}, ...] """ global jenkins_url ret = [] url = f"{job['url']}/consoleFull" with urllib.request.urlopen(url) as response: for line in io.TextIOWrapper(response, encoding='utf-8'): if " completed with result " in line: url = line.split("