diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-03 12:10:38 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-03 12:10:38 +0900 |
| commit | dffb6244ebb432477b83631d68584bbc7186dd94 (patch) | |
| tree | 4be972cb21de544c53108a244ba4288f4467d544 /crates/mozart-registry/src | |
| parent | ed77ff97d6c137ef58f0464b7a9b08bc2b875bd2 (diff) | |
| download | php-mozart-dffb6244ebb432477b83631d68584bbc7186dd94.tar.gz php-mozart-dffb6244ebb432477b83631d68584bbc7186dd94.tar.zst php-mozart-dffb6244ebb432477b83631d68584bbc7186dd94.zip | |
fix(install): treat dev-reference shifts as upgrades
Composer's Transaction fires an UpdateOperation when an installed
package's source/dist reference moved, even if the version string is
unchanged — that is how a `dev-main#abcd` root require pinning a new
commit propagates through `composer install`. Mozart was checking only
(name, version) and short-circuiting to Skip, so the package stayed
pinned to whatever reference installed.json carried.
Compare references in compute_operations and route mismatches into
Action::Update. The trace recorder needs the from-side display string
to include the reference suffix (`dev-master abc123`) so the EXPECT
output matches Composer's UpdateOperation::format; thread that through
PackageOperation::Update as a separate from_full_pretty field while
keeping from_version (sans suffix) for the upgrade-vs-downgrade
direction check, which has to compare normalized versions like
Composer's VersionParser::isUpgrade does.
Unblocks update_reference, update_reference_picks_latest, and
updating_dev_updates_url_and_reference installer fixtures.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart-registry/src')
| -rw-r--r-- | crates/mozart-registry/src/installer_executor/mod.rs | 65 | ||||
| -rw-r--r-- | crates/mozart-registry/src/installer_executor/trace_recorder.rs | 3 |
2 files changed, 62 insertions, 6 deletions
diff --git a/crates/mozart-registry/src/installer_executor/mod.rs b/crates/mozart-registry/src/installer_executor/mod.rs index ff3d7a8..704031f 100644 --- a/crates/mozart-registry/src/installer_executor/mod.rs +++ b/crates/mozart-registry/src/installer_executor/mod.rs @@ -15,6 +15,7 @@ use std::path::PathBuf; +use crate::installed::InstalledPackageEntry; use crate::lockfile::{LockAlias, LockedPackage}; pub mod filesystem; @@ -30,9 +31,13 @@ pub enum PackageOperation<'a> { /// `package.dist`/`package.source`. Install { package: &'a LockedPackage }, /// Replace an existing install with a new version. `from_version` is the - /// pretty version that was installed before. + /// pretty version that was installed before (no reference suffix — + /// drives the upgrade-vs-downgrade direction). `from_full_pretty` is the + /// formatted display string (`dev-master abc123`) used verbatim in the + /// trace output. Update { from_version: &'a str, + from_full_pretty: &'a str, package: &'a LockedPackage, }, /// Mark an alias of a real package as installed. No filesystem effects — @@ -72,15 +77,65 @@ pub fn format_full_pretty_version(pkg: &LockedPackage) -> String { /// alternate pretty version (used by `MarkAliasInstalled` so the alias's /// `3.2.x-dev` text is rendered with the *target's* reference). pub fn format_full_pretty_with_pretty(pretty_version: &str, pkg: &LockedPackage) -> String { - let is_dev = mozart_semver::Version::parse(&pkg.version) + let source_ref = pkg.source.as_ref().and_then(|s| s.reference.as_deref()); + let dist_ref = pkg.dist.as_ref().and_then(|d| d.reference.as_deref()); + let source_type = pkg.source.as_ref().map(|s| s.source_type.as_str()); + format_full_pretty_with_refs( + pretty_version, + &pkg.version, + source_ref, + dist_ref, + source_type, + ) +} + +/// Mirror Composer's `BasePackage::getFullPrettyVersion()` for an +/// `InstalledPackageEntry`. Same display rules as +/// [`format_full_pretty_version`] but pulls source/dist info out of the +/// installed.json `source`/`dist` JSON values. +pub fn format_full_pretty_version_for_installed(entry: &InstalledPackageEntry) -> String { + let source_ref = entry + .source + .as_ref() + .and_then(|v| v.get("reference")) + .and_then(|v| v.as_str()); + let dist_ref = entry + .dist + .as_ref() + .and_then(|v| v.get("reference")) + .and_then(|v| v.as_str()); + let source_type = entry + .source + .as_ref() + .and_then(|v| v.get("type")) + .and_then(|v| v.as_str()); + format_full_pretty_with_refs( + &entry.version, + &entry.version, + source_ref, + dist_ref, + source_type, + ) +} + +/// Core of `BasePackage::getFullPrettyVersion()` factored over raw +/// fields so both [`LockedPackage`] and [`InstalledPackageEntry`] can share +/// the rendering logic. `version` drives the dev-stability check; the result +/// is `pretty_version` plus a reference suffix when the package is a dev +/// branch backed by git/hg (with sha1 references truncated to 7 chars). +fn format_full_pretty_with_refs( + pretty_version: &str, + version: &str, + source_ref: Option<&str>, + dist_ref: Option<&str>, + source_type: Option<&str>, +) -> String { + let is_dev = mozart_semver::Version::parse(version) .map(|v| matches!(v.pre_release.as_deref(), Some("dev")) || v.is_dev_branch) .unwrap_or(false); if !is_dev { return pretty_version.to_string(); } - let source_ref = pkg.source.as_ref().and_then(|s| s.reference.as_deref()); - let dist_ref = pkg.dist.as_ref().and_then(|d| d.reference.as_deref()); - let source_type = pkg.source.as_ref().map(|s| s.source_type.as_str()); // Composer falls back to dist reference only when no source type is set // (or the package isn't git/hg — in which case the dev display is skipped // entirely above). diff --git a/crates/mozart-registry/src/installer_executor/trace_recorder.rs b/crates/mozart-registry/src/installer_executor/trace_recorder.rs index 44fceea..785d161 100644 --- a/crates/mozart-registry/src/installer_executor/trace_recorder.rs +++ b/crates/mozart-registry/src/installer_executor/trace_recorder.rs @@ -69,6 +69,7 @@ impl InstallerExecutor for TraceRecorderExecutor { } PackageOperation::Update { from_version, + from_full_pretty, package, } => { let action = if is_upgrade(from_version, &package.version) { @@ -80,7 +81,7 @@ impl InstallerExecutor for TraceRecorderExecutor { "{} {} ({} => {})", action, package.name, - from_version, + from_full_pretty, format_full_pretty_version(package) )); } |
