init: guarddog-nexus project skeleton
This commit is contained in:
17
.env.example
Normal file
17
.env.example
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
NEXUS_URL=http://nexus:8081
|
||||||
|
NEXUS_USERNAME=admin
|
||||||
|
NEXUS_PASSWORD=admin123
|
||||||
|
NEXUS_REPOSITORIES=pypi-proxy
|
||||||
|
|
||||||
|
DATABASE_PATH=/data/guarddog.db
|
||||||
|
|
||||||
|
HOST=0.0.0.0
|
||||||
|
PORT=8080
|
||||||
|
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
LOG_SYSLOG_HOST=
|
||||||
|
LOG_SYSLOG_PORT=514
|
||||||
|
|
||||||
|
WEBHOOK_SECRET=
|
||||||
|
SCAN_TIMEOUT_SECONDS=300
|
||||||
|
TEMP_DIR=/tmp/guarddog-nexus
|
||||||
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.pytest_cache/
|
||||||
|
.ruff_cache/
|
||||||
|
.mypy_cache/
|
||||||
|
data/
|
||||||
|
*.db
|
||||||
|
.env
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
37
Makefile
Normal file
37
Makefile
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
.PHONY: install dev test lint format typecheck docker-build docker-up docker-down clean
|
||||||
|
|
||||||
|
install:
|
||||||
|
pip install -e .
|
||||||
|
|
||||||
|
dev:
|
||||||
|
pip install -e ".[dev]"
|
||||||
|
|
||||||
|
test:
|
||||||
|
pytest -v
|
||||||
|
|
||||||
|
lint:
|
||||||
|
ruff check guarddog_nexus tests
|
||||||
|
|
||||||
|
format:
|
||||||
|
ruff format guarddog_nexus tests
|
||||||
|
ruff check --fix guarddog_nexus tests
|
||||||
|
|
||||||
|
typecheck:
|
||||||
|
mypy guarddog_nexus
|
||||||
|
|
||||||
|
docker-build:
|
||||||
|
docker compose build
|
||||||
|
|
||||||
|
docker-up:
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
docker-down:
|
||||||
|
docker compose down -v
|
||||||
|
|
||||||
|
docker-logs:
|
||||||
|
docker compose logs -f
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf dist build *.egg-info
|
||||||
|
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
||||||
|
find . -type f -name '*.pyc' -delete
|
||||||
34
guarddog_nexus/config.py
Normal file
34
guarddog_nexus/config.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
"""Configuration via environment variables."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Config:
|
||||||
|
nexus_url: str = os.getenv("NEXUS_URL", "http://localhost:8081")
|
||||||
|
nexus_username: str = os.getenv("NEXUS_USERNAME", "admin")
|
||||||
|
nexus_password: str = os.getenv("NEXUS_PASSWORD", "admin123")
|
||||||
|
nexus_repositories: list[str] = field(default_factory=lambda: _parse_repos())
|
||||||
|
|
||||||
|
database_path: str = os.getenv("DATABASE_PATH", "data/guarddog.db")
|
||||||
|
|
||||||
|
host: str = os.getenv("HOST", "0.0.0.0")
|
||||||
|
port: int = int(os.getenv("PORT", "8080"))
|
||||||
|
|
||||||
|
log_level: str = os.getenv("LOG_LEVEL", "INFO")
|
||||||
|
log_syslog_host: str = os.getenv("LOG_SYSLOG_HOST", "")
|
||||||
|
log_syslog_port: int = int(os.getenv("LOG_SYSLOG_PORT", "514"))
|
||||||
|
|
||||||
|
webhook_secret: str = os.getenv("WEBHOOK_SECRET", "")
|
||||||
|
|
||||||
|
scan_timeout_seconds: int = int(os.getenv("SCAN_TIMEOUT_SECONDS", "300"))
|
||||||
|
temp_dir: str = os.getenv("TEMP_DIR", "/tmp/guarddog-nexus")
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_repos() -> list[str]:
|
||||||
|
raw = os.getenv("NEXUS_REPOSITORIES", "")
|
||||||
|
return [r.strip() for r in raw.split(",") if r.strip()]
|
||||||
|
|
||||||
|
|
||||||
|
config = Config()
|
||||||
27
guarddog_nexus/database.py
Normal file
27
guarddog_nexus/database.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
"""Async SQLite database setup via SQLAlchemy."""
|
||||||
|
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
||||||
|
from sqlalchemy.orm import DeclarativeBase
|
||||||
|
|
||||||
|
from guarddog_nexus.config import config
|
||||||
|
|
||||||
|
DATABASE_URL = f"sqlite+aiosqlite:///{config.database_path}"
|
||||||
|
|
||||||
|
_engine = create_async_engine(DATABASE_URL, echo=False)
|
||||||
|
_async_session = async_sessionmaker(_engine, class_=AsyncSession, expire_on_commit=False)
|
||||||
|
|
||||||
|
|
||||||
|
class Base(DeclarativeBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
async def init_db():
|
||||||
|
import guarddog_nexus.models # noqa: F401
|
||||||
|
|
||||||
|
async with _engine.begin() as conn:
|
||||||
|
await conn.run_sync(Base.metadata.create_all)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_session() -> AsyncSession:
|
||||||
|
async with _async_session() as session:
|
||||||
|
yield session
|
||||||
50
pyproject.toml
Normal file
50
pyproject.toml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "guarddog-nexus"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "GuardDog integration with Sonatype Nexus — scan packages via webhooks"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.10"
|
||||||
|
license = {text = "MIT"}
|
||||||
|
dependencies = [
|
||||||
|
"fastapi>=0.115.0",
|
||||||
|
"uvicorn[standard]>=0.30.0",
|
||||||
|
"jinja2>=3.1.0",
|
||||||
|
"httpx>=0.27.0",
|
||||||
|
"sqlalchemy[asyncio]>=2.0.30",
|
||||||
|
"aiosqlite>=0.20.0",
|
||||||
|
"python-multipart>=0.0.9",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
dev = [
|
||||||
|
"pytest>=8.0",
|
||||||
|
"pytest-asyncio>=0.23.0",
|
||||||
|
"pytest-httpx>=0.30.0",
|
||||||
|
"ruff>=0.4.0",
|
||||||
|
"mypy>=1.10.0",
|
||||||
|
"httpx>=0.27.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
guarddog-nexus = "guarddog_nexus.main:main"
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
target-version = "py310"
|
||||||
|
line-length = 100
|
||||||
|
select = ["E", "F", "I", "W"]
|
||||||
|
|
||||||
|
[tool.ruff.lint.isort]
|
||||||
|
known-first-party = ["guarddog_nexus"]
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
python_version = "3.10"
|
||||||
|
strict = true
|
||||||
|
ignore_missing_imports = true
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
asyncio_mode = "auto"
|
||||||
|
testpaths = ["tests"]
|
||||||
Reference in New Issue
Block a user