Skip to content

Commit

Permalink
Implement self-update command
Browse files Browse the repository at this point in the history
  • Loading branch information
tgalopin committed Jun 22, 2016
1 parent f6335d8 commit 489b7d3
Show file tree
Hide file tree
Showing 5 changed files with 341 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .rmt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ prerequisites:

pre-release-actions:
update-version-class:
class: AcmePhp\Cli\Application
class: src/Cli/Application.php
vcs-commit: ~

post-release-actions:
Expand Down
3 changes: 2 additions & 1 deletion box.json.dist
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@
"main": "bin/acme",
"output": "build/acmephp.phar",
"chmod": "0755",
"stub": true
"stub": true,
"key": "../private.key"
}
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@
"league/flysystem": "^1.0.19",
"league/flysystem-sftp": "^1.0.7",
"webmozart/assert": "^1.0",
"webmozart/path-util": "^2.3"
"webmozart/path-util": "^2.3",
"padraic/phar-updater": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.22",
Expand Down
2 changes: 2 additions & 0 deletions src/Cli/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use AcmePhp\Cli\Command\Helper\DistinguishedNameHelper;
use AcmePhp\Cli\Command\RegisterCommand;
use AcmePhp\Cli\Command\RequestCommand;
use AcmePhp\Cli\Command\SelfUpdateCommand;
use AcmePhp\Cli\Command\StatusCommand;
use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Input\InputOption;
Expand Down Expand Up @@ -47,6 +48,7 @@ protected function getDefaultCommands()
new CheckCommand(),
new RequestCommand(),
new StatusCommand(),
new SelfUpdateCommand(),
]);
}

Expand Down
334 changes: 334 additions & 0 deletions src/Cli/Command/SelfUpdateCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,334 @@
<?php

/*
* This file is part of the Acme PHP project.
*
* (c) Titouan Galopin <galopintitouan@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace AcmePhp\Cli\Command;

use Humbug\SelfUpdate\Strategy\GithubStrategy;
use Humbug\SelfUpdate\Strategy\ShaStrategy;
use Humbug\SelfUpdate\Updater;
use Humbug\SelfUpdate\VersionParser;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
* Self-update command.
*
* Heavily inspired from https://github.com/padraic/phar-updater.
*
* @author Titouan Galopin <galopintitouan@gmail.com>
*/
class SelfUpdateCommand extends Command
{
const PACKAGE_NAME = 'acmephp/acmephp';
const FILE_NAME = 'acmephp.phar';
const VERSION_URL = 'https://acmephp.github.io/downloads/acmephp.version';
const PHAR_URL = 'https://acmephp.github.io/downloads/acmephp.phar';

/**
* @var string
*/
protected $version;

/**
* @var OutputInterface
*/
protected $output;

protected function configure()
{
$this
->setName('self-update')
->setDescription('Update acmephp.phar to most recent stable, pre-release or development build.')
->addOption(
'dev',
'd',
InputOption::VALUE_NONE,
'Update to most recent development build of Acme PHP.'
)
->addOption(
'non-dev',
'N',
InputOption::VALUE_NONE,
'Update to most recent non-development (alpha/beta/stable) build of Acme PHP tagged on Github.'
)
->addOption(
'pre',
'p',
InputOption::VALUE_NONE,
'Update to most recent pre-release version of Acme PHP (alpha/beta/rc) tagged on Github.'
)
->addOption(
'stable',
's',
InputOption::VALUE_NONE,
'Update to most recent stable version tagged on Github.'
)
->addOption(
'rollback',
'r',
InputOption::VALUE_NONE,
'Rollback to previous version of Acme PHP if available on filesystem.'
)
->addOption(
'check',
'c',
InputOption::VALUE_NONE,
'Checks what updates are available across all possible stability tracks.'
);
}

/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->output = $output;
$this->version = $this->getApplication()->getVersion();
$parser = new VersionParser();

/*
* Check for ancilliary options
*/
if ($input->getOption('rollback')) {
$this->rollback();

return;
}

if ($input->getOption('check')) {
$this->printAvailableUpdates();

return;
}

/*
* Update to any specified stability option
*/
if ($input->getOption('dev')) {
$this->updateToDevelopmentBuild();

return;
}

if ($input->getOption('pre')) {
$this->updateToPreReleaseBuild();

return;
}

if ($input->getOption('stable')) {
$this->updateToStableBuild();

return;
}

if ($input->getOption('non-dev')) {
$this->updateToMostRecentNonDevRemote();

return;
}

/*
* If current build is stable, only update to more recent stable
* versions if available. User may specify otherwise using options.
*/
if ($parser->isStable($this->version)) {
$this->updateToStableBuild();

return;
}

/*
* By default, update to most recent remote version regardless
* of stability.
*/
$this->updateToMostRecentNonDevRemote();
}

protected function getStableUpdater()
{
$updater = new Updater();
$updater->setStrategy(Updater::STRATEGY_GITHUB);

return $this->getGithubReleasesUpdater($updater);
}

