This is an automated email from the ASF dual-hosted git repository.

arm pushed a commit to branch arm
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git


The following commit(s) were added to refs/heads/arm by this push:
     new 2951bd2e Possible LDAP implementation (commit to be updated)
2951bd2e is described below

commit 2951bd2ee767fcb70cfa1ccf296c464c0b1ea06c
Author: Alastair McFarlane <[email protected]>
AuthorDate: Mon Mar 30 16:51:20 2026 +0100

    Possible LDAP implementation (commit to be updated)
---
 atr/ldap.py             |  4 +++
 atr/{svn => }/pubsub.py | 74 +++++++++++++++++--------------------------------
 atr/server.py           | 16 +++++------
 atr/svn/commits.py      | 64 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 101 insertions(+), 57 deletions(-)

diff --git a/atr/ldap.py b/atr/ldap.py
index e05fe99b..4de9a17a 100644
--- a/atr/ldap.py
+++ b/atr/ldap.py
@@ -242,6 +242,10 @@ async def github_to_apache(github_numeric_uid: int) -> str:
     return ldap_params.results_list[0].uid[0]
 
 
+async def handle_update(payload: dict):
+    return
+
+
 async def is_active(asf_uid: str) -> bool:
     import atr.config as config
 
diff --git a/atr/svn/pubsub.py b/atr/pubsub.py
similarity index 52%
rename from atr/svn/pubsub.py
rename to atr/pubsub.py
index 0950901b..3c23cf93 100644
--- a/atr/svn/pubsub.py
+++ b/atr/pubsub.py
@@ -19,33 +19,33 @@ import asyncio
 import os
 import pathlib
 import urllib.parse
-from typing import TYPE_CHECKING, Final
+from typing import Any
 
 import asfpy.pubsub
 
+import atr.ldap as ldap
 import atr.log as log
-import atr.svn as svn
+import atr.svn.commits as commits
 
-if TYPE_CHECKING:
-    from collections.abc import Sequence
 
-# TODO: Check that these prefixes are correct
-_WATCHED_PREFIXES: Final[tuple[str, ...]] = (
-    "/svn/dist/dev",
-    "/svn/dist/release",
-)
+def is_ldap_payload(payload: dict[str, Any]) -> bool:
+    return "ldap" in payload.get("pubsub_topics", [])
 
 
-class SVNListener:
+def is_commit_payload(payload: dict[str, Any]) -> bool:
+    return "commit" in payload.get("pubsub_topics", [])
+
+
+class PubSubListener:
     def __init__(
         self,
-        working_copy_root: os.PathLike | str,
+        svn_working_copy_root: os.PathLike | str,
         url: str,
         username: str,
         password: str,
-        topics: str = "commit/svn",
+        topics: str = "commit/svn,private/ldap",
     ) -> None:
-        self.working_copy_root = pathlib.Path(working_copy_root)
+        self.svn_working_copy_root = pathlib.Path(svn_working_copy_root)
         self.url = url
         self.username = username
         self.password = password
@@ -57,23 +57,23 @@ class SVNListener:
         # Or does asfpy.pubsub.listen() already do this?
         if not self.url:
             log.error("PubSub URL is not configured")
-            log.warning("SVNListener disabled: no URL provided")
+            log.warning("PubSubListener disabled: no URL provided")
             return
 
         if (not self.username) or (not self.password):
             log.error("PubSub credentials not configured")
-            log.warning("SVNListener disabled: missing credentials")
+            log.warning("PubSubListener disabled: missing credentials")
             return
 
         if not self.url.startswith("https://";):
             log.error(
                 f"PubSub URL must use HTTPS protocol: {self.url!r}. Example: 
'https://pubsub.apache.org:2069'",
             )
-            log.warning("SVNListener disabled due to invalid URL")
+            log.warning("PubSubListener disabled due to invalid URL")
             return
 
         full_url = urllib.parse.urljoin(self.url, self.topics)
