Add sbuild and debian source stages for Debian package building and source management
Some checks are pending
Checks / Spelling (push) Waiting to run
Checks / Python Linters (push) Waiting to run
Checks / Shell Linters (push) Waiting to run
Checks / 📦 Packit config lint (push) Waiting to run
Checks / 🔍 Check for valid snapshot urls (push) Waiting to run
Checks / 🔍 Check JSON files for formatting consistency (push) Waiting to run
Generate / Documentation (push) Waiting to run
Generate / Test Data (push) Waiting to run
Tests / Unittest (push) Waiting to run
Tests / Assembler test (legacy) (push) Waiting to run
Tests / Smoke run: unittest as normal user on default runner (push) Waiting to run

This commit is contained in:
robojerk 2025-08-22 18:22:25 -07:00
parent 6a17af5a62
commit 6a744c6c5b
4 changed files with 387 additions and 0 deletions

View file

@ -0,0 +1,132 @@
#!/usr/bin/python3
import os
import sys
import subprocess
import json
import tempfile
import shutil
import osbuild.api
def download_source_package(package_name, suite, mirror, output_dir):
"""Download Debian source package"""
# Create temporary directory for download
with tempfile.TemporaryDirectory() as temp_dir:
os.chdir(temp_dir)
# Use apt-get source to download package
cmd = ["apt-get", "source", package_name]
print(f"Downloading source package: {' '.join(cmd)}")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"Error downloading source package:")
print(f"stdout: {result.stdout}")
print(f"stderr: {result.stderr}")
return False
# Copy downloaded files to output directory
for file in os.listdir("."):
src = os.path.join(temp_dir, file)
dst = os.path.join(output_dir, file)
if os.path.isdir(file):
shutil.copytree(src, dst)
else:
shutil.copy2(src, dst)
print(f"Copied: {file}")
return True
def setup_apt_sources(tree, suite, mirror, components=None):
"""Set up apt sources for the specified suite"""
if components is None:
components = ["main", "contrib", "non-free"]
sources_content = f"""deb {mirror} {suite} {' '.join(components)}
deb {mirror} {suite}-updates {' '.join(components)}
deb {mirror} {suite}-security {' '.join(components)}
"""
sources_path = os.path.join(tree, "etc/apt/sources.list")
os.makedirs(os.path.dirname(sources_path), exist_ok=True)
with open(sources_path, "w") as f:
f.write(sources_content)
print(f"Created sources.list for {suite}")
def update_package_lists(tree):
"""Update package lists"""
cmd = ["chroot", tree, "apt-get", "update"]
print("Updating package lists...")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"Error updating package lists:")
print(f"stdout: {result.stdout}")
print(f"stderr: {result.stderr}")
return False
print("Package lists updated successfully")
return True
def main(tree, options):
"""Main function for debian source stage"""
# Get options
package_name = options.get("package")
suite = options.get("suite", "bookworm")
mirror = options.get("mirror", "http://deb.debian.org/debian")
components = options.get("components", ["main", "contrib", "non-free"])
output_dir = options.get("output_dir", ".")
if not package_name:
print("No package name specified")
return 1
# Create output directory
os.makedirs(output_dir, exist_ok=True)
# Set up apt sources
setup_apt_sources(tree, suite, mirror, components)
# Update package lists
if not update_package_lists(tree):
return 1
# Download source package
if not download_source_package(package_name, suite, mirror, output_dir):
return 1
# Write source info
source_info = {
"package": package_name,
"suite": suite,
"mirror": mirror,
"components": components,
"output_dir": output_dir
}
info_file = os.path.join(output_dir, "debian-source.json")
with open(info_file, "w") as f:
json.dump(source_info, f, indent=2)
print(f"Source package {package_name} downloaded successfully")
return 0
if __name__ == '__main__':
args = osbuild.api.arguments()
r = main(args["tree"], args["options"])
sys.exit(r)

View file

@ -0,0 +1,57 @@
{
"name": "org.osbuild.debian.source",
"version": "1",
"description": "Download and manage Debian source packages",
"options": {
"type": "object",
"properties": {
"package": {
"type": "string",
"description": "Name of the Debian source package to download"
},
"suite": {
"type": "string",
"default": "bookworm",
"description": "Debian suite for the package"
},
"mirror": {
"type": "string",
"default": "http://deb.debian.org/debian",
"description": "Debian mirror for package sources"
},
"components": {
"type": "array",
"items": {
"type": "string"
},
"default": ["main", "contrib", "non-free"],
"description": "Debian repository components to use"
},
"output_dir": {
"type": "string",
"default": ".",
"description": "Directory for downloaded source packages"
}
},
"required": ["package"]
},
"inputs": {
"type": "object",
"additionalProperties": false
},
"devices": {
"type": "object",
"additionalProperties": false
},
"mounts": {
"type": "object",
"additionalProperties": false
},
"capabilities": {
"type": "array",
"items": {
"type": "string"
},
"default": ["CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_FOWNER", "CAP_FSETID", "CAP_MKNOD", "CAP_SETGID", "CAP_SETUID"]
}
}

