diff --git a/scripts/g.extension/g.extension.html b/scripts/g.extension/g.extension.html index cdbadb07bf6..df26a14bb5b 100644 --- a/scripts/g.extension/g.extension.html +++ b/scripts/g.extension/g.extension.html @@ -126,6 +126,49 @@

Compilation and installation

as manual page are compiled, not only the source code (which is really necessary to compile just in case of C). +

Using an alternative addon server (MS-Windows)

+ +On MS-Windows, g.extension downloads precompiled addon ZIPs from the +official WinGRASS server (http://wingrass.fsv.cvut.cz) by default. +The base URL of the server can be redirected to a mirror or a private addon +server by setting ADDONS_BASE_URL. The setting affects both the +addon listing (g.extension -l, -c, -g) +and the install download URL, so listing and install always come from the same +place. + +

+The setting can be applied in two ways: + +

+ +

+To revert to the default server, unset the value: + +

+g.gisenv unset=ADDONS_BASE_URL
+
+ +

+Alternative servers are: +

+ +

+Note, settings explained in this section are is honoured on MS-Windows only; on +Linux/macOS it has no effect. +

EXAMPLES

Download and install of an extension

@@ -143,6 +186,35 @@

Download and install of an extension

g.extension r.stream.distance +

Installing from an alternative addon server (MS-Windows)

+ +On MS-Windows, redirect the addon server to a mirror or private host +once per session, then use g.extension normally — listing and install +both go through the configured server. + +
+# Point at the alternative server (persists across GRASS sessions)
+g.gisenv set="ADDONS_BASE_URL=https://ecodiv.earth/share"
+
+# List what the alternative server provides
+g.extension -l
+
+# Install an extension from it
+g.extension extension=r.tpi
+
+# Revert to the default WinGRASS server
+g.gisenv unset=ADDONS_BASE_URL
+
+ +

+For a one-off shell session, set the environment variable instead: + +

+# cmd.exe
+set GRASS_ADDONS_BASE_URL=https://ecodiv.earth/share
+g.extension extension=r.tpi
+
+

Download and install of an extension when behind a proxy

Example for an open http proxy: @@ -204,8 +276,10 @@

Installing from various online repositories: GitHub, GitLab, Bitbucket

g.extension r.example url=http://example.com/.../r.example?format=zip -Note that because of MS-Windows operating system architecture, -only official repository is supported on this platform. +On Windows, this will work also, but only when pointing at a server with +pre-compiled addons and set up using the same directory layout as the +official WinGRASS server, namely {base}/grass{MM}/addons/grass-{M.M.P}/ +containing one ZIP per extension and a modules.xml listing.

Install a specific version from Addons

@@ -248,6 +322,12 @@

KNOWN ISSUES

(a Python replacement for Python scripts should be implemented). +

+On Windows, it is now possible to point at an alternative server. However, note +that this server should be set up using the same directory layout as the +official WinGRASS server, namely {base}/grass{MM}/addons/grass-{M.M.P}/ +containing pre-compiled extensions as ZIP file, and a modules.xml listing. +

TROUBLESHOOTING

Since extensions have to be compiled on Unix based systems (Linux, Mac OSX etc.) @@ -276,4 +356,5 @@

AUTHORS

Markus Neteler (original shell script)
Martin Landa, Czech Technical University in Prague, Czech Republic (Python rewrite)
-Vaclav Petras, NCSU GeoForAll Lab (support for general sources, partial refactoring) +Vaclav Petras, NCSU GeoForAll Lab (support for general sources, partial refactoring)
+Paulo van Breugel, HAS green academy (add option for Windows users to point at alternative repositories) diff --git a/scripts/g.extension/g.extension.md b/scripts/g.extension/g.extension.md index 78767c5059f..893177fb2a6 100644 --- a/scripts/g.extension/g.extension.md +++ b/scripts/g.extension/g.extension.md @@ -115,6 +115,42 @@ extensions. The reason is that more things such as manual page are compiled, not only the source code (which is really necessary to compile just in case of C). +### Using an alternative addon server (MS-Windows) + +On MS-Windows, *g.extension* downloads precompiled addon ZIPs from the +official WinGRASS server (`http://wingrass.fsv.cvut.cz`) by default. +The base URL of the server can be redirected to a mirror or a private +addon server by setting `ADDONS_BASE_URL`. The setting affects both the +addon listing (`g.extension -l`, `-c`, `-g`) and the install download +URL, so listing and install always come from the same place. + +The setting can be applied in two ways: + +- From inside GRASS (persists across sessions, settable from the GUI's + variables panel as well): + + ```sh + g.gisenv set="ADDONS_BASE_URL=https://your.server.example" + ``` + +- As a shell environment variable named `GRASS_ADDONS_BASE_URL` (useful + for one-off shell sessions or CI jobs). The gisenv setting takes + precedence over the environment variable; if neither is set, the + default WinGRASS server is used. + +To revert to the default server, unset the value: + +```sh +g.gisenv unset=ADDONS_BASE_URL +``` + +Alternative servers are: + +- (Python extensions only) + +Note, settings explained in this section are is honoured on MS-Windows only; on +Linux/macOS it has no effect. + ## EXAMPLES ### Download and install of an extension @@ -132,6 +168,34 @@ convenience, a shorter syntax can be used: g.extension r.stream.distance ``` +### Installing from an alternative addon server (MS-Windows) + +On MS-Windows, redirect the addon server to a mirror or private host +once per session, then use *g.extension* normally — listing and install +both go through the configured server. + +```sh +# Point at the alternative server (persists across GRASS sessions) +g.gisenv set="ADDONS_BASE_URL=https://ecodiv.earth/share" + +# List what the alternative server provides +g.extension -l + +# Install an extension from it +g.extension extension=r.tpi + +# Revert to the default WinGRASS server +g.gisenv unset=ADDONS_BASE_URL +``` + +For a one-off shell session, set the environment variable instead: + +```sh +# cmd.exe +set GRASS_ADDONS_BASE_URL=https://ecodiv.earth/share +g.extension extension=r.tpi +``` + ### Download and install of an extension when behind a proxy Example for an open http proxy: @@ -198,8 +262,10 @@ can be used: g.extension r.example url=http://example.com/.../r.example?format=zip ``` -Note that because of MS-Windows operating system architecture, only -official repository is supported on this platform. +On Windows, this will work also, but only when pointing at a server with +pre-compiled addons and set up using the same directory layout as the +official WinGRASS server, namely `{base}/grass{MM}/addons/grass-{M.M.P}/` +containing one ZIP per extension and a `modules.xml` listing. ### Install a specific version from Addons @@ -239,6 +305,11 @@ On MS-Windows, only the official repository is working because there is no way of compiling the modules (a Python replacement for Python scripts should be implemented). +On Windows, it is now possible to point at an alternative server. However, note +that this server should be set up using the same directory layout as the +official WinGRASS server, namely `{base}/grass{MM}/addons/grass-{M.M.P}/` +containing pre-compiled extensions as ZIP file, and a `modules.xml` listing. + ## TROUBLESHOOTING Since extensions have to be compiled on Unix based systems (Linux, Mac @@ -269,3 +340,5 @@ Martin Landa, Czech Technical University in Prague, Czech Republic Vaclav Petras, [NCSU GeoForAll Lab](https://geospatial.ncsu.edu/geoforall/) (support for general sources, partial refactoring) +Paulo van Breugel, HAS green academy (add option for Windows users to +point at alternative repositories) diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py old mode 100755 new mode 100644 index ab1428e5934..7db2c306391 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -8,7 +8,7 @@ # Vaclav Petras (support for general sources) # PURPOSE: Tool to download and install extensions into local installation # -# COPYRIGHT: (C) 2009-2025 by Markus Neteler, and the GRASS Development Team +# COPYRIGHT: (C) 2009-2026 by Markus Neteler, and the GRASS Development Team # # This program is free software under the GNU General # Public License (>=v2). Read the file COPYING that @@ -1188,7 +1188,9 @@ def install_extension(source=None, url=None, xmlurl=None, branch=None): ret1 = 0 new_modules_ext = None if sys.platform == "win32": - ret1, new_modules_ext, new_files_ext = install_extension_win(extension) + ret1, new_modules_ext, new_files_ext = install_extension_win( + extension, url=url + ) else: ( ret1, @@ -1590,21 +1592,59 @@ def install_module_xml(mlist): return mlist -def install_extension_win(name): - """Install extension on MS Windows""" +def get_addons_base_url(): + """Resolve the base URL of the addon server (Windows binary repository). + + Precedence (highest first): + 1. ``ADDONS_BASE_URL`` set via ``g.gisenv set="ADDONS_BASE_URL=..."``. + Persists across GRASS sessions and is settable from inside GRASS. + 2. ``GRASS_ADDONS_BASE_URL`` shell environment variable. Useful for + CI / system administrators / one-off shell sessions. + 3. The built-in default ``http://wingrass.fsv.cvut.cz`` (the official + WinGRASS server). + """ + try: + gisenv_value = gs.gisenv().get("ADDONS_BASE_URL") + except Exception: # noqa: BLE001 + # gisenv() can fail outside a GRASS session; fall through. + gisenv_value = None + if gisenv_value: + return gisenv_value.rstrip("/") + env_value = os.environ.get("GRASS_ADDONS_BASE_URL") + if env_value: + return env_value.rstrip("/") + return "http://wingrass.fsv.cvut.cz" + + +def install_extension_win(name, url=None): + """Install extension on MS Windows.""" gs.message( _("Downloading precompiled GRASS Addons <{}>...").format(options["extension"]) ) - # build base URL - base_url = ( - "http://wingrass.fsv.cvut.cz/" - f"grass{VERSION[0]}{VERSION[1]}/addons/" - f"grass-{VERSION[0]}.{VERSION[1]}.{VERSION[2]}" - ) + # Resolve the final ZIP URL. + if url: + if not url.lower().endswith(".zip"): + gs.fatal( + _( + "On MS-Windows, the 'url' option must point to a ZIP file " + "(URL ending in '.zip'). To use a different addon server, " + "set the ADDONS_BASE_URL gisenv variable or the " + "GRASS_ADDONS_BASE_URL environment variable instead." + ) + ) + zip_url = url + else: + base = get_addons_base_url() + base_url = ( + f"{base}/" + f"grass{VERSION[0]}{VERSION[1]}/addons/" + f"grass-{VERSION[0]}.{VERSION[1]}.{VERSION[2]}" + ) + zip_url = f"{base_url}/{name}.zip" # resolve ZIP URL - source, url = resolve_source_code(url="{0}/{1}.zip".format(base_url, name)) + source, url = resolve_source_code(url=zip_url) # to hide non-error messages from subprocesses outdev = open(os.devnull, "w") if gs.verbosity() <= 2 else sys.stdout @@ -2597,11 +2637,29 @@ def resolve_xmlurl_prefix(url, source=None): """ gs.debug("resolve_xmlurl_prefix(url={0}, source={1})".format(url, source)) if source in {"official", "official_fork"}: - # use pregenerated modules XML file - # Define branch to fetch from (latest or current version) - version_branch = get_version_branch(VERSION[0]) - - url = "https://grass.osgeo.org/addons/{}/".format(version_branch) + # On MS-Windows only, the user can redirect both listing and install + # to a custom addon server via the ADDONS_BASE_URL gisenv setting or + # the GRASS_ADDONS_BASE_URL env var. + # On non-Windows, this override is intentionally not honoured + override = None + if sys.platform == "win32": + try: + override = gs.gisenv().get("ADDONS_BASE_URL") or os.environ.get( + "GRASS_ADDONS_BASE_URL" + ) + except Exception: # noqa: BLE001 + override = os.environ.get("GRASS_ADDONS_BASE_URL") + if override: + override = override.rstrip("/") + url = ( + f"{override}/grass{VERSION[0]}{VERSION[1]}/addons/" + f"grass-{VERSION[0]}.{VERSION[1]}.{VERSION[2]}/" + ) + else: + # use pregenerated modules XML file + # Define branch to fetch from (latest or current version) + version_branch = get_version_branch(VERSION[0]) + url = "https://grass.osgeo.org/addons/{}/".format(version_branch) # else try to get extensions XMl from SVN repository (provided URL) # the exact action depends on subsequent code (somewhere) @@ -2930,7 +2988,7 @@ def main(): if options["operation"] == "add": check_dirs() if sys.platform == "win32": - install_extension() + install_extension(url=original_url) else: if original_url == "" or flags["o"]: # Query GitHub API only if extension will be downloaded