diff --git a/README.md b/README.md index c60a604..e99277a 100644 --- a/README.md +++ b/README.md @@ -233,6 +233,26 @@ options: The number of builds to fetch when searching for a reproducer. ``` +### `squad-create-reproducer-from-testrun`: Get a reproducer for a given TestRun ID. + +This script fetches the build or test reproducer for a given TestRun ID. + +``` +usage: squad-create-reproducer-from-testrun [-h] --testrun TESTRUN [--debug] + [--filename FILENAME] [--local | --plan] + +Provide a SQUAD TestRun ID to download the build or test reproducer for that TestRun. The +reproducer will be printed to the terminal and written to a file. + +optional arguments: + -h, --help show this help message and exit + --testrun TESTRUN The TestRun ID of the build or test to fetch the reproducer for. + --debug Display debug messages. + --filename FILENAME Name for the reproducer file, 'reproducer' by default. + --local Fetch a TuxRun or TuxMake reproducer rather than a TuxSuite reproducer. + --plan Fetch a TuxPlan reproducer rather than a TuxTest of TuxBuild reproducer. +``` + ### `squad-create-skipfile-reproducers`: Creating skipfile reproducers The `squad-create-skipfile-reproducers` script can be used to create TuxRun or diff --git a/fetch-and-launch-reproducer.sh b/fetch-and-launch-reproducer.sh new file mode 100644 index 0000000..8a5e0a0 --- /dev/null +++ b/fetch-and-launch-reproducer.sh @@ -0,0 +1,11 @@ +# Environment variables needed: +# - TUXSUITE_TOKEN - the token to submit tuxsuite reproducer + +TESTRUN=$1 +REPRODUCER=$2 +pip install -r requirements.txt +inputs="--testrun $TESTRUN --filename $REPRODUCER" +echo $inputs +python validate-inputs.py $inputs +python squad-create-reproducer-from-testrun $inputs --plan +tuxsuite plan $REPRODUCER \ No newline at end of file diff --git a/squad-create-reproducer-from-testrun b/squad-create-reproducer-from-testrun new file mode 100755 index 0000000..89743a1 --- /dev/null +++ b/squad-create-reproducer-from-testrun @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# vim: set ts=4 +# +# Copyright 2023-present Linaro Limited +# +# SPDX-License-Identifier: MIT + + +from argparse import ArgumentParser +from logging import INFO, basicConfig, getLogger +from os import chmod, getenv +from stat import S_IRUSR, S_IWUSR, S_IXUSR +from sys import exit + +from squad_client.core.api import SquadApi + +from squadutilslib import ( + ReproducerNotFound, + get_reproducer_from_testrun, +) + +squad_host_url = "https://qa-reports.linaro.org/" +SquadApi.configure(cache=3600, url=getenv("SQUAD_HOST", squad_host_url)) + +basicConfig(level=INFO) +logger = getLogger(__name__) + + +def parse_args(raw_args): + parser = ArgumentParser( + description="Provide a SQUAD TestRun ID to download the build or test reproducer for that TestRun." + + " The reproducer will be printed to the terminal and written to a file." + ) + + parser.add_argument( + "--testrun", + required=True, + help="The TestRun ID of the build or test to fetch the reproducer for.", + ) + + # Optional arguments: + parser.add_argument( + "--debug", + required=False, + action="store_true", + default=False, + help="Display debug messages.", + ) + + parser.add_argument( + "--filename", + required=False, + default="reproducer", + help="Name for the reproducer file, 'reproducer' by default.", + ) + + group = parser.add_mutually_exclusive_group() + + group.add_argument( + "--local", + required=False, + action="store_true", + default=False, + help="Fetch a TuxRun or TuxMake reproducer rather than a TuxSuite reproducer.", + ) + + group.add_argument( + "--plan", + required=False, + action="store_true", + default=False, + help="Fetch a TuxPlan reproducer rather than a TuxTest of TuxBuild reproducer.", + ) + + return parser.parse_args(raw_args) + + +def run(raw_args=None): + args = parse_args(raw_args) + + try: + reproducer, is_test_reproducer = get_reproducer_from_testrun( + args.testrun, args.filename, args.plan, args.local + ) + + except ReproducerNotFound as e: + logger.error(f"No reproducer could be found for TestRun {args.testrun}") + logger.error(f"{e}") + return -1 + + print(reproducer) + + # Make the script executable + chmod(args.filename, S_IXUSR | S_IRUSR | S_IWUSR) + logger.info(f"file created: {args.filename}") + + +if __name__ == "__main__": + exit(run()) diff --git a/squadutilslib.py b/squadutilslib.py index 0eb167a..6791404 100644 --- a/squadutilslib.py +++ b/squadutilslib.py @@ -58,6 +58,62 @@ def get_file(path, filename=None): raise Exception(f"Path {path} not found") +def get_reproducer_from_testrun(testrun_id, filename, plan=False, local=False): + """Given a testrun, download its reproducer.""" + testrun = TestRun(testrun_id) + is_test_reproducer = None + reproducer = None + + if local and plan: + logger.error("Error: not valid to request both plan=True and local=True.") + raise ReproducerNotFound + + # If there is a download_url try to treat it as a build + if testrun.metadata.download_url: + try: + if local: + reproducer_file = get_file( + testrun.metadata.download_url + "/tuxmake_reproducer.sh", filename + ) + elif plan: + reproducer_file = get_file( + testrun.metadata.download_url + "/tux_plan.yaml", filename + ) + else: + reproducer_file = get_file( + testrun.metadata.download_url + "/tuxsuite_reproducer.sh", filename + ) + is_test_reproducer = False + with open(reproducer_file) as f: + reproducer = f.read() + except HTTPError: + pass + + if not reproducer: + # If no build reproducer was found, treat it as a test + try: + if local: + reproducer_file = get_file( + testrun.metadata.job_url + "/reproducer", filename + ) + elif plan: + reproducer_file = get_file( + testrun.metadata.job_url + "/tux_plan", filename + ) + else: + reproducer_file = get_file( + testrun.metadata.job_url + "/tuxsuite_reproducer", filename + ) + is_test_reproducer = True + with open(reproducer_file) as f: + reproducer = f.read() + except HTTPError: + logger.error("No build or test reproducer found.") + raise ReproducerNotFound + + return reproducer, is_test_reproducer + + def filter_projects(projects, pattern): filtered = [] for p in projects: @@ -193,20 +249,11 @@ def get_reproducer( # In theory there should only be one of those logger.debug(f"Testrun id: {testrun.id}") - try: - if local: - reproducer = get_file( - f"{testrun.job_url}/reproducer", filename=filename - ) - else: - reproducer = get_file( - f"{testrun.job_url}/tuxsuite_reproducer", filename=filename - ) - except HTTPError: - logger.error(f"Reproducer not found at {testrun.job_url}!") - raise ReproducerNotFound + reproducer, is_test_reproducer = get_reproducer_from_testrun( + testrun_id=testrun.id, filename=filename, plan=False, local=local + ) return ( - Path(reproducer).read_text(encoding="utf-8"), + reproducer, Build(getid(testrun.build)).metadata.git_describe, testrun.metadata.build_name, ) diff --git a/validate-inputs.py b/validate-inputs.py new file mode 100644 index 0000000..d16c385 --- /dev/null +++ b/validate-inputs.py @@ -0,0 +1,44 @@ +from argparse import ArgumentParser +import logging + + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +def parse_args(raw_args): + parser = ArgumentParser() + + parser.add_argument( + "--testrun", + required=True, + help="The ID of the TestRun.", + ) + + parser.add_argument( + "--filename", + required=True, + help="The reproducer file.", + ) + + parser.add_argument( + "--plan", + required=False, + action="store_true", + default=False, + help="Fetch a TuxPlan reproducer rather than a TuxTest of TuxBuild reproducer.", + ) + + return parser.parse_args(raw_args) + + +def run(raw_args=None): + args = parse_args(raw_args) + + logger.debug(args) + + return 0 + + +if __name__ == "__main__": + exit(run())