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]