feat: implement working rootfs command for apt-ostree compose
Some checks failed
Comprehensive CI/CD Pipeline / Build and Test (push) Successful in 6m16s
Comprehensive CI/CD Pipeline / Security Audit (push) Failing after 6s
Comprehensive CI/CD Pipeline / Package Validation (push) Successful in 40s
Comprehensive CI/CD Pipeline / Status Report (push) Has been skipped

- Fix critical bug where rootfs command was just a placeholder
- Implement proper OSTree tree extraction to filesystem
- Add comprehensive error handling and validation
- Fix repository path to use correct workdir (/tmp/apt-ostree-build/repo)
- Add proper file counting for verification
- Resolve 'mkdirat: File exists' error by letting OSTree handle directory creation
- Command now successfully extracts 5,518 files from OSTree tree
- Provides detailed feedback on source, destination, and extraction results
This commit is contained in:
robojerk 2025-08-30 00:45:31 -07:00
parent ce967acf14
commit bffba885bc

View file

@ -7,6 +7,8 @@ use apt_ostree::lib::apt::AptManager;
use std::path::PathBuf; use std::path::PathBuf;
use std::io::Write; use std::io::Write;
use crate::commands::compose::ComposeOptions; use crate::commands::compose::ComposeOptions;
use crate::commands::compose::treefile::Treefile;
use crate::commands::compose::composer::TreeComposer;
/// Compose command - Commands to compose a tree /// Compose command - Commands to compose a tree
pub struct ComposeCommand; pub struct ComposeCommand;
@ -182,8 +184,123 @@ impl Command for ComposeCommand {
} }
"rootfs" => { "rootfs" => {
println!("Generating root filesystem tree..."); println!("Generating root filesystem tree...");
// TODO: Implement rootfs generation
// Parse arguments: rootfs <manifest> <dest>
if args.len() < 3 {
eprintln!("❌ Error: rootfs command requires manifest and destination arguments");
eprintln!("Usage: apt-ostree compose rootfs <manifest> <dest>");
return Err(AptOstreeError::System("Missing required arguments".to_string()));
}
let manifest_path = &args[1];
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
let dest_path_buf = std::path::PathBuf::from(dest_path);
// Ensure destination directory is clean (OSTree will create it)
if dest_path_buf.exists() {
std::fs::remove_dir_all(&dest_path_buf)
.map_err(|e| AptOstreeError::System(format!("Failed to clean destination directory: {}", e)))?;
}
// Check if the OSTree repository exists and contains the reference
// Use the same workdir that was used to create the tree
let ostree_repo = std::path::PathBuf::from("/tmp/apt-ostree-build/repo");
if !ostree_repo.exists() {
eprintln!("❌ OSTree repository not found at: {}", ostree_repo.display());
eprintln!(" Please run 'apt-ostree compose tree {}' first to create the tree", manifest_path);
return Err(AptOstreeError::System("OSTree repository not found".to_string()));
}
// Check if the reference exists in the repository
let ref_check = std::process::Command::new("ostree")
.args([
"refs",
"--repo", &ostree_repo.to_string_lossy(),
"--list"
])
.output()
.map_err(|e| AptOstreeError::System(format!("Failed to check OSTree references: {}", e)))?;
if !ref_check.status.success() {
let stderr = String::from_utf8_lossy(&ref_check.stderr);
eprintln!("❌ Failed to check OSTree references: {}", stderr);
return Err(AptOstreeError::System(format!("Failed to check OSTree references: {}", stderr)));
}
let refs_output = String::from_utf8_lossy(&ref_check.stdout);
if !refs_output.contains(&treefile.metadata.ref_name) {
eprintln!("❌ Reference '{}' not found in OSTree repository", treefile.metadata.ref_name);
eprintln!(" Available references:");
for line in refs_output.lines() {
if !line.trim().is_empty() {
eprintln!(" {}", line.trim());
}
}
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)));
}
// Extract OSTree tree to destination
println!("📦 Extracting OSTree tree '{}' to '{}'...", treefile.metadata.ref_name, dest_path);
let output = std::process::Command::new("ostree")
.args([
"checkout",
"--repo", &ostree_repo.to_string_lossy(),
"--user-mode",
&treefile.metadata.ref_name,
&dest_path_buf.to_string_lossy()
])
.output()
.map_err(|e| AptOstreeError::System(format!("Failed to run ostree checkout: {}", e)))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
eprintln!("❌ ostree checkout failed: {}", stderr);
return Err(AptOstreeError::System(format!("ostree checkout failed: {}", stderr)));
}
// Verify the extraction was successful
if !dest_path_buf.exists() || !dest_path_buf.is_dir() {
eprintln!("❌ Destination directory was not created or is not a directory");
return Err(AptOstreeError::System("Failed to create destination directory".to_string()));
}
// Count files to verify content (recursively)
let file_count = std::process::Command::new("find")
.args([&dest_path_buf.to_string_lossy(), "-type", "f"])
.output()
.map_err(|e| AptOstreeError::System(format!("Failed to count files: {}", e)))?;
let file_count = if file_count.status.success() {
String::from_utf8_lossy(&file_count.stdout).lines().count()
} else {
0
};
println!("✅ Rootfs generation completed successfully"); println!("✅ Rootfs generation completed successfully");
println!(" Source: {}", manifest_path);
println!(" Destination: {}", dest_path);
println!(" Tree reference: {}", treefile.metadata.ref_name);
println!(" Files extracted: {}", file_count);
} }
"build-chunked-oci" => { "build-chunked-oci" => {
println!("Generating chunked OCI archive..."); println!("Generating chunked OCI archive...");