Mercurial > piecrust2
comparison util/generate_changelog.py @ 544:9a00e694b42c
cm: Changelog generator script.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Fri, 31 Jul 2015 23:35:07 -0700 |
parents | |
children | 442cf576ae25 |
comparison
equal
deleted
inserted
replaced
543:bedfa0156bf7 | 544:9a00e694b42c |
---|---|
1 import os | |
2 import os.path | |
3 import re | |
4 import sys | |
5 import subprocess | |
6 | |
7 | |
8 hg_log_template = ("{if(tags, '>>{tags};{date|shortdate}\n')}" | |
9 "{desc|firstline}\n\n") | |
10 | |
11 re_add_tag_changeset = re.compile('^Added tag [^\s]+ for changeset [\w\d]+$') | |
12 re_merge_pr_changeset = re.compile('^Merge pull request') | |
13 re_tag = re.compile('^\d+\.\d+\.\d+([ab]\d+)?(rc\d+)?$') | |
14 re_change = re.compile('^(\w+):') | |
15 re_clean_code_span = re.compile('([^\s])``([^\s]+)') | |
16 | |
17 category_commands = [ | |
18 'chef', 'bake', 'find', 'help', 'import', 'init', 'paths', 'plugin', | |
19 'plugins', 'prepare', 'purge', 'root', 'routes', 'serve', | |
20 'showconfig', 'showrecord', 'sources', 'theme', 'themes'] | |
21 category_core = [ | |
22 'internal', 'bug', 'templating', 'formatting', 'performance', | |
23 'data', 'config', 'rendering', 'render', 'debug', 'reporting', | |
24 'linker', 'pagination', 'routing', 'caching'] | |
25 category_project = ['build', 'cm', 'docs', 'tests', 'setup'] | |
26 categories = [ | |
27 ('commands', category_commands), | |
28 ('core', category_core), | |
29 ('project', category_project), | |
30 ('miscellaneous', None)] | |
31 category_names = list(map(lambda i: i[0], categories)) | |
32 | |
33 | |
34 def generate(): | |
35 out_file = 'CHANGELOG.rst' | |
36 if len(sys.argv) > 1: | |
37 out_file = sys.argv[1] | |
38 | |
39 print("Generating %s" % out_file) | |
40 | |
41 if not os.path.exists('.hg'): | |
42 raise Exception("You must run this script from the root of a " | |
43 "Mercurial clone of the PieCrust repository.") | |
44 hglog = subprocess.check_output([ | |
45 'hg', 'log', | |
46 '--rev', 'reverse(::master)', | |
47 '--template', hg_log_template]) | |
48 hglog = hglog.decode('utf8') | |
49 | |
50 templates = _get_templates() | |
51 | |
52 with open(out_file, 'w') as fp: | |
53 fp.write(templates['header']) | |
54 | |
55 skip = False | |
56 in_desc = False | |
57 current_version = 0 | |
58 current_version_info = None | |
59 current_changes = None | |
60 for line in hglog.splitlines(): | |
61 if line == '': | |
62 skip = False | |
63 in_desc = False | |
64 continue | |
65 | |
66 if not in_desc and line.startswith('>>'): | |
67 tags, tag_date = line[2:].split(';') | |
68 if re_tag.match(tags): | |
69 if current_version > 0: | |
70 _write_version_changes( | |
71 templates, | |
72 current_version, current_version_info, | |
73 current_changes, fp) | |
74 | |
75 current_version += 1 | |
76 current_version_info = tags, tag_date | |
77 current_changes = {} | |
78 in_desc = True | |
79 else: | |
80 skip = True | |
81 continue | |
82 | |
83 if skip or current_version == 0: | |
84 continue | |
85 | |
86 if re_add_tag_changeset.match(line): | |
87 continue | |
88 if re_merge_pr_changeset.match(line): | |
89 continue | |
90 | |
91 m = re_change.match(line) | |
92 if m: | |
93 ch_type = m.group(1) | |
94 for cat_name, ch_types in categories: | |
95 if ch_types is None or ch_type in ch_types: | |
96 msgs = current_changes.setdefault(cat_name, []) | |
97 msgs.append(line) | |
98 break | |
99 else: | |
100 assert False, ("Change '%s' should have gone in the " | |
101 "misc. bucket." % line) | |
102 else: | |
103 msgs = current_changes.setdefault('miscellaneous', []) | |
104 msgs.append(line) | |
105 | |
106 if current_version > 0: | |
107 _write_version_changes( | |
108 templates, | |
109 current_version, current_version_info, | |
110 current_changes, fp) | |
111 | |
112 | |
113 def _write_version_changes(templates, version, version_info, changes, fp): | |
114 tokens = { | |
115 'num': str(version), | |
116 'version': version_info[0], | |
117 'date': version_info[1]} | |
118 tpl = _multi_replace(templates['version_title'], tokens) | |
119 fp.write(tpl) | |
120 | |
121 for i, cat_name in enumerate(category_names): | |
122 msgs = changes.get(cat_name) | |
123 if not msgs: | |
124 continue | |
125 | |
126 tokens = { | |
127 'sub_num': str(i), | |
128 'category': cat_name.title()} | |
129 tpl = _multi_replace(templates['category_title'], tokens) | |
130 fp.write(tpl) | |
131 | |
132 for msg in msgs: | |
133 msg = msg.replace('`', '``').rstrip('\n') | |
134 msg = re_clean_code_span.sub(r'\1`` \2', msg) | |
135 fp.write('* ' + msg + '\n') | |
136 | |
137 | |
138 def _multi_replace(s, tokens): | |
139 for token in tokens: | |
140 s = s.replace('%%%s%%' % token, tokens[token]) | |
141 return s | |
142 | |
143 | |
144 def _get_templates(): | |
145 tpl_dir = os.path.join(os.path.dirname(__file__), 'changelog') | |
146 tpls = {} | |
147 for name in os.listdir(tpl_dir): | |
148 tpl = _get_template(os.path.join(tpl_dir, name)) | |
149 name_no_ext, _ = os.path.splitext(name) | |
150 tpls[name_no_ext] = tpl | |
151 return tpls | |
152 | |
153 | |
154 def _get_template(filename): | |
155 with open(filename, 'r', encoding='utf8') as fp: | |
156 return fp.read() | |
157 | |
158 | |
159 if __name__ == '__main__': | |
160 generate() | |
161 |