Skip to content

Commit

Permalink
[ci] Don't update Jenkinsfile timestamp on image updates (apache#12621)
Browse files Browse the repository at this point in the history
The timestamp in the Jenkinsfile is there to prevent post-merge
conflicts from different PRs that edit the templates merging
non-sequentially. This is not an issue when a line is edited in place
though, which is often the case when Docker image tags are updated. This
PR makes it so the timestamp is not updated in these cases which should
reduce merge conflicts on these types of PRs.
  • Loading branch information
driazati authored Aug 29, 2022
1 parent 0de2219 commit c31a762
Showing 1 changed file with 58 additions and 4 deletions.
62 changes: 58 additions & 4 deletions ci/jenkins/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -82,16 +83,62 @@ 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()

with open(JENKINSFILE) as f:
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),
Expand All @@ -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")
Expand Down

0 comments on commit c31a762

Please sign in to comment.