feat: поддержка Go и npm экосистем
- setup-nexus.sh: создание go-proxy (proxy.golang.org) и npm-proxy (registry.npmjs.org) - nexus_client.py: extract_go_info() и extract_npm_info() для парсинга путей Go: packages/github.com/gorilla/mux/@v/v1.8.0.zip → name=github.com/gorilla/mux ver=v1.8.0 npm: packages/lodash/-/lodash-4.17.21.tgz → name=lodash ver=4.17.21 - nexus_client.py: EXTRACTORS dict + extract_package_info() универсальный extractor - webhooks.py: _detect_ecosystem() — определяет экосистему из asset.format - harvester.py: использует extract_package_info() вместо extract_pypi_info() - Всё в Docker-контейнере, на хосте ничего не ставится - GuardDog поддерживает go и npm из коробки
This commit is contained in:
@@ -7,6 +7,7 @@ import httpx
|
||||
|
||||
from guarddog_nexus.config import config
|
||||
from guarddog_nexus.constants import (
|
||||
NPM_PATH_PREFIX,
|
||||
PYPI_PATH_PREFIX,
|
||||
SHA256_CHUNK_SIZE,
|
||||
)
|
||||
@@ -24,6 +25,70 @@ def extract_pypi_info(asset_path: str) -> tuple[str, str] | None:
|
||||
return None
|
||||
|
||||
|
||||
def extract_go_info(asset_path: str) -> tuple[str, str] | None:
|
||||
"""Extract module and version from a Go proxy asset path.
|
||||
|
||||
Path format: packages/github.com/gin-gonic/gin/@v/v1.9.0.zip
|
||||
"""
|
||||
cleaned = asset_path.strip("/")
|
||||
# Find @v/ marker
|
||||
idx = cleaned.find("/@v/")
|
||||
if idx == -1:
|
||||
return None
|
||||
if cleaned.startswith(PYPI_PATH_PREFIX + "/"):
|
||||
module = cleaned[len(PYPI_PATH_PREFIX) + 1 : idx]
|
||||
else:
|
||||
module = cleaned[:idx]
|
||||
if not module:
|
||||
return None
|
||||
# Version: after @v/ up to the next / or end
|
||||
ver_start = idx + 4 # len("/@v/")
|
||||
rest = cleaned[ver_start:]
|
||||
version = rest.split("/")[0] if "/" in rest else rest
|
||||
if version.endswith(".zip"):
|
||||
version = version[:-4]
|
||||
return module, version
|
||||
|
||||
|
||||
def extract_npm_info(asset_path: str) -> tuple[str, str] | None:
|
||||
"""Extract package name and version from an npm proxy asset path.
|
||||
|
||||
Path format: packages/react/-/react-18.2.0.tgz
|
||||
"""
|
||||
parts = asset_path.strip("/").split("/")
|
||||
if len(parts) < 4 or parts[0] != NPM_PATH_PREFIX:
|
||||
return None
|
||||
name = parts[1]
|
||||
# Last segment: <name>-<version>.tgz
|
||||
last = parts[-1]
|
||||
if last.startswith(name + "-"):
|
||||
raw = last[len(name) + 1 :]
|
||||
for ext in (".tgz", ".tar.gz"):
|
||||
if raw.endswith(ext):
|
||||
return name, raw[: -len(ext)]
|
||||
return None
|
||||
|
||||
|
||||
# Map of ecosystem → extractor function
|
||||
EXTRACTORS = {
|
||||
"pypi": extract_pypi_info,
|
||||
"go": extract_go_info,
|
||||
"npm": extract_npm_info,
|
||||
}
|
||||
|
||||
|
||||
def extract_package_info(asset_path: str, ecosystem: str) -> tuple[str, str] | None:
|
||||
"""Extract package name and version based on ecosystem."""
|
||||
extractor = EXTRACTORS.get(ecosystem)
|
||||
if extractor:
|
||||
return extractor(asset_path)
|
||||
# Fallback for unknown ecosystems — try simple parts split
|
||||
parts = asset_path.strip("/").split("/")
|
||||
if len(parts) >= 3:
|
||||
return parts[1], parts[2]
|
||||
return None
|
||||
|
||||
|
||||
async def download_asset(download_url: str, dest_dir: str) -> str | None:
|
||||
"""Download an asset from Nexus using async httpx."""
|
||||
dest_path = os.path.join(dest_dir, os.path.basename(download_url.split("?")[0]))
|
||||
|
||||
Reference in New Issue
Block a user