protected function getPreReleaseUpdater()
{
$updater = new Updater();
$updater->setStrategy(Updater::STRATEGY_GITHUB);
$updater->getStrategy()->setStability(GithubStrategy::UNSTABLE);

return $this->getGithubReleasesUpdater($updater);
}

protected function getMostRecentNonDevUpdater()
{
$updater = new Updater();
$updater->setStrategy(Updater::STRATEGY_GITHUB);
$updater->getStrategy()->setStability(GithubStrategy::ANY);

return $this->getGithubReleasesUpdater($updater);
}

protected function getGithubReleasesUpdater(Updater $updater)
{
$updater->getStrategy()->setPackageName(self::PACKAGE_NAME);
$updater->getStrategy()->setPharName(self::FILE_NAME);
$updater->getStrategy()->setCurrentLocalVersion($this->version);

return $updater;
}

protected function getDevelopmentUpdater()
{
$updater = new Updater();
$updater->getStrategy()->setPharUrl(self::PHAR_URL);
$updater->getStrategy()->setVersionUrl(self::VERSION_URL);

return $updater;
}

protected function updateToStableBuild()
{
$this->update($this->getStableUpdater());
}

protected function updateToPreReleaseBuild()
{
$this->update($this->getPreReleaseUpdater());
}

protected function updateToMostRecentNonDevRemote()
{
$this->update($this->getMostRecentNonDevUpdater());
}

protected function updateToDevelopmentBuild()
{
$this->update($this->getDevelopmentUpdater());
}

protected function update(Updater $updater)
{
$this->output->writeln('Updating...'.PHP_EOL);
try {
$result = $updater->update();

$newVersion = $updater->getNewVersion();
$oldVersion = $updater->getOldVersion();
if (strlen($newVersion) == 40) {
$newVersion = 'dev-'.$newVersion;
}
if (strlen($oldVersion) == 40) {
$oldVersion = 'dev-'.$oldVersion;
}

if ($result) {
$this->output->writeln('<fg=green>Acme PHP has been updated.</fg=green>');
$this->output->writeln(sprintf(
'<fg=green>Current version is:</fg=green> <options=bold>%s</options=bold>.',
$newVersion
));
$this->output->writeln(sprintf(
'<fg=green>Previous version was:</fg=green> <options=bold>%s</options=bold>.',
$oldVersion
));
} else {
$this->output->writeln('<fg=green>Acme PHP is currently up to date.</fg=green>');
$this->output->writeln(sprintf(
'<fg=green>Current version is:</fg=green> <options=bold>%s</options=bold>.',
$oldVersion
));
}
} catch (\Exception $e) {
$this->output->writeln(sprintf('Error: <fg=yellow>%s</fg=yellow>', $e->getMessage()));
}
$this->output->write(PHP_EOL);
$this->output->writeln('You can also select update stability using --dev, --pre (alpha/beta/rc) or --stable.');
}

protected function rollback()
{
$updater = new Updater();
try {
$result = $updater->rollback();
if ($result) {
$this->output->writeln('<fg=green>Acme PHP has been rolled back to prior version.</fg=green>');
} else {
$this->output->writeln('<fg=red>Rollback failed for reasons unknown.</fg=red>');
}
} catch (\Exception $e) {
$this->output->writeln(sprintf('Error: <fg=yellow>%s</fg=yellow>', $e->getMessage()));
}
}

protected function printAvailableUpdates()
{
$this->printCurrentLocalVersion();
$this->printCurrentStableVersion();
$this->printCurrentPreReleaseVersion();
$this->printCurrentDevVersion();
$this->output->writeln('You can select update stability using --dev, --pre or --stable when self-updating.');
}

protected function printCurrentLocalVersion()
{
$this->output->writeln(sprintf(
'Your current local build version is: <options=bold>%s</options=bold>',
$this->version
));
}

protected function printCurrentStableVersion()
{
$this->printVersion($this->getStableUpdater());
}

protected function printCurrentPreReleaseVersion()
{
$this->printVersion($this->getPreReleaseUpdater());
}

protected function printCurrentDevVersion()
{
$this->printVersion($this->getDevelopmentUpdater());
}

protected function printVersion(Updater $updater)
{
$stability = 'stable';
if ($updater->getStrategy() instanceof ShaStrategy) {
$stability = 'development';
} elseif ($updater->getStrategy() instanceof GithubStrategy
&& $updater->getStrategy()->getStability() == GithubStrategy::UNSTABLE) {
$stability = 'pre-release';
}

try {
if ($updater->hasUpdate()) {
$this->output->writeln(sprintf(
'The current %s build available remotely is: <options=bold>%s</options=bold>',
$stability,
$updater->getNewVersion()
));
} elseif (false == $updater->getNewVersion()) {
$this->output->writeln(sprintf('There are no %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: <fg=yellow>%s</fg=yellow>', $e->getMessage()));
}
}
}

0 comments on commit 489b7d3

Please sign in to comment.