aur-shield v0.1.0: AI-powered AUR firewall
- FastAPI server with scan/build/repo endpoints - LLM scanner (Ollama) with regex pre-scan - makepkg/devtools builder with chroot isolation - Scan cache with TTL + PKGBUILD hash - Client installer + safe-yay wrapper - Docs + config example
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
"""Package builder — runs makepkg in a clean environment."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
from .config import ServerConfig, BuildConfig
|
||||
|
||||
|
||||
@dataclass
|
||||
class BuildResult:
|
||||
success: bool
|
||||
pkg_files: list[str] # paths to .pkg.tar.zst
|
||||
log: str
|
||||
error: str = ""
|
||||
|
||||
|
||||
def build_package(
|
||||
pkgbuild: str,
|
||||
package_name: str,
|
||||
server_cfg: ServerConfig,
|
||||
build_cfg: BuildConfig,
|
||||
) -> BuildResult:
|
||||
"""Build a package from a PKGBUILD string."""
|
||||
|
||||
work_dir = Path(server_cfg.work_dir) / package_name
|
||||
work_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Write PKGBUILD
|
||||
pkgbuild_path = work_dir / "PKGBUILD"
|
||||
pkgbuild_path.write_text(pkgbuild)
|
||||
|
||||
# Clean previous build artifacts
|
||||
for f in work_dir.glob("*.pkg.tar.*"):
|
||||
f.unlink()
|
||||
|
||||
log_lines: list[str] = []
|
||||
|
||||
try:
|
||||
if build_cfg.use_devtools and shutil.which("extra-x86_64-build"):
|
||||
# Use devtools chroot for isolation
|
||||
cmd = [
|
||||
"extra-x86_64-build",
|
||||
"--", "-cC",
|
||||
]
|
||||
cwd = work_dir
|
||||
else:
|
||||
# Fallback: makepkg directly
|
||||
cmd = [
|
||||
"makepkg", "-sf", "--noconfirm", "--noprogressbar",
|
||||
]
|
||||
cwd = work_dir
|
||||
|
||||
log_lines.append(f"Building {package_name} with: {' '.join(cmd)}")
|
||||
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
cwd=cwd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=build_cfg.timeout,
|
||||
env={**os.environ, "MAKEFLAGS": "-j$(nproc)"},
|
||||
)
|
||||
|
||||
log_lines.append(result.stdout)
|
||||
if result.stderr:
|
||||
log_lines.append("--- stderr ---")
|
||||
log_lines.append(result.stderr)
|
||||
|
||||
if result.returncode != 0:
|
||||
return BuildResult(
|
||||
success=False,
|
||||
pkg_files=[],
|
||||
log="\n".join(log_lines),
|
||||
error=f"Build failed (exit {result.returncode})",
|
||||
)
|
||||
|
||||
# Find built packages
|
||||
pkg_files = [
|
||||
str(f) for f in work_dir.glob("*.pkg.tar.zst")
|
||||
]
|
||||
|
||||
if not pkg_files:
|
||||
return BuildResult(
|
||||
success=False,
|
||||
pkg_files=[],
|
||||
log="\n".join(log_lines),
|
||||
error="Build succeeded but no .pkg.tar.zst found",
|
||||
)
|
||||
|
||||
log_lines.append(f"Built: {pkg_files}")
|
||||
return BuildResult(
|
||||
success=True,
|
||||
pkg_files=pkg_files,
|
||||
log="\n".join(log_lines),
|
||||
)
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
return BuildResult(
|
||||
success=False,
|
||||
pkg_files=[],
|
||||
log="\n".join(log_lines),
|
||||
error=f"Build timed out after {build_cfg.timeout}s",
|
||||
)
|
||||
except Exception as e:
|
||||
return BuildResult(
|
||||
success=False,
|
||||
pkg_files=[],
|
||||
log="\n".join(log_lines),
|
||||
error=str(e),
|
||||
)
|
||||
|
||||
|
||||
def add_to_repo(
|
||||
pkg_files: list[str],
|
||||
repo_dir: str,
|
||||
) -> str:
|
||||
"""Add built packages to the local pacman repo using repo-add."""
|
||||
|
||||
repo_path = Path(repo_dir)
|
||||
repo_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
db_name = "aur-shield.db"
|
||||
db_file = repo_path / f"{db_name}.tar.gz"
|
||||
|
||||
# Copy packages to repo dir
|
||||
copied = []
|
||||
for pkg in pkg_files:
|
||||
src = Path(pkg)
|
||||
dst = repo_path / src.name
|
||||
shutil.copy2(src, dst)
|
||||
copied.append(str(dst))
|
||||
|
||||
if not copied:
|
||||
return str(db_file)
|
||||
|
||||
# Run repo-add
|
||||
cmd = ["repo-add", str(db_file)] + copied
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f"repo-add failed: {result.stderr}")
|
||||
|
||||
return str(db_file)
|
||||
Reference in New Issue
Block a user