mirror of
https://github.com/getgrav/grav.git
synced 2026-03-01 10:01:26 +01:00
@@ -20,8 +20,8 @@ use InvalidArgumentException;
|
||||
class GravCore extends AbstractPackageCollection
|
||||
{
|
||||
/** @var string */
|
||||
protected $repository = 'https://getgrav.org/downloads/grav.json';
|
||||
|
||||
// protected $repository = 'https://getgrav.org/downloads/grav.json';
|
||||
protected $repository = 'http://localhost:8043/grav.json';
|
||||
/** @var array */
|
||||
private $data;
|
||||
/** @var string */
|
||||
|
||||
@@ -65,6 +65,17 @@ use const JSON_PRETTY_PRINT;
|
||||
*/
|
||||
class SafeUpgradeService
|
||||
{
|
||||
/**
|
||||
* Version identifier for this SafeUpgradeService implementation.
|
||||
* This is used to verify that the correct version is loaded during upgrades.
|
||||
*
|
||||
* IMPORTANT: Increment this with each release that changes SafeUpgradeService.
|
||||
* Format: YYYYMMDD (release date)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const IMPLEMENTATION_VERSION = '20251106'; // 2025-11-06 - Added preflight to Install.php
|
||||
|
||||
/** @var string */
|
||||
private $rootPath;
|
||||
/** @var string */
|
||||
|
||||
@@ -15,8 +15,9 @@ use Grav\Common\HTTP\Response;
|
||||
use Grav\Common\GPM\Installer;
|
||||
use Grav\Common\GPM\Upgrader;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Upgrade\SafeUpgradeService;
|
||||
use Grav\Console\GpmCommand;
|
||||
// NOTE: SafeUpgradeService removed - no longer used in this file
|
||||
// Preflight is now handled in Install.php after downloading the package
|
||||
use Grav\Installer\Install;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
@@ -162,11 +163,9 @@ class SelfupgradeCommand extends GpmCommand
|
||||
|
||||
$this->displayGPMRelease();
|
||||
|
||||
$safeUpgrade = $this->createSafeUpgradeService();
|
||||
$preflight = $safeUpgrade->preflight();
|
||||
if (!$this->handlePreflightReport($preflight)) {
|
||||
return 1;
|
||||
}
|
||||
// NOTE: Preflight checks are now run in Install.php AFTER downloading the package.
|
||||
// This ensures we use the NEW SafeUpgradeService from the package, not the old one.
|
||||
// Running preflight here would load the OLD class into memory and prevent the new one from loading.
|
||||
|
||||
$update = $this->upgrader->getAssets()['grav-update'];
|
||||
|
||||
@@ -333,7 +332,8 @@ class SelfupgradeCommand extends GpmCommand
|
||||
}
|
||||
|
||||
$io->newLine();
|
||||
$safeUpgrade->clearRecoveryFlag();
|
||||
// Clear recovery flag - upgrade completed successfully
|
||||
$recovery->clearUpgradeWindow('core-upgrade');
|
||||
}
|
||||
|
||||
if ($this->tmp && is_dir($this->tmp)) {
|
||||
@@ -375,23 +375,6 @@ class SelfupgradeCommand extends GpmCommand
|
||||
return $this->tmp . DS . $package['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SafeUpgradeService
|
||||
*/
|
||||
protected function createSafeUpgradeService(): SafeUpgradeService
|
||||
{
|
||||
$config = null;
|
||||
try {
|
||||
$config = Grav::instance()['config'] ?? null;
|
||||
} catch (\Throwable $e) {
|
||||
$config = null;
|
||||
}
|
||||
|
||||
return new SafeUpgradeService([
|
||||
'config' => $config,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $preflight
|
||||
* @return bool
|
||||
|
||||
@@ -15,8 +15,10 @@ use Grav\Common\Cache;
|
||||
use Grav\Common\GPM\Installer;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Plugins;
|
||||
use Grav\Common\Upgrade\SafeUpgradeService;
|
||||
use RuntimeException;
|
||||
// NOTE: SafeUpgradeService is NOT imported via 'use' to avoid autoloader loading OLD class
|
||||
// When Install.php is included during upgrade, the autoloader is from the OLD installation
|
||||
// and would load the OLD SafeUpgradeService. We manually require the NEW one when needed.
|
||||
use function class_exists;
|
||||
use function dirname;
|
||||
use function function_exists;
|
||||
@@ -296,6 +298,40 @@ ERR;
|
||||
$this->updater->install();
|
||||
|
||||
if ($this->shouldUseSafeUpgrade()) {
|
||||
// CRITICAL: Check if SafeUpgradeService is already loaded from old installation
|
||||
// If it is, PHP will use the OLD version instead of the NEW one from this package
|
||||
$expectedPath = $this->location . '/system/src/Grav/Common/Upgrade/SafeUpgradeService.php';
|
||||
|
||||
if (class_exists('Grav\\Common\\Upgrade\\SafeUpgradeService', false)) {
|
||||
// Class is already loaded - check if it's from the correct location
|
||||
$reflection = new \ReflectionClass('Grav\\Common\\Upgrade\\SafeUpgradeService');
|
||||
$loadedFrom = $reflection->getFileName();
|
||||
|
||||
$loadedFromReal = realpath($loadedFrom) ?: $loadedFrom;
|
||||
$expectedReal = realpath($expectedPath) ?: $expectedPath;
|
||||
|
||||
if ($loadedFromReal !== $expectedReal) {
|
||||
// OLD SafeUpgradeService is already loaded - we cannot proceed safely
|
||||
throw new RuntimeException(
|
||||
sprintf(
|
||||
"CRITICAL: SafeUpgradeService was already loaded from the old installation.\n" .
|
||||
"This prevents the new version from being used and will cause bugs to persist.\n\n" .
|
||||
"Loaded from: %s\n" .
|
||||
"Expected: %s\n\n" .
|
||||
"WORKAROUND: This is a known issue in Grav < 1.7.50.9.\n" .
|
||||
"The old CLI/Admin code loads SafeUpgradeService before downloading the package.\n" .
|
||||
"Once you upgrade to 1.7.50.9+, this issue will be fixed.\n\n" .
|
||||
"For now, you have two options:\n" .
|
||||
"1. Use manual upgrade: Download zip, extract, copy files\n" .
|
||||
"2. Wait for automatic fix in next release",
|
||||
$loadedFromReal,
|
||||
$expectedReal
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// SafeUpgradeService was loaded in shouldUseSafeUpgrade() from the NEW package
|
||||
$options = [];
|
||||
try {
|
||||
$grav = Grav::instance();
|
||||
@@ -306,7 +342,63 @@ ERR;
|
||||
// ignore
|
||||
}
|
||||
|
||||
$service = new SafeUpgradeService($options);
|
||||
// Use fully qualified class name (no 'use' statement to avoid autoloader)
|
||||
$service = new \Grav\Common\Upgrade\SafeUpgradeService($options);
|
||||
|
||||
// CRITICAL: Verify we're using the NEW SafeUpgradeService from this package, not the old one
|
||||
$this->verifySafeUpgradeServiceVersion($service);
|
||||
|
||||
// Run preflight checks using the NEW SafeUpgradeService
|
||||
// This ensures preflight logic is from the package being installed, not the old installation
|
||||
try {
|
||||
$preflight = $service->preflight();
|
||||
|
||||
// Check for pending plugin/theme updates
|
||||
if (!empty($preflight['plugins_pending'])) {
|
||||
$pending = $preflight['plugins_pending'];
|
||||
$list = [];
|
||||
foreach ($pending as $slug => $info) {
|
||||
$current = $info['current'] ?? 'unknown';
|
||||
$available = $info['available'] ?? 'unknown';
|
||||
$list[] = sprintf('%s (v%s → v%s)', $slug, $current, $available);
|
||||
}
|
||||
|
||||
throw new RuntimeException(
|
||||
'Please update the following plugins/themes before upgrading Grav: ' .
|
||||
implode(', ', $list) . '. ' .
|
||||
'Run "bin/gpm update" or update via Admin panel first.'
|
||||
);
|
||||
}
|
||||
|
||||
// PSR log conflicts - log warning but don't block for now
|
||||
if (!empty($preflight['psr_log_conflicts'])) {
|
||||
$conflicts = $preflight['psr_log_conflicts'];
|
||||
error_log(
|
||||
'WARNING: PSR/log conflicts detected in plugins: ' .
|
||||
implode(', ', array_keys($conflicts)) .
|
||||
'. These may need updating after Grav upgrade.'
|
||||
);
|
||||
}
|
||||
|
||||
// Monolog conflicts - log warning but don't block for now
|
||||
if (!empty($preflight['monolog_conflicts'])) {
|
||||
$conflicts = $preflight['monolog_conflicts'];
|
||||
error_log(
|
||||
'WARNING: Monolog API conflicts detected in plugins: ' .
|
||||
implode(', ', array_keys($conflicts)) .
|
||||
'. These may need updating after Grav upgrade.'
|
||||
);
|
||||
}
|
||||
} catch (RuntimeException $e) {
|
||||
// Preflight failed - abort upgrade with clear message
|
||||
throw new RuntimeException(
|
||||
'Upgrade preflight checks failed: ' . $e->getMessage(),
|
||||
0,
|
||||
$e
|
||||
);
|
||||
}
|
||||
|
||||
// Preflight passed - proceed with upgrade
|
||||
if ($this->progressCallback) {
|
||||
$service->setProgressCallback(function (string $stage, string $message, ?int $percent = null, array $extra = []) {
|
||||
$this->relayProgress($stage, $message, $percent);
|
||||
@@ -345,8 +437,16 @@ ERR;
|
||||
*/
|
||||
private function shouldUseSafeUpgrade(): bool
|
||||
{
|
||||
if (!class_exists(SafeUpgradeService::class)) {
|
||||
return false;
|
||||
// CRITICAL: Check if class exists WITHOUT triggering autoloader
|
||||
// If not loaded yet, manually load the NEW one from this package
|
||||
if (!class_exists('Grav\\Common\\Upgrade\\SafeUpgradeService', false)) {
|
||||
// Class not loaded yet - try to load from NEW package
|
||||
$serviceFile = $this->location . '/system/src/Grav/Common/Upgrade/SafeUpgradeService.php';
|
||||
if (!file_exists($serviceFile)) {
|
||||
return false; // SafeUpgradeService not available in this package
|
||||
}
|
||||
// Load the NEW SafeUpgradeService from this package
|
||||
require_once $serviceFile;
|
||||
}
|
||||
|
||||
if (null !== self::$forceSafeUpgrade) {
|
||||
@@ -479,6 +579,65 @@ ERR;
|
||||
return $matches[1] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that we're using the NEW SafeUpgradeService from this package,
|
||||
* not the OLD one from the current installation.
|
||||
*
|
||||
* This is CRITICAL to ensure bugfixes in SafeUpgradeService are actually used.
|
||||
*
|
||||
* @param object $service The SafeUpgradeService instance (no type hint to avoid early loading)
|
||||
* @return void
|
||||
* @throws RuntimeException if the wrong version is loaded
|
||||
*/
|
||||
protected function verifySafeUpgradeServiceVersion(object $service): void
|
||||
{
|
||||
// Get the file path where SafeUpgradeService was loaded from
|
||||
$reflection = new \ReflectionClass($service);
|
||||
$loadedFrom = $reflection->getFileName();
|
||||
|
||||
// Expected: should be from THIS package in $this->location
|
||||
// e.g., /tmp/grav-update-abc123/grav-update/system/src/Grav/Common/Upgrade/SafeUpgradeService.php
|
||||
$expectedPath = $this->location . '/system/src/Grav/Common/Upgrade/SafeUpgradeService.php';
|
||||
|
||||
// Normalize paths for comparison
|
||||
$loadedFromReal = realpath($loadedFrom) ?: $loadedFrom;
|
||||
$expectedReal = realpath($expectedPath) ?: $expectedPath;
|
||||
|
||||
if ($loadedFromReal !== $expectedReal) {
|
||||
// CRITICAL ERROR: We're using the OLD SafeUpgradeService!
|
||||
// This means bugfixes in the new version won't be applied.
|
||||
error_log(sprintf(
|
||||
'CRITICAL: SafeUpgradeService loaded from WRONG location!' . "\n" .
|
||||
' Expected (NEW): %s' . "\n" .
|
||||
' Actual (OLD): %s' . "\n" .
|
||||
'This indicates a class loading issue that will prevent bugfixes from being applied.',
|
||||
$expectedReal,
|
||||
$loadedFromReal
|
||||
));
|
||||
|
||||
throw new RuntimeException(
|
||||
'CRITICAL: SafeUpgradeService was loaded from the old installation instead of the new package. ' .
|
||||
'This is a known issue that has been fixed. Please upgrade using CLI: bin/gpm self-upgrade -f'
|
||||
);
|
||||
}
|
||||
|
||||
// Additional check: verify IMPLEMENTATION_VERSION constant exists
|
||||
// (Added in this fix - old versions won't have it)
|
||||
if (!defined('Grav\\Common\\Upgrade\\SafeUpgradeService::IMPLEMENTATION_VERSION')) {
|
||||
error_log(
|
||||
'WARNING: SafeUpgradeService::IMPLEMENTATION_VERSION not defined. ' .
|
||||
'This suggests an old version is loaded.'
|
||||
);
|
||||
} else {
|
||||
$version = constant('Grav\\Common\\Upgrade\\SafeUpgradeService::IMPLEMENTATION_VERSION');
|
||||
error_log(sprintf(
|
||||
'SafeUpgradeService verification PASSED. Using version %s from: %s',
|
||||
$version,
|
||||
$loadedFromReal
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
protected function legacySupport(): void
|
||||
{
|
||||
// Support install for Grav 1.6.0 - 1.6.20 by loading the original class from the older version of Grav.
|
||||
|
||||
Reference in New Issue
Block a user