-        log.info(f"SVNListener starting with URL: {full_url}")
+        log.info(f"PubSubListener starting with URL: {full_url}")
 
         try:
             async for payload in asfpy.pubsub.listen(
@@ -84,40 +84,16 @@ class SVNListener:
                 if (payload is None) or ("stillalive" in payload):
                     continue
 
-                pubsub_path = str(payload.get("pubsub_path", ""))
-                if not pubsub_path.startswith(_WATCHED_PREFIXES):
-                    # Ignore commits outside dist/dev or dist/release
+                if is_commit_payload(payload):
+                    await commits.handle(payload, self.svn_working_copy_root)
+                elif is_ldap_payload(payload):
+                    await ldap.handle_update(payload)
+                else:
                     continue
-                log.debug(f"PubSub payload: {payload}")
-                await self._process_payload(payload)
         except asyncio.CancelledError:
-            log.info("SVNListener cancelled, shutting down gracefully")
+            log.info("PubSubListener cancelled, shutting down gracefully")
             raise
         except Exception as exc:
-            log.exception(f"SVNListener error: {exc}")
+            log.exception(f"PubSubListener error: {exc}")
         finally:
-            log.info("SVNListener.start() finished")
-
-    async def _process_payload(self, payload: dict) -> None:
-        """
-        Update each changed file in the local working copy.
-
-        Payload format that we listen to:
-            {
-              "commit": {
-                 "changed": ["/path/inside/repo/foo.txt", ...]
-              },
-              ...
-            }
-        """
-        changed: Sequence[str] = payload.get("commit", {}).get("changed", [])
-        for repo_path in changed:
-            prefix = next((p for p in _WATCHED_PREFIXES if 
repo_path.startswith(p)), "")
-            if not prefix:
-                continue
-            local_path = self.working_copy_root / repo_path[len(prefix) 
:].lstrip("/")
-            try:
-                await svn.update(local_path)
-                log.info(f"svn updated {local_path}")
-            except Exception as exc:
-                log.warning(f"failed svn update {local_path}: {exc}")
+            log.info("PubSubListener.start() finished")
diff --git a/atr/server.py b/atr/server.py
index 98c73b5b..865d1043 100644
--- a/atr/server.py
+++ b/atr/server.py
@@ -59,8 +59,8 @@ import atr.manager as manager
 import atr.models.sql as sql
 import atr.paths as paths
 import atr.preload as preload
+import atr.pubsub as pubsub
 import atr.ssh as ssh
-import atr.svn.pubsub as pubsub
 import atr.tasks as tasks
 import atr.tasks.quarantine as quarantine
 import atr.template as template
@@ -329,7 +329,7 @@ def _app_setup_lifecycle(app: base.QuartApp, app_config: 
type[config.AppConfig])
         if ssh_server:
             await ssh.server_stop(ssh_server)
 
-        if task := app.extensions.get("svn_listener"):
+        if task := app.extensions.get("pubsub_listener"):
             task.cancel()
             with contextlib.suppress(asyncio.CancelledError):
                 await task
@@ -708,19 +708,19 @@ async def _initialise_pubsub(conf: 
type[config.AppConfig], app: base.QuartApp):
     valid_pubsub_url = bool(parsed_pubsub_url and parsed_pubsub_url.scheme and 
parsed_pubsub_url.netloc)
 
     if valid_pubsub_url and pubsub_url and pubsub_user and pubsub_password:
-        log.info("Starting PubSub SVN listener")
-        listener = pubsub.SVNListener(
-            working_copy_root=conf.SVN_STORAGE_DIR,
+        log.info("Starting PubSub listener")
+        listener = pubsub.PubSubListener(
+            svn_working_copy_root=conf.SVN_STORAGE_DIR,
             url=pubsub_url,
             username=pubsub_user,
             password=pubsub_password,
         )
         task = asyncio.create_task(listener.start())
-        app.extensions["svn_listener"] = task
-        log.info("PubSub SVN listener task created")
+        app.extensions["pubsub_listener"] = task
+        log.info("PubSub listener task created")
     else:
         log.info(
-            "PubSub SVN listener not started: "
+            "PubSub listener not started: "
             f"pubsub_url={bool(valid_pubsub_url)} "
             f"pubsub_user={bool(pubsub_user)} "
             # Essential to use bool(...) here to avoid logging the password
diff --git a/atr/svn/commits.py b/atr/svn/commits.py
new file mode 100644
index 00000000..9508720d
--- /dev/null
+++ b/atr/svn/commits.py
@@ -0,0 +1,64 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import pathlib
+from typing import TYPE_CHECKING, Final
+
+import atr.log as log
+import atr.svn as svn
+
+if TYPE_CHECKING:
+    from collections.abc import Sequence
+
+# TODO: Check that these prefixes are correct
+_WATCHED_PREFIXES: Final[tuple[str, ...]] = (
+    "/svn/dist/dev",
+    "/svn/dist/release",
+)
+
+
+async def handle(payload: dict, working_copy_root: pathlib.Path) -> None:
+    pubsub_path = str(payload.get("pubsub_path", ""))
+    # Ignore commits outside dist/dev or dist/release
+    if pubsub_path.startswith(_WATCHED_PREFIXES):
+        log.debug(f"PubSub payload: {payload}")
+        await _process_payload(payload, working_copy_root)
+
+
+async def _process_payload(payload: dict, working_copy_root: pathlib.Path) -> 
None:
+    """
+    Update each changed file in the local working copy.
+
+    Payload format that we listen to:
+        {
+          "commit": {
+             "changed": ["/path/inside/repo/foo.txt", ...]
+          },
+          ...
+        }
+    """
+    changed: Sequence[str] = payload.get("commit", {}).get("changed", [])
+    for repo_path in changed:
+        prefix = next((p for p in _WATCHED_PREFIXES if 
repo_path.startswith(p)), "")
+        if not prefix:
+            continue
+        local_path = working_copy_root / repo_path[len(prefix) :].lstrip("/")
+        try:
+            await svn.update(local_path)
+            log.info(f"svn updated {local_path}")
+        except Exception as exc:
+            log.warning(f"failed svn update {local_path}: {exc}")


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to