From cb4df1923ac5b0e80f7831db985d2668888435f0 Mon Sep 17 00:00:00 2001 From: Angelina Date: Mon, 14 Jul 2025 13:07:51 +0000 Subject: [PATCH] ci-license: Check for current year in the license job When a file with an Arm copyright header is modified, CI will fail if the header does not include the current year, and it will print the paths of the offending files. Signed-off-by: Angelina Dobardzieva --- release_changes/202507141540.change.md | 1 + tools/ci/pipeline-baseline-fri.yml | 3 +- tools/scripts/copyright_date_check.py | 65 ++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 release_changes/202507141540.change.md create mode 100755 tools/scripts/copyright_date_check.py diff --git a/release_changes/202507141540.change.md b/release_changes/202507141540.change.md new file mode 100644 index 00000000..7314fa1e --- /dev/null +++ b/release_changes/202507141540.change.md @@ -0,0 +1 @@ +ci-license: Check for current year in the license job diff --git a/tools/ci/pipeline-baseline-fri.yml b/tools/ci/pipeline-baseline-fri.yml index 9e27a3b6..846334d1 100644 --- a/tools/ci/pipeline-baseline-fri.yml +++ b/tools/ci/pipeline-baseline-fri.yml @@ -27,8 +27,9 @@ license: tags: - iotmsw-amd64 script: - - scancode -l --ignore "**/*.tmp/**" --json-pp scancode_report.json $PWD/.. + - scancode -lc --ignore "**/*.tmp/**" --json-pp scancode_report.json $PWD/.. - jsonschema -i scancode_report.json $PWD/tools/ci/license/license.schema + - python3 ${PWD}/tools/scripts/copyright_date_check.py artifacts: paths: - scancode_report.json diff --git a/tools/scripts/copyright_date_check.py b/tools/scripts/copyright_date_check.py new file mode 100755 index 00000000..ad73ac8a --- /dev/null +++ b/tools/scripts/copyright_date_check.py @@ -0,0 +1,65 @@ +# Copyright 2025 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +from datetime import datetime, timezone +import json +import pathlib +import os +import subprocess +import sys + +HOLDER = "Arm" +CURRENT_YEAR = str(datetime.now(timezone.utc).year) + + +# Ensures all modified files with Arm copyright have the current year; +# lists the paths of files that need updating +def main(): + outdated_year_files = [] + report = json.loads(pathlib.Path("scancode_report.json").read_text()) + repo_root = pathlib.Path(os.getcwd()).resolve() + path_prefix = f"{repo_root.parent.name}/{repo_root.name}/" + + # Builds a dictionary of all files and their corresponding copyright + all_copyrights = {} + for file in report.get("files", []): + scanned_path = file.get("path", "") + if not scanned_path.startswith(path_prefix): + continue + relative_path = scanned_path[slice(len(path_prefix), None)] + all_copyrights[relative_path] = [ + cp["value"] for cp in file.get("copyrights", []) + ] + + # Determine what files have been modified by looking at the commit range + base = os.getenv("CI_MERGE_REQUEST_DIFF_BASE_SHA") or "HEAD^" + head = os.getenv("CI_COMMIT_SHA") or "HEAD" + changed = set( + subprocess.check_output( + ["git", "diff", "--name-only", f"{base}...{head}"], text=True + ).splitlines() + ) + + for relative_path in changed: + file_copyrights = all_copyrights.get(relative_path, []) + # If a file doesn't have a copyright, or it is correctly formatted, skip it + if (not file_copyrights) or ( + any(HOLDER in cp and CURRENT_YEAR in cp for cp in file_copyrights) + ): + continue + if any(HOLDER in cp for cp in file_copyrights): + outdated_year_files.append(relative_path) + + if outdated_year_files: + print( + "Please update the copyright year to", + CURRENT_YEAR, + "in the following files:", + ) + print("\n".join(f" - {p}" for p in outdated_year_files)) + sys.exit(1) + + +if __name__ == "__main__": + main()