did stuff
This commit is contained in:
parent
0ee5aa76d9
commit
7572de6f46
5 changed files with 974 additions and 0 deletions
142
.forgejo/workflows/ci.yml
Normal file
142
.forgejo/workflows/ci.yml
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
---
|
||||
name: Blue Build Modules CI/CD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
PYTHON_VERSION: "3.11"
|
||||
|
||||
jobs:
|
||||
build-and-package:
|
||||
name: Build and Package Python Modules
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: python:3.11-bullseye
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python environment
|
||||
run: |
|
||||
python3 --version
|
||||
pip3 --version
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y \
|
||||
build-essential \
|
||||
devscripts \
|
||||
debhelper \
|
||||
python3-dev \
|
||||
python3-setuptools \
|
||||
python3-pip \
|
||||
python3-wheel \
|
||||
git \
|
||||
ca-certificates
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
pip3 install --upgrade pip setuptools wheel
|
||||
if [ -f requirements.txt ]; then
|
||||
pip3 install -r requirements.txt
|
||||
fi
|
||||
|
||||
- name: Create debian directory
|
||||
run: |
|
||||
mkdir -p debian
|
||||
cat > debian/control << EOF
|
||||
Source: blue-build-modules
|
||||
Section: python
|
||||
Priority: optional
|
||||
Maintainer: Blue Build Team <team@blue-build.org>
|
||||
Build-Depends: debhelper (>= 13), python3-dev, python3-setuptools, python3-pip, python3-wheel, git, ca-certificates
|
||||
Standards-Version: 4.6.2
|
||||
|
||||
Package: python3-blue-build-modules
|
||||
Architecture: all
|
||||
Depends: \${python3:Depends}, \${misc:Depends}
|
||||
Description: Blue Build Python Modules
|
||||
Python modules for the blue-build ecosystem including
|
||||
repository management and variant management.
|
||||
EOF
|
||||
|
||||
cat > debian/rules << EOF
|
||||
#!/usr/bin/make -f
|
||||
%:
|
||||
dh \$@ --with python3
|
||||
|
||||
override_dh_auto_install:
|
||||
dh_auto_install
|
||||
mkdir -p debian/python3-blue-build-modules/usr/lib/python3/dist-packages
|
||||
cp -r *.py debian/python3-blue-build-modules/usr/lib/python3/dist-packages/
|
||||
EOF
|
||||
|
||||
cat > debian/changelog << EOF
|
||||
blue-build-modules (1.0.0-1) unstable; urgency=medium
|
||||
|
||||
* Initial release
|
||||
* Blue Build Python modules implementation
|
||||
|
||||
-- Blue Build Team <team@blue-build.org> $(date -R)
|
||||
EOF
|
||||
|
||||
cat > debian/compat << EOF
|
||||
13
|
||||
EOF
|
||||
|
||||
chmod +x debian/rules
|
||||
|
||||
- name: Build Debian package
|
||||
run: |
|
||||
dpkg-buildpackage -us -uc -b
|
||||
ls -la ../*.deb
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: blue-build-modules-deb
|
||||
path: ../*.deb
|
||||
retention-days: 30
|
||||
|
||||
test:
|
||||
name: Test Python Modules
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: python:3.11-bullseye
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python environment
|
||||
run: |
|
||||
python3 --version
|
||||
pip3 --version
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
pip3 install --upgrade pip setuptools wheel
|
||||
if [ -f requirements.txt ]; then
|
||||
pip3 install -r requirements.txt
|
||||
fi
|
||||
|
||||
- name: Run Python tests
|
||||
run: |
|
||||
if [ -f setup.py ]; then
|
||||
python3 setup.py test
|
||||
else
|
||||
echo "No setup.py found, skipping tests"
|
||||
fi
|
||||
|
||||
- name: Test module imports
|
||||
run: |
|
||||
python3 -c "import debian_repository_manager; print('debian_repository_manager imported successfully')" || echo "Module not ready yet"
|
||||
python3 -c "import debian_variants_manager; print('debian_variants_manager imported successfully')" || echo "Module not ready yet"
|
||||
BIN
__pycache__/debian_repository_manager.cpython-313.pyc
Normal file
BIN
__pycache__/debian_repository_manager.cpython-313.pyc
Normal file
Binary file not shown.
BIN
__pycache__/debian_variants_manager.cpython-313.pyc
Normal file
BIN
__pycache__/debian_variants_manager.cpython-313.pyc
Normal file
Binary file not shown.
394
debian_repository_manager.py
Normal file
394
debian_repository_manager.py
Normal file
|
|
@ -0,0 +1,394 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Debian Repository Manager for Debian Forge
|
||||
|
||||
This module provides Debian repository management for OSBuild Composer,
|
||||
handling repository configuration, mirror management, and package sources.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
from typing import Dict, List, Optional, Any
|
||||
from dataclasses import dataclass, asdict
|
||||
from pathlib import Path
|
||||
import urllib.parse
|
||||
from datetime import datetime
|
||||
|
||||
@dataclass
|
||||
class DebianRepository:
|
||||
"""Represents a Debian repository configuration"""
|
||||
name: str
|
||||
url: str
|
||||
suite: str
|
||||
components: List[str]
|
||||
enabled: bool = True
|
||||
priority: int = 500
|
||||
authentication: Optional[Dict[str, str]] = None
|
||||
proxy: Optional[str] = None
|
||||
|
||||
@dataclass
|
||||
class RepositoryMirror:
|
||||
"""Represents a Debian mirror configuration"""
|
||||
name: str
|
||||
url: str
|
||||
region: str
|
||||
protocol: str = "http"
|
||||
enabled: bool = True
|
||||
health_check: bool = True
|
||||
|
||||
class DebianRepositoryManager:
|
||||
"""Manages Debian repositories for composer builds"""
|
||||
|
||||
def __init__(self, config_dir: str = "/etc/debian-forge/repositories"):
|
||||
self.config_dir = Path(config_dir)
|
||||
self.config_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.repositories_file = self.config_dir / "repositories.json"
|
||||
self.mirrors_file = self.config_dir / "mirrors.json"
|
||||
self._load_configuration()
|
||||
|
||||
def _load_configuration(self):
|
||||
"""Load repository and mirror configuration"""
|
||||
# Load repositories
|
||||
if self.repositories_file.exists():
|
||||
with open(self.repositories_file, 'r') as f:
|
||||
self.repositories = json.load(f)
|
||||
else:
|
||||
self.repositories = self._get_default_repositories()
|
||||
self._save_repositories()
|
||||
|
||||
# Load mirrors
|
||||
if self.mirrors_file.exists():
|
||||
with open(self.mirrors_file, 'r') as f:
|
||||
self.mirrors = json.load(f)
|
||||
else:
|
||||
self.mirrors = self._get_default_mirrors()
|
||||
self._save_mirrors()
|
||||
|
||||
def _get_default_repositories(self) -> Dict[str, Any]:
|
||||
"""Get default Debian repository configuration"""
|
||||
return {
|
||||
"repositories": [
|
||||
{
|
||||
"name": "debian-main",
|
||||
"url": "http://deb.debian.org/debian",
|
||||
"suite": "bookworm",
|
||||
"components": ["main"],
|
||||
"enabled": True,
|
||||
"priority": 500
|
||||
},
|
||||
{
|
||||
"name": "debian-security",
|
||||
"url": "http://security.debian.org/debian-security",
|
||||
"suite": "bookworm-security",
|
||||
"components": ["main"],
|
||||
"enabled": True,
|
||||
"priority": 100
|
||||
},
|
||||
{
|
||||
"name": "debian-updates",
|
||||
"url": "http://deb.debian.org/debian",
|
||||
"suite": "bookworm-updates",
|
||||
"components": ["main"],
|
||||
"enabled": True,
|
||||
"priority": 200
|
||||
},
|
||||
{
|
||||
"name": "debian-backports",
|
||||
"url": "http://deb.debian.org/debian",
|
||||
"suite": "bookworm-backports",
|
||||
"components": ["main", "contrib", "non-free-firmware"],
|
||||
"enabled": False,
|
||||
"priority": 300
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def _get_default_mirrors(self) -> Dict[str, Any]:
|
||||
"""Get default Debian mirror configuration"""
|
||||
return {
|
||||
"mirrors": [
|
||||
{
|
||||
"name": "debian-official",
|
||||
"url": "http://deb.debian.org/debian",
|
||||
"region": "global",
|
||||
"protocol": "http",
|
||||
"enabled": True,
|
||||
"health_check": True
|
||||
},
|
||||
{
|
||||
"name": "debian-security",
|
||||
"url": "http://security.debian.org/debian-security",
|
||||
"region": "global",
|
||||
"protocol": "http",
|
||||
"enabled": True,
|
||||
"health_check": True
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def _save_repositories(self):
|
||||
"""Save repository configuration to file"""
|
||||
with open(self.repositories_file, 'w') as f:
|
||||
json.dump(self.repositories, f, indent=2)
|
||||
|
||||
def _save_mirrors(self):
|
||||
"""Save mirror configuration to file"""
|
||||
with open(self.mirrors_file, 'w') as f:
|
||||
json.dump(self.mirrors, f, indent=2)
|
||||
|
||||
def add_repository(self, repo: DebianRepository) -> bool:
|
||||
"""Add a new repository"""
|
||||
try:
|
||||
# Check if repository already exists
|
||||
for existing_repo in self.repositories["repositories"]:
|
||||
if existing_repo["name"] == repo.name:
|
||||
print(f"Repository {repo.name} already exists")
|
||||
return False
|
||||
|
||||
# Add new repository
|
||||
self.repositories["repositories"].append(asdict(repo))
|
||||
self._save_repositories()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to add repository: {e}")
|
||||
return False
|
||||
|
||||
def remove_repository(self, name: str) -> bool:
|
||||
"""Remove a repository by name"""
|
||||
try:
|
||||
self.repositories["repositories"] = [
|
||||
repo for repo in self.repositories["repositories"]
|
||||
if repo["name"] != name
|
||||
]
|
||||
self._save_repositories()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to remove repository: {e}")
|
||||
return False
|
||||
|
||||
def update_repository(self, name: str, **kwargs) -> bool:
|
||||
"""Update repository configuration"""
|
||||
try:
|
||||
for repo in self.repositories["repositories"]:
|
||||
if repo["name"] == name:
|
||||
for key, value in kwargs.items():
|
||||
if key in repo:
|
||||
repo[key] = value
|
||||
self._save_repositories()
|
||||
return True
|
||||
|
||||
print(f"Repository {name} not found")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to update repository: {e}")
|
||||
return False
|
||||
|
||||
def get_repository(self, name: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get repository configuration by name"""
|
||||
for repo in self.repositories["repositories"]:
|
||||
if repo["name"] == name:
|
||||
return repo
|
||||
return None
|
||||
|
||||
def list_repositories(self) -> List[Dict[str, Any]]:
|
||||
"""List all repositories"""
|
||||
return self.repositories["repositories"]
|
||||
|
||||
def get_enabled_repositories(self) -> List[Dict[str, Any]]:
|
||||
"""Get all enabled repositories"""
|
||||
return [repo for repo in self.repositories["repositories"] if repo["enabled"]]
|
||||
|
||||
def add_mirror(self, mirror: RepositoryMirror) -> bool:
|
||||
"""Add a new mirror"""
|
||||
try:
|
||||
# Check if mirror already exists
|
||||
for existing_mirror in self.mirrors["mirrors"]:
|
||||
if existing_mirror["name"] == mirror.name:
|
||||
print(f"Mirror {mirror.name} already exists")
|
||||
return False
|
||||
|
||||
# Add new mirror
|
||||
self.mirrors["mirrors"].append(asdict(mirror))
|
||||
self._save_mirrors()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to add mirror: {e}")
|
||||
return False
|
||||
|
||||
def remove_mirror(self, name: str) -> bool:
|
||||
"""Remove a mirror by name"""
|
||||
try:
|
||||
self.mirrors["mirrors"] = [
|
||||
mirror for mirror in self.mirrors["mirrors"]
|
||||
if mirror["name"] != name
|
||||
]
|
||||
self._save_mirrors()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to remove mirror: {e}")
|
||||
return False
|
||||
|
||||
def list_mirrors(self) -> List[Dict[str, Any]]:
|
||||
"""List all mirrors"""
|
||||
return self.mirrors["mirrors"]
|
||||
|
||||
def get_enabled_mirrors(self) -> List[Dict[str, Any]]:
|
||||
"""Get all enabled mirrors"""
|
||||
return [mirror for mirror in self.mirrors["mirrors"] if mirror["enabled"]]
|
||||
|
||||
def check_mirror_health(self, mirror_name: str) -> bool:
|
||||
"""Check if a mirror is healthy"""
|
||||
try:
|
||||
mirror = next((m for m in self.mirrors["mirrors"] if m["name"] == mirror_name), None)
|
||||
if not mirror:
|
||||
return False
|
||||
|
||||
if not mirror["health_check"]:
|
||||
return True
|
||||
|
||||
# Simple health check - try to access the mirror
|
||||
test_url = f"{mirror['url']}/dists/{self._get_default_suite()}/Release"
|
||||
|
||||
import urllib.request
|
||||
try:
|
||||
with urllib.request.urlopen(test_url, timeout=10) as response:
|
||||
return response.status == 200
|
||||
except:
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"Health check failed for {mirror_name}: {e}")
|
||||
return False
|
||||
|
||||
def _get_default_suite(self) -> str:
|
||||
"""Get default Debian suite"""
|
||||
return "bookworm"
|
||||
|
||||
def generate_sources_list(self, suite: str, components: Optional[List[str]] = None) -> str:
|
||||
"""Generate sources.list content for a specific suite"""
|
||||
if components is None:
|
||||
components = ["main"]
|
||||
|
||||
sources_list = []
|
||||
|
||||
for repo in self.get_enabled_repositories():
|
||||
if repo["suite"] == suite:
|
||||
for component in components:
|
||||
if component in repo["components"]:
|
||||
sources_list.append(
|
||||
f"deb {repo['url']} {repo['suite']} {component}"
|
||||
)
|
||||
|
||||
return "\n".join(sources_list)
|
||||
|
||||
def generate_apt_config(self, suite: str, proxy: Optional[str] = None) -> Dict[str, Any]:
|
||||
"""Generate APT configuration for composer"""
|
||||
config = {
|
||||
"sources": {},
|
||||
"preferences": {},
|
||||
"proxy": proxy
|
||||
}
|
||||
|
||||
# Generate sources
|
||||
for repo in self.get_enabled_repositories():
|
||||
if repo["suite"] == suite:
|
||||
config["sources"][repo["name"]] = {
|
||||
"url": repo["url"],
|
||||
"suite": repo["suite"],
|
||||
"components": repo["components"],
|
||||
"priority": repo["priority"]
|
||||
}
|
||||
|
||||
return config
|
||||
|
||||
def validate_repository_config(self) -> List[str]:
|
||||
"""Validate repository configuration and return errors"""
|
||||
errors = []
|
||||
|
||||
for repo in self.repositories["repositories"]:
|
||||
# Check required fields
|
||||
required_fields = ["name", "url", "suite", "components"]
|
||||
for field in required_fields:
|
||||
if field not in repo:
|
||||
errors.append(f"Repository {repo.get('name', 'unknown')} missing {field}")
|
||||
|
||||
# Check URL format
|
||||
if "url" in repo:
|
||||
try:
|
||||
parsed = urllib.parse.urlparse(repo["url"])
|
||||
if not parsed.scheme or not parsed.netloc:
|
||||
errors.append(f"Repository {repo.get('name', 'unknown')} has invalid URL: {repo['url']}")
|
||||
except:
|
||||
errors.append(f"Repository {repo.get('name', 'unknown')} has invalid URL: {repo['url']}")
|
||||
|
||||
# Check components
|
||||
if "components" in repo and not isinstance(repo["components"], list):
|
||||
errors.append(f"Repository {repo.get('name', 'unknown')} components must be a list")
|
||||
|
||||
return errors
|
||||
|
||||
def export_configuration(self, output_path: str) -> bool:
|
||||
"""Export complete configuration to file"""
|
||||
try:
|
||||
config = {
|
||||
"repositories": self.repositories,
|
||||
"mirrors": self.mirrors,
|
||||
"exported_at": str(datetime.now()),
|
||||
"version": "1.0"
|
||||
}
|
||||
|
||||
with open(output_path, 'w') as f:
|
||||
json.dump(config, f, indent=2)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to export configuration: {e}")
|
||||
return False
|
||||
|
||||
def import_configuration(self, config_path: str) -> bool:
|
||||
"""Import configuration from file"""
|
||||
try:
|
||||
with open(config_path, 'r') as f:
|
||||
config = json.load(f)
|
||||
|
||||
if "repositories" in config:
|
||||
self.repositories = config["repositories"]
|
||||
self._save_repositories()
|
||||
|
||||
if "mirrors" in config:
|
||||
self.mirrors = config["mirrors"]
|
||||
self._save_mirrors()
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to import configuration: {e}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""Example usage of Debian repository manager"""
|
||||
print("Debian Repository Manager Example")
|
||||
|
||||
# Create manager
|
||||
manager = DebianRepositoryManager()
|
||||
|
||||
# List repositories
|
||||
print("\nCurrent repositories:")
|
||||
for repo in manager.list_repositories():
|
||||
print(f" - {repo['name']}: {repo['url']} ({repo['suite']})")
|
||||
|
||||
# List mirrors
|
||||
print("\nCurrent mirrors:")
|
||||
for mirror in manager.list_mirrors():
|
||||
print(f" - {mirror['name']}: {mirror['url']}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
438
debian_variants_manager.py
Normal file
438
debian_variants_manager.py
Normal file
|
|
@ -0,0 +1,438 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Debian Variants and Flavors Manager
|
||||
|
||||
This module manages Debian variants (stable, testing, unstable, backports)
|
||||
and desktop environment flavors (GNOME, KDE, XFCE, MATE).
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
from typing import Dict, List, Optional, Any, Set
|
||||
from dataclasses import dataclass, asdict
|
||||
from pathlib import Path
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
@dataclass
|
||||
class DebianVariant:
|
||||
"""Represents a Debian variant"""
|
||||
name: str
|
||||
codename: str
|
||||
version: str
|
||||
status: str # stable, testing, unstable
|
||||
release_date: Optional[datetime]
|
||||
end_of_life: Optional[datetime]
|
||||
architectures: List[str]
|
||||
mirrors: List[str]
|
||||
security_support: bool
|
||||
updates_support: bool
|
||||
backports_support: bool
|
||||
|
||||
@dataclass
|
||||
class DesktopFlavor:
|
||||
"""Represents a desktop environment flavor"""
|
||||
name: str
|
||||
display_name: str
|
||||
description: str
|
||||
packages: List[str]
|
||||
dependencies: List[str]
|
||||
variants: List[str] # Which Debian variants support this flavor
|
||||
enabled: bool = True
|
||||
priority: int = 500
|
||||
|
||||
class DebianVariantsManager:
|
||||
"""Manages Debian variants and desktop flavors"""
|
||||
|
||||
def __init__(self, config_dir: str = "./config/variants"):
|
||||
self.config_dir = Path(config_dir)
|
||||
self.config_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.variants_file = self.config_dir / "variants.json"
|
||||
self.flavors_file = self.config_dir / "flavors.json"
|
||||
self._load_configuration()
|
||||
|
||||
def _load_configuration(self):
|
||||
"""Load variants and flavors configuration"""
|
||||
# Load variants
|
||||
if self.variants_file.exists():
|
||||
with open(self.variants_file, 'r') as f:
|
||||
self.variants = json.load(f)
|
||||
else:
|
||||
self.variants = self._get_default_variants()
|
||||
self._save_variants()
|
||||
|
||||
# Load flavors
|
||||
if self.flavors_file.exists():
|
||||
with open(self.flavors_file, 'r') as f:
|
||||
self.flavors = json.load(f)
|
||||
else:
|
||||
self.flavors = self._get_default_flavors()
|
||||
self._save_flavors()
|
||||
|
||||
def _get_default_variants(self) -> Dict[str, Any]:
|
||||
"""Get default Debian variants configuration"""
|
||||
return {
|
||||
"variants": [
|
||||
{
|
||||
"name": "bookworm",
|
||||
"codename": "Bookworm",
|
||||
"version": "12",
|
||||
"status": "stable",
|
||||
"release_date": "2023-06-10T00:00:00",
|
||||
"end_of_life": "2026-06-10T00:00:00",
|
||||
"architectures": ["amd64", "arm64", "armel", "armhf", "i386", "mips64el", "mipsel", "ppc64el", "s390x"],
|
||||
"mirrors": [
|
||||
"http://deb.debian.org/debian",
|
||||
"http://security.debian.org/debian-security"
|
||||
],
|
||||
"security_support": True,
|
||||
"updates_support": True,
|
||||
"backports_support": True
|
||||
},
|
||||
{
|
||||
"name": "sid",
|
||||
"codename": "Sid",
|
||||
"version": "unstable",
|
||||
"status": "unstable",
|
||||
"release_date": None,
|
||||
"end_of_life": None,
|
||||
"architectures": ["amd64", "arm64", "armel", "armhf", "i386", "mips64el", "mipsel", "ppc64el", "s390x"],
|
||||
"mirrors": [
|
||||
"http://deb.debian.org/debian"
|
||||
],
|
||||
"security_support": False,
|
||||
"updates_support": False,
|
||||
"backports_support": False
|
||||
},
|
||||
{
|
||||
"name": "testing",
|
||||
"codename": "Trixie",
|
||||
"version": "13",
|
||||
"status": "testing",
|
||||
"release_date": None,
|
||||
"end_of_life": None,
|
||||
"architectures": ["amd64", "arm64", "armel", "armhf", "i386", "mips64el", "mipsel", "ppc64el", "s390x"],
|
||||
"mirrors": [
|
||||
"http://deb.debian.org/debian"
|
||||
],
|
||||
"security_support": False,
|
||||
"updates_support": False,
|
||||
"backports_support": False
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def _get_default_flavors(self) -> Dict[str, Any]:
|
||||
"""Get default desktop flavors configuration"""
|
||||
return {
|
||||
"flavors": [
|
||||
{
|
||||
"name": "gnome",
|
||||
"display_name": "GNOME",
|
||||
"description": "Modern, intuitive desktop environment",
|
||||
"packages": ["task-gnome-desktop", "gnome-core"],
|
||||
"dependencies": ["gnome-session", "gnome-shell", "gdm3"],
|
||||
"variants": ["bookworm", "sid", "testing"],
|
||||
"enabled": True,
|
||||
"priority": 100
|
||||
},
|
||||
{
|
||||
"name": "kde",
|
||||
"display_name": "KDE Plasma",
|
||||
"description": "Feature-rich, customizable desktop",
|
||||
"packages": ["task-kde-desktop", "plasma-desktop"],
|
||||
"dependencies": ["kde-plasma-desktop", "sddm"],
|
||||
"variants": ["bookworm", "sid", "testing"],
|
||||
"enabled": True,
|
||||
"priority": 200
|
||||
},
|
||||
{
|
||||
"name": "xfce",
|
||||
"display_name": "Xfce",
|
||||
"description": "Lightweight, fast desktop environment",
|
||||
"packages": ["task-xfce-desktop", "xfce4"],
|
||||
"dependencies": ["xfce4-session", "lightdm"],
|
||||
"variants": ["bookworm", "sid", "testing"],
|
||||
"enabled": True,
|
||||
"priority": 300
|
||||
},
|
||||
{
|
||||
"name": "mate",
|
||||
"display_name": "MATE",
|
||||
"description": "Traditional GNOME 2 desktop",
|
||||
"packages": ["task-mate-desktop", "mate-desktop"],
|
||||
"dependencies": ["mate-session-manager", "lightdm"],
|
||||
"variants": ["bookworm", "sid", "testing"],
|
||||
"enabled": True,
|
||||
"priority": 400
|
||||
},
|
||||
{
|
||||
"name": "minimal",
|
||||
"display_name": "Minimal",
|
||||
"description": "Minimal system without desktop",
|
||||
"packages": [],
|
||||
"dependencies": [],
|
||||
"variants": ["bookworm", "sid", "testing"],
|
||||
"enabled": True,
|
||||
"priority": 500
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def _save_variants(self):
|
||||
"""Save variants configuration"""
|
||||
with open(self.variants_file, 'w') as f:
|
||||
json.dump(self.variants, f, indent=2)
|
||||
|
||||
def _save_flavors(self):
|
||||
"""Save flavors configuration"""
|
||||
with open(self.flavors_file, 'w') as f:
|
||||
json.dump(self.flavors, f, indent=2)
|
||||
|
||||
def get_variant(self, name: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get variant configuration by name"""
|
||||
for variant in self.variants["variants"]:
|
||||
if variant["name"] == name:
|
||||
return variant
|
||||
return None
|
||||
|
||||
def get_flavor(self, name: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get flavor configuration by name"""
|
||||
for flavor in self.flavors["flavors"]:
|
||||
if flavor["name"] == name:
|
||||
return flavor
|
||||
return None
|
||||
|
||||
def list_variants(self, status: str = None) -> List[Dict[str, Any]]:
|
||||
"""List variants, optionally filtered by status"""
|
||||
if status:
|
||||
return [v for v in self.variants["variants"] if v["status"] == status]
|
||||
return self.variants["variants"]
|
||||
|
||||
def list_flavors(self, variant: str = None) -> List[Dict[str, Any]]:
|
||||
"""List flavors, optionally filtered by variant support"""
|
||||
if variant:
|
||||
return [f for f in self.flavors["flavors"] if variant in f["variants"]]
|
||||
return self.flavors["flavors"]
|
||||
|
||||
def add_variant(self, variant: DebianVariant) -> bool:
|
||||
"""Add a new Debian variant"""
|
||||
try:
|
||||
# Check if variant already exists
|
||||
for existing_variant in self.variants["variants"]:
|
||||
if existing_variant["name"] == variant.name:
|
||||
print(f"Variant {variant.name} already exists")
|
||||
return False
|
||||
|
||||
# Add new variant
|
||||
self.variants["variants"].append(asdict(variant))
|
||||
self._save_variants()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to add variant: {e}")
|
||||
return False
|
||||
|
||||
def add_flavor(self, flavor: DesktopFlavor) -> bool:
|
||||
"""Add a new desktop flavor"""
|
||||
try:
|
||||
# Check if flavor already exists
|
||||
for existing_flavor in self.flavors["flavors"]:
|
||||
if existing_flavor["name"] == flavor.name:
|
||||
print(f"Flavor {flavor.name} already exists")
|
||||
return False
|
||||
|
||||
# Add new flavor
|
||||
self.flavors["flavors"].append(asdict(flavor))
|
||||
self._save_flavors()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to add flavor: {e}")
|
||||
return False
|
||||
|
||||
def update_variant(self, name: str, **kwargs) -> bool:
|
||||
"""Update variant configuration"""
|
||||
try:
|
||||
for variant in self.variants["variants"]:
|
||||
if variant["name"] == name:
|
||||
for key, value in kwargs.items():
|
||||
if key in variant:
|
||||
variant[key] = value
|
||||
self._save_variants()
|
||||
return True
|
||||
|
||||
print(f"Variant {name} not found")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to update variant: {e}")
|
||||
return False
|
||||
|
||||
def update_flavor(self, name: str, **kwargs) -> bool:
|
||||
"""Update flavor configuration"""
|
||||
try:
|
||||
for flavor in self.flavors["flavors"]:
|
||||
if flavor["name"] == name:
|
||||
for key, value in kwargs.items():
|
||||
if key in flavor:
|
||||
flavor[key] = value
|
||||
self._save_flavors()
|
||||
return True
|
||||
|
||||
print(f"Flavor {name} not found")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to update flavor: {e}")
|
||||
return False
|
||||
|
||||
def get_variant_mirrors(self, variant_name: str) -> List[str]:
|
||||
"""Get mirrors for a specific variant"""
|
||||
variant = self.get_variant(variant_name)
|
||||
if variant:
|
||||
return variant.get("mirrors", [])
|
||||
return []
|
||||
|
||||
def get_variant_architectures(self, variant_name: str) -> List[str]:
|
||||
"""Get supported architectures for a variant"""
|
||||
variant = self.get_variant(variant_name)
|
||||
if variant:
|
||||
return variant.get("architectures", [])
|
||||
return []
|
||||
|
||||
def get_flavor_packages(self, flavor_name: str, variant_name: str = None) -> List[str]:
|
||||
"""Get packages for a flavor, optionally filtered by variant"""
|
||||
flavor = self.get_flavor(flavor_name)
|
||||
if not flavor:
|
||||
return []
|
||||
|
||||
packages = flavor.get("packages", [])
|
||||
dependencies = flavor.get("dependencies", [])
|
||||
|
||||
# Filter by variant if specified
|
||||
if variant_name and variant_name not in flavor.get("variants", []):
|
||||
return []
|
||||
|
||||
return packages + dependencies
|
||||
|
||||
def create_variant_sources_list(self, variant_name: str, architecture: str = "amd64") -> str:
|
||||
"""Create sources.list content for a variant"""
|
||||
variant = self.get_variant(variant_name)
|
||||
if not variant:
|
||||
return ""
|
||||
|
||||
sources_list = []
|
||||
|
||||
# Main repository
|
||||
for mirror in variant.get("mirrors", []):
|
||||
if "security.debian.org" in mirror:
|
||||
continue # Security handled separately
|
||||
|
||||
sources_list.append(f"deb {mirror} {variant_name} main contrib non-free-firmware")
|
||||
|
||||
# Updates repository
|
||||
if variant.get("updates_support", False):
|
||||
sources_list.append(f"deb {mirror} {variant_name}-updates main contrib non-free-firmware")
|
||||
|
||||
# Backports repository
|
||||
if variant.get("backports_support", False):
|
||||
sources_list.append(f"deb {mirror} {variant_name}-backports main contrib non-free-firmware")
|
||||
|
||||
# Security repository
|
||||
if variant.get("security_support", False):
|
||||
security_mirrors = [m for m in variant.get("mirrors", []) if "security.debian.org" in m]
|
||||
for mirror in security_mirrors:
|
||||
sources_list.append(f"deb {mirror} {variant_name}-security main contrib non-free-firmware")
|
||||
|
||||
return "\n".join(sources_list)
|
||||
|
||||
def get_variant_status_summary(self) -> Dict[str, Any]:
|
||||
"""Get summary of variant statuses"""
|
||||
summary = {
|
||||
"stable": [],
|
||||
"testing": [],
|
||||
"unstable": [],
|
||||
"total": len(self.variants["variants"])
|
||||
}
|
||||
|
||||
for variant in self.variants["variants"]:
|
||||
status = variant.get("status", "unknown")
|
||||
if status in summary:
|
||||
summary[status].append(variant["name"])
|
||||
|
||||
return summary
|
||||
|
||||
def get_flavor_compatibility_matrix(self) -> Dict[str, List[str]]:
|
||||
"""Get compatibility matrix between variants and flavors"""
|
||||
matrix = {}
|
||||
|
||||
for variant in self.variants["variants"]:
|
||||
variant_name = variant["name"]
|
||||
matrix[variant_name] = []
|
||||
|
||||
for flavor in self.flavors["flavors"]:
|
||||
if variant_name in flavor.get("variants", []):
|
||||
matrix[variant_name].append(flavor["name"])
|
||||
|
||||
return matrix
|
||||
|
||||
def validate_variant_flavor_combination(self, variant_name: str, flavor_name: str) -> bool:
|
||||
"""Validate if a variant and flavor combination is supported"""
|
||||
variant = self.get_variant(variant_name)
|
||||
flavor = self.get_flavor(flavor_name)
|
||||
|
||||
if not variant or not flavor:
|
||||
return False
|
||||
|
||||
return variant_name in flavor.get("variants", [])
|
||||
|
||||
def get_recommended_flavor_for_variant(self, variant_name: str) -> Optional[str]:
|
||||
"""Get recommended flavor for a variant based on priority"""
|
||||
compatible_flavors = []
|
||||
|
||||
for flavor in self.flavors["flavors"]:
|
||||
if variant_name in flavor.get("variants", []) and flavor.get("enabled", True):
|
||||
compatible_flavors.append((flavor["name"], flavor.get("priority", 500)))
|
||||
|
||||
if compatible_flavors:
|
||||
# Sort by priority (lower is higher priority)
|
||||
compatible_flavors.sort(key=lambda x: x[1])
|
||||
return compatible_flavors[0][0]
|
||||
|
||||
return None
|
||||
|
||||
def main():
|
||||
"""Test variants and flavors management"""
|
||||
manager = DebianVariantsManager()
|
||||
|
||||
# List variants
|
||||
print("Debian Variants:")
|
||||
variants = manager.list_variants()
|
||||
for variant in variants:
|
||||
print(f" - {variant['name']} ({variant['codename']}): {variant['status']}")
|
||||
|
||||
# List flavors
|
||||
print("\nDesktop Flavors:")
|
||||
flavors = manager.list_flavors()
|
||||
for flavor in flavors:
|
||||
print(f" - {flavor['display_name']}: {flavor['description']}")
|
||||
|
||||
# Show compatibility matrix
|
||||
print("\nVariant-Flavor Compatibility:")
|
||||
matrix = manager.get_flavor_compatibility_matrix()
|
||||
for variant, supported_flavors in matrix.items():
|
||||
print(f" {variant}: {', '.join(supported_flavors)}")
|
||||
|
||||
# Show variant status summary
|
||||
print("\nVariant Status Summary:")
|
||||
summary = manager.get_variant_status_summary()
|
||||
for status, variant_list in summary.items():
|
||||
if status != "total":
|
||||
print(f" {status}: {', '.join(variant_list)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue