aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/repository/composer_repository.rs
AgeCommit message (Collapse)Author
2026-08-06refactor: replace literal-list in_array_strict with matches!nsfisis
Call sites whose haystack was an inline array of literals (or a local built solely to feed one) had to wrap both sides in PhpMixed just to compare, allocating a String per element on every call. matches! does the same test against the underlying &str/i64/Option directly, so the PhpMixed round trip and its .to_string()/.clone()/.iter().map() conversions are gone. Sites whose haystack is a runtime value or a named constant array are left on in_array_strict: inlining a named constant would duplicate its contents at the call site. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06refactor(php-shim): split in_array into strict and loose variantsnsfisis
2026-08-02fix(repository): resolve remaining late-binding hazards from the auditnsfisis
Three fixes for the ComposerRepository/FilesystemRepository/PlatformRepository hazards where inner-composition delegation skipped PHP's late-bound virtual dispatch: - ComposerRepository::has_package now builds its packageMap through the late-bound getPackages() equivalent, so lazy-providers repos surface the LogicException and available-packages repos load their package list, as in PHP, instead of silently answering false from the raw array. - RepositoryInterface::get_repo_name returns anyhow::Result<String>: PHP's getRepoName() counts through the late-bound initialize(), which is fallible in file-reading subclasses. FilesystemRepository and PackageRepository now run that initialization instead of freezing the inner array repository to an empty state (which also made a later write() truncate installed.json). Supporting changes keep the initialization chain callable from &self: JsonFile::read takes &self (indent moved into a RefCell), FilesystemRepository dev_mode became a Cell, and WritableArrayRepository dev_package_names a RefCell. - PlatformRepository::new routes constructor packages through its own add_package so the override handling and full platform initialization run as they do via PHP's parent constructor; the inner find_package/add_package delegations inside add_package (and ComposerRepository::add_package) gained the same is_initialized guard, since the constructor path would otherwise freeze the repository. Same defect class as 7db937af, 97b5211a and 3e367f78. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02fix(repository): restore late-bound initialize in ComposerRepository ↵nsfisis
fallthroughs findPackage()/findPackages()/search() delegate their non-lazy, non-provider fallthrough to the inner ArrayRepository, whose self-initialization froze the packages array before ComposerRepository::initialize could read the root file, so a plain v1-style repo (inline "packages" in packages.json) always answered empty. Guard the delegations with the same is_initialized() check used by count()/hasPackage(). getProviders() had the inverse defect: PHP reads the raw $this->packages property, but the port went through count(), whose initialize poisoned the initialization flag so the root file would never load afterwards. Check the raw field for non-empty instead, matching PHP's truthiness test. Same defect class as 97b5211a and the 3e367f78 downloader fix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02fix(json): propagate JsonFile::encode errors instead of unwrappingnsfisis
PHP's JsonFile::encode throws a RuntimeException when json_encode fails; the port swallowed that into an .unwrap() marked TODO(phase-c). Return anyhow::Result from encode/encode_with_options and propagate at every call site (print_table and list_repositories become Result-returning to carry it). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02chore(todo): consolidate TODO comments into the five fixed marker tagsnsfisis
Retag every Shirabe-authored TODO comment to one of the fixed tags: phase-c, phase-d, plugin, php-runtime, phase-e. Upstream-authored TODO comments from Composer/Symfony are left untouched to preserve the ported code shape. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02fix(repository): run lazy initialization in count/has_packagensfisis
PHP's ArrayRepository::count()/hasPackage() call $this->initialize(), which late-binds to the concrete repository class and lazily loads its packages. The Rust pass-throughs skipped that: they ran ArrayRepository's stub initialize instead, returning 0/false and marking the repository initialized with an empty package list, which made ensure_initialized() skip the real initialization forever after. Take &mut self in RepositoryInterface::count/has_package so the lazy repositories (Filesystem, Platform, Composer) can guard with their real initialize, and return Result from has_package since that initialization can fail (PHP propagates the exception). InstallerInterface::is_installed and InstallationManager::is_package_installed/mark_alias_installed propagate the same way, which also resolves the TODO(phase-d) markers on Package/Path/Artifact/Vcs repositories about initialization errors being swallowed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25perf(advisory): share AnySecurityAdvisory via Rcnsfisis
PHP's SecurityAdvisoryPoolFilter stores advisory *object references* in $securityRemovedVersions, and PoolOptimizer::applyRemovalsToPool hands that array to the new Pool by copy-on-write. Porting AnySecurityAdvisory as a value type turned both of those into deep copies. Measured on `require laravel/framework` (offline, warm cache): 113 distinct advisories were duplicated into 314,309 copies of ~1.08 KiB, retaining 331.9 MiB in the filter loop and another 331.9 MiB when apply_removals_to_pool cloned the whole map. 3.54s -> 2.62s (-26%), peak RSS 975 MB -> 343 MB, which matches the upper bound measured by ablation. Composer runs the same workload in 1.49s. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25refactor: replace redundant clones with movesnsfisis
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25feat(tracing): init tracing subscriber from $SHIRABE_TRACINGnsfisis
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-24fix(composer-repository): initialize wrapper before delegating loadPackagesnsfisis
For a simple, non-lazy composer repository (no providers-url/ metadata-url/providers-lazy-url), load_packages() delegates to self.inner.load_packages() (PHP: parent::loadPackages()). PHP's ArrayRepository::loadPackages() calls $this->getPackages(), which virtual-dispatches through ComposerRepository::getPackages() down to ComposerRepository::initialize() (the real HTTP/file fetch). Composition doesn't get that dispatch for free: self.inner is a bare ArrayRepository, so self.inner.load_packages() ends up calling ArrayRepository::initialize() — a no-op stub that just sets an empty package list — instead of ComposerRepository::initialize(). The repository's packages.json fetched and parsed successfully, but the result was silently discarded, so the repo always looked empty. get_packages() already carries the identical guard with the same diagnosis in its own comment; load_packages() was just missing it. Documented the same latent gap in find_package/find_packages/count/ has_package, which call into ArrayRepository the same unguarded way but aren't known to be exercised by any test yet.
2026-07-20perf(composer-repository): avoid redundant clones when checking security ↵nsfisis
advisories get_security_advisories cloned each package's full metadata blob (hundreds of versions for popular packages) just to peek at its "security-advisories" field. Same pattern already fixed in load_async_packages by 2862d2da; move ownership through pattern matches and IndexMap::shift_remove instead. Measured with laravel/laravel create-project: response processing in the security-advisories metadata branch drops ~80% (236ms -> 48ms), and the whole run_security_advisory_filter phase drops from ~420-500ms to ~250-270ms. End-to-end, shirabe now matches or edges out upstream Composer (6.57s vs 6.85s in this run) instead of trailing by ~1.2s.
2026-07-20perf(composer-repository): avoid redundant clones when loading async package ↵nsfisis
metadata load_async_packages cloned each response's version metadata up to three times (spec_list/response, response_arr, versions_mixed, then every per-version field) before building the versions Vec. Move ownership through pattern matches and IndexMap::shift_remove instead. Measured with laravel/laravel create-project: per-batch response processing time in load_async_packages drops ~39% (1.30s -> 0.80s cumulative), narrowing the E2E gap vs upstream Composer from 1.37s to 1.14s on average.
2026-07-19fix(search-command): un-ignore test_search by fixing JSON output and ↵nsfisis
abandoned propagation The json format branch ignored search results entirely and always wrote null. Encode results into the same name/description/abandoned/url shape Composer's array-backed repositories produce. Also fix ComposerRepository's RepositoryInterface::search adapter, which dropped the abandoned field from raw API results even when present.
2026-07-18perf(regex): eliminate per-call clone overhead in preg_* dispatchnsfisis
regex::Regex::clone() does not share the underlying meta engine's search-cache pool, so every fresh clone pays a ~10us warmup cost on its first use. Two changes together eliminate this across nearly all preg_* call sites: - A php_regex! macro resolves PHP-style patterns to a per-call-site &'static regex::Regex (via regex-macro's LazyLock), applied at the majority of call sites throughout the codebase. - Call sites still passing dynamic pattern strings go through PATTERN_CACHE, which now stores Arc<(Regex, bool)> and hands out Arc::clone()s instead of cloning the Regex itself. PregPattern::resolve() returns a ResolvedPattern enum (Arc or 'static reference) rather than an owned Regex, so neither path ever clones the Regex proper. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18fix(composer-repository): match List for security-advisories fieldnsfisis
security-advisories in a p2 provider response is a JSON array, which decodes to PhpMixed::List rather than PhpMixed::Array. The check only matched Array, so it always missed and silently fell through to the api-url fallback instead of using the already-cached provider data. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17perf(sync-executor): drive HTTP fetches through one real top-level runtimensfisis
Replace sync_executor::block_on's reactor-less busy-spin poller with tokio::task::block_in_place + Handle::current().block_on(), riding a single tokio Runtime entered once in main.rs (falling back to a disposable one when no ambient runtime exists, e.g. in tests). This lets HttpDownloader::dispatch await CurlDownloader::download directly instead of bouncing through the separate curl_runtime() bridge, which is now deleted. Manual create-project verification against the real network caught a concurrency bug this exposed: async_fetch_file held http_downloader's RefMut across the await on add(), which only panics once downloads genuinely overlap. add() only needs &self, so borrow() fixes it. With everything now sharing one real reactor, the FuturesOrdered fan-out added for ComposerRepository::get_security_advisories/ load_async_packages finally overlaps for real: fetching 8 packages' metadata dropped from ~7-40s to a consistent ~3-4s in a before/after comparison, with identical resulting lock files. sync_executor::block_on's call sites are still synchronous rather than async fn propagated up to Command::execute, which remains the end goal (see the TODO(phase-e) in sync_executor.rs) - nested block_on calls elsewhere don't get this same overlap, only prevented panics.
2026-07-17refactor(composer-repository): fan out async metadata downloads concurrentlynsfisis
get_security_advisories/load_async_packages serialized every start_cached_async_download call through sync_executor::block_on per name, matching PHP's structure but not its promise-based concurrency. Wrap the mutable state those downloads touch (cache, fresh_metadata_urls, packages_not_found_cache, degraded_mode) in RefCell/Cell so start_cached_async_download and its async_fetch_file helpers can drop to &self, then fan out all downloads for a batch via FuturesOrdered before processing responses sequentially in original order, mirroring PHP's build-all-promises-then-wait shape. Real I/O overlap still awaits item 7 (HttpDownloader::add currently resolves through the curl_runtime()/sync_executor::block_on bridge either way), so this is architectural groundwork, not a perf win yet.
2026-07-05fix(composer-repository): use create_packages for lazy metadata-url packagesnsfisis
load_async_packages (the v2 metadata-url/packagist protocol path) called a separate create_packages_static helper instead of the instance method create_packages. PHP has a single createPackages method used everywhere, so this duplicate silently skipped the notification-url injection (and dist-mirror/transport-options setup) that create_packages performs. Every package resolved via the lazy provider path ended up missing notification-url in composer.lock/installed.json. Removed the now-dead duplicate.
2026-06-28fix(http): avoid nested tokio runtime panic in download pathnsfisis
The CurlDownloader owned a tokio runtime and block_on'd reqwest from its sync tick(), while the repository/installer/downloader sync bridges each created another Runtime and block_on'd async fns that reach that leaf. Driving one Runtime::block_on from within another panics with "Cannot start a runtime from within a runtime", hit by `require` when fetching p2 metadata. Switch CurlDownloader to a blocking reqwest client (its own internal thread, never nested) and replace the per-call Runtime::new().block_on bridges with a no-reactor sync_executor::block_on helper. No awaited future parks on a reactor once the only async I/O is blocking, so the helper can be nested freely. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28refactor: add linternsfisis
2026-06-27refactor: fix compiler warnings and clippy warningsnsfisis
2026-06-26test: port 70 perforce/repository/downloader/installer/dispatcher testsnsfisis
Port perforce (36), locker (10), composer_repository (7), installation_manager (6), file_downloader (5), and event_dispatcher (6) tests via the mock infra. Fix production porting bugs surfaced en route: BufferIO::get_output look-behind regex, ComposerRepository list-form package iteration and initialize dispatch, gethostname and spl_autoload_functions shims; add EventDispatcher get_listeners test seam. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24refactor(semver): re-export shirabe-semver at crate root, drop ↵nsfisis
composer::semver stubs Flatten shirabe-semver's modules into glob re-exports at the crate root and route all consumers through the short paths. Remove the duplicate composer::semver stubs from shirabe-external-packages in favor of the shirabe-semver types. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24refactor(crates): split metadata-minifier and spdx-licenses into own cratesnsfisis
Move MetadataMinifier and SpdxLicenses out of shirabe-external-packages into dedicated shirabe-metadata-minifier and shirabe-spdx-licenses crates, updating all import sites accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20refactor(php-shim): drop Box wrapping from PhpMixed List/Arraynsfisis
The List and Array variants of PhpMixed boxed their elements unnecessarily. Store PhpMixed values directly and update all callers accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20refactor(clippy): resolve idiomatic lint warningsnsfisis
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20refactor: auto-fix clippy warningsnsfisis
2026-06-14refactor: fix warningsnsfisis
2026-06-14refactor(pcre): drop Result from Preg method return typesnsfisis
The Preg methods panic on PCRE failure (per the file header rationale), so their anyhow::Result wrappers never carried an Err. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14refactor(pcre): drop strict-groups Preg variantsnsfisis
Rust's type system already distinguishes participating from non-participating capture groups via Option, so the *StrictGroups methods add no safety here. Remove them and switch callers to the plain variants.
2026-06-10feat(phase-c): resolve exception-handling phase-b TODOsnsfisis
* Catch specific exception types instead of broad/placeholder handling. * Drop the shim Countable trait.
2026-06-09feat(event-dispatcher): unify event dispatch via EventInterface traitnsfisis
Extract a superclass-trait EventInterface from the base Event. pool_builder's PrePoolCreateEvent stays deferred: its constructor needs owned, non-cloneable Request and repository boxes the builder only holds by reference (owned-payload blocker). The event is plugin-only, so its construction is re-tagged TODO(plugin). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08feat(phase-c): resolve PhpMixed-conversion phase-b TODOsnsfisis
Implement the foundational PhpMixed conversion infrastructure (From<bool|i64|f64|String>, order-sensitive PartialEq matching PHP ===) and resolve the category-G phase-b TODOs that depend on it: - Fix VCS driver cache paths that discarded parsed JSON or diverged on null caches (svn/forgejo/gitlab/git-bitbucket/github). - Wire up real conversions previously stubbed or dropped: suggests platform config, audit ignore-severities, composer_repository search and ProviderInfo, class_loader prefix/classmap merges, locker lock diff comparison, advisory JSON serialization, SPDX license fields. - Make GenericRule take a typed ReasonData; populate RULE_ROOT_REQUIRE with the constraint and convert PhpMixed at the call sites.
2026-06-07feat(metadata-minifier): port expand for minified package metadatansfisis
Implement MetadataMinifier::expand with the PHP list-of-arrays signature (Vec<IndexMap>) and wire it into ComposerRepository so composer/2.0 minified package metadata is expanded, resolving the phase-b TODO. minify() is left unported as it is not used in Composer itself. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07feat(shirabe): resolve advisory instanceof TODOs via AnySecurityAdvisorynsfisis
Add an Ignored variant to the advisory enum (renamed to AnySecurityAdvisory) so the PHP three-class hierarchy PartialSecurityAdvisory -> SecurityAdvisory -> IgnoredSecurityAdvisory maps one-to-one onto enum variants. Replace the hard-coded auditor downcasts with as_security_advisory() (PHP `instanceof SecurityAdvisory`, true for both full and ignored) and as_ignored(), implementing severity/cve/source ignore filtering, the toIgnoredAdvisory conversion, and the table/plain row output faithfully. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06refactor(http-response): take url directly instead of request mapnsfisis
Response only ever reads the url out of the request array, so accept it as a String directly. With url always present the 'url key missing' LogicException can no longer fire, so Response::new and CurlResponse::new return Self instead of a double Result. Also drops the unused from_php_mixed/to_php_mixed stubs and the request_to_map helper. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06feat(package-repository): filter advisories by affected version constraintnsfisis
Implement the previously stubbed phase-b TODO so getSecurityAdvisories skips advisories whose affected_versions do not match the requested package constraint. Share the package VersionParser with PartialSecurityAdvisory::create across both PackageRepository and ComposerRepository instead of a separate semver parser instance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06refactor(repository): make read methods fallible and take &mut selfnsfisis
Change RepositoryInterface and WritableRepositoryInterface read methods (find_package, find_packages, get_packages, load_packages, search, get_providers, get_canonical_packages) to take &mut self and return anyhow::Result, so lazy-loading repositories such as ComposerRepository can perform fallible I/O and mutate internal state on access. Update all implementors and call sites to propagate the Result and pass mutable references. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05refactor(json): model JsonFile encode/write options as a typed structnsfisis
Replace the i64 bitmask + encode_with_indent split with a JsonEncodeOptions struct (Default = JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE, default indent). encode/write each get a default form plus an explicit encode_with_options/write_with_options variant, mirroring PHP's optional $options argument. write_with_options always encodes with self.indent, matching PHP write(). Also reconcile call sites with the PHP sources: most ported sites passed 0 where Composer omits the argument (= default flags), so JsonManipulator/ShowCommand and JsonConfigSource now use the default options; only ComposerRepository and Locker genuinely pass 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04feat(package): implement PackageInterface accessor layer for ↵nsfisis
Package/CompletePackage/RootPackage Fill the trait-impl todo!() across the package accessor layer so loaded packages are actually usable (the ArrayLoader::load path depends on it): - Package: implement BasePackage/PackageInterface from struct fields and existing inherent methods; Display via get_unique_name. - CompletePackage: delegate PackageInterface to inner Package. - RootPackage: delegate CompletePackageInterface/PackageInterface to inner CompletePackage; RootPackageInterface link setters delegate to Package. Correct three unfaithful trait signatures found during implementation: - get_target_dir returns Option<String> (PHP computes a normalized value; a borrow cannot represent it, and AliasPackage could not implement &str across its aliasOf handle). - RootPackageInterface link setters take IndexMap<String, Link>, matching Package and the real ArrayLoader caller (PHP RootPackage inherits Package::setRequires; the Link[] docblock was imprecise). - get_full_pretty_version takes a DisplayMode enum instead of a raw i64; the match is now exhaustive, so it returns String without an error path. Move mirror conversion to the boundaries: PackageInterface mirror methods use Vec<Mirror> (the typed form, matching the inherent methods), with array<->Mirror conversion done by the producer (ComposerRepository) and consumer (ArrayDumper). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02feat(resolver): port SecurityAdvisoryPoolFilter::filternsfisis
Implement the security advisory pool filter end to end, plus the remaining actionable wirings it unblocked. - Unify the PartialSecurityAdvisory|SecurityAdvisory union as the PartialOrFullSecurityAdvisory enum and make the advisory types Clone, so advisories can be collected and stored; Pool.security_removed_versions now carries the union. This also unblocks PoolOptimizer's clone of the security-removed versions. - Thread the filter result through run_security_advisory_filter/build_pool as anyhow::Result. - Introduce typed PlatformRepositoryHandle and pass platform repos as handles through determine_requirements instead of &PlatformRepository. - Wire RuleSetGenerator's is_unacceptable_fixed_or_locked_package check and UpdateCommand's non-locked installed-packages branch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29refactor(io): unify IOInterface params to Rc<RefCell<dyn _>>nsfisis
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28refactor(repository): introduce Rc<RefCell<_>> handles for repositoriesnsfisis
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27refactor(package): pass package handles by value throughoutnsfisis
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26refactor(io): share IOInterface via Rc<RefCell<dyn _>> handlensfisis
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25refactor(package): introduce Rc<RefCell<_>> handles for packagesnsfisis
PHP packages have reference semantics, so introduce shared-ownership handles over an AnyPackage enum (PackageInterfaceHandle and friends) and replace Box<dyn PackageInterface> throughout. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23refactor(semver): change ConstraintInterface to a closed enumnsfisis
Replace the dyn ConstraintInterface trait objects with an AnyConstraint enum closing over its four implementors (Simple, Multi, MatchAll, MatchNone), mirroring the earlier Rule enum conversion. Rename constraint.rs to simple_constraint.rs to match the renamed Constraint type. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23refactor(promise): drop \React\Promisensfisis
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23refactor(promise): rewrite promise bodies to async/awaitnsfisis
Mechanically convert promise-returning function bodies to async/await: resolve() returns the value directly, forwarding calls get .await, and simple .then chains become await sequences. Also collapse the installer double-Option (Result<Option<Option<PhpMixed>>> -> Result<Option<PhpMixed>>). Hard spots that depend on the Loop::wait / job-machine boundary (accept/reject orchestration, closures capturing &mut self, batch waits) are left intact and marked with TODO(phase-c-promise) for manual porting. The crate does not compile yet; traits still need #[async_trait]. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>