Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ console = { version = "0.16", default-features = false, features = [
"ansi-parsing",
] }

[dev-dependencies]
tempfile = "3"

[target.'cfg(not(target_os = "macos"))'.dependencies]
rusqlite = { workspace = true, features = ["bundled"] }
native-tls = { version = "0.2", features = ["vendored"] }
Expand Down
1 change: 1 addition & 0 deletions crates/cli/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
mod main;
mod sync;
64 changes: 64 additions & 0 deletions crates/cli/src/tests/sync.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use libpkgx::config::Config;
use libpkgx::sync;
use rusqlite::Connection;
use std::sync::Mutex;
use tempfile::TempDir;

// serialize tests that mutate PKGX_PANTRY_DIR
static ENV_MUTEX: Mutex<()> = Mutex::new(());

fn test_config(pantry_dir: &std::path::Path, db_file: &std::path::Path) -> Config {
Config {
pantry_dir: pantry_dir.to_path_buf(),
pantry_db_file: db_file.to_path_buf(),
dist_url: "http://localhost:0".to_string(),
pkgx_dir: pantry_dir.to_path_buf(),
}
}

#[tokio::test]
async fn test_update_with_pantry_dir_rebuilds_db_when_projects_exists() {
let _lock = ENV_MUTEX.lock().unwrap();
let tmp = TempDir::new().unwrap();
let pantry_dir = tmp.path().join("pantry");
std::fs::create_dir_all(pantry_dir.join("projects")).unwrap();
let db_file = tmp.path().join("pantry.2.db");

let config = test_config(&pantry_dir, &db_file);
let mut conn = Connection::open(&db_file).unwrap();

std::env::set_var("PKGX_PANTRY_DIR", &pantry_dir);
let result = sync::update(&config, &mut conn).await;
std::env::remove_var("PKGX_PANTRY_DIR");

assert!(
result.is_ok(),
"update should succeed when projects/ exists"
);
}

#[tokio::test]
async fn test_update_with_pantry_dir_errors_when_projects_missing() {
let _lock = ENV_MUTEX.lock().unwrap();
let tmp = TempDir::new().unwrap();
let pantry_dir = tmp.path().join("pantry");
std::fs::create_dir_all(&pantry_dir).unwrap();
let db_file = tmp.path().join("pantry.2.db");

let config = test_config(&pantry_dir, &db_file);
let mut conn = Connection::open(&db_file).unwrap();

std::env::set_var("PKGX_PANTRY_DIR", &pantry_dir);
let result = sync::update(&config, &mut conn).await;
std::env::remove_var("PKGX_PANTRY_DIR");

assert!(
result.is_err(),
"update should fail when projects/ is missing"
);
let err = result.unwrap_err().to_string();
assert!(
err.contains("missing projects/"),
"error should mention missing projects/, got: {err}"
);
}
11 changes: 10 additions & 1 deletion crates/lib/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,16 @@ pub async fn ensure(config: &Config, conn: &mut Connection) -> Result<(), Box<dy

pub async fn update(config: &Config, conn: &mut Connection) -> Result<(), Box<dyn Error>> {
if std::env::var("PKGX_PANTRY_DIR").is_ok() {
return Err("PKGX_PANTRY_DIR is set, refusing to update pantry")?;
if config.pantry_dir.join("projects").is_dir() {
let lockfile = lock(config)?;
pantry_db::cache(config, conn)?;
FileExt::unlock(&lockfile)?;
return Ok(());
} else {
return Err(
"PKGX_PANTRY_DIR is set but does not contain a pantry (missing projects/)",
)?;
}
}
replace(config, conn).await
Comment on lines 34 to 47
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When PKGX_PANTRY_DIR is set, update() now calls ensure(), which will call replace() (download + archive.unpack(dest)) if pantry_dir/projects is missing. That means a misconfigured PKGX_PANTRY_DIR can cause us to extract the pantry tarball into an arbitrary user-provided directory (potentially overwriting files), whereas we previously refused to update in this mode. Consider keeping the safety guard: if PKGX_PANTRY_DIR is set, only rebuild/cache the DB when pantry_dir/projects exists; otherwise return a clear error about the invalid pantry dir rather than downloading/extracting into it.

Copilot uses AI. Check for mistakes.
}
Expand Down
Loading