diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-03 19:34:31 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-03 19:34:31 +0900 |
| commit | 031b7e0bdf6fc021dbc16fd86d403bda42c90e7b (patch) | |
| tree | afac8612e85ae26a0b1219fe6074d6817d4ab4b0 | |
| parent | d554b62e1b578a88b796f34e6eb82b5c452cd785 (diff) | |
| download | php-mozart-031b7e0bdf6fc021dbc16fd86d403bda42c90e7b.tar.gz php-mozart-031b7e0bdf6fc021dbc16fd86d403bda42c90e7b.tar.zst php-mozart-031b7e0bdf6fc021dbc16fd86d403bda42c90e7b.zip | |
fix(install): skip MarkAliasInstalled when alias was already present
Composer's `Transaction::calculateOperations` only emits a
MarkAliasInstalledOperation when the alias isn't already in
`presentAliasMap`. Mirror that here: walk installed.json for each
package being installed/updated, recover its prior alias set (explicit
`extra.branch-alias` entries plus the synthetic `9999999-dev` alias for
`default-branch: true` dev packages), and suppress the trace line when
the new lock's alias normalized version was already there. Avoids the
spurious "Marking ... as installed" emitted on a same-alias dev ref bump.
| -rw-r--r-- | crates/mozart-registry/src/resolver.rs | 2 | ||||
| -rw-r--r-- | crates/mozart/src/commands/install.rs | 84 | ||||
| -rw-r--r-- | crates/mozart/tests/installer.rs | 6 |
3 files changed, 88 insertions, 4 deletions
diff --git a/crates/mozart-registry/src/resolver.rs b/crates/mozart-registry/src/resolver.rs index 0716246..f7feddd 100644 --- a/crates/mozart-registry/src/resolver.rs +++ b/crates/mozart-registry/src/resolver.rs @@ -208,7 +208,7 @@ fn has_numeric_alias_prefix(branch: &str) -> bool { /// This is the form Composer's `Locker::lockPackages` writes into the /// `aliases` block of `composer.lock` and the form `Pool` indexes for /// constraint matching, so Mozart needs to use it too. -pub(crate) fn normalize_branch_alias_target(alias_target: &str) -> Option<String> { +pub fn normalize_branch_alias_target(alias_target: &str) -> Option<String> { let trimmed = alias_target.trim(); let lower = trimmed.to_lowercase(); let base = lower.strip_suffix("-dev")?; diff --git a/crates/mozart/src/commands/install.rs b/crates/mozart/src/commands/install.rs index 5c56fbc..62a7d21 100644 --- a/crates/mozart/src/commands/install.rs +++ b/crates/mozart/src/commands/install.rs @@ -391,6 +391,72 @@ fn collect_stale_installed_aliases( stale } +/// Collect the alias normalized-versions a previous install recorded for +/// `pkg_name`. Mirrors Composer's `presentAliasMap` seeding: +/// `LocalRepository::loadPackages` runs every installed entry through +/// `ArrayLoader`, which surfaces an `AliasPackage` for each +/// `extra.branch-alias` entry plus the synthetic +/// `9999999.9999999.9999999.9999999-dev` alias for `default-branch: true` +/// dev packages without an explicit alias. The new install/upgrade should +/// only emit a MarkAliasInstalled trace line for an alias that wasn't +/// already in this set. +fn previously_installed_alias_versions( + installed: &installed::InstalledPackages, + pkg_name: &str, +) -> Vec<String> { + let mut out = Vec::new(); + for entry in &installed.packages { + if !entry.name.eq_ignore_ascii_case(pkg_name) { + continue; + } + let version_lower = entry.version.to_lowercase(); + let is_dev_branch = version_lower.starts_with("dev-") || version_lower.ends_with("-dev"); + if !is_dev_branch { + continue; + } + + let mut emitted_explicit_alias = false; + if let Some(branch_alias_map) = entry + .extra_fields + .get("extra") + .and_then(|e| e.get("branch-alias")) + .and_then(|b| b.as_object()) + { + for (source, target) in branch_alias_map { + if !source.eq_ignore_ascii_case(&entry.version) { + continue; + } + let Some(target_str) = target.as_str() else { + continue; + }; + if !target_str.to_lowercase().ends_with("-dev") { + continue; + } + if let Some(normalized) = + mozart_registry::resolver::normalize_branch_alias_target(target_str) + { + out.push(normalized); + emitted_explicit_alias = true; + } + } + } + + // Synthesize the default-branch alias when `default-branch: true` is + // recorded and no explicit branch-alias took its place. Mirrors + // `ArrayLoader::getBranchAlias`'s default-branch fallback. + if !emitted_explicit_alias + && entry + .extra_fields + .get("default-branch") + .and_then(|v| v.as_bool()) + .unwrap_or(false) + { + out.push("9999999.9999999.9999999.9999999-dev".to_string()); + } + } + out +} + /// Compare an installed-package entry's source/dist references with a /// locked package's. Mirrors the reference-equality leg of Composer's /// `Transaction::calculateOperations` update-detection: a same-version @@ -900,9 +966,23 @@ pub async fn install_from_lock( // lock was hand-written without a matching `aliases[]` entry). // The two sources can name the same alias version, so dedupe by // `alias_normalized` to avoid emitting the trace line twice. + // + // Also skip aliases that were already in installed.json under the + // same name+normalized version: Composer's + // `Transaction::calculateOperations` only emits a + // MarkAliasInstalledOperation when the alias is *not* already in + // `presentAliasMap`. An update that keeps the same alias version + // (e.g. `dev-main` ref bump on a `default-branch` package) does + // not retrigger the alias mark. + let already_installed_aliases = + previously_installed_alias_versions(&installed, &pkg.name); let mut emitted_alias_versions: Vec<String> = Vec::new(); for alias in &lock.aliases { if alias.package.eq_ignore_ascii_case(&pkg.name) && alias.version == pkg.version { + if already_installed_aliases.contains(&alias.alias_normalized) { + emitted_alias_versions.push(alias.alias_normalized.clone()); + continue; + } executor .install_package( PackageOperation::MarkAliasInstalled { alias, target: pkg }, @@ -917,6 +997,10 @@ pub async fn install_from_lock( if emitted_alias_versions.contains(&alias.alias_normalized) { continue; } + if already_installed_aliases.contains(&alias.alias_normalized) { + emitted_alias_versions.push(alias.alias_normalized.clone()); + continue; + } executor .install_package( PackageOperation::MarkAliasInstalled { alias, target: pkg }, diff --git a/crates/mozart/tests/installer.rs b/crates/mozart/tests/installer.rs index 6213a6c..7767f55 100644 --- a/crates/mozart/tests/installer.rs +++ b/crates/mozart/tests/installer.rs @@ -352,7 +352,7 @@ installer_fixture!(unbounded_conflict_does_not_match_default_branch_with_numeric installer_fixture!(unbounded_conflict_matches_default_branch); installer_fixture!(update_abandoned_package_required_but_blocked_via_audit_config); installer_fixture!(update_alias); -installer_fixture!(update_alias_lock, ignore); +installer_fixture!(update_alias_lock); installer_fixture!(update_alias_lock2); installer_fixture!(update_all); installer_fixture!(update_all_dry_run); @@ -380,8 +380,8 @@ installer_fixture!( installer_fixture!(update_allow_list_with_dependency_conflict); installer_fixture!(update_changes_url, ignore); installer_fixture!(update_dev_ignores_providers); -installer_fixture!(update_dev_packages_updates_repo_url, ignore); -installer_fixture!(update_dev_to_new_ref_picks_up_changes, ignore); +installer_fixture!(update_dev_packages_updates_repo_url); +installer_fixture!(update_dev_to_new_ref_picks_up_changes); installer_fixture!(update_downgrades_unstable_packages, ignore); installer_fixture!(update_ignore_platform_package_requirement_list); installer_fixture!(update_ignore_platform_package_requirement_list_upper_bounds); |
