[Git][debian-gis-team/pygeoapi][master] 3 commits: New upstream version 0.23.4

Edward Betts (@edward) gitlab at salsa.debian.org
Fri Jun 26 10:55:30 BST 2026



Edward Betts pushed to branch master at Debian GIS Project / pygeoapi


Commits:
042cbf51 by Edward Betts at 2026-06-25T20:04:41+01:00
New upstream version 0.23.4
- - - - -
b0ede4ed by Edward Betts at 2026-06-25T20:04:54+01:00
Update upstream source from tag 'upstream/0.23.4'

Update to upstream version '0.23.4'
with Debian dir 6aedd4b5e429fa05b6a2dcd7357a3b58ecfe713d
- - - - -
fe270c27 by Edward Betts at 2026-06-26T10:55:00+01:00
New upstream release

* Upload to Debian sid. (Closes: #1140752)
* Run unit tests during build and via autopkgtest.
* Build and install documentation.
* Add more detail to package description.
* Update copyright to match LICENSE.md.
* Add debian/salsa-ci.yml.
* Add my name to Uploaders and debian/copyright.

- - - - -


21 changed files:

- debian/changelog
- debian/clean
- debian/control
- debian/copyright
- + debian/doc-base
- + debian/docs
- + debian/pybuild.testfiles
- + debian/pybuild.testfiles.autopkgtest
- debian/rules
- + debian/salsa-ci.yml
- docs/source/conf.py
- docs/source/configuration.rst
- pygeoapi/__init__.py
- pygeoapi/process/base.py
- pygeoapi/process/manager/base.py
- pygeoapi/provider/filesystem.py
- pygeoapi/resources/schemas/config/pygeoapi-config-0.x.yml
- pygeoapi/util.py
- requirements.txt
- tests/other/test_util.py
- tests/provider/test_filesystem_provider.py


Changes:

=====================================
debian/changelog
=====================================
@@ -1,3 +1,16 @@
+pygeoapi (0.23.4-1) UNRELEASED; urgency=medium
+
+  * New upstream release.
+  * Upload to Debian sid. (Closes: #1140752)
+  * Run unit tests during build and via autopkgtest.
+  * Build and install documentation.
+  * Add more detail to package description.
+  * Update copyright to match LICENSE.md.
+  * Add debian/salsa-ci.yml.
+  * Add my name to Uploaders and debian/copyright.
+
+ -- Edward Betts <edward at 4angle.com>  Fri, 26 Jun 2026 08:53:09 +0100
+
 pygeoapi (0.23.2-1) UNRELEASED; urgency=medium
 
   [ Angelos Tzotsos ]


=====================================
debian/clean
=====================================
@@ -1 +1,2 @@
 *-info/
+docs/_build/


=====================================
debian/control
=====================================
@@ -2,28 +2,75 @@ Source: pygeoapi
 Section: python
 Maintainer: Debian GIS Project <pkg-grass-devel at lists.alioth.debian.org>
 Uploaders: Angelos Tzotsos <gcpp.kalxas at gmail.com>,
-           Felix Delattre <debian at xama.nu>
+           Felix Delattre <debian at xama.nu>,
+           Edward Betts <edward at 4angle.com>,
 Build-Depends: debhelper-compat (= 13),
-               dh-python,
+               dh-doc-privacy,
                dh-sequence-python3,
+               dh-sequence-sphinxdoc <!nodoc>,
                pybuild-plugin-pyproject,
                python3-all,
-               python3-setuptools
+               python3-setuptools,
+Build-Depends-Indep: libsqlite3-mod-spatialite <!nocheck>,
+                     python3-django-cors-headers <!nocheck>,
+                     python3-elasticsearch <!nocheck>,
+                     python3-filelock <!nocheck>,
+                     python3-fiona <!nocheck>,
+                     python3-flask <!nocheck>,
+                     python3-flask-cors <!nocheck>,
+                     python3-gdal <!nocheck>,
+                     python3-geoalchemy2 <!nocheck>,
+                     python3-geopandas <!nocheck>,
+                     python3-httpx <!nocheck>,
+                     python3-jsonpatch <!nocheck>,
+                     python3-jsonschema <!nocheck>,
+                     python3-netcdf4 <!nocheck>,
+                     python3-oracledb <!nocheck>,
+                     python3-owslib <!nocheck>,
+                     python3-polyfactory <!nocheck>,
+                     python3-pyarrow <!nocheck>,
+                     python3-pydantic <!nocheck>,
+                     python3-pygeofilter <!nocheck>,
+                     python3-pyld <!nocheck>,
+                     python3-pymongo <!nocheck>,
+                     python3-pyproj <!nocheck>,
+                     python3-pytest <!nocheck>,
+                     python3-rasterio <!nocheck>,
+                     python3-s3fs <!nocheck>,
+                     python3-sodapy <!nocheck>,
+                     python3-sphinx <!nodoc>,
+                     python3-sphinx-rtd-theme <!nodoc>,
+                     python3-starlette <!nocheck>,
+                     python3-tinydb <!nocheck>,
+                     python3-xarray <!nocheck>,
+                     python3-zarr <!nocheck>,
 Standards-Version: 4.7.4
 Vcs-Browser: https://salsa.debian.org/debian-gis-team/pygeoapi
 Vcs-Git: https://salsa.debian.org/debian-gis-team/pygeoapi.git
+Testsuite: autopkgtest-pkg-pybuild
 Homepage: https://pygeoapi.io/
 
 Package: python3-pygeoapi
 Architecture: all
-Depends: ${python3:Depends},
-         ${misc:Depends}
+Depends: ${misc:Depends},
+         ${python3:Depends},
+         ${sphinxdoc:Depends},
 Suggests: python3-elasticsearch,
           python3-fiona,
           python3-geojson,
           python3-pyproj,
-          python3-rasterio
-Description: pygeoapi provides an API to geospatial data
- pygeoapi is a Python server implementation of the OGC API suite of standards
- and provides the capability to deploy a RESTful OGC API endpoint using
- OpenAPI, GeoJSON, and HTML.
+          python3-rasterio,
+Built-Using:
+ ${sphinxdoc:Built-Using},
+Description: OGC API server for publishing geospatial data
+ This library publishes geospatial resources through RESTful HTTP services that
+ conform to the OGC API family of standards. It exposes configured collections
+ using standard web representations, including OpenAPI descriptions, GeoJSON
+ responses, and HTML pages for browsing.
+ .
+ pygeoapi acts as a service layer between HTTP clients and geospatial data
+ sources. It talks to web clients, GIS applications, catalogues, and other OGC
+ API consumers over HTTP, and reads from configured data providers such as
+ files, databases, and web-accessible services. It can describe available
+ collections, return features and metadata, and provide machine-readable API
+ definitions for client software.


=====================================
debian/copyright
=====================================
@@ -4,11 +4,12 @@ Upstream-Contact: Tom Kralidis <tomkralidis at gmail.com>
 Source: https://github.com/geopython/pygeoapi
 
 Files: *
-Copyright: 2018-2024, Tom Kralidis <tomkralidis at gmail.com>
+Copyright: 2018-2026 pygeoapi development team
 License: MIT
 
 Files: debian/*
-Copyright: 2021-2024, Angelos Tzotsos <gcpp.kalxas at gmail.com>
+Copyright:      2026, Edward Betts <edward at 4angle.com>
+           2021-2024, Angelos Tzotsos <gcpp.kalxas at gmail.com>
                 2023, Felix Delattre <debian at xama.nu>
 License: GPL-2+
 


=====================================
debian/doc-base
=====================================
@@ -0,0 +1,8 @@
+Document: pygeoapi
+Title: pygeoapi documentation
+Author: Tom Kralidis <tomkralidis at gmail.com>
+Section: Programming
+
+Format: HTML
+Index: /usr/share/doc/python3-pygeoapi/html/index.html
+Files: /usr/share/doc/python3-pygeoapi/html/*.html


=====================================
debian/docs
=====================================
@@ -0,0 +1,2 @@
+README.md
+docs/_build/html


=====================================
debian/pybuild.testfiles
=====================================
@@ -0,0 +1,19 @@
+pyproject.toml
+pytest.ini
+pygeoapi-config.yml
+tests/data tests/data
+tests/pygeoapi-test-asyncapi.yml tests/pygeoapi-test-asyncapi.yml
+tests/pygeoapi-test-config-admin-empty-resources.yml tests/pygeoapi-test-config-admin-empty-resources.yml
+tests/pygeoapi-test-config-admin.yml tests/pygeoapi-test-config-admin.yml
+tests/pygeoapi-test-config-apirules.yml tests/pygeoapi-test-config-apirules.yml
+tests/pygeoapi-test-config-enclosure.yml tests/pygeoapi-test-config-enclosure.yml
+tests/pygeoapi-test-config-envvars.yml tests/pygeoapi-test-config-envvars.yml
+tests/pygeoapi-test-config-failing-collection.yml tests/pygeoapi-test-config-failing-collection.yml
+tests/pygeoapi-test-config-hidden-resources.yml tests/pygeoapi-test-config-hidden-resources.yml
+tests/pygeoapi-test-config-ogr.yml tests/pygeoapi-test-config-ogr.yml
+tests/pygeoapi-test-config-postgresql-manager.yml tests/pygeoapi-test-config-postgresql-manager.yml
+tests/pygeoapi-test-config-postgresql.yml tests/pygeoapi-test-config-postgresql.yml
+tests/pygeoapi-test-config-pubsub.yml tests/pygeoapi-test-config-pubsub.yml
+tests/pygeoapi-test-config.yml tests/pygeoapi-test-config.yml
+tests/pygeoapi-test-openapi.yml tests/pygeoapi-test-openapi.yml
+tests/pygeoapi-test-stac-api-config.yml tests/pygeoapi-test-stac-api-config.yml


=====================================
debian/pybuild.testfiles.autopkgtest
=====================================
@@ -0,0 +1,3 @@
+pyproject.toml
+pytest.ini
+tests tests


=====================================
debian/rules
=====================================
@@ -1,13 +1,38 @@
 #!/usr/bin/make -f
-# -*- makefile -*-
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-export PYBUILD_NAME=pygeoapi
+export http_proxy=
+export https_proxy=
+export PYBUILD_TEST_ARGS=-x --ignore=tests/provider/test_xarray_netcdf_provider.py \
+	--ignore=tests/provider/test_xarray_zarr_provider.py \
+	--ignore=tests/provider/test_opensearch__provider.py \
+	--ignore=tests/provider/test_elasticsearch__provider.py \
+	--ignore=tests/manager/test_postgresql_manager.py \
+	--ignore=tests/provider/test_csw_provider.py \
+	--ignore=tests/provider/test_esri_provider.py \
+	--ignore=tests/provider/test_filesystem_provider.py \
+	--ignore=tests/provider/test_mongo_provider.py \
+	--ignore=tests/provider/test_mysql_provider.py \
+	--ignore=tests/provider/test_oracle_provider.py \
+	--ignore=tests/provider/test_postgresql_mvt_provider.py \
+	--ignore=tests/provider/test_postgresql_provider.py \
+	--ignore=tests/provider/test_sensorthings_edr_provider.py \
+	--ignore=tests/provider/test_sensorthings_provider.py \
+	--ignore=tests/other/test_ogr_capabilities.py \
+	--ignore=tests/api/test_coverages.py \
+	--ignore=tests/api/test_environmental_data_retrieval.py \
+	--ignore=tests/provider/test_api_ogr_provider.py \
+	--ignore=tests/provider/test_parquet_provider.py \
+	--ignore-glob=tests/provider/test_ogr_*.py \
+    --ignore=tests/api/test_pubsub.py \
+	--ignore=tests/other/test_django.py \
+    -k "not test_admin and not test_describe_collections and not test_describe_processes and not test_django_edr_without_instance_id and not test_get_oas and not test_is_request_allowed and not test_modify_pygeofilter and not test_prefetcher and not test_unique_subclass_query_types and not test_apirules_active and not test_apirules_inactive and not test_api_exception and not test_gzip"
 
 %:
 	dh $@ --buildsystem pybuild
 
-override_dh_auto_test:
-	@echo "nocheck set, not running tests"
+override_dh_auto_build:
+	dh_auto_build $@ --buildsystem=pybuild
+	dh_doc_privacy docs README.md
+	cd docs; PYTHONPATH=$(CURDIR) LC_ALL=C.UTF-8 LANGUAGE=C.UTF-8 sphinx-build -bhtml -d _build/doctrees source _build/html
+
+before-pybuild-autopkgtest:
+	cp debian/pybuild.testfiles.autopkgtest debian/pybuild.testfiles


=====================================
debian/salsa-ci.yml
=====================================
@@ -0,0 +1,3 @@
+---
+include:
+  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml


=====================================
docs/source/conf.py
=====================================
@@ -112,7 +112,7 @@ today_fmt = '%Y-%m-%d'
 # built documents.
 #
 # The short X.Y version.
-version = '0.23.2'
+version = '0.23.4'
 # The full version, including alpha/beta/rc tags.
 release = version
 


=====================================
docs/source/configuration.rst
=====================================
@@ -292,6 +292,10 @@ default.
           type: process  # REQUIRED (collection, process, or stac-collection)
           processor:
               name: HelloWorld  # Python path of process definition
+          # optional, allow for internal HTTP request execution
+          # if set to True, enables requests to link local ranges and loopback
+          # default: False
+          allow_internal_requests: True
 
 
 .. seealso::


=====================================
pygeoapi/__init__.py
=====================================
@@ -30,7 +30,7 @@
 #
 # =================================================================
 
-__version__ = '0.23.2'
+__version__ = '0.23.4'
 
 import click
 try:


=====================================
pygeoapi/process/base.py
=====================================
@@ -3,7 +3,7 @@
 # Authors: Tom Kralidis <tomkralidis at gmail.com>
 #          Francesco Martinelli <francesco.martinelli at ingv.it>
 #
-# Copyright (c) 2022 Tom Kralidis
+# Copyright (c) 2026 Tom Kralidis
 # Copyright (c) 2024 Francesco Martinelli
 #
 # Permission is hereby granted, free of charge, to any person
@@ -53,6 +53,8 @@ class BaseProcessor:
         self.name = processor_def['name']
         self.metadata = process_metadata
         self.supports_outputs = False
+        self.allow_internal_requests = processor_def.get(
+            'allow_internal_requests', False)
 
     def set_job_id(self, job_id: str) -> None:
         """


=====================================
pygeoapi/process/manager/base.py
=====================================
@@ -46,10 +46,12 @@ from pygeoapi.process.base import (
     BaseProcessor,
     JobNotFoundError,
     JobResultNotFoundError,
+    ProcessorExecuteError,
     UnknownProcessError,
 )
 from pygeoapi.util import (
     get_current_datetime,
+    is_request_allowed,
     JobStatus,
     ProcessExecutionMode,
     RequestedProcessExecutionMode,
@@ -105,7 +107,11 @@ class BaseManager:
         except KeyError as err:
             raise UnknownProcessError('Invalid process identifier') from err
         else:
-            return load_plugin('process', process_conf['processor'])
+            pp = load_plugin('process', process_conf['processor'])
+            pp.allow_internal_requests = process_conf.get(
+                'allow_internal_requests', False)
+
+            return pp
 
     def get_jobs(self,
                  status: JobStatus = None,
@@ -394,13 +400,13 @@ class BaseManager:
         """
 
         job_id = str(uuid.uuid1())
-        processor = self.get_processor(process_id)
-        processor.set_job_id(job_id)
+        self.processor = self.get_processor(process_id)
+        self.processor.set_job_id(job_id)
         extra_execute_handler_parameters = {
             'requested_response': requested_response
         }
 
-        job_control_options = processor.metadata.get(
+        job_control_options = self.processor.metadata.get(
             'jobControlOptions', [])
 
         if execution_mode == RequestedProcessExecutionMode.respond_async:
@@ -473,7 +479,7 @@ class BaseManager:
         # TODO: handler's response could also be allowed to include more HTTP
         # headers
         mime_type, outputs, status = handler(
-            processor,
+            self.processor,
             job_id,
             data_dict,
             requested_outputs,
@@ -483,26 +489,37 @@ class BaseManager:
 
     def _send_in_progress_notification(self, subscriber: Optional[Subscriber]):
         if subscriber and subscriber.in_progress_uri:
-            response = requests.post(subscriber.in_progress_uri, json={})
-            LOGGER.debug(
-                f'In progress notification response: {response.status_code}'
-            )
+            self.__do_subscriber_request(subscriber.in_progress_uri)
 
     def _send_success_notification(
             self, subscriber: Optional[Subscriber], outputs: Any
     ):
-        if subscriber:
-            response = requests.post(subscriber.success_uri, json=outputs)
-            LOGGER.debug(
-                f'Success notification response: {response.status_code}'
-            )
+        if subscriber and subscriber.success_uri:
+            self.__do_subscriber_request(subscriber.success_uri, outputs)
 
     def _send_failed_notification(self, subscriber: Optional[Subscriber]):
         if subscriber and subscriber.failed_uri:
-            response = requests.post(subscriber.failed_uri, json={})
-            LOGGER.debug(
-                f'Failed notification response: {response.status_code}'
-            )
+            self.__do_subscriber_request(subscriber.failed_uri)
+
+    def __do_subscriber_request(self, url: str, data: dict = {}) -> None:
+        """
+        Helper function to execute a subscriber URL via HTTP POST
+
+        :param url: `str` of URL
+        :param data: `dict` of request payload
+
+        :returns: `None`
+        """
+
+        if not is_request_allowed(url, self.processor.allow_internal_requests):
+            msg = 'URL not allowed'
+            LOGGER.error(f'{msg}: {url}')
+            raise ProcessorExecuteError(msg)
+
+        response = requests.post(url, json=data)
+        LOGGER.debug(
+            f'Response: {response.status_code}'
+        )
 
     def __repr__(self):
         return f'<BaseManager> {self.name}'


=====================================
pygeoapi/provider/filesystem.py
=====================================
@@ -2,7 +2,7 @@
 #
 # Authors: Tom Kralidis <tomkralidis at gmail.com>
 #
-# Copyright (c) 2023 Tom Kralidis
+# Copyright (c) 2026 Tom Kralidis
 #
 # Permission is hereby granted, free of charge, to any person
 # obtaining a copy of this software and associated documentation
@@ -34,6 +34,7 @@ import logging
 import os
 
 from pygeoapi.provider.base import (BaseProvider, ProviderConnectionError,
+                                    ProviderInvalidQueryError,
                                     ProviderNotFoundError)
 from pygeoapi.util import file_modified_iso8601, get_path_basename, url_join
 
@@ -76,9 +77,15 @@ class FileSystemProvider(BaseProvider):
         root_link = None
         child_links = []
 
-        data_path = os.path.join(self.data, dirpath)
+        if '..' in dirpath:
+            msg = 'Invalid path requested'
+            LOGGER.error(f'{msg}: {dirpath}')
+            raise ProviderInvalidQueryError(msg)
+
         data_path = self.data + dirpath
 
+        LOGGER.debug(f'Data path: {data_path}')
+
         if '/' not in dirpath:  # root
             root_link = baseurl
         else:


=====================================
pygeoapi/resources/schemas/config/pygeoapi-config-0.x.yml
=====================================
@@ -682,7 +682,11 @@ properties:
                                           For custom built plugins, use the import path (e.g. `mypackage.provider.MyProvider`)
                               required:
                                   - name
-                      required:
+                          allow_internal_requests:
+                              type: boolean
+                              description: whether to allow internal HTTP requests
+                              default: false
+                      requred:
                           - type
                           - processor
 definitions:


=====================================
pygeoapi/util.py
=====================================
@@ -36,6 +36,7 @@ from datetime import date, datetime, time, timezone
 from decimal import Decimal
 from enum import Enum
 from heapq import heappush
+import ipaddress
 import json
 import logging
 import mimetypes
@@ -43,6 +44,7 @@ import os
 import pathlib
 from pathlib import Path
 import re
+import socket
 from typing import Any, IO, Union, List, Optional
 from urllib.parse import urlparse
 from urllib.request import urlopen
@@ -754,3 +756,30 @@ def remove_url_auth(url: str) -> str:
     u = urlparse(url)
     auth = f'{u.username}:{u.password}@'
     return url.replace(auth, '')
+
+
+def is_request_allowed(url: str, allow_internal: bool = False) -> bool:
+    """
+    Test whether an HTTP request is allowed to be executed
+
+    :param url: `str` of URL
+    :param allow_internal: `bool` of whether internal requests are
+                           allowed (default `False`)
+
+    :returns: `bool` of whether HTTP request execution is allowed
+    """
+
+    is_allowed = False
+
+    u = urlparse(url)
+
+    ip = socket.gethostbyname(u.hostname)
+
+    is_private = ipaddress.ip_address(ip).is_private
+
+    if not is_private:
+        is_allowed = True
+    if is_private and allow_internal:
+        is_allowed = True
+
+    return is_allowed


=====================================
requirements.txt
=====================================
@@ -1,18 +1,18 @@
-Babel==2.10.3
-click==8.1.6
-filelock==3.25.0
-Flask==3.1.3
-jinja2==3.1.2
-jsonschema==4.26.0
-pydantic==1.10.14
-pygeofilter==0.3.3
-pygeoif==1.6.0
-pyproj==3.6.1
-python-dateutil==2.8.2
-pytz==2026.1.post1
-PyYAML==6.0.1
-rasterio==1.3.9
-requests==2.31.0
-shapely==2.0.3
-SQLAlchemy==2.0.48
-tinydb==3.15.2
+Babel
+click
+filelock
+Flask
+jinja2
+jsonschema
+pydantic
+pygeofilter
+pygeoif
+pyproj
+python-dateutil
+pytz
+PyYAML
+rasterio
+requests
+shapely
+SQLAlchemy
+tinydb


=====================================
tests/other/test_util.py
=====================================
@@ -2,7 +2,7 @@
 #
 # Authors: Tom Kralidis <tomkralidis at gmail.com>
 #
-# Copyright (c) 2025 Tom Kralidis
+# Copyright (c) 2026 Tom Kralidis
 #
 # Permission is hereby granted, free of charge, to any person
 # obtaining a copy of this software and associated documentation
@@ -321,3 +321,21 @@ def test_get_choice_from_headers():
                                         'accept') == 'application/ld+json'
     assert util.get_choice_from_headers(
         {'accept-language': 'en_US', 'accept': '*/*'}, 'accept') == '*/*'
+
+
+ at pytest.mark.parametrize('url,allow_internal,result', [
+    ['http://127.0.0.1/test', False, False],
+    ['http://127.0.0.1/test', True, True],
+    ['http://192.168.0.12/test', False, False],
+    ['http://192.168.0.12/test', True, True],
+    ['http://169.254.0.11/test', False, False],
+    ['http://169.254.0.11/test', True, True],
+    ['http://0.0.0.0/test', True, True],
+    ['http://0.0.0.0/test', False, False],
+    ['http://localhost:5000/test', False, False],
+    ['http://localhost:5000/test', True, True],
+    ['https://pygeoapi.io', False, True],
+    ['https://pygeoapi.io', True, True]
+])
+def test_is_request_allowed(url, allow_internal, result):
+    assert util.is_request_allowed(url, allow_internal) is result


=====================================
tests/provider/test_filesystem_provider.py
=====================================
@@ -2,7 +2,7 @@
 #
 # Authors: Tom Kralidis <tomkralidis at gmail.com>
 #
-# Copyright (c) 2021 Tom Kralidis
+# Copyright (c) 2026 Tom Kralidis
 #
 # Permission is hereby granted, free of charge, to any person
 # obtaining a copy of this software and associated documentation
@@ -30,6 +30,7 @@
 import os
 import pytest
 
+from pygeoapi.provider.base import ProviderInvalidQueryError
 from pygeoapi.provider.filesystem import FileSystemProvider
 
 THISDIR = os.path.dirname(os.path.realpath(__file__))
@@ -73,3 +74,6 @@ def test_query(config):
         'osm_id': 'int'
     }
     assert r['assets']['default']['href'] == 'http://example.org/stac/poi_portugal.gpkg'  # noqa
+
+    with pytest.raises(ProviderInvalidQueryError):
+        _ = p.get_data_path(baseurl, urlpath, '../../poi_portugal')



View it on GitLab: https://salsa.debian.org/debian-gis-team/pygeoapi/-/compare/5b5becad831d33ebd7d7589974c52ef9c0043343...fe270c27de42157ad6dd71b6afc24c7803077f71

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/pygeoapi/-/compare/5b5becad831d33ebd7d7589974c52ef9c0043343...fe270c27de42157ad6dd71b6afc24c7803077f71
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20260626/8a8d11c3/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list