2013-10-24 13:59:39 -04:00
< ? php
/**
2016-07-30 10:26:21 -04:00
* @ copyright Copyright ( c ) 2016 , ownCloud , Inc .
2016-07-21 11:07:57 -04:00
*
2015-03-26 06:44:34 -04:00
* @ author Andreas Fischer < bantu @ owncloud . com >
2020-03-31 04:49:10 -04:00
* @ author Christoph Wurst < christoph @ winzerhof - wurst . at >
2016-07-21 11:07:57 -04:00
* @ author Joas Schilling < coding @ schilljs . com >
2016-05-26 13:56:05 -04:00
* @ author Lukas Reschke < lukas @ statuscode . ch >
2015-03-26 06:44:34 -04:00
* @ author Morris Jobke < hey @ morrisjobke . de >
2020-12-16 08:54:15 -05:00
* @ author Nils Wittenbrink < nilswittenbrink @ web . de >
2015-03-26 06:44:34 -04:00
* @ author Owen Winkler < a_github @ midnightcircus . com >
2017-11-06 09:56:42 -05:00
* @ author Robin Appelman < robin @ icewind . nl >
* @ author Sander Ruitenbeek < sander @ grids . be >
2015-03-26 06:44:34 -04:00
* @ author Thomas Müller < thomas . mueller @ tmit . eu >
2016-07-21 11:07:57 -04:00
* @ author Thomas Pulzer < t . pulzer @ kniel . de >
2021-06-04 15:52:51 -04:00
* @ author Valdnet < 47037905 + Valdnet @ users . noreply . github . com >
2020-12-16 08:54:15 -05:00
* @ author Vincent Petry < vincent @ nextcloud . com >
2015-03-26 06:44:34 -04:00
*
* @ license AGPL - 3.0
*
* This code is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License , version 3 ,
* as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License , version 3 ,
2019-12-03 13:57:53 -05:00
* along with this program . If not , see < http :// www . gnu . org / licenses />
2015-03-26 06:44:34 -04:00
*
2013-10-24 13:59:39 -04:00
*/
namespace OC\Core\Command ;
2015-06-23 11:07:28 -04:00
use OC\Console\TimestampFormatter ;
2022-08-22 10:56:01 -04:00
use OC\DB\MigratorExecuteSqlEvent ;
2022-08-22 11:59:26 -04:00
use OC\Repair\Events\RepairAdvanceEvent ;
use OC\Repair\Events\RepairErrorEvent ;
use OC\Repair\Events\RepairFinishEvent ;
use OC\Repair\Events\RepairInfoEvent ;
use OC\Repair\Events\RepairStartEvent ;
use OC\Repair\Events\RepairStepEvent ;
use OC\Repair\Events\RepairWarningEvent ;
2013-10-28 10:15:56 -04:00
use OC\Updater ;
2023-11-23 04:22:34 -05:00
use OCP\EventDispatcher\Event ;
use OCP\EventDispatcher\IEventDispatcher ;
use OCP\IConfig ;
use OCP\Util ;
2013-10-24 13:59:39 -04:00
use Symfony\Component\Console\Command\Command ;
2016-03-30 17:38:26 -04:00
use Symfony\Component\Console\Helper\ProgressBar ;
2013-10-24 13:59:39 -04:00
use Symfony\Component\Console\Input\InputInterface ;
use Symfony\Component\Console\Output\OutputInterface ;
class Upgrade extends Command {
2020-04-10 10:54:27 -04:00
public const ERROR_SUCCESS = 0 ;
public const ERROR_NOT_INSTALLED = 1 ;
public const ERROR_MAINTENANCE_MODE = 2 ;
public const ERROR_UP_TO_DATE = 0 ;
public const ERROR_INVALID_ARGUMENTS = 4 ;
public const ERROR_FAILURE = 5 ;
2014-09-22 05:59:13 -04:00
2023-06-12 11:50:14 -04:00
public function __construct (
2024-01-11 11:32:58 -05:00
private IConfig $config
2023-06-12 11:50:14 -04:00
) {
2014-11-03 07:53:59 -05:00
parent :: __construct ();
2014-09-22 06:04:48 -04:00
}
2013-10-24 13:59:39 -04:00
protected function configure () {
$this
-> setName ( 'upgrade' )
2018-01-19 08:00:27 -05:00
-> setDescription ( 'run upgrade routines after installation of a new release. The release has to be installed before.' );
2013-10-24 13:59:39 -04:00
}
2014-03-14 05:48:07 -04:00
/**
* Execute the upgrade command
*
* @ param InputInterface $input input interface
* @ param OutputInterface $output output interface
*/
2020-06-26 08:54:51 -04:00
protected function execute ( InputInterface $input , OutputInterface $output ) : int {
2020-04-10 08:19:56 -04:00
if ( Util :: needUpgrade ()) {
2015-06-23 11:07:28 -04:00
if ( OutputInterface :: VERBOSITY_NORMAL < $output -> getVerbosity ()) {
// Prepend each line with a little timestamp
$timestampFormatter = new TimestampFormatter ( $this -> config , $output -> getFormatter ());
$output -> setFormatter ( $timestampFormatter );
}
2014-09-22 05:59:13 -04:00
$self = $this ;
2024-01-11 11:32:58 -05:00
$updater = \OCP\Server :: get ( Updater :: class );
2021-11-30 10:45:51 -05:00
$incompatibleOverwrites = $this -> config -> getSystemValue ( 'app_install_overwrite' , []);
2013-10-28 10:15:56 -04:00
2022-08-22 11:59:26 -04:00
/** @var IEventDispatcher $dispatcher */
$dispatcher = \OC :: $server -> get ( IEventDispatcher :: class );
2016-03-30 17:38:26 -04:00
$progress = new ProgressBar ( $output );
2016-04-04 10:20:53 -04:00
$progress -> setFormat ( " %message% \n %current%/%max% [%bar%] %percent:3s%% " );
2022-08-23 08:57:00 -04:00
$listener = function ( MigratorExecuteSqlEvent $event ) use ( $progress , $output ) : void {
2022-08-22 10:56:01 -04:00
$message = $event -> getSql ();
if ( OutputInterface :: VERBOSITY_NORMAL < $output -> getVerbosity ()) {
$output -> writeln ( ' Executing SQL ' . $message );
} else {
if ( strlen ( $message ) > 60 ) {
$message = substr ( $message , 0 , 57 ) . '...' ;
}
$progress -> setMessage ( $message );
if ( $event -> getCurrentStep () === 1 ) {
$output -> writeln ( '' );
$progress -> start ( $event -> getMaxStep ());
}
$progress -> setProgress ( $event -> getCurrentStep ());
if ( $event -> getCurrentStep () === $event -> getMaxStep ()) {
$progress -> setMessage ( 'Done' );
$progress -> finish ();
$output -> writeln ( '' );
2016-03-30 17:38:26 -04:00
}
}
2016-04-04 10:20:53 -04:00
};
2022-08-23 08:57:00 -04:00
$repairListener = function ( Event $event ) use ( $progress , $output ) : void {
2022-08-22 11:59:26 -04:00
if ( $event instanceof RepairStartEvent ) {
$progress -> setMessage ( 'Starting ...' );
$output -> writeln ( $event -> getCurrentStepName ());
$output -> writeln ( '' );
$progress -> start ( $event -> getMaxStep ());
} elseif ( $event instanceof RepairAdvanceEvent ) {
$desc = $event -> getDescription ();
if ( ! empty ( $desc )) {
$progress -> setMessage ( $desc );
}
2022-08-25 10:26:31 -04:00
$progress -> advance ( $event -> getIncrement ());
2022-08-22 11:59:26 -04:00
} elseif ( $event instanceof RepairFinishEvent ) {
$progress -> setMessage ( 'Done' );
$progress -> finish ();
$output -> writeln ( '' );
} elseif ( $event instanceof RepairStepEvent ) {
if ( OutputInterface :: VERBOSITY_NORMAL < $output -> getVerbosity ()) {
$output -> writeln ( '<info>Repair step: ' . $event -> getStepName () . '</info>' );
}
} elseif ( $event instanceof RepairInfoEvent ) {
if ( OutputInterface :: VERBOSITY_NORMAL < $output -> getVerbosity ()) {
$output -> writeln ( '<info>Repair info: ' . $event -> getMessage () . '</info>' );
}
} elseif ( $event instanceof RepairWarningEvent ) {
$output -> writeln ( '<error>Repair warning: ' . $event -> getMessage () . '</error>' );
} elseif ( $event instanceof RepairErrorEvent ) {
$output -> writeln ( '<error>Repair error: ' . $event -> getMessage () . '</error>' );
2016-04-27 07:19:00 -04:00
}
};
2022-08-22 11:59:26 -04:00
$dispatcher -> addListener ( MigratorExecuteSqlEvent :: class , $listener );
$dispatcher -> addListener ( RepairStartEvent :: class , $repairListener );
$dispatcher -> addListener ( RepairAdvanceEvent :: class , $repairListener );
$dispatcher -> addListener ( RepairFinishEvent :: class , $repairListener );
$dispatcher -> addListener ( RepairStepEvent :: class , $repairListener );
$dispatcher -> addListener ( RepairInfoEvent :: class , $repairListener );
$dispatcher -> addListener ( RepairWarningEvent :: class , $repairListener );
$dispatcher -> addListener ( RepairErrorEvent :: class , $repairListener );
2019-02-06 11:08:41 -05:00
2014-06-05 10:19:24 -04:00
2020-04-09 07:53:40 -04:00
$updater -> listen ( '\OC\Updater' , 'maintenanceEnabled' , function () use ( $output ) {
2013-10-28 17:26:44 -04:00
$output -> writeln ( '<info>Turned on maintenance mode</info>' );
2013-10-28 10:15:56 -04:00
});
2020-04-09 07:53:40 -04:00
$updater -> listen ( '\OC\Updater' , 'maintenanceDisabled' , function () use ( $output ) {
2015-05-19 04:27:53 -04:00
$output -> writeln ( '<info>Turned off maintenance mode</info>' );
});
2020-04-09 07:53:40 -04:00
$updater -> listen ( '\OC\Updater' , 'maintenanceActive' , function () use ( $output ) {
2015-05-19 04:27:53 -04:00
$output -> writeln ( '<info>Maintenance mode is kept active</info>' );
});
$updater -> listen ( '\OC\Updater' , 'updateEnd' ,
2020-04-09 07:53:40 -04:00
function ( $success ) use ( $output , $self ) {
2015-06-23 11:07:28 -04:00
if ( $success ) {
2016-09-21 09:12:00 -04:00
$message = " <info>Update successful</info> " ;
2015-06-23 11:07:28 -04:00
} else {
2016-09-21 09:12:00 -04:00
$message = " <error>Update failed</error> " ;
2015-06-23 11:07:28 -04:00
}
2014-09-22 05:59:13 -04:00
$output -> writeln ( $message );
});
2020-04-09 07:53:40 -04:00
$updater -> listen ( '\OC\Updater' , 'dbUpgradeBefore' , function () use ( $output ) {
2015-10-21 03:17:38 -04:00
$output -> writeln ( '<info>Updating database schema</info>' );
});
2020-04-09 07:53:40 -04:00
$updater -> listen ( '\OC\Updater' , 'dbUpgrade' , function () use ( $output ) {
2013-10-28 17:26:44 -04:00
$output -> writeln ( '<info>Updated database</info>' );
2013-10-28 10:15:56 -04:00
});
2021-11-30 10:45:51 -05:00
$updater -> listen ( '\OC\Updater' , 'incompatibleAppDisabled' , function ( $app ) use ( $output , & $incompatibleOverwrites ) {
if ( ! in_array ( $app , $incompatibleOverwrites )) {
$output -> writeln ( '<comment>Disabled incompatible app: ' . $app . '</comment>' );
}
2015-02-17 06:00:39 -05:00
});
2020-04-09 07:53:40 -04:00
$updater -> listen ( '\OC\Updater' , 'upgradeAppStoreApp' , function ( $app ) use ( $output ) {
2021-05-21 09:45:54 -04:00
$output -> writeln ( '<info>Update app ' . $app . ' from App Store</info>' );
2017-05-11 14:35:17 -04:00
});
2015-10-21 03:17:38 -04:00
$updater -> listen ( '\OC\Updater' , 'appSimulateUpdate' , function ( $app ) use ( $output ) {
$output -> writeln ( " <info>Checking whether the database schema for < $app > can be updated (this can take a long time depending on the database size)</info> " );
});
2015-06-23 04:43:45 -04:00
$updater -> listen ( '\OC\Updater' , 'appUpgradeStarted' , function ( $app , $version ) use ( $output ) {
$output -> writeln ( " <info>Updating < $app > ...</info> " );
});
2015-02-24 06:52:16 -05:00
$updater -> listen ( '\OC\Updater' , 'appUpgrade' , function ( $app , $version ) use ( $output ) {
$output -> writeln ( " <info>Updated < $app > to $version </info> " );
});
2020-04-09 07:53:40 -04:00
$updater -> listen ( '\OC\Updater' , 'failure' , function ( $message ) use ( $output , $self ) {
2014-09-22 05:59:13 -04:00
$output -> writeln ( " <error> $message </error> " );
2013-10-28 10:15:56 -04:00
});
2020-04-09 07:53:40 -04:00
$updater -> listen ( '\OC\Updater' , 'setDebugLogLevel' , function ( $logLevel , $logLevelName ) use ( $output ) {
2020-09-10 09:17:59 -04:00
$output -> writeln ( " <info>Setting log level to debug</info> " );
2015-09-29 08:35:32 -04:00
});
2020-04-09 07:53:40 -04:00
$updater -> listen ( '\OC\Updater' , 'resetLogLevel' , function ( $logLevel , $logLevelName ) use ( $output ) {
2020-09-10 09:17:59 -04:00
$output -> writeln ( " <info>Resetting log level</info> " );
2015-09-29 08:35:32 -04:00
});
2020-04-09 07:53:40 -04:00
$updater -> listen ( '\OC\Updater' , 'startCheckCodeIntegrity' , function () use ( $output ) {
2016-01-22 07:18:00 -05:00
$output -> writeln ( " <info>Starting code integrity check...</info> " );
});
2020-04-09 07:53:40 -04:00
$updater -> listen ( '\OC\Updater' , 'finishedCheckCodeIntegrity' , function () use ( $output ) {
2016-01-22 07:18:00 -05:00
$output -> writeln ( " <info>Finished code integrity check</info> " );
});
2013-10-28 10:15:56 -04:00
2015-06-23 04:03:27 -04:00
$success = $updater -> upgrade ();
2014-03-14 05:48:07 -04:00
$this -> postUpgradeCheck ( $input , $output );
2020-04-10 08:19:56 -04:00
if ( ! $success ) {
2015-06-23 04:03:27 -04:00
return self :: ERROR_FAILURE ;
}
2013-10-28 16:50:28 -04:00
return self :: ERROR_SUCCESS ;
2020-04-10 08:19:56 -04:00
} elseif ( $this -> config -> getSystemValueBool ( 'maintenance' )) {
2016-07-30 09:39:32 -04:00
//Possible scenario: Nextcloud core is updated but an app failed
2021-01-15 10:38:25 -05:00
$output -> writeln ( '<comment>Nextcloud is in maintenance mode</comment>' );
2013-10-28 17:26:44 -04:00
$output -> write ( '<comment>Maybe an upgrade is already in process. Please check the '
2016-07-04 05:50:32 -04:00
. 'logfile (data/nextcloud.log). If you want to re-run the '
2013-10-28 17:19:15 -04:00
. 'upgrade procedure, remove the "maintenance mode" from '
2022-01-12 14:44:38 -05:00
. 'config.php and call this script again.</comment>' , true );
2013-10-28 17:19:15 -04:00
return self :: ERROR_MAINTENANCE_MODE ;
2013-10-28 10:15:56 -04:00
} else {
2016-07-30 09:39:32 -04:00
$output -> writeln ( '<info>Nextcloud is already latest version</info>' );
2013-10-28 17:19:15 -04:00
return self :: ERROR_UP_TO_DATE ;
2013-10-28 10:15:56 -04:00
}
2013-10-24 13:59:39 -04:00
}
2014-03-14 05:48:07 -04:00
/**
* Perform a post upgrade check ( specific to the command line tool )
*
* @ param InputInterface $input input interface
* @ param OutputInterface $output output interface
*/
protected function postUpgradeCheck ( InputInterface $input , OutputInterface $output ) {
2020-03-26 04:30:18 -04:00
$trustedDomains = $this -> config -> getSystemValue ( 'trusted_domains' , []);
2014-03-14 05:48:07 -04:00
if ( empty ( $trustedDomains )) {
$output -> write (
'<warning>The setting "trusted_domains" could not be ' .
'set automatically by the upgrade script, ' .
'please set it manually</warning>'
);
}
}
2013-10-24 13:59:39 -04:00
}