From e6de9db77e9a72dc246c451a2b3323498b145efb Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Thu, 16 Oct 2025 23:17:34 -0600 Subject: [PATCH] preserver root files --- .../Common/Upgrade/SafeUpgradeService.php | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/system/src/Grav/Common/Upgrade/SafeUpgradeService.php b/system/src/Grav/Common/Upgrade/SafeUpgradeService.php index 8f9098763..a9eface4a 100644 --- a/system/src/Grav/Common/Upgrade/SafeUpgradeService.php +++ b/system/src/Grav/Common/Upgrade/SafeUpgradeService.php @@ -171,6 +171,7 @@ class SafeUpgradeService // Ensure ignored directories are replaced with live copies. $this->hydrateIgnoredDirectories($packagePath, $ignores); + $this->carryOverRootFiles($packagePath, $ignores); $manifest = $this->buildManifest($stageId, $targetVersion, $packagePath, $backupPath); $manifestPath = $stagePath . DIRECTORY_SEPARATOR . 'manifest.json'; @@ -422,6 +423,57 @@ class SafeUpgradeService } } + /** + * Carry over non-dot root files that are absent from the staged package. + * + * @param string $packagePath + * @param array $ignores + * @return void + */ + private function carryOverRootFiles(string $packagePath, array $ignores): void + { + $strategic = $ignores ?: $this->ignoredDirs; + $skip = array_map(static function ($value) { + return trim((string)$value, '/'); + }, $strategic); + $skip = array_filter($skip, static function ($value) { + return $value !== ''; + }); + $skip = array_values(array_unique($skip)); + + $iterator = new DirectoryIterator($this->rootPath); + foreach ($iterator as $entry) { + if ($entry->isDot()) { + continue; + } + + $name = $entry->getFilename(); + if ($name === '' || $name[0] === '.') { + continue; + } + + if (in_array($name, $skip, true)) { + continue; + } + + $target = $packagePath . DIRECTORY_SEPARATOR . $name; + if (file_exists($target)) { + continue; + } + + $source = $entry->getPathname(); + Folder::create(dirname($target)); + + if ($entry->isDir() && !$entry->isLink()) { + Folder::rcopy($source, $target, true); + } elseif ($entry->isFile()) { + copy($source, $target); + } elseif ($entry->isLink()) { + @symlink(readlink($source), $target); + } + } + } + /** * Build manifest metadata for a staged upgrade. *