aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-03 12:15:02 +0900
committernsfisis <nsfisis@gmail.com>2026-05-03 12:15:02 +0900
commit7badb54195131da9c3561c351138c0ba083e38e4 (patch)
tree0498ab8c3bfcf90e01fcecf451a10ad3844514ff /crates
parentdffb6244ebb432477b83631d68584bbc7186dd94 (diff)
downloadphp-mozart-7badb54195131da9c3561c351138c0ba083e38e4.tar.gz
php-mozart-7badb54195131da9c3561c351138c0ba083e38e4.tar.zst
php-mozart-7badb54195131da9c3561c351138c0ba083e38e4.zip
fix(install): switch update trace to dist-ref mode when source refs match
Composer's UpdateOperation::format renders the from/to versions through DISPLAY_SOURCE_REF_IF_DEV first, but if both sides come out identical it re-renders in DISPLAY_SOURCE_REF (when source refs differ) or DISPLAY_DIST_REF (when only dist refs differ) so the trace doesn't show a useless `pkg (X => X)` line. Mozart skipped the switch and emitted the default form on both halves, so a same-version-different-dist-ref update showed up as `dev-master def000 => dev-master def000` instead of `dev-master def000 => dev-master`. Add format_update_pretty_versions to render the pair Composer's way and plumb the resolved to_full_pretty through PackageOperation::Update so the trace recorder uses it verbatim. Unblocks update_installed_reference and update_picks_up_change_of_vcs_type installer fixtures. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates')
-rw-r--r--crates/mozart-registry/src/installer_executor/mod.rs87
-rw-r--r--crates/mozart-registry/src/installer_executor/trace_recorder.rs6
-rw-r--r--crates/mozart/src/commands/install.rs35
-rw-r--r--crates/mozart/tests/installer.rs4
4 files changed, 110 insertions, 22 deletions
diff --git a/crates/mozart-registry/src/installer_executor/mod.rs b/crates/mozart-registry/src/installer_executor/mod.rs
index 704031f..a774490 100644
--- a/crates/mozart-registry/src/installer_executor/mod.rs
+++ b/crates/mozart-registry/src/installer_executor/mod.rs
@@ -32,12 +32,15 @@ pub enum PackageOperation<'a> {
Install { package: &'a LockedPackage },
/// Replace an existing install with a new version. `from_version` is the
/// 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.
+ /// drives the upgrade-vs-downgrade direction). `from_full_pretty` /
+ /// `to_full_pretty` are the formatted display strings used verbatim in
+ /// the trace output; the caller renders them via
+ /// [`format_update_pretty_versions`] so the SOURCE_REF / DIST_REF mode
+ /// switch from Composer's `UpdateOperation::format` lands on both sides.
Update {
from_version: &'a str,
from_full_pretty: &'a str,
+ to_full_pretty: &'a str,
package: &'a LockedPackage,
},
/// Mark an alias of a real package as installed. No filesystem effects —
@@ -118,6 +121,84 @@ pub fn format_full_pretty_version_for_installed(entry: &InstalledPackageEntry) -
)
}
+/// Render the from/to display strings for an update trace line, mirroring
+/// Composer's `UpdateOperation::format`. Defaults to `DISPLAY_SOURCE_REF_IF_DEV`,
+/// then if both sides render identically:
+///
+/// - source references differ → re-render in `DISPLAY_SOURCE_REF` mode,
+/// - else dist references differ → re-render in `DISPLAY_DIST_REF` mode.
+///
+/// Without the switch, two same-version-different-reference packages would
+/// produce a useless `pkg (X => X)` trace line.
+pub fn format_update_pretty_versions(
+ from_entry: &InstalledPackageEntry,
+ to_pkg: &LockedPackage,
+) -> (String, String) {
+ let from_default = format_full_pretty_version_for_installed(from_entry);
+ let to_default = format_full_pretty_version(to_pkg);
+ if from_default != to_default {
+ return (from_default, to_default);
+ }
+
+ let from_source_ref = from_entry
+ .source
+ .as_ref()
+ .and_then(|v| v.get("reference"))
+ .and_then(|v| v.as_str());
+ let from_source_type = from_entry
+ .source
+ .as_ref()
+ .and_then(|v| v.get("type"))
+ .and_then(|v| v.as_str());
+ let to_source_ref = to_pkg.source.as_ref().and_then(|s| s.reference.as_deref());
+ let to_source_type = to_pkg.source.as_ref().map(|s| s.source_type.as_str());
+
+ if from_source_ref != to_source_ref {
+ return (
+ format_with_explicit_reference(&from_entry.version, from_source_ref, from_source_type),
+ format_with_explicit_reference(&to_pkg.version, to_source_ref, to_source_type),
+ );
+ }
+
+ let from_dist_ref = from_entry
+ .dist
+ .as_ref()
+ .and_then(|v| v.get("reference"))
+ .and_then(|v| v.as_str());
+ let to_dist_ref = to_pkg.dist.as_ref().and_then(|d| d.reference.as_deref());
+
+ if from_dist_ref != to_dist_ref {
+ return (
+ format_with_explicit_reference(&from_entry.version, from_dist_ref, from_source_type),
+ format_with_explicit_reference(&to_pkg.version, to_dist_ref, to_source_type),
+ );
+ }
+
+ (from_default, to_default)
+}
+
+/// Render `pretty_version` with an explicitly chosen reference, mirroring
+/// Composer's `BasePackage::getFullPrettyVersion` with `DISPLAY_SOURCE_REF`
+/// or `DISPLAY_DIST_REF`: skip the dev-stability gate, just truncate sha1
+/// references and concatenate. A `None` reference falls back to the bare
+/// pretty version.
+fn format_with_explicit_reference(
+ pretty_version: &str,
+ reference: Option<&str>,
+ source_type: Option<&str>,
+) -> String {
+ let Some(reference) = reference else {
+ return pretty_version.to_string();
+ };
+ if matches!(source_type, Some("svn")) {
+ return format!("{} {}", pretty_version, reference);
+ }
+ if reference.len() == 40 {
+ return format!("{} {}", pretty_version, &reference[..7]);
+ }
+ format!("{} {}", pretty_version, reference)
+}
+
/// 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
diff --git a/crates/mozart-registry/src/installer_executor/trace_recorder.rs b/crates/mozart-registry/src/installer_executor/trace_recorder.rs
index 785d161..5c49160 100644
--- a/crates/mozart-registry/src/installer_executor/trace_recorder.rs
+++ b/crates/mozart-registry/src/installer_executor/trace_recorder.rs
@@ -70,6 +70,7 @@ impl InstallerExecutor for TraceRecorderExecutor {
PackageOperation::Update {
from_version,
from_full_pretty,
+ to_full_pretty,
package,
} => {
let action = if is_upgrade(from_version, &package.version) {
@@ -79,10 +80,7 @@ impl InstallerExecutor for TraceRecorderExecutor {
};
self.trace.push(format!(
"{} {} ({} => {})",
- action,
- package.name,
- from_full_pretty,
- format_full_pretty_version(package)
+ action, package.name, from_full_pretty, to_full_pretty
));
}
PackageOperation::MarkAliasInstalled { alias, target } => {
diff --git a/crates/mozart/src/commands/install.rs b/crates/mozart/src/commands/install.rs
index f809a86..7aac3af 100644
--- a/crates/mozart/src/commands/install.rs
+++ b/crates/mozart/src/commands/install.rs
@@ -5,7 +5,7 @@ use mozart_core::console_format;
use mozart_registry::installed;
use mozart_registry::installer_executor::{
ExecuteContext, FilesystemExecutor, InstallerExecutor, PackageOperation,
- format_full_pretty_version_for_installed,
+ format_full_pretty_version, format_update_pretty_versions,
};
use mozart_registry::lockfile;
use std::collections::BTreeMap;
@@ -675,10 +675,11 @@ pub async fn install_from_lock(
}
for (pkg, action) in &ops {
- // Owned scratch buffer the Update branch borrows for
- // `PackageOperation::Update::from_version`. Declared at loop
- // scope so the borrow outlives the await call.
- let from_version_buf;
+ // Owned scratch buffers the Update branch borrows for
+ // `PackageOperation::Update::{from_full_pretty,to_full_pretty}`.
+ // Declared at loop scope so the borrows outlive the await call.
+ let from_full_pretty_buf;
+ let to_full_pretty_buf;
let op = match action {
Action::Skip => continue,
Action::Install => {
@@ -695,24 +696,32 @@ pub async fn install_from_lock(
pkg.name,
pkg.version
));
- // Pull the previously-installed version from installed.json
+ // Pull the previously-installed entry from installed.json
// so the trace recorder can format
// `Upgrading pkg (oldVersion => newVersion)`. The plain
// version drives the upgrade/downgrade direction; the
- // full-pretty form (with the dev reference suffix) is
- // what shows up in the trace, mirroring Composer's
- // `UpdateOperation::format`.
+ // full-pretty pair is rendered through
+ // `format_update_pretty_versions` so Composer's
+ // SOURCE_REF / DIST_REF mode switch (used when both
+ // sides would otherwise render identically) lands on
+ // both halves.
let from_entry = installed
.packages
.iter()
.find(|p| p.name.eq_ignore_ascii_case(&pkg.name));
let from_version = from_entry.map(|p| p.version.as_str()).unwrap_or("");
- from_version_buf = from_entry
- .map(format_full_pretty_version_for_installed)
- .unwrap_or_default();
+ if let Some(entry) = from_entry {
+ let (from, to) = format_update_pretty_versions(entry, pkg);
+ from_full_pretty_buf = from;
+ to_full_pretty_buf = to;
+ } else {
+ from_full_pretty_buf = String::new();
+ to_full_pretty_buf = format_full_pretty_version(pkg);
+ }
PackageOperation::Update {
from_version,
- from_full_pretty: &from_version_buf,
+ from_full_pretty: &from_full_pretty_buf,
+ to_full_pretty: &to_full_pretty_buf,
package: pkg,
}
}
diff --git a/crates/mozart/tests/installer.rs b/crates/mozart/tests/installer.rs
index 93c3496..c680764 100644
--- a/crates/mozart/tests/installer.rs
+++ b/crates/mozart/tests/installer.rs
@@ -404,7 +404,7 @@ installer_fixture!(update_ignore_platform_package_requirement_wildcard);
installer_fixture!(update_ignore_platform_package_requirements);
installer_fixture!(update_installed_alias);
installer_fixture!(update_installed_alias_dry_run);
-installer_fixture!(update_installed_reference, ignore);
+installer_fixture!(update_installed_reference);
installer_fixture!(update_installed_reference_dry_run);
installer_fixture!(update_mirrors_changes_url, ignore);
installer_fixture!(update_mirrors_fails_with_new_req, ignore);
@@ -414,7 +414,7 @@ installer_fixture!(update_package_present_in_lock_but_not_at_all_in_remote);
installer_fixture!(update_package_present_in_lock_but_not_in_remote);
installer_fixture!(update_package_present_in_lock_but_not_in_remote_due_to_min_stability);
installer_fixture!(update_package_present_in_lower_repo_prio_but_not_main_due_to_min_stability);
-installer_fixture!(update_picks_up_change_of_vcs_type, ignore);
+installer_fixture!(update_picks_up_change_of_vcs_type);
installer_fixture!(update_prefer_lowest_stable);
installer_fixture!(update_reference);
installer_fixture!(update_reference_picks_latest);