mirror of
				https://github.com/getgrav/grav.git
				synced 2025-10-31 02:15:55 +01:00 
			
		
		
		
	| @@ -39,6 +39,8 @@ use function uniqid; | ||||
| use function trim; | ||||
| use function strpos; | ||||
| use function unlink; | ||||
| use function ltrim; | ||||
| use function preg_replace; | ||||
| use const GRAV_ROOT; | ||||
| use const GLOB_ONLYDIR; | ||||
| use const JSON_PRETTY_PRINT; | ||||
| @@ -75,15 +77,29 @@ class SafeUpgradeService | ||||
|         $root = $options['root'] ?? GRAV_ROOT; | ||||
|         $this->rootPath = rtrim($root, DIRECTORY_SEPARATOR); | ||||
|         $this->parentDir = $options['parent_dir'] ?? dirname($this->rootPath); | ||||
|         $defaultStaging = $options['staging_root'] ?? ($this->parentDir . DIRECTORY_SEPARATOR . 'grav-upgrades'); | ||||
|         if (!$this->ensureWritable($defaultStaging)) { | ||||
|             $fallback = getenv('HOME') ? getenv('HOME') . DIRECTORY_SEPARATOR . 'grav-upgrades' : sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'grav-upgrades'; | ||||
|             if (!$this->ensureWritable($fallback)) { | ||||
|                 throw new RuntimeException(sprintf('Unable to create staging directory at %s or fallback %s. Adjust permissions or configure system.updates.staging_root.', $defaultStaging, $fallback)); | ||||
|             } | ||||
|             $defaultStaging = $fallback; | ||||
|  | ||||
|         $candidates = []; | ||||
|         if (!empty($options['staging_root'])) { | ||||
|             $candidates[] = $options['staging_root']; | ||||
|         } | ||||
|         $candidates[] = $this->parentDir . DIRECTORY_SEPARATOR . 'grav-upgrades'; | ||||
|         if (getenv('HOME')) { | ||||
|             $candidates[] = getenv('HOME') . DIRECTORY_SEPARATOR . 'grav-upgrades'; | ||||
|         } | ||||
|         $candidates[] = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'grav-upgrades'; | ||||
|  | ||||
|         $this->stagingRoot = null; | ||||
|         foreach ($candidates as $candidate) { | ||||
|             $resolved = $this->resolveStagingPath($candidate); | ||||
|             if ($resolved) { | ||||
|                 $this->stagingRoot = $resolved; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (null === $this->stagingRoot) { | ||||
|             throw new RuntimeException('Unable to locate writable staging directory. Configure system.updates.staging_root or adjust permissions.'); | ||||
|         } | ||||
|         $this->stagingRoot = realpath($defaultStaging) ?: $defaultStaging; | ||||
|         $this->manifestStore = $options['manifest_store'] ?? ($this->rootPath . DIRECTORY_SEPARATOR . 'user' . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'upgrades'); | ||||
|         if (isset($options['ignored_dirs']) && is_array($options['ignored_dirs'])) { | ||||
|             $this->ignoredDirs = $options['ignored_dirs']; | ||||
| @@ -509,19 +525,54 @@ class SafeUpgradeService | ||||
|      * @param string $path | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function ensureWritable(string $path): bool | ||||
|     private function resolveStagingPath(?string $path): ?string | ||||
|     { | ||||
|         if (null === $path || $path === '') { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         $expanded = $path; | ||||
|         if (0 === strpos($expanded, '~')) { | ||||
|             $home = getenv('HOME'); | ||||
|             if ($home) { | ||||
|                 $expanded = rtrim($home, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . ltrim($expanded, '~\/'); | ||||
|             } | ||||
|         } | ||||
|         if (!$this->isAbsolutePath($expanded)) { | ||||
|             $expanded = $this->rootPath . DIRECTORY_SEPARATOR . ltrim($expanded, DIRECTORY_SEPARATOR); | ||||
|         } | ||||
|  | ||||
|         $expanded = $this->normalizePath($expanded); | ||||
|  | ||||
|         try { | ||||
|             Folder::create($path); | ||||
|             Folder::create($expanded); | ||||
|         } catch (\RuntimeException $e) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         return is_writable($expanded) ? $expanded : null; | ||||
|     } | ||||
|  | ||||
|     private function isAbsolutePath(string $path): bool | ||||
|     { | ||||
|         if ($path === '') { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (!is_writable($path)) { | ||||
|             return false; | ||||
|         if ($path[0] === '/' || $path[0] === '\\') { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|         return (bool)preg_match('#^[A-Za-z]:[\\/]#', $path); | ||||
|     } | ||||
|  | ||||
|     private function normalizePath(string $path): string | ||||
|     { | ||||
|         $path = str_replace('\\', '/', $path); | ||||
|         $path = preg_replace('#/+#', '/', $path); | ||||
|         $path = rtrim($path, '/'); | ||||
|  | ||||
|         return str_replace('/', DIRECTORY_SEPARATOR, $path); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -278,7 +278,18 @@ class SelfupgradeCommand extends GpmCommand | ||||
|      */ | ||||
|     protected function createSafeUpgradeService(): SafeUpgradeService | ||||
|     { | ||||
|         return new SafeUpgradeService(); | ||||
|         $config = null; | ||||
|         try { | ||||
|             $config = Grav::instance()['config'] ?? null; | ||||
|         } catch (\Throwable $e) { | ||||
|             $config = null; | ||||
|         } | ||||
|  | ||||
|         $stagingRoot = $config ? $config->get('system.updates.staging_root') : null; | ||||
|  | ||||
|         return new SafeUpgradeService([ | ||||
|             'staging_root' => $stagingRoot, | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -262,7 +262,17 @@ ERR; | ||||
|             $this->updater->install(); | ||||
|  | ||||
|             if ($this->shouldUseSafeUpgrade()) { | ||||
|                 $service = new SafeUpgradeService(); | ||||
|                 $options = []; | ||||
|                 try { | ||||
|                     $grav = Grav::instance(); | ||||
|                     if ($grav && isset($grav['config'])) { | ||||
|                         $options['staging_root'] = $grav['config']->get('system.updates.staging_root'); | ||||
|                     } | ||||
|                 } catch (\Throwable $e) { | ||||
|                     // ignore | ||||
|                 } | ||||
|  | ||||
|                 $service = new SafeUpgradeService($options); | ||||
|                 $service->promote($this->location, $this->getVersion(), $this->ignores); | ||||
|                 Installer::setError(Installer::OK); | ||||
|             } else { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user