From 11b1448aa5186b83d1b4bac1a602b2db34154f21 Mon Sep 17 00:00:00 2001 From: Oliver Smith Date: Sun, 19 May 2019 22:20:39 +0200 Subject: [PATCH] CI: add check_changed_aports_versions.py (!382) Make sure that changed aports always have a higher version than what is currently in master. This check can be skipped with ci:skip-vercheck (in square brackets). Related: #187 --- .gitlab-ci.yml | 2 + .gitlab-ci/check_changed_aports_versions.py | 133 ++++++++++++++++++++ .gitlab-ci/common.py | 4 +- 3 files changed, 137 insertions(+), 2 deletions(-) create mode 100755 .gitlab-ci/check_changed_aports_versions.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 47983309a..4e712bca6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -49,9 +49,11 @@ aports-static: before_script: - .gitlab-ci/install_pmbootstrap.sh pytest script: + - apk -q add git - su pmos -c "pmbootstrap kconfig check" - su pmos -c ".gitlab-ci/run_testcases.sh -m 'not pmaports_upstream_compat'" + - su pmos -c ".gitlab-ci/check_changed_aports_versions.py" artifacts: when: on_failure paths: diff --git a/.gitlab-ci/check_changed_aports_versions.py b/.gitlab-ci/check_changed_aports_versions.py new file mode 100755 index 000000000..bacfda39b --- /dev/null +++ b/.gitlab-ci/check_changed_aports_versions.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +# Copyright 2019 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later + +import glob +import tempfile +import sys +import subprocess + +# Same dir +import common + +# pmbootstrap +import testcases.add_pmbootstrap_to_import_path # noqa +import pmb.parse +import pmb.parse.version +import pmb.helpers.logging + + +def get_package_version(args, package, revision, check=True): + # Redirect stderr to /dev/null, so git doesn't complain about files not + # existing in master for new packages + stderr = None + if not check: + stderr = subprocess.DEVNULL + + # Run something like "git show upstream/master:main/hello-world/APKBUILD" + pmaports_dir = common.get_pmaports_dir() + pattern = pmaports_dir + "/*/" + package + "/APKBUILD" + path = glob.glob(pattern)[0][len(pmaports_dir + "/"):] + apkbuild_content = common.run_git(["show", revision + ":" + path], check, + stderr) + if not apkbuild_content: + return None + + # Save APKBUILD to a temporary path and parse it from there. (Not the best + # way to do things, but good enough for this CI script.) + with tempfile.TemporaryDirectory() as tempdir: + with open(tempdir + "/APKBUILD", "w", encoding="utf-8") as handle: + handle.write(apkbuild_content) + parsed = pmb.parse.apkbuild(args, tempdir + "/APKBUILD", False, False) + + return parsed["pkgver"] + "-r" + parsed["pkgrel"] + + +def version_compare_operator(result): + """ :param result: return value from pmb.parse.version.compare() """ + if result == -1: + return "<" + elif result == 0: + return "==" + elif result == 1: + return ">" + + raise RuntimeError("Unexpected version_compare_operator input: " + result) + + +def exit_with_error_message(): + print() + print("ERROR: Modified package(s) don't have an increased version!") + print() + print("This can either happen if you did not change the pkgver/pkgrel") + print("variables in the APKBUILDs. Or you did change them, but the") + print("packages have been updated in the official master branch, and now") + print("your versions are not higher anymore.") + print() + print("Your options:") + print("a) If you made changes to the packages, and did not increase the") + print(" pkgrel/pkgver: increase them now, and force push your branch.") + print(" => https://postmarketos.org/howto-bump-pkgrel-pkgver") + print("b) If you had already increased the package versions, rebase on") + print(" master, increase the versions again and then force push:") + print(" => https://postmarketos.org/rebase") + print("c) If you made a change, that does not require rebuilding the") + print(" packages, such as only changing the arch=... line: you can") + print(" disable this check by adding '[ci:skip-vercheck]' to the") + print(" latest commit message, then force push.") + print() + print("Thank you and sorry for the inconvenience.") + exit(1) + + +def check_versions(args, packages): + error = False + for package in packages: + # Get versions, skip new packages + head = get_package_version(args, package, "HEAD") + master = get_package_version(args, package, "upstream/master", False) + if not master: + print("- {}: {} (HEAD) (new package)".format(package, head)) + continue + + # Compare head and master versions + result = pmb.parse.version.compare(head, master) + if result != 1: + error = True + + # Print result line ("- hello-world: 1-r2 (HEAD) > 1-r1 (master)") + formatstr = "- {}: {} (HEAD) {} {} (master)" + if result != 1: + formatstr += " [ERROR]" + operator = version_compare_operator(result) + print(formatstr.format(package, head, operator, master)) + + if error: + exit_with_error_message() + + +if __name__ == "__main__": + # Get and print modified packages + common.add_upstream_git_remote() + packages = common.get_changed_packages() + + # Verify modified package count + common.get_changed_packages_sanity_check(len(packages)) + if len(packages) == 0: + print("no aports changed in this branch") + exit(0) + + # Potentially skip this check + if common.commit_message_has_string("[ci:skip-vercheck]"): + print("WARNING: not checking for changed package versions" + " ([ci:skip-vercheck])!") + exit(0) + + # Initialize args (so we can use pmbootstrap's APKBUILD parsing) + sys.argv = ["pmbootstrap.py", "chroot"] + args = pmb.parse.arguments() + pmb.helpers.logging.init(args) + + # Verify package versions + print("checking changed package versions...") + check_versions(args, packages) diff --git a/.gitlab-ci/common.py b/.gitlab-ci/common.py index 188a8d86f..d3600a60d 100755 --- a/.gitlab-ci/common.py +++ b/.gitlab-ci/common.py @@ -13,11 +13,11 @@ def get_pmaports_dir(): return os.path.realpath(os.path.join(os.path.dirname(__file__) + "/..")) -def run_git(parameters, check=True): +def run_git(parameters, check=True, stderr=None): """ Run git in the pmaports dir and return the output """ cmd = ["git", "-C", get_pmaports_dir()] + parameters try: - return subprocess.check_output(cmd).decode() + return subprocess.check_output(cmd, stderr=stderr).decode() except subprocess.CalledProcessError: if check: raise