forked from jenkinsci/jenkins
-
Notifications
You must be signed in to change notification settings - Fork 0
/
update-since-todo.py
executable file
·131 lines (111 loc) · 3.95 KB
/
update-since-todo.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/usr/bin/env python3
import argparse
import fileinput
import io
import shutil
import subprocess
GIT = shutil.which("git")
def update_file(file, lineno, old, new):
"""
Replace all occurrences of the old substring by the new substring.
:param file: The file to update.
:param lineno: The line number to update.
:param old: The old substring.
:param new: The new substring.
"""
print("\tUpdating file in place")
with fileinput.FileInput(file, inplace=True) as f:
for line in f:
if f.lineno() == lineno and old in line:
print(line.replace(old, new), end="")
else:
print(line, end="")
def analyze_file(file, lineno, commits_and_tags, dry_run=False):
"""
Analyze the given file.
:param file: The file to analyze.
:param lineno: The line number to analyze.
:param commits_and_tags: The output dictionary mapping commits to release tags.
:param dry_run: Whether or not this is a dry run.
"""
print(f"Analyzing {file}:{lineno}")
line_sha = (
subprocess.check_output(
[GIT, "blame", "--porcelain", "-L", f"{lineno},{lineno}", file], text=True
)
.split("\n", 1)[0]
.split(" ", 1)[0]
)
print(f"\tfirst sha: {line_sha}")
first_tag = subprocess.check_output(
[GIT, "tag", "--sort=creatordate", "--contains", line_sha, "jenkins-*"],
text=True,
).split("\n", 1)[0]
if first_tag:
print(f"\tfirst tag was {first_tag}")
commits_and_tags[line_sha] = first_tag
if not dry_run:
update_file(
file,
int(lineno),
"@since TODO",
"@since " + first_tag.replace("jenkins-", ""),
)
else:
print(
"\tNot updating file, no tag found. "
"Normal if the associated PR/commit is not merged and released yet; "
"otherwise make sure to fetch tags from jenkinsci/jenkins"
)
def analyze_files(commits_and_tags, dry_run=False):
"""
Analyze all files in the repository.
:param commits_and_tags: The output dictionary mapping commits to release tags.
:param dry_run: Whether or not this is a dry run.
"""
cmd = [
GIT,
"grep",
"--line-number",
"@since TODO",
"--",
"*.java",
"*.jelly",
"*.js",
]
with subprocess.Popen(cmd, stdout=subprocess.PIPE) as proc:
for line in io.TextIOWrapper(proc.stdout):
parts = line.rstrip().split(":", 2)
analyze_file(parts[0], parts[1], commits_and_tags, dry_run=dry_run)
retcode = proc.wait()
if retcode:
raise subprocess.CalledProcessError(retcode, cmd)
print()
def display_results(commits_and_tags):
"""
Display the results of the analysis.
:param commits_and_tags: The output dictionary mapping commits to release tags.
"""
print("List of commits introducing new API and the first release they went in:")
releases = {release for release in commits_and_tags.values()}
for release in sorted(releases):
print(f"* https://github.com/jenkinsci/jenkins/releases/tag/{release}")
for commit, first_release in commits_and_tags.items():
if release == first_release:
print(f" - https://github.com/jenkinsci/jenkins/commit/{commit}")
def main():
"""
Update '@since TODO' entries with actual Jenkins release versions.
This script is a developer tool, to be used by maintainers.
"""
parser = argparse.ArgumentParser(
description="Update '@since TODO' entries with actual Jenkins release versions."
)
parser.add_argument("-n", "--dry-run", help="Dry run", action="store_true")
args = parser.parse_args()
commits_and_tags = {}
analyze_files(commits_and_tags, dry_run=args.dry_run)
if commits_and_tags:
display_results(commits_and_tags)
if __name__ == "__main__":
main()