aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-02 16:53:41 +0900
committernsfisis <nsfisis@gmail.com>2026-05-02 16:53:41 +0900
commitc1733d88510b7afb88f7a17849de514365e42c84 (patch)
tree57f9eb854cb226059df90d190626e090ae07d639
parent82501a36a0fa6725d656742da42c860e75a89b89 (diff)
downloadphp-mozart-c1733d88510b7afb88f7a17849de514365e42c84.tar.gz
php-mozart-c1733d88510b7afb88f7a17849de514365e42c84.tar.zst
php-mozart-c1733d88510b7afb88f7a17849de514365e42c84.zip
refactor(registry): introduce Repository and InstallerExecutor traits
Sets up DI scaffolding for in-process installer E2E tests, mirroring how Composer's PHPUnit suite swaps Packagist (FactoryMock) and the install manager (InstallationManagerMock) without touching the network or filesystem. Additions: - Repository trait + RepositorySet (Composer's RepositoryInterface analog), with PackagistRepository, InlinePackageRepository, VcsRepository impls. - InstallerExecutor trait (Composer's InstallationManager analog) with FilesystemExecutor extracted from install_from_lock. install_from_lock now delegates per-package install/uninstall verbs to FilesystemExecutor; console output orchestration stays in the caller so existing --EXPECT-OUTPUT-shape assertions remain comparable. No behavior change - all 136 enabled installer fixtures still pass. Also tightens the installer_fixture\! ignore form to a single token (installer_fixture\!(name, ignore)) for readability. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
-rw-r--r--Cargo.lock12
-rw-r--r--Cargo.toml1
-rw-r--r--crates/mozart-registry/Cargo.toml1
-rw-r--r--crates/mozart-registry/src/installer_executor/filesystem.rs222
-rw-r--r--crates/mozart-registry/src/installer_executor/mod.rs84
-rw-r--r--crates/mozart-registry/src/lib.rs2
-rw-r--r--crates/mozart-registry/src/repository/inline_package_repo.rs63
-rw-r--r--crates/mozart-registry/src/repository/mod.rs127
-rw-r--r--crates/mozart-registry/src/repository/packagist_repo.rs58
-rw-r--r--crates/mozart-registry/src/repository/vcs_repo.rs63
-rw-r--r--crates/mozart/src/commands/install.rs202
-rw-r--r--crates/mozart/tests/installer.rs226
12 files changed, 711 insertions, 350 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d1ae1cb..890314b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -98,6 +98,17 @@ dependencies = [
]
[[package]]
+name = "async-trait"
+version = "0.1.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1170,6 +1181,7 @@ name = "mozart-registry"
version = "0.1.0"
dependencies = [
"anyhow",
+ "async-trait",
"filetime",
"flate2",
"md5",
diff --git a/Cargo.toml b/Cargo.toml
index 324c0a5..e358c5c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,6 +20,7 @@ mozart-test-harness = { path = "crates/mozart-test-harness" }
mozart-vcs = { path = "crates/mozart-vcs" }
anyhow = "1.0.102"
assert_cmd = "2.1.2"
+async-trait = "0.1.83"
base64 = "0.22.1"
bzip2 = "0.5.2"
clap = { version = "4.5.60", features = ["derive"] }
diff --git a/crates/mozart-registry/Cargo.toml b/crates/mozart-registry/Cargo.toml
index af65026..abde30d 100644
--- a/crates/mozart-registry/Cargo.toml
+++ b/crates/mozart-registry/Cargo.toml
@@ -10,6 +10,7 @@ mozart-sat-resolver.workspace = true
mozart-semver.workspace = true
mozart-vcs.workspace = true
anyhow.workspace = true
+async-trait.workspace = true
filetime.workspace = true
flate2.workspace = true
md5.workspace = true
diff --git a/crates/mozart-registry/src/installer_executor/filesystem.rs b/crates/mozart-registry/src/installer_executor/filesystem.rs
new file mode 100644
index 0000000..82acc42
--- /dev/null
+++ b/crates/mozart-registry/src/installer_executor/filesystem.rs
@@ -0,0 +1,222 @@
+//! Production [`InstallerExecutor`] that touches the real filesystem.
+//!
+//! This is the verb behind `mozart install` / `mozart update` — it pulls
+//! dist archives via [`crate::downloader`], clones VCS sources via
+//! [`mozart_vcs`], and removes vendor directories. Test code substitutes a
+//! recording-only executor instead (added in a later step).
+
+use std::path::Path;
+
+use crate::cache::Cache;
+use crate::downloader;
+
+use super::{ExecuteContext, InstallerExecutor, PackageOperation};
+
+pub struct FilesystemExecutor {
+ files_cache: Cache,
+}
+
+impl FilesystemExecutor {
+ pub fn new(files_cache: Cache) -> Self {
+ Self { files_cache }
+ }
+}
+
+#[async_trait::async_trait]
+impl InstallerExecutor for FilesystemExecutor {
+ async fn install_package(
+ &mut self,
+ op: PackageOperation<'_>,
+ ctx: &ExecuteContext,
+ ) -> anyhow::Result<()> {
+ let pkg = op.package();
+
+ // Try source install if --prefer-source and source info is available.
+ if ctx.prefer_source
+ && let Some(source) = &pkg.source
+ {
+ return install_from_source(
+ &source.source_type,
+ &source.url,
+ source.reference.as_deref().unwrap_or("HEAD"),
+ &ctx.vendor_dir,
+ &pkg.name,
+ );
+ }
+
+ // 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() {
+ return Ok(());
+ }
+
+ let dist = pkg.dist.as_ref().ok_or_else(|| {
+ anyhow::anyhow!(
+ "Package {} has no dist information. Use --prefer-source to install from VCS.",
+ pkg.name,
+ )
+ })?;
+
+ let mut progress = downloader::DownloadProgress::new(
+ !ctx.no_progress,
+ format!("{} ({})", pkg.name, pkg.version),
+ );
+
+ downloader::install_package(
+ &dist.url,
+ &dist.dist_type,
+ dist.shasum.as_deref(),
+ &ctx.vendor_dir,
+ &pkg.name,
+ Some(&mut progress),
+ &self.files_cache,
+ )
+ .await?;
+
+ progress.finish();
+ Ok(())
+ }
+
+ fn uninstall_package(&mut self, name: &str, ctx: &ExecuteContext) -> anyhow::Result<()> {
+ let pkg_dir = ctx.vendor_dir.join(name);
+ if pkg_dir.exists() {
+ std::fs::remove_dir_all(&pkg_dir)?;
+ }
+ Ok(())
+ }
+
+ fn cleanup_after_uninstalls(&mut self, ctx: &ExecuteContext) -> anyhow::Result<()> {
+ cleanup_empty_vendor_dirs(&ctx.vendor_dir)
+ }
+}
+
+/// Remove empty vendor namespace directories left behind after package
+/// removals. Skips the `composer/` and `bin/` directories. Mirrors the
+/// post-uninstall cleanup Composer does in `LibraryInstaller::removeCode`.
+fn cleanup_empty_vendor_dirs(vendor_dir: &Path) -> anyhow::Result<()> {
+ if let Ok(entries) = std::fs::read_dir(vendor_dir) {
+ for entry in entries.flatten() {
+ let path = entry.path();
+ if path.is_dir() {
+ let name = entry.file_name().to_string_lossy().to_string();
+ if name == "composer" || name == "bin" {
+ continue;
+ }
+ if std::fs::read_dir(&path)?.next().is_none() {
+ std::fs::remove_dir(&path)?;
+ }
+ }
+ }
+ }
+ Ok(())
+}
+
+/// Install a package from VCS source (git/svn/hg). Lifted from the previous
+/// `commands/install.rs::install_from_source`. Mirrors the per-driver
+/// dispatch in `Composer\Downloader\VcsDownloader::install`.
+fn install_from_source(
+ source_type: &str,
+ url: &str,
+ reference: &str,
+ vendor_dir: &Path,
+ package_name: &str,
+) -> anyhow::Result<()> {
+ let target = vendor_dir.join(package_name);
+ if target.exists() {
+ std::fs::remove_dir_all(&target)?;
+ }
+
+ match source_type {
+ "git" => {
+ let process = mozart_vcs::process::ProcessExecutor::new();
+ let git_util = mozart_vcs::util::git::GitUtil::new(
+ process,
+ vendor_dir.join(".cache").join("git"),
+ );
+ let downloader = mozart_vcs::downloader::git::GitDownloader::new(git_util);
+ use mozart_vcs::downloader::VcsDownloader;
+ downloader.download(url, reference, &target)?;
+ downloader.install(url, reference, &target)?;
+ }
+ "svn" => {
+ let process = mozart_vcs::process::ProcessExecutor::new();
+ let svn_util = mozart_vcs::util::svn::SvnUtil::new(process);
+ let downloader = mozart_vcs::downloader::svn::SvnDownloader::new(svn_util);
+ use mozart_vcs::downloader::VcsDownloader;
+ downloader.install(url, reference, &target)?;
+ }
+ "hg" => {
+ let process = mozart_vcs::process::ProcessExecutor::new();
+ let hg_util = mozart_vcs::util::hg::HgUtil::new(process);
+ let downloader = mozart_vcs::downloader::hg::HgDownloader::new(hg_util);
+ use mozart_vcs::downloader::VcsDownloader;
+ downloader.install(url, reference, &target)?;
+ }
+ _ => {
+ anyhow::bail!("Unsupported source type for VCS install: {}", source_type);
+ }
+ }
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use tempfile::tempdir;
+
+ fn make_executor() -> FilesystemExecutor {
+ FilesystemExecutor::new(Cache::new(std::env::temp_dir().join("__no_cache"), false))
+ }
+
+ #[test]
+ fn cleanup_after_uninstalls_removes_empty_namespace_dirs() {
+ let dir = tempdir().unwrap();
+ let vendor_dir = dir.path().join("vendor");
+ std::fs::create_dir_all(&vendor_dir).unwrap();
+
+ let empty_ns = vendor_dir.join("old-vendor");
+ std::fs::create_dir_all(&empty_ns).unwrap();
+
+ let nonempty_ns = vendor_dir.join("psr");
+ std::fs::create_dir_all(nonempty_ns.join("log")).unwrap();
+
+ std::fs::create_dir_all(vendor_dir.join("composer")).unwrap();
+
+ let mut exec = make_executor();
+ exec.cleanup_after_uninstalls(&ExecuteContext {
+ vendor_dir: vendor_dir.clone(),
+ no_progress: true,
+ prefer_source: false,
+ })
+ .unwrap();
+
+ assert!(!empty_ns.exists());
+ assert!(vendor_dir.join("psr").exists());
+ assert!(vendor_dir.join("composer").exists());
+ }
+
+ #[test]
+ fn cleanup_after_uninstalls_preserves_bin_dir() {
+ let dir = tempdir().unwrap();
+ let vendor_dir = dir.path().join("vendor");
+ std::fs::create_dir_all(&vendor_dir).unwrap();
+
+ let bin_dir = vendor_dir.join("bin");
+ std::fs::create_dir_all(&bin_dir).unwrap();
+
+ let mut exec = make_executor();
+ exec.cleanup_after_uninstalls(&ExecuteContext {
+ vendor_dir: vendor_dir.clone(),
+ no_progress: true,
+ prefer_source: false,
+ })
+ .unwrap();
+
+ assert!(bin_dir.exists());
+ }
+}
diff --git a/crates/mozart-registry/src/installer_executor/mod.rs b/crates/mozart-registry/src/installer_executor/mod.rs
new file mode 100644
index 0000000..fde4c49
--- /dev/null
+++ b/crates/mozart-registry/src/installer_executor/mod.rs
@@ -0,0 +1,84 @@
+//! Installation execution abstraction.
+//!
+//! Mirrors `Composer\Installer\InstallationManager`: the per-operation
+//! side-effect surface (download, extract, remove from vendor/) lives behind
+//! a trait so test code can substitute a recording-only implementation
+//! (Composer's `InstallationManagerMock`) without going anywhere near the
+//! filesystem or the network.
+//!
+//! The orchestration loop (computing operations from lock vs installed,
+//! emitting console messages, writing `installed.json`, generating the
+//! autoloader) stays in the caller. The executor is purely the verb —
+//! "install this package" / "uninstall this package" — so test traces match
+//! Composer's `(string) $operation` byte-for-byte without the executor
+//! having to also reproduce console formatting.
+
+use std::path::PathBuf;
+
+use crate::lockfile::LockedPackage;
+
+pub mod filesystem;
+
+pub use filesystem::FilesystemExecutor;
+
+/// One install or update operation handed to [`InstallerExecutor::install_package`].
+#[derive(Debug, Clone, Copy)]
+pub enum PackageOperation<'a> {
+ /// First-time install. The whole package directory is created from
+ /// `package.dist`/`package.source`.
+ Install { package: &'a LockedPackage },
+ /// Replace an existing install with a new version. `from_version` is the
+ /// pretty version that was installed before.
+ Update {
+ from_version: &'a str,
+ package: &'a LockedPackage,
+ },
+}
+
+impl<'a> PackageOperation<'a> {
+ pub fn package(&self) -> &'a LockedPackage {
+ match self {
+ PackageOperation::Install { package }
+ | PackageOperation::Update { package, .. } => package,
+ }
+ }
+}
+
+/// Per-call configuration shared across executor methods. Owned by the
+/// caller (typically `install_from_lock`) so the executor sees a consistent
+/// view across an entire install/update run.
+#[derive(Debug, Clone)]
+pub struct ExecuteContext {
+ pub vendor_dir: PathBuf,
+ /// Suppress download progress bars.
+ pub no_progress: bool,
+ /// Prefer cloning from VCS source over downloading dist archives.
+ pub prefer_source: bool,
+}
+
+/// Side-effect surface for install/update/uninstall operations.
+///
+/// Implementations are stateful — `&mut self` lets a recorder accumulate
+/// trace lines and lets the filesystem implementation hold long-lived
+/// handles (caches, progress bars). All methods return `anyhow::Result` so
+/// callers can short-circuit on the first failure, mirroring Composer's
+/// fail-fast `InstallationManager::execute`.
+#[async_trait::async_trait]
+pub trait InstallerExecutor: Send + Sync {
+ /// Perform side effects for one install or update operation.
+ async fn install_package(
+ &mut self,
+ op: PackageOperation<'_>,
+ ctx: &ExecuteContext,
+ ) -> anyhow::Result<()>;
+
+ /// Perform side effects for one uninstall.
+ fn uninstall_package(&mut self, name: &str, ctx: &ExecuteContext) -> anyhow::Result<()>;
+
+ /// Hook called once after every uninstall has run. Default no-op.
+ /// Composer cleans up empty namespace directories here; the recorder
+ /// has no work to do.
+ fn cleanup_after_uninstalls(&mut self, _ctx: &ExecuteContext) -> anyhow::Result<()> {
+ Ok(())
+ }
+}
diff --git a/crates/mozart-registry/src/lib.rs b/crates/mozart-registry/src/lib.rs
index a4afacd..e60d7b0 100644
--- a/crates/mozart-registry/src/lib.rs
+++ b/crates/mozart-registry/src/lib.rs
@@ -2,8 +2,10 @@ pub mod cache;
pub mod downloader;
pub mod inline_package;
pub mod installed;
+pub mod installer_executor;
pub mod lockfile;
pub mod packagist;
+pub mod repository;
pub mod resolver;
pub mod vcs_bridge;
pub mod version;
diff --git a/crates/mozart-registry/src/repository/inline_package_repo.rs b/crates/mozart-registry/src/repository/inline_package_repo.rs
new file mode 100644
index 0000000..1043559
--- /dev/null
+++ b/crates/mozart-registry/src/repository/inline_package_repo.rs
@@ -0,0 +1,63 @@
+//! [`Repository`] for inline `type: package` repositories.
+//!
+//! Wraps [`crate::inline_package::collect_inline_packages`]. The data is
+//! embedded in `composer.json` so there's no I/O — the repo just filters
+//! its in-memory list by queried name.
+//!
+//! Mirrors `Composer\Repository\PackageRepository` (which extends
+//! `ArrayRepository`). Only the package's own `name` is matched against
+//! queries — `replace`/`provide` targets are NOT advertised here, exactly
+//! like Composer's `ArrayRepository::loadPackages` checks `getName()` only.
+//! Replacement satisfaction happens later in the solver once the replacing
+//! package is loaded transitively.
+
+use super::{LoadResult, NamedPackagistVersion, PackageQuery, Repository};
+use crate::inline_package::{InlinePackage, collect_inline_packages};
+use mozart_core::package::RawRepository;
+
+pub struct InlinePackageRepository {
+ id: String,
+ packages: Vec<InlinePackage>,
+}
+
+impl InlinePackageRepository {
+ /// Build from the raw `repositories` array of a `composer.json`. Non-
+ /// `package` entries are ignored.
+ pub fn from_repositories(repositories: &[RawRepository]) -> Self {
+ Self {
+ id: "package".to_string(),
+ packages: collect_inline_packages(repositories),
+ }
+ }
+
+ pub fn package_count(&self) -> usize {
+ self.packages.len()
+ }
+}
+
+#[async_trait::async_trait]
+impl Repository for InlinePackageRepository {
+ fn id(&self) -> &str {
+ &self.id
+ }
+
+ async fn load_packages(&self, queries: &[PackageQuery<'_>]) -> anyhow::Result<LoadResult> {
+ let mut result = LoadResult::default();
+ for query in queries {
+ let mut found_any = false;
+ for ipkg in &self.packages {
+ if ipkg.name == query.name {
+ found_any = true;
+ result.packages.push(NamedPackagistVersion {
+ name: ipkg.name.clone(),
+ version: ipkg.version.clone(),
+ });
+ }
+ }
+ if found_any {
+ result.names_found.push(query.name.to_string());
+ }
+ }
+ Ok(result)
+ }
+}
diff --git a/crates/mozart-registry/src/repository/mod.rs b/crates/mozart-registry/src/repository/mod.rs
new file mode 100644
index 0000000..1ab8797
--- /dev/null
+++ b/crates/mozart-registry/src/repository/mod.rs
@@ -0,0 +1,127 @@
+//! Repository abstraction over package metadata sources.
+//!
+//! Mirrors Composer's `Composer\Repository\RepositoryInterface::loadPackages`
+//! and `Composer\Repository\RepositoryManager`. The resolver and lockfile
+//! generator query a [`RepositorySet`] instead of calling Packagist directly,
+//! so test code can substitute a set without `PackagistRepository` (mirroring
+//! Composer's `FactoryMock` injecting `repositories: ['packagist' => false]`).
+//!
+//! Concrete implementations live in sibling modules: [`packagist_repo`] for
+//! the live Packagist HTTP repo, [`inline_package_repo`] for `type: package`
+//! entries embedded in `composer.json`, and [`vcs_repo`] for VCS repositories.
+
+use crate::packagist::PackagistVersion;
+
+pub mod inline_package_repo;
+pub mod packagist_repo;
+pub mod vcs_repo;
+
+/// One name-keyed lookup against a repository.
+///
+/// Matches the `$packageNameMap` argument of Composer's `loadPackages`. The
+/// constraint is informational — repositories may use it to skip versions
+/// that obviously can't match (an optimization), but the resolver still
+/// re-checks every returned version when generating rules.
+#[derive(Debug, Clone)]
+pub struct PackageQuery<'a> {
+ pub name: &'a str,
+ /// Raw constraint string from `composer.json`, e.g. `"^1.2"`. `None`
+ /// when the caller wants every version (transitive exploration).
+ pub constraint: Option<&'a str>,
+}
+
+/// Result of a single [`Repository::load_packages`] call.
+///
+/// Mirrors Composer's `['packages' => ..., 'namesFound' => ...]` tuple.
+/// `names_found` lets [`RepositorySet`] short-circuit lower-priority repos
+/// once an upstream repo has authoritatively answered for a name (Composer's
+/// "first repo wins" semantics).
+#[derive(Debug, Default)]
+pub struct LoadResult {
+ pub packages: Vec<NamedPackagistVersion>,
+ pub names_found: Vec<String>,
+}
+
+/// A `PackagistVersion` paired with the canonical package name it answers
+/// for. Inline `type: package` repos can return packages whose own `name`
+/// field differs from the queried name when they declare `replace`/`provide`,
+/// so callers need both.
+#[derive(Debug, Clone)]
+pub struct NamedPackagistVersion {
+ pub name: String,
+ pub version: PackagistVersion,
+}
+
+/// A source of package metadata. Mirrors Composer's `RepositoryInterface`.
+///
+/// Implementations should return an empty [`LoadResult`] (not an error) when
+/// they simply don't know a queried name — [`RepositorySet`] uses that to
+/// fall through to the next repo. Reserve `Err` for genuine I/O failures
+/// the caller cannot route around.
+#[async_trait::async_trait]
+pub trait Repository: Send + Sync {
+ /// Identifier for diagnostics (`"packagist.org"`, `"package"`, `"vcs:<url>"`).
+ fn id(&self) -> &str;
+
+ /// Look up every version of every queried name this repo knows about.
+ async fn load_packages(&self, queries: &[PackageQuery<'_>]) -> anyhow::Result<LoadResult>;
+}
+
+/// Ordered list of repositories. Mirrors `Composer\Repository\RepositoryManager`.
+///
+/// `load_packages` queries each repo in order. Once a repo authoritatively
+/// answers for a name (i.e. lists it in `names_found`), later repos are not
+/// asked about that name — matching Composer's first-repo-wins priority.
+pub struct RepositorySet {
+ repos: Vec<Box<dyn Repository>>,
+}
+
+impl RepositorySet {
+ pub fn new(repos: Vec<Box<dyn Repository>>) -> Self {
+ Self { repos }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.repos.is_empty()
+ }
+
+ pub fn len(&self) -> usize {
+ self.repos.len()
+ }
+
+ /// Iterate over repositories in priority order.
+ pub fn repos(&self) -> impl Iterator<Item = &dyn Repository> {
+ self.repos.iter().map(|b| b.as_ref())
+ }
+
+ /// Query every repo, accumulating packages and tracking which names have
+ /// been authoritatively answered. Names already covered by an earlier
+ /// repo are dropped from the query passed to later repos.
+ pub async fn load_packages(
+ &self,
+ queries: &[PackageQuery<'_>],
+ ) -> anyhow::Result<Vec<NamedPackagistVersion>> {
+ use std::collections::HashSet;
+
+ let mut packages: Vec<NamedPackagistVersion> = Vec::new();
+ let mut answered: HashSet<String> = HashSet::new();
+
+ for repo in &self.repos {
+ let pending: Vec<PackageQuery<'_>> = queries
+ .iter()
+ .filter(|q| !answered.contains(q.name))
+ .cloned()
+ .collect();
+ if pending.is_empty() {
+ break;
+ }
+ let result = repo.load_packages(&pending).await?;
+ for name in result.names_found {
+ answered.insert(name);
+ }
+ packages.extend(result.packages);
+ }
+
+ Ok(packages)
+ }
+}
diff --git a/crates/mozart-registry/src/repository/packagist_repo.rs b/crates/mozart-registry/src/repository/packagist_repo.rs
new file mode 100644
index 0000000..17208c1
--- /dev/null
+++ b/crates/mozart-registry/src/repository/packagist_repo.rs
@@ -0,0 +1,58 @@
+//! [`Repository`] backed by the live Packagist HTTP API.
+//!
+//! Wraps the existing [`crate::packagist::fetch_package_versions`] so the
+//! resolver sees the same data either through this trait or via the legacy
+//! direct call. Construction takes ownership of the [`Cache`] handle so
+//! callers no longer thread it through `ResolveRequest` / `LockFileGenerationRequest`.
+
+use super::{LoadResult, NamedPackagistVersion, PackageQuery, Repository};
+use crate::cache::Cache;
+use crate::packagist;
+
+pub struct PackagistRepository {
+ id: String,
+ cache: Cache,
+}
+
+impl PackagistRepository {
+ pub fn new(cache: Cache) -> Self {
+ Self {
+ id: "packagist.org".to_string(),
+ cache,
+ }
+ }
+}
+
+#[async_trait::async_trait]
+impl Repository for PackagistRepository {
+ fn id(&self) -> &str {
+ &self.id
+ }
+
+ async fn load_packages(&self, queries: &[PackageQuery<'_>]) -> anyhow::Result<LoadResult> {
+ let mut result = LoadResult::default();
+ for query in queries {
+ // Mirror the existing transitive-loop tolerance: a 404 / network
+ // failure for one name is not fatal — it just means this repo
+ // contributes nothing for that name. `RepositorySet` falls
+ // through, and the solver fails later if no repo knows it.
+ let versions =
+ match packagist::fetch_package_versions(query.name, &self.cache).await {
+ Ok(v) => v,
+ Err(_) => continue,
+ };
+ // `fetch_package_versions` returning Ok counts as "this repo
+ // authoritatively knows the name", even if the version list is
+ // empty (matches Composer `ArrayRepository::loadPackages` which
+ // adds the name to `namesFound` regardless of constraint match).
+ result.names_found.push(query.name.to_string());
+ for version in versions {
+ result.packages.push(NamedPackagistVersion {
+ name: query.name.to_string(),
+ version,
+ });
+ }
+ }
+ Ok(result)
+ }
+}
diff --git a/crates/mozart-registry/src/repository/vcs_repo.rs b/crates/mozart-registry/src/repository/vcs_repo.rs
new file mode 100644
index 0000000..fff5f6f
--- /dev/null
+++ b/crates/mozart-registry/src/repository/vcs_repo.rs
@@ -0,0 +1,63 @@
+//! [`Repository`] for VCS-type repositories.
+//!
+//! Wraps [`crate::vcs_bridge::scan_vcs_repositories`] + [`crate::vcs_bridge::vcs_to_packagist_version`].
+//! Scanning is expensive (clones / fetches), so we do it once at construction
+//! and serve subsequent queries from the in-memory cache. Mirrors
+//! `Composer\Repository\Vcs\VcsRepository`'s lazy-then-memoized behavior.
+
+use super::{LoadResult, NamedPackagistVersion, PackageQuery, Repository};
+use crate::packagist::PackagistVersion;
+use crate::vcs_bridge::{scan_vcs_repositories, vcs_to_packagist_version};
+use mozart_core::package::RawRepository;
+
+pub struct VcsRepository {
+ id: String,
+ versions: Vec<(String, PackagistVersion)>,
+}
+
+impl VcsRepository {
+ /// Scan every VCS-type entry in `repositories` and cache the resulting
+ /// versions. Non-VCS entries are ignored. This performs network I/O.
+ pub async fn from_repositories(repositories: &[RawRepository]) -> Self {
+ let scanned = scan_vcs_repositories(repositories).await;
+ let versions = scanned
+ .iter()
+ .map(|v| (v.name.clone(), vcs_to_packagist_version(v)))
+ .collect();
+ Self {
+ id: "vcs".to_string(),
+ versions,
+ }
+ }
+
+ pub fn version_count(&self) -> usize {
+ self.versions.len()
+ }
+}
+
+#[async_trait::async_trait]
+impl Repository for VcsRepository {
+ fn id(&self) -> &str {
+ &self.id
+ }
+
+ async fn load_packages(&self, queries: &[PackageQuery<'_>]) -> anyhow::Result<LoadResult> {
+ let mut result = LoadResult::default();
+ for query in queries {
+ let mut found_any = false;
+ for (name, version) in &self.versions {
+ if name == query.name {
+ found_any = true;
+ result.packages.push(NamedPackagistVersion {
+ name: name.clone(),
+ version: version.clone(),
+ });
+ }
+ }
+ if found_any {
+ result.names_found.push(query.name.to_string());
+ }
+ }
+ Ok(result)
+ }
+}
diff --git a/crates/mozart/src/commands/install.rs b/crates/mozart/src/commands/install.rs
index 1cc4e6f..b303ade 100644
--- a/crates/mozart/src/commands/install.rs
+++ b/crates/mozart/src/commands/install.rs
@@ -1,8 +1,10 @@
use clap::Args;
use mozart_core::console;
use mozart_core::console_format;
-use mozart_registry::downloader;
use mozart_registry::installed;
+use mozart_registry::installer_executor::{
+ ExecuteContext, FilesystemExecutor, InstallerExecutor, PackageOperation,
+};
use mozart_registry::lockfile;
use std::collections::{BTreeMap, HashSet};
use std::path::{Path, PathBuf};
@@ -236,27 +238,6 @@ pub fn locked_to_installed_entry(
}
}
-/// Clean up empty vendor namespace directories after removals.
-pub fn cleanup_empty_vendor_dirs(vendor_dir: &Path) -> anyhow::Result<()> {
- if let Ok(entries) = std::fs::read_dir(vendor_dir) {
- for entry in entries.flatten() {
- let path = entry.path();
- if path.is_dir() {
- let name = entry.file_name().to_string_lossy().to_string();
- // Skip "composer" dir and "bin" dir
- if name == "composer" || name == "bin" {
- continue;
- }
- // If the namespace dir is empty, remove it
- if std::fs::read_dir(&path)?.next().is_none() {
- std::fs::remove_dir(&path)?;
- }
- }
- }
- }
- Ok(())
-}
-
/// Check whether a package name refers to a platform package.
///
/// Platform packages are: names starting with "php", "ext-", or "lib-".
@@ -435,56 +416,6 @@ fn warn_platform_requirements(
}
}
-/// Create a download progress tracker for a package.
-fn make_progress(show: bool, pkg_name: &str, version: &str) -> downloader::DownloadProgress {
- downloader::DownloadProgress::new(show, format!("{pkg_name} ({version})"))
-}
-
-/// Install a package from VCS source (git/svn/hg).
-fn install_from_source(
- source_type: &str,
- url: &str,
- reference: &str,
- vendor_dir: &Path,
- package_name: &str,
-) -> anyhow::Result<()> {
- let target = vendor_dir.join(package_name);
- if target.exists() {
- std::fs::remove_dir_all(&target)?;
- }
-
- match source_type {
- "git" => {
- let process = mozart_vcs::process::ProcessExecutor::new();
- let git_util =
- mozart_vcs::util::git::GitUtil::new(process, vendor_dir.join(".cache").join("git"));
- let downloader = mozart_vcs::downloader::git::GitDownloader::new(git_util);
- use mozart_vcs::downloader::VcsDownloader;
- downloader.download(url, reference, &target)?;
- downloader.install(url, reference, &target)?;
- }
- "svn" => {
- let process = mozart_vcs::process::ProcessExecutor::new();
- let svn_util = mozart_vcs::util::svn::SvnUtil::new(process);
- let downloader = mozart_vcs::downloader::svn::SvnDownloader::new(svn_util);
- use mozart_vcs::downloader::VcsDownloader;
- downloader.install(url, reference, &target)?;
- }
- "hg" => {
- let process = mozart_vcs::process::ProcessExecutor::new();
- let hg_util = mozart_vcs::util::hg::HgUtil::new(process);
- let downloader = mozart_vcs::downloader::hg::HgDownloader::new(hg_util);
- use mozart_vcs::downloader::VcsDownloader;
- downloader.install(url, reference, &target)?;
- }
- _ => {
- anyhow::bail!("Unsupported source type for VCS install: {}", source_type);
- }
- }
-
- Ok(())
-}
-
pub async fn install_from_lock(
lock: &lockfile::LockFile,
working_dir: &Path,
@@ -575,8 +506,15 @@ pub async fn install_from_lock(
console.info(&console_format!(" - Would remove <info>{}</info>", name));
}
} else {
+ let mut executor = FilesystemExecutor::new(files_cache);
+ let exec_ctx = ExecuteContext {
+ vendor_dir: vendor_dir.to_path_buf(),
+ no_progress: config.no_progress,
+ prefer_source: config.prefer_source,
+ };
+
for (pkg, action) in &ops {
- match action {
+ let op = match action {
Action::Skip => continue,
Action::Install => {
console.info(&console_format!(
@@ -584,6 +522,7 @@ pub async fn install_from_lock(
pkg.name,
pkg.version
));
+ PackageOperation::Install { package: pkg }
}
Action::Update => {
console.info(&console_format!(
@@ -591,69 +530,29 @@ pub async fn install_from_lock(
pkg.name,
pkg.version
));
+ // The previous-version string is unknown to install_from_lock
+ // (it only sees the post-update lock). Pass the new version
+ // as a placeholder; this path is unused by the recorder, and
+ // Composer's `Upgrading` trace string is generated upstream
+ // by the resolver, not by InstallationManager itself.
+ PackageOperation::Update {
+ from_version: &pkg.version,
+ package: pkg,
+ }
}
- }
-
- // Try source install if --prefer-source and source info is available
- if config.prefer_source
- && let Some(source) = &pkg.source
- {
- install_from_source(
- &source.source_type,
- &source.url,
- source.reference.as_deref().unwrap_or("HEAD"),
- vendor_dir,
- &pkg.name,
- )?;
- 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(|| {
- 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);
-
- downloader::install_package(
- &dist.url,
- &dist.dist_type,
- dist.shasum.as_deref(),
- vendor_dir,
- &pkg.name,
- Some(&mut progress),
- &files_cache,
- )
- .await?;
-
- progress.finish();
+ };
+ executor.install_package(op, &exec_ctx).await?;
}
// Handle removals
for name in &removals {
console.info(&console_format!(" - Removing <info>{}</info>", name));
- let pkg_dir = vendor_dir.join(name);
- if pkg_dir.exists() {
- std::fs::remove_dir_all(&pkg_dir)?;
- }
+ executor.uninstall_package(name, &exec_ctx)?;
}
// Step 7: Clean up empty vendor namespace directories
if !removals.is_empty() {
- cleanup_empty_vendor_dirs(vendor_dir)?;
+ executor.cleanup_after_uninstalls(&exec_ctx)?;
}
// Step 8: Write updated vendor/composer/installed.json (unless download_only)
@@ -1233,57 +1132,6 @@ mod tests {
}
// -----------------------------------------------------------------------
- // cleanup_empty_vendor_dirs tests
- // -----------------------------------------------------------------------
-
- #[test]
- fn test_cleanup_empty_vendor_dirs_removes_empty() {
- let dir = tempdir().unwrap();
- let vendor_dir = dir.path().join("vendor");
- std::fs::create_dir_all(&vendor_dir).unwrap();
-
- // Create an empty namespace dir
- let empty_ns = vendor_dir.join("old-vendor");
- std::fs::create_dir_all(&empty_ns).unwrap();
-
- // Create a non-empty namespace dir
- let nonempty_ns = vendor_dir.join("psr");
- std::fs::create_dir_all(nonempty_ns.join("log")).unwrap();
-
- // Create the composer dir (should be skipped)
- std::fs::create_dir_all(vendor_dir.join("composer")).unwrap();
-
- cleanup_empty_vendor_dirs(&vendor_dir).unwrap();
-
- assert!(!empty_ns.exists(), "empty namespace dir should be removed");
- assert!(
- vendor_dir.join("psr").exists(),
- "non-empty namespace dir should remain"
- );
- assert!(
- vendor_dir.join("composer").exists(),
- "composer dir should be preserved"
- );
- }
-
- #[test]
- fn test_cleanup_empty_vendor_dirs_skips_bin() {
- let dir = tempdir().unwrap();
- let vendor_dir = dir.path().join("vendor");
- std::fs::create_dir_all(&vendor_dir).unwrap();
-
- let bin_dir = vendor_dir.join("bin");
- std::fs::create_dir_all(&bin_dir).unwrap();
-
- cleanup_empty_vendor_dirs(&vendor_dir).unwrap();
-
- assert!(
- bin_dir.exists(),
- "bin dir should be preserved even if empty"
- );
- }
-
- // -----------------------------------------------------------------------
// Platform requirement check tests
// -----------------------------------------------------------------------
diff --git a/crates/mozart/tests/installer.rs b/crates/mozart/tests/installer.rs
index e1bc7a5..719a721 100644
--- a/crates/mozart/tests/installer.rs
+++ b/crates/mozart/tests/installer.rs
@@ -57,9 +57,9 @@ macro_rules! installer_fixture {
run_installer_fixture(stringify!($name));
}
};
- ($name:ident, ignore = $reason:literal) => {
+ ($name:ident, ignore) => {
#[test]
- #[ignore = $reason]
+ #[ignore = "not implemented yet"]
fn $name() {
run_installer_fixture(stringify!($name));
}
@@ -67,77 +67,38 @@ macro_rules! installer_fixture {
}
installer_fixture!(abandoned_listed);
-installer_fixture!(
- alias_in_complex_constraints,
- ignore = "mozart binary cannot yet run this fixture"
-);
-installer_fixture!(
- alias_in_lock,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(alias_in_complex_constraints, ignore);
+installer_fixture!(alias_in_lock, ignore);
installer_fixture!(alias_in_lock2);
-installer_fixture!(
- alias_on_unloadable_package,
- ignore = "mozart binary cannot yet run this fixture"
-);
-installer_fixture!(
- alias_solver_problems,
- ignore = "mozart binary cannot yet run this fixture"
-);
-installer_fixture!(
- alias_solver_problems2,
- ignore = "mozart binary cannot yet run this fixture"
-);
-installer_fixture!(
- alias_with_reference,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(alias_on_unloadable_package, ignore);
+installer_fixture!(alias_solver_problems, ignore);
+installer_fixture!(alias_solver_problems2, ignore);
+installer_fixture!(alias_with_reference, ignore);
installer_fixture!(aliased_priority);
installer_fixture!(aliased_priority_conflicting);
-installer_fixture!(
- aliases_with_require_dev,
- ignore = "mozart binary cannot yet run this fixture"
-);
-installer_fixture!(
- broken_deps_do_not_replace,
- ignore = "mozart binary cannot yet run this fixture"
-);
-installer_fixture!(
- circular_dependency,
- ignore = "mozart binary cannot yet run this fixture"
-);
-installer_fixture!(
- circular_dependency2,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(aliases_with_require_dev, ignore);
+installer_fixture!(broken_deps_do_not_replace, ignore);
+installer_fixture!(circular_dependency, ignore);
+installer_fixture!(circular_dependency2, ignore);
installer_fixture!(circular_dependency_errors);
installer_fixture!(conflict_against_provided_by_dep_package_works);
installer_fixture!(conflict_against_provided_package_works);
installer_fixture!(conflict_against_replaced_by_dep_package_problem);
-installer_fixture!(
- conflict_against_replaced_package_problem,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(conflict_against_replaced_package_problem, ignore);
installer_fixture!(conflict_between_dependents);
installer_fixture!(conflict_between_root_and_dependent);
installer_fixture!(conflict_downgrade);
installer_fixture!(conflict_downgrade_nested);
installer_fixture!(
conflict_on_root_with_alias_prevents_update_if_not_required,
- ignore = "mozart binary cannot yet run this fixture"
-);
-installer_fixture!(
- conflict_with_alias_in_lock_does_prevents_install,
- ignore = "mozart binary cannot yet run this fixture"
+ ignore
);
+installer_fixture!(conflict_with_alias_in_lock_does_prevents_install, ignore);
installer_fixture!(conflict_with_alias_prevents_update);
-installer_fixture!(
- conflict_with_alias_prevents_update_if_not_required,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(conflict_with_alias_prevents_update_if_not_required, ignore);
installer_fixture!(
conflict_with_all_dependencies_option_dont_recommend_to_use_it,
- ignore = "mozart binary cannot yet run this fixture"
+ ignore
);
installer_fixture!(deduplicate_solver_problems);
installer_fixture!(disjunctive_multi_constraints);
@@ -145,40 +106,19 @@ installer_fixture!(full_update_minimal_changes);
installer_fixture!(github_issues_4319);
installer_fixture!(github_issues_4795);
installer_fixture!(github_issues_4795_2);
-installer_fixture!(
- github_issues_7051,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(github_issues_7051, ignore);
installer_fixture!(github_issues_8902);
-installer_fixture!(
- github_issues_8903,
- ignore = "mozart binary cannot yet run this fixture"
-);
-installer_fixture!(
- github_issues_9012,
- ignore = "mozart binary cannot yet run this fixture"
-);
-installer_fixture!(
- github_issues_9290,
- ignore = "mozart binary cannot yet run this fixture"
-);
-installer_fixture!(
- hint_main_rename,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(github_issues_8903, ignore);
+installer_fixture!(github_issues_9012, ignore);
+installer_fixture!(github_issues_9290, ignore);
+installer_fixture!(hint_main_rename, ignore);
installer_fixture!(install_aliased_alias);
-installer_fixture!(
- install_branch_alias_composer_repo,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(install_branch_alias_composer_repo, ignore);
installer_fixture!(install_dev);
installer_fixture!(install_dev_using_dist);
installer_fixture!(install_forces_reinstall_if_abandon_changes);
installer_fixture!(install_from_incomplete_lock);
-installer_fixture!(
- install_from_incomplete_lock_with_ignore,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(install_from_incomplete_lock_with_ignore, ignore);
installer_fixture!(install_from_lock_removes_package);
installer_fixture!(install_funding_notice);
installer_fixture!(install_funding_notice_env);
@@ -186,59 +126,35 @@ installer_fixture!(install_funding_notice_not_displayed_env);
installer_fixture!(install_ignore_platform_package_requirement_list);
installer_fixture!(install_ignore_platform_package_requirement_wildcard);
installer_fixture!(install_ignore_platform_package_requirements);
-installer_fixture!(
- install_missing_alias_from_lock,
- ignore = "mozart binary cannot yet run this fixture"
-);
-installer_fixture!(
- install_overridden_platform_packages,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(install_missing_alias_from_lock, ignore);
+installer_fixture!(install_overridden_platform_packages, ignore);
installer_fixture!(install_package_and_its_provider_skips_original);
installer_fixture!(install_prefers_repos_over_package_versions);
installer_fixture!(install_reference);
-installer_fixture!(
- install_security_advisory_matching_dependency,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(install_security_advisory_matching_dependency, ignore);
installer_fixture!(install_self_from_root);
installer_fixture!(install_simple);
installer_fixture!(install_without_lock);
installer_fixture!(load_replaced_package_if_replacer_dropped);
installer_fixture!(outdated_lock_file_fails_install);
installer_fixture!(outdated_lock_file_with_new_platform_reqs_fails);
-installer_fixture!(
- partial_update_always_updates_symlinked_path_repos,
- ignore = "mozart binary cannot yet run this fixture"
-);
-installer_fixture!(
- partial_update_downgrades_non_allow_listed_unstable,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(partial_update_always_updates_symlinked_path_repos, ignore);
+installer_fixture!(partial_update_downgrades_non_allow_listed_unstable, ignore);
installer_fixture!(partial_update_forces_dev_reference_from_lock_for_non_updated_packages);
installer_fixture!(partial_update_from_lock);
installer_fixture!(partial_update_from_lock_with_root_alias);
installer_fixture!(partial_update_installs_from_lock_even_missing);
installer_fixture!(partial_update_keeps_older_dep_if_still_required);
installer_fixture!(partial_update_keeps_older_dep_if_still_required_with_provide);
-installer_fixture!(
- partial_update_loads_root_aliases_for_path_repos,
- ignore = "mozart binary cannot yet run this fixture"
-);
-installer_fixture!(
- partial_update_security_advisory_matching_locked_dep,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(partial_update_loads_root_aliases_for_path_repos, ignore);
+installer_fixture!(partial_update_security_advisory_matching_locked_dep, ignore);
installer_fixture!(
partial_update_security_advisory_matching_locked_dep_with_dependencies,
- ignore = "mozart binary cannot yet run this fixture"
+ ignore
);
installer_fixture!(partial_update_with_dependencies_provide);
installer_fixture!(partial_update_with_dependencies_replace);
-installer_fixture!(
- partial_update_with_deps_warns_root,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(partial_update_with_deps_warns_root, ignore);
installer_fixture!(partial_update_with_symlinked_path_repos);
installer_fixture!(partial_update_without_lock);
installer_fixture!(platform_ext_solver_problems);
@@ -246,71 +162,47 @@ installer_fixture!(plugins_are_installed_first);
installer_fixture!(prefer_lowest_branches);
installer_fixture!(problems_reduce_versions);
installer_fixture!(provider_can_coexist_with_other_version_of_provided);
-installer_fixture!(
- provider_conflicts,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(provider_conflicts, ignore);
installer_fixture!(provider_conflicts2);
installer_fixture!(provider_conflicts3);
-installer_fixture!(
- provider_dev_require_can_satisfy_require,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(provider_dev_require_can_satisfy_require, ignore);
installer_fixture!(provider_gets_picked_together_with_other_version_of_provided);
installer_fixture!(
provider_gets_picked_together_with_other_version_of_provided_conflict,
- ignore = "mozart binary cannot yet run this fixture"
+ ignore
);
installer_fixture!(provider_gets_picked_together_with_other_version_of_provided_indirect);
installer_fixture!(provider_packages_can_be_installed_if_selected);
installer_fixture!(
provider_packages_can_be_installed_together_with_provided_if_both_installable,
- ignore = "mozart binary cannot yet run this fixture"
+ ignore
);
installer_fixture!(
provider_packages_can_not_be_installed_unless_selected,
- ignore = "mozart binary cannot yet run this fixture"
+ ignore
);
installer_fixture!(provider_satisfies_its_own_requirement);
installer_fixture!(remove_deletes_unused_deps);
installer_fixture!(remove_does_nothing_if_removal_requires_update_of_dep);
-installer_fixture!(
- replace_alias,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(replace_alias, ignore);
installer_fixture!(replace_priorities);
installer_fixture!(replace_range_require_single_version);
installer_fixture!(replace_root_require);
installer_fixture!(replaced_packages_should_not_be_installed);
installer_fixture!(
replaced_packages_should_not_be_installed_when_installing_from_lock,
- ignore = "mozart binary cannot yet run this fixture"
+ ignore
);
installer_fixture!(replacer_satisfies_its_own_requirement);
-installer_fixture!(
- repositories_priorities,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(repositories_priorities, ignore);
installer_fixture!(repositories_priorities2);
installer_fixture!(repositories_priorities3);
-installer_fixture!(
- repositories_priorities4,
- ignore = "mozart binary cannot yet run this fixture"
-);
-installer_fixture!(
- repositories_priorities5,
- ignore = "mozart binary cannot yet run this fixture"
-);
-installer_fixture!(
- root_alias_change_with_circular_dep,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(repositories_priorities4, ignore);
+installer_fixture!(repositories_priorities5, ignore);
+installer_fixture!(root_alias_change_with_circular_dep, ignore);
installer_fixture!(root_alias_gets_loaded_for_locked_pkgs);
installer_fixture!(root_requirements_do_not_affect_locked_versions);
-installer_fixture!(
- solver_problem_with_hash_in_branch,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(solver_problem_with_hash_in_branch, ignore);
installer_fixture!(solver_problems);
installer_fixture!(solver_problems_with_disabled_platform);
installer_fixture!(suggest_installed);
@@ -320,16 +212,13 @@ installer_fixture!(suggest_replaced);
installer_fixture!(suggest_uninstalled);
installer_fixture!(
unbounded_conflict_does_not_match_default_branch_with_branch_alias,
- ignore = "mozart binary cannot yet run this fixture"
+ ignore
);
installer_fixture!(unbounded_conflict_does_not_match_default_branch_with_numeric_branch);
-installer_fixture!(
- unbounded_conflict_matches_default_branch,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(unbounded_conflict_matches_default_branch, ignore);
installer_fixture!(
update_abandoned_package_required_but_blocked_via_audit_config,
- ignore = "mozart binary cannot yet run this fixture"
+ ignore
);
installer_fixture!(update_alias);
installer_fixture!(update_alias_lock);
@@ -349,10 +238,7 @@ installer_fixture!(update_allow_list_removes_unused);
installer_fixture!(update_allow_list_require_new_replace);
installer_fixture!(update_allow_list_warns_non_existing_patterns);
installer_fixture!(update_allow_list_with_dependencies);
-installer_fixture!(
- update_allow_list_with_dependencies_alias,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(update_allow_list_with_dependencies_alias, ignore);
installer_fixture!(update_allow_list_with_dependencies_new_requirement);
installer_fixture!(update_allow_list_with_dependencies_require_new);
installer_fixture!(update_allow_list_with_dependencies_require_new_replace);
@@ -362,10 +248,7 @@ installer_fixture!(update_changes_url);
installer_fixture!(update_dev_ignores_providers);
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 = "mozart binary cannot yet run this fixture"
-);
+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);
installer_fixture!(update_ignore_platform_package_requirement_wildcard);
@@ -383,7 +266,7 @@ 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,
- ignore = "mozart binary cannot yet run this fixture"
+ ignore
);
installer_fixture!(update_picks_up_change_of_vcs_type);
installer_fixture!(update_prefer_lowest_stable);
@@ -391,13 +274,10 @@ installer_fixture!(update_reference);
installer_fixture!(update_reference_picks_latest);
installer_fixture!(update_removes_unused_locked_dep);
installer_fixture!(update_requiring_decision_reverts_and_learning_positive_literals);
-installer_fixture!(
- update_security_advisory_matching_direct_dependency,
- ignore = "mozart binary cannot yet run this fixture"
-);
+installer_fixture!(update_security_advisory_matching_direct_dependency, ignore);
installer_fixture!(
update_security_advisory_matching_indirect_dependency,
- ignore = "mozart binary cannot yet run this fixture"
+ ignore
);
installer_fixture!(update_syncs_outdated);
installer_fixture!(update_to_empty_from_blank);