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

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


The following commit(s) were added to refs/heads/main by this push:
     new 71d78ad  Add support for Maven staging (repository.a.o) - using 4443 
Nexus 3 new version
71d78ad is described below

commit 71d78add2ebb70e9b44ae11299ef5618e3a1ead5
Author: Alastair McFarlane <[email protected]>
AuthorDate: Thu Jan 15 12:09:33 2026 +0000

    Add support for Maven staging (repository.a.o) - using 4443 Nexus 3 new 
version
---
 atr/models/sql.py                    |  1 +
 atr/storage/writers/distributions.py | 92 ++++++++++++++++++++++++++++++++----
 2 files changed, 85 insertions(+), 8 deletions(-)

diff --git a/atr/models/sql.py b/atr/models/sql.py
index 296c65d..2a58dde 100644
--- a/atr/models/sql.py
+++ b/atr/models/sql.py
@@ -123,6 +123,7 @@ class DistributionPlatform(enum.Enum):
         gh_slug="maven",
         
template_url="https://search.maven.org/solrsearch/select?q=g:{owner_namespace}+AND+a:{package}+AND+v:{version}&core=gav&rows=20&wt=json";,
         # Java ASF projects use staging URLs along the lines of
+        
template_staging_url="https://repository.apache.org:4443/repository/maven-staging/{owner_namespace}/{package}/maven-metadata.xml";,
         # 
https://repository.apache.org/content/repositories/orgapachePROJECT-NNNN/
         # There's no JSON, but each individual package has maven-metadata.xml
         requires_owner_namespace=True,
diff --git a/atr/storage/writers/distributions.py 
b/atr/storage/writers/distributions.py
index 9179eed..4569b1c 100644
--- a/atr/storage/writers/distributions.py
+++ b/atr/storage/writers/distributions.py
@@ -197,12 +197,22 @@ class CommitteeMember(CommitteeParticipant):
         dd: distribution.Data,
     ) -> tuple[sql.Distribution, bool, distribution.Metadata]:
         template_url = await self.__template_url(dd, staging)
-        api_url = template_url.format(
-            owner_namespace=dd.owner_namespace,
-            package=dd.package,
-            version=dd.version,
-        )
-        api_oc = await self.__json_from_distribution_platform(api_url, 
dd.platform, dd.version)
+
+        if dd.platform == sql.DistributionPlatform.MAVEN and staging:
+            owner = (dd.owner_namespace or "").replace(".", "/")
+            api_url = template_url.format(
+                owner_namespace=owner,
+                package=dd.package,
+                version=dd.version,
+            )
+            api_oc = await self.__json_from_maven_xml(api_url, dd.version)
+        else:
+            api_url = template_url.format(
+                owner_namespace=dd.owner_namespace,
+                package=dd.package,
+                version=dd.version,
+            )
+            api_oc = await self.__json_from_distribution_platform(api_url, 
dd.platform, dd.version)
         match api_oc:
             case outcome.Result(result):
                 pass
@@ -337,6 +347,68 @@ class CommitteeMember(CommitteeParticipant):
                     return outcome.Error(e)
         return outcome.Result(result)
 
+    async def __json_from_maven_xml(self, api_url: str, version: str) -> 
outcome.Outcome[basic.JSON]:
+        import datetime
+        import xml.etree.ElementTree as ET
+
+        try:
+            async with aiohttp.ClientSession() as session:
+                async with session.get(api_url) as response:
+                    response.raise_for_status()
+                    xml_text = await response.text()
+
+            # Parse the XML
+            root = ET.fromstring(xml_text)
+
+            # Extract versioning info
+            group = root.find("groupId")
+            artifact = root.find("artifactId")
+            versioning = root.find("versioning")
+            if versioning is None:
+                e = RuntimeError("No versioning element found in Maven 
metadata")
+                return outcome.Error(e)
+
+            # Get lastUpdated timestamp (format: yyyyMMddHHmmss)
+            last_updated_elem = versioning.find("lastUpdated")
+            if last_updated_elem is None or not last_updated_elem.text:
+                e = RuntimeError("No lastUpdated timestamp found in Maven 
metadata")
+                return outcome.Error(e)
+
+            # Convert lastUpdated string to Unix timestamp in milliseconds
+            last_updated_str = last_updated_elem.text
+            dt = datetime.datetime.strptime(last_updated_str, "%Y%m%d%H%M%S")
+            dt = dt.replace(tzinfo=datetime.UTC)
+            timestamp_ms = int(dt.timestamp() * 1000)
+
+            # Verify the version exists
+            versions_elem = versioning.find("versions")
+            if versions_elem is not None:
+                versions = [v.text for v in versions_elem.findall("version") 
if v.text]
+                if version not in versions:
+                    e = RuntimeError(f"Version '{version}' not found in Maven 
metadata")
+                    return outcome.Error(e)
+
+            # Convert to dict matching MavenResponse structure
+            result_dict = {
+                "response": {
+                    "start": 0,
+                    "docs": [
+                        {
+                            "g": group.text if group is not None else "",
+                            "a": artifact.text if artifact is not None else "",
+                            "v": version,
+                            "timestamp": timestamp_ms,
+                        }
+                    ],
+                }
+            }
+            result = basic.as_json(result_dict)
+            return outcome.Result(result)
+        except aiohttp.ClientError as e:
+            return outcome.Error(e)
+        except ET.ParseError as e:
+            return outcome.Error(RuntimeError(f"Failed to parse Maven XML: 
{e}"))
+
     async def __template_url(
         self,
         dd: distribution.Data,
@@ -345,9 +417,13 @@ class CommitteeMember(CommitteeParticipant):
         if staging is False:
             return dd.platform.value.template_url
 
-        supported = {sql.DistributionPlatform.ARTIFACT_HUB, 
sql.DistributionPlatform.PYPI}
+        supported = {
+            sql.DistributionPlatform.ARTIFACT_HUB,
+            sql.DistributionPlatform.PYPI,
+            sql.DistributionPlatform.MAVEN,
+        }
         if dd.platform not in supported:
-            raise storage.AccessError("Staging is currently supported only for 
ArtifactHub and PyPI.")
+            raise storage.AccessError("Staging is currently supported only for 
ArtifactHub, PyPI and Maven Central.")
 
         template_url = dd.platform.value.template_staging_url
         if template_url is None:


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

Reply via email to