Hello community, here is the log from the commit of package platformsh-cli for openSUSE:Factory checked in at 2019-01-21 11:00:46 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/platformsh-cli (Old) and /work/SRC/openSUSE:Factory/.platformsh-cli.new.28833 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "platformsh-cli" Mon Jan 21 11:00:46 2019 rev:61 rq:667022 version:3.39.0 Changes: -------- --- /work/SRC/openSUSE:Factory/platformsh-cli/platformsh-cli.changes 2019-01-10 15:23:37.638312692 +0100 +++ /work/SRC/openSUSE:Factory/.platformsh-cli.new.28833/platformsh-cli.changes 2019-01-21 11:02:29.767109127 +0100 @@ -1,0 +2,14 @@ +Fri Jan 18 16:06:31 UTC 2019 - ji...@boombatower.com + +- Update to version 3.39.0: + * Release v3.39.0 + * Add tunnel:single command: allow opening a tunnel without posix/pcntl (#768) + * Drush 9 compatibility: move alias auto-remove key under "options" + * Remove Drush 9-beta compatibility + * Fix: login does not sufficiently log out from old account (#770) + * Account for differing app roots in Drush aliases + * Unused function + * Release v3.38.1 + * Fix incorrect "You cannot use both the <environment> argument and the --environment option" error in environment:activate command + +------------------------------------------------------------------- Old: ---- platformsh-cli-3.38.0.tar.xz New: ---- platformsh-cli-3.39.0.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ platformsh-cli.spec ++++++ --- /var/tmp/diff_new_pack.ASgxs6/_old 2019-01-21 11:02:30.659107979 +0100 +++ /var/tmp/diff_new_pack.ASgxs6/_new 2019-01-21 11:02:30.659107979 +0100 @@ -17,7 +17,7 @@ Name: platformsh-cli -Version: 3.38.0 +Version: 3.39.0 Release: 0 Summary: Tool for managing Platform.sh services from the command line # See licenses.txt for dependency licenses. ++++++ _service ++++++ --- /var/tmp/diff_new_pack.ASgxs6/_old 2019-01-21 11:02:30.683107948 +0100 +++ /var/tmp/diff_new_pack.ASgxs6/_new 2019-01-21 11:02:30.683107948 +0100 @@ -2,7 +2,7 @@ <service name="tar_scm" mode="disabled"> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> - <param name="revision">refs/tags/v3.38.0</param> + <param name="revision">refs/tags/v3.39.0</param> <param name="url">git://github.com/platformsh/platformsh-cli.git</param> <param name="scm">git</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.ASgxs6/_old 2019-01-21 11:02:30.699107927 +0100 +++ /var/tmp/diff_new_pack.ASgxs6/_new 2019-01-21 11:02:30.703107922 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">git://github.com/platformsh/platformsh-cli.git</param> - <param name="changesrevision">89037ac97082af56f5346c0fc5dfeb3d8a2d0061</param> + <param name="changesrevision">d0dd97e777b3c7b2fe5432004477ef3eb9f44fcf</param> </service> </servicedata> ++++++ licenses.txt ++++++ --- /var/tmp/diff_new_pack.ASgxs6/_old 2019-01-21 11:02:30.731107886 +0100 +++ /var/tmp/diff_new_pack.ASgxs6/_new 2019-01-21 11:02:30.735107880 +0100 @@ -17,7 +17,7 @@ padraic/humbug_get_contents 1.1.2 BSD-3-Clause padraic/phar-updater v1.0.6 BSD-3-Clause paragonie/random_compat v2.0.17 MIT -platformsh/client v0.23.0 MIT +platformsh/client v0.23.2 MIT platformsh/console-form v0.0.23 MIT psr/container 1.0.0 MIT psr/log 1.1.0 MIT ++++++ platformsh-cli-3.38.0.tar.xz -> platformsh-cli-3.39.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/README.md new/platformsh-cli-3.39.0/README.md --- old/platformsh-cli-3.38.0/README.md 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/README.md 2019-01-18 12:39:27.000000000 +0100 @@ -184,6 +184,7 @@ tunnel:info View relationship info for SSH tunnels tunnel:list (tunnels) List SSH tunnels tunnel:open Open SSH tunnels to an app's relationships + tunnel:single Open a single SSH tunnel to an app relationship user user:add Add a user to the project user:delete Delete a user from the project diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/composer.json new/platformsh-cli-3.39.0/composer.json --- old/platformsh-cli-3.38.0/composer.json 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/composer.json 2019-01-18 12:39:27.000000000 +0100 @@ -8,7 +8,7 @@ "guzzlehttp/guzzle": "^5.3", "guzzlehttp/ringphp": "^1.1", "platformsh/console-form": ">=0.0.22 <2.0", - "platformsh/client": ">=0.23.0 <2.0", + "platformsh/client": ">=0.23.2 <2.0", "symfony/console": "^3.0 >=3.2", "symfony/yaml": "^3.0 || ^2.6", "symfony/finder": "^3.0", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/composer.lock new/platformsh-cli-3.39.0/composer.lock --- old/platformsh-cli-3.38.0/composer.lock 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/composer.lock 2019-01-18 12:39:27.000000000 +0100 @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7316086542b599673424191e18406f6a", + "content-hash": "b2769ae9f93d12b94dc0614825c1d4b6", "packages": [ { "name": "cocur/slugify", @@ -712,16 +712,16 @@ }, { "name": "platformsh/client", - "version": "v0.23.0", + "version": "v0.23.2", "source": { "type": "git", "url": "https://github.com/platformsh/platformsh-client-php.git", - "reference": "9b0fc3004d5abdc3cc54b0bea80f779e23660289" + "reference": "028f723d4581b09c28f2ab5029b51a2e9363bd15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/platformsh/platformsh-client-php/zipball/9b0fc3004d5abdc3cc54b0bea80f779e23660289", - "reference": "9b0fc3004d5abdc3cc54b0bea80f779e23660289", + "url": "https://api.github.com/repos/platformsh/platformsh-client-php/zipball/028f723d4581b09c28f2ab5029b51a2e9363bd15", + "reference": "028f723d4581b09c28f2ab5029b51a2e9363bd15", "shasum": "" }, "require": { @@ -757,7 +757,7 @@ } ], "description": "Platform.sh API client", - "time": "2019-01-09T14:36:49+00:00" + "time": "2019-01-16T17:37:27+00:00" }, { "name": "platformsh/console-form", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/config.yaml new/platformsh-cli-3.39.0/config.yaml --- old/platformsh-cli-3.38.0/config.yaml 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/config.yaml 2019-01-18 12:39:27.000000000 +0100 @@ -1,7 +1,7 @@ # Metadata about the CLI application itself. application: name: 'Platform.sh CLI' - version: '3.38.0' + version: '3.39.0' executable: 'platform' package_name: 'platformsh/cli' installer_url: 'https://platform.sh/cli/installer' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/dist/manifest.json new/platformsh-cli-3.39.0/dist/manifest.json --- old/platformsh-cli-3.38.0/dist/manifest.json 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/dist/manifest.json 2019-01-18 12:39:27.000000000 +0100 @@ -1,10 +1,10 @@ [ { "name": "platform.phar", - "sha1": "5144fb0283e09583c7f5933324168194b6df4e61", - "sha256": "f6aa40652c031374338fdedc2b5fe45419477cb8d8c688639819f8cac11ddd13", - "url": "https://github.com/platformsh/platformsh-cli/releases/download/v3.38.0/platform.phar", - "version": "3.38.0", + "sha1": "a522451e18a9aa6d50c93b76903e13efd7a4c9a6", + "sha256": "f764ee7f83e659276e2876e3b80fda134045ab2ed9192d7f8574f2d56d559b28", + "url": "https://github.com/platformsh/platformsh-cli/releases/download/v3.39.0/platform.phar", + "version": "3.39.0", "php": { "min": "5.5.9" }, @@ -162,6 +162,11 @@ "notes": "* Allow restoring snapshots to another environment in snapshot:restore command\n (with new --target and --branch-from options).\n* Sort interactive project/environment choices alphabetically.\n* Fix \"Name\"/\"name\" column should always have been \"Title\"/\"title\" in\n environment:list command.\n* Define \"created\" and \"updated\" columns in environment:list command.\n* Remove interactivity check from installer.\n* Fix: environments with an empty name (e.g. '0') not being recognized.\n* Fix documentation in user:list, user:add and user:update commands.\n* Various changes and new features in certificate:list command:\n - only show non-expired certificates by default\n - add --ignore-expiry option for the previous behavior\n - add --exclude-domain option to filter out certificates from the list\n - add --pipe-domains option to list domains covered by certificates\n - add \"certs\" alias\n - define \"domains\" column (for the --columns option)", "show from": "3.37.0", "hide from": "3.38.0" + }, + { + "notes": "New features:\n\n* Add tunnel:single command (allow opening a tunnel without posix/pcntl).\n\nBug fixes:\n\n* Fix bug where login to a new account does not log out from the old account.\n* Account for differing app roots in Drush aliases - allows generated Drush\n aliases to work with Platform.sh Enterprise environments.\n* Fix Drush 9 alias compatibility (and remove Drush 9-beta compatibility).", + "show from": "3.38.0", + "hide from": "3.39.0" } ] } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/src/Application.php new/platformsh-cli-3.39.0/src/Application.php --- old/platformsh-cli-3.38.0/src/Application.php 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/src/Application.php 2019-01-18 12:39:27.000000000 +0100 @@ -190,6 +190,7 @@ $commands[] = new Command\Tunnel\TunnelInfoCommand(); $commands[] = new Command\Tunnel\TunnelListCommand(); $commands[] = new Command\Tunnel\TunnelOpenCommand(); + $commands[] = new Command\Tunnel\TunnelSingleCommand(); $commands[] = new Command\User\UserAddCommand(); $commands[] = new Command\User\UserDeleteCommand(); $commands[] = new Command\User\UserListCommand(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/src/Command/Auth/BrowserLoginCommand.php new/platformsh-cli-3.39.0/src/Command/Auth/BrowserLoginCommand.php --- old/platformsh-cli-3.38.0/src/Command/Auth/BrowserLoginCommand.php 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/src/Command/Auth/BrowserLoginCommand.php 2019-01-18 12:39:27.000000000 +0100 @@ -208,13 +208,18 @@ $this->stdErr->writeln('Login information received. Verifying...'); $token = $this->getAccessToken($code, $localUrl, $tokenUrl); - // Finalize login: clear the cache and save the new credentials. + // Finalize login: call logOut() on the old connector, clear the cache + // and save the new credentials. + $connector = $this->api()->getClient(false)->getConnector(); + $session = $connector->getSession(); + $connector->logOut(); + /** @var \Doctrine\Common\Cache\CacheProvider $cache */ $cache = $this->getService('cache'); $cache->flushAll(); // Save the new tokens to the persistent session. - $this->saveAccessToken($token, $this->api()->getClient(false)->getConnector()->getSession()); + $this->saveAccessToken($token, $session); // Reset the API client so that it will use the new tokens. $client = $this->api()->getClient(false, true); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/src/Command/CommandBase.php new/platformsh-cli-3.39.0/src/Command/CommandBase.php --- old/platformsh-cli-3.38.0/src/Command/CommandBase.php 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/src/Command/CommandBase.php 2019-01-18 12:39:27.000000000 +0100 @@ -974,7 +974,9 @@ // Select the environment. $envOptionName = 'environment'; - if ($input->hasArgument($this->envArgName) && $input->getArgument($this->envArgName) !== null) { + if ($input->hasArgument($this->envArgName) + && $input->getArgument($this->envArgName) !== null + && $input->getArgument($this->envArgName) !== []) { if ($input->hasOption($envOptionName) && $input->getOption($envOptionName) !== null) { throw new ConsoleInvalidArgumentException( sprintf( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/src/Command/Tunnel/TunnelCloseCommand.php new/platformsh-cli-3.39.0/src/Command/Tunnel/TunnelCloseCommand.php --- old/platformsh-cli-3.38.0/src/Command/Tunnel/TunnelCloseCommand.php 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/src/Command/Tunnel/TunnelCloseCommand.php 2019-01-18 12:39:27.000000000 +0100 @@ -20,7 +20,6 @@ protected function execute(InputInterface $input, OutputInterface $output) { - $this->checkSupport(); $tunnels = $this->getTunnelInfo(); $allTunnelsCount = count($tunnels); if (!$allTunnelsCount) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/src/Command/Tunnel/TunnelCommandBase.php new/platformsh-cli-3.39.0/src/Command/Tunnel/TunnelCommandBase.php --- old/platformsh-cli-3.38.0/src/Command/Tunnel/TunnelCommandBase.php 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/src/Command/Tunnel/TunnelCommandBase.php 2019-01-18 12:39:27.000000000 +0100 @@ -15,19 +15,6 @@ protected $tunnelInfo; protected $canBeRunMultipleTimes = false; - public function checkSupport() - { - $messages = []; - foreach (['pcntl', 'posix'] as $extension) { - if (!extension_loaded($extension)) { - $messages[] = sprintf('The "%s" extension is required.', $extension); - } - } - if (count($messages)) { - throw new \RuntimeException(implode("\n", $messages)); - } - } - /** * Check whether a tunnel is already open. * @@ -257,7 +244,7 @@ } $project = $this->getSelectedProject(); $environment = $this->hasSelectedEnvironment() ? $this->getSelectedEnvironment() : null; - $appName = $this->selectApp($input); + $appName = $this->hasSelectedEnvironment() ? $this->selectApp($input) : null; foreach ($tunnels as $key => $tunnel) { if ($tunnel['projectId'] !== $project->id || ($environment !== null && $tunnel['environmentId'] !== $environment->id) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/src/Command/Tunnel/TunnelInfoCommand.php new/platformsh-cli-3.39.0/src/Command/Tunnel/TunnelInfoCommand.php --- old/platformsh-cli-3.38.0/src/Command/Tunnel/TunnelInfoCommand.php 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/src/Command/Tunnel/TunnelInfoCommand.php 2019-01-18 12:39:27.000000000 +0100 @@ -23,9 +23,6 @@ protected function execute(InputInterface $input, OutputInterface $output) { - $this->checkSupport(); - $this->validateInput($input); - $tunnels = $this->getTunnelInfo(); $relationships = []; foreach ($this->filterTunnels($tunnels, $input) as $key => $tunnel) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/src/Command/Tunnel/TunnelListCommand.php new/platformsh-cli-3.39.0/src/Command/Tunnel/TunnelListCommand.php --- old/platformsh-cli-3.38.0/src/Command/Tunnel/TunnelListCommand.php 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/src/Command/Tunnel/TunnelListCommand.php 2019-01-18 12:39:27.000000000 +0100 @@ -23,7 +23,6 @@ protected function execute(InputInterface $input, OutputInterface $output) { - $this->checkSupport(); $tunnels = $this->getTunnelInfo(); $allTunnelsCount = count($tunnels); if (!$allTunnelsCount) { @@ -31,12 +30,19 @@ return 1; } + $executable = $this->config()->get('application.executable'); + // Filter tunnels according to the current project and environment, if // available. if (!$input->getOption('all')) { $tunnels = $this->filterTunnels($tunnels, $input); if (!count($tunnels)) { - $this->stdErr->writeln('No tunnels found. Use --all to view all tunnels.'); + $this->stdErr->writeln('No tunnels found.'); + $this->stdErr->writeln(sprintf( + 'List all tunnels with: <info>%s tunnels --all</info>', + $executable + )); + return 1; } } @@ -57,7 +63,6 @@ $table->render($rows, $headers); if (!$table->formatIsMachineReadable()) { - $executable = $this->config()->get('application.executable'); $this->stdErr->writeln(''); if (!$input->getOption('all') && count($tunnels) < $allTunnelsCount) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/src/Command/Tunnel/TunnelOpenCommand.php new/platformsh-cli-3.39.0/src/Command/Tunnel/TunnelOpenCommand.php --- old/platformsh-cli-3.38.0/src/Command/Tunnel/TunnelOpenCommand.php 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/src/Command/Tunnel/TunnelOpenCommand.php 2019-01-18 12:39:27.000000000 +0100 @@ -20,6 +20,19 @@ $this->addEnvironmentOption(); $this->addAppOption(); Ssh::configureInput($this->getDefinition()); + $this->setHelp(<<<EOF +This command opens SSH tunnels to all of the relationships of an application. + +Connections can then be made to the application's services as if they were +local, for example a local MySQL client can be used, or the Solr web +administration endpoint can be accessed through a local browser. + +This command requires the posix and pcntl PHP extensions (as multiple +background CLI processes are created to keep the SSH tunnels open). The +<info>tunnel:single</info> command can be used on systems without these +extensions. +EOF + ); } /** @@ -40,6 +53,7 @@ if (!$questionHelper->confirm($confirmText, false)) { return 1; } + $this->stdErr->writeln(''); } $appName = $this->selectApp($input); @@ -135,8 +149,8 @@ $this->saveTunnelInfo(); $this->stdErr->writeln(sprintf( - 'SSH tunnel opened on port %s to relationship: <info>%s</info>', - $localPort, + 'SSH tunnel opened on port <info>%s</info> to relationship: <info>%s</info>', + $tunnel['localPort'], $relationshipString )); $processIds[] = $pid; @@ -167,4 +181,17 @@ return 0; } + + private function checkSupport() + { + $messages = []; + foreach (['pcntl', 'posix'] as $extension) { + if (!extension_loaded($extension)) { + $messages[] = sprintf('The "%s" extension is required.', $extension); + } + } + if (count($messages)) { + throw new \RuntimeException(implode("\n", $messages)); + } + } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/src/Command/Tunnel/TunnelSingleCommand.php new/platformsh-cli-3.39.0/src/Command/Tunnel/TunnelSingleCommand.php --- old/platformsh-cli-3.38.0/src/Command/Tunnel/TunnelSingleCommand.php 1970-01-01 01:00:00.000000000 +0100 +++ new/platformsh-cli-3.39.0/src/Command/Tunnel/TunnelSingleCommand.php 2019-01-18 12:39:27.000000000 +0100 @@ -0,0 +1,202 @@ +<?php +namespace Platformsh\Cli\Command\Tunnel; + +use GuzzleHttp\Url; +use Platformsh\Cli\Service\Relationships; +use Platformsh\Cli\Service\Ssh; +use Platformsh\Cli\Console\ProcessManager; +use Platformsh\Cli\Util\PortUtil; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class TunnelSingleCommand extends TunnelCommandBase +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('tunnel:single') + ->setDescription('Open a single SSH tunnel to an app relationship') + ->addOption('port', null, InputOption::VALUE_REQUIRED, 'The local port'); + $this->addProjectOption(); + $this->addEnvironmentOption(); + $this->addAppOption(); + Relationships::configureInput($this->getDefinition()); + Ssh::configureInput($this->getDefinition()); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->validateInput($input); + $project = $this->getSelectedProject(); + $environment = $this->getSelectedEnvironment(); + + $appName = $this->selectApp($input); + $sshUrl = $environment->getSshUrl($appName); + + /** @var \Platformsh\Cli\Service\Relationships $relationshipsService */ + $relationshipsService = $this->getService('relationships'); + $relationships = $relationshipsService->getRelationships($sshUrl); + if (!$relationships) { + $this->stdErr->writeln('No relationships found.'); + return 1; + } + + $service = $relationshipsService->chooseService($sshUrl, $input, $output); + if (!$service) { + return 1; + } + + if ($environment->id === 'master') { + /** @var \Platformsh\Cli\Service\QuestionHelper $questionHelper */ + $questionHelper = $this->getService('question_helper'); + $confirmText = sprintf( + 'Are you sure you want to open an SSH tunnel to' + . ' the relationship <comment>%s</comment> on the' + . ' <comment>%s</comment> (production) environment?', + $service['_relationship_name'], + $environment->id + ); + if (!$questionHelper->confirm($confirmText, false)) { + return 1; + } + $this->stdErr->writeln(''); + } + + /** @var \Platformsh\Cli\Service\Ssh $ssh */ + $ssh = $this->getService('ssh'); + $sshArgs = $ssh->getSshArgs(); + + $remoteHost = $service['host']; + $remotePort = $service['port']; + + if ($localPort = $input->getOption('port')) { + if (!PortUtil::validatePort($localPort)) { + $this->stdErr->writeln(sprintf('Invalid port: <error>%s</error>', $localPort)); + + return 1; + } + if (PortUtil::isPortInUse($localPort)) { + $this->stdErr->writeln(sprintf('Port already in use: <error>%s</error>', $localPort)); + + return 1; + } + } else { + $localPort = $this->getPort(); + } + + $tunnel = [ + 'projectId' => $project->id, + 'environmentId' => $environment->id, + 'appName' => $appName, + 'relationship' => $service['_relationship_name'], + 'serviceKey' => $service['_relationship_key'], + 'remotePort' => $remotePort, + 'remoteHost' => $remoteHost, + 'localPort' => $localPort, + 'service' => $service, + 'pid' => null, + ]; + + $relationshipString = $this->formatTunnelRelationship($tunnel); + + if ($openTunnelInfo = $this->isTunnelOpen($tunnel)) { + $this->stdErr->writeln(sprintf( + 'A tunnel is already open for the relationship <info>%s</info> (on port %s)', + $relationshipString, + $openTunnelInfo['localPort'] + )); + + return 1; + } + + $pidFile = $this->getPidFile($tunnel); + + $processManager = new ProcessManager(); + $process = $this->createTunnelProcess($sshUrl, $remoteHost, $remotePort, $localPort, $sshArgs); + $pid = $processManager->startProcess($process, $pidFile, $this->stdErr); + + // Wait a very small time to capture any immediate errors. + usleep(100000); + if (!$process->isRunning() && !$process->isSuccessful()) { + $this->stdErr->writeln(trim($process->getErrorOutput())); + $this->stdErr->writeln(sprintf( + 'Failed to open tunnel for relationship: <error>%s</error>', + $relationshipString + )); + unlink($pidFile); + + return 1; + } + + $tunnel['pid'] = $pid; + $this->tunnelInfo[] = $tunnel; + $this->saveTunnelInfo(); + + $this->stdErr->writeln(''); + + $this->stdErr->writeln(sprintf( + 'SSH tunnel opened on port %s to relationship: <info>%s</info>', + $tunnel['localPort'], + $relationshipString + )); + + $localService = array_merge($service, array_intersect_key([ + 'host' => self::LOCAL_IP, + 'port' => $tunnel['localPort'], + ], $service)); + $info = [ + 'username' => 'Username', + 'password' => 'Password', + 'scheme' => 'Scheme', + 'host' => 'Host', + 'port' => 'Port', + 'path' => 'Path', + ]; + foreach ($info as $key => $category) { + if (isset($localService[$key])) { + $this->stdErr->writeln(sprintf(' <info>%s</info>: %s', $category, $localService[$key])); + } + } + + $this->stdErr->writeln(''); + + if (isset($localService['scheme']) && in_array($localService['scheme'], ['http', 'https'], true)) { + $this->stdErr->writeln(sprintf('URL: <info>%s</info>', $this->getServiceUrl($localService))); + $this->stdErr->writeln(''); + } + + $this->stdErr->writeln('Quitting this command (with Ctrl+C or equivalent) will close the tunnel.'); + + $this->stdErr->writeln(''); + + $processManager->monitor($this->stdErr); + + return $process->isSuccessful() ? 0 : 1; + } + + /** + * Build a URL to a service. + * + * @param array $service + * + * @return string + */ + private function getServiceUrl(array $service) + { + $map = ['username' => 'user', 'password' => 'pass']; + $urlParts = []; + foreach ($service as $key => $value) { + $newKey = isset($map[$key]) ? $map[$key] : $key; + $urlParts[$newKey] = $value; + } + + return Url::buildUrl($urlParts); + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/src/Console/ProcessManager.php new/platformsh-cli-3.39.0/src/Console/ProcessManager.php --- old/platformsh-cli-3.38.0/src/Console/ProcessManager.php 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/src/Console/ProcessManager.php 2019-01-18 12:39:27.000000000 +0100 @@ -163,11 +163,17 @@ // If the process has been stopped via another method, remove it // from the list, and log a message. $exitCode = $process->getExitCode(); - if ($exitCode === 143 || $exitCode === 147) { - $log->writeln(sprintf('Process killed: %s', $process->getCommandLine())); + if ($signal = $this->getSignal($exitCode)) { + $log->writeln(sprintf('Process stopped with signal %s: %s', $signal, $process->getCommandLine())); } elseif ($exitCode > 0) { $log->writeln(sprintf( - 'Process stopped unexpectedly with exit code %s: %s', + 'Process failed with exit code %s: %s', + $exitCode, + $process->getCommandLine() + )); + } else { + $log->writeln(sprintf( + 'Process stopped with exit code %s: %s', $exitCode, $process->getCommandLine() )); @@ -178,4 +184,55 @@ } } } + + /** + * @param int $exitCode + * + * @return string|false + */ + private function getSignal($exitCode) + { + if ($exitCode < 128 || $exitCode > 162) { + return false; + } + + $signals = [ + 1 => 'SIGHUP', + 2 => 'SIGINT', + 3 => 'SIGQUIT', + 4 => 'SIGILL', + 5 => 'SIGTRAP', + 6 => 'SIGABRT', + 7 => 'SIGEMT', + 8 => 'SIGFPE', + 9 => 'SIGKILL', + 10 => 'SIGBUS', + 11 => 'SIGSEGV', + 12 => 'SIGSYS', + 13 => 'SIGPIPE', + 14 => 'SIGALRM', + 15 => 'SIGTERM', + 16 => 'SIGUSR1', + 17 => 'SIGUSR2', + 18 => 'SIGCHLD', + 19 => 'SIGPWR', + 20 => 'SIGWINCH', + 21 => 'SIGURG', + 22 => 'SIGPOLL', + 23 => 'SIGSTOP', + 24 => 'SIGTSTP', + 25 => 'SIGCONT', + 26 => 'SIGTTIN', + 27 => 'SIGTTOU', + 28 => 'SIGVTALRM', + 29 => 'SIGPROF', + 30 => 'SIGXCPU', + 31 => 'SIGXFSZ', + 32 => 'SIGWAITING', + 33 => 'SIGLWP', + 34 => 'SIGAIO', + ]; + + return $signals[$exitCode - 128]; + } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/src/Service/Drush.php new/platformsh-cli-3.39.0/src/Service/Drush.php --- old/platformsh-cli-3.38.0/src/Service/Drush.php 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/src/Service/Drush.php 2019-01-18 12:39:27.000000000 +0100 @@ -255,18 +255,6 @@ } /** - * @return string - */ - protected function getAutoRemoveKey() - { - return preg_replace( - '/[^a-z-]+/', - '-', - str_replace('.', '', strtolower($this->config->get('application.name'))) - ) . '-auto-remove'; - } - - /** * Get the alias group for a project. * * @param Project $project diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/src/Service/Relationships.php new/platformsh-cli-3.39.0/src/Service/Relationships.php --- old/platformsh-cli-3.38.0/src/Service/Relationships.php 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/src/Service/Relationships.php 2019-01-18 12:39:27.000000000 +0100 @@ -75,31 +75,66 @@ } if (empty($relationships)) { - $stdErr->writeln(sprintf('No relationships found matching scheme(s): <error>%s</error>.', implode(', ', $schemes))); + if (!empty($schemes)) { + $stdErr->writeln(sprintf('No relationships found matching scheme(s): <error>%s</error>.', implode(', ', $schemes))); + } else { + $stdErr->writeln(sprintf('No relationships found')); + } return false; } + // Collapse relationships and services into a flat list. + $choices = []; + foreach ($relationships as $name => $relationship) { + $serviceCount = count($relationship); + foreach ($relationship as $key => $service) { + $identifier = $name . ($serviceCount > 1 ? '.' . $key : ''); + $choices[$identifier] = $identifier; + } + } + // Use the --relationship option, if specified. + $identifier = false; if ($input->hasOption('relationship') && ($relationshipName = $input->getOption('relationship'))) { - if (!isset($relationships[$relationshipName])) { - $stdErr->writeln('Relationship not found: ' . $relationshipName); + // Normalise the relationship name to remove a trailing ".0". + if (substr($relationshipName, -2) === '.0') { + $relationshipName = substr($relationshipName, 0, strlen($relationshipName) - 2); + } + if (!isset($choices[$relationshipName])) { + $stdErr->writeln('Relationship not found: <error>' . $relationshipName . '</error>'); return false; } - $relationships = array_intersect_key($relationships, [$relationshipName => true]); + $identifier = $relationshipName; } - $questionHelper = new QuestionHelper($input, $output); - $choices = []; - $separator = '.'; - foreach ($relationships as $name => $relationship) { - $serviceCount = count($relationship); - foreach ($relationship as $key => $service) { - $choices[$name . $separator . $key] = $name . ($serviceCount > 1 ? '.' . $key : ''); + if (!$identifier && count($choices) === 1) { + $identifier = reset($choices); + } + + if (!$identifier && !$input->isInteractive()) { + $stdErr->writeln('More than one relationship found.'); + if ($input->hasOption('relationship')) { + $stdErr->writeln('Use the <error>--relationship</error> (-r) option to specify a relationship. Options:'); + foreach (array_keys($choices) as $identifier) { + $stdErr->writeln(' ' . $identifier); + } } + return false; + } + + if (!$identifier) { + $questionHelper = new QuestionHelper($input, $output); + $identifier = $questionHelper->choose($choices, 'Enter a number to choose a relationship:'); + $stdErr->writeln(''); + } + + if (strpos($identifier, '.') !== false) { + list($name, $key) = explode('.', $identifier, 2); + } else { + $name = $identifier; + $key = 0; } - $choice = $questionHelper->choose($choices, 'Enter a number to choose a relationship:'); - list($name, $key) = explode($separator, $choice, 2); $service = $relationships[$name][$key]; // Add metadata about the service. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/src/SiteAlias/DrushAlias.php new/platformsh-cli-3.39.0/src/SiteAlias/DrushAlias.php --- old/platformsh-cli-3.38.0/src/SiteAlias/DrushAlias.php 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/src/SiteAlias/DrushAlias.php 2019-01-18 12:39:27.000000000 +0100 @@ -60,7 +60,7 @@ $autoRemoveKey = $this->getAutoRemoveKey(); $userDefinedAliases = []; foreach ($existingAliases as $name => $alias) { - if (!empty($alias[$autoRemoveKey])) { + if (!empty($alias[$autoRemoveKey]) || !empty($alias['options'][$autoRemoveKey])) { // This is probably for a deleted environment. continue; } @@ -220,7 +220,9 @@ { return [ 'root' => $app->getLocalWebRoot(), - $this->getAutoRemoveKey() => true, + 'options' => [ + $this->getAutoRemoveKey() => true, + ], ]; } @@ -238,9 +240,13 @@ return false; } + // The 'root' can be a relative path, relative to the home directory. + // Conveniently, the home directory is the same as the app root. $alias = [ - 'root' => '/app/' . $app->getDocumentRoot(), - $this->getAutoRemoveKey() => true, + 'root' => $app->getDocumentRoot(), + 'options' => [ + $this->getAutoRemoveKey() => true, + ], ]; $sshUrl = $environment->getSshUrl($app->getName()); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/src/SiteAlias/DrushYaml.php new/platformsh-cli-3.39.0/src/SiteAlias/DrushYaml.php --- old/platformsh-cli-3.38.0/src/SiteAlias/DrushYaml.php 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/src/SiteAlias/DrushYaml.php 2019-01-18 12:39:27.000000000 +0100 @@ -12,13 +12,6 @@ */ protected function getFilename($groupName) { - // Preserve backwards compatibility for Drush 9-beta. - // See issue https://github.com/platformsh/platformsh-cli/issues/655 - $version = $this->drush->getVersion(); - if ($version !== false && version_compare($version, '8', '>') && version_compare($version, '9.0.0-rc1', '<')) { - return $this->drush->getSiteAliasDir() . '/' . $groupName . '.alias.yml'; - } - return $this->drush->getSiteAliasDir() . '/' . $groupName . '.site.yml'; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.38.0/tests/Service/DrushServiceTest.php new/platformsh-cli-3.39.0/tests/Service/DrushServiceTest.php --- old/platformsh-cli-3.38.0/tests/Service/DrushServiceTest.php 2019-01-09 15:57:31.000000000 +0100 +++ new/platformsh-cli-3.39.0/tests/Service/DrushServiceTest.php 2019-01-18 12:39:27.000000000 +0100 @@ -75,8 +75,8 @@ $this->assertArrayHasKey('_local', $aliases); // Check that YAML aliases exist. - $this->assertFileExists($homeDir . '/.drush/site-aliases/test.alias.yml'); - $aliases = Yaml::parse(file_get_contents($homeDir . '/.drush/site-aliases/test.alias.yml')); + $this->assertFileExists($homeDir . '/.drush/site-aliases/test.site.yml'); + $aliases = Yaml::parse(file_get_contents($homeDir . '/.drush/site-aliases/test.site.yml')); $this->assertArrayHasKey('master', $aliases); $this->assertArrayHasKey('_local', $aliases); } @@ -145,8 +145,8 @@ $this->assertArrayHasKey('_local--drupal2', $aliases); // Check that YAML aliases exist. - $this->assertFileExists($homeDir . '/.drush/site-aliases/test.alias.yml'); - $aliases = Yaml::parse(file_get_contents($homeDir . '/.drush/site-aliases/test.alias.yml')); + $this->assertFileExists($homeDir . '/.drush/site-aliases/test.site.yml'); + $aliases = Yaml::parse(file_get_contents($homeDir . '/.drush/site-aliases/test.site.yml')); $this->assertArrayHasKey('master--drupal1', $aliases); $this->assertArrayHasKey('_local--drupal1', $aliases); $this->assertArrayHasKey('master--drupal2', $aliases); ++++++ platformsh-cli-vendor.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/autoload.php new/vendor/autoload.php --- old/vendor/autoload.php 2019-01-09 19:05:00.765710176 +0100 +++ new/vendor/autoload.php 2019-01-18 17:06:36.218319811 +0100 @@ -4,4 +4,4 @@ require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInit434b3fb8d6425b9c0fdd81e20c14d3f7::getLoader(); +return ComposerAutoloaderInitf77d77365fc1f7ea6d2d32ddf8a94760::getLoader(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/composer/autoload_real.php new/vendor/composer/autoload_real.php --- old/vendor/composer/autoload_real.php 2019-01-09 19:05:00.765710176 +0100 +++ new/vendor/composer/autoload_real.php 2019-01-18 17:06:36.218319811 +0100 @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit434b3fb8d6425b9c0fdd81e20c14d3f7 +class ComposerAutoloaderInitf77d77365fc1f7ea6d2d32ddf8a94760 { private static $loader; @@ -19,15 +19,15 @@ return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit434b3fb8d6425b9c0fdd81e20c14d3f7', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInitf77d77365fc1f7ea6d2d32ddf8a94760', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); - spl_autoload_unregister(array('ComposerAutoloaderInit434b3fb8d6425b9c0fdd81e20c14d3f7', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInitf77d77365fc1f7ea6d2d32ddf8a94760', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { require_once __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInit434b3fb8d6425b9c0fdd81e20c14d3f7::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInitf77d77365fc1f7ea6d2d32ddf8a94760::getInitializer($loader)); } else { $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { @@ -48,19 +48,19 @@ $loader->register(true); if ($useStaticLoader) { - $includeFiles = Composer\Autoload\ComposerStaticInit434b3fb8d6425b9c0fdd81e20c14d3f7::$files; + $includeFiles = Composer\Autoload\ComposerStaticInitf77d77365fc1f7ea6d2d32ddf8a94760::$files; } else { $includeFiles = require __DIR__ . '/autoload_files.php'; } foreach ($includeFiles as $fileIdentifier => $file) { - composerRequire434b3fb8d6425b9c0fdd81e20c14d3f7($fileIdentifier, $file); + composerRequiref77d77365fc1f7ea6d2d32ddf8a94760($fileIdentifier, $file); } return $loader; } } -function composerRequire434b3fb8d6425b9c0fdd81e20c14d3f7($fileIdentifier, $file) +function composerRequiref77d77365fc1f7ea6d2d32ddf8a94760($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { require $file; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/composer/autoload_static.php new/vendor/composer/autoload_static.php --- old/vendor/composer/autoload_static.php 2019-01-09 19:05:00.765710176 +0100 +++ new/vendor/composer/autoload_static.php 2019-01-18 17:06:36.218319811 +0100 @@ -4,7 +4,7 @@ namespace Composer\Autoload; -class ComposerStaticInit434b3fb8d6425b9c0fdd81e20c14d3f7 +class ComposerStaticInitf77d77365fc1f7ea6d2d32ddf8a94760 { public static $files = array ( '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', @@ -201,9 +201,9 @@ public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInit434b3fb8d6425b9c0fdd81e20c14d3f7::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInit434b3fb8d6425b9c0fdd81e20c14d3f7::$prefixDirsPsr4; - $loader->classMap = ComposerStaticInit434b3fb8d6425b9c0fdd81e20c14d3f7::$classMap; + $loader->prefixLengthsPsr4 = ComposerStaticInitf77d77365fc1f7ea6d2d32ddf8a94760::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInitf77d77365fc1f7ea6d2d32ddf8a94760::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInitf77d77365fc1f7ea6d2d32ddf8a94760::$classMap; }, null, ClassLoader::class); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/composer/installed.json new/vendor/composer/installed.json --- old/vendor/composer/installed.json 2019-01-09 19:05:00.345706414 +0100 +++ new/vendor/composer/installed.json 2019-01-18 17:06:35.814316193 +0100 @@ -731,17 +731,17 @@ }, { "name": "platformsh/client", - "version": "v0.23.0", - "version_normalized": "0.23.0.0", + "version": "v0.23.2", + "version_normalized": "0.23.2.0", "source": { "type": "git", "url": "https://github.com/platformsh/platformsh-client-php.git", - "reference": "9b0fc3004d5abdc3cc54b0bea80f779e23660289" + "reference": "028f723d4581b09c28f2ab5029b51a2e9363bd15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/platformsh/platformsh-client-php/zipball/9b0fc3004d5abdc3cc54b0bea80f779e23660289", - "reference": "9b0fc3004d5abdc3cc54b0bea80f779e23660289", + "url": "https://api.github.com/repos/platformsh/platformsh-client-php/zipball/028f723d4581b09c28f2ab5029b51a2e9363bd15", + "reference": "028f723d4581b09c28f2ab5029b51a2e9363bd15", "shasum": "" }, "require": { @@ -754,7 +754,7 @@ "require-dev": { "phpunit/phpunit": "~4.5" }, - "time": "2019-01-09T14:36:49+00:00", + "time": "2019-01-16T17:37:27+00:00", "type": "library", "extra": { "patches": { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/platformsh/client/src/Connection/Connector.php new/vendor/platformsh/client/src/Connection/Connector.php --- old/vendor/platformsh/client/src/Connection/Connector.php 2019-01-09 15:36:49.000000000 +0100 +++ new/vendor/platformsh/client/src/Connection/Connector.php 2019-01-16 18:37:27.000000000 +0100 @@ -33,9 +33,6 @@ /** @var SessionInterface */ protected $session; - /** @var bool */ - protected $loggedOut = false; - /** * @param array $config * Possible configuration keys are: @@ -117,7 +114,7 @@ */ public function logOut() { - $this->loggedOut = true; + $this->oauth2Plugin = null; try { $this->revokeTokens(); @@ -156,9 +153,7 @@ public function __destruct() { - if ($this->loggedOut) { - $this->session->clear(); - } elseif ($this->oauth2Plugin) { + if ($this->oauth2Plugin) { // Save the access token for future requests. $token = $this->getOauth2Plugin()->getAccessToken(false); if ($token !== null) { @@ -180,10 +175,10 @@ */ public function logIn($username, $password, $force = false, $totp = null) { - $this->loggedOut = false; if (!$force && $this->isLoggedIn() && $this->session->get('username') === $username) { return; } + $this->logOut(); $client = $this->getGuzzleClient([ 'base_url' => $this->config['accounts'], 'defaults' => [