improve: enhance rootfs command robustness and flexibility

- Remove dependency on manifest parsing for reference discovery
- Automatically detect and use first available OSTree reference
- Simplify command usage - no need to parse treefile for reference name
- Add better error handling for empty repositories
- Improve user experience by showing which reference is being used
- Make command more flexible for different use cases
This commit is contained in:
robojerk 2025-09-01 18:14:38 -07:00
parent bffba885bc
commit aadf99a3e1
3 changed files with 101 additions and 32 deletions

2
.gitignore vendored
View file

@ -27,6 +27,8 @@ debian/*.postrm
debian/*.prerm debian/*.prerm
debian/*.triggers debian/*.triggers
*issue.md
# Package archives and tarballs # Package archives and tarballs
*.tar *.tar
*.tar.gz *.tar.gz

View file

@ -0,0 +1,83 @@
api_version: "1.0"
kind: "tree"
metadata:
ref_name: "test/minimal"
version: "0.1.0"
description: "Minimal test tree for bootc image generation"
repositories:
- name: "debian"
url: "http://deb.debian.org/debian"
suite: "trixie"
components: ["main"]
enabled: true
packages:
base: ["bash", "coreutils", "grep", "gawk", "sed", "linux-image-amd64", "linux-headers-amd64"]
additional: []
excludes: []
output:
generate_container: true
container_path: "/tmp/apt-ostree-container"
export_formats:
- "docker-archive"
- "oci"
system:
# Create required bootc directories
directories:
- "/sysroot"
- "/usr/lib/bootc"
- "/usr/lib/ostree"
- "/usr/lib/systemd/system-preset"
# Enable required systemd services
services:
- "systemd-networkd"
- "systemd-resolved"
- "systemd-sysusers"
- "systemd-tmpfiles-setup"
# Create composefs configuration
files:
- path: "/usr/lib/ostree/prepare-root.conf"
content: |
[prepare-root]
composefs=1
composefs-store=/ostree/repo
mode: "0644"
owner: "root:root"
- path: "/usr/lib/bootc/install/00-debian.toml"
content: |
[install]
filesystem = "ext4"
root-fs-type = "ext4"
[install.kernel-args]
default = ["console=ttyS0,115200", "quiet"]
mode: "0644"
owner: "root:root"
- path: "/usr/lib/systemd/tmpfiles.d/10-bootc.conf"
content: |
# Bootc required directories
d /var/log 0755 root root
d /var/cache 0755 root root
d /var/tmp 1777 root root
d /tmp 1777 root root
d /run 0755 root root
d /sysroot 0755 root root
d /ostree 0755 root root
d /boot 0755 root root
mode: "0644"
owner: "root:root"
# Post-installation cleanup and setup
postinstall:
- "echo 'Setting up bootc-compatible filesystem...'"
- "mkdir -p /sysroot /ostree /usr/lib/bootc /usr/lib/ostree"
- "echo 'Cleaning up log files for reproducible builds...'"
- "find /var/log -type f -name '*.log' -delete"
- "find /var/log -type f -name '*.log.*' -delete"
- "find /var/cache -type f -delete"
- "echo 'Setting up systemd tmpfiles...'"
- "systemd-tmpfiles --create --remove"
- "echo 'Bootc setup completed successfully'"

View file

@ -195,23 +195,6 @@ impl Command for ComposeCommand {
let manifest_path = &args[1]; let manifest_path = &args[1];
let dest_path = &args[2]; let dest_path = &args[2];
// Parse the manifest/treefile to get the reference name
let treefile_content = match std::fs::read_to_string(manifest_path) {
Ok(content) => content,
Err(e) => {
eprintln!("❌ Failed to read manifest file: {}", e);
return Err(AptOstreeError::System(format!("Failed to read manifest: {}", e)));
}
};
let treefile = match Treefile::parse_treefile_content(&treefile_content) {
Ok(tf) => tf,
Err(e) => {
eprintln!("❌ Failed to parse manifest: {}", e);
return Err(e);
}
};
// Extract the existing OSTree tree to the destination directory // Extract the existing OSTree tree to the destination directory
let dest_path_buf = std::path::PathBuf::from(dest_path); let dest_path_buf = std::path::PathBuf::from(dest_path);
@ -221,16 +204,17 @@ impl Command for ComposeCommand {
.map_err(|e| AptOstreeError::System(format!("Failed to clean destination directory: {}", e)))?; .map_err(|e| AptOstreeError::System(format!("Failed to clean destination directory: {}", e)))?;
} }
// Check if the OSTree repository exists and contains the reference // Check if the OSTree repository exists and contains any references
// Use the same workdir that was used to create the tree // Use the same workdir that was used to create the tree
let ostree_repo = std::path::PathBuf::from("/tmp/apt-ostree-build/repo"); let ostree_repo = std::path::PathBuf::from("/tmp/apt-ostree-build/repo");
if !ostree_repo.exists() { if !ostree_repo.exists() {
eprintln!("❌ OSTree repository not found at: {}", ostree_repo.display()); eprintln!("❌ OSTree repository not found at: {}", ostree_repo.display());
eprintln!(" Please run 'apt-ostree compose tree {}' first to create the tree", manifest_path); eprintln!(" Please run 'apt-ostree compose tree {}' first to create the tree", manifest_path);
return Err(AptOstreeError::System("OSTree repository not found".to_string())); return Err(AptOstreeError::System("OSTree repository not found".to_string()));
} }
// Check if the reference exists in the repository // Check if there are any references in the repository
let ref_check = std::process::Command::new("ostree") let ref_check = std::process::Command::new("ostree")
.args([ .args([
"refs", "refs",
@ -247,26 +231,26 @@ impl Command for ComposeCommand {
} }
let refs_output = String::from_utf8_lossy(&ref_check.stdout); let refs_output = String::from_utf8_lossy(&ref_check.stdout);
if !refs_output.contains(&treefile.metadata.ref_name) { let refs: Vec<&str> = refs_output.lines().filter(|line| !line.trim().is_empty()).collect();
eprintln!("❌ Reference '{}' not found in OSTree repository", treefile.metadata.ref_name);
eprintln!(" Available references:"); if refs.is_empty() {
for line in refs_output.lines() { eprintln!("❌ No OSTree references found in repository");
if !line.trim().is_empty() {
eprintln!(" {}", line.trim());
}
}
eprintln!(" Please run 'apt-ostree compose tree {}' first to create the tree", manifest_path); eprintln!(" Please run 'apt-ostree compose tree {}' first to create the tree", manifest_path);
return Err(AptOstreeError::System(format!("Reference '{}' not found", treefile.metadata.ref_name))); return Err(AptOstreeError::System("No OSTree references found".to_string()));
} }
// Extract OSTree tree to destination // Use the first available reference
println!("📦 Extracting OSTree tree '{}' to '{}'...", treefile.metadata.ref_name, dest_path); let ref_name = refs[0];
println!("📦 Using OSTree reference: {}", ref_name);
// Now extract the OSTree tree to destination
println!("📦 Extracting OSTree tree '{}' to '{}'...", ref_name, dest_path);
let output = std::process::Command::new("ostree") let output = std::process::Command::new("ostree")
.args([ .args([
"checkout", "checkout",
"--repo", &ostree_repo.to_string_lossy(), "--repo", &ostree_repo.to_string_lossy(),
"--user-mode", "--user-mode",
&treefile.metadata.ref_name, ref_name,
&dest_path_buf.to_string_lossy() &dest_path_buf.to_string_lossy()
]) ])
.output() .output()
@ -299,7 +283,7 @@ impl Command for ComposeCommand {
println!("✅ Rootfs generation completed successfully"); println!("✅ Rootfs generation completed successfully");
println!(" Source: {}", manifest_path); println!(" Source: {}", manifest_path);
println!(" Destination: {}", dest_path); println!(" Destination: {}", dest_path);
println!(" Tree reference: {}", treefile.metadata.ref_name); println!(" Tree reference: {}", ref_name);
println!(" Files extracted: {}", file_count); println!(" Files extracted: {}", file_count);
} }
"build-chunked-oci" => { "build-chunked-oci" => {