changelog.py (4541B)
1 #!/usr/bin/env python3 2 # Copied from ElementsProject/lightning, thanks! :) 3 4 from collections import namedtuple 5 from datetime import datetime 6 from mako.template import Template 7 import argparse 8 import os 9 import re 10 import requests 11 import shlex 12 import subprocess 13 import sys 14 15 # What sections do we support in the changelog: 16 sections = [ 17 'added', 18 'changed', 19 'deprecated', 20 'fixed', 21 'removed', 22 'experimental', 23 ] 24 25 repo = 'damus-io/damus' 26 27 Entry = namedtuple("Entry", ["commit", "pullreq", "content", "section", "author"]) 28 Link = namedtuple("Link", ["ref", "content", "url"]) 29 30 31 def git(cmd): 32 cmd = shlex.split(cmd) 33 out = subprocess.check_output(['git'] + cmd) 34 return out.decode('UTF-8') 35 36 37 def get_commit_range(): 38 """Find a commit range that we should collect the CHANGELOG for. 39 """ 40 description = git("describe") 41 version = description.split('-')[0] 42 return "{version}..master".format(version=version) 43 44 45 def get_log_entries(commitrange): 46 commit = None 47 logs = git("log {commitrange}".format(commitrange=commitrange)) 48 entries = [] 49 author = "" 50 51 for l in logs.split('\n'): 52 m = re.match(r'^commit ([A-Fa-f0-9]{40})$', l) 53 a = re.match(r'^Author: ([^<]+)<.*$', l) 54 if m: 55 commit = m.group(1) 56 57 if a: 58 author = "(" + a.group(1)[:-1] + ")" 59 60 m = re.match( 61 r'^\s+Changelog-({}): (.*)$'.format("|".join(sections)), l, re.IGNORECASE) 62 63 if not m: 64 continue 65 66 # Now try to resolve the pull request that originated this commit: 67 headers = { 68 'Accept': 'application/vnd.github.groot-preview+json', 69 } 70 71 if os.environ.get('GH_TOKEN'): 72 headers['Authorization'] = 'token ' + os.environ.get('GH_TOKEN') 73 74 #url = 'https://api.github.com/repos/{repo}/commits/{commit}/pulls'.format(repo=repo, commit=commit) 75 #content = requests.get(url, headers=headers).json() 76 #if content and content.get(0) is not None: 77 # pullreq = content[0]['number'] 78 #else: 79 # pullreq = None 80 pullreq = None 81 82 e = Entry(commit, pullreq, m.group(2), m.group(1).lower(), author) 83 entries.append(e) 84 85 return entries 86 87 88 def linkify(entries): 89 links = [] 90 for e in entries: 91 if e.pullreq is not None: 92 links.append(Link( 93 ref='#{}'.format(e.pullreq), 94 content=e.content, 95 url="https://github.com/{repo}/pull/{pullreq}".format(repo=repo, pullreq=e.pullreq) 96 )) 97 return list(set(links)) 98 99 100 def group(entries): 101 groups = {s: [] for s in sections} 102 for e in entries: 103 groups[e.section].append(e) 104 for s in sections: 105 if len(groups[s]) == 0: 106 del groups[s] 107 return groups 108 109 110 def commit_date(commitsha): 111 """Get the date of the specified commit. 112 """ 113 line = git("show -s --format=%ci") 114 dt = datetime.strptime(line.strip(), '%Y-%m-%d %H:%M:%S %z') 115 return dt 116 117 118 template = Template("""<%def name="group(entries)"> 119 % for e in entries: 120 % if e.pullreq is not None: 121 - ${e.content} ([#${e.pullreq}]) 122 % else: 123 - ${e.content} ${e.author} 124 % endif 125 % endfor 126 127 </%def><%def name="group_links(entries)"> 128 % for e in entries: 129 [${e.pullreq}]: https://github.com/${repo}/pull/${e.pullreq} 130 % endfor 131 </%def> 132 133 ${h2} [${version}] - ${date.strftime("%Y-%m-%d")} 134 135 % for section in sections: 136 ${h3} ${section.capitalize()} 137 ${group(groups[section]) | trim} 138 % endfor 139 140 % for l in links: 141 [${l.ref}]: ${l.url} 142 % endfor 143 [${version}]: https://github.com/${repo}/releases/tag/v${version}""") 144 145 146 if __name__ == "__main__": 147 parser = argparse.ArgumentParser( 148 description='Generate a changelog summary for a given commit range' 149 ) 150 parser.add_argument('commitrange', type=str, nargs='?', 151 help='Range of commits to consider (format: <from_commit>..<to_commit>', 152 default=get_commit_range()) 153 154 args = parser.parse_args() 155 156 if '..' not in args.commitrange: 157 print("Commit range must include '..' to separate 'from_commit' and 'to_commit'") 158 sys.exit(1) 159 160 fromcommit, tocommit = args.commitrange.split('..') 161 entries = get_log_entries(args.commitrange) 162 groups = group(entries) 163 date = commit_date(tocommit) 164 165 print(template.render( 166 groups=groups, 167 repo=repo, 168 sections=groups.keys(), 169 h2='##', 170 h3='###', 171 version=tocommit[1:], 172 date=date, 173 links=linkify(entries), 174 )) 175 176