From ffd72408acc503f13f98cd14446e10d7c2b2197a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0tetiar?= Date: Tue, 3 Sep 2024 07:39:52 +0000 Subject: [PATCH] scripts: gen_config.py: allow only use of Git hashes for feed revisions in profiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the current setup, YAML profiles for feed revision references can only use weak Git tag-based references, like so in profiles/prpl.yml: ```yaml - name: feed_amx uri: https://gitlab.com/prpl-foundation/prplos/feeds/feed_amx.git revision: v11.9.0 - name: feed_gmap uri: https://gitlab.com/prpl-foundation/prplos/feeds/feed_gmap.git revision: v1.7.2 ``` Git tags can be mutable, meaning they can be changed or moved. This introduces a risk where the reproducibility of builds cannot be guaranteed. Therefore, relying solely on Git tags for referencing revisions is not ideal. To enhance the reproducibility and reliability of the builds, the feed Git revision should support either of the following: 1. Full Git SHA-1 hash of the Git object (a 40-character string). 2. Human-readable reference like a Git tag, followed by an @ separator and then full (40-character) Git SHA Examples of the currently only valid feed revisions: ```yaml - name: feed_gmap uri: revision: 178c8c0dea56bfe6e5f3d0af84be4b0156041291 - name: feed_gmap uri: revision: v1.7.2@178c8c0dea56bfe6e5f3d0af84be4b0156041291 ``` Closes: PCF-1388 Signed-off-by: Petr Štetiar --- scripts/gen_config.py | 65 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/scripts/gen_config.py b/scripts/gen_config.py index 358ba9935a..25338bcd55 100755 --- a/scripts/gen_config.py +++ b/scripts/gen_config.py @@ -4,6 +4,7 @@ import yaml from pathlib import Path from shutil import rmtree import io +import re import sys from subprocess import run from os import getenv @@ -18,12 +19,10 @@ def run_cmd(cmd: list): return run(cmd) -def die(msg: str): - """Quit script with error message - - msg (str): Error message to print - """ - print(msg) +def die(*messages: str) -> None: + """Prints each message on a new line and exits the program.""" + for message in messages: + print(message) quit(1) @@ -76,6 +75,49 @@ def load_yaml(fname: str, profile: dict): return profile +def extract_sha1_from_revision(revision: str) -> str: + """ + Validates the given revision string and extracts the SHA-1 hash. + + A valid revision can be: + + 1. A full 40-character Git SHA-1 hash. + 2. A human readable reference like Git tag followed by '@' and a full 40-character Git SHA-1 hash. + + :param revision: The revision string to validate and extract the SHA-1 hash from. + :return: The extracted SHA-1 hash if valid, otherwise None. + """ + + full_sha1_pattern = r"^[a-f0-9]{40}$" + tag_full_sha1_pattern = r"^[^@]+@([a-f0-9]{40})$" + + if re.match(full_sha1_pattern, revision): + return revision + elif re.match(tag_full_sha1_pattern, revision): + return re.match(tag_full_sha1_pattern, revision).group(1) + else: + return None + + +def handle_feed_revision(profile_feed: dict, feeds: list): + revision = profile_feed.get("revision") + if not revision: + die(f"Please specify `revision` for the following feed: {profile_feed}") + + sha1 = extract_sha1_from_revision(revision) + if not sha1: + die( + f"Invalid feed revision {revision} in {profile_feed} feed, valid `revision` is:", + " 1. A full 40-character Git SHA-1 hash.", + " 2. A human readable reference like Git tag followed by '@' and a full 40-character Git SHA-1 hash.", + ) + + f = profile_feed + feeds.append( + f'{f.get("method", "src-git")},{f["name"]},{f["uri"]}^{sha1}' + ) + + if "list" in sys.argv: print(f"Profiles in {profile_folder}") @@ -133,14 +175,11 @@ with open("feeds.conf.default", "r") as default_feeds: for p in profile.get("feeds", []): try: - f = profile["feeds"].get(p) - if not "revision" in f: - die(f"Please specify revision for the following feed: {f}") - feeds.append( - f'{f.get("method", "src-git")},{f["name"]},{f["uri"]}^{f.get("revision")}' - ) + profile_feeds = profile["feeds"].get(p) + handle_feed_revision(profile_feeds, feeds) except: - print(f"Badly configured feed: {f}") + print(f"Badly configured feed: {profile_feeds}") + quit(1) if run_cmd(["./scripts/feeds", "setup", *feeds]).returncode: die(f"Error setting up feeds")