diff --git a/bin/phpmnd b/bin/phpmnd index e4d3d9e..afd74f8 100755 --- a/bin/phpmnd +++ b/bin/phpmnd @@ -37,4 +37,28 @@ if (false === $loaded) { ini_set('xdebug.max_nesting_level', 10000); $application = new Povils\PHPMND\Console\Application; -$application->run(); +$application->add(new Povils\PHPMND\Console\Command); +if ('phar:' === substr(__FILE__, 0, 5)) { + $application->add(new Povils\PHPMND\Console\Command\SelfUpdate); +} + +$output = new Symfony\Component\Console\Output\ConsoleOutput(); +$input = new \Symfony\Component\Console\Input\ArgvInput(prepareArgv()); +$application->run($input, $output); + +function prepareArgv() +{ + $argv = $_SERVER['argv']; + + $found = false; + + if (isset($argv[1]) && ('run' === $argv[1] || 'self-update' === $argv[1])) { + $found = true; + } + + if (!$found) { + array_splice($argv, 1, 0, ['run']); + } + + return $argv; +} diff --git a/box.json.dist b/box.json.dist index 173eeb9..85be828 100644 --- a/box.json.dist +++ b/box.json.dist @@ -40,7 +40,8 @@ "phar-io/manifest", "phar-io/version", "theseer/tokenizer", - "sebastian/object-reflector" + "sebastian/object-reflector", + "squizlabs/php_codesniffer" ], "in": [ "vendor" diff --git a/composer.json b/composer.json index 57d698e..2d098e3 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,8 @@ }, "require-dev": { "phpunit/phpunit": "^5.7 || ^6.0", - "squizlabs/php_codesniffer": "^2.8.1" + "squizlabs/php_codesniffer": "^2.8.1", + "padraic/phar-updater": "^1.0.3" }, "autoload": { "psr-4": { diff --git a/src/Console/Application.php b/src/Console/Application.php index 8a50101..3a94890 100644 --- a/src/Console/Application.php +++ b/src/Console/Application.php @@ -16,40 +16,11 @@ class Application extends BaseApplication { const VERSION = '1.1.0'; const COMMAND_NAME = 'phpmnd'; + const PACKAGIST_PACKAGE_NAME = 'povils/phpmnd'; public function __construct() { - parent::__construct('phpmnd', self::VERSION); - } - - /** - * @inheritdoc - */ - protected function getCommandName(InputInterface $input) - { - return self::COMMAND_NAME; - } - - /** - * @inheritdoc - */ - protected function getDefaultCommands() - { - $defaultCommands = parent::getDefaultCommands(); - $defaultCommands[] = new Command; - - return $defaultCommands; - } - - /** - * @inheritdoc - */ - public function getDefinition() - { - $inputDefinition = parent::getDefinition(); - $inputDefinition->setArguments(); - - return $inputDefinition; + parent::__construct(self::COMMAND_NAME, self::VERSION); } /** @@ -70,8 +41,8 @@ public function doRun(InputInterface $input, OutputInterface $output) exit; } - if (null === $input->getFirstArgument()) { - $input = new ArrayInput(['--help']); + if ('run' === (string) $input) { + $input = new ArrayInput(['run','--help']); } return parent::doRun($input, $output); diff --git a/src/Console/Command.php b/src/Console/Command.php index 6aa3842..7fec06a 100644 --- a/src/Console/Command.php +++ b/src/Console/Command.php @@ -31,7 +31,8 @@ class Command extends BaseCommand protected function configure() { $this - ->setName('phpmnd') + ->setName('run') + ->setDescription('Runs PHPMND. Executed by default when no other command provided.') ->setDefinition( [ new InputArgument( diff --git a/src/Console/Command/SelfUpdate.php b/src/Console/Command/SelfUpdate.php new file mode 100644 index 0000000..844d9d0 --- /dev/null +++ b/src/Console/Command/SelfUpdate.php @@ -0,0 +1,209 @@ +setName('self-update') + ->setDescription('Update phpmnd.phar to most recent stable build.') + ->addOption( + 'rollback', + 'r', + InputOption::VALUE_NONE, + 'Rollback to previous version of PHPMND if available on filesystem.' + ) + ->addOption( + 'check', + 'c', + InputOption::VALUE_NONE, + 'Checks whether an update is available.' + ) + ; + } + + /** + * Execute the command. + * + * @param InputInterface $input + * @param OutputInterface $output + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->output = $output; + $this->version = $this->getApplication()->getVersion(); + + /** + * Check for ancilliary options + */ + if ($input->getOption('rollback')) { + $this->rollback(); + return; + } + + if ($input->getOption('check')) { + $this->printAvailableUpdates(); + return; + } + + $this->updateToStableBuild(); + } + + /** + * Perform update using phar-updater configured for stable versions. + */ + private function updateToStableBuild() + { + $this->update($this->getStableUpdater()); + } + + /** + * Get phar-updater instance. + */ + private function getStableUpdater() + { + $updater = new Updater(null, false); + $updater->setStrategy(Updater::STRATEGY_GITHUB); + return $this->getGithubReleasesUpdater($updater); + } + + /** + * Perform in-place update of phar. + */ + private function update(Updater $updater) + { + $this->output->writeln('Updating...'.PHP_EOL); + try { + $result = $updater->update(); + + $newVersion = $updater->getNewVersion(); + $oldVersion = $updater->getOldVersion(); + + if ($result) { + $this->output->writeln('PHPMND has been updated.'); + $this->output->writeln(sprintf( + 'Current version is: %s.', + $newVersion + )); + $this->output->writeln(sprintf( + 'Previous version was: %s.', + $oldVersion + )); + } else { + $this->output->writeln('PHPMND is currently up to date.'); + $this->output->writeln(sprintf( + 'Current version is: %s.', + $oldVersion + )); + } + } catch (\Exception $e) { + $this->output->writeln(sprintf('Error: %s', $e->getMessage())); + } + $this->output->write(PHP_EOL); + } + + /** + * Attempt to rollback to the previous phar version. + */ + private function rollback() + { + $updater = new Updater(null, false); + try { + $result = $updater->rollback(); + if ($result) { + $this->output->writeln('PHPMND has been rolled back to prior version.'); + } else { + $this->output->writeln('Rollback failed for reasons unknown.'); + } + } catch (\Exception $e) { + $this->output->writeln(sprintf('Error: %s', $e->getMessage())); + } + } + + private function printAvailableUpdates() + { + $this->printCurrentLocalVersion(); + $this->printCurrentStableVersion(); + } + + /** + * Print the current version of the phar in use. + */ + private function printCurrentLocalVersion() + { + $this->output->writeln(sprintf( + 'Your current local build version is: %s', + $this->version + )); + } + + /** + * Send updater to version printer. + */ + private function printCurrentStableVersion() + { + $this->printVersion($this->getStableUpdater()); + } + + /** + * Print a remotely available version. + * @param Updater $updater + */ + private function printVersion(Updater $updater) + { + $stability = 'stable'; + try { + if ($updater->hasUpdate()) { + $this->output->writeln(sprintf( + 'The current %s build available remotely is: %s', + $stability, + $updater->getNewVersion() + )); + } elseif (false == $updater->getNewVersion()) { + $this->output->writeln(sprintf('There are no new %s builds available.', $stability)); + } else { + $this->output->writeln(sprintf('You have the current %s build installed.', $stability)); + } + } catch (\Exception $e) { + $this->output->writeln(sprintf('Error: %s', $e->getMessage())); + } + } + + /** + * @param Updater $updater + * @return Updater + */ + private function getGithubReleasesUpdater(Updater $updater) + { + $updater->getStrategy()->setPackageName(Application::PACKAGIST_PACKAGE_NAME); + $updater->getStrategy()->setPharName(self::REMOTE_FILENAME); + $updater->getStrategy()->setCurrentLocalVersion($this->version); + return $updater; + } +}