144
stages/org.osbuild.sbuild Normal file
View file

@ -0,0 +1,144 @@
#!/usr/bin/python3
import os
import sys
import subprocess
import tempfile
import shutil
import json
import osbuild.api
def run_sbuild_command(cmd, cwd=None, env=None):
"""Run sbuild command and return result"""
if env is None:
env = {}
result = subprocess.run(cmd, cwd=cwd, env=env, capture_output=True, text=True)
if result.returncode != 0:
print(f"Error running sbuild command: {' '.join(cmd)}")
print(f"stdout: {result.stdout}")
print(f"stderr: {result.stderr}")
return False, result.stderr
return True, result.stdout
def create_sbuild_chroot(tree, suite, arch, mirror):
"""Create sbuild chroot for building packages"""
chroot_name = f"{suite}-{arch}-sbuild"
# Create sbuild chroot
cmd = ["sbuild-createchroot", "--arch", arch, suite, chroot_name, mirror]
print(f"Creating sbuild chroot: {' '.join(cmd)}")
success, output = run_sbuild_command(cmd)
if not success:
return False, None
print(f"Created sbuild chroot: {chroot_name}")
return True, chroot_name
def build_package_with_sbuild(chroot_name, source_dir, output_dir):
"""Build package using sbuild"""
# Change to source directory
os.chdir(source_dir)
# Run sbuild
cmd = ["sbuild", "--arch", "amd64", "--dist", chroot_name.split("-")[0], "."]
print(f"Building package with sbuild: {' '.join(cmd)}")
success, output = run_sbuild_command(cmd)
if not success:
return False
# Copy built packages to output directory
for file in os.listdir("."):
if file.endswith(".deb") or file.endswith(".dsc") or file.endswith(".tar.gz"):
src = os.path.join(source_dir, file)
dst = os.path.join(output_dir, file)
shutil.copy2(src, dst)
print(f"Copied: {file}")
return True
def setup_sbuild_config(tree, chroot_name):
"""Set up sbuild configuration for the chroot"""
sbuild_conf_dir = f"{tree}/etc/sbuild"
os.makedirs(sbuild_conf_dir, exist_ok=True)
# Create sbuild configuration
sbuild_conf = f"""# sbuild configuration for {chroot_name}
$build_environment = 'chroot';
$build_arch_all = 1;
$build_source = 1;
$build_binary = 1;
$build_arch_any = 1;
$build_indep = 1;
$build_dep = 1;
$build_conf = 1;
$build_progress = 1;
$build_verbose = 1;
"""
conf_file = os.path.join(sbuild_conf_dir, "sbuild.conf")
with open(conf_file, "w") as f:
f.write(sbuild_conf)
print(f"Created sbuild configuration: {conf_file}")
def main(tree, options):
"""Main function for sbuild stage"""
# Get options
suite = options.get("suite", "bookworm")
arch = options.get("arch", "amd64")
mirror = options.get("mirror", "http://deb.debian.org/debian")
source_dir = options.get("source_dir", ".")
output_dir = options.get("output_dir", ".")
# Create output directory
os.makedirs(output_dir, exist_ok=True)
# Create sbuild chroot
success, chroot_name = create_sbuild_chroot(tree, suite, arch, mirror)
if not success:
return 1
# Set up sbuild configuration
setup_sbuild_config(tree, chroot_name)
# Build package
if not build_package_with_sbuild(chroot_name, source_dir, output_dir):
return 1
# Write build info
build_info = {
"chroot": chroot_name,
"suite": suite,
"arch": arch,
"mirror": mirror,
"output_dir": output_dir
}
info_file = os.path.join(output_dir, "sbuild-build.json")
with open(info_file, "w") as f:
json.dump(build_info, f, indent=2)
print("sbuild package building completed successfully")
return 0
if __name__ == '__main__':
args = osbuild.api.arguments()
r = main(args["tree"], args["options"])
sys.exit(r)

View file

@ -0,0 +1,54 @@
{
"name": "org.osbuild.sbuild",
"version": "1",
"description": "Build Debian packages using sbuild chroot environments",
"options": {
"type": "object",
"properties": {
"suite": {
"type": "string",
"default": "bookworm",
"description": "Debian suite for the build environment"
},
"arch": {
"type": "string",
"default": "amd64",
"description": "Target architecture for building"
},
"mirror": {
"type": "string",
"default": "http://deb.debian.org/debian",
"description": "Debian mirror for chroot creation"
},
"source_dir": {
"type": "string",
"default": ".",
"description": "Directory containing package source"
},
"output_dir": {
"type": "string",
"default": ".",
"description": "Directory for built packages"
}
}
},
"inputs": {
"type": "object",
"additionalProperties": false
},
"devices": {
"type": "object",
"additionalProperties": false
},
"mounts": {
"type": "object",
"additionalProperties": false
},
"capabilities": {
"type": "array",
"items": {
"type": "string"
},
"default": ["CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_FOWNER", "CAP_FSETID", "CAP_MKNOD", "CAP_SETGID", "CAP_SETUID", "CAP_SYS_CHROOT"]
}
}