setName('self-upgrade') ->setAliases(['selfupgrade', 'selfupdate']) ->addOption( 'force', 'f', InputOption::VALUE_NONE, 'Force re-fetching the data from remote' ) ->addOption( 'all-yes', 'y', InputOption::VALUE_NONE, 'Assumes yes (or best approach) instead of prompting' ) ->addOption( 'overwrite', 'o', InputOption::VALUE_NONE, 'Option to overwrite packages if they already exist' ) ->setDescription('Detects and performs an update of Grav itself when available') ->setHelp('The update command updates Grav itself when a new version is available'); } protected function serve() { $this->upgrader = new Upgrader($this->input->getOption('force')); $this->all_yes = $this->input->getOption('all-yes'); $this->overwrite = $this->input->getOption('overwrite'); $this->displayGPMRelease(); $update = $this->upgrader->getAssets()['grav-update']; $local = $this->upgrader->getLocalVersion(); $remote = $this->upgrader->getRemoteVersion(); $release = strftime('%c', strtotime($this->upgrader->getReleaseDate())); if (!$this->upgrader->meetsRequirements()) { $this->output->writeln('ATTENTION:'); $this->output->writeln(' Grav has increased the minimum PHP requirement.'); $this->output->writeln(' You are currently running PHP ' . phpversion() . ', but PHP ' . $this->upgrader->minPHPVersion() . ' is required.'); $this->output->writeln(' Additional information: http://getgrav.org/blog/changing-php-requirements'); $this->output->writeln(''); $this->output->writeln('Selfupgrade aborted.'); $this->output->writeln(''); exit; } if (!$this->overwrite && !$this->upgrader->isUpgradable()) { $this->output->writeln("You are already running the latest version of Grav (v{$local}) released on {$release}"); exit; } Installer::isValidDestination(GRAV_ROOT . '/system'); if (Installer::IS_LINK === Installer::lastErrorCode()) { $this->output->writeln('ATTENTION: Grav is symlinked, cannot upgrade, aborting...'); $this->output->writeln(''); $this->output->writeln("You are currently running a symbolically linked Grav v{$local}. Latest available is v{$remote}."); exit; } // not used but preloaded just in case! new ArrayInput([]); $questionHelper = $this->getHelper('question'); $this->output->writeln("Grav v{$remote} is now available [release date: {$release}]."); $this->output->writeln('You are currently using v' . GRAV_VERSION . '.'); if (!$this->all_yes) { $question = new ConfirmationQuestion('Would you like to read the changelog before proceeding? [y|N] ', false); $answer = $questionHelper->ask($this->input, $this->output, $question); if ($answer) { $changelog = $this->upgrader->getChangelog(GRAV_VERSION); $this->output->writeln(''); foreach ($changelog as $version => $log) { $title = $version . ' [' . $log['date'] . ']'; $content = preg_replace_callback('/\d\.\s\[\]\(#(.*)\)/', function ($match) { return "\n" . ucfirst($match[1]) . ':'; }, $log['content']); $this->output->writeln($title); $this->output->writeln(str_repeat('-', \strlen($title))); $this->output->writeln($content); $this->output->writeln(''); } $question = new ConfirmationQuestion('Press [ENTER] to continue.', true); $questionHelper->ask($this->input, $this->output, $question); } $question = new ConfirmationQuestion('Would you like to upgrade now? [y|N] ', false); $answer = $questionHelper->ask($this->input, $this->output, $question); if (!$answer) { $this->output->writeln('Aborting...'); exit; } } $this->output->writeln(''); $this->output->writeln("Preparing to upgrade to v{$remote}.."); $this->output->write(" |- Downloading upgrade [{$this->formatBytes($update['size'])}]... 0%"); $this->file = $this->download($update); $this->output->write(' |- Installing upgrade... '); $installation = $this->upgrade(); if (!$installation) { $this->output->writeln(" '- Installation failed or aborted."); $this->output->writeln(''); } else { $this->output->writeln(" '- Success! "); $this->output->writeln(''); } // clear cache after successful upgrade $this->clearCache('all'); } /** * @param array $package * * @return string */ private function download($package) { $tmp_dir = Grav::instance()['locator']->findResource('tmp://', true, true); $this->tmp = $tmp_dir . '/Grav-' . uniqid('', false); $output = Response::get($package['download'], [], [$this, 'progress']); Folder::create($this->tmp); $this->output->write("\x0D"); $this->output->write(" |- Downloading upgrade [{$this->formatBytes($package['size'])}]... 100%"); $this->output->writeln(''); file_put_contents($this->tmp . DS . $package['name'], $output); return $this->tmp . DS . $package['name']; } /** * @return bool */ private function upgrade() { if ($this->file) { $folder = Installer::unZip($this->file, $this->tmp . '/zip'); } else { $folder = false; } static::upgradeGrav($this->file, $folder); $errorCode = Installer::lastErrorCode(); if ($this->tmp) { Folder::delete($this->tmp); } if ($errorCode & (Installer::ZIP_OPEN_ERROR | Installer::ZIP_EXTRACT_ERROR)) { $this->output->write("\x0D"); // extra white spaces to clear out the buffer properly $this->output->writeln(' |- Installing upgrade... error '); $this->output->writeln(" | '- " . Installer::lastErrorMsg()); return false; } $this->output->write("\x0D"); // extra white spaces to clear out the buffer properly $this->output->writeln(' |- Installing upgrade... ok '); return true; } /** * @param array $progress */ public function progress($progress) { $this->output->write("\x0D"); $this->output->write(" |- Downloading upgrade [{$this->formatBytes($progress['filesize']) }]... " . str_pad($progress['percent'], 5, ' ', STR_PAD_LEFT) . '%'); } /** * @param int|float $size * @param int $precision * * @return string */ public function formatBytes($size, $precision = 2) { $base = log($size) / log(1024); $suffixes = array('', 'k', 'M', 'G', 'T'); return round(1024 ** ($base - floor($base)), $precision) . $suffixes[(int)floor($base)]; } private function upgradeGrav($zip, $folder, $keepFolder = false) { static $ignores = [ 'backup', 'cache', 'images', 'logs', 'tmp', 'user', '.htaccess', 'robots.txt' ]; if (!is_dir($folder)) { Installer::setError('Invalid source folder'); } try { $script = $folder . '/system/install.php'; /** Install $installer */ if ((file_exists($script) && $install = include $script) && is_callable($install)) { $install($zip); } else { Installer::install( $zip, GRAV_ROOT, ['sophisticated' => true, 'overwrite' => true, 'ignore_symlinks' => true, 'ignores' => $ignores], $folder, $keepFolder ); Cache::clearCache(); } } catch (\Exception $e) { Installer::setError($e->getMessage()); } } }