aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/installer/installation_manager.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-28 23:10:43 +0900
committernsfisis <nsfisis@gmail.com>2026-06-28 23:10:43 +0900
commit1a85368838c1e482f2a7020ff1cbdc87fcb092cc (patch)
tree6c89ebc81f401abc1af8116eebb10905697d6649 /crates/shirabe/src/installer/installation_manager.rs
parent1e72ee3ddaf581753e30b39dfc33b48d44a25ef7 (diff)
downloadphp-shirabe-1a85368838c1e482f2a7020ff1cbdc87fcb092cc.tar.gz
php-shirabe-1a85368838c1e482f2a7020ff1cbdc87fcb092cc.tar.zst
php-shirabe-1a85368838c1e482f2a7020ff1cbdc87fcb092cc.zip
fix(installer): execute install/update/uninstall ops in execute_batch
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.
Diffstat (limited to 'crates/shirabe/src/installer/installation_manager.rs')
-rw-r--r--crates/shirabe/src/installer/installation_manager.rs93
1 files changed, 65 insertions, 28 deletions
diff --git a/crates/shirabe/src/installer/installation_manager.rs b/crates/shirabe/src/installer/installation_manager.rs
index ea6f668..c1e65a8 100644
--- a/crates/shirabe/src/installer/installation_manager.rs
+++ b/crates/shirabe/src/installer/installation_manager.rs
@@ -616,20 +616,59 @@ impl InstallationManager {
let _dispatcher = self.event_dispatcher.as_ref();
let _io = self.io.as_ref();
- let installer = self.get_installer(&package.get_type())?;
- // TODO(phase-c-promise): PHP chains prepare()->then(install/update/uninstall)->then(cleanup
- // + repo.write); the single-threaded loop awaits prepare and leaves the rest as phase-b work.
- installer
- .prepare(&op_type, package, initial_package)
- .await?;
+ {
+ let installer = self.get_installer(&package.get_type())?;
+ installer
+ .prepare(&op_type, package.clone(), initial_package.clone())
+ .await?;
+ }
// PHP: $promise = $promise->then(fn() => $this->{$type}(...))->then($cleanupPromises[$index])
// ->then(fn() => $repo->write($devMode, $this->io)); the chained steps run install/
- // update/uninstall, then cleanup, then persist the repository.
- // TODO(phase-c): this promise chain (install step -> cleanup_promises[index] ->
- // repo.write) needs the React\Promise model and the cleanup callables wired (see above);
- // both stay todo!(), so only prepare() is awaited here.
- let _ = cleanup_promises.get(&index);
+ // update/uninstall, then cleanup, then persist the repository. The single-threaded
+ // loop awaits the steps serially instead of composing React promises.
+ let op_result = match op_type.as_str() {
+ "install" => {
+ let op = operation
+ .as_install_operation()
+ .expect("op_type == \"install\" implies InstallOperation");
+ self.install(repo, op).await
+ }
+ "update" => {
+ let op = operation
+ .as_update_operation()
+ .expect("op_type == \"update\" implies UpdateOperation");
+ self.update(repo, op).await
+ }
+ "uninstall" => {
+ let op = operation
+ .as_uninstall_operation()
+ .expect("op_type == \"uninstall\" implies UninstallOperation");
+ self.uninstall(repo, op).await
+ }
+ _ => unreachable!("op_type is one of install/update/uninstall"),
+ };
+
+ // PHP rejects the promise with an "<op> of <name> failed" message before rethrowing.
+ if let Err(e) = op_result {
+ self.io.write_error(&format!(
+ " <error>{} of {} failed</error>",
+ shirabe_php_shim::ucfirst(&op_type),
+ package.get_pretty_name()
+ ));
+ return Err(e);
+ }
+
+ // TODO(phase-c-promise): cleanup_promises[index] currently resolves to a no-op future
+ // (the real installer.cleanup() chain depends on the Rc/Arc installer rework).
+ if let Some(cleanup) = cleanup_promises.get(&index)
+ && let Some(fut) = cleanup()
+ {
+ fut.await?;
+ }
+
+ // PHP: ->then(fn() => $repo->write($devMode, $this)) persists the repository after each op.
+ repo.write(dev_mode, self);
let event_name_post = match op_type.as_str() {
"install" => PackageEvents::POST_PACKAGE_INSTALL,
@@ -672,14 +711,14 @@ impl InstallationManager {
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
operation: &InstallOperation,
- ) -> Option<PhpMixed> {
+ ) -> Result<Option<PhpMixed>> {
let package = operation.get_package();
let package_type = package.get_type();
- let installer = self.get_installer(&package_type).ok()?;
- let promise = installer.install(repo, package.clone()).await.ok()?;
+ let installer = self.get_installer(&package_type)?;
+ let promise = installer.install(repo, package.clone()).await?;
self.mark_for_notification(package.clone());
- promise
+ Ok(promise)
}
/// Executes update operation.
@@ -687,7 +726,7 @@ impl InstallationManager {
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
operation: &UpdateOperation,
- ) -> Option<PhpMixed> {
+ ) -> Result<Option<PhpMixed>> {
let initial = operation.get_initial_package().clone();
let target = operation.get_target_package().clone();
@@ -695,20 +734,18 @@ impl InstallationManager {
let target_type = target.get_type();
if initial_type == target_type {
- let installer = self.get_installer(&initial_type).ok()?;
- let promise = installer.update(repo, initial, target.clone()).await.ok()?;
+ let installer = self.get_installer(&initial_type)?;
+ let promise = installer.update(repo, initial, target.clone()).await?;
self.mark_for_notification(target.clone());
- promise
+ Ok(promise)
} else {
// PHP: uninstall initial, then install target via the target-type installer.
let _ = self
- .get_installer(&initial_type)
- .ok()?
+ .get_installer(&initial_type)?
.uninstall(repo, initial)
- .await
- .ok()?;
- let installer = self.get_installer(&target_type).ok()?;
- installer.install(repo, target).await.ok()?
+ .await?;
+ let installer = self.get_installer(&target_type)?;
+ installer.install(repo, target).await
}
}
@@ -717,12 +754,12 @@ impl InstallationManager {
&mut self,
repo: &mut dyn InstalledRepositoryInterface,
operation: &UninstallOperation,
- ) -> Option<PhpMixed> {
+ ) -> Result<Option<PhpMixed>> {
let package = operation.get_package();
let package_type = package.get_type();
- let installer = self.get_installer(&package_type).ok()?;
+ let installer = self.get_installer(&package_type)?;
- installer.uninstall(repo, package).await.ok()?
+ installer.uninstall(repo, package).await
}
/// Executes markAliasInstalled operation.