From c930fe3795221618712efd97bef254f94345fd90 Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Thu, 25 Apr 2024 10:48:10 +0100 Subject: [PATCH 01/27] Add coverity push script Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 454 +++++++++++++++++++++++++++++++++ 1 file changed, 454 insertions(+) create mode 100755 coverity/push_coverity_scan.py diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py new file mode 100755 index 000000000..6a9890766 --- /dev/null +++ b/coverity/push_coverity_scan.py @@ -0,0 +1,454 @@ +#!/usr/bin/env python3 + +""" Build mbedtls using the coverity toolset and upload said build to coverity. + +A small script designed to be run both in the CI and on local users machines. + +Required: + +1. Path to mbedtls directory +2. A project coverity token (got from the Coverity site / Project Settings +3. An email address to send notifications to. + +(The last of these is annoying, but things will not work without it, all that it +is used for is to notify that the build was submitted) + +The token can either be passed in as an argument with -t, or via the environment +in the variable 'COVERITY_TOKEN' + +Other options: + +* -b / --branch: If specified, this branch will be checked out prior to build. +* -c / --covtools: If specified, the coverity tools will be downloaded here. If + there is already a set of coverity tools in the specified directory, they + will be checked for appropriate version, and overwritten only if necessary. +* -p / --pre-build-step: Specify the command to run pre-build - defaults to + 'make clean'. +* -s / --build-step: Specify the command to run to build the project - defaults + to 'make -j'. + +* -l / --log: Specify a file to log information on the build to. +* -m / --backupdir: If specified, this will be used as a directory to backup + built tar files to. +* -v / --verbose: If specified, all logging will be done to stdout. + +""" + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + +import argparse +from subprocess import Popen, PIPE +import shlex +from traceback import format_exc + +from typing import Tuple, Iterable + +import os +import pathlib +import shutil +from tempfile import mkstemp, mkdtemp +from datetime import datetime +import tarfile +import hashlib +import logging +import sys + +import requests + +class BuildError(Exception): + """ Exception class for build errors """ + pass + + +class ConfigError(Exception): + """ Exception class for configuration errors """ + pass + + +def do_shell_exec(exec_string: str, expected_result: int = 0) -> Tuple[bool, str, str]: + + """ Execute the given string in a shell, try to ascertain success + """ + + shell_process = Popen(shlex.split(exec_string), stdin=PIPE, stdout=PIPE, stderr=PIPE) + + (shell_stdout, shell_stderr) = shell_process.communicate() + + if shell_process.returncode != expected_result: + return False, shell_stdout.decode("utf-8"), shell_stderr.decode("utf-8") + + return True, shell_stdout.decode("utf-8"), shell_stderr.decode("utf-8") + +def check_coverity_scan_tools_version(token: str, tools_dir: str) -> bool: + + """ Get the md5 of the coverity tools package from coverity, so we can check we have the latest + version. + """ + + post_data = [('token', token), + ('project', 'ARMmbed/mbedtls'), + ('md5', '1')] + + # Presumption of linux here, could support other build types? + md5_request = requests.get('https://scan.coverity.com/download/linux64', data=post_data, + timeout=60) + md5_request.raise_for_status() + + md5_path = pathlib.Path(tools_dir) + md5_path = md5_path / 'coverity_tool.md5' + + with md5_path.open('r') as md5_file: + tools_hash = md5_file.read() + + return tools_hash == md5_request.text + +def backup_config_files(mbedtls_dir: pathlib.Path, restore: bool) -> None: + + """Backup / Restore config file.""" + + config_path = pathlib.Path(mbedtls_dir) + config_path = config_path / 'include' / 'mbedtls' / 'mbedtls_config.h' + config_path.resolve() + + backup_path = config_path.with_suffix('.h.bak') + + if restore: + if backup_path.is_file(): + backup_path.replace(config_path) + else: + shutil.copy(config_path, config_path.with_suffix('.h.bak')) + +def filter_root_tar_dir(tar_file: tarfile.TarFile) -> Iterable[tarfile.TarInfo]: + + """ This function allows extraction of the contents of the first containing directory in the tar + directly to the target folder, by stripping the first containing folder from each path. + """ + + for tar_member in tar_file.getmembers(): + + member_path = pathlib.Path(tar_member.path) + + if len(member_path.parts) > 1: + tar_member.path = str(member_path.relative_to(*member_path.parts[:1])) + yield tar_member + +def md5_hash(buffer: bytes) -> str: + + """ Return the md5 hash of the passed in buffer in hex as a string """ + hash_md5 = hashlib.md5() + bytes_len = len(buffer) + buffer_start = 0 + buffer_end = 4096 + + while buffer_end < bytes_len: + + hash_md5.update(buffer[buffer_start:buffer_end]) + + buffer_start += 4096 + buffer_end += 4096 + + hash_md5.update(buffer[buffer_start:bytes_len]) + + return hash_md5.hexdigest() + +def download_coverity_scan_tools(logger: logging.Logger, token: str, tools_dir: str): + + """ Download the required coverity scan tools to the given directory, using the passed in token. + """ + + post_data = [('token', token), + ('project', 'ARMmbed/mbedtls')] + + logger.log(logging.INFO, "Downloading Coverity Scan....") + + # Presumption of linux here, could support other build types? + package_request = requests.get('https://scan.coverity.com/download/linux64', data=post_data, + timeout=60) + package_request.raise_for_status() + + # Write this to a temp file, as we need to extract it + temp_file_handle, temp_file_name = mkstemp() + + tools_hash = md5_hash(package_request.content) + + with os.fdopen(temp_file_handle, "wb") as temp_file: + temp_file.write(package_request.content) + + with tarfile.open(temp_file_name, "r:gz") as tar_file: + tar_file.extractall(path=tools_dir, members=filter_root_tar_dir(tar_file), filter='data') + + os.unlink(temp_file_name) + + md5_path = pathlib.Path(tools_dir) + md5_path = md5_path / 'coverity_tool.md5' + + with md5_path.open('w') as md5_file: + md5_file.write(tools_hash) + +def build_mbedtls(logger: logging.Logger, mbedtls_dir: pathlib.Path, tools_dir: pathlib.Path, + branch: str, pre_build_step: str, build_step: str, tar_file_name: str) -> None: + + + """ Build mbedtls located in the passed in dir, using the tools specified, using the given + pre-build and build commands. Tar the results up into the given file name, as required by + coverity. """ + + os.chdir(mbedtls_dir) + + # Ensure that given git directory is up to date. + success, std_out, std_err = do_shell_exec('git fetch --all') + + logger.log(logging.INFO, std_out) + + if not success: + raise BuildError(std_err) + + # Switch to correct branch. + if branch != '': + success, std_out, std_err = do_shell_exec('git checkout {}'.format(branch)) + + logger.log(logging.INFO, std_out) + + if not success: + raise BuildError(std_err) + + success, std_out, std_err = do_shell_exec('scripts/config.py full_no_platform') + + logger.log(logging.INFO, std_out) + + + # do pre-build steps + success, std_out, std_err = do_shell_exec(pre_build_step) + + logger.log(logging.INFO, std_out) + + if not success: + raise BuildError(std_err) + + # build + coverity_tool = tools_dir / 'bin' / 'cov-build' + + success, std_out, std_err = do_shell_exec('{} --dir cov-int {}'.format(str(coverity_tool), + build_step)) + + logger.log(logging.INFO, std_out) + + if not success: + raise BuildError(std_err) + + # TODO, ensure enough units were compiled.. + + # tar up the results + cov_int_dir = mbedtls_dir / 'cov-int' / '' + + logger.log(logging.INFO, 'Writing {} to tar file : {}'.format(cov_int_dir, tar_file_name)) + + with tarfile.open(tar_file_name, "w:gz") as tar_file: + tar_file.add(str(cov_int_dir), recursive=True, arcname='cov-int') + tar_file.close() + +def upload_build(logger: logging.Logger, token: str, email_address: str, tar_file_name: str): + + """ Upload the build (tar file specified) to the url given by the project, using the passed in + auth token + """ + + base_url = 'https://scan.coverity.com/projects/4583/builds/' + + # Step 1. Initialise a build, get an upload url and build ID + logger.log(logging.INFO, 'Requesting/initialising coverity build') + + # TODO - Allow passing in version, description? + tar_file_path = pathlib.Path(tar_file_name) + + build_post_data = [('version', ''), + ('description', ''), + ('email', email_address), + ('token', token), + ('file_name', tar_file_path.name)] + + build_request = requests.post(base_url + 'init', data=build_post_data, timeout=60) + build_request.raise_for_status() + + build_response = build_request.json() + + logger.log(logging.INFO, 'Got coverity build ID {}'.format(build_response['build_id'])) + + # Step 2. Upload the previously created tar file to the upload url received in the last step. + logger.log(logging.INFO, 'Uploading tar file to {}'.format(build_response['url'])) + + with open(tar_file_name, 'rb') as data_file: + upload_headers = {'Content-type': 'application/json'} + upload_request = requests.put(build_response['url'], data=data_file, + headers=upload_headers, timeout=60) + upload_request.raise_for_status() + + # Step 4. Trigger the build analysis on coverity (important, if this doesn't happen the build + # will be 'stuck' - it can be cancelled via the website, where it will be seen as 'in queue' but + # won't move forwards). + + logger.log(logging.INFO, 'Triggering coverity build') + + trigger_post_data = [('token', token)] + trigger_url = '{}/{}/enqueue'.format(base_url, build_response['build_id']) + trigger_request = requests.put(trigger_url, data=trigger_post_data, timeout=60) + + trigger_request.raise_for_status() + +def main(): + + parser = argparse.ArgumentParser(description='Push MbedTLS build to Coverity Scan') + parser.add_argument('-b', '--branch', help='Branch to check out in mbedtls project', + default='') + parser.add_argument('-c', '--covtools', + help='Directory to store downloaded coverity tools in') + parser.add_argument('-e', '--email', help='Email address to send build notifications to', + required=True) + parser.add_argument('-p', '--pre-build-step', help='Command to run pre-build', + default='make clean') + parser.add_argument('-s', '--build-step', help='Command to run to build the project', + default='make -j') + parser.add_argument('-t', '--token', help='Coverity Scan Token') + parser.add_argument('-l', '--log', help='File to log to') + parser.add_argument('-m', '--backupdir', help='Directory to backup tar files to') + parser.add_argument('-v', '--verbose', action='store_true', + help='Verbose logging to stdout') + + parser.add_argument('mbedtlsdir', help='MbedTLS directory') + + args = parser.parse_args() + + logger = logging.getLogger("Push_Coverity") + logger.setLevel(logging.DEBUG) + + stdout_log_formatter = logging.Formatter("%(levelname)s: %(message)s") + stdout_log_handler = logging.StreamHandler(sys.stdout) + + if args.verbose: + stdout_log_handler.setLevel(logging.DEBUG) + else: + stdout_log_handler.setLevel(logging.ERROR) + + stdout_log_handler.setFormatter(stdout_log_formatter) + + logger.addHandler(stdout_log_handler) + + if args.log is not None: + file_log_formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s", + datefmt="%H:%M:%S") + file_log_handler = logging.FileHandler(args.log) + file_log_handler.setLevel(logging.DEBUG) + file_log_handler.setFormatter(file_log_formatter) + + logger.addHandler(file_log_handler) + + ret_code = 0 + + logger.log(logging.INFO, "### Script starting.") + + tools_path_set = False + tar_file_set = False + + try: + tools_path = pathlib.Path('') + tar_file = pathlib.Path('') + + mbedtls_path = pathlib.Path(args.mbedtlsdir) + mbedtls_path = mbedtls_path.resolve() + + token_found = False + if 'COVERITY_TOKEN' in os.environ: + coverity_token = os.environ['COVERITY_TOKEN'] + token_found = True + + # Allow passed argument token to override + if args.token is not None: + coverity_token = args.token + token_found = True + + if not token_found: + raise ConfigError('Coverity token not found') + + if args.covtools is None: + # If no cov tools dir specified, then use a temporary (long path, + # given the need to redownload each time). + dir_path = mkdtemp() + tools_path = pathlib.Path(dir_path) + tools_path_set = True + + download_coverity_scan_tools(logger, coverity_token, tools_path) + else: + # Coverity tools dir specified, see if it exists, contains tools and + # those tools are up to date. + tools_path = pathlib.Path(args.toolsdir) + tools_path = tools_path.resolve() + tools_path_set = True + + if not tools_path.is_dir(): + + logger.log(logging.INFO, 'Tools dir does not exist, creating.') + tools_path.mkdir() + + download_coverity_scan_tools(logger, coverity_token, tools_path) + else: + hash_path = tools_path / 'coverity_tool.md5' + + if not hash_path.is_file(): + logger.log(logging.INFO, 'Hash file does not exist, re-downloading.') + download_coverity_scan_tools(logger, coverity_token, tools_path) + else: + # Attempt to check if our coverity scan package is up to date. + if not check_coverity_scan_tools_version(coverity_token, tools_path): + logger.log(logging.INFO, 'Hash file differs, re-downloading tools.') + download_coverity_scan_tools(logger, coverity_token, tools_path) + + backup_config_files(mbedtls_path, False) + + if args.backupdir is not None: + backup_path = pathlib.Path(args.backupdir) + + if not backup_path.is_dir(): + raise ConfigError('Backup dir specfied does not exist.') + + backup_path = backup_path.resolve() + tar_file = backup_path / datetime.today().strftime('mbedtls-%y-%m-%d.tar.gz') + tar_file_set = True + + else: + tar_file_handle, tar_file_name = mkstemp() + tar_file = pathlib.Path(tar_file_name) + tar_file_set = True + + if not mbedtls_path.is_dir(): + raise ConfigError('MBedTLS directory specified does not exist.') + + build_mbedtls(logger, mbedtls_path, tools_path, args.branch, + args.pre_build_step, args.build_step, tar_file) + + # send completed tar file to coverity + upload_build(logger, coverity_token, args.email, tar_file) + + except requests.exceptions.RequestException as e: + logger.log(logging.ERROR, format_exc()) + ret_code = 1 + except: + logger.log(logging.ERROR, 'Exception occurred: {}'.format(format_exc())) + ret_code = 1 + + finally: + # Clean up, if necessary + if args.backupdir is None and tar_file_set: + os.unlink(tar_file) + + if args.covtools is None and tools_path_set: + shutil.rmtree(tools_path) + + backup_config_files(mbedtls_path, True) + + logger.log(logging.INFO, "### Script done.") + return ret_code + + +if __name__ == "__main__": + sys.exit(main()) From a1cd639840768ccd19e08ae268e3bda2b6d6a30c Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 10 May 2024 16:55:00 +0100 Subject: [PATCH 02/27] Add return types for all functions Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 6a9890766..4ac66d856 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -152,7 +152,7 @@ def md5_hash(buffer: bytes) -> str: return hash_md5.hexdigest() -def download_coverity_scan_tools(logger: logging.Logger, token: str, tools_dir: str): +def download_coverity_scan_tools(logger: logging.Logger, token: str, tools_dir: str) -> None: """ Download the required coverity scan tools to the given directory, using the passed in token. """ @@ -248,7 +248,7 @@ def build_mbedtls(logger: logging.Logger, mbedtls_dir: pathlib.Path, tools_dir: tar_file.add(str(cov_int_dir), recursive=True, arcname='cov-int') tar_file.close() -def upload_build(logger: logging.Logger, token: str, email_address: str, tar_file_name: str): +def upload_build(logger: logging.Logger, token: str, email_address: str, tar_file_name: str) -> None: """ Upload the build (tar file specified) to the url given by the project, using the passed in auth token @@ -296,7 +296,7 @@ def upload_build(logger: logging.Logger, token: str, email_address: str, tar_fil trigger_request.raise_for_status() -def main(): +def main() -> int: parser = argparse.ArgumentParser(description='Push MbedTLS build to Coverity Scan') parser.add_argument('-b', '--branch', help='Branch to check out in mbedtls project', From d98e9319b378f0b5344c12c1c6339269820eb968 Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 10 May 2024 17:09:11 +0100 Subject: [PATCH 03/27] Remove unnecessary function Mistaken usage of hashlib.md5(), much simpler form is available. Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 4ac66d856..24d4cfd03 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -133,25 +133,6 @@ def filter_root_tar_dir(tar_file: tarfile.TarFile) -> Iterable[tarfile.TarInfo]: tar_member.path = str(member_path.relative_to(*member_path.parts[:1])) yield tar_member -def md5_hash(buffer: bytes) -> str: - - """ Return the md5 hash of the passed in buffer in hex as a string """ - hash_md5 = hashlib.md5() - bytes_len = len(buffer) - buffer_start = 0 - buffer_end = 4096 - - while buffer_end < bytes_len: - - hash_md5.update(buffer[buffer_start:buffer_end]) - - buffer_start += 4096 - buffer_end += 4096 - - hash_md5.update(buffer[buffer_start:bytes_len]) - - return hash_md5.hexdigest() - def download_coverity_scan_tools(logger: logging.Logger, token: str, tools_dir: str) -> None: """ Download the required coverity scan tools to the given directory, using the passed in token. @@ -170,7 +151,7 @@ def download_coverity_scan_tools(logger: logging.Logger, token: str, tools_dir: # Write this to a temp file, as we need to extract it temp_file_handle, temp_file_name = mkstemp() - tools_hash = md5_hash(package_request.content) + tools_hash = hashlib.md5(package_request.content).hexdigest() with os.fdopen(temp_file_handle, "wb") as temp_file: temp_file.write(package_request.content) From 7270881bfbcb297f992b22cf08e3a1dbf5e3bc4b Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 10 May 2024 17:56:26 +0100 Subject: [PATCH 04/27] Change default build step to make -j$(nproc) -j without a number means unlimited, and is likely a bad idea on limited systems. Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 24d4cfd03..782b7925b 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -53,6 +53,7 @@ import hashlib import logging import sys +from multiprocessing import cpu_count import requests @@ -289,7 +290,7 @@ def main() -> int: parser.add_argument('-p', '--pre-build-step', help='Command to run pre-build', default='make clean') parser.add_argument('-s', '--build-step', help='Command to run to build the project', - default='make -j') + default = 'make -j{}'.format(cpu_count())) parser.add_argument('-t', '--token', help='Coverity Scan Token') parser.add_argument('-l', '--log', help='File to log to') parser.add_argument('-m', '--backupdir', help='Directory to backup tar files to') From 4c5ddc2c688a268bbbc2abade1dce9d2c1b54f0c Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 10 May 2024 18:02:18 +0100 Subject: [PATCH 05/27] Change no branch handling to none (Rather than empty string) Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 782b7925b..e35b4e941 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -187,7 +187,7 @@ def build_mbedtls(logger: logging.Logger, mbedtls_dir: pathlib.Path, tools_dir: raise BuildError(std_err) # Switch to correct branch. - if branch != '': + if branch is not None: success, std_out, std_err = do_shell_exec('git checkout {}'.format(branch)) logger.log(logging.INFO, std_out) @@ -281,8 +281,7 @@ def upload_build(logger: logging.Logger, token: str, email_address: str, tar_fil def main() -> int: parser = argparse.ArgumentParser(description='Push MbedTLS build to Coverity Scan') - parser.add_argument('-b', '--branch', help='Branch to check out in mbedtls project', - default='') + parser.add_argument('-b', '--branch', help='Branch to check out in mbedtls project') parser.add_argument('-c', '--covtools', help='Directory to store downloaded coverity tools in') parser.add_argument('-e', '--email', help='Email address to send build notifications to', From c73f1b96c8544a376316481799a26387b4479781 Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 10 May 2024 18:07:24 +0100 Subject: [PATCH 06/27] Fix mispellings of trademark Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index e35b4e941..c2d34c529 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 -""" Build mbedtls using the coverity toolset and upload said build to coverity. +""" Build Mbed TLS using the coverity toolset and upload said build to coverity. A small script designed to be run both in the CI and on local users machines. Required: -1. Path to mbedtls directory +1. Path to Mbed TLS directory 2. A project coverity token (got from the Coverity site / Project Settings 3. An email address to send notifications to. @@ -280,7 +280,7 @@ def upload_build(logger: logging.Logger, token: str, email_address: str, tar_fil def main() -> int: - parser = argparse.ArgumentParser(description='Push MbedTLS build to Coverity Scan') + parser = argparse.ArgumentParser(description='Push Mbed TLS build to Coverity Scan') parser.add_argument('-b', '--branch', help='Branch to check out in mbedtls project') parser.add_argument('-c', '--covtools', help='Directory to store downloaded coverity tools in') @@ -296,7 +296,7 @@ def main() -> int: parser.add_argument('-v', '--verbose', action='store_true', help='Verbose logging to stdout') - parser.add_argument('mbedtlsdir', help='MbedTLS directory') + parser.add_argument('mbedtlsdir', help='Mbed TLS directory') args = parser.parse_args() @@ -402,7 +402,7 @@ def main() -> int: tar_file_set = True if not mbedtls_path.is_dir(): - raise ConfigError('MBedTLS directory specified does not exist.') + raise ConfigError('MBed TLS directory specified does not exist.') build_mbedtls(logger, mbedtls_path, tools_path, args.branch, args.pre_build_step, args.build_step, tar_file) From ee04afc275af5c14e29269ccfde9b5fbea553027 Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 10 May 2024 18:48:45 +0100 Subject: [PATCH 07/27] Make comments more PEP257 compliant Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 36 +++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index c2d34c529..3c89f908b 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -68,9 +68,7 @@ class ConfigError(Exception): def do_shell_exec(exec_string: str, expected_result: int = 0) -> Tuple[bool, str, str]: - - """ Execute the given string in a shell, try to ascertain success - """ + """ Execute the given string in a shell, try to ascertain success. """ shell_process = Popen(shlex.split(exec_string), stdin=PIPE, stdout=PIPE, stderr=PIPE) @@ -82,9 +80,9 @@ def do_shell_exec(exec_string: str, expected_result: int = 0) -> Tuple[bool, str return True, shell_stdout.decode("utf-8"), shell_stderr.decode("utf-8") def check_coverity_scan_tools_version(token: str, tools_dir: str) -> bool: + """ Get the md5 of the coverity tools package from coverity. - """ Get the md5 of the coverity tools package from coverity, so we can check we have the latest - version. + Enable us to check that we have the latest version, saving a potential large download. """ post_data = [('token', token), @@ -105,8 +103,7 @@ def check_coverity_scan_tools_version(token: str, tools_dir: str) -> bool: return tools_hash == md5_request.text def backup_config_files(mbedtls_dir: pathlib.Path, restore: bool) -> None: - - """Backup / Restore config file.""" + """Backup / Restore config file. """ config_path = pathlib.Path(mbedtls_dir) config_path = config_path / 'include' / 'mbedtls' / 'mbedtls_config.h' @@ -122,8 +119,10 @@ def backup_config_files(mbedtls_dir: pathlib.Path, restore: bool) -> None: def filter_root_tar_dir(tar_file: tarfile.TarFile) -> Iterable[tarfile.TarInfo]: - """ This function allows extraction of the contents of the first containing directory in the tar - directly to the target folder, by stripping the first containing folder from each path. + """ Filter the tar file root dir out. + + This function allows extraction of the contents of the first containing directory in the tar + file directly to the target folder, by stripping the first containing folder from each path. """ for tar_member in tar_file.getmembers(): @@ -136,7 +135,9 @@ def filter_root_tar_dir(tar_file: tarfile.TarFile) -> Iterable[tarfile.TarInfo]: def download_coverity_scan_tools(logger: logging.Logger, token: str, tools_dir: str) -> None: - """ Download the required coverity scan tools to the given directory, using the passed in token. + """ Download the coverity scan tools. + + Download the required coverity scan tools to the given directory, using the passed in token. """ post_data = [('token', token), @@ -170,11 +171,12 @@ def download_coverity_scan_tools(logger: logging.Logger, token: str, tools_dir: def build_mbedtls(logger: logging.Logger, mbedtls_dir: pathlib.Path, tools_dir: pathlib.Path, branch: str, pre_build_step: str, build_step: str, tar_file_name: str) -> None: + """ Build Mbed TLS, using the coverity tools. - - """ Build mbedtls located in the passed in dir, using the tools specified, using the given - pre-build and build commands. Tar the results up into the given file name, as required by - coverity. """ + Build the MBed TLS source located in the passed in dir, using the tools specified, using the + given pre-build and build commands. Tar the results up into the given file name, as required by + coverity. + """ os.chdir(mbedtls_dir) @@ -232,8 +234,10 @@ def build_mbedtls(logger: logging.Logger, mbedtls_dir: pathlib.Path, tools_dir: def upload_build(logger: logging.Logger, token: str, email_address: str, tar_file_name: str) -> None: - """ Upload the build (tar file specified) to the url given by the project, using the passed in - auth token + """ Upload build to coverity. + + Upload the build (tar file specified) to the url given by the project, using the passed in auth + token. """ base_url = 'https://scan.coverity.com/projects/4583/builds/' From 3c882dbaa3fd2b4648c63bcf41daca5e2426406f Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 10 May 2024 20:51:26 +0100 Subject: [PATCH 08/27] Remove temp file when downloading coverity tools Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 3c89f908b..028c5c0e0 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -54,6 +54,7 @@ import logging import sys from multiprocessing import cpu_count +from io import BytesIO import requests @@ -150,19 +151,12 @@ def download_coverity_scan_tools(logger: logging.Logger, token: str, tools_dir: timeout=60) package_request.raise_for_status() - # Write this to a temp file, as we need to extract it - temp_file_handle, temp_file_name = mkstemp() - tools_hash = hashlib.md5(package_request.content).hexdigest() - with os.fdopen(temp_file_handle, "wb") as temp_file: - temp_file.write(package_request.content) - - with tarfile.open(temp_file_name, "r:gz") as tar_file: + # Extract the (filtered) downloaded tar file to the target dir. + with tarfile.open(fileobj=BytesIO(package_request.content)) as tar_file: tar_file.extractall(path=tools_dir, members=filter_root_tar_dir(tar_file), filter='data') - os.unlink(temp_file_name) - md5_path = pathlib.Path(tools_dir) md5_path = md5_path / 'coverity_tool.md5' From 82201a5c699854bb0912d3ab0cb664e166b6097f Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Mon, 13 May 2024 17:11:57 +0100 Subject: [PATCH 09/27] Rename misnamed query params Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 028c5c0e0..1ba8f26f6 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -86,12 +86,12 @@ def check_coverity_scan_tools_version(token: str, tools_dir: str) -> bool: Enable us to check that we have the latest version, saving a potential large download. """ - post_data = [('token', token), - ('project', 'ARMmbed/mbedtls'), - ('md5', '1')] + query_data = [('token', token), + ('project', 'ARMmbed/mbedtls'), + ('md5', '1')] # Presumption of linux here, could support other build types? - md5_request = requests.get('https://scan.coverity.com/download/linux64', data=post_data, + md5_request = requests.get('https://scan.coverity.com/download/linux64', data=query_data, timeout=60) md5_request.raise_for_status() @@ -141,13 +141,13 @@ def download_coverity_scan_tools(logger: logging.Logger, token: str, tools_dir: Download the required coverity scan tools to the given directory, using the passed in token. """ - post_data = [('token', token), - ('project', 'ARMmbed/mbedtls')] + query_data = [('token', token), + ('project', 'ARMmbed/mbedtls')] logger.log(logging.INFO, "Downloading Coverity Scan....") # Presumption of linux here, could support other build types? - package_request = requests.get('https://scan.coverity.com/download/linux64', data=post_data, + package_request = requests.get('https://scan.coverity.com/download/linux64', data=query_data, timeout=60) package_request.raise_for_status() @@ -270,9 +270,9 @@ def upload_build(logger: logging.Logger, token: str, email_address: str, tar_fil logger.log(logging.INFO, 'Triggering coverity build') - trigger_post_data = [('token', token)] + trigger_query_data = [('token', token)] trigger_url = '{}/{}/enqueue'.format(base_url, build_response['build_id']) - trigger_request = requests.put(trigger_url, data=trigger_post_data, timeout=60) + trigger_request = requests.put(trigger_url, data=trigger_query_data, timeout=60) trigger_request.raise_for_status() From 5ae850c4a34cc8add54041911969578bd5eccc7f Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 31 May 2024 18:08:37 +0100 Subject: [PATCH 10/27] Switch over to using subprocess.run() Refactor previous function as now too small to really be required (error handling now done by exceptions thrown by run(), and only a single API call now required) Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 75 +++++++++++----------------------- 1 file changed, 23 insertions(+), 52 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 1ba8f26f6..f733cb30b 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -38,11 +38,11 @@ # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later import argparse -from subprocess import Popen, PIPE +from subprocess import run, CalledProcessError import shlex from traceback import format_exc -from typing import Tuple, Iterable +from typing import Iterable import os import pathlib @@ -58,28 +58,10 @@ import requests -class BuildError(Exception): - """ Exception class for build errors """ - pass - - class ConfigError(Exception): """ Exception class for configuration errors """ pass - -def do_shell_exec(exec_string: str, expected_result: int = 0) -> Tuple[bool, str, str]: - """ Execute the given string in a shell, try to ascertain success. """ - - shell_process = Popen(shlex.split(exec_string), stdin=PIPE, stdout=PIPE, stderr=PIPE) - - (shell_stdout, shell_stderr) = shell_process.communicate() - - if shell_process.returncode != expected_result: - return False, shell_stdout.decode("utf-8"), shell_stderr.decode("utf-8") - - return True, shell_stdout.decode("utf-8"), shell_stderr.decode("utf-8") - def check_coverity_scan_tools_version(token: str, tools_dir: str) -> bool: """ Get the md5 of the coverity tools package from coverity. @@ -175,49 +157,33 @@ def build_mbedtls(logger: logging.Logger, mbedtls_dir: pathlib.Path, tools_dir: os.chdir(mbedtls_dir) # Ensure that given git directory is up to date. - success, std_out, std_err = do_shell_exec('git fetch --all') - - logger.log(logging.INFO, std_out) - - if not success: - raise BuildError(std_err) + result = run(['git', 'fetch', '--all'], capture_output=True, check=True) + logger.log(logging.INFO, result.stdout.decode("utf-8")) # Switch to correct branch. if branch is not None: - success, std_out, std_err = do_shell_exec('git checkout {}'.format(branch)) - - logger.log(logging.INFO, std_out) - - if not success: - raise BuildError(std_err) - - success, std_out, std_err = do_shell_exec('scripts/config.py full_no_platform') - - logger.log(logging.INFO, std_out) + result = run(['git', 'checkout', branch], capture_output=True, check=True) + logger.log(logging.INFO, result.stdout.decode("utf-8")) + # Ensure correct library build configuration. + result = run(['scripts/config.py', 'full_no_platform'], capture_output=True, check=True) + logger.log(logging.INFO, result.stdout.decode("utf-8")) - # do pre-build steps - success, std_out, std_err = do_shell_exec(pre_build_step) - logger.log(logging.INFO, std_out) + # Do pre-build steps. + result = run(shlex.split(pre_build_step), capture_output=True, check=True) + logger.log(logging.INFO, result.stdout.decode("utf-8")) - if not success: - raise BuildError(std_err) - - # build + # Build. coverity_tool = tools_dir / 'bin' / 'cov-build' - - success, std_out, std_err = do_shell_exec('{} --dir cov-int {}'.format(str(coverity_tool), - build_step)) - - logger.log(logging.INFO, std_out) - - if not success: - raise BuildError(std_err) + result = run([str(coverity_tool), '--dir', 'cov-int'] + shlex.split(build_step), + capture_output=True, + check=True) + logger.log(logging.INFO, result.stdout.decode("utf-8")) # TODO, ensure enough units were compiled.. - # tar up the results + # Tar up the results... cov_int_dir = mbedtls_dir / 'cov-int' / '' logger.log(logging.INFO, 'Writing {} to tar file : {}'.format(cov_int_dir, tar_file_name)) @@ -411,6 +377,11 @@ def main() -> int: except requests.exceptions.RequestException as e: logger.log(logging.ERROR, format_exc()) ret_code = 1 + except CalledProcessError as e: + logger.log(logging.ERROR, 'Command {} returned {}\n Output : {}'.format(e.cmd, + e.returncode, + e.stderr.decode("utf-8"))) + ret_code = 1 except: logger.log(logging.ERROR, 'Exception occurred: {}'.format(format_exc())) ret_code = 1 From 7e95fc99efb9a121effbf732b9ecc1ed13dd6cfc Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 31 May 2024 18:21:49 +0100 Subject: [PATCH 11/27] Run pre-build step in a shell. Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index f733cb30b..99afd25c4 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -171,7 +171,7 @@ def build_mbedtls(logger: logging.Logger, mbedtls_dir: pathlib.Path, tools_dir: # Do pre-build steps. - result = run(shlex.split(pre_build_step), capture_output=True, check=True) + result = run(pre_build_step, capture_output=True, check=True, shell=True) logger.log(logging.INFO, result.stdout.decode("utf-8")) # Build. From 2310f0c5559b7a37d7e1e1cfbdeb73520fda7218 Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 31 May 2024 18:39:17 +0100 Subject: [PATCH 12/27] Add building of generated files to prebuild step. Also make branch change recurse into submodules, as we now have one. Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 99afd25c4..c201fe135 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -162,7 +162,8 @@ def build_mbedtls(logger: logging.Logger, mbedtls_dir: pathlib.Path, tools_dir: # Switch to correct branch. if branch is not None: - result = run(['git', 'checkout', branch], capture_output=True, check=True) + result = run(['git', 'checkout', '--recurse-submodules', branch], capture_output=True, + check=True) logger.log(logging.INFO, result.stdout.decode("utf-8")) # Ensure correct library build configuration. @@ -251,7 +252,7 @@ def main() -> int: parser.add_argument('-e', '--email', help='Email address to send build notifications to', required=True) parser.add_argument('-p', '--pre-build-step', help='Command to run pre-build', - default='make clean') + default='make clean && tests/scripts/check-generated-files.sh -u') parser.add_argument('-s', '--build-step', help='Command to run to build the project', default = 'make -j{}'.format(cpu_count())) parser.add_argument('-t', '--token', help='Coverity Scan Token') From 707fe27f1ff8cb738784d580352f9ecb3ad544fd Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 31 May 2024 21:08:12 +0100 Subject: [PATCH 13/27] Improve temporary tar file creation logic Use 'with' context, copy to backup file if required. Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 42 +++++++++++++++------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index c201fe135..976312143 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -47,7 +47,7 @@ import os import pathlib import shutil -from tempfile import mkstemp, mkdtemp +from tempfile import mkdtemp, NamedTemporaryFile from datetime import datetime import tarfile import hashlib @@ -294,11 +294,9 @@ def main() -> int: logger.log(logging.INFO, "### Script starting.") tools_path_set = False - tar_file_set = False try: tools_path = pathlib.Path('') - tar_file = pathlib.Path('') mbedtls_path = pathlib.Path(args.mbedtlsdir) mbedtls_path = mbedtls_path.resolve() @@ -351,29 +349,30 @@ def main() -> int: backup_config_files(mbedtls_path, False) - if args.backupdir is not None: - backup_path = pathlib.Path(args.backupdir) + with NamedTemporaryFile() as tar_file_handle: - if not backup_path.is_dir(): - raise ConfigError('Backup dir specfied does not exist.') + tar_file = pathlib.Path(tar_file_handle.name) - backup_path = backup_path.resolve() - tar_file = backup_path / datetime.today().strftime('mbedtls-%y-%m-%d.tar.gz') - tar_file_set = True + if not mbedtls_path.is_dir(): + raise ConfigError('MBed TLS directory specified does not exist.') - else: - tar_file_handle, tar_file_name = mkstemp() - tar_file = pathlib.Path(tar_file_name) - tar_file_set = True + build_mbedtls(logger, mbedtls_path, tools_path, args.branch, + args.pre_build_step, args.build_step, tar_file) + + # send completed tar file to coverity + upload_build(logger, coverity_token, args.email, tar_file) - if not mbedtls_path.is_dir(): - raise ConfigError('MBed TLS directory specified does not exist.') + # If we want a backup of the tar file, then make one. + if args.backupdir is not None: + backup_path = pathlib.Path(args.backupdir) - build_mbedtls(logger, mbedtls_path, tools_path, args.branch, - args.pre_build_step, args.build_step, tar_file) + if not backup_path.is_dir(): + raise ConfigError('Backup dir specfied does not exist.') + + backup_path = backup_path.resolve() + backup_path = backup_path / datetime.today().strftime('mbedtls-%y-%m-%d.tar.gz') + shutil.copy(tar_file, backup_path) - # send completed tar file to coverity - upload_build(logger, coverity_token, args.email, tar_file) except requests.exceptions.RequestException as e: logger.log(logging.ERROR, format_exc()) @@ -389,9 +388,6 @@ def main() -> int: finally: # Clean up, if necessary - if args.backupdir is None and tar_file_set: - os.unlink(tar_file) - if args.covtools is None and tools_path_set: shutil.rmtree(tools_path) From 7f566268203e7c51d78f390f1e6c7c9657b20e4b Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 29 Nov 2024 14:07:28 +0000 Subject: [PATCH 14/27] Add git submodule update call The addition of the framework necessitates this. Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 976312143..2cb84d7a4 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -166,6 +166,9 @@ def build_mbedtls(logger: logging.Logger, mbedtls_dir: pathlib.Path, tools_dir: check=True) logger.log(logging.INFO, result.stdout.decode("utf-8")) + result = run(['git', 'submodule', 'update', '--init'], capture_output=True, check=True) + logger.log(logging.INFO, result.stdout.decode("utf-8")) + # Ensure correct library build configuration. result = run(['scripts/config.py', 'full_no_platform'], capture_output=True, check=True) logger.log(logging.INFO, result.stdout.decode("utf-8")) From 9cd43e56112c7931cfb606b0ecc9ff6a164f3d1a Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 29 Nov 2024 14:09:24 +0000 Subject: [PATCH 15/27] Fix copypasta with tools directory Missed as logic causes the script to just redownload the tools. Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 2cb84d7a4..4d6e59457 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -328,7 +328,7 @@ def main() -> int: else: # Coverity tools dir specified, see if it exists, contains tools and # those tools are up to date. - tools_path = pathlib.Path(args.toolsdir) + tools_path = pathlib.Path(args.covtools) tools_path = tools_path.resolve() tools_path_set = True From 2c8f84ea3bd69da9bb87097981d7f1abd1312a76 Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 29 Nov 2024 14:11:26 +0000 Subject: [PATCH 16/27] Fix resolvable error with backup dir Instead of erroring out if the specified backup directory is non-existent, just create it. Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 4d6e59457..d4f7a90f4 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -370,7 +370,8 @@ def main() -> int: backup_path = pathlib.Path(args.backupdir) if not backup_path.is_dir(): - raise ConfigError('Backup dir specfied does not exist.') + logger.log(logging.INFO, 'Backup dir does not exist, creating.') + backup_path.mkdir() backup_path = backup_path.resolve() backup_path = backup_path / datetime.today().strftime('mbedtls-%y-%m-%d.tar.gz') From 0adbd0a07e254e4be05dc2825931ab02dc226efb Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 29 Nov 2024 14:50:25 +0000 Subject: [PATCH 17/27] Support other operating systems for coverity tools Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index d4f7a90f4..f1f573853 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -30,6 +30,8 @@ * -l / --log: Specify a file to log information on the build to. * -m / --backupdir: If specified, this will be used as a directory to backup built tar files to. +* -o / --os: If specified, override the OS specification for the coverity tools + default is 64 bit linux * -v / --verbose: If specified, all logging will be done to stdout. """ @@ -62,7 +64,7 @@ class ConfigError(Exception): """ Exception class for configuration errors """ pass -def check_coverity_scan_tools_version(token: str, tools_dir: str) -> bool: +def check_coverity_scan_tools_version(token: str, tools_os: str, tools_dir: str) -> bool: """ Get the md5 of the coverity tools package from coverity. Enable us to check that we have the latest version, saving a potential large download. @@ -72,8 +74,7 @@ def check_coverity_scan_tools_version(token: str, tools_dir: str) -> bool: ('project', 'ARMmbed/mbedtls'), ('md5', '1')] - # Presumption of linux here, could support other build types? - md5_request = requests.get('https://scan.coverity.com/download/linux64', data=query_data, + md5_request = requests.get('https://scan.coverity.com/download/' + tools_os, data=query_data, timeout=60) md5_request.raise_for_status() @@ -116,7 +117,7 @@ def filter_root_tar_dir(tar_file: tarfile.TarFile) -> Iterable[tarfile.TarInfo]: tar_member.path = str(member_path.relative_to(*member_path.parts[:1])) yield tar_member -def download_coverity_scan_tools(logger: logging.Logger, token: str, tools_dir: str) -> None: +def download_coverity_scan_tools(logger: logging.Logger, token: str, tools_os: str, tools_dir: str) -> None: """ Download the coverity scan tools. @@ -128,8 +129,7 @@ def download_coverity_scan_tools(logger: logging.Logger, token: str, tools_dir: logger.log(logging.INFO, "Downloading Coverity Scan....") - # Presumption of linux here, could support other build types? - package_request = requests.get('https://scan.coverity.com/download/linux64', data=query_data, + package_request = requests.get('https://scan.coverity.com/download/' + tools_os, data=query_data, timeout=60) package_request.raise_for_status() @@ -261,9 +261,11 @@ def main() -> int: parser.add_argument('-t', '--token', help='Coverity Scan Token') parser.add_argument('-l', '--log', help='File to log to') parser.add_argument('-m', '--backupdir', help='Directory to backup tar files to') + parser.add_argument('-o', '--os', help='Specify OS for coverity tools', + choices=['linux64', 'linux-ARM64', 'freebsd64', 'win64'], + default='linux64') parser.add_argument('-v', '--verbose', action='store_true', help='Verbose logging to stdout') - parser.add_argument('mbedtlsdir', help='Mbed TLS directory') args = parser.parse_args() @@ -324,7 +326,7 @@ def main() -> int: tools_path = pathlib.Path(dir_path) tools_path_set = True - download_coverity_scan_tools(logger, coverity_token, tools_path) + download_coverity_scan_tools(logger, coverity_token, args.os, tools_path) else: # Coverity tools dir specified, see if it exists, contains tools and # those tools are up to date. @@ -337,18 +339,18 @@ def main() -> int: logger.log(logging.INFO, 'Tools dir does not exist, creating.') tools_path.mkdir() - download_coverity_scan_tools(logger, coverity_token, tools_path) + download_coverity_scan_tools(logger, coverity_token, args.os, tools_path) else: hash_path = tools_path / 'coverity_tool.md5' if not hash_path.is_file(): logger.log(logging.INFO, 'Hash file does not exist, re-downloading.') - download_coverity_scan_tools(logger, coverity_token, tools_path) + download_coverity_scan_tools(logger, coverity_token, args.os, tools_path) else: # Attempt to check if our coverity scan package is up to date. - if not check_coverity_scan_tools_version(coverity_token, tools_path): + if not check_coverity_scan_tools_version(coverity_token, args.os, tools_path): logger.log(logging.INFO, 'Hash file differs, re-downloading tools.') - download_coverity_scan_tools(logger, coverity_token, tools_path) + download_coverity_scan_tools(logger, coverity_token, args.os, tools_path) backup_config_files(mbedtls_path, False) From 55d172348c4f8a0eb6d462266c98c73918b7f5be Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 29 Nov 2024 14:51:58 +0000 Subject: [PATCH 18/27] Add logging of config file backups Useful for debug more than anything else. Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index f1f573853..194c8022c 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -86,7 +86,7 @@ def check_coverity_scan_tools_version(token: str, tools_os: str, tools_dir: str) return tools_hash == md5_request.text -def backup_config_files(mbedtls_dir: pathlib.Path, restore: bool) -> None: +def backup_config_files(logger: logging.Logger, mbedtls_dir: pathlib.Path, restore: bool) -> None: """Backup / Restore config file. """ config_path = pathlib.Path(mbedtls_dir) @@ -97,8 +97,12 @@ def backup_config_files(mbedtls_dir: pathlib.Path, restore: bool) -> None: if restore: if backup_path.is_file(): + logger.log(logging.INFO, "replacing {} with {}".format(config_path, backup_path)) backup_path.replace(config_path) + else: + logger.log(logging.INFO, "backup {} does not exist".format(backup_path)) else: + logger.log(logging.INFO, "backing up {} to {}".format(config_path, backup_path)) shutil.copy(config_path, config_path.with_suffix('.h.bak')) def filter_root_tar_dir(tar_file: tarfile.TarFile) -> Iterable[tarfile.TarInfo]: @@ -352,7 +356,7 @@ def main() -> int: logger.log(logging.INFO, 'Hash file differs, re-downloading tools.') download_coverity_scan_tools(logger, coverity_token, args.os, tools_path) - backup_config_files(mbedtls_path, False) + backup_config_files(logger, mbedtls_path, False) with NamedTemporaryFile() as tar_file_handle: @@ -397,7 +401,7 @@ def main() -> int: if args.covtools is None and tools_path_set: shutil.rmtree(tools_path) - backup_config_files(mbedtls_path, True) + backup_config_files(logger, mbedtls_path, True) logger.log(logging.INFO, "### Script done.") return ret_code From 4dc88dbfca78d0f0f3413dffe75d6ea8a640cdac Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 29 Nov 2024 14:53:24 +0000 Subject: [PATCH 19/27] Fix alphabetical ordering of argument options .. and update documentation to add new options. Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 194c8022c..6408f53f4 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -22,16 +22,18 @@ * -c / --covtools: If specified, the coverity tools will be downloaded here. If there is already a set of coverity tools in the specified directory, they will be checked for appropriate version, and overwritten only if necessary. -* -p / --pre-build-step: Specify the command to run pre-build - defaults to - 'make clean'. -* -s / --build-step: Specify the command to run to build the project - defaults - to 'make -j'. - +* -e / --email: Email address to send build notifications to +* -t / --token: The Coverity Scan token - needs to be got from the coverity web + UI * -l / --log: Specify a file to log information on the build to. * -m / --backupdir: If specified, this will be used as a directory to backup built tar files to. * -o / --os: If specified, override the OS specification for the coverity tools default is 64 bit linux +* -p / --pre-build-step: Specify the command to run pre - build. + defaults to 'make clean'. +* -s / --build-step: Specify the command to run to build the project. + defaults to 'make -j '. * -v / --verbose: If specified, all logging will be done to stdout. """ @@ -258,8 +260,6 @@ def main() -> int: help='Directory to store downloaded coverity tools in') parser.add_argument('-e', '--email', help='Email address to send build notifications to', required=True) - parser.add_argument('-p', '--pre-build-step', help='Command to run pre-build', - default='make clean && tests/scripts/check-generated-files.sh -u') parser.add_argument('-s', '--build-step', help='Command to run to build the project', default = 'make -j{}'.format(cpu_count())) parser.add_argument('-t', '--token', help='Coverity Scan Token') @@ -268,6 +268,8 @@ def main() -> int: parser.add_argument('-o', '--os', help='Specify OS for coverity tools', choices=['linux64', 'linux-ARM64', 'freebsd64', 'win64'], default='linux64') + parser.add_argument('-p', '--pre-build-step', help='Command to run pre-build', + default='make clean && tests/scripts/check-generated-files.sh -u') parser.add_argument('-v', '--verbose', action='store_true', help='Verbose logging to stdout') parser.add_argument('mbedtlsdir', help='Mbed TLS directory') From 77b9f2b311ea54d86b7b4d9770663a0b66b60c2d Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 29 Nov 2024 15:40:38 +0000 Subject: [PATCH 20/27] Fix issue whereby config.h was left modified If config.h was changed upstream, it would be backed up prior to these changes being applied locally, and then restored into the wrong state. Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 6408f53f4..0f2af6259 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -175,6 +175,12 @@ def build_mbedtls(logger: logging.Logger, mbedtls_dir: pathlib.Path, tools_dir: result = run(['git', 'submodule', 'update', '--init'], capture_output=True, check=True) logger.log(logging.INFO, result.stdout.decode("utf-8")) + + # Backup config files here prior to running config.py, as the branch checkout may also have + # changed them, and backing up before this point will end up with old versions of the file being + # restored. + backup_config_files(logger, mbedtls_path, False) + # Ensure correct library build configuration. result = run(['scripts/config.py', 'full_no_platform'], capture_output=True, check=True) logger.log(logging.INFO, result.stdout.decode("utf-8")) @@ -358,8 +364,6 @@ def main() -> int: logger.log(logging.INFO, 'Hash file differs, re-downloading tools.') download_coverity_scan_tools(logger, coverity_token, args.os, tools_path) - backup_config_files(logger, mbedtls_path, False) - with NamedTemporaryFile() as tar_file_handle: tar_file = pathlib.Path(tar_file_handle.name) From ca2d4b4a5d4749c3eb1fbfd73dcf287f6ad2cfcb Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 6 Dec 2024 15:00:51 +0000 Subject: [PATCH 21/27] Fix copypasta Moving the backup of config files fixed the previous issue, but created another one. Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 0f2af6259..f610a94b1 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -179,7 +179,7 @@ def build_mbedtls(logger: logging.Logger, mbedtls_dir: pathlib.Path, tools_dir: # Backup config files here prior to running config.py, as the branch checkout may also have # changed them, and backing up before this point will end up with old versions of the file being # restored. - backup_config_files(logger, mbedtls_path, False) + backup_config_files(logger, mbedtls_dir, False) # Ensure correct library build configuration. result = run(['scripts/config.py', 'full_no_platform'], capture_output=True, check=True) From fa1fbe209b2ff447fb4f18bf24dab017c679873e Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 6 Dec 2024 15:03:09 +0000 Subject: [PATCH 22/27] Log both stdout and stderr on CalledProcessError Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index f610a94b1..80be87acc 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -394,8 +394,10 @@ def main() -> int: logger.log(logging.ERROR, format_exc()) ret_code = 1 except CalledProcessError as e: - logger.log(logging.ERROR, 'Command {} returned {}\n Output : {}'.format(e.cmd, + logger.log(logging.ERROR, + 'Command {} returned {}\n StdOut : {}\n StdErr : {}'.format(e.cmd, e.returncode, + e.stdout.decode("utf-8"), e.stderr.decode("utf-8"))) ret_code = 1 except: From c15b488f43f5c46205550d6a9745989ba1392241 Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 6 Dec 2024 15:14:52 +0000 Subject: [PATCH 23/27] Add a --no-upload option Enable testing without consuming a coverity credit Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 80be87acc..469074aa7 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -28,6 +28,8 @@ * -l / --log: Specify a file to log information on the build to. * -m / --backupdir: If specified, this will be used as a directory to backup built tar files to. +* -n / --no-upload: If set, build the Coverity tar file, but do not request a + build or upload it. * -o / --os: If specified, override the OS specification for the coverity tools default is 64 bit linux * -p / --pre-build-step: Specify the command to run pre - build. @@ -271,6 +273,9 @@ def main() -> int: parser.add_argument('-t', '--token', help='Coverity Scan Token') parser.add_argument('-l', '--log', help='File to log to') parser.add_argument('-m', '--backupdir', help='Directory to backup tar files to') + parser.add_argument('-n', '--no-upload', + help='Build the Coverity tar file, but do not request a build or upload it', + action = 'store_true') parser.add_argument('-o', '--os', help='Specify OS for coverity tools', choices=['linux64', 'linux-ARM64', 'freebsd64', 'win64'], default='linux64') @@ -374,8 +379,12 @@ def main() -> int: build_mbedtls(logger, mbedtls_path, tools_path, args.branch, args.pre_build_step, args.build_step, tar_file) - # send completed tar file to coverity - upload_build(logger, coverity_token, args.email, tar_file) + # Set this if you want to test without consuming the Coverity credit. The tar file will + # still obviously be built, and if you use a backup directory to save it, can still be + # manually uploaded via the website afterwards. + if not args.no_upload: + # send completed tar file to coverity + upload_build(logger, coverity_token, args.email, tar_file) # If we want a backup of the tar file, then make one. if args.backupdir is not None: From 2da0cefa519f3caee2c6c84069c8067ecfbaa3a7 Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Thu, 19 Dec 2024 12:38:10 +0000 Subject: [PATCH 24/27] Add recursive flag to submodule update With the addition of tf-psa-crypto as a submodule, which itself has framework as a submodule, git submodule calls now need to be recursive Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 469074aa7..5ea82ee21 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -174,7 +174,8 @@ def build_mbedtls(logger: logging.Logger, mbedtls_dir: pathlib.Path, tools_dir: check=True) logger.log(logging.INFO, result.stdout.decode("utf-8")) - result = run(['git', 'submodule', 'update', '--init'], capture_output=True, check=True) + result = run(['git', 'submodule', 'update', '--init', '--recursive'], capture_output=True, + check=True) logger.log(logging.INFO, result.stdout.decode("utf-8")) From dfb02ad06638a3d5c40eab1fd2619f9c523179f3 Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Wed, 22 Jan 2025 15:04:56 +0000 Subject: [PATCH 25/27] Backup tf-psa-crypto config file as well as mbedtls Required some re-work to have two files. May not entirely be the optimal way of doing this, but probably only worth more work if more files are required. Signed-off-by: Paul Elliott --- coverity/push_coverity_scan.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 5ea82ee21..9dc3a0064 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -90,11 +90,9 @@ def check_coverity_scan_tools_version(token: str, tools_os: str, tools_dir: str) return tools_hash == md5_request.text -def backup_config_files(logger: logging.Logger, mbedtls_dir: pathlib.Path, restore: bool) -> None: - """Backup / Restore config file. """ +def backup_config_file(logger: logging.Logger, config_path: pathlib.Path, restore: bool) -> None: + """Backup / Restore a single config file. """ - config_path = pathlib.Path(mbedtls_dir) - config_path = config_path / 'include' / 'mbedtls' / 'mbedtls_config.h' config_path.resolve() backup_path = config_path.with_suffix('.h.bak') @@ -109,6 +107,20 @@ def backup_config_files(logger: logging.Logger, mbedtls_dir: pathlib.Path, resto logger.log(logging.INFO, "backing up {} to {}".format(config_path, backup_path)) shutil.copy(config_path, config_path.with_suffix('.h.bak')) + +def backup_config_files(logger: logging.Logger, mbedtls_dir: pathlib.Path, restore: bool) -> None: + """Backup / Restore all config files that we will change. """ + + config_path = pathlib.Path(mbedtls_dir) + config_path = config_path / 'include' / 'mbedtls' / 'mbedtls_config.h' + + backup_config_file(logger, config_path, restore) + + config_path = pathlib.Path(mbedtls_dir) + config_path = config_path / 'tf-psa-crypto' / 'include' / 'psa' / 'crypto_config.h' + + backup_config_file(logger, config_path, restore) + def filter_root_tar_dir(tar_file: tarfile.TarFile) -> Iterable[tarfile.TarInfo]: """ Filter the tar file root dir out. From d1f14be094479f4f04e40b6f2852c6f0fe0a3478 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Mon, 7 Apr 2025 09:38:41 +0200 Subject: [PATCH 26/27] Update pre-build step Update pre-build step due to the removal of check-generated-files.sh. Signed-off-by: Ronald Cron --- coverity/push_coverity_scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 9dc3a0064..3bfdc66fa 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -293,7 +293,7 @@ def main() -> int: choices=['linux64', 'linux-ARM64', 'freebsd64', 'win64'], default='linux64') parser.add_argument('-p', '--pre-build-step', help='Command to run pre-build', - default='make clean && tests/scripts/check-generated-files.sh -u') + default='make neat && make generated_files') parser.add_argument('-v', '--verbose', action='store_true', help='Verbose logging to stdout') parser.add_argument('mbedtlsdir', help='Mbed TLS directory') From 2d6ee186b8554f2bda9974546a3591446011abfa Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 30 Sep 2025 09:35:35 +0100 Subject: [PATCH 27/27] Fix script to work with deprecated Make For now, use the legacy/transition path of running make -f scripts/legacy.make In future, the script will need to be migrated to CMake. Signed-off-by: David Horstmann --- coverity/push_coverity_scan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coverity/push_coverity_scan.py b/coverity/push_coverity_scan.py index 3bfdc66fa..75adc1141 100755 --- a/coverity/push_coverity_scan.py +++ b/coverity/push_coverity_scan.py @@ -282,7 +282,7 @@ def main() -> int: parser.add_argument('-e', '--email', help='Email address to send build notifications to', required=True) parser.add_argument('-s', '--build-step', help='Command to run to build the project', - default = 'make -j{}'.format(cpu_count())) + default = 'make -f scripts/legacy.make -j{}'.format(cpu_count())) parser.add_argument('-t', '--token', help='Coverity Scan Token') parser.add_argument('-l', '--log', help='File to log to') parser.add_argument('-m', '--backupdir', help='Directory to backup tar files to') @@ -293,7 +293,7 @@ def main() -> int: choices=['linux64', 'linux-ARM64', 'freebsd64', 'win64'], default='linux64') parser.add_argument('-p', '--pre-build-step', help='Command to run pre-build', - default='make neat && make generated_files') + default='make -f scripts/legacy.make neat && make -f scripts/legacy.make generated_files') parser.add_argument('-v', '--verbose', action='store_true', help='Verbose logging to stdout') parser.add_argument('mbedtlsdir', help='Mbed TLS directory')