aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-02-23 01:52:22 +0900
committernsfisis <nsfisis@gmail.com>2026-02-23 01:59:22 +0900
commit612af0aaacda404b8e177d0c1a6d3bd937e8d39a (patch)
treee7a74752686e3216a533f7437d3838dc10c58328 /crates/mozart
parentcc8f22ff7f1d9ed32e14f5b59a5498f8aa653091 (diff)
downloadphp-mozart-612af0aaacda404b8e177d0c1a6d3bd937e8d39a.tar.gz
php-mozart-612af0aaacda404b8e177d0c1a6d3bd937e8d39a.tar.zst
php-mozart-612af0aaacda404b8e177d0c1a6d3bd937e8d39a.zip
fix(update): implement --with constraints, inline shorthand, and APCu passthrough
- Parse and apply --with temporary constraints to the resolver - Support inline constraint shorthand (vendor/pkg:1.0.*) - Reject --lock combined with specific package names - Filter magic keywords (lock/nothing/mirrors) from package list - Pass APCu CLI flags through to InstallConfig instead of hardcoding Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart')
-rw-r--r--crates/mozart/src/commands/create_project.rs1
-rw-r--r--crates/mozart/src/commands/remove.rs4
-rw-r--r--crates/mozart/src/commands/require.rs3
-rw-r--r--crates/mozart/src/commands/show.rs14
-rw-r--r--crates/mozart/src/commands/update.rs59
5 files changed, 71 insertions, 10 deletions
diff --git a/crates/mozart/src/commands/create_project.rs b/crates/mozart/src/commands/create_project.rs
index b010a75..6a5b815 100644
--- a/crates/mozart/src/commands/create_project.rs
+++ b/crates/mozart/src/commands/create_project.rs
@@ -410,6 +410,7 @@ pub async fn execute(
ignore_platform_reqs: args.ignore_platform_reqs,
ignore_platform_req_list: args.ignore_platform_req.clone(),
repo_cache: None,
+ temporary_constraints: HashMap::new(),
};
console.info("Resolving dependencies...");
diff --git a/crates/mozart/src/commands/remove.rs b/crates/mozart/src/commands/remove.rs
index acf878c..88fa4da 100644
--- a/crates/mozart/src/commands/remove.rs
+++ b/crates/mozart/src/commands/remove.rs
@@ -250,6 +250,7 @@ pub async fn execute(
ignore_platform_reqs: args.ignore_platform_reqs,
ignore_platform_req_list: args.ignore_platform_req.clone(),
repo_cache: None,
+ temporary_constraints: HashMap::new(),
};
// Print header messages
@@ -495,6 +496,7 @@ async fn remove_unused(
ignore_platform_reqs: args.ignore_platform_reqs,
ignore_platform_req_list: args.ignore_platform_req.clone(),
repo_cache: None,
+ temporary_constraints: HashMap::new(),
};
console.info("Resolving dependencies to detect unused packages...");
@@ -823,6 +825,7 @@ mod tests {
ignore_platform_reqs: false,
ignore_platform_req_list: vec![],
repo_cache: None,
+ temporary_constraints: HashMap::new(),
};
let resolved = resolve(&request)
.await
@@ -858,6 +861,7 @@ mod tests {
ignore_platform_reqs: false,
ignore_platform_req_list: vec![],
repo_cache: None,
+ temporary_constraints: HashMap::new(),
};
let resolved2 = resolve(&request2)
.await
diff --git a/crates/mozart/src/commands/require.rs b/crates/mozart/src/commands/require.rs
index ef5ff04..4ea739d 100644
--- a/crates/mozart/src/commands/require.rs
+++ b/crates/mozart/src/commands/require.rs
@@ -655,6 +655,7 @@ pub async fn execute(
ignore_platform_reqs: args.ignore_platform_reqs,
ignore_platform_req_list: args.ignore_platform_req.clone(),
repo_cache: None,
+ temporary_constraints: HashMap::new(),
};
// Print header messages
@@ -1026,6 +1027,7 @@ mod tests {
ignore_platform_reqs: false,
ignore_platform_req_list: vec![],
repo_cache: None,
+ temporary_constraints: HashMap::new(),
};
let resolved = resolver::resolve(&request)
@@ -1078,6 +1080,7 @@ mod tests {
ignore_platform_reqs: false,
ignore_platform_req_list: vec![],
repo_cache: None,
+ temporary_constraints: HashMap::new(),
};
let resolved = resolver::resolve(&request)
diff --git a/crates/mozart/src/commands/show.rs b/crates/mozart/src/commands/show.rs
index 9eae36e..f194bce 100644
--- a/crates/mozart/src/commands/show.rs
+++ b/crates/mozart/src/commands/show.rs
@@ -138,12 +138,14 @@ pub async fn execute(
// Fix 5: --format with invalid value
if let Some(ref fmt) = args.format
- && fmt != "text" && fmt != "json" {
- anyhow::bail!(
- "Unsupported format \"{}\". See help for supported formats.",
- fmt
- );
- }
+ && fmt != "text"
+ && fmt != "json"
+ {
+ anyhow::bail!(
+ "Unsupported format \"{}\". See help for supported formats.",
+ fmt
+ );
+ }
// Fix 6: --self with a package argument
if args.self_info && args.package.is_some() {
diff --git a/crates/mozart/src/commands/update.rs b/crates/mozart/src/commands/update.rs
index f33f1b0..06e6b22 100644
--- a/crates/mozart/src/commands/update.rs
+++ b/crates/mozart/src/commands/update.rs
@@ -737,12 +737,61 @@ pub async fn execute(
let vendor_dir = working_dir.join("vendor");
// Step 4: Handle --lock mode (early return)
+ // Fix 4: Reject combining --lock with specific package names
if args.lock {
+ let non_magic: Vec<_> = args
+ .packages
+ .iter()
+ .filter(|p| !matches!(p.to_lowercase().as_str(), "lock" | "nothing" | "mirrors"))
+ .collect();
+ if !non_magic.is_empty() {
+ anyhow::bail!(
+ "You cannot simultaneously update only a selection of packages and regenerate the lock file metadata."
+ );
+ }
return handle_lock_mode(&lock_path, &composer_json_content, args.dry_run, console);
}
let dev_mode = !args.no_dev;
+ // Fix 1C + Fix 2: Parse --with constraints and inline constraint shorthand.
+ let mut temporary_constraints: HashMap<String, String> = HashMap::new();
+
+ // Parse --with constraints (format: "vendor/package:constraint")
+ for with_entry in &args.with {
+ if let Some((name, constraint)) = with_entry.split_once(':') {
+ let name = name.trim().to_lowercase();
+ let constraint = constraint.trim().to_string();
+ if !name.is_empty() && !constraint.is_empty() {
+ temporary_constraints.insert(name, constraint);
+ }
+ }
+ }
+
+ // Fix 2: Parse inline constraint shorthand from package arguments
+ // (e.g. "vendor/package:1.0.*" -> name="vendor/package", constraint="1.0.*")
+ let mut raw_packages: Vec<String> = Vec::new();
+ for pkg in &args.packages {
+ if let Some((name, constraint)) = pkg.split_once(':') {
+ let name = name.trim().to_string();
+ let constraint = constraint.trim().to_string();
+ if !name.is_empty() && !constraint.is_empty() {
+ temporary_constraints.insert(name.to_lowercase(), constraint);
+ raw_packages.push(name);
+ } else {
+ raw_packages.push(pkg.clone());
+ }
+ } else {
+ raw_packages.push(pkg.clone());
+ }
+ }
+
+ // Fix 5: Filter magic keywords from package list
+ let raw_packages: Vec<String> = raw_packages
+ .into_iter()
+ .filter(|p| !matches!(p.to_lowercase().as_str(), "lock" | "nothing" | "mirrors"))
+ .collect();
+
// Step 5: Build the resolve request from composer.json
// Filter out platform packages from require list for the resolver (they're handled separately)
let require: Vec<(String, String)> = composer_json
@@ -785,6 +834,7 @@ pub async fn execute(
ignore_platform_reqs: args.ignore_platform_reqs,
ignore_platform_req_list: args.ignore_platform_req.clone(),
repo_cache: None,
+ temporary_constraints,
};
// Step 6: Print header and run resolver
@@ -828,7 +878,7 @@ pub async fn execute(
// Note: wildcard expansion and dependency traversal both require a lock file.
// If --minimal-changes is requested without specific packages, we pin all packages.
// --root-reqs: treat root requirements as the package list
- let effective_packages: Vec<String> = if args.root_reqs && args.packages.is_empty() {
+ let effective_packages: Vec<String> = if args.root_reqs && raw_packages.is_empty() {
let mut root_pkgs: Vec<String> = composer_json
.require
.keys()
@@ -846,7 +896,7 @@ pub async fn execute(
}
root_pkgs
} else {
- args.packages.clone()
+ raw_packages
};
let update_packages: Vec<String> = if !effective_packages.is_empty() {
@@ -1140,8 +1190,8 @@ pub async fn execute(
ignore_platform_req: args.ignore_platform_req.clone(),
optimize_autoloader: args.optimize_autoloader,
classmap_authoritative: args.classmap_authoritative,
- apcu_autoloader: false,
- apcu_autoloader_prefix: None,
+ apcu_autoloader: args.apcu_autoloader || args.apcu_autoloader_prefix.is_some(),
+ apcu_autoloader_prefix: args.apcu_autoloader_prefix.clone(),
download_only: false,
},
)
@@ -1858,6 +1908,7 @@ mod tests {
ignore_platform_reqs: false,
ignore_platform_req_list: vec![],
repo_cache: None,
+ temporary_constraints: HashMap::new(),
};
let resolved = resolve(&request).await.expect("Resolution should succeed");