Some checks failed
particle-os CI / Test particle-os (push) Failing after 2s
particle-os CI / Integration Test (push) Has been skipped
particle-os CI / Security & Quality (push) Failing after 1s
Test particle-os Basic Functionality / test-basic (push) Failing after 1s
Tests / test (1.21.x) (push) Failing after 1s
Tests / test (1.22.x) (push) Failing after 1s
particle-os CI / Build and Release (push) Has been skipped
266 lines
10 KiB
Python
266 lines
10 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Integration script between apt-ostree and deb-bootc-image-builder
|
|
|
|
This script demonstrates the complete Debian Atomic pipeline:
|
|
1. apt-ostree creates OSTree commits from treefiles
|
|
2. deb-bootc-image-builder creates bootable images from OSTree commits
|
|
|
|
This mimics the Fedora Atomic flow: treefiles → rpm-ostree → bootc-image-builder
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import subprocess
|
|
import json
|
|
import tempfile
|
|
import shutil
|
|
from pathlib import Path
|
|
from typing import Dict, Any, Optional
|
|
|
|
class DebianAtomicPipeline:
|
|
"""Complete Debian Atomic pipeline integration"""
|
|
|
|
def __init__(self, workspace_path: str):
|
|
self.workspace_path = Path(workspace_path)
|
|
self.apt_ostree_path = self.workspace_path / "apt-ostree"
|
|
self.deb_bootc_path = self.workspace_path / "deb-bootc-image-builder"
|
|
self.atomic_configs_path = self.workspace_path / "debian-atomic-configs"
|
|
|
|
# Validate paths
|
|
self._validate_paths()
|
|
|
|
# Temporary working directory
|
|
self.temp_dir = Path(tempfile.mkdtemp(prefix="debian-atomic-"))
|
|
print(f"Working directory: {self.temp_dir}")
|
|
|
|
def _validate_paths(self):
|
|
"""Validate that all required components exist"""
|
|
required_paths = [
|
|
self.apt_ostree_path,
|
|
self.deb_bootc_path,
|
|
self.atomic_configs_path
|
|
]
|
|
|
|
for path in required_paths:
|
|
if not path.exists():
|
|
raise FileNotFoundError(f"Required path not found: {path}")
|
|
|
|
print("✅ All required components found")
|
|
|
|
def create_ostree_commit(self, treefile: str, variant: str) -> Dict[str, Any]:
|
|
"""Step 1: Use apt-ostree to create OSTree commit from treefile"""
|
|
print(f"\n🔧 Step 1: Creating OSTree commit from {treefile}")
|
|
|
|
# Build apt-ostree if needed
|
|
apt_ostree_bin = self.apt_ostree_path / "target" / "debug" / "apt-ostree"
|
|
if not apt_ostree_bin.exists():
|
|
print("Building apt-ostree...")
|
|
self._build_apt_ostree()
|
|
|
|
# Create OSTree commit
|
|
treefile_path = self.atomic_configs_path / "treefiles" / treefile
|
|
if not treefile_path.exists():
|
|
raise FileNotFoundError(f"Treefile not found: {treefile_path}")
|
|
|
|
# Run apt-ostree compose
|
|
result = subprocess.run([
|
|
str(apt_ostree_bin), "compose", str(treefile_path)
|
|
], capture_output=True, text=True, cwd=self.temp_dir)
|
|
|
|
if result.returncode != 0:
|
|
print(f"Error creating OSTree commit: {result.stderr}")
|
|
raise RuntimeError("Failed to create OSTree commit")
|
|
|
|
print("✅ OSTree commit created successfully")
|
|
|
|
# Extract commit information
|
|
commit_info = self._extract_commit_info()
|
|
return commit_info
|
|
|
|
def _build_apt_ostree(self):
|
|
"""Build apt-ostree binary"""
|
|
print("Building apt-ostree...")
|
|
result = subprocess.run([
|
|
"cargo", "build", "--bin", "apt-ostree"
|
|
], cwd=self.apt_ostree_path, capture_output=True, text=True)
|
|
|
|
if result.returncode != 0:
|
|
print(f"Build failed: {result.stderr}")
|
|
raise RuntimeError("Failed to build apt-ostree")
|
|
|
|
print("✅ apt-ostree built successfully")
|
|
|
|
def _extract_commit_info(self) -> Dict[str, Any]:
|
|
"""Extract commit information from apt-ostree output"""
|
|
# Look for the repository created by apt-ostree
|
|
repo_path = self.temp_dir / "apt-ostree-repo"
|
|
if not repo_path.exists():
|
|
raise RuntimeError("OSTree repository not found")
|
|
|
|
# Get commit information
|
|
commits_dir = repo_path / "commits"
|
|
if commits_dir.exists():
|
|
commits = list(commits_dir.iterdir())
|
|
if commits:
|
|
commit_id = commits[0].name
|
|
print(f"Found commit: {commit_id}")
|
|
|
|
# Read metadata
|
|
metadata_file = commits[0] / "metadata.json"
|
|
if metadata_file.exists():
|
|
with open(metadata_file) as f:
|
|
metadata = json.load(f)
|
|
|
|
return {
|
|
"commit_id": commit_id,
|
|
"repo_path": str(repo_path),
|
|
"metadata": metadata
|
|
}
|
|
|
|
raise RuntimeError("No commits found in repository")
|
|
|
|
def create_bootable_image(self, commit_info: Dict[str, Any], output_format: str = "raw") -> str:
|
|
"""Step 2: Use deb-bootc-image-builder to create bootable image from OSTree commit"""
|
|
print(f"\n🔧 Step 2: Creating bootable image from OSTree commit")
|
|
|
|
# Check if deb-bootc-image-builder binary exists
|
|
bib_bin = self.deb_bootc_path / "bib" / "bootc-image-builder"
|
|
if not bib_bin.exists():
|
|
print("Building deb-bootc-image-builder...")
|
|
self._build_deb_bootc()
|
|
|
|
# Create a recipe that uses the OSTree commit
|
|
recipe = self._create_ostree_recipe(commit_info)
|
|
recipe_path = self.temp_dir / "ostree-recipe.yml"
|
|
|
|
with open(recipe_path, 'w') as f:
|
|
json.dump(recipe, f, indent=2)
|
|
|
|
print(f"Created recipe: {recipe_path}")
|
|
|
|
# Run deb-bootc-image-builder
|
|
output_image = self.temp_dir / f"debian-atomic-{output_format}.img"
|
|
|
|
result = subprocess.run([
|
|
str(bib_bin), "build", str(recipe_path), "--output", str(output_image)
|
|
], capture_output=True, text=True, cwd=self.temp_dir)
|
|
|
|
if result.returncode != 0:
|
|
print(f"Error creating bootable image: {result.stderr}")
|
|
raise RuntimeError("Failed to create bootable image")
|
|
|
|
print(f"✅ Bootable image created: {output_image}")
|
|
return str(output_image)
|
|
|
|
def _build_deb_bootc(self):
|
|
"""Build deb-bootc-image-builder"""
|
|
print("Building deb-bootc-image-builder...")
|
|
result = subprocess.run([
|
|
"go", "build", "-o", "bib/bootc-image-builder", "./cmd/bootc-image-builder"
|
|
], cwd=self.deb_bootc_path, capture_output=True, text=True)
|
|
|
|
if result.returncode != 0:
|
|
print(f"Build failed: {result.stderr}")
|
|
raise RuntimeError("Failed to build deb-bootc-image-builder")
|
|
|
|
print("✅ deb-bootc-image-builder built successfully")
|
|
|
|
def _create_ostree_recipe(self, commit_info: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""Create a recipe that uses the OSTree commit"""
|
|
return {
|
|
"name": "debian-atomic-ostree",
|
|
"description": "Debian Atomic system built from OSTree commit",
|
|
"base_image": "debian:trixie-slim",
|
|
"stages": [
|
|
{
|
|
"name": "ostree_integration",
|
|
"type": "ostree",
|
|
"config": {
|
|
"commit_id": commit_info["commit_id"],
|
|
"repo_path": commit_info["repo_path"],
|
|
"branch": commit_info["metadata"].get("branch", "debian-atomic")
|
|
}
|
|
},
|
|
{
|
|
"name": "system_config",
|
|
"type": "system",
|
|
"config": {
|
|
"hostname": "debian-atomic",
|
|
"timezone": "UTC",
|
|
"locale": "en_US.UTF-8"
|
|
}
|
|
}
|
|
],
|
|
"output": {
|
|
"format": "raw",
|
|
"size": "5G"
|
|
}
|
|
}
|
|
|
|
def run_complete_pipeline(self, treefile: str, variant: str, output_format: str = "raw") -> str:
|
|
"""Run the complete Debian Atomic pipeline"""
|
|
print(f"🚀 Starting Debian Atomic pipeline")
|
|
print(f"Treefile: {treefile}")
|
|
print(f"Variant: {variant}")
|
|
print(f"Output format: {output_format}")
|
|
|
|
try:
|
|
# Step 1: Create OSTree commit
|
|
commit_info = self.create_ostree_commit(treefile, variant)
|
|
|
|
# Step 2: Create bootable image
|
|
output_image = self.create_bootable_image(commit_info, output_format)
|
|
|
|
print(f"\n🎉 Pipeline completed successfully!")
|
|
print(f"Output image: {output_image}")
|
|
|
|
return output_image
|
|
|
|
except Exception as e:
|
|
print(f"❌ Pipeline failed: {e}")
|
|
raise
|
|
|
|
def cleanup(self):
|
|
"""Clean up temporary files"""
|
|
if self.temp_dir.exists():
|
|
shutil.rmtree(self.temp_dir)
|
|
print(f"Cleaned up: {self.temp_dir}")
|
|
|
|
def main():
|
|
"""Main function to demonstrate the integration"""
|
|
if len(sys.argv) < 2:
|
|
print("Usage: python3 integrate-with-apt-ostree.py <treefile> [variant] [output_format]")
|
|
print("Example: python3 integrate-with-apt-ostree.py minimal.yaml minimal raw")
|
|
sys.exit(1)
|
|
|
|
treefile = sys.argv[1]
|
|
variant = sys.argv[2] if len(sys.argv) > 2 else "minimal"
|
|
output_format = sys.argv[3] if len(sys.argv) > 3 else "raw"
|
|
|
|
# Get workspace path (assuming script is in deb-bootc-image-builder)
|
|
script_dir = Path(__file__).parent
|
|
workspace_path = script_dir.parent
|
|
|
|
try:
|
|
pipeline = DebianAtomicPipeline(str(workspace_path))
|
|
output_image = pipeline.run_complete_pipeline(treefile, variant, output_format)
|
|
|
|
print(f"\n📋 Summary:")
|
|
print(f"✅ OSTree commit created from {treefile}")
|
|
print(f"✅ Bootable image created: {output_image}")
|
|
print(f"✅ Debian Atomic pipeline completed successfully!")
|
|
|
|
print(f"\n🧪 To test the image:")
|
|
print(f"qemu-system-x86_64 -m 2G -drive file={output_image},format=raw -nographic")
|
|
|
|
except Exception as e:
|
|
print(f"❌ Pipeline failed: {e}")
|
|
sys.exit(1)
|
|
finally:
|
|
# Don't cleanup automatically for testing
|
|
# pipeline.cleanup()
|
|
pass
|
|
|
|
if __name__ == "__main__":
|
|
main()
|