From 9bfa0a93e729d7a69ac4f1816f321b8ca1e5d392 Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Thu, 31 Jul 2025 14:39:20 +0530 Subject: [PATCH 1/5] Add support for purl to download URL Batch 2 Signed-off-by: Tushar Goel --- src/packageurl/contrib/purl2url.py | 14 ++++++++++++++ tests/contrib/test_purl2url.py | 2 ++ 2 files changed, 16 insertions(+) diff --git a/src/packageurl/contrib/purl2url.py b/src/packageurl/contrib/purl2url.py index 6926150..57e3c50 100644 --- a/src/packageurl/contrib/purl2url.py +++ b/src/packageurl/contrib/purl2url.py @@ -513,6 +513,20 @@ def build_swift_download_url(purl): return f"https://{namespace}/{name}/archive/{version}.zip" +@download_router.route("pkg:luarocks/.*") +def build_luarocks_download_url(purl): + """ + Return a LuaRocks download URL from the `purl` string. + """ + purl_data = PackageURL.from_string(purl) + + name = purl_data.name + version = purl_data.version + + if name and version: + return f"https://luarocks.org/{name}-{version}.src.rock" + + def get_repo_download_url(purl): """ Return ``download_url`` if present in ``purl`` qualifiers or diff --git a/tests/contrib/test_purl2url.py b/tests/contrib/test_purl2url.py index 52d1ec3..8da188c 100644 --- a/tests/contrib/test_purl2url.py +++ b/tests/contrib/test_purl2url.py @@ -105,6 +105,8 @@ def test_purl2url_get_download_url(): "pkg:pub/http@0.13.3": "https://pub.dev/api/archives/http-0.13.3.tar.gz", "pkg:swift/github.com/Alamofire/Alamofire@5.4.3": "https://github.com/Alamofire/Alamofire/archive/5.4.3.zip", "pkg:swift/github.com/RxSwiftCommunity/RxFlow@2.12.4": "https://github.com/RxSwiftCommunity/RxFlow/archive/2.12.4.zip", + "pkg:luarocks/luasocket@3.1.0-1": "https://luarocks.org/luasocket-3.1.0-1.src.rock", + "pkg:luarocks/hisham/luafilesystem@1.8.0-1": "https://luarocks.org/luafilesystem-1.8.0-1.src.rock", # From `download_url` qualifier "pkg:github/yarnpkg/yarn@1.3.2?download_url=https://github.com/yarnpkg/yarn/releases/download/v1.3.2/yarn-v1.3.2.tar.gz&version_prefix=v": "https://github.com/yarnpkg/yarn/releases/download/v1.3.2/yarn-v1.3.2.tar.gz", "pkg:generic/lxc-master.tar.gz?download_url=https://salsa.debian.org/lxc-team/lxc/-/archive/master/lxc-master.tar.gz": "https://salsa.debian.org/lxc-team/lxc/-/archive/master/lxc-master.tar.gz", From 909014d25ac66d0a74079f562c26760969afcc39 Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Thu, 31 Jul 2025 18:45:12 +0530 Subject: [PATCH 2/5] Add conda support Signed-off-by: Tushar Goel --- src/packageurl/contrib/purl2url.py | 44 ++++++++++++++++++++++++++++++ tests/contrib/test_purl2url.py | 1 + 2 files changed, 45 insertions(+) diff --git a/src/packageurl/contrib/purl2url.py b/src/packageurl/contrib/purl2url.py index 57e3c50..aef93b5 100644 --- a/src/packageurl/contrib/purl2url.py +++ b/src/packageurl/contrib/purl2url.py @@ -527,6 +527,50 @@ def build_luarocks_download_url(purl): return f"https://luarocks.org/{name}-{version}.src.rock" +@download_router.route("pkg:conda/.*") +def build_conda_download_url(purl): + """ + Resolve a Conda PURL to a real downloadable URL + + Supported qualifiers: + - channel: e.g., main, conda-forge (required for deterministic base) + - subdir: e.g., linux-64, osx-arm64, win-64, noarch + - build: exact build string (optional but recommended) + - type: 'conda' or 'tar.bz2' (preference; fallback to whichever exists) + """ + p = PackageURL.from_string(purl) + if not p.name or not p.version: + return None + + q = p.qualifiers or {} + name = p.name + version = p.version + build = q.get("build") + channel = q.get("channel") or "main" + subdir = q.get("subdir") or "noarch" + req_type = q.get("type") + + def _conda_base_for_channel(channel: str) -> str: + """ + Map a conda channel to its base URL. + - 'main' / 'defaults' -> repo.anaconda.com + - any other channel -> conda.anaconda.org/ + """ + ch = (channel or "").lower() + if ch in ("main", "defaults"): + return "https://repo.anaconda.com/pkgs/main" + return f"https://conda.anaconda.org/{ch}" + + base = _conda_base_for_channel(channel) + + package_identifier = ( + f"{name}-{version}-{build}.{req_type}" if build else f"{name}-{version}.{req_type}" + ) + + download_url = f"{base}/{subdir}/{package_identifier}" + return download_url + + def get_repo_download_url(purl): """ Return ``download_url`` if present in ``purl`` qualifiers or diff --git a/tests/contrib/test_purl2url.py b/tests/contrib/test_purl2url.py index 8da188c..2ace01c 100644 --- a/tests/contrib/test_purl2url.py +++ b/tests/contrib/test_purl2url.py @@ -107,6 +107,7 @@ def test_purl2url_get_download_url(): "pkg:swift/github.com/RxSwiftCommunity/RxFlow@2.12.4": "https://github.com/RxSwiftCommunity/RxFlow/archive/2.12.4.zip", "pkg:luarocks/luasocket@3.1.0-1": "https://luarocks.org/luasocket-3.1.0-1.src.rock", "pkg:luarocks/hisham/luafilesystem@1.8.0-1": "https://luarocks.org/luafilesystem-1.8.0-1.src.rock", + "pkg:conda/absl-py@0.4.1?build=py36h06a4308_0&channel=main&subdir=linux-64&type=tar.bz2": "https://repo.anaconda.com/pkgs/main/linux-64/absl-py-0.4.1-py36h06a4308_0.tar.bz2", # From `download_url` qualifier "pkg:github/yarnpkg/yarn@1.3.2?download_url=https://github.com/yarnpkg/yarn/releases/download/v1.3.2/yarn-v1.3.2.tar.gz&version_prefix=v": "https://github.com/yarnpkg/yarn/releases/download/v1.3.2/yarn-v1.3.2.tar.gz", "pkg:generic/lxc-master.tar.gz?download_url=https://salsa.debian.org/lxc-team/lxc/-/archive/master/lxc-master.tar.gz": "https://salsa.debian.org/lxc-team/lxc/-/archive/master/lxc-master.tar.gz", From 70546e3c8d042762b9199961e9727a682186f806 Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Thu, 31 Jul 2025 18:53:09 +0530 Subject: [PATCH 3/5] Add repo url support for luarocks Signed-off-by: Tushar Goel --- src/packageurl/contrib/purl2url.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/packageurl/contrib/purl2url.py b/src/packageurl/contrib/purl2url.py index aef93b5..217e560 100644 --- a/src/packageurl/contrib/purl2url.py +++ b/src/packageurl/contrib/purl2url.py @@ -520,11 +520,15 @@ def build_luarocks_download_url(purl): """ purl_data = PackageURL.from_string(purl) + qualifiers = purl_data.qualifiers or {} + + repository_url = qualifiers.get("repository_url", "https://luarocks.org") + name = purl_data.name version = purl_data.version if name and version: - return f"https://luarocks.org/{name}-{version}.src.rock" + return f"{repository_url}/{name}-{version}.src.rock" @download_router.route("pkg:conda/.*") From f82e957273a511c4b9049806438797a1b70fb1f2 Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Thu, 31 Jul 2025 19:23:08 +0530 Subject: [PATCH 4/5] Build alpm download URL Signed-off-by: Tushar Goel --- src/packageurl/contrib/purl2url.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/packageurl/contrib/purl2url.py b/src/packageurl/contrib/purl2url.py index 217e560..151cd2b 100644 --- a/src/packageurl/contrib/purl2url.py +++ b/src/packageurl/contrib/purl2url.py @@ -575,6 +575,21 @@ def _conda_base_for_channel(channel: str) -> str: return download_url +@download_router.route("pkg:alpm/.*") +def build_alpm_download_url(purl_str): + purl = PackageURL.from_string(purl_str) + name = purl.name + version = purl.version + arch = purl.qualifiers.get("arch", "any") + + if not name or not version: + return None + + first_letter = name[0] + url = f"https://archive.archlinux.org/packages/{first_letter}/{name}/{name}-{version}-{arch}.pkg.tar.zst" + return url + + def get_repo_download_url(purl): """ Return ``download_url`` if present in ``purl`` qualifiers or From cc3c4567c9037bb81d8ed2a9fa3bd4b51d192c50 Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Thu, 31 Jul 2025 19:52:59 +0530 Subject: [PATCH 5/5] Add tests Signed-off-by: Tushar Goel --- tests/contrib/test_purl2url.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/contrib/test_purl2url.py b/tests/contrib/test_purl2url.py index 2ace01c..7585670 100644 --- a/tests/contrib/test_purl2url.py +++ b/tests/contrib/test_purl2url.py @@ -108,6 +108,7 @@ def test_purl2url_get_download_url(): "pkg:luarocks/luasocket@3.1.0-1": "https://luarocks.org/luasocket-3.1.0-1.src.rock", "pkg:luarocks/hisham/luafilesystem@1.8.0-1": "https://luarocks.org/luafilesystem-1.8.0-1.src.rock", "pkg:conda/absl-py@0.4.1?build=py36h06a4308_0&channel=main&subdir=linux-64&type=tar.bz2": "https://repo.anaconda.com/pkgs/main/linux-64/absl-py-0.4.1-py36h06a4308_0.tar.bz2", + "pkg:alpm/arch/pacman@6.0.1-1?arch=x86_64": "https://archive.archlinux.org/packages/p/pacman/pacman-6.0.1-1-x86_64.pkg.tar.zst", # From `download_url` qualifier "pkg:github/yarnpkg/yarn@1.3.2?download_url=https://github.com/yarnpkg/yarn/releases/download/v1.3.2/yarn-v1.3.2.tar.gz&version_prefix=v": "https://github.com/yarnpkg/yarn/releases/download/v1.3.2/yarn-v1.3.2.tar.gz", "pkg:generic/lxc-master.tar.gz?download_url=https://salsa.debian.org/lxc-team/lxc/-/archive/master/lxc-master.tar.gz": "https://salsa.debian.org/lxc-team/lxc/-/archive/master/lxc-master.tar.gz",