66import tomllib
77import yaml
88from pathlib import Path
9- from tempfile import TemporaryDirectory
109from packaging .version import Version
1110from git import Repo
1211
@@ -25,6 +24,12 @@ def options():
2524 "'supported'. Defaults to 'supported', see `supported_release_branches` in "
2625 "`plugin_template.yml`." ,
2726 )
27+ parser .add_argument (
28+ "--no-fetch" ,
29+ default = False ,
30+ action = "store_true" ,
31+ help = "Don't fetch remote. Run faster at the expense of maybe being outdated." ,
32+ )
2833 return parser .parse_args ()
2934
3035
@@ -70,97 +75,100 @@ def check_pyproject_dependencies(repo, from_commit, to_commit):
7075
7176
7277def main (options , template_config ):
73- with TemporaryDirectory () as d :
74- # Clone from upstream to ensure we have updated branches & main
75- GITHUB_ORG = template_config ["github_org" ]
76- PLUGIN_NAME = template_config ["plugin_name" ]
77- UPSTREAM_REMOTE = f"https://github.com/{ GITHUB_ORG } /{ PLUGIN_NAME } .git"
78- DEFAULT_BRANCH = template_config ["plugin_default_branch" ]
79-
80- repo = Repo .clone_from (UPSTREAM_REMOTE , d , filter = "blob:none" )
81- heads = [h .split ("/" )[- 1 ] for h in repo .git .ls_remote ("--heads" ).split ("\n " )]
82- available_branches = [h for h in heads if re .search (RELEASE_BRANCH_REGEX , h )]
83- available_branches .sort (key = lambda ver : Version (ver ))
84- available_branches .append (DEFAULT_BRANCH )
85-
86- branches = options .branches
87- if branches == "supported" :
88- with open (f"{ d } /template_config.yml" , mode = "r" ) as f :
89- tc = yaml .safe_load (f )
90- branches = set (tc ["supported_release_branches" ])
91- latest_release_branch = tc ["latest_release_branch" ]
92- if latest_release_branch is not None :
93- branches .add (latest_release_branch )
94- branches .add (DEFAULT_BRANCH )
95- else :
96- branches = set (branches .split ("," ))
97-
98- if diff := branches - set (available_branches ):
99- print (f"Supplied branches contains non-existent branches! { diff } " )
100- exit (1 )
101-
102- print (f"Checking for releases on branches: { branches } " )
103-
104- releases = []
105- for branch in branches :
106- if branch != DEFAULT_BRANCH :
107- # Check if a Z release is needed
108- reasons = []
109- changes = repo .git .ls_tree ("-r" , "--name-only" , f"origin/{ branch } " , "CHANGES/" )
110- z_changelog = False
111- for change in changes .split ("\n " ):
112- # Check each changelog file to make sure everything checks out
113- _ , ext = os .path .splitext (change )
114- if ext in Y_CHANGELOG_EXTS :
115- print (
116- f"Warning: A non-backported changelog ({ change } ) is present in the "
117- f"{ branch } release branch!"
118- )
119- elif ext in Z_CHANGELOG_EXTS :
120- z_changelog = True
121- if z_changelog :
122- reasons .append ("Backports" )
123-
124- last_tag = repo .git .describe ("--tags" , "--abbrev=0" , f"origin/{ branch } " )
125- req_txt_diff = repo .git .diff (
126- f"{ last_tag } " , f"origin/{ branch } " , "--name-only" , "--" , "requirements.txt"
127- )
128- if req_txt_diff :
129- reasons .append ("requirements.txt" )
130- pyproject_diff = repo .git .diff (
131- f"{ last_tag } " , f"origin/{ branch } " , "--name-only" , "--" , "pyproject.toml"
132- )
133- if pyproject_diff :
134- reasons .extend (check_pyproject_dependencies (repo , last_tag , f"origin/{ branch } " ))
135-
136- if reasons :
137- curr_version = Version (last_tag )
138- assert curr_version .base_version .startswith (
139- branch
140- ), "Current-version has to belong to the current branch!"
141- next_version = Version (f"{ branch } .{ curr_version .micro + 1 } " )
78+ DEFAULT_BRANCH = template_config ["plugin_default_branch" ]
79+
80+ repo = Repo ()
81+
82+ upstream_default_branch = next (
83+ (branch for branch in repo .branches if branch .name == DEFAULT_BRANCH )
84+ ).tracking_branch ()
85+ remote = upstream_default_branch .remote_name
86+ if not options .no_fetch :
87+ repo .remote (remote ).fetch ()
88+
89+ # Warning: This will not work if branch names contain "/" but we don't really care here.
90+ heads = [h .split ("/" )[- 1 ] for h in repo .git .branch ("--remote" ).split ("\n " )]
91+ available_branches = [h for h in heads if re .search (RELEASE_BRANCH_REGEX , h )]
92+ available_branches .sort (key = lambda ver : Version (ver ))
93+ available_branches .append (DEFAULT_BRANCH )
94+
95+ branches = options .branches
96+ if branches == "supported" :
97+ tc = yaml .safe_load (repo .git .show (f"{ upstream_default_branch } :template_config.yml" ))
98+ branches = set (tc ["supported_release_branches" ])
99+ latest_release_branch = tc ["latest_release_branch" ]
100+ if latest_release_branch is not None :
101+ branches .add (latest_release_branch )
102+ branches .add (DEFAULT_BRANCH )
103+ else :
104+ branches = set (branches .split ("," ))
105+
106+ if diff := branches - set (available_branches ):
107+ print (f"Supplied branches contains non-existent branches! { diff } " )
108+ exit (1 )
109+
110+ print (f"Checking for releases on branches: { branches } " )
111+
112+ releases = []
113+ for branch in branches :
114+ if branch != DEFAULT_BRANCH :
115+ # Check if a Z release is needed
116+ reasons = []
117+ changes = repo .git .ls_tree ("-r" , "--name-only" , f"{ remote } /{ branch } " , "CHANGES/" )
118+ z_changelog = False
119+ for change in changes .split ("\n " ):
120+ # Check each changelog file to make sure everything checks out
121+ _ , ext = os .path .splitext (change )
122+ if ext in Y_CHANGELOG_EXTS :
142123 print (
143- f"A Z-release is needed for { branch } , "
144- f"Prev: { last_tag } , "
145- f"Next: { next_version .base_version } , "
146- f"Reason: { ',' .join (reasons )} "
124+ f"Warning: A non-backported changelog ({ change } ) is present in the "
125+ f"{ branch } release branch!"
147126 )
127+ elif ext in Z_CHANGELOG_EXTS :
128+ z_changelog = True
129+ if z_changelog :
130+ reasons .append ("Backports" )
131+
132+ last_tag = repo .git .describe ("--tags" , "--abbrev=0" , f"{ remote } /{ branch } " )
133+ req_txt_diff = repo .git .diff (
134+ f"{ last_tag } " , f"{ remote } /{ branch } " , "--name-only" , "--" , "requirements.txt"
135+ )
136+ if req_txt_diff :
137+ reasons .append ("requirements.txt" )
138+ pyproject_diff = repo .git .diff (
139+ f"{ last_tag } " , f"{ remote } /{ branch } " , "--name-only" , "--" , "pyproject.toml"
140+ )
141+ if pyproject_diff :
142+ reasons .extend (check_pyproject_dependencies (repo , last_tag , f"{ remote } /{ branch } " ))
143+
144+ if reasons :
145+ curr_version = Version (last_tag )
146+ assert curr_version .base_version .startswith (
147+ branch
148+ ), "Current-version has to belong to the current branch!"
149+ next_version = Version (f"{ branch } .{ curr_version .micro + 1 } " )
150+ print (
151+ f"A Z-release is needed for { branch } , "
152+ f"Prev: { last_tag } , "
153+ f"Next: { next_version .base_version } , "
154+ f"Reason: { ',' .join (reasons )} "
155+ )
156+ releases .append (next_version )
157+ else :
158+ # Check if a Y release is needed
159+ changes = repo .git .ls_tree ("-r" , "--name-only" , DEFAULT_BRANCH , "CHANGES/" )
160+ for change in changes .split ("\n " ):
161+ _ , ext = os .path .splitext (change )
162+ if ext in Y_CHANGELOG_EXTS :
163+ # We don't put Y release bumps in the commit message, check file instead.
164+ # The 'current_version' is always the dev of the next version to release.
165+ next_version = current_version (repo , DEFAULT_BRANCH ).base_version
166+ print (f"A new Y-release is needed! New Version: { next_version } " )
148167 releases .append (next_version )
149- else :
150- # Check if a Y release is needed
151- changes = repo .git .ls_tree ("-r" , "--name-only" , DEFAULT_BRANCH , "CHANGES/" )
152- for change in changes .split ("\n " ):
153- _ , ext = os .path .splitext (change )
154- if ext in Y_CHANGELOG_EXTS :
155- # We don't put Y release bumps in the commit message, check file instead.
156- # The 'current_version' is always the dev of the next version to release.
157- next_version = current_version (repo , DEFAULT_BRANCH ).base_version
158- print (f"A new Y-release is needed! New Version: { next_version } " )
159- releases .append (next_version )
160- break
161-
162- if len (releases ) == 0 :
163- print ("No new releases to perform." )
168+ break
169+
170+ if len (releases ) == 0 :
171+ print ("No new releases to perform." )
164172
165173
166174if __name__ == "__main__" :
0 commit comments