diff --git a/bin/gpm b/bin/gpm
index 3e9374702..b811b0be1 100755
--- a/bin/gpm
+++ b/bin/gpm
@@ -35,5 +35,6 @@ $app->addCommands(array(
new \Grav\Console\Gpm\InfoCommand(),
new \Grav\Console\Gpm\InstallCommand(),
new \Grav\Console\Gpm\UpdateCommand(),
+ new \Grav\Console\Gpm\SelfupgradeCommand(),
));
$app->run();
diff --git a/system/src/Grav/Console/Gpm/SelfupgradeCommand.php b/system/src/Grav/Console/Gpm/SelfupgradeCommand.php
new file mode 100644
index 000000000..0a05914da
--- /dev/null
+++ b/system/src/Grav/Console/Gpm/SelfupgradeCommand.php
@@ -0,0 +1,122 @@
+setName("self-upgrade")
+ ->setAliases(['selfupgrade'])
+ ->addOption(
+ 'force',
+ 'f',
+ InputOption::VALUE_NONE,
+ 'Force re-fetching the data from remote'
+ )
+ ->setDescription("Detects and performs an update of plugins and themes when available")
+ ->setHelp('The update command updates plugins and themes when a new version is available');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $this->setupConsole($input, $output);
+ $this->upgrader = new Upgrader($this->input->getOption('force'));
+
+ $local = $this->upgrader->getLocalVersion();
+ $remote = $this->upgrader->getRemoteVersion();
+ $update = $this->upgrader->getAssets()->{'grav-update'};
+ $release = strftime('%c', strtotime($this->upgrader->getReleaseDate()));
+
+ if (!$this->upgrader->isUpgradable()) {
+ $this->output->writeln("You are already running the latest version of Grav (v" . $local . ") released on " . $release);
+ exit;
+ }
+
+ $this->output->writeln("Preparing to upgrade Grav to v" . $remote . " [release date: " . $release . "]");
+
+ $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('');
+ }
+ }
+
+ private function download($package)
+ {
+ $this->tmp = sys_get_temp_dir() . DS . 'Grav-' . uniqid();
+ $output = Response::get($package->download, [], [$this, 'progress']);
+
+ Folder::mkdir($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;
+ }
+
+ private function upgrade()
+ {
+ $installer = Installer::install($this->file, GRAV_ROOT, ['sophisticated' => true, 'overwrite' => true, 'ignore_symlinks' => true]);
+ $errorCode = Installer::lastErrorCode();
+ 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;
+ }
+
+ 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) . '%');
+ }
+
+ public function formatBytes($size, $precision = 2)
+ {
+ $base = log($size) / log(1024);
+ $suffixes = array('', 'k', 'M', 'G', 'T');
+
+ return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)];
+ }
+}