| Age | Commit message (Collapse) | Author |
|
Application::find and BaseCommand::reset_composer are now available,
so the deferred update_dependencies/run_dump_autoload_command stubs
can find, reset, and run the sibling command directly, matching
InitCommand's PHP behavior.
|
|
BaseCommand::createComposerInstance and initialize() OR in the
Application's getDisablePluginsByDefault()/getDisableScriptsByDefault()
on top of the --no-plugins/--no-scripts flags; this was deferred with
a TODO(phase-c) since the shared Application handle wasn't wired up
yet. The same get_application() + downcast pattern used by get_io()
now covers this too.
|
|
Convert errors/warnings/config to RefCell so load() can satisfy the
trait's &self signature, matching upstream's `instanceof
ValidatingArrayLoader` check in VcsRepository. This makes the
InvalidPackageException downcast path in VcsRepository reachable for
the first time instead of being permanently dead code.
|
|
fallback paths
VcsDriverInterface::get_source/get_dist were made fallible in the Rust
port even though PHP's are infallible, forcing GitBitbucketDriver's
fallback delegation to silently swallow errors via
unwrap_or_default()/ok().flatten(). Make the trait infallible to match
PHP, updating the mechanical Ok(...) wrapping in all implementors.
Also narrow attempt_clone_fallback's cleanup to only trigger on
RuntimeException, mirroring PHP's catch (\RuntimeException $e), using
the same downcast pattern already used by has_composer_file for
TransportException.
|
|
paths
Both code paths were left as phase-c placeholders (debug-formatted
reason_data, and a security-advisory fallback that ignored
getMatchingSecurityAdvisories entirely). The blockers noted in those
TODOs were already resolved elsewhere (RuleSetGenerator now wires
reason_data for alias rules, and BasePackageHandle/PackageInterfaceHandle
are the same type), so port the PHP logic faithfully.
|
|
AutoloadGenerator::dump
The two phase-c TODOs blocking these calls were already resolved
elsewhere (RepositoryInterfaceHandle::as_installed_repository_interface_mut
was added after this file was ported), so reinstall now actually
performs the uninstall/install operations and regenerates the
autoloader instead of no-oping. Mirrors the pattern already used in
installer.rs and dump_autoload_command.rs.
|
|
get_remote_contents was a full stub always returning None, so any
file:// download raised a TransportException. Read local files
directly for the file scheme, mirroring PHP's file_get_contents
transparently handling the file:// stream wrapper. Also fixes
file_get_contents5 to strip the file:// prefix like the 0-arg
variant already did.
|
|
Platform::get_env("CI").is_some()/is_none() only checked whether the
variable was set, unlike PHP's (bool) Platform::getEnv('CI') which
treats "" and "0" as falsy. CI="0" (used by some CI providers to
explicitly disable CI mode) would previously flip behavior compared to
Composer.
|
|
Real Composer shows a ProgressBar during InstallationManager's
waitOnPromises (`Package operations: N installs` ... `0/109 [>---] 0%`
... `100%`), but this port never wired one up: output_progress was set
by callers and never consulted. This adds the same gating PHP uses
(output_progress, ConsoleIO, not CI, not debug, more than one
operation) for both the download phase and the install/extract phase.
The port runs downloads/installs serially rather than as concurrently
polled promises (see the existing TODO(phase-c-promise) notes), so
there is no active-job count to poll for intermediate snapshots.
Stepping the bar per completed operation was tried first, but it
interleaves with the "- Installing ..." lines mid-terminal-line since
both share the same overwrite/newline state; rendering a single 0% ->
100% jump after each phase avoids that garbling at the cost of the
timing-driven intermediate snapshots real Composer shows.
|
|
parse_autoloads_type stores exclude-from-classmap and classmap as
PhpMixed::Array (string-keyed), but dump()/create_loader() read them
with as_list(), which only matches PhpMixed::List and thus always
returned None. exclude-from-classmap patterns declared by vendor
packages (e.g. symfony/service-contracts' /Test/) were consequently
never excluded from the generated classmap. Switched both call sites
to as_array()/.values(), matching the already-correct usage earlier in
the same function.
|
|
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.
|
|
Cache::read/write/copy_to called write_error (always visible) instead
of write_error3(.., IOInterface::DEBUG) like the PHP source, so these
messages leaked into default-verbosity output instead of only showing
under -vvv, producing extra lines not present in real Composer's
output.
|
|
The --profile flag parsed the option but never actually enabled the
timing/memory output, because ConsoleIO::enableDebugging existed only as an
inherent method, unreachable through the `dyn IOInterface` handle held by
Application. Promote enable_debugging to an IOInterface trait method
(default panics, since only ConsoleIO/BufferIO ever legitimately receive
this call) so do_run can invoke it directly without downcasting.
|
|
Wires PartialComposer.as_full() to fetch the PluginManager, replacing
the todo!() stub. Mirrors PHP's assertion that $this->composer must be
a fully-loaded Composer instance.
|
|
ArgvInput stores variadic arguments as PhpMixed::List, not
PhpMixed::Array, so as_array() always returned None and the search
query was silently empty, causing packagist to reject every request
with a 400 Bad Request.
|
|
PHP's addOverriddenPackage calls parent::addPackage() (a non-virtual
call straight to ArrayRepository), but the port called self.add_package,
re-entering PlatformRepository::add_package. Since the newly created
override package's name also starts with "php-", this recursed
forever and blew the stack.
|
|
Avoid colliding with an existing Composer installation on the same
machine by defaulting COMPOSER_HOME/CACHE_DIR/DATA_DIR paths to
shirabe/Shirabe instead of composer/Composer, while keeping the env
var names, composer.json/lock, and vendor/composer/ unchanged for
ecosystem compatibility.
|
|
AnyPackage::dup() (PHP's `clone $package`) copied the `repository` and
`id` fields verbatim instead of resetting them like PHP's
BasePackage::__clone() does. LibraryInstaller::install() relies on the
duplicate being unbound so it can register the package with the local
repository; without the reset, add_package() silently failed with "A
package can only be added to one repository", leaving installed.json
empty after every install/create-project run.
|
|
Two related "already borrowed" panics reachable from
AutoloadGenerator::dump() (which holds the local-repository,
installation-manager, and config RefCells for the duration of its own
statement, per the temporary-lifetime-extension pattern fixed
separately in create_project_command.rs):
- ensure_bin_dir_is_in_path called config.borrow_mut() to read
"bin-dir", but Config::get only needs &self; use borrow() so it can
coexist with an outer borrow instead of conflicting with it.
- make_autoloader's real body needed composer_handle.borrow_mut() plus
the same local-repository/installation-manager RefCells the caller
already holds mutably, which cannot be made reentrant-safe without a
larger restructuring. Since all 3 call sites already discard its
return value, and its only effect (registering a Composer-generated
ClassLoader for autoloading during event-listener PHP execution) is
unobservable in this port — there's no embedded PHP interpreter to
register it into, and class_exists for user-defined classes is a
hardcoded-false shim so the caller's very next check always treats
the class as unavailable regardless — make it a genuine no-op.
This unblocks the post-autoload-dump event for any script listener
naming a PHP class (e.g. Illuminate\Foundation\ComposerScripts), which
every create-project/install run reaches once real packages get
installed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
PHP's createRepositorySet does `$this->fixedRootPackage = clone
$this->package;` once and then passes that same object both to `new
RootPackageRepository($this->fixedRootPackage)` and, later, to
createRequest($this->fixedRootPackage) — object identity matters
because the solver assigns a pool id by mutating the package object
itself.
The port instead called RootPackageInterfaceHandle::dup() a second
time when registering the RootPackageRepository, producing a second
object that never went through the pool and so never got an id.
create_request's `request.fix_package(root_package_handle)` then
referenced a package with id -1, which add_rules_for_request treats as
a real bug: "Fixed package ... was not added to solver pool." This
surfaced whenever a create-project run reached the second-stage
install (i.e. every dist-installed project with real dependencies).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
install_project's fluent builder chain called config.borrow_mut() four
times inline as method arguments. Rust extends every argument
temporary's lifetime to the end of the enclosing statement, so the
first borrow_mut() was still alive when the second one ran, panicking
with "already borrowed". Compute each value into a local before the
chain instead, matching the pattern already used in
install/update/require/remove_command.rs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
Route the platform-repository seams that need real PHP introspection
through php-rpc instead of todo!()/hardcoded shims:
- Runtime::invoke now handles the two dynamic callables
PlatformRepository actually reaches (inet_pton, curl_version) via new
php-rpc calls; other callables remain unsupported.
- Runtime::get_extension_info uses a new `extension_info` php-rpc call
(ReflectionExtension::info() + output buffering) instead of todo!().
- Runtime::get_extensions/get_extension_version now query the real PHP
process (get_loaded_extensions, phpversion) instead of the
shirabe-php-shim's hardcoded "standard CLI environment" model, so
platform requirement checks see the extensions actually installed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
Several Preg::*() call sites lost their PHP delimiter (and in one case
the `i` modifier) during porting, since preg_*() expects the delimiter
to be preserved in the caller's pattern literal and stripped internally.
This made compile_php_pattern panic or silently misparse the pattern.
Un-ignore the Version tests that were blocked by this bug.
|
|
|
|
Runtime::hasConstant/getConstant need a real PHP interpreter's defined()/
constant() to answer platform requirement checks (e.g. PHP_ZTS, PHP_INT_SIZE),
which the shim can't provide since Rust constants aren't queryable by string.
Extend shirabe-php-rpc's protocol to carry one string argument and return the
full PHP scalar range, add defined/constant dispatch entries to the worker,
and wire Runtime and get_php_version/get_php_binary onto them.
|
|
Extends no_banned_use to cover std::any::Any, std::io::Read/Write, and
std::process::Command, and teaches the linter to allow `as _` imports
so trait methods can still be brought into scope without binding the
banned name. Fully qualifies all existing usages across the codebase.
|
|
PlatformRepository::initialize() (php-version/extension detection) was
never invoked: the RepositoryInterface impl delegated straight to the
inner ArrayRepository without the ensure_initialized lazy-init guard
that sibling repositories (FilesystemRepository, PathRepository) use.
As a result pool.what_provides("php") was always empty, and any
package requiring php failed platform resolution. Also fixes the
trait search() bypassing PlatformRepository's own SEARCH_VENDOR
override.
|
|
PathRepository already implements RepositoryInterface and create_repository
already handles the "path" class, so the prior ignore reason no longer
applies; verify the FilterRepository/PathRepository wrapping via
RepositoryInterfaceHandle::is/downcast_rc.
|
|
Port testNoPluginsDisablesPluginsWhenScriptCommandsExist and
testScriptCommandTakesPriorityOverAbbreviatedBuiltinCommand. Both stay
#[ignore]d because do_run panics at the script-registration todo!()
(application.rs:2461) when composer.json has scripts. Add a test-only
ApplicationHandle::__get_composer accessor so the first test's
getPluginManager assertions can be expressed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Wire a git downloader into the test's DownloadManager (mirroring
Factory::createDownloadManager) so the archive path clones the package
source as PHP does. Both remain #[ignore]'d on PharData tar archiving
(new_with_format/build_from_iterator are todo!() in the php-shim).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
The faithful port drives a real HttpDownloader + Loop over a file://
dist URL, but RemoteFilesystem::get_remote_contents is a phase-c stub
returning None, so the download fails with a "could not be downloaded"
TransportException before reaching the ZipArchive "is not a zip
archive" path. Kept #[ignore] with the corrected reason (the previous
curl_multi_init note was wrong, file:// never uses curl).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
These three tests exercise getContents/copy against a file:// URL (and a
real network download for BitBucket). They remain #[ignore] because
get_remote_contents has no stream/file layer yet (TODO(phase-c)) and
returns None, so the calls raise a TransportException. The ignore
reasons are updated to reflect the actual failure point.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Port testVendorDirExcludedFromWorkingDir, testUpLevelRelativePaths,
testGeneratesPlatformCheck (all 12 data-provider rows), and both
testAbsoluteSymlinkWith* tests from the Composer suite.
testVendorDirExcludedFromWorkingDir passes. The other four expose
behavioral gaps in shirabe (exclude-from-classmap with up-level/symlink
paths, psr-4 symlink warnings, get_platform_check provider matching), so
they keep their fully-ported bodies but are marked #[ignore] with the
specific incompatibility rather than weakening expectations.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Add a minimal shirabe-php-rpc crate that spawns the system PHP as a
child process and asks it for runtime information over a Unix domain
socket, then use it to fill the `--version` PHP line with the real
\PHP_VERSION and \PHP_BINARY instead of fixed placeholder values.
See docs/dev/php-rpc.md for the design and scope.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Model Git::runCommand's mixed $commandOutput parameter with the
RunCommandOutput trait (one impl per PHP mode: discard, by-ref capture,
callable handler), mirroring ProcessExecutor's IntoExecOutput. This moves
the is_callable($commandOutput) value-inspection into the type system and
drops the dependency on the shim's incomplete is_callable.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Resolve the two todo!() placeholders by passing self.inner.io.clone()
to VcsDriverBase::new and Forgejo::new, matching the PHP source's
$this->io arguments and the GitHub/GitLab driver idiom.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
StringInput inherits __toString from ArgvInput in PHP; mirror that with a
Display impl delegating to its inner ArgvInput, and use it in
GlobalCommand::input_to_string.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Resolve the remaining todo! in update_requirements_after_resolution by
passing the stability-flags rewriter closure to Locker::update_hash,
porting the static closure from RequireCommand.php.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Port AuditCommand::getPackages's non-locked path: build an
InstalledRepository from the local repository and return its packages,
filtered by RootPackage requires when --no-dev is set. The prior
TODO(phase-c) assumption (InstalledRepository::new vs get_local_repository
type mismatch) no longer holds since both sides use RepositoryInterfaceHandle.
Enables the two previously ignored audit command tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
It is faithful to PHP's by-value array/string semantics.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
The `!@json_decode($children)` guards in addSubNode/removeSubNode were
translated as `is_null() || as_bool() == Some(false)`, which drops PHP's
other falsy cases (empty array/string, "0", 0). At the removeSubNode site
(assoc=true) this diverged from Composer: an empty object node decodes to
an empty array, which PHP treats as falsy and aborts on, but the Rust
guard kept going. Replace both with php_truthy, the faithful rendering of
PHP `!`, preserving the deliberate empty-`{}` behavior difference between
the assoc=false (addSubNode) and assoc=true (removeSubNode) sites.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Auditor::audit took io as &mut dyn IOInterface, forcing the audit and
installer post-audit call sites to hold a borrow_mut() on the shared IO
RefCell for the whole call. During advisory fetching the repositories
write to their own clones of the same handle, so the borrow_mut()
collided with their borrow() and panicked with 'RefCell already mutably
borrowed' on 'audit --locked'. Take the Rc<RefCell<dyn IOInterface>>
handle instead so writes borrow briefly and never overlap. Un-ignore the
locked-audit regression test that this unblocks.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Validate JSON syntax with serde_json's parse errors in JsonFile, and detect
duplicate keys in ConfigValidator with a hand-written serde visitor, dropping
the now-unused JsonParser/Lexer/DuplicateKeyException ports. ParsingException
is kept as the thrown error type and downcast signal.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Add a no_banned_use linter that forbids importing anyhow::Result, and
update all call sites to reference it via its fully-qualified path so
it is never confused with std::result::Result.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
The platform: section is empty because PlatformRepository::get_packages
routes to ArrayRepository::initialize instead of its own initialize, which
needs the still-todo!() runtime::constant() shim. Record the real blocker.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
flattenRepositories must recurse into InstalledRepository (which extends
CompositeRepository in PHP) and ShowCommand must unwrap FilterRepository
when categorizing repos. Without this, installed/locked/platform packages
all fell through to the "available" bucket, dropping the version column
and per-section grouping. Un-ignores 10 show_command tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
execute_batch only awaited prepare(), leaving the install/update/
uninstall + cleanup + repo.write promise chain as a todo!() stub, so
packages were downloaded to cache but never installed into the target
directory. Wire the operation step (mirroring PHP's promise chain),
propagate errors from the install/update/uninstall wrappers instead of
swallowing them with .ok()?, and write the repo after each op.
Un-ignore the create-project functional tests and the install/remove
command tests this unblocks. The remaining --no-install case still fails
on a separate install-path bug; its ignore reason is updated to match.
|
|
Port composer/tests/Composer/Test/InstallerTest.php. testInstaller (the
provideInstaller cases) is fully ported and passes; the three integration
tests port doTestIntegration in full (the .test fixture loader, FactoryMock,
the in-process console Application with install/update commands, and the
PHPUnit assertStringMatchesFormat matcher) and remain #[ignore]'d since the
install pipeline is not yet executable end-to-end.
Add test-only `__`-seams to the concrete types the test depends on, since
their consumers (e.g. Locker takes the concrete InstallationManager) and the
subclass-style mocks have no trait to mock: InstallationManager (recording
mock + as_any), Factory (__create_mock), VersionGuesser, and
InstalledFilesystemRepository. The production path (mock: false) is unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Implements the todo!() stubs in all_functional_test.rs by faithfully
porting parseTestFile, cleanOutput and the inline %regex% EXPECT matcher,
plus a subprocess runner that drives CARGO_BIN_EXE_shirabe (in place of
PHP's composer.phar) over the reused Fixtures/functional .test files.
The five fixtures are wired as individual #[serial] #[ignore] tests with
per-fixture reasons: the two create-project cases need network + a real
git clone, and the three update cases need the not-yet-implemented Plugin
API. test_build_phar stays an ignored stub since Rust has no phar build.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|