5 use Symfony\Component\Console\Command\Command;
6 use Symfony\Component\Console\Input\InputInterface;
7 use Symfony\Component\Console\Output\OutputInterface;
8 use Symfony\Component\Filesystem\Filesystem as sfFilesystem;
11 * Update the robo.phar from the latest github release
13 * @author Alexander Menk <alex.menk@gmail.com>
15 class SelfUpdateCommand extends Command
17 const SELF_UPDATE_COMMAND_NAME = 'self:update';
19 protected $gitHubRepository;
21 protected $currentVersion;
23 protected $applicationName;
25 public function __construct($applicationName = null, $currentVersion = null, $gitHubRepository = null)
27 $this->applicationName = $applicationName;
28 $this->currentVersion = $currentVersion;
29 $this->gitHubRepository = $gitHubRepository;
31 parent::__construct(self::SELF_UPDATE_COMMAND_NAME);
37 protected function configure()
39 $app = $this->applicationName;
42 ->setAliases(array('update'))
43 ->setDescription("Updates $app to the latest version.")
46 The <info>self-update</info> command checks github for newer
47 versions of $app and if found, installs the latest.
52 protected function getLatestReleaseFromGithub()
58 'User-Agent: ' . $this->applicationName . ' (' . $this->gitHubRepository . ')' . ' Self-Update (PHP)'
63 $context = stream_context_create($opts);
65 $releases = file_get_contents('https://api.github.com/repos/' . $this->gitHubRepository . '/releases', false, $context);
66 $releases = json_decode($releases);
68 if (! isset($releases[0])) {
69 throw new \Exception('API error - no release found at GitHub repository ' . $this->gitHubRepository);
72 $version = $releases[0]->tag_name;
73 $url = $releases[0]->assets[0]->browser_download_url;
75 return [ $version, $url ];
81 protected function execute(InputInterface $input, OutputInterface $output)
83 if (empty(\Phar::running())) {
84 throw new \Exception(self::SELF_UPDATE_COMMAND_NAME . ' only works when running the phar version of ' . $this->applicationName . '.');
87 $localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
88 $programName = basename($localFilename);
89 $tempFilename = dirname($localFilename) . '/' . basename($localFilename, '.phar') . '-temp.phar';
91 // check for permissions in local filesystem before start connection process
92 if (! is_writable($tempDirectory = dirname($tempFilename))) {
94 $programName . ' update failed: the "' . $tempDirectory .
95 '" directory used to download the temp file could not be written'
99 if (! is_writable($localFilename)) {
100 throw new \Exception(
101 $programName . ' update failed: the "' . $localFilename . '" file could not be written (execute with sudo)'
105 list( $latest, $downloadUrl ) = $this->getLatestReleaseFromGithub();
108 if ($this->currentVersion == $latest) {
109 $output->writeln('No update available');
113 $fs = new sfFilesystem();
115 $output->writeln('Downloading ' . $this->applicationName . ' (' . $this->gitHubRepository . ') ' . $latest);
117 $fs->copy($downloadUrl, $tempFilename);
119 $output->writeln('Download finished');
122 \error_reporting(E_ALL); // supress notices
124 @chmod($tempFilename, 0777 & ~umask());
125 // test the phar validity
126 $phar = new \Phar($tempFilename);
127 // free the variable to unlock the file
129 @rename($tempFilename, $localFilename);
130 $output->writeln('<info>Successfully updated ' . $programName . '</info>');
132 } catch (\Exception $e) {
133 @unlink($tempFilename);
134 if (! $e instanceof \UnexpectedValueException && ! $e instanceof \PharException) {
137 $output->writeln('<error>The download is corrupted (' . $e->getMessage() . ').</error>');
138 $output->writeln('<error>Please re-run the self-update command to try again.</error>');
145 * This is a workaround to prevent warning of dispatcher after replacing
150 protected function _exit()