/** * Updates a given package. * * This is the wrong method if you are looking * for something like 'composer update'. Look * at the 'configurePackage' method instead. * * @throws ComposerException * @param $packageName * @return mixed */ public function updatePackage($packageName) { if (!$this->packageExists($packageName)) { return false; } $newPackageLocation = $this->resolvePackagePath($packageName); $oldPackageLocation = $this->normalizePath($newPackageLocation . '_{updating_in_progress}'); $installationInstructionsPath = $this->normalizePath($oldPackageLocation . DIRECTORY_SEPARATOR . '_newup_install_instructions'); if ($this->files->exists($oldPackageLocation)) { $this->files->deleteDirectory($oldPackageLocation, false); } $this->files->makeDirectory($oldPackageLocation, 0755, true); $this->files->copyDirectory($newPackageLocation, $oldPackageLocation); $this->files->deleteDirectory($newPackageLocation, false); // Retrieve the old installation instructions that were stored // when the package template was first installed. $installInstructions = $this->files->get($installationInstructionsPath); // Write updated initiated here in case of early failures. $this->writePackageUpdateInitiated($newPackageLocation, $installInstructions); try { $this->addPackage($installInstructions); // Write updated initiated here in case of failures during configuration. $this->writePackageUpdateInitiated($newPackageLocation, $installInstructions); $this->configurePackage($packageName); $this->files->deleteDirectory($oldPackageLocation, false); $this->log->info('Updated package template', ['package' => $packageName]); // If we are at this point of the update process, we can safely assume that // it is okay to remove the update initiated file. $this->removePackageUpdateInitiatedFile($newPackageLocation); return true; } catch (\Exception $e) { // There was an issue updating the package. We will rollback. $this->files->deleteDirectory($newPackageLocation, false); $this->files->makeDirectory($newPackageLocation, 0755, true); $this->files->copyDirectory($oldPackageLocation, $newPackageLocation); $this->files->deleteDirectory($oldPackageLocation, false); $this->log->debug('Package updated failed', ['package' => $packageName, 'message' => $e->getMessage(), 'exception' => $e]); throw new ComposerException('There was an unexpected failure during the package update process.', $e->getCode(), $e); } }
/** * Execute the console command. * * @return mixed */ public function handle() { if (!$this->confirm("Are you sure you want to run the analysis tool? This tool will make modifications to the template storage." . PHP_EOL . "While it is intended to clean up the template storage, it could potentially fail. [yes|no]", false)) { $this->comment('Analysis aborted by user'); return; } $this->info('Starting analysis of template store...'); $this->line('Store located at ' . $this->templateStorageEngine->getStoragePath()); $vendors = $this->templateStorageEngine->getInstalledVendors(); $packages = $this->templateStorageEngine->getInstalledPackages(true); $packageCount = 0; foreach ($packages as $package) { $packageCount += count($package['packages']); } $this->line('Found ' . count($vendors) . ' vendors and ' . $packageCount . ' packages...'); $this->comment(PHP_EOL . 'Searching for failed update processes...'); $failedUpdateProcesses = []; $garbagePackageDirectories = []; foreach ($packages as $vp) { foreach ($vp['packages'] as $package) { if (Str::endsWith($package['path'], '_{updating_in_progress}')) { $garbagePackageDirectories[] = $package; $this->line('Identified package as possible garbage ' . $package['path']); } else { if ($this->files->exists($package['path'] . DIRECTORY_SEPARATOR . '_newup_update_initiated')) { $failedUpdateProcesses[] = $package; $this->line('Identified failed package update process ' . $package['path']); } } } } $this->line('Found ' . count($failedUpdateProcesses) . ' failed update processes'); $this->line('Found ' . count($garbagePackageDirectories) . ' update directories ready for cleanup'); $this->comment(PHP_EOL . 'Analyzing search results...'); $recoverablePackages = []; $nonRecoverablePackages = []; foreach ($failedUpdateProcesses as $failedUpdate) { if ($this->files->exists($failedUpdate['path'] . '_{updating_in_progress}')) { $this->line('Identified package as recoverable ' . $failedUpdate['path']); $recoverablePackages[] = $failedUpdate; } else { $this->line('Identified a non-recoverable package ' . $failedUpdate['path']); $nonRecoverablePackages[] = $failedUpdate; } } $this->line('Found ' . count($recoverablePackages) . ' recoverable failed update processes'); $this->line('Found ' . count($nonRecoverablePackages) . ' non-recoverable failed update processes'); $this->comment(PHP_EOL . 'Cleaning up non-recoverable update processes...'); if (count($nonRecoverablePackages) > 0) { foreach ($nonRecoverablePackages as $nonRecoverablePackage) { $this->files->deleteDirectory($nonRecoverablePackage['path'], false); $this->line('Cleaning up directory ' . $nonRecoverablePackage['path']); } } else { $this->line('Nothing to clean up'); } $this->comment(PHP_EOL . 'Cleaning up failed update processes...'); if (count($garbagePackageDirectories) > 0) { foreach ($garbagePackageDirectories as $garbage) { $originalPackagePath = $garbage['path']; $originalPackagePath = str_replace('_{updating_in_progress}', '', $originalPackagePath); if ($this->files->exists($originalPackagePath)) { if ($this->files->exists($originalPackagePath . DIRECTORY_SEPARATOR . '_newup_update_initiated')) { $this->comment('Recovering failed update process for ' . $originalPackagePath); $this->line('Removing failed update files at ' . $originalPackagePath); $this->files->deleteDirectory($originalPackagePath); $this->line('Recovering files...'); $this->files->copyDirectory($garbage['path'], $originalPackagePath); if ($this->files->exists($originalPackagePath)) { $this->info('Recovered files at ' . $originalPackagePath); } else { $this->error('Could not recover files at ' . $originalPackagePath); } $this->line('Cleaning up garbage files at ' . $garbage['path']); $this->files->deleteDirectory($garbage['path']); } else { // Original package path does not exist, remove trash. $this->line('Removing garbage ' . $garbage['path'] . ' because ' . $originalPackagePath . ' no longer exists'); $this->files->deleteDirectory($garbage['path']); } } else { // Original package path does not exist, remove trash. $this->line('Removing garbage ' . $garbage['path'] . ' because ' . $originalPackagePath . ' no longer exists'); $this->files->deleteDirectory($garbage['path']); } } } else { $this->line('Nothing to clean up'); } $this->comment(PHP_EOL . 'Analysis complete'); }