aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-07-24 20:22:47 +0900
committernsfisis <nsfisis@gmail.com>2026-07-24 20:22:47 +0900
commitdacc1cb0d7d1397701166571d74580a706c2ce73 (patch)
treee8f5269c7a88282ade45d6bd9c5770b8c008fc03
parent929b2c938ba8c2b83f360b0ba3a04f64075282f8 (diff)
downloadphp-shirabe-dacc1cb0d7d1397701166571d74580a706c2ce73.tar.gz
php-shirabe-dacc1cb0d7d1397701166571d74580a706c2ce73.tar.zst
php-shirabe-dacc1cb0d7d1397701166571d74580a706c2ce73.zip
fix(locker): return stability-flags as int, not string
Locker::get_stability_flags returned IndexMap<String, String>, converting each value via PhpMixed::as_string(), which only matches the String variant. composer.lock's "stability-flags" values are always JSON integers (BasePackage::STABILITIES), so every flag silently decoded to "" and installer.rs's downstream .parse::<i64>() defaulted it to 0 (stable). This made any locked package pinned via a non-stable stability-flags entry look "unacceptable" during `composer install`, silently dropping it from the solver's pool instead of fixing/requiring it — turning a real dependency conflict into a spurious "lock file needs changes" result. Return i64 directly, matching RootPackageInterface::get_stability_flags and set_lock_data's existing convention for this same PHP array shape.
-rw-r--r--crates/shirabe/src/installer.rs6
-rw-r--r--crates/shirabe/src/package/locker.rs11
-rw-r--r--crates/shirabe/tests/installer_test.rs4
3 files changed, 8 insertions, 13 deletions
diff --git a/crates/shirabe/src/installer.rs b/crates/shirabe/src/installer.rs
index 6d943ea7..cfe2432c 100644
--- a/crates/shirabe/src/installer.rs
+++ b/crates/shirabe/src/installer.rs
@@ -1373,16 +1373,10 @@ impl Installer {
.borrow_mut()
.get_minimum_stability()
.unwrap_or_else(|_| String::new());
- // locker stores stability flags as stringified ints; recover the int form here.
stability_flags = self
.locker
.borrow_mut()
.get_stability_flags()
- .map(|m| {
- m.into_iter()
- .map(|(k, v)| (k, v.parse::<i64>().unwrap_or(0)))
- .collect()
- })
.unwrap_or_default();
let mut tmp: IndexMap<String, AnyConstraint> = IndexMap::new();
diff --git a/crates/shirabe/src/package/locker.rs b/crates/shirabe/src/package/locker.rs
index f7542d1f..535b5fdf 100644
--- a/crates/shirabe/src/package/locker.rs
+++ b/crates/shirabe/src/package/locker.rs
@@ -350,8 +350,9 @@ impl Locker {
.to_string())
}
- /// @return array<string, string>
- pub fn get_stability_flags(&mut self) -> anyhow::Result<IndexMap<String, String>> {
+ /// @return array<string, int> despite the upstream `@return array<string, string>` docblock;
+ /// the values are BasePackage::STABILITIES ints, written verbatim by set_lock_data.
+ pub fn get_stability_flags(&mut self) -> anyhow::Result<IndexMap<String, i64>> {
let lock_data = self.get_lock_data()?;
Ok(lock_data
@@ -359,7 +360,7 @@ impl Locker {
.and_then(|v| match v {
PhpMixed::Array(m) => Some(
m.iter()
- .map(|(k, v)| (k.clone(), v.as_string().unwrap_or("").to_string()))
+ .map(|(k, v)| (k.clone(), v.as_int().unwrap_or(0)))
.collect(),
),
_ => None,
@@ -1016,7 +1017,7 @@ pub trait LockerInterface: std::fmt::Debug {
fn get_dev_package_names(&mut self) -> anyhow::Result<Vec<String>>;
fn get_platform_requirements(&mut self, with_dev_reqs: bool) -> anyhow::Result<Vec<Link>>;
fn get_minimum_stability(&mut self) -> anyhow::Result<String>;
- fn get_stability_flags(&mut self) -> anyhow::Result<IndexMap<String, String>>;
+ fn get_stability_flags(&mut self) -> anyhow::Result<IndexMap<String, i64>>;
fn get_prefer_stable(&mut self) -> anyhow::Result<Option<bool>>;
fn get_prefer_lowest(&mut self) -> anyhow::Result<Option<bool>>;
fn get_platform_overrides(&mut self) -> anyhow::Result<IndexMap<String, String>>;
@@ -1084,7 +1085,7 @@ impl LockerInterface for Locker {
self.get_minimum_stability()
}
- fn get_stability_flags(&mut self) -> anyhow::Result<IndexMap<String, String>> {
+ fn get_stability_flags(&mut self) -> anyhow::Result<IndexMap<String, i64>> {
self.get_stability_flags()
}
diff --git a/crates/shirabe/tests/installer_test.rs b/crates/shirabe/tests/installer_test.rs
index c8b47b86..b4dc3811 100644
--- a/crates/shirabe/tests/installer_test.rs
+++ b/crates/shirabe/tests/installer_test.rs
@@ -1331,7 +1331,7 @@ pool_optimizer_test! {
pool_optimizer_conflict_downgrade_nested => "conflict-downgrade-nested.test";
pool_optimizer_conflict_downgrade => "conflict-downgrade.test";
pool_optimizer_conflict_on_root_with_alias_prevents_update_if_not_required => "conflict-on-root-with-alias-prevents-update-if-not-required.test";
- pool_optimizer_conflict_with_alias_in_lock_does_prevents_install => "conflict-with-alias-in-lock-does-prevents-install.test", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=1";
+ pool_optimizer_conflict_with_alias_in_lock_does_prevents_install => "conflict-with-alias-in-lock-does-prevents-install.test";
pool_optimizer_conflict_with_alias_prevents_update_if_not_required => "conflict-with-alias-prevents-update-if-not-required.test";
pool_optimizer_conflict_with_alias_prevents_update => "conflict-with-alias-prevents-update.test";
pool_optimizer_conflict_with_all_dependencies_option_dont_recommend_to_use_it => "conflict-with-all-dependencies-option-dont-recommend-to-use-it.test";
@@ -1521,7 +1521,7 @@ raw_pool_test! {
raw_pool_conflict_downgrade_nested => "conflict-downgrade-nested.test";
raw_pool_conflict_downgrade => "conflict-downgrade.test";
raw_pool_conflict_on_root_with_alias_prevents_update_if_not_required => "conflict-on-root-with-alias-prevents-update-if-not-required.test";
- raw_pool_conflict_with_alias_in_lock_does_prevents_install => "conflict-with-alias-in-lock-does-prevents-install.test", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=0";
+ raw_pool_conflict_with_alias_in_lock_does_prevents_install => "conflict-with-alias-in-lock-does-prevents-install.test";
raw_pool_conflict_with_alias_prevents_update_if_not_required => "conflict-with-alias-prevents-update-if-not-required.test";
raw_pool_conflict_with_alias_prevents_update => "conflict-with-alias-prevents-update.test";
raw_pool_conflict_with_all_dependencies_option_dont_recommend_to_use_it => "conflict-with-all-dependencies-option-dont-recommend-to-use-it.test";