Show More
@@ -1,179 +1,181 b'' | |||||
1 | #!/usr/bin/env python3 |
|
1 | #!/usr/bin/env python3 | |
2 | """Generate release notes from our commit log. |
|
2 | """Generate release notes from our commit log. | |
3 |
|
3 | |||
4 | This uses the relnotes extension directives when they're available, |
|
4 | This uses the relnotes extension directives when they're available, | |
5 | and falls back to our old pre-relnotes logic that used to live in the |
|
5 | and falls back to our old pre-relnotes logic that used to live in the | |
6 | release-tools repo. |
|
6 | release-tools repo. | |
7 | """ |
|
7 | """ | |
8 | import argparse |
|
8 | import argparse | |
9 | import re |
|
9 | import re | |
10 | import subprocess |
|
10 | import subprocess | |
11 |
|
11 | |||
12 | # Regenerate this list with |
|
12 | # Regenerate this list with | |
13 | # hg export 'grep("\.\. [a-z]+::")' | grep '^\.\.' | \ |
|
13 | # hg export 'grep("\.\. [a-z]+::")' | grep '^\.\.' | \ | |
14 | # sed 's/.. //;s/::.*//' | sort -u |
|
14 | # sed 's/.. //;s/::.*//' | sort -u | |
15 | rnsections = ["api", "bc", "container", "feature", "fix", "note", "perf"] |
|
15 | rnsections = ["api", "bc", "container", "feature", "fix", "note", "perf"] | |
16 |
|
16 | |||
17 | rules = { |
|
17 | rules = { | |
18 | # keep |
|
18 | # keep | |
19 | r"\(issue": 100, |
|
19 | r"\(issue": 100, | |
20 | r"\(BC\)": 100, |
|
20 | r"\(BC\)": 100, | |
21 | r"\(API\)": 100, |
|
21 | r"\(API\)": 100, | |
22 | # core commands, bump up |
|
22 | # core commands, bump up | |
23 | r"(commit|files|log|pull|push|patch|status|tag|summary)(|s|es):": 20, |
|
23 | r"(commit|files|log|pull|push|patch|status|tag|summary)(|s|es):": 20, | |
24 | r"(annotate|alias|branch|bookmark|clone|graft|import|verify).*:": 20, |
|
24 | r"(annotate|alias|branch|bookmark|clone|graft|import|verify).*:": 20, | |
25 | # extensions, bump up |
|
25 | # extensions, bump up | |
26 | r"(mq|shelve|rebase):": 20, |
|
26 | r"(mq|shelve|rebase):": 20, | |
27 | # newsy |
|
27 | # newsy | |
28 | r": deprecate": 20, |
|
28 | r": deprecate": 20, | |
29 | r"(option|feature|command|support)": 10, |
|
29 | r"(option|feature|command|support)": 10, | |
30 | # bug-like? |
|
30 | # bug-like? | |
31 | r"(fix|don't break|improve)": 7, |
|
31 | r"(fix|don't break|improve)": 7, | |
32 | # boring stuff, bump down |
|
32 | # boring stuff, bump down | |
33 | r"^contrib": -5, |
|
33 | r"^contrib": -5, | |
34 | r"debug": -5, |
|
34 | r"debug": -5, | |
35 | r"help": -5, |
|
35 | r"help": -5, | |
36 | r"(doc|bundle2|obsolete|obsmarker|rpm|setup|debug\S+:)": -15, |
|
36 | r"(doc|bundle2|obsolete|obsmarker|rpm|setup|debug\S+:)": -15, | |
37 | r"(check-code|check-commit|import-checker)": -20, |
|
37 | r"(check-code|check-commit|import-checker)": -20, | |
38 | # cleanups and refactoring |
|
38 | # cleanups and refactoring | |
39 | r"(cleanup|whitespace|nesting|indent|spelling|comment)": -20, |
|
39 | r"(cleanup|whitespace|nesting|indent|spelling|comment)": -20, | |
40 | r"(typo|hint|note|style:|correct doc)": -20, |
|
40 | r"(typo|hint|note|style:|correct doc)": -20, | |
41 | r"_": -10, |
|
41 | r"_": -10, | |
42 | r"(argument|absolute_import|attribute|assignment|mutable)": -15, |
|
42 | r"(argument|absolute_import|attribute|assignment|mutable)": -15, | |
43 | r"(unused|useless|unnecessary|duplicate|deprecated|scope|True|False)": -10, |
|
43 | r"(unused|useless|unnecessary|duplicate|deprecated|scope|True|False)": -10, | |
44 | r"(redundant|pointless|confusing|uninitialized|meaningless|dead)": -10, |
|
44 | r"(redundant|pointless|confusing|uninitialized|meaningless|dead)": -10, | |
45 | r": (drop|remove|inherit|rename|simplify|naming|inline)": -10, |
|
45 | r": (drop|remove|inherit|rename|simplify|naming|inline)": -10, | |
46 | r"(docstring|document .* method)": -20, |
|
46 | r"(docstring|document .* method)": -20, | |
47 | r"(factor|extract|prepare|split|replace| import)": -20, |
|
47 | r"(factor|extract|prepare|split|replace| import)": -20, | |
48 | r": add.*(function|method|implementation|test|example)": -10, |
|
48 | r": add.*(function|method|implementation|test|example)": -10, | |
49 | r": (move|extract) .* (to|into|from)": -20, |
|
49 | r": (move|extract) .* (to|into|from)": -20, | |
50 | r": implement ": -5, |
|
50 | r": implement ": -5, | |
51 | r": use .* implementation": -20, |
|
51 | r": use .* implementation": -20, | |
52 | r"\S\S\S+\.\S\S\S\S+": -5, |
|
52 | r"\S\S\S+\.\S\S\S\S+": -5, | |
53 | r": use .* instead of": -20, |
|
53 | r": use .* instead of": -20, | |
54 | r"__": -5, |
|
54 | r"__": -5, | |
55 | # dumb keywords |
|
55 | # dumb keywords | |
56 | r"\S+/\S+:": -10, |
|
56 | r"\S+/\S+:": -10, | |
57 | r"\S+\.\S+:": -10, |
|
57 | r"\S+\.\S+:": -10, | |
58 | # drop |
|
58 | # drop | |
59 | r"^i18n-": -50, |
|
59 | r"^i18n-": -50, | |
60 | r"^i18n:.*(hint|comment)": -50, |
|
60 | r"^i18n:.*(hint|comment)": -50, | |
61 | r"perf:": -50, |
|
61 | r"perf:": -50, | |
62 | r"check-code:": -50, |
|
62 | r"check-code:": -50, | |
63 | r"Added.*for changeset": -50, |
|
63 | r"Added.*for changeset": -50, | |
64 | r"tests?:": -50, |
|
64 | r"tests?:": -50, | |
65 | r"test-": -50, |
|
65 | r"test-": -50, | |
66 | r"add.* tests": -50, |
|
66 | r"add.* tests": -50, | |
67 | r"^_": -50, |
|
67 | r"^_": -50, | |
68 | } |
|
68 | } | |
69 |
|
69 | |||
70 | cutoff = 10 |
|
70 | cutoff = 10 | |
71 | commits = [] |
|
71 | commits = [] | |
72 |
|
72 | |||
73 | groupings = [ |
|
73 | groupings = [ | |
74 | (r"util|parsers|repo|ctx|context|revlog|filelog|alias|cmdutil", "core"), |
|
74 | (r"util|parsers|repo|ctx|context|revlog|filelog|alias|cmdutil", "core"), | |
75 | (r"revset|templater|ui|dirstate|hook|i18n|transaction|wire", "core"), |
|
75 | (r"revset|templater|ui|dirstate|hook|i18n|transaction|wire", "core"), | |
76 | (r"color|pager", "core"), |
|
76 | (r"color|pager", "core"), | |
77 | (r"hgweb|paper|coal|gitweb", "hgweb"), |
|
77 | (r"hgweb|paper|coal|gitweb", "hgweb"), | |
78 | (r"pull|push|revert|resolve|annotate|bookmark|branch|clone", "commands"), |
|
78 | (r"pull|push|revert|resolve|annotate|bookmark|branch|clone", "commands"), | |
79 | (r"commands|commit|config|files|graft|import|log|merge|patch", "commands"), |
|
79 | (r"commands|commit|config|files|graft|import|log|merge|patch", "commands"), | |
80 | (r"phases|status|summary|amend|tag|help|verify", "commands"), |
|
80 | (r"phases|status|summary|amend|tag|help|verify", "commands"), | |
81 | (r"rebase|mq|convert|eol|histedit|largefiles", "extensions"), |
|
81 | (r"rebase|mq|convert|eol|histedit|largefiles", "extensions"), | |
82 | (r"shelve|unshelve", "extensions"), |
|
82 | (r"shelve|unshelve", "extensions"), | |
83 | ] |
|
83 | ] | |
84 |
|
84 | |||
85 | def main(): |
|
85 | def main(): | |
86 | ap = argparse.ArgumentParser() |
|
86 | ap = argparse.ArgumentParser() | |
87 | ap.add_argument( |
|
87 | ap.add_argument( | |
88 | "startrev", |
|
88 | "startrev", | |
89 | metavar="REV", |
|
89 | metavar="REV", | |
90 | type=str, |
|
90 | type=str, | |
91 | nargs=1, |
|
91 | nargs=1, | |
92 | help=( |
|
92 | help=( | |
93 | "Starting revision for the release notes. This revision " |
|
93 | "Starting revision for the release notes. This revision " | |
94 | "won't be included, but later revisions will." |
|
94 | "won't be included, but later revisions will." | |
95 | ), |
|
95 | ), | |
96 | ) |
|
96 | ) | |
97 | ap.add_argument( |
|
97 | ap.add_argument( | |
98 | "--stoprev", |
|
98 | "--stoprev", | |
99 | metavar="REV", |
|
99 | metavar="REV", | |
100 | type=str, |
|
100 | type=str, | |
101 | default="@", |
|
101 | default="@", | |
102 | nargs=1, |
|
102 | nargs=1, | |
103 | help=( |
|
103 | help=( | |
104 | "Stop revision for release notes. This revision will be included," |
|
104 | "Stop revision for release notes. This revision will be included," | |
105 | " but no later revisions will. This revision needs to be " |
|
105 | " but no later revisions will. This revision needs to be " | |
106 | "a descendant of startrev." |
|
106 | "a descendant of startrev." | |
107 | ), |
|
107 | ), | |
108 | ) |
|
108 | ) | |
109 | args = ap.parse_args() |
|
109 | args = ap.parse_args() | |
110 | fromext = subprocess.check_output( |
|
110 | fromext = subprocess.check_output( | |
111 | [ |
|
111 | [ | |
112 | "hg", |
|
112 | "hg", | |
|
113 | "--config", | |||
|
114 | "extensions.releasenotes=", | |||
113 | "releasenotes", |
|
115 | "releasenotes", | |
114 | "-r", |
|
116 | "-r", | |
115 | "%s::%s" % (args.startrev[0], args.stoprev[0]), |
|
117 | "%s::%s" % (args.startrev[0], args.stoprev[0]), | |
116 | ] |
|
118 | ] | |
117 | ).decode("utf-8") |
|
119 | ).decode("utf-8") | |
118 | # Find all release notes from un-relnotes-flagged commits. |
|
120 | # Find all release notes from un-relnotes-flagged commits. | |
119 | for entry in sorted( |
|
121 | for entry in sorted( | |
120 | subprocess.check_output( |
|
122 | subprocess.check_output( | |
121 | [ |
|
123 | [ | |
122 | "hg", |
|
124 | "hg", | |
123 | "log", |
|
125 | "log", | |
124 | "-r", |
|
126 | "-r", | |
125 | r'%s::%s - merge() - grep("\n\.\. (%s)::")' |
|
127 | r'%s::%s - merge() - grep("\n\.\. (%s)::")' | |
126 | % (args.startrev[0], args.stoprev[0], "|".join(rnsections)), |
|
128 | % (args.startrev[0], args.stoprev[0], "|".join(rnsections)), | |
127 | "-T", |
|
129 | "-T", | |
128 | r"{desc|firstline}\n", |
|
130 | r"{desc|firstline}\n", | |
129 | ] |
|
131 | ] | |
130 | ) |
|
132 | ) | |
131 | .decode("utf-8") |
|
133 | .decode("utf-8") | |
132 | .splitlines() |
|
134 | .splitlines() | |
133 | ): |
|
135 | ): | |
134 | desc = entry.replace("`", "'") |
|
136 | desc = entry.replace("`", "'") | |
135 |
|
137 | |||
136 | score = 0 |
|
138 | score = 0 | |
137 | for rule, val in rules.items(): |
|
139 | for rule, val in rules.items(): | |
138 | if re.search(rule, desc): |
|
140 | if re.search(rule, desc): | |
139 | score += val |
|
141 | score += val | |
140 |
|
142 | |||
141 | desc = desc.replace("(issue", "(Bts:issue") |
|
143 | desc = desc.replace("(issue", "(Bts:issue") | |
142 |
|
144 | |||
143 | if score >= cutoff: |
|
145 | if score >= cutoff: | |
144 | commits.append(desc) |
|
146 | commits.append(desc) | |
145 | # Group unflagged notes. |
|
147 | # Group unflagged notes. | |
146 | groups = {} |
|
148 | groups = {} | |
147 | bcs = [] |
|
149 | bcs = [] | |
148 | apis = [] |
|
150 | apis = [] | |
149 |
|
151 | |||
150 | for d in commits: |
|
152 | for d in commits: | |
151 | if "(BC)" in d: |
|
153 | if "(BC)" in d: | |
152 | bcs.append(d) |
|
154 | bcs.append(d) | |
153 | if "(API)" in d: |
|
155 | if "(API)" in d: | |
154 | apis.append(d) |
|
156 | apis.append(d) | |
155 | for rule, g in groupings: |
|
157 | for rule, g in groupings: | |
156 | if re.match(rule, d): |
|
158 | if re.match(rule, d): | |
157 | groups.setdefault(g, []).append(d) |
|
159 | groups.setdefault(g, []).append(d) | |
158 | break |
|
160 | break | |
159 | else: |
|
161 | else: | |
160 | groups.setdefault("unsorted", []).append(d) |
|
162 | groups.setdefault("unsorted", []).append(d) | |
161 | print(fromext) |
|
163 | print(fromext) | |
162 | # print legacy release notes sections |
|
164 | # print legacy release notes sections | |
163 | for g in sorted(groups): |
|
165 | for g in sorted(groups): | |
164 | print("\n=== %s ===" % g) |
|
166 | print("\n=== %s ===" % g) | |
165 | for d in sorted(groups[g]): |
|
167 | for d in sorted(groups[g]): | |
166 | print(" * %s" % d) |
|
168 | print(" * %s" % d) | |
167 |
|
169 | |||
168 | print("\n=== BC ===\n") |
|
170 | print("\n=== BC ===\n") | |
169 |
|
171 | |||
170 | for d in sorted(bcs): |
|
172 | for d in sorted(bcs): | |
171 | print(" * %s" % d) |
|
173 | print(" * %s" % d) | |
172 |
|
174 | |||
173 | print("\n=== API Changes ===\n") |
|
175 | print("\n=== API Changes ===\n") | |
174 |
|
176 | |||
175 | for d in sorted(apis): |
|
177 | for d in sorted(apis): | |
176 | print(" * %s" % d) |
|
178 | print(" * %s" % d) | |
177 |
|
179 | |||
178 | if __name__ == "__main__": |
|
180 | if __name__ == "__main__": | |
179 | main() |
|
181 | main() |
General Comments 0
You need to be logged in to leave comments.
Login now