Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 84 additions & 3 deletions scripts/g.extension/g.extension.html
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,49 @@ <h3>Compilation and installation</h3>
as manual page are compiled, not only the source code (which is really
necessary to compile just in case of C).

<h3>Using an alternative addon server (MS-Windows)</h3>

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

<p>
The setting can be applied in two ways:

<ul>
<li>From inside GRASS (persists across sessions, settable from the GUI's
variables panel as well):
<div class="code"><pre>
g.gisenv set="ADDONS_BASE_URL=https://your.server.example"
</pre></div>
</li>
<li>As a shell environment variable named <code>GRASS_ADDONS_BASE_URL</code> (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.</li>
</ul>

<p>
To revert to the default server, unset the value:

<div class="code"><pre>
g.gisenv unset=ADDONS_BASE_URL
</pre></div>

<p>
Alternative servers are:
<ul>
<li><a href="https://ecodiv.earth/share">https://ecodiv.earth/share</a> (Python extensions only)</li>
</ul>

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

<h2>EXAMPLES</h2>

<h3>Download and install of an extension</h3>
Expand All @@ -143,6 +186,35 @@ <h3>Download and install of an extension</h3>
g.extension r.stream.distance
</pre></div>

<h3>Installing from an alternative addon server (MS-Windows)</h3>

On MS-Windows, redirect the addon server to a mirror or private host
once per session, then use <em>g.extension</em> normally — listing and install
both go through the configured server.

<div class="code"><pre>
# 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
</pre></div>

<p>
For a one-off shell session, set the environment variable instead:

<div class="code"><pre>
# cmd.exe
set GRASS_ADDONS_BASE_URL=https://ecodiv.earth/share
g.extension extension=r.tpi
</pre></div>

<h3>Download and install of an extension when behind a proxy</h3>

Example for an open http proxy:
Expand Down Expand Up @@ -204,8 +276,10 @@ <h3>Installing from various online repositories: GitHub, GitLab, Bitbucket</h3>
g.extension r.example url=http://example.com/.../r.example?format=zip
</pre></div>

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 <code>{base}/grass{MM}/addons/grass-{M.M.P}/</code>
containing one ZIP per extension and a <code>modules.xml</code> listing.

<h3>Install a specific version from Addons</h3>

Expand Down Expand Up @@ -248,6 +322,12 @@ <h2>KNOWN ISSUES</h2>
<!-- what does it mean?? -->
(a Python replacement for Python scripts should be implemented).

<p>
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 <code>{base}/grass{MM}/addons/grass-{M.M.P}/</code>
containing pre-compiled extensions as ZIP file, and a <code>modules.xml</code> listing.

<h2>TROUBLESHOOTING</h2>

Since extensions have to be compiled on Unix based systems (Linux, Mac OSX etc.)
Expand Down Expand Up @@ -276,4 +356,5 @@ <h2>AUTHORS</h2>

Markus Neteler (original shell script)<br>
Martin Landa, Czech Technical University in Prague, Czech Republic (Python rewrite)<br>
Vaclav Petras, <a href="https://geospatial.ncsu.edu/geoforall/">NCSU GeoForAll Lab</a> (support for general sources, partial refactoring)
Vaclav Petras, <a href="https://geospatial.ncsu.edu/geoforall/">NCSU GeoForAll Lab</a> (support for general sources, partial refactoring)<br>
Paulo van Breugel, HAS green academy (add option for Windows users to point at alternative repositories)
77 changes: 75 additions & 2 deletions scripts/g.extension/g.extension.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

- <https://ecodiv.earth/share> (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
Expand All @@ -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:
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
92 changes: 75 additions & 17 deletions scripts/g.extension/g.extension.py
100755 → 100644
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason the executable flag was lost on this file? See the mode change 100755 -> 100644 on GitHub.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that is not an intentional change.

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# Vaclav Petras <wenzeslaus gmail com> (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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down
Loading