aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart-registry/src
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-08 23:40:37 +0900
committernsfisis <nsfisis@gmail.com>2026-05-08 23:41:06 +0900
commit18d5a78fee75e0a466355e6edfb634d8f7a7565a (patch)
tree68837bab501f8725bd0af1068adb892d2ca7c4dd /crates/mozart-registry/src
parentd770693bac655da4a21144b4cae7592536fecb8b (diff)
downloadphp-mozart-18d5a78fee75e0a466355e6edfb634d8f7a7565a.tar.gz
php-mozart-18d5a78fee75e0a466355e6edfb634d8f7a7565a.tar.zst
php-mozart-18d5a78fee75e0a466355e6edfb634d8f7a7565a.zip
fix(require): align with Composer's RequireCommand pipeline
- Add mozart-registry::version_selector::VersionSelector mirroring Composer\Package\Version\VersionSelector; wraps find_best_candidate and find_recommended_require_version_string for per-arg resolution - Decompose execute() into named helpers matching Composer's structure: CommandState, revert_composer_file, get_inconsistent_require_keys, get_packages_by_require_key, update_file, update_file_cleanly (stub for PR 3), do_update, update_requirements_after_resolution (stub for PR 2) - Fix firstRequire gating: compute first_require from the original file before applying changes; apply setUpdateAllowList only when !first_require and lock exists (prevents over-pinning on fresh projects) - Add --fixed gate: bail when fixed && !dev && type != "project", matching Composer L173-189 wording verbatim - Wire --no-security-blocking + COMPOSER_NO_SECURITY_BLOCKING env var into block_insecure in ResolveRequest (was always false) - Wire COMPOSER_NO_AUDIT env var to skip audit step (tracked) - Match Composer's revertComposerFile messaging: "deleting <file>" for newly-created, "reverting <file> and <lock> to their" / "to its" for existing files; also removes lock file on newly-created revert - Auto-create "{\n}\n" when composer.json is missing or empty, mirroring Composer L138-152; delete file on dry-run cleanup (finally block) - Add resolution-failure hint: "You can also try re-running mozart require with an explicit version constraint…" for unversioned packages - Update deprecated-flag warnings to stderr (write_error) with Composer-matching wording for --no-suggest Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart-registry/src')
-rw-r--r--crates/mozart-registry/src/advisory.rs31
-rw-r--r--crates/mozart-registry/src/lib.rs1
-rw-r--r--crates/mozart-registry/src/version_selector.rs48
3 files changed, 65 insertions, 15 deletions
diff --git a/crates/mozart-registry/src/advisory.rs b/crates/mozart-registry/src/advisory.rs
index 97242b3..8cf112e 100644
--- a/crates/mozart-registry/src/advisory.rs
+++ b/crates/mozart-registry/src/advisory.rs
@@ -230,29 +230,30 @@ impl Auditor {
}
// Check by advisory ID
- if is_active
- && let Some(reason) = ignore_list.get(&adv.advisory_id) {
- is_active = false;
- ignore_reason = reason.clone();
- }
+ if is_active && let Some(reason) = ignore_list.get(&adv.advisory_id) {
+ is_active = false;
+ ignore_reason = reason.clone();
+ }
// Check by severity
if is_active
&& let Some(ref sev) = adv.severity
- && let Some(reason) = ignored_severities.get(sev.as_str()) {
- is_active = false;
- ignore_reason = reason
- .clone()
- .or_else(|| Some(format!("{sev} severity is ignored")));
- }
+ && let Some(reason) = ignored_severities.get(sev.as_str())
+ {
+ is_active = false;
+ ignore_reason = reason
+ .clone()
+ .or_else(|| Some(format!("{sev} severity is ignored")));
+ }
// Check by CVE
if is_active
&& let Some(ref cve) = adv.cve
- && let Some(reason) = ignore_list.get(cve.as_str()) {
- is_active = false;
- ignore_reason = reason.clone();
- }
+ && let Some(reason) = ignore_list.get(cve.as_str())
+ {
+ is_active = false;
+ ignore_reason = reason.clone();
+ }
// Check by source remote IDs
if is_active {
diff --git a/crates/mozart-registry/src/lib.rs b/crates/mozart-registry/src/lib.rs
index 8f9af91..e35056c 100644
--- a/crates/mozart-registry/src/lib.rs
+++ b/crates/mozart-registry/src/lib.rs
@@ -15,3 +15,4 @@ pub mod repository_filter;
pub mod resolver;
pub mod vcs_bridge;
pub mod version;
+pub mod version_selector;
diff --git a/crates/mozart-registry/src/version_selector.rs b/crates/mozart-registry/src/version_selector.rs
new file mode 100644
index 0000000..7aa409e
--- /dev/null
+++ b/crates/mozart-registry/src/version_selector.rs
@@ -0,0 +1,48 @@
+use crate::cache::Cache;
+use crate::packagist::{self, PackagistVersion};
+use crate::version;
+use mozart_core::package::Stability;
+
+/// Mirrors `Composer\Package\Version\VersionSelector`.
+pub struct VersionSelector {
+ preferred_stability: Stability,
+ repo_cache: Cache,
+}
+
+impl VersionSelector {
+ pub fn new(preferred_stability: Stability, repo_cache: Cache) -> Self {
+ Self {
+ preferred_stability,
+ repo_cache,
+ }
+ }
+
+ /// Fetch versions from Packagist and pick the best candidate.
+ /// Mirrors `VersionSelector::findBestCandidate()`.
+ pub async fn find_best_candidate(
+ &self,
+ package_name: &str,
+ ) -> anyhow::Result<Option<PackagistVersion>> {
+ let versions = packagist::fetch_package_versions(package_name, &self.repo_cache).await?;
+ Ok(version::find_best_candidate(&versions, self.preferred_stability).cloned())
+ }
+
+ /// Generate a recommended constraint string from a concrete version.
+ /// Mirrors `VersionSelector::findRecommendedRequireVersion()`.
+ pub fn find_recommended_require_version_string(
+ &self,
+ pkg: &PackagistVersion,
+ fixed: bool,
+ ) -> String {
+ if fixed {
+ pkg.version.clone()
+ } else {
+ let stability = version::stability_of(&pkg.version_normalized);
+ version::find_recommended_require_version(
+ &pkg.version,
+ &pkg.version_normalized,
+ stability,
+ )
+ }
+ }
+}