Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/private/Repair.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ public static function getBeforeUpgradeRepairSteps() {
new Collation(\OC::$server->getConfig(), $connection),
new SqliteAutoincrement($connection),
new SearchLuceneTables(),
new Apps(\OC::$server->getAppManager(), \OC::$server->getEventDispatcher(), \OC::$server->getConfig()),
new Apps(\OC::$server->getAppManager(), \OC::$server->getEventDispatcher(), \OC::$server->getConfig(), new \OC_Defaults()),
];

//There is no need to delete all previews on every single update
Expand Down
155 changes: 100 additions & 55 deletions lib/private/Repair/Apps.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,22 @@ class Apps implements IRepairStep {
/** @var IConfig */
private $config;

/** @var \OC_Defaults */
private $defaults;

/**
* Apps constructor.
*
* @param IAppManager $appManager
* @param EventDispatcher $eventDispatcher
* @param IConfig $config
* @param \OC_Defaults $defaults
*/
public function __construct(IAppManager $appManager, EventDispatcher $eventDispatcher, IConfig $config) {
public function __construct(IAppManager $appManager, EventDispatcher $eventDispatcher, IConfig $config, \OC_Defaults $defaults) {
$this->appManager = $appManager;
$this->eventDispatcher = $eventDispatcher;
$this->config = $config;
$this->defaults = $defaults;
}

/**
Expand All @@ -75,7 +82,7 @@ public function getName() {
*/
private function isCoreUpdate() {
$installedVersion = $this->config->getSystemValue('version', '0.0.0');
$currentVersion = implode('.', \OCP\Util::getVersion());
$currentVersion = implode('.', Util::getVersion());
$versionDiff = version_compare($currentVersion, $installedVersion);
if ($versionDiff > 0) {
return true;
Expand All @@ -90,78 +97,78 @@ private function isCoreUpdate() {
private function requiresMarketEnable() {
$installedVersion = $this->config->getSystemValue('version', '0.0.0');
$versionDiff = version_compare('10.0.0', $installedVersion);
if ($versionDiff >= 0) {
return true;
if ($versionDiff < 0) {
return false;
}
return false;
return true;

}

/**
* @param IOutput $output
* @throws RepairException
*/
public function run(IOutput $output) {
$isCoreUpdate = $this->isCoreUpdate();

if ($this->config->getSystemValue('has_internet_connection', true) !== true) {
$link = $this->defaults->buildDocLinkToKey('admin-marketplace-apps');
$output->info('No internet connection available - no app updates will be taken from the marketplace.');
$output->info("How to update apps in such situation please see $link");
return;
}
$appsToUpgrade = $this->getAppsToUpgrade();
$failedCompatibleApps = [];
$failedMissingApps = $appsToUpgrade[self::KEY_MISSING];
$failedIncompatibleApps = $appsToUpgrade[self::KEY_INCOMPATIBLE];
$hasNotUpdatedCompatibleApps = 0;
$requiresMarketEnable = $this->requiresMarketEnable();

if($isCoreUpdate && $requiresMarketEnable) {
// Then we need to enable the market app to support app updates / downloads during upgrade
$output->info('Enabling market app to assist with update');
// delete old value that might influence old APIs
if ($this->config->getSystemValue('appstoreenabled', null) !== null) {
$this->config->deleteSystemValue('appstoreenabled');
}
$this->appManager->enableApp('market');
}
// fix market app state
$shallContactMarketplace = $this->fixMarketAppState($output);
if ($shallContactMarketplace) {
// Check if we can use the marketplace to update apps as needed?
if ($this->appManager->isEnabledForUser('market')) {
// Use market to fix missing / old apps
$this->loadApp('market');
$output->info('Using market to update existing apps');
try {
// Try to update incompatible apps
if (!empty($appsToUpgrade[self::KEY_INCOMPATIBLE])) {
$output->info('Attempting to update the following existing but incompatible app from market: ' . implode(', ', $appsToUpgrade[self::KEY_INCOMPATIBLE]));
$failedIncompatibleApps = $this->getAppsFromMarket(
$output,
$appsToUpgrade[self::KEY_INCOMPATIBLE],
'upgradeAppStoreApp'
);
}

// Check if we can use the marketplace to update apps as needed?
if($this->appManager->isEnabledForUser('market')) {
// Use market to fix missing / old apps
$this->loadApp('market');
$output->info('Using market to update existing apps');
try {
// Try to update incompatible apps
if(!empty($appsToUpgrade[self::KEY_INCOMPATIBLE])) {
$output->info('Attempting to update the following existing but incompatible app from market: '.implode(', ', $appsToUpgrade[self::KEY_INCOMPATIBLE]));
$failedIncompatibleApps = $this->getAppsFromMarket(
$output,
$appsToUpgrade[self::KEY_INCOMPATIBLE],
'upgradeAppStoreApp'
);
}
// Try to download missing apps
if (!empty($appsToUpgrade[self::KEY_MISSING])) {
$output->info('Attempting to update the following missing apps from market: ' . implode(', ', $appsToUpgrade[self::KEY_MISSING]));
$failedMissingApps = $this->getAppsFromMarket(
$output,
$appsToUpgrade[self::KEY_MISSING],
'reinstallAppStoreApp'
);
}

// Try to download missing apps
if(!empty($appsToUpgrade[self::KEY_MISSING])) {
$output->info('Attempting to update the following missing apps from market: '.implode(', ', $appsToUpgrade[self::KEY_MISSING]));
$failedMissingApps = $this->getAppsFromMarket(
$output,
$appsToUpgrade[self::KEY_MISSING],
'reinstallAppStoreApp'
);
}
// Try to update compatible apps
if (!empty($appsToUpgrade[self::KEY_COMPATIBLE])) {
$output->info('Attempting to update the following existing compatible apps from market: ' . implode(', ', $appsToUpgrade[self::KEY_MISSING]));
$failedCompatibleApps = $this->getAppsFromMarket(
$output,
$appsToUpgrade[self::KEY_COMPATIBLE],
'upgradeAppStoreApp'
);
}

// Try to update compatible apps
if(!empty($appsToUpgrade[self::KEY_COMPATIBLE])) {
$output->info('Attempting to update the following existing compatible apps from market: '.implode(', ', $appsToUpgrade[self::KEY_MISSING]));
$failedCompatibleApps = $this->getAppsFromMarket(
$output,
$appsToUpgrade[self::KEY_COMPATIBLE],
'upgradeAppStoreApp'
);
$hasNotUpdatedCompatibleApps = count($failedCompatibleApps);
} catch (AppManagerException $e) {
$output->warning('No connection to marketplace: ' . $e->getPrevious());
}

$hasNotUpdatedCompatibleApps = count($failedCompatibleApps);
} catch (AppManagerException $e) {
$output->warning('No connection to marketplace: ' . $e->getPrevious());
} else {
// No market available, output error and continue attempt
$output->warning('Market app is unavailable for updating of apps. Enable with: occ app:enable market');
}
} else {
// No market available, output error and continue attempt
$output->warning('Market app is unavailable for updating of apps. Enable with: occ app:enable market');
}

$hasBlockingMissingApps = count($failedMissingApps);
Expand Down Expand Up @@ -190,6 +197,7 @@ public function run(IOutput $output) {
*
* @param IOutput $output
* @param string[] $appList
* @param string $event
* @return array
* @throws AppManagerException
*/
Expand Down Expand Up @@ -274,4 +282,41 @@ function ($appId) {
protected function loadApp($app) {
OC_App::loadApp($app, false);
}

/**
* @return bool
*/
private function isAppStoreEnabled() {
// if appstoreenabled was explicitly disabled we shall not use the market app for upgrade
$appStoreEnabled = $this->config->getSystemValue('appstoreenabled', null);
if ($appStoreEnabled === false) {
return false;
}
return true;
}

private function fixMarketAppState(IOutput $output) {
// no core update -> nothing to do
if (!$this->isCoreUpdate()) {
return false;
}

// no update from a version before 10.0 -> nothing to do, but allow apps to be updated
if (!$this->requiresMarketEnable()) {
return true;
}
// if the appstore was explicitly disabled -> disable market app as well
if (!$this->isAppStoreEnabled()) {
$this->appManager->disableApp('market');
$link = $this->defaults->buildDocLinkToKey('admin-marketplace-apps');
$output->info('Appstore was disabled in past versions and marketplace interactions are disabled for now as well.');
$output->info('If you would like to get automated app updates on upgrade please enable the market app and remove "appstoreenabled" from your config.');
$output->info("Please note that the market app is not recommended for clustered setups - see $link");
return false;
}
// Then we need to enable the market app to support app updates / downloads during upgrade
$output->info('Enabling market app to assist with update');
$this->appManager->enableApp('market');
return true;
}
}
34 changes: 19 additions & 15 deletions tests/lib/Repair/AppsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,53 +19,57 @@
*
*/
namespace Test\Repair;
use OC\Repair\Apps;
use OCP\App\IAppManager;
use OCP\IConfig;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Test\TestCase;

/**
* Tests to check version comparison
*
* @see \OC\Repair\AppsTest
*/
class AppsTest extends \Test\TestCase {
class AppsTest extends TestCase {

/** @var \OC\Repair\AvatarPermissions */
/** @var Apps */
protected $repair;

/** @var IAppManager */
/** @var IAppManager | \PHPUnit_Framework_MockObject_MockObject */
protected $appManager;
/** @var EventDispatcher */
/** @var EventDispatcher | \PHPUnit_Framework_MockObject_MockObject */
protected $eventDispatcher;
/** @var IConfig */
/** @var IConfig | \PHPUnit_Framework_MockObject_MockObject*/
protected $config;
/** @var \OC_Defaults | \PHPUnit_Framework_MockObject_MockObject */
private $defaults;

protected function setUp() {
parent::setUp();

$this->appManager = $this->getMockBuilder(IAppManager::class)->getMock();
$this->eventDispatcher = $this->getMockBuilder(EventDispatcher::class)->getMock();
$this->config = $this->getMockBuilder(IConfig::class)->getMock();
$this->repair = new \OC\Repair\Apps($this->appManager, $this->eventDispatcher, $this->config);
$this->appManager = $this->createMock(IAppManager::class);
$this->defaults = $this->createMock(\OC_Defaults::class);
$this->eventDispatcher = $this->createMock(EventDispatcher::class);
$this->config = $this->createMock(IConfig::class);
$this->repair = new Apps($this->appManager, $this->eventDispatcher, $this->config, $this->defaults);
}

public function testMarketEnableVersionCompare10() {
$this->config->expects($this->once())->method('getSystemValue')->with('version', '0.0.0')->will($this->returnValue('10.0.0'));
$this->config->expects($this->once())->method('getSystemValue')->with('version', '0.0.0')->willReturn('10.0.0');
$this->assertTrue($this->invokePrivate($this->repair, 'requiresMarketEnable'));
}

public function testMarketEnableVersionCompare9() {
$this->config->expects($this->once())->method('getSystemValue')->with('version', '0.0.0')->will($this->returnValue('9.1.5'));
$this->config->expects($this->once())->method('getSystemValue')->with('version', '0.0.0')->willReturn('9.1.5');
$this->assertTrue($this->invokePrivate($this->repair, 'requiresMarketEnable'));
}

public function testMarketEnableVersionCompareFuture() {
$this->config->expects($this->once())->method('getSystemValue')->with('version', '0.0.0')->will($this->returnValue('10.0.2'));
$this->config->expects($this->once())->method('getSystemValue')->with('version', '0.0.0')->willReturn('10.0.2');
$this->assertFalse($this->invokePrivate($this->repair, 'requiresMarketEnable'));
}

public function testMarketEnableVersionCompareCurrent() {
$this->config->expects($this->once())->method('getSystemValue')->with('version', '0.0.0')->will($this->returnValue('10.0.1'));
$this->config->expects($this->once())->method('getSystemValue')->with('version', '0.0.0')->willReturn('10.0.1');
$this->assertFalse($this->invokePrivate($this->repair, 'requiresMarketEnable'));
}
}
}