feat: update depot version to 0.28.0 and implement package replacement handling in database

This commit is contained in:
2026-03-20 21:31:42 -05:00
parent bdb34c5c88
commit 9c4f686eb4
8 changed files with 408 additions and 114 deletions
+64 -8
View File
@@ -165,6 +165,10 @@ pub fn register_package_with_replacement(
"DELETE FROM provides WHERE package_id = ?1",
params![pkg_id],
)?;
tx.execute(
"DELETE FROM replaces WHERE package_id = ?1",
params![pkg_id],
)?;
tx.execute("DELETE FROM files WHERE package_id = ?1", params![pkg_id])?;
tx.execute(
"DELETE FROM directories WHERE package_id = ?1",
@@ -179,6 +183,13 @@ pub fn register_package_with_replacement(
)?;
}
for replaces in &spec.alternatives.replaces {
tx.execute(
"INSERT OR IGNORE INTO replaces (package_id, replaces_name) VALUES (?1, ?2)",
params![pkg_id, replaces],
)?;
}
// Detect ownership conflicts and separate into auto-removable vs fatal.
let mut fatal_conflicts: Vec<(String, String)> = Vec::new();
let mut auto_conflicts: Vec<(String, String)> = Vec::new();
@@ -375,7 +386,6 @@ pub fn remove_package(db_path: &Path, name: &str, rootfs: &Path) -> Result<()> {
}
// Remove directories (only if empty and not owned by another package)
let mut dirs_removed = 0;
for dir in &directories {
let path = rootfs.join(dir);
@@ -401,7 +411,6 @@ pub fn remove_package(db_path: &Path, name: &str, rootfs: &Path) -> Result<()> {
if verbose_remove_output() {
crate::log_info!(" Removed directory: {}", dir);
}
dirs_removed += 1;
}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
// Already gone
@@ -421,12 +430,6 @@ pub fn remove_package(db_path: &Path, name: &str, rootfs: &Path) -> Result<()> {
// Remove from database
delete_package_rows(&conn, pkg_id)?;
crate::log_info!(
"Removed {} files and {} directories",
files.len(),
dirs_removed
);
if !removal_errors.is_empty() {
crate::log_warn!("Failed to remove some paths:");
for err in removal_errors {
@@ -520,6 +523,14 @@ fn init_db(conn: &Connection) -> Result<()> {
UNIQUE(package_id, provides_name)
);
CREATE TABLE IF NOT EXISTS replaces (
id INTEGER PRIMARY KEY,
package_id INTEGER NOT NULL,
replaces_name TEXT NOT NULL,
FOREIGN KEY (package_id) REFERENCES packages(id),
UNIQUE(package_id, replaces_name)
);
CREATE TABLE IF NOT EXISTS files (
id INTEGER PRIMARY KEY,
package_id INTEGER NOT NULL,
@@ -537,6 +548,7 @@ fn init_db(conn: &Connection) -> Result<()> {
CREATE INDEX IF NOT EXISTS idx_files_package ON files(package_id);
CREATE INDEX IF NOT EXISTS idx_provides_name ON provides(provides_name);
CREATE INDEX IF NOT EXISTS idx_replaces_name ON replaces(replaces_name);
CREATE INDEX IF NOT EXISTS idx_directories_package ON directories(package_id);
CREATE INDEX IF NOT EXISTS idx_directories_path ON directories(path);
",
@@ -614,6 +626,10 @@ fn delete_package_rows(conn: &Connection, pkg_id: i64) -> Result<()> {
"DELETE FROM provides WHERE package_id = ?1",
params![pkg_id],
)?;
conn.execute(
"DELETE FROM replaces WHERE package_id = ?1",
params![pkg_id],
)?;
conn.execute("DELETE FROM packages WHERE id = ?1", params![pkg_id])?;
Ok(())
}
@@ -628,6 +644,10 @@ fn delete_package_rows_tx(tx: &rusqlite::Transaction<'_>, pkg_id: i64) -> Result
"DELETE FROM provides WHERE package_id = ?1",
params![pkg_id],
)?;
tx.execute(
"DELETE FROM replaces WHERE package_id = ?1",
params![pkg_id],
)?;
tx.execute("DELETE FROM packages WHERE id = ?1", params![pkg_id])?;
Ok(())
}
@@ -803,6 +823,24 @@ pub fn get_all_provides(db_path: &Path) -> Result<std::collections::HashSet<Stri
Ok(names)
}
/// Get set of all replacement names satisfied by installed packages.
pub fn get_all_replaces(db_path: &Path) -> Result<std::collections::HashSet<String>> {
use std::collections::HashSet;
if !db_path.exists() {
return Ok(HashSet::new());
}
let conn = Connection::open(db_path)?;
init_db(&conn)?;
let mut stmt = conn.prepare("SELECT replaces_name FROM replaces")?;
let names: HashSet<String> = stmt
.query_map([], |row| row.get(0))?
.filter_map(|r| r.ok())
.collect();
Ok(names)
}
/// Get version of a specific installed package
pub fn get_package_version(db_path: &Path, name: &str) -> Result<Option<String>> {
if !db_path.exists() {
@@ -1179,4 +1217,22 @@ mod tests {
assert!(files.contains(&"usr/bin/new_file".to_string()));
assert!(!files.contains(&"usr/bin/shared_dir/old_file".to_string()));
}
#[test]
fn register_package_persists_replacements() {
let tmp = tempfile::tempdir().unwrap();
let db_path = tmp.path().join("packages.db");
let dest = tmp.path().join("dest");
std::fs::create_dir_all(dest.join("usr/bin")).unwrap();
std::fs::write(dest.join("usr/bin/vx"), "vx").unwrap();
let mut spec = mk_spec("vx", "1.0");
spec.alternatives.replaces = vec!["grep".into(), "patch".into()];
register_package(&db_path, &spec, &dest).unwrap();
let replaces = get_all_replaces(&db_path).unwrap();
assert!(replaces.contains("grep"));
assert!(replaces.contains("patch"));
}
}