diff --git a/core/Command/Upgrade.php b/core/Command/Upgrade.php
index 0451156a3ec06..9fd0f2f8de48a 100644
--- a/core/Command/Upgrade.php
+++ b/core/Command/Upgrade.php
@@ -70,7 +70,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$self = $this;
$updater = Server::get(Updater::class);
- $incompatibleOverwrites = $this->config->getSystemValue('app_install_overwrite', []);
/** @var IEventDispatcher $dispatcher */
$dispatcher = Server::get(IEventDispatcher::class);
@@ -161,7 +160,9 @@ function ($success) use ($output, $self): void {
$updater->listen('\OC\Updater', 'dbUpgrade', function () use ($output): void {
$output->writeln('Updated database');
});
- $updater->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use ($output, &$incompatibleOverwrites): void {
+ $updater->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use ($output): void {
+ // Read per event, the overwrites are cleared during a major upgrade
+ $incompatibleOverwrites = $this->config->getSystemValue('app_install_overwrite', []);
if (!in_array($app, $incompatibleOverwrites)) {
$output->writeln('Disabled incompatible app: ' . $app . '');
}
diff --git a/core/Controller/UpdateController.php b/core/Controller/UpdateController.php
index 14308c12d51f5..a027b30d05377 100644
--- a/core/Controller/UpdateController.php
+++ b/core/Controller/UpdateController.php
@@ -86,7 +86,6 @@ public function update(): DataResponse {
\OC_User::setIncognitoMode(true);
$incompatibleApps = [];
- $incompatibleOverwrites = $this->config->getSystemValue('app_install_overwrite', []);
$this->dispatcher->addListener(
MigratorExecuteSqlEvent::class,
@@ -127,7 +126,9 @@ function (MigratorExecuteSqlEvent $event) use ($eventSource): void {
$this->updater->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($eventSource): void {
$eventSource->send('success', $this->l->t('Updated "%1$s" to %2$s', [$app, $version]));
});
- $this->updater->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use (&$incompatibleApps, &$incompatibleOverwrites): void {
+ $this->updater->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use (&$incompatibleApps): void {
+ // Read per event, the overwrites are cleared during a major upgrade
+ $incompatibleOverwrites = $this->config->getSystemValue('app_install_overwrite', []);
if (!in_array($app, $incompatibleOverwrites)) {
$incompatibleApps[] = $app;
}
diff --git a/lib/private/Updater.php b/lib/private/Updater.php
index b33dfcf8b855d..d6fba6789aa88 100644
--- a/lib/private/Updater.php
+++ b/lib/private/Updater.php
@@ -188,6 +188,16 @@ public function isUpgradePossible(string $oldVersion, string $newVersion, array
|| isset($allowedPreviousVersions[$currentVendor][$oldVersion]);
}
+ /**
+ * Whether the upgrade crosses a major version boundary
+ */
+ private function isMajorUpgrade(string $installedVersion, string $currentVersion): bool {
+ $installedMajor = (int)explode('.', $installedVersion)[0];
+ $currentMajor = (int)explode('.', $currentVersion)[0];
+
+ return $currentMajor > $installedMajor;
+ }
+
/**
* runs the update actions in maintenance mode, does not upgrade the source files
* except the main .htaccess file
@@ -204,6 +214,11 @@ private function doUpgrade(string $currentVersion, string $installedVersion): vo
throw new \Exception('Updates between multiple major versions and downgrades are unsupported.');
}
+ // A force-enable applies to the major version it was granted on
+ if ($this->isMajorUpgrade($installedVersion, $currentVersion)) {
+ $this->config->deleteSystemValue('app_install_overwrite');
+ }
+
// Update .htaccess files
try {
Setup::updateHtaccess();
diff --git a/tests/lib/UpdaterTest.php b/tests/lib/UpdaterTest.php
index 059b4edeb340a..41f88d2c10d40 100644
--- a/tests/lib/UpdaterTest.php
+++ b/tests/lib/UpdaterTest.php
@@ -110,6 +110,25 @@ public function testIsUpgradePossible($oldVersion, $newVersion, $allowedVersions
$this->assertSame($result, $this->updater->isUpgradePossible($oldVersion, $newVersion, $allowedVersions));
}
+ /**
+ * @return array
+ */
+ public static function majorUpgradeTestData(): array {
+ return [
+ // Same major version
+ ['33.0.0.10', '33.1.2.3', false],
+ // Major upgrade
+ ['33.0.5.1', '34.0.0.10', true],
+ // Downgrade, only reachable with debug enabled
+ ['34.0.0.10', '33.0.5.1', false],
+ ];
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('majorUpgradeTestData')]
+ public function testIsMajorUpgrade(string $installedVersion, string $currentVersion, bool $result): void {
+ $this->assertSame($result, self::invokePrivate($this->updater, 'isMajorUpgrade', [$installedVersion, $currentVersion]));
+ }
+
public function testUpgradeAppStoreAppsRestoresMissingAutoDisabledAppBeforeEnabling(): void {
$this->installer->expects($this->once())
->method('isUpdateAvailable')