aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart/src
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-01 22:26:16 +0900
committernsfisis <nsfisis@gmail.com>2026-05-01 22:26:16 +0900
commit4d587407cc9471dc8bfc0544eac0f8c7041fba0d (patch)
tree7b45b8e6e3dc5b1b3325b7cc6783375273a56784 /crates/mozart/src
parent8a87adf120d5057b06d0474b293fab079e1ce967 (diff)
downloadphp-mozart-4d587407cc9471dc8bfc0544eac0f8c7041fba0d.tar.gz
php-mozart-4d587407cc9471dc8bfc0544eac0f8c7041fba0d.tar.zst
php-mozart-4d587407cc9471dc8bfc0544eac0f8c7041fba0d.zip
feat(registry): support inline 'type: package' repositories
Composer's PackageRepository lets composer.json embed full package metadata under repositories[].package, mirroring the on-disk Packagist response shape. The vast majority of installer fixtures under composer/tests/Composer/Test/Fixtures/installer (179 of 189) rely on this — they declare every package they need inline rather than hitting the network. Three pieces wire this into Mozart: 1. mozart-core::package::RawRepository: relax `url` to Option<String> (Composer enforces presence per repo type, not at JSON parse) and add `package: Option<Value>` to receive the inline definition, which can be a single object or an array. 2. mozart-registry::inline_package: a new module that walks `&[RawRepository]`, picks out type=package entries, and reshapes each `package` payload into a PackagistVersion (auto-computing version_normalized when omitted, matching Packagist's output). 3. resolver::resolve and lockfile::generate_lock_file: feed inline packages into the SAT pool builder and short-circuit the Packagist fetch when generating the lock entry for a resolved inline package. The package-name set is shared with the existing VCS-skip logic so the seed and transitive loops don't double-fetch. One additional install-time change: in install_from_lock, packages that have neither dist nor source are now skipped silently instead of bailing with "no dist or source information". This mirrors Composer's MetapackageInstaller (no installer for type=metapackage) and is also what Composer's own AllFunctionalTest exercises via InstallationManagerMock — most inline-package fixtures define synthetic packages with no download metadata, expecting the install operation to be recorded but not actually run. Net effect: installer fixture scoreboard jumps from 7/187 to 103/187. The 84 fixtures still ignored hit issues unrelated to inline-package plumbing — aliases, replace/provide chains, dev-reference handling, allow-list updates, etc. — and are tracked separately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart/src')
-rw-r--r--crates/mozart/src/commands/init.rs9
-rw-r--r--crates/mozart/src/commands/install.rs26
2 files changed, 22 insertions, 13 deletions
diff --git a/crates/mozart/src/commands/init.rs b/crates/mozart/src/commands/init.rs
index 25600f7..49176ab 100644
--- a/crates/mozart/src/commands/init.rs
+++ b/crates/mozart/src/commands/init.rs
@@ -700,12 +700,17 @@ fn parse_repositories(repos: &[String]) -> anyhow::Result<Vec<RawRepository>> {
.as_str()
.ok_or_else(|| anyhow::anyhow!("Repository JSON must contain a 'url' field"))?
.to_string();
- result.push(RawRepository { repo_type, url });
+ result.push(RawRepository {
+ repo_type,
+ url: Some(url),
+ package: None,
+ });
} else {
// Plain URL
result.push(RawRepository {
repo_type: "vcs".to_string(),
- url: repo.clone(),
+ url: Some(repo.clone()),
+ package: None,
});
}
}
diff --git a/crates/mozart/src/commands/install.rs b/crates/mozart/src/commands/install.rs
index a10dd69..1cc4e6f 100644
--- a/crates/mozart/src/commands/install.rs
+++ b/crates/mozart/src/commands/install.rs
@@ -608,18 +608,22 @@ pub async fn install_from_lock(
continue;
}
+ // A package with neither dist nor source has no install action.
+ // This covers Composer's `type: metapackage` (modeled explicitly
+ // as "no installer") and inline `type: package` definitions used
+ // in test fixtures that intentionally omit download metadata.
+ // Mozart records the operation and the installed.json entry but
+ // performs no filesystem work, mirroring Composer's
+ // MetapackageInstaller.
+ if pkg.dist.is_none() && pkg.source.is_none() {
+ continue;
+ }
+
let dist = pkg.dist.as_ref().ok_or_else(|| {
- if pkg.source.is_some() {
- anyhow::anyhow!(
- "Package {} has no dist information. Use --prefer-source to install from VCS.",
- pkg.name,
- )
- } else {
- anyhow::anyhow!(
- "Package {} has no dist or source information",
- pkg.name,
- )
- }
+ anyhow::anyhow!(
+ "Package {} has no dist information. Use --prefer-source to install from VCS.",
+ pkg.name,
+ )
})?;
let mut progress = make_progress(!config.no_progress, &pkg.name, &pkg.version);