Files
poky/scripts/yocto_testresults_query.py
Richard Purdie 0549c498bd selftest/scripts: Update old git protocol references
git protocol accesses to our infrastructure are currently struggling and this
has highlighted a number of places we're making those obsolete access forms.

Update them to use https instead of the git protocol since it is preferred
and more reliable.

The devtool test needed quoting to handle the ';' in the url. The -f option
to devtool also shows a deprecation warning so remove that.

There were internal references to git protocol urls inside the nested git
submodules test report, which means those repos need updating to use
new git revisions.

(From OE-Core rev: cbb3e323b74d4351c772a9bcd553008c31a220f0)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
(cherry picked from commit 1ceba42623c5187d2f5a100d6a523abcdc75d34e)
Signed-off-by: Yoann Congal <yoann.congal@smile.fr>
Signed-off-by: Paul Barker <paul@pbarker.dev>
2026-05-04 13:57:32 +01:00

132 lines
5.6 KiB
Python
Executable File

#!/usr/bin/env python3
# Yocto Project test results management tool
# This script is an thin layer over resulttool to manage tes results and regression reports.
# Its main feature is to translate tags or branch names to revisions SHA1, and then to run resulttool
# with those computed revisions
#
# Copyright (C) 2023 OpenEmbedded Contributors
#
# SPDX-License-Identifier: MIT
#
import sys
import os
import argparse
import subprocess
import tempfile
import lib.scriptutils as scriptutils
script_path = os.path.dirname(os.path.realpath(__file__))
poky_path = os.path.abspath(os.path.join(script_path, ".."))
resulttool = os.path.abspath(os.path.join(script_path, "resulttool"))
logger = scriptutils.logger_create(sys.argv[0])
testresults_default_url="https://git.yoctoproject.org/yocto-testresults"
def create_workdir():
workdir = tempfile.mkdtemp(prefix='yocto-testresults-query.')
logger.info(f"Shallow-cloning testresults in {workdir}")
subprocess.check_call(["git", "clone", testresults_default_url, workdir, "--depth", "1"])
return workdir
def get_sha1(pokydir, revision):
try:
rev = subprocess.check_output(["git", "rev-list", "-n", "1", revision], cwd=pokydir).decode('utf-8').strip()
logger.info(f"SHA-1 revision for {revision} in {pokydir} is {rev}")
return rev
except subprocess.CalledProcessError:
logger.error(f"Can not find SHA-1 for {revision} in {pokydir}")
return None
def get_branch(tag):
# The tags in test results repository, as returned by git rev-list, have the following form:
# refs/tags/<branch>/<count>-g<sha1>/<num>
return '/'.join(tag.split("/")[2:-2])
def fetch_testresults(workdir, sha1):
logger.info(f"Fetching test results for {sha1} in {workdir}")
rawtags = subprocess.check_output(["git", "ls-remote", "--refs", "--tags", "origin", f"*{sha1}*"], cwd=workdir).decode('utf-8').strip()
if not rawtags:
raise Exception(f"No reference found for commit {sha1} in {workdir}")
branch = ""
for rev in [rawtag.split()[1] for rawtag in rawtags.splitlines()]:
if not branch:
branch = get_branch(rev)
logger.info(f"Fetching matching revision: {rev}")
subprocess.check_call(["git", "fetch", "--depth", "1", "origin", f"{rev}:{rev}"], cwd=workdir)
return branch
def compute_regression_report(workdir, basebranch, baserevision, targetbranch, targetrevision, args):
logger.info(f"Running resulttool regression between SHA1 {baserevision} and {targetrevision}")
command = [resulttool, "regression-git", "--branch", basebranch, "--commit", baserevision, "--branch2", targetbranch, "--commit2", targetrevision, workdir]
if args.limit:
command.extend(["-l", args.limit])
report = subprocess.check_output(command).decode("utf-8")
return report
def print_report_with_header(report, baseversion, baserevision, targetversion, targetrevision):
print("========================== Regression report ==============================")
print(f'{"=> Target:": <16}{targetversion: <16}({targetrevision})')
print(f'{"=> Base:": <16}{baseversion: <16}({baserevision})')
print("===========================================================================\n")
print(report, end='')
def regression(args):
logger.info(f"Compute regression report between {args.base} and {args.target}")
if args.testresultsdir:
workdir = args.testresultsdir
else:
workdir = create_workdir()
try:
baserevision = get_sha1(poky_path, args.base)
targetrevision = get_sha1(poky_path, args.target)
if not baserevision or not targetrevision:
logger.error("One or more revision(s) missing. You might be targeting nonexistant tags/branches, or are in wrong repository (you must use Poky and not oe-core)")
if not args.testresultsdir:
subprocess.check_call(["rm", "-rf", workdir])
sys.exit(1)
basebranch = fetch_testresults(workdir, baserevision)
targetbranch = fetch_testresults(workdir, targetrevision)
report = compute_regression_report(workdir, basebranch, baserevision, targetbranch, targetrevision, args)
print_report_with_header(report, args.base, baserevision, args.target, targetrevision)
finally:
if not args.testresultsdir:
subprocess.check_call(["rm", "-rf", workdir])
def main():
parser = argparse.ArgumentParser(description="Yocto Project test results helper")
subparsers = parser.add_subparsers(
help="Supported commands for test results helper",
required=True)
parser_regression_report = subparsers.add_parser(
"regression-report",
help="Generate regression report between two fixed revisions. Revisions can be branch name or tag")
parser_regression_report.add_argument(
'base',
help="Revision or tag against which to compare results (i.e: the older)")
parser_regression_report.add_argument(
'target',
help="Revision or tag to compare against the base (i.e: the newer)")
parser_regression_report.add_argument(
'-t',
'--testresultsdir',
help=f"An existing test results directory. {sys.argv[0]} will automatically clone it and use default branch if not provided")
parser_regression_report.add_argument(
'-l',
'--limit',
help=f"Maximum number of changes to display per test. Can be set to 0 to print all changes")
parser_regression_report.set_defaults(func=regression)
args = parser.parse_args()
args.func(args)
if __name__ == '__main__':
try:
ret = main()
except Exception:
ret = 1
import traceback
traceback.print_exc()
sys.exit(ret)