diff --git a/ci/jenkins/generate.py b/ci/jenkins/generate.py index 901d413364b3..3ccdedc6d924 100644 --- a/ci/jenkins/generate.py +++ b/ci/jenkins/generate.py @@ -18,11 +18,12 @@ import jinja2 import argparse import difflib -import re import datetime +import re import textwrap from pathlib import Path +from typing import List REPO_ROOT = Path(__file__).resolve().parent.parent.parent @@ -82,9 +83,51 @@ def lines_without_generated_tag(content): ] +def is_changed_images_only(lines: List[str]) -> bool: + """ + Return True if 'line' only edits an image tag or if 'line' is not a changed + line in a diff + """ + added_images = [] + removed_images = [] + diff_lines = [] + + for line in lines[2:]: + if not line.startswith("-") and not line.startswith("+"): + # not a diff line, ignore it + continue + + diff_lines.append(line) + + if len(diff_lines) == 0: + # no changes made + return True + + for line in diff_lines: + is_add = line.startswith("+") + line = line.strip().lstrip("+").lstrip("-") + match = re.search( + r"^(ci_[a-zA-Z0-9]+) = \'.*\'$", + line.strip().lstrip("+").lstrip("-"), + flags=re.MULTILINE, + ) + if match is None: + # matched a non-image line, quit early + return False + + if is_add: + added_images.append(match.groups()[0]) + else: + removed_images.append(match.groups()[0]) + + # make sure that the added image lines match the removed image lines + return len(added_images) > 0 and added_images == removed_images + + if __name__ == "__main__": help = "Regenerate Jenkinsfile from template" parser = argparse.ArgumentParser(description=help) + parser.add_argument("--force", action="store_true", help="always overwrite timestamp") parser.add_argument("--check", action="store_true", help="just verify the output didn't change") args = parser.parse_args() @@ -92,6 +135,10 @@ def lines_without_generated_tag(content): content = f.read() data["generated_time"] = datetime.datetime.now().isoformat() + timestamp_match = re.search(r"^// Generated at (.*)$", content, flags=re.MULTILINE) + if not timestamp_match: + raise RuntimeError("Could not find timestamp in Jenkinsfile") + original_timestamp = timestamp_match.groups()[0] environment = jinja2.Environment( loader=jinja2.FileSystemLoader(REPO_ROOT), @@ -103,11 +150,18 @@ def lines_without_generated_tag(content): template = environment.get_template(str(JENKINSFILE_TEMPLATE.relative_to(REPO_ROOT))) new_content = template.render(**data) - diff = "".join( - difflib.unified_diff( + diff = [ + line + for line in difflib.unified_diff( lines_without_generated_tag(content), lines_without_generated_tag(new_content) ) - ) + ] + if not args.force and is_changed_images_only(diff): + new_content = new_content.replace(data["generated_time"], original_timestamp) + print("Detected only Docker-image name changed, skipping timestamp update") + + diff = "".join(diff) + if args.check: if not diff: print("Success, the newly generated Jenkinsfile matched the one on disk")