2020from github import Github
2121import os
2222import re
23-
23+ import subprocess
2424
2525def print_pulls (repo_name , title , pulls ):
26- if len (pulls ) > 0 :
26+ if len (pulls ) > 0 :
2727 print ("**{}:**" .format (title ))
2828 print ()
29- for pull , commit in pulls :
29+ for ( pull , commit ) in pulls :
3030 url = "https://github.com/{}/pull/{}" .format (repo_name , pull .number )
31- print (
32- "- {} [#{}]({}) ({})" .format (
33- pull .title , pull .number , url , commit .author .login
34- )
35- )
31+ print ("- {} [#{}]({}) ({})" .format (pull .title , pull .number , url , commit .author .login ))
3632 print ()
3733
3834
39- def generate_changelog (repo , repo_name , tag1 , tag2 ):
35+ def generate_changelog (repo , repo_name , tag1 , tag2 , version ):
36+
4037 # get a list of commits between two tags
4138 print (f"Fetching list of commits between { tag1 } and { tag2 } " , file = sys .stderr )
4239 comparison = repo .compare (tag1 , tag2 )
@@ -55,65 +52,113 @@ def generate_changelog(repo, repo_name, tag1, tag2):
5552 all_pulls .append ((pull , commit ))
5653
5754 # we split the pulls into categories
58- # TODO: make categories configurable
5955 breaking = []
6056 bugs = []
6157 docs = []
6258 enhancements = []
59+ performance = []
60+ other = []
6361
6462 # categorize the pull requests based on GitHub labels
6563 print ("Categorizing pull requests" , file = sys .stderr )
66- for pull , commit in all_pulls :
64+ for (pull , commit ) in all_pulls :
65+
6766 # see if PR title uses Conventional Commits
68- cc_type = ""
69- # cc_scope = ''
70- cc_breaking = ""
71- parts = re .findall (r" ^([a-z]+)(\([a-z]+\))?(!)?:" , pull .title )
67+ cc_type = ''
68+ cc_scope = ''
69+ cc_breaking = ''
70+ parts = re .findall (r' ^([a-z]+)(\([a-z]+\))?(!)?:' , pull .title )
7271 if len (parts ) == 1 :
7372 parts_tuple = parts [0 ]
74- cc_type = parts_tuple [0 ] # fix, feat, docs, chore
75- # cc_scope = parts_tuple[1] # component within project
76- cc_breaking = parts_tuple [2 ] == "!"
73+ cc_type = parts_tuple [0 ] # fix, feat, docs, chore
74+ cc_scope = parts_tuple [1 ] # component within project
75+ cc_breaking = parts_tuple [2 ] == '!'
7776
7877 labels = [label .name for label in pull .labels ]
79- # print(pull.number, labels, parts, file=sys.stderr)
80- if "api change" in labels or cc_breaking :
78+ if 'api change' in labels or cc_breaking :
8179 breaking .append ((pull , commit ))
82- elif " bug" in labels or cc_type == " fix" :
80+ elif ' bug' in labels or cc_type == ' fix' :
8381 bugs .append ((pull , commit ))
84- elif "enhancement" in labels or cc_type == "feat" :
82+ elif 'performance' in labels or cc_type == 'perf' :
83+ performance .append ((pull , commit ))
84+ elif 'enhancement' in labels or cc_type == 'feat' :
8585 enhancements .append ((pull , commit ))
86- elif " documentation" in labels or cc_type == " docs" :
86+ elif ' documentation' in labels or cc_type == ' docs' or cc_type == 'doc' :
8787 docs .append ((pull , commit ))
88+ else :
89+ other .append ((pull , commit ))
8890
8991 # produce the changelog content
9092 print ("Generating changelog content" , file = sys .stderr )
93+
94+ # ASF header
95+ print ("""<!--
96+ Licensed to the Apache Software Foundation (ASF) under one
97+ or more contributor license agreements. See the NOTICE file
98+ distributed with this work for additional information
99+ regarding copyright ownership. The ASF licenses this file
100+ to you under the Apache License, Version 2.0 (the
101+ "License"); you may not use this file except in compliance
102+ with the License. You may obtain a copy of the License at
103+
104+ http://www.apache.org/licenses/LICENSE-2.0
105+
106+ Unless required by applicable law or agreed to in writing,
107+ software distributed under the License is distributed on an
108+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
109+ KIND, either express or implied. See the License for the
110+ specific language governing permissions and limitations
111+ under the License.
112+ -->\n """ )
113+
114+ print (f"# Apache DataFusion Python { version } Changelog\n " )
115+
116+ # get the number of commits
117+ commit_count = subprocess .check_output (f"git log --pretty=oneline { tag1 } ..{ tag2 } | wc -l" , shell = True , text = True ).strip ()
118+
119+ # get number of contributors
120+ contributor_count = subprocess .check_output (f"git shortlog -sn { tag1 } ..{ tag2 } | wc -l" , shell = True , text = True ).strip ()
121+
122+ print (f"This release consists of { commit_count } commits from { contributor_count } contributors. "
123+ f"See credits at the end of this changelog for more information.\n " )
124+
91125 print_pulls (repo_name , "Breaking changes" , breaking )
126+ print_pulls (repo_name , "Performance related" , performance )
92127 print_pulls (repo_name , "Implemented enhancements" , enhancements )
93128 print_pulls (repo_name , "Fixed bugs" , bugs )
94129 print_pulls (repo_name , "Documentation updates" , docs )
95- print_pulls (repo_name , "Merged pull requests " , all_pulls )
130+ print_pulls (repo_name , "Other " , other )
96131
132+ # show code contributions
133+ credits = subprocess .check_output (f"git shortlog -sn { tag1 } ..{ tag2 } " , shell = True , text = True ).rstrip ()
134+
135+ print ("## Credits\n " )
136+ print ("Thank you to everyone who contributed to this release. Here is a breakdown of commits (PRs merged) "
137+ "per contributor.\n " )
138+ print ("```" )
139+ print (credits )
140+ print ("```\n " )
141+
142+ print ("Thank you also to everyone who contributed in other ways such as filing issues, reviewing "
143+ "PRs, and providing feedback on this release.\n " )
97144
98145def cli (args = None ):
99146 """Process command line arguments."""
100147 if not args :
101148 args = sys .argv [1 :]
102149
103150 parser = argparse .ArgumentParser ()
104- parser .add_argument (
105- "project" , help = "The project name e.g. apache/datafusion-python"
106- )
107- parser .add_argument ("tag1" , help = "The previous release tag" )
108- parser .add_argument ("tag2" , help = "The current release tag" )
151+ parser .add_argument ("tag1" , help = "The previous commit or tag (e.g. 0.1.0)" )
152+ parser .add_argument ("tag2" , help = "The current commit or tag (e.g. HEAD)" )
153+ parser .add_argument ("version" , help = "The version number to include in the changelog" )
109154 args = parser .parse_args ()
110155
111156 token = os .getenv ("GITHUB_TOKEN" )
157+ project = "apache/datafusion-python"
112158
113159 g = Github (token )
114- repo = g .get_repo (args .project )
115- generate_changelog (repo , args .project , args .tag1 , args .tag2 )
116-
160+ repo = g .get_repo (project )
161+ generate_changelog (repo , project , args .tag1 , args .tag2 , args .version )
117162
118163if __name__ == "__main__" :
119- cli ()
164+ cli ()
0 commit comments