7777# % description: Specific branch to fetch addon from (only used when fetching from git)
7878# % required: no
7979# % multiple: no
80- # % answer: main
8180# %end
8281
8382# %flag
169168
170169from six .moves .urllib import request as urlrequest
171170from six .moves .urllib .error import HTTPError , URLError
171+ from six .moves .urllib .parse import urlparse
172172
173173# Get the XML parsing exceptions to catch. The behavior changed with Python 2.7
174174# and ElementTree 1.3.
191191 "User-Agent" : "Mozilla/5.0" ,
192192}
193193HTTP_STATUS_CODES = list (http .HTTPStatus )
194+ GIT_URL = "https://github.com/OSGeo/grass-addons"
194195
195196
196197def replace_shebang_win (python_file ):
@@ -234,6 +235,78 @@ def urlopen(url, *args, **kwargs):
234235 return urlrequest .urlopen (request , * args , ** kwargs )
235236
236237
238+ def get_version_branch (major_version ):
239+ """Check if version branch for the current GRASS version exists,
240+ if not, take branch for the previous version
241+ For the official repo we assume that at least one version branch is present"""
242+ version_branch = f"grass{ major_version } "
243+ try :
244+ urlrequest .urlopen (f"{ GIT_URL } /tree/{ version_branch } /src" )
245+ except URLError :
246+ version_branch = "grass{}" .format (int (major_version ) - 1 )
247+ return version_branch
248+
249+
250+ def get_github_branches (
251+ github_api_url = "https://api.github.com/repos/OSGeo/grass-addons/branches" ,
252+ version_only = True ,
253+ ):
254+ """Get ordered list of branch names in repo using github API
255+ For the official repo we assume that at least one version branch is present
256+ Due to strict rate limits in the github API (60 calls per hour) this function
257+ is currently not used."""
258+ req = urlrequest .urlopen (github_api_url )
259+ content = json .loads (req .read ())
260+ branches = [repo_branch ["name" ] for repo_branch in content ]
261+ if version_only :
262+ branches = [
263+ version_branch
264+ for version_branch in branches
265+ if version_branch .startswith ("grass" )
266+ ]
267+ branches .sort ()
268+ return branches
269+
270+
271+ def get_default_branch (full_url ):
272+ """Get default branch for repository in known hosting services
273+ (currently only implemented for github, gitlab and bitbucket API)
274+ In all other cases "main" is used as default"""
275+ # Parse URL
276+ url_parts = urlparse (full_url )
277+ # Get organization and repository component
278+ try :
279+ organization , repository = url_parts .path .split ("/" )[1 :3 ]
280+ except URLError :
281+ gscript .fatal (
282+ _ (
283+ "Cannot retrieve organization and repository from URL: <{}>." .format (
284+ full_url
285+ )
286+ )
287+ )
288+ # Construct API call and retrieve default branch
289+ api_calls = {
290+ "github.com" : f"https://api.github.com/repos/{ organization } /{ repository } " ,
291+ "gitlab.com" : f"https://gitlab.com/api/v4/projects/{ organization } %2F{ repository } " ,
292+ "bitbucket.org" : f"https://api.bitbucket.org/2.0/repositories/{ organization } /{ repository } /branching-model?" ,
293+ }
294+ # Try to get default branch via API. The API call is known to fail a) if the full_url
295+ # does not belong to an implemented hosting service or b) if the rate limit of the
296+ # API is exceeded
297+ try :
298+ req = urlrequest .urlopen (api_calls .get (url_parts .netloc ))
299+ content = json .loads (req .read ())
300+ # For github and gitlab
301+ default_branch = content .get ("default_branch" )
302+ # For bitbucket
303+ if not default_branch :
304+ default_branch = content .get ("development" ).get ("name" )
305+ except URLError :
306+ default_branch = "main"
307+ return default_branch
308+
309+
237310def download_addons_paths_file (url , response_format , * args , ** kwargs ):
238311 """Generates JSON file containing the download URLs of the official
239312 Addons
@@ -1403,8 +1476,6 @@ def download_source_code_official_github(url, name, outdev, directory=None):
14031476 """
14041477 if not directory :
14051478 directory = os .path .join (os .getcwd , name )
1406- classchar = name .split ("." , 1 )[0 ]
1407- moduleclass = expand_module_class_name (classchar )
14081479 if grass .call (["svn" , "export" , url , directory ], stdout = outdev ) != 0 :
14091480 grass .fatal (_ ("GRASS Addons <%s> not found" ) % name )
14101481 return directory
@@ -1555,7 +1626,7 @@ def download_source_code(
15551626 response = urlopen (url )
15561627 except URLError :
15571628 # Try download add-on from 'master' branch if default "main" fails
1558- if branch == "main" :
1629+ if not branch :
15591630 try :
15601631 url = url .replace ("main" , "master" )
15611632 gscript .message (
@@ -1611,8 +1682,6 @@ def download_source_code(
16111682def install_extension_std_platforms (name , source , url , branch ):
16121683 """Install extension on standard platforms"""
16131684 gisbase = os .getenv ("GISBASE" )
1614- # TODO: workaround, https://github.com/OSGeo/grass-addons/issues/528
1615- source_url = "https://github.com/OSGeo/grass-addons/tree/master/grass7/"
16161685
16171686 # to hide non-error messages from subprocesses
16181687 if grass .verbosity () <= 2 :
@@ -1694,7 +1763,7 @@ def install_extension_std_platforms(name, source, url, branch):
16941763 "SCRIPTDIR=%s" % dirs ["script" ],
16951764 "STRINGDIR=%s" % dirs ["string" ],
16961765 "ETC=%s" % os .path .join (dirs ["etc" ]),
1697- "SOURCE_URL=%s" % source_url ,
1766+ "SOURCE_URL=%s" % url ,
16981767 ]
16991768
17001769 install_cmd = [
@@ -2140,7 +2209,10 @@ def resolve_xmlurl_prefix(url, source=None):
21402209 gscript .debug ("resolve_xmlurl_prefix(url={0}, source={1})" .format (url , source ))
21412210 if source == "official" :
21422211 # use pregenerated modules XML file
2143- url = "https://grass.osgeo.org/addons/grass%s/" % version [0 ]
2212+ # Define branch to fetch from (latest or current version)
2213+ version_branch = get_version_branch (version [0 ])
2214+
2215+ url = "https://grass.osgeo.org/addons/{}/" .format (version_branch )
21442216 # else try to get extensions XMl from SVN repository (provided URL)
21452217 # the exact action depends on subsequent code (somewhere)
21462218
@@ -2218,7 +2290,10 @@ def resolve_known_host_service(url, name, branch):
22182290 else :
22192291 actual_start = ""
22202292 if "branch" in match ["url_end" ]:
2221- suffix = match ["url_end" ].format (name = name , branch = branch )
2293+ suffix = match ["url_end" ].format (
2294+ name = name ,
2295+ branch = branch if branch else get_default_branch (url ),
2296+ )
22222297 else :
22232298 suffix = match ["url_end" ].format (name = name )
22242299 url = "{prefix}{base}{suffix}" .format (
@@ -2299,47 +2374,32 @@ def resolve_source_code(url=None, name=None, branch=None, fork=False):
22992374 >>> resolve_source_code('https://bitbucket.org/joe-user/grass-module') # doctest: +SKIP
23002375 ('remote_zip', 'https://bitbucket.org/joe-user/grass-module/get/default.zip')
23012376 """
2302- if not url and name :
2303- module_class = get_module_class_name (name )
2304- # note: 'trunk' is required to make URL usable for 'svn export' call
2305- git_url = (
2306- "https://github.com/OSGeo/grass-addons/trunk/"
2307- "grass{version}/{module_class}/{module_name}" .format (
2308- version = version [0 ], module_class = module_class , module_name = name
2309- )
2310- )
2311- # trac_url = 'https://trac.osgeo.org/grass/browser/grass-addons/' \
2312- # 'grass{version}/{module_class}/{module_name}?format=zip' \
2313- # .format(version=version[0],
2314- # module_class=module_class, module_name=name)
2315- # return 'official', trac_url
2316- return "official" , git_url
2317-
2318- if url and fork :
2377+ # Handle URL for the offical repo
2378+ if name and (not url or fork ):
23192379 module_class = get_module_class_name (name )
2320-
23212380 # note: 'trunk' is required to make URL usable for 'svn export' call
2322- if branch in ["master" , "main" ]:
2323- svn_reference = "trunk"
2381+ # and fetches the default branch
2382+ if not branch :
2383+ # Fetch from default branch
2384+ version_branch = get_version_branch (version [0 ])
2385+ try :
2386+ url = url .rstrip ("/" ) if url else GIT_URL
2387+ urlrequest .urlopen (f"{ url } /tree/{ version_branch } /src" )
2388+ svn_reference = "branches/{}" .format (version_branch )
2389+ except URLError :
2390+ svn_reference = "trunk"
23242391 else :
23252392 svn_reference = "branches/{}" .format (branch )
23262393
2327- git_url = (
2328- "{url}/{branch}/"
2329- "grass{version}/{module_class}/{module_name}" .format (
2330- url = url ,
2331- version = version [0 ],
2332- module_class = module_class ,
2333- module_name = name ,
2334- branch = svn_reference ,
2335- )
2336- )
2337- # trac_url = 'https://trac.osgeo.org/grass/browser/grass-addons/' \
2338- # 'grass{version}/{module_class}/{module_name}?format=zip' \
2339- # .format(version=version[0],
2340- # module_class=module_class, module_name=name)
2341- # return 'official', trac_url
2342- return "official_fork" , git_url
2394+ if not url :
2395+ # Set URL for the given GRASS version
2396+ git_url = f"{ GIT_URL } /{ svn_reference } /src/{ module_class } /{ name } "
2397+ return "official" , git_url
2398+ else :
2399+ # Forks from the official repo should reflect the current structure
2400+ url = url .rstrip ("/" )
2401+ git_url = f"{ url } /{ svn_reference } /src/{ module_class } /{ name } "
2402+ return "official_fork" , git_url
23432403
23442404 # Check if URL can be found
23452405 # Catch corner case if local URL is given starting with file://
@@ -2351,20 +2411,20 @@ def resolve_source_code(url=None, name=None, branch=None, fork=False):
23512411 open_url = urlopen (url )
23522412 open_url .close ()
23532413 url_validated = True
2354- except :
2414+ except URLError :
23552415 pass
23562416 else :
23572417 try :
23582418 open_url = urlopen ("http://" + url )
23592419 open_url .close ()
23602420 url_validated = True
2361- except :
2421+ except URLError :
23622422 pass
23632423 try :
23642424 open_url = urlopen ("https://" + url )
23652425 open_url .close ()
23662426 url_validated = True
2367- except :
2427+ except URLError :
23682428 pass
23692429
23702430 if not url_validated :
@@ -2402,10 +2462,9 @@ def get_addons_paths(gg_addons_base_dir):
24022462 and their paths (mkhmtl.py tool)
24032463 """
24042464 get_addons_paths .json_file = "addons_paths.json"
2405-
2406- url = (
2407- "https://api.github.com/repos/OSGeo/grass-addons/git/trees/" "main?recursive=1"
2408- )
2465+ # Define branch to fetch from (latest or current version)
2466+ addons_branch = get_version_branch (version [0 ])
2467+ url = f"https://api.github.com/repos/OSGeo/grass-addons/git/trees/{ addons_branch } ?recursive=1"
24092468
24102469 response = download_addons_paths_file (
24112470 url = url ,
@@ -2505,10 +2564,7 @@ def main():
25052564
25062565 grass_version = grass .version ()
25072566 version = grass_version ["version" ].split ("." )
2508- # TODO: update temporary workaround of using grass7 subdir of addon-repo, see
2509- # https://github.com/OSGeo/grass-addons/issues/528
2510- version [0 ] = 7
2511- version [1 ] = 9
2567+
25122568 build_platform = grass_version ["build_platform" ].split ("-" , 1 )[0 ]
25132569
25142570 sys .exit (main ())
0 commit comments