diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-03 23:59:29 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-03 23:59:29 +0900 |
| commit | 837327901f28b229695c7cfd435a2c4f5fe2763d (patch) | |
| tree | 639539e9559abb512645de4ddbd7814c01ea0be8 /crates/mozart/src/commands | |
| parent | b922f2c7c98496564745435db5cf8d0608a52820 (diff) | |
| download | php-mozart-837327901f28b229695c7cfd435a2c4f5fe2763d.tar.gz php-mozart-837327901f28b229695c7cfd435a2c4f5fe2763d.tar.zst php-mozart-837327901f28b229695c7cfd435a2c4f5fe2763d.zip | |
fix(update): honor symlink:false on path repos during partial update
A path repo locked with `transport-options.symlink: false` is in
copy-mode and Composer's PoolBuilder keeps that entry pinned at its
lock version on a partial update. The previous unconditional skip
treated every path-repo dist as "always reload from disk", which
caused non-allow-listed copy-mode packages to drift.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart/src/commands')
| -rw-r--r-- | crates/mozart/src/commands/update.rs | 38 |
1 files changed, 32 insertions, 6 deletions
diff --git a/crates/mozart/src/commands/update.rs b/crates/mozart/src/commands/update.rs index ac5a685..5690081 100644 --- a/crates/mozart/src/commands/update.rs +++ b/crates/mozart/src/commands/update.rs @@ -359,6 +359,23 @@ fn locked_version_normalized(pkg: &lockfile::LockedPackage) -> String { }) } +/// True when a locked package's `transport-options.symlink` is explicitly +/// `false`. Composer's `PoolBuilder::buildPool` only treats path repos as +/// "always reload from disk" when symlinks are enabled (the default); a +/// `symlink: false` path repo is copy-mode and gets pinned at its locked +/// version on a partial update so only the explicitly requested packages +/// move. The flag rides on the lock entry under `transport-options`, which +/// `LockedPackage` parks in `extra_fields` since the schema does not call +/// it out by name. +fn is_path_symlink_disabled(pkg: &lockfile::LockedPackage) -> bool { + pkg.extra_fields + .get("transport-options") + .and_then(|v| v.as_object()) + .and_then(|m| m.get("symlink")) + .and_then(|v| v.as_bool()) + == Some(false) +} + /// For a partial update (when specific packages are named on the CLI), swap back /// the versions of packages that were NOT requested to be updated. /// @@ -1193,12 +1210,21 @@ pub async fn run( if updated.contains(&name_lower) { continue; } - // Path-repo packages are always reloaded from disk - // by Composer (PoolBuilder treats path repos as - // canonical sources, not lock-bound). Skip them so - // the pool sees the live on-disk version, matching - // Composer's "path repos always update" behaviour. - if p.dist.as_ref().map(|d| d.dist_type.as_str()) == Some("path") { + // Path-repo packages backed by a symlink are always + // reloaded from disk by Composer (PoolBuilder treats + // them as canonical sources, not lock-bound). Skip + // them so the pool sees the live on-disk version, + // matching Composer's "path repos always update" + // behaviour. The exception — and the reason for the + // explicit `transport-options.symlink == false` + // check — is a copy-mode path repo: with symlinks + // disabled, Composer keeps the locked entry pinned + // so a partial update only refreshes the named + // package(s), not every path-repo dep on disk. + // Mirrors `PoolBuilder::buildPool` lines 230-235. + if p.dist.as_ref().map(|d| d.dist_type.as_str()) == Some("path") + && !is_path_symlink_disabled(p) + { continue; } names.insert(name_lower.clone()); |
