This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-release.git
The following commit(s) were added to refs/heads/main by this push:
new 41ccdd9 Add upload date extraction for npm
41ccdd9 is described below
commit 41ccdd9a51f03435c884b2b1ede5579e721d7761
Author: Sean B. Palmer <[email protected]>
AuthorDate: Thu Aug 7 15:10:13 2025 +0100
Add upload date extraction for npm
---
atr/routes/distribute.py | 30 ++++++++++++++++++++----------
1 file changed, 20 insertions(+), 10 deletions(-)
diff --git a/atr/routes/distribute.py b/atr/routes/distribute.py
index c11344e..1463cd8 100644
--- a/atr/routes/distribute.py
+++ b/atr/routes/distribute.py
@@ -63,7 +63,7 @@ class MavenResponse(schema.Lax):
class NpmResponse(schema.Lax):
- pass
+ time: dict[str, str] = pydantic.Field(default_factory=dict)
class PyPIUrl(schema.Lax):
@@ -105,11 +105,11 @@ class Platform(enum.Enum):
)
NPM = PlatformValue(
name="npm",
- template_url="https://registry.npmjs.org/{package}/{version}",
+ template_url="https://registry.npmjs.org/{package}",
)
NPM_SCOPED = PlatformValue(
name="npm (scoped)",
-
template_url="https://registry.npmjs.org/@{owner_namespace}/{package}/{version}",
+ template_url="https://registry.npmjs.org/@{owner_namespace}/{package}",
requires_owner_namespace=True,
)
PYPI = PlatformValue(
@@ -225,16 +225,21 @@ async def _distribute_page(
return await template.blank("Distribute", content=block.collect())
-async def _distribute_post_api(api_url: str) -> outcome.Outcome[basic.JSON]:
+async def _distribute_post_api(api_url: str, platform: Platform, version: str)
-> outcome.Outcome[basic.JSON]:
try:
async with aiohttp.ClientSession() as session:
async with session.get(api_url) as response:
response.raise_for_status()
response_json = await response.json()
- return outcome.Result(basic.as_json(response_json))
+ result = basic.as_json(response_json)
except aiohttp.ClientError as e:
- # Can be 404
return outcome.Error(e)
+ match platform:
+ case Platform.NPM | Platform.NPM_SCOPED:
+ if version not in NpmResponse.model_validate(result).time:
+ e = RuntimeError(f"Version '{version}' not found")
+ return outcome.Error(e)
+ return outcome.Result(result)
async def _distribute_post_validated(form: DistributeForm, project: str,
version: str) -> str:
@@ -244,7 +249,7 @@ async def _distribute_post_validated(form: DistributeForm,
project: str, version
package=dd.package,
version=dd.version,
)
- api_oc = await _distribute_post_api(api_url)
+ api_oc = await _distribute_post_api(api_url, dd.platform, dd.version)
block = htm.Block()
@@ -271,7 +276,7 @@ async def _distribute_post_validated(form: DistributeForm,
project: str, version
### Upload date
block.h2["Upload date"]
- upload_date = _platform_upload_date(form.platform.data, result)
+ upload_date = _platform_upload_date(dd.platform, result, dd.version)
if upload_date is not None:
block.pre[str(upload_date)]
else:
@@ -316,7 +321,7 @@ def _distribute_post_table(block: htm.Block, dd:
DistributeData) -> None:
block.table(".table.table-striped.table-bordered")[tbody]
-def _platform_upload_date(platform: Platform, data: basic.JSON) ->
datetime.datetime | None: # noqa: C901
+def _platform_upload_date(platform: Platform, data: basic.JSON, version: str)
-> datetime.datetime | None: # noqa: C901
match platform:
case Platform.ARTIFACTHUB:
if not (versions :=
ArtifactHubResponse.model_validate(data).available_versions):
@@ -337,7 +342,12 @@ def _platform_upload_date(platform: Platform, data:
basic.JSON) -> datetime.date
return None
return datetime.datetime.fromtimestamp(timestamp / 1000,
tz=datetime.UTC)
case Platform.NPM | Platform.NPM_SCOPED:
- return None
+ if not (times := NpmResponse.model_validate(data).time):
+ return None
+ # Versions can be in the form "1.2.3" or "v1.2.3", so we check for
both
+ if not (upload_time := times.get(version) or
times.get(f"v{version}")):
+ return None
+ return datetime.datetime.fromisoformat(upload_time.rstrip("Z"))
case Platform.PYPI:
if not (urls := PyPIResponse.model_validate(data).urls):
return None
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]