Implement system state management with TOML serialization

- Introduced `SystemState` struct to manage system state, including stage, target, architecture, and layers.
- Added functions to load and save system state from/to a TOML file.
- Implemented layer management functions: add, set, and remove packages from layers.
- Created initialization function for LBI layout, including directory and symlink creation.
- Added tests for layer management and LBI layout initialization.
This commit is contained in:
2026-05-20 19:54:00 -05:00
parent d61d59f4f5
commit 1833a2c42d
18 changed files with 8820 additions and 354 deletions
+84 -1
View File
@@ -10,6 +10,8 @@ use std::fs;
use std::path::Path;
use walkdir::WalkDir;
const DEPOT_BOOTSTRAP_IGNORE_SBASE_CONFLICTS: &str = "DEPOT_BOOTSTRAP_IGNORE_SBASE_CONFLICTS";
fn format_licenses(licenses: &[String]) -> String {
licenses.join(", ")
}
@@ -18,6 +20,14 @@ fn verbose_remove_output() -> bool {
std::env::var_os("DEPOT_VERBOSE_REMOVE").is_some()
}
fn should_ignore_sbase_conflicts() -> bool {
std::env::var_os(DEPOT_BOOTSTRAP_IGNORE_SBASE_CONFLICTS).is_some()
}
fn should_auto_clear_conflict(owner: &str, path: &str) -> bool {
(owner == "sbase" && should_ignore_sbase_conflicts()) || is_auto_removable_path(path)
}
/// Installed package row from the local package database.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InstalledPackageRecord {
@@ -213,7 +223,7 @@ pub fn register_package_with_replacement(
if let Ok(owner) = owner_res
&& owner != spec.package.name
{
if is_auto_removable_path(file) {
if should_auto_clear_conflict(&owner, file) {
auto_conflicts.push((file.clone(), owner));
} else {
fatal_conflicts.push((file.clone(), owner));
@@ -241,6 +251,15 @@ pub fn register_package_with_replacement(
)?;
if let Some(rootfs) = &rootfs_opt {
if destdir.join(f).symlink_metadata().is_ok() {
crate::log_info!(
"Auto-cleared DB ownership for path: {} (previously owned by {})",
f,
owner
);
continue;
}
let disk_path = rootfs.join(f);
if disk_path.exists() {
let _ = std::fs::remove_file(&disk_path);
@@ -1056,6 +1075,7 @@ mod tests {
use crate::package::{
Alternatives, Build, BuildFlags, BuildType, Dependencies, PackageInfo, PackageSpec, Source,
};
use crate::test_support::TestEnv;
use std::path::PathBuf;
fn mk_spec(name: &str, version: &str) -> PackageSpec {
@@ -1243,6 +1263,69 @@ mod tests {
assert!(files_b.contains(&"usr/share/perl5/shared.pm".to_string()));
}
#[test]
fn register_package_auto_clears_sbase_conflicts_when_requested() {
let tmp = tempfile::tempdir().unwrap();
let rootfs = tmp.path().join("rootfs");
let db_path = crate::config::Config::for_rootfs(&rootfs).installed_db_path(&rootfs);
std::fs::create_dir_all(rootfs.join("system/binaries")).unwrap();
std::fs::write(rootfs.join("system/binaries/find"), "sbase find").unwrap();
let spec_a = mk_spec("sbase", "1.0");
let dest_a = tmp.path().join("dest_a");
std::fs::create_dir_all(dest_a.join("system/binaries")).unwrap();
std::fs::write(dest_a.join("system/binaries/find"), "sbase find").unwrap();
register_package(&db_path, &spec_a, &dest_a).unwrap();
let spec_b = mk_spec("bfs", "4.1");
let dest_b = tmp.path().join("dest_b");
std::fs::create_dir_all(dest_b.join("system/binaries")).unwrap();
std::fs::write(dest_b.join("system/binaries/find"), "bfs find").unwrap();
let mut env = TestEnv::new();
env.set_var(DEPOT_BOOTSTRAP_IGNORE_SBASE_CONFLICTS, "1");
register_package(&db_path, &spec_b, &dest_b).unwrap();
let files_sbase = get_package_files(&db_path, "sbase").unwrap();
assert!(!files_sbase.contains(&"system/binaries/find".to_string()));
let files_bfs = get_package_files(&db_path, "bfs").unwrap();
assert!(files_bfs.contains(&"system/binaries/find".to_string()));
}
#[test]
fn register_package_auto_clear_preserves_new_payload_on_disk() {
let tmp = tempfile::tempdir().unwrap();
let rootfs = tmp.path().join("rootfs");
let db_path = crate::config::Config::for_rootfs(&rootfs).installed_db_path(&rootfs);
std::fs::create_dir_all(rootfs.join("system/binaries")).unwrap();
std::fs::write(rootfs.join("system/binaries/find"), "sbase find").unwrap();
let spec_a = mk_spec("sbase", "1.0");
let dest_a = tmp.path().join("dest_a");
std::fs::create_dir_all(dest_a.join("system/binaries")).unwrap();
std::fs::write(dest_a.join("system/binaries/find"), "sbase find").unwrap();
register_package(&db_path, &spec_a, &dest_a).unwrap();
std::fs::write(rootfs.join("system/binaries/find"), "bfs find").unwrap();
let spec_b = mk_spec("bfs", "4.1");
let dest_b = tmp.path().join("dest_b");
std::fs::create_dir_all(dest_b.join("system/binaries")).unwrap();
std::fs::write(dest_b.join("system/binaries/find"), "bfs find").unwrap();
let mut env = TestEnv::new();
env.set_var(DEPOT_BOOTSTRAP_IGNORE_SBASE_CONFLICTS, "1");
register_package(&db_path, &spec_b, &dest_b).unwrap();
assert_eq!(
std::fs::read_to_string(rootfs.join("system/binaries/find")).unwrap(),
"bfs find"
);
let files_sbase = get_package_files(&db_path, "sbase").unwrap();
assert!(!files_sbase.contains(&"system/binaries/find".to_string()));
let files_bfs = get_package_files(&db_path, "bfs").unwrap();
assert!(files_bfs.contains(&"system/binaries/find".to_string()));
}
#[test]
fn get_package_files_missing_package_returns_empty() {
let tmp = tempfile::tempdir().unwrap();