193 lines
7.3 KiB
PHP
193 lines
7.3 KiB
PHP
<?php
|
|
|
|
namespace Leantime\Command;
|
|
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Contracts\Container\BindingResolutionException;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Leantime\Core\Configuration\AppSettings;
|
|
use Leantime\Domain\Plugins\Services\Plugins;
|
|
use Symfony\Component\Console\Attribute\AsCommand;
|
|
use Symfony\Component\Console\Input\ArrayInput;
|
|
use Symfony\Component\Console\Input\InputInterface;
|
|
use Symfony\Component\Console\Input\InputOption;
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
|
|
|
/**
|
|
* Class UpdateLeantime
|
|
*/
|
|
#[AsCommand(
|
|
name: 'system:update',
|
|
description: 'Updates Leantime to the latest version from Github',
|
|
)]
|
|
class UpdateLeantime extends Command
|
|
{
|
|
protected function configure(): void
|
|
{
|
|
parent::configure();
|
|
$this->addOption(name: 'skipDbBackup', mode: InputOption::VALUE_NONE);
|
|
}
|
|
|
|
/**
|
|
* Executes the update process.
|
|
*
|
|
* @param InputInterface $input The input interface object.
|
|
* @param OutputInterface $output The output interface object.
|
|
* @return int 0 if everything went fine, or an exit code.
|
|
*
|
|
* @throws BindingResolutionException|\Throwable
|
|
*/
|
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
|
{
|
|
$appSettings = app()->make(AppSettings::class);
|
|
|
|
$io = new SymfonyStyle($input, $output);
|
|
|
|
$currentVersion = $appSettings->appVersion;
|
|
$io->text('Starting the updater');
|
|
|
|
// Check Versions + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */
|
|
$io->section('Version Check');
|
|
$io->text('Your current version is: v'.$currentVersion);
|
|
$url = 'https://github.com/leantime/leantime/releases/latest';
|
|
$io->text('Checking latest version on Github...');
|
|
|
|
// Create stream context to follow redirects
|
|
$context = stream_context_create(
|
|
[
|
|
'http' => [
|
|
'method' => 'GET',
|
|
'header' => 'Accept: application/json',
|
|
'follow_location' => true,
|
|
],
|
|
],
|
|
);
|
|
|
|
// Use file_get_contents() with HTTP context to fetch url
|
|
$result = file_get_contents($url, false, $context);
|
|
$jsonResponse = json_decode($result, true);
|
|
$latestVersion = $jsonResponse['tag_name'] ?? null;
|
|
|
|
$io->text('The latest Leantime version is: '.$latestVersion);
|
|
|
|
// Build download URL
|
|
if (version_compare($currentVersion, ltrim($latestVersion, 'v'), '>=')) {
|
|
$io->success('You are on the most up to date version');
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
// Backup DB + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */
|
|
$io->section('Database Backup');
|
|
$skipBackup = $input->getOption('skipDbBackup');
|
|
|
|
if ($skipBackup === false) {
|
|
$backUp = new ArrayInput([
|
|
// The command name is passed as first argument
|
|
'command' => 'db:backup',
|
|
]);
|
|
|
|
$this->getApplication()->doRun($backUp, $output);
|
|
}
|
|
|
|
// Download and extract + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */
|
|
$io->section('Download & Extract');
|
|
|
|
$io->text('Downloading latest version...');
|
|
$downloadUrl = 'https://github.com/leantime/leantime/releases/download/'.$latestVersion.'/Leantime-'.$latestVersion.'.zip';
|
|
$zipFile = storage_path('/framework/cache/latest.zip');
|
|
Http::sink($zipFile)->get($downloadUrl);
|
|
|
|
$io->text('Extracting Archive...');
|
|
|
|
if (! class_exists(\ZipArchive::class)) {
|
|
$io->text('ZipArchive not found. Cannot auto-update until the php zip extension is installed. On linux, \'sudo apt install php-zip\'');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$zip = new \ZipArchive;
|
|
if ($zip->open($zipFile) === true) {
|
|
$zip->extractTo(storage_path('/framework/cache/leantime'));
|
|
$zip->close();
|
|
$io->success('New update zip file successfully extracted to '.storage_path('/framework/cache/leantime'));
|
|
} else {
|
|
$io->text('Error opening downloaded zip file!');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
// Disable Plugins + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */
|
|
// If we got here everything is ready to go and we just need to move the files.
|
|
// Let's disable plugins
|
|
$io->section('Disabling Plugins');
|
|
|
|
/** @var Plugins $plugins */
|
|
$plugins = app()->make(Plugins::class);
|
|
$enabledPlugins = $plugins->getAllPlugins(enabledOnly: true);
|
|
foreach ($enabledPlugins as $plugin) {
|
|
if ($plugin->type != 'system' && isset($plugin->id)) {
|
|
$plugins->disablePlugin($plugin->id);
|
|
$io->text($plugin->name.': Disabled');
|
|
}
|
|
}
|
|
|
|
$io->success('Plugins disabled successfully');
|
|
|
|
// Applying Update + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */
|
|
$io->section('Applying Update');
|
|
$cp_retval = 0;
|
|
$cp_output = [];
|
|
exec('cp -r '.storage_path('/framework/cache/leantime').'/* '.APP_ROOT.'/', $cp_output, $cp_retval);
|
|
$io->text('Returned with status '.$cp_retval.' and output:');
|
|
$io->text($cp_output);
|
|
|
|
if ($cp_retval == 0) {
|
|
$io->success('Files were updated');
|
|
} else {
|
|
$io->error('Could not apply update. Please check the output above for more information.');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
// Clear Cache + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */
|
|
$io->section('Clearing Cache');
|
|
|
|
// Deliberately NOT a shell glob: inside the double quotes the shell never expanded
|
|
// `*.php`, so this delete silently did nothing for years. A stale
|
|
// bootstrap/cache/packages.php then survives updates and Laravel trusts it as-is —
|
|
// any provider added to composer.json since (e.g. laravel/mcp) never loads.
|
|
foreach (glob(APP_ROOT.'/bootstrap/cache/*.php') ?: [] as $cachedFile) {
|
|
unlink($cachedFile);
|
|
}
|
|
exec('rm -rf "'.APP_ROOT.'/storage/framework/composerPaths.php"');
|
|
exec('rm -rf "'.APP_ROOT.'/storage/framework/viewPaths.php"');
|
|
|
|
exec('rm -rf "'.APP_ROOT.'/storage/framework/cache/leantime"');
|
|
exec('rm -rf "'.APP_ROOT.'/storage/framework/cache/latest.zip"');
|
|
|
|
exec('find "'.APP_ROOT.'/storage/framework/cache" -type f ! -name ".gitignore" -delete');
|
|
exec('find "'.APP_ROOT.'/storage/framework/sessions" -type f ! -name ".gitignore" -delete');
|
|
exec('find "'.APP_ROOT.'/storage/framework/views" -type f ! -name ".gitignore" -delete');
|
|
|
|
$io->success('Clearing Cache Complete');
|
|
|
|
// Enable Plugins + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */
|
|
$io->section('Re-enabling Plugins');
|
|
foreach ($enabledPlugins as $plugin) {
|
|
if ($plugin->type != 'system' && isset($plugin->id)) {
|
|
$plugins->enablePlugin($plugin->id);
|
|
$io->text($plugin->name.': Enabled');
|
|
}
|
|
}
|
|
|
|
$io->success('Plugins were enabled');
|
|
|
|
$io->section('Summary');
|
|
$io->success('Update applied Successfully');
|
|
|
|
return Command::SUCCESS;
|
|
}
|
|
}
|