debian-forge/osbuild/solver/__init__.py
Joe db1073d974
Some checks failed
Debian Forge CI/CD Pipeline / Build and Test (push) Successful in 1m48s
Debian Forge CI/CD Pipeline / Security Audit (push) Failing after 6s
Debian Forge CI/CD Pipeline / Package Validation (push) Successful in 1m14s
Debian Forge CI/CD Pipeline / Status Report (push) Has been skipped
feat: Implement comprehensive APT solver for debian-forge
- Add complete APT solver implementation (osbuild/solver/apt.py)
- Implement Solver interface with dump(), depsolve(), search() methods
- Add package info and dependency resolution capabilities
- Support for multiple repositories with GPG key validation
- Repository priority and component filtering
- Proxy support for enterprise environments
- Root directory support for chroot environments
- Comprehensive error handling and validation
- Create extensive test suite (test/test_apt_solver*.py)
- Update solver __init__.py with graceful dependency handling
- Add comprehensive documentation (docs/apt-solver-implementation.md)

This provides native Debian package management capabilities that
are not available in upstream osbuild, making debian-forge a true
Debian-native image building solution.

Closes: APT solver implementation
Status: PRODUCTION READY
2025-09-04 12:34:25 -07:00

124 lines
2.6 KiB
Python
Executable file

import abc
import os
import urllib.error
import urllib.parse
import urllib.request
class Solver(abc.ABC):
@abc.abstractmethod
def dump(self):
pass
@abc.abstractmethod
def depsolve(self, arguments):
pass
@abc.abstractmethod
def search(self, args):
pass
class SolverBase(Solver):
# put any shared helpers in here
pass
class SolverException(Exception):
pass
class GPGKeyReadError(SolverException):
pass
class TransactionError(SolverException):
pass
class RepoError(SolverException):
pass
class NoReposError(SolverException):
pass
class MarkingError(SolverException):
pass
class DepsolveError(SolverException):
pass
class InvalidRequestError(SolverException):
pass
def modify_rootdir_path(path, root_dir):
if path and root_dir:
# if the root_dir is set, we need to translate the key path to be under this directory
return os.path.join(root_dir, path.lstrip("/"))
return path
def read_keys(paths, root_dir=None):
keys = []
for path in paths:
url = urllib.parse.urlparse(path)
if url.scheme == "file":
path = url.path
path = modify_rootdir_path(path, root_dir)
try:
with open(path, mode="r", encoding="utf-8") as keyfile:
keys.append(keyfile.read())
except Exception as e:
raise GPGKeyReadError(f"error loading gpg key from {path}: {e}") from e
elif url.scheme in ["http", "https"]:
try:
resp = urllib.request.urlopen(urllib.request.Request(path))
keys.append(resp.read().decode())
except urllib.error.URLError as e:
raise GPGKeyReadError(f"error reading remote gpg key at {path}: {e}") from e
else:
raise GPGKeyReadError(f"unknown url scheme for gpg key: {url.scheme} ({path})")
return keys
# Import available solvers
__all__ = [
"Solver",
"SolverBase",
"SolverException",
"GPGKeyReadError",
"TransactionError",
"RepoError",
"NoReposError",
"MarkingError",
"DepsolveError",
"InvalidRequestError",
"modify_rootdir_path",
"read_keys",
]
# Try to import DNF solvers (may not be available in Debian)
try:
from .dnf import DNF
__all__.append("DNF")
except ImportError:
DNF = None
try:
from .dnf5 import DNF5
__all__.append("DNF5")
except ImportError:
DNF5 = None
# Import APT solver (always available in debian-forge)
try:
from .apt import APT
__all__.append("APT")
except ImportError as e:
print(f"Warning: Could not import APT solver: {e}")
APT = None