Hello community, here is the log from the commit of package platformsh-cli for openSUSE:Factory checked in at 2017-10-19 19:33:02 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/platformsh-cli (Old) and /work/SRC/openSUSE:Factory/.platformsh-cli.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "platformsh-cli" Thu Oct 19 19:33:02 2017 rev:21 rq:534961 version:3.21.0 Changes: -------- --- /work/SRC/openSUSE:Factory/platformsh-cli/platformsh-cli.changes 2017-10-17 01:52:13.711248171 +0200 +++ /work/SRC/openSUSE:Factory/.platformsh-cli.new/platformsh-cli.changes 2017-10-19 19:33:04.702233062 +0200 @@ -1,0 +2,20 @@ +Wed Oct 18 13:52:28 UTC 2017 - [email protected] + +- Update to version 3.21.0: + * Fix installer bug writing multiple times to config + * Fix installer bug writing multiple times to config + * Use full namespace name in list command + * add mount:list (lists) command + * add mount:pull (mpull) and mount:push (mpush) commands + * mount commands: use selected environment + * mount commands: various improvements + * Add a confirmation question and another interactive default for --target and --source + * Rename mount commands + * mount commands: restore --refresh option, add --delete option + * List mounts as a table + * Support logging in with a username (#636) + * Fix: service dependencies of Filesystem did not get output class, initialized too early + * Switch default output to ConsoleOutput + * Release v3.21.0 + +------------------------------------------------------------------- Old: ---- platformsh-cli-3.20.5.tar.xz New: ---- platformsh-cli-3.21.0.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ platformsh-cli.spec ++++++ --- /var/tmp/diff_new_pack.4PVYHL/_old 2017-10-19 19:33:05.378201435 +0200 +++ /var/tmp/diff_new_pack.4PVYHL/_new 2017-10-19 19:33:05.378201435 +0200 @@ -17,7 +17,7 @@ Name: platformsh-cli -Version: 3.20.5 +Version: 3.21.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.4PVYHL/_old 2017-10-19 19:33:05.406200125 +0200 +++ /var/tmp/diff_new_pack.4PVYHL/_new 2017-10-19 19:33:05.406200125 +0200 @@ -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.20.5</param> + <param name="revision">refs/tags/v3.21.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.4PVYHL/_old 2017-10-19 19:33:05.422199376 +0200 +++ /var/tmp/diff_new_pack.4PVYHL/_new 2017-10-19 19:33:05.422199376 +0200 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">git://github.com/platformsh/platformsh-cli.git</param> - <param name="changesrevision">f8e216ec75f1a2f6fdf3e4765c60b624fda06ea3</param> + <param name="changesrevision">0bbbd4c416e3474f27488107cb8a49b6ddeb9100</param> </service> </servicedata> ++++++ platformsh-cli-3.20.5.tar.xz -> platformsh-cli-3.21.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.20.5/README.md new/platformsh-cli-3.21.0/README.md --- old/platformsh-cli-3.20.5/README.md 2017-10-13 10:57:00.000000000 +0200 +++ new/platformsh-cli-3.21.0/README.md 2017-10-18 10:41:52.000000000 +0200 @@ -126,6 +126,10 @@ local:build (build) Build the current project locally local:dir (dir) Find the local project root local:drush-aliases (drush-aliases) Find the project's Drush aliases +mount + mount:download Download files from a mount, using rsync + mount:list (mounts) Get a list of mounts + mount:upload Upload files to a mount, using rsync project project:delete Delete a project project:get (get) Clone a project locally diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.20.5/config.yaml new/platformsh-cli-3.21.0/config.yaml --- old/platformsh-cli-3.20.5/config.yaml 2017-10-13 10:57:00.000000000 +0200 +++ new/platformsh-cli-3.21.0/config.yaml 2017-10-18 10:41:52.000000000 +0200 @@ -1,7 +1,7 @@ # Metadata about the CLI application itself. application: name: 'Platform.sh CLI' - version: '3.20.5' + version: '3.21.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.20.5/dist/installer.php new/platformsh-cli-3.21.0/dist/installer.php --- old/platformsh-cli-3.20.5/dist/installer.php 2017-10-13 10:57:00.000000000 +0200 +++ new/platformsh-cli-3.21.0/dist/installer.php 2017-10-18 10:41:52.000000000 +0200 @@ -293,7 +293,7 @@ if (!$currentShellConfig = file_get_contents($shellConfigFile)) { return false; } - if (strpos($key, $currentShellConfig) !== false) { + if (strpos($currentShellConfig, $key) !== false) { return true; } $newShellConfig = rtrim($currentShellConfig, PHP_EOL) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.20.5/services.yaml new/platformsh-cli-3.21.0/services.yaml --- old/platformsh-cli-3.20.5/services.yaml 2017-10-13 10:57:00.000000000 +0200 +++ new/platformsh-cli-3.21.0/services.yaml 2017-10-18 10:41:52.000000000 +0200 @@ -13,9 +13,6 @@ class: '\Platformsh\Cli\Service\CacheFactory' config: class: '\Platformsh\Cli\Service\Config' - calls: - - method: setFs - arguments: ['@fs'] drush: class: '\Platformsh\Cli\Service\Drush' arguments: ['@config', '@shell'] @@ -35,7 +32,7 @@ class: '\Platformsh\Cli\Local\LocalProject' arguments: ['@config', '@git'] output: - class: '\Symfony\Component\Console\Output\NullOutput' + class: '\Symfony\Component\Console\Output\ConsoleOutput' property_formatter: class: '\Platformsh\Cli\Service\PropertyFormatter' arguments: ['@input'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.20.5/src/Application.php new/platformsh-cli-3.21.0/src/Application.php --- old/platformsh-cli-3.20.5/src/Application.php 2017-10-13 10:57:00.000000000 +0200 +++ new/platformsh-cli-3.21.0/src/Application.php 2017-10-18 10:41:52.000000000 +0200 @@ -134,6 +134,9 @@ $commands[] = new Command\Local\LocalCleanCommand(); $commands[] = new Command\Local\LocalDrushAliasesCommand(); $commands[] = new Command\Local\LocalDirCommand(); + $commands[] = new Command\Mount\MountListCommand(); + $commands[] = new Command\Mount\MountDownloadCommand(); + $commands[] = new Command\Mount\MountUploadCommand(); $commands[] = new Command\Project\ProjectCurlCommand(); $commands[] = new Command\Project\ProjectCreateCommand(); $commands[] = new Command\Project\ProjectDeleteCommand(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.20.5/src/Command/Auth/LoginCommand.php new/platformsh-cli-3.21.0/src/Command/Auth/LoginCommand.php --- old/platformsh-cli-3.20.5/src/Command/Auth/LoginCommand.php 2017-10-13 10:57:00.000000000 +0200 +++ new/platformsh-cli-3.21.0/src/Command/Auth/LoginCommand.php 2017-10-18 10:41:52.000000000 +0200 @@ -48,9 +48,13 @@ $cache->flushAll(); $info = $this->api()->getClient(false)->getAccountInfo(); - if (isset($info['mail'])) { + if (isset($info['username'], $info['mail'])) { $this->stdErr->writeln(''); - $this->stdErr->writeln('You are logged in as <info>' . $info['mail'] . '</info>.'); + $this->stdErr->writeln(sprintf( + 'You are logged in as <info>%s</info> (%s).', + $info['username'], + $info['mail'] + )); } } @@ -59,18 +63,8 @@ /** @var \Platformsh\Cli\Service\QuestionHelper $questionHelper */ $questionHelper = $this->getService('question_helper'); - $question = new Question('Your email address: '); - $question->setValidator( - function ($answer) { - if (empty($answer) || !filter_var($answer, FILTER_VALIDATE_EMAIL)) { - throw new \RuntimeException( - 'Please provide a valid email address.' - ); - } - - return $answer; - } - ); + $question = new Question('Your email address or username: '); + $question->setValidator([$this, 'validateUsernameOrEmail']); $question->setMaxAttempts(5); $email = $questionHelper->ask($input, $output, $question); @@ -139,4 +133,35 @@ } } } + + /** + * Validation callback for the username or email address. + * + * @param string $username + * + * @return string + */ + public function validateUsernameOrEmail($username) + { + $username = trim($username); + if (!strlen($username) || (!filter_var($username, FILTER_VALIDATE_EMAIL) && !$this->validateUsername($username))) { + throw new \RuntimeException( + 'Please enter a valid email address or username.' + ); + } + + return $username; + } + + /** + * Validate a username. + * + * @param string $username + * + * @return bool + */ + protected function validateUsername($username) + { + return preg_match('/^[a-z0-9][a-z0-9-]{0,30}[a-z0-9]$/', $username) === 1; + } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.20.5/src/Command/Mount/MountCommandBase.php new/platformsh-cli-3.21.0/src/Command/Mount/MountCommandBase.php --- old/platformsh-cli-3.20.5/src/Command/Mount/MountCommandBase.php 1970-01-01 01:00:00.000000000 +0100 +++ new/platformsh-cli-3.21.0/src/Command/Mount/MountCommandBase.php 2017-10-18 10:41:52.000000000 +0200 @@ -0,0 +1,184 @@ +<?php + +namespace Platformsh\Cli\Command\Mount; + +use Platformsh\Cli\Command\CommandBase; +use Symfony\Component\Console\Output\OutputInterface; + +abstract class MountCommandBase extends CommandBase +{ + + /** + * Get the remote application config. + * + * @param string $sshUrl + * @param bool $refresh + * + * @return array + */ + protected function getAppConfig($sshUrl, $refresh = true) + { + /** @var \Platformsh\Cli\Service\RemoteEnvVars $envVarService */ + $envVarService = $this->getService('remote_env_vars'); + + $result = $envVarService->getEnvVar('APPLICATION', $sshUrl, $refresh); + + return (array) json_decode(base64_decode($result), true); + } + + /** + * Format the mounts as an array of options for a ChoiceQuestion. + * + * @param array $mounts + * + * @return array + */ + protected function getMountsAsOptions(array $mounts) + { + $options = []; + foreach ($mounts as $path => $id) { + $normalized = $this->normalizeMountPath($path); + $options[$normalized] = sprintf('<question>%s</question>: %s', $normalized, trim($id, '/')); + } + + return $options; + } + + /** + * Get the path under '.platform/local/shared' for a mount. + * + * @param string $path + * @param array $mounts + * + * @return string|false + */ + protected function getSharedPath($path, array $mounts) + { + $normalized = $this->normalizeMountPath($path); + foreach ($mounts as $path => $uri) { + if ($this->normalizeMountPath($path) === $normalized + && preg_match('#^shared:files/(.+)$#', $uri, $matches)) { + return trim($matches[1], '/'); + } + } + + return false; + } + + /** + * Normalize a path to a mount. + * + * @param string $path + * + * @return string + */ + protected function normalizeMountPath($path) + { + return trim(trim($path), '/'); + } + + /** + * Validate and normalize a path to a mount. + * + * @param string $inputPath + * @param array $mounts + * + * @return string + * The normalized mount path. + */ + protected function validateMountPath($inputPath, array $mounts) + { + $normalized = trim(trim($inputPath), '/'); + foreach (array_keys($mounts) as $path) { + if (trim(trim($path), '/') === $normalized) { + return $normalized; + } + } + + throw new \InvalidArgumentException(sprintf('Mount not found: <error>%s</error>', $inputPath)); + } + + /** + * Validate a directory argument. + * + * @param string $directory + * @param bool $writable + */ + protected function validateDirectory($directory, $writable = false) + { + if (!is_dir($directory)) { + throw new \InvalidArgumentException(sprintf('Directory not found: %s', $directory)); + } elseif (!is_readable($directory)) { + throw new \InvalidArgumentException(sprintf('Directory not readable: %s', $directory)); + } elseif ($writable && !is_writable($directory)) { + throw new \InvalidArgumentException(sprintf('Directory not writable: %s', $directory)); + } + } + + /** + * Push the local contents to the chosen mount. + * + * @param string $sshUrl + * @param string $mountPath + * @param string $localPath + * @param bool $up + * @param bool $delete + */ + protected function runSync($sshUrl, $mountPath, $localPath, $up, $delete = false) + { + /** @var \Platformsh\Cli\Service\Shell $shell */ + $shell = $this->getService('shell'); + + $mountPathAbsolute = $this->getAppDir($sshUrl) . '/' . $mountPath; + + if ($up) { + $this->stdErr->writeln(sprintf('Uploading files from <info>%s</info> to the remote mount <info>%s</info>', $localPath, $mountPathAbsolute)); + } else { + $this->stdErr->writeln(sprintf('Downloading files from the remote mount <info>%s</info> to <info>%s</info>', $mountPathAbsolute, $localPath)); + } + + $params = ['rsync', '--archive', '--compress', '--human-readable']; + + if ($this->stdErr->isVeryVerbose()) { + $params[] = '-vv'; + } elseif (!$this->stdErr->isQuiet()) { + $params[] = '-v'; + } + + if ($up) { + $params[] = rtrim($localPath, '/') . '/'; + $params[] = sprintf('%s:%s', $sshUrl, $mountPathAbsolute); + } else { + $params[] = sprintf('%s:%s/', $sshUrl, $mountPathAbsolute); + $params[] = $localPath; + } + + if ($delete) { + $params[] = '--delete'; + } + + $start = microtime(true); + $shell->execute($params, null, true, false, [], null); + + $this->stdErr->writeln(sprintf(' time: %ss', number_format(microtime(true) - $start, 2)), OutputInterface::VERBOSITY_NORMAL); + + if ($up) { + $this->stdErr->writeln('The upload completed successfully.'); + } else { + $this->stdErr->writeln('The download completed successfully.'); + } + } + + /** + * @param string $sshUrl + * + * @return string + */ + private function getAppDir($sshUrl) + { + /** @var \Platformsh\Cli\Service\RemoteEnvVars $envVarService */ + $envVarService = $this->getService('remote_env_vars'); + + return $envVarService->getEnvVar('APP_DIR', $sshUrl, false, 86400) ?: '/app'; + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.20.5/src/Command/Mount/MountDownloadCommand.php new/platformsh-cli-3.21.0/src/Command/Mount/MountDownloadCommand.php --- old/platformsh-cli-3.20.5/src/Command/Mount/MountDownloadCommand.php 1970-01-01 01:00:00.000000000 +0100 +++ new/platformsh-cli-3.21.0/src/Command/Mount/MountDownloadCommand.php 2017-10-18 10:41:52.000000000 +0200 @@ -0,0 +1,120 @@ +<?php + +namespace Platformsh\Cli\Command\Mount; + +use Platformsh\Cli\Local\LocalApplication; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\Question; + +class MountDownloadCommand extends MountCommandBase +{ + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('mount:download') + ->setDescription('Download files from a mount, using rsync') + ->addOption('mount', 'm', InputOption::VALUE_REQUIRED, 'The mount (as an app-relative path)') + ->addOption('target', null, InputOption::VALUE_REQUIRED, 'The directory to which files will be downloaded') + ->addOption('delete', null, InputOption::VALUE_NONE, 'Whether to delete extraneous files in the target directory') + ->addOption('refresh', null, InputOption::VALUE_NONE, 'Whether to refresh the cache'); + $this->addProjectOption(); + $this->addEnvironmentOption(); + $this->addAppOption(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->validateInput($input); + + $appName = $this->selectApp($input); + $sshUrl = $this->getSelectedEnvironment() + ->getSshUrl($appName); + + $appConfig = $this->getAppConfig($sshUrl, (bool) $input->getOption('refresh')); + + if (empty($appConfig['mounts'])) { + $this->stdErr->writeln(sprintf('The app "%s" doesn\'t define any mounts.', $appConfig['name'])); + + return 1; + } + + /** @var \Platformsh\Cli\Service\QuestionHelper $questionHelper */ + $questionHelper = $this->getService('question_helper'); + /** @var \Platformsh\Cli\Service\Filesystem $fs */ + $fs = $this->getService('fs'); + + if ($input->getOption('mount')) { + $mountPath = $this->validateMountPath($input->getOption('mount'), $appConfig['mounts']); + } elseif ($input->isInteractive()) { + $mountPath = $questionHelper->choose( + $this->getMountsAsOptions($appConfig['mounts']), + 'Enter a number to choose a mount to download from:' + ); + } else { + $this->stdErr->writeln('The <error>--mount</error> option must be specified (in non-interactive mode).'); + + return 1; + } + + $target = null; + $defaultTarget = null; + if ($input->getOption('target')) { + $target = $input->getOption('target'); + } elseif ($projectRoot = $this->getProjectRoot()) { + if ($sharedPath = $this->getSharedPath($mountPath, $appConfig['mounts'])) { + if (file_exists($projectRoot . '/' . $this->config()->get('local.shared_dir') . '/' . $sharedPath)) { + $defaultTarget = $projectRoot . '/' . $this->config()->get('local.shared_dir') . '/' . $sharedPath; + } + } + + $applications = LocalApplication::getApplications($projectRoot, $this->config()); + $appPath = $projectRoot; + foreach ($applications as $path => $candidateApp) { + if ($candidateApp->getName() === $appName) { + $appPath = $path; + break; + } + } + if (is_dir($appPath . '/' . $mountPath)) { + $defaultTarget = $appPath . '/' . $mountPath; + } + } + + if (empty($target) && $input->isInteractive()) { + $questionText = 'Target directory'; + if ($defaultTarget !== null) { + $formattedDefaultTarget = $fs->formatPathForDisplay($defaultTarget); + $questionText .= ' <question>[' . $formattedDefaultTarget . ']</question>'; + } + $questionText .= ': '; + $target = $questionHelper->ask($input, $this->stdErr, new Question($questionText, $defaultTarget)); + } + + if (empty($target)) { + $this->stdErr->writeln('The target directory must be specified.'); + + return 1; + } + + $this->validateDirectory($target, true); + + $confirmText = "\nThis will <options=bold>add, replace, and delete</> files in the local directory '<comment>" . $fs->formatPathForDisplay($target) . "</comment>'." + . "\n\nAre you sure you want to continue?"; + if (!$questionHelper->confirm($confirmText)) { + return 1; + } + + $this->runSync($sshUrl, $mountPath, $target, false, (bool) $input->getOption('delete')); + + return 0; + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.20.5/src/Command/Mount/MountListCommand.php new/platformsh-cli-3.21.0/src/Command/Mount/MountListCommand.php --- old/platformsh-cli-3.20.5/src/Command/Mount/MountListCommand.php 1970-01-01 01:00:00.000000000 +0100 +++ new/platformsh-cli-3.21.0/src/Command/Mount/MountListCommand.php 2017-10-18 10:41:52.000000000 +0200 @@ -0,0 +1,70 @@ +<?php + +namespace Platformsh\Cli\Command\Mount; + +use Platformsh\Cli\Service\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class MountListCommand extends MountCommandBase +{ + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('mount:list') + ->setAliases(['mounts']) + ->setDescription('Get a list of mounts') + ->addOption('paths', null, InputOption::VALUE_NONE, 'Output the mount paths only (one per line)') + ->addOption('refresh', null, InputOption::VALUE_NONE, 'Whether to refresh the cache'); + Table::configureInput($this->getDefinition()); + $this->addProjectOption(); + $this->addEnvironmentOption(); + $this->addAppOption(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->validateInput($input); + + $sshUrl = $this->getSelectedEnvironment() + ->getSshUrl($this->selectApp($input)); + + $appConfig = $this->getAppConfig($sshUrl, (bool) $input->getOption('refresh')); + + $mounts = $appConfig['mounts']; + if (empty($mounts)) { + $this->stdErr->writeln(sprintf('The app "%s" doesn\'t define any mounts.', $appConfig['name'])); + + return 0; + } + + if ($input->getOption('paths')) { + foreach (array_keys($mounts) as $path) { + $output->writeln($this->normalizeMountPath($path)); + } + + return 0; + } + + $header = ['Path', 'Definition']; + $rows = []; + foreach ($mounts as $path => $definition) { + $rows[] = [$this->normalizeMountPath($path), $definition]; + } + + /** @var \Platformsh\Cli\Service\Table $table */ + $table = $this->getService('table'); + $this->stdErr->writeln(sprintf('Mounts in the app <info>%s</info> (environment <info>%s</info>):', $appConfig['name'], $this->getSelectedEnvironment()->id)); + $table->render($rows, $header); + + return 0; + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.20.5/src/Command/Mount/MountUploadCommand.php new/platformsh-cli-3.21.0/src/Command/Mount/MountUploadCommand.php --- old/platformsh-cli-3.20.5/src/Command/Mount/MountUploadCommand.php 1970-01-01 01:00:00.000000000 +0100 +++ new/platformsh-cli-3.21.0/src/Command/Mount/MountUploadCommand.php 2017-10-18 10:41:52.000000000 +0200 @@ -0,0 +1,120 @@ +<?php + +namespace Platformsh\Cli\Command\Mount; + +use Platformsh\Cli\Local\LocalApplication; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\Question; + +class MountUploadCommand extends MountCommandBase +{ + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('mount:upload') + ->setDescription('Upload files to a mount, using rsync') + ->addOption('source', null, InputOption::VALUE_REQUIRED, 'A directory containing files to upload') + ->addOption('mount', 'm', InputOption::VALUE_REQUIRED, 'The mount (as an app-relative path)') + ->addOption('delete', null, InputOption::VALUE_NONE, 'Whether to delete extraneous files in the mount') + ->addOption('refresh', null, InputOption::VALUE_NONE, 'Whether to refresh the cache'); + $this->addProjectOption(); + $this->addEnvironmentOption(); + $this->addAppOption(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->validateInput($input); + $appName = $this->selectApp($input); + + $sshUrl = $this->getSelectedEnvironment() + ->getSshUrl($appName); + + $appConfig = $this->getAppConfig($sshUrl, (bool) $input->getOption('refresh')); + + if (empty($appConfig['mounts'])) { + $this->stdErr->writeln(sprintf('The app "%s" doesn\'t define any mounts.', $appConfig['name'])); + + return 1; + } + + /** @var \Platformsh\Cli\Service\QuestionHelper $questionHelper */ + $questionHelper = $this->getService('question_helper'); + /** @var \Platformsh\Cli\Service\Filesystem $fs */ + $fs = $this->getService('fs'); + + if ($input->getOption('mount')) { + $mountPath = $this->validateMountPath($input->getOption('mount'), $appConfig['mounts']); + } elseif ($input->isInteractive()) { + $mountPath = $questionHelper->choose( + $this->getMountsAsOptions($appConfig['mounts']), + 'Enter a number to choose a mount to upload to:' + ); + } else { + $this->stdErr->writeln('The <error>--mount</error> option must be specified (in non-interactive mode).'); + + return 1; + } + + $source = null; + $defaultSource = null; + if ($input->getOption('source')) { + $source = $input->getOption('source'); + } elseif ($projectRoot = $this->getProjectRoot()) { + if ($sharedPath = $this->getSharedPath($mountPath, $appConfig['mounts'])) { + if (file_exists($projectRoot . '/' . $this->config()->get('local.shared_dir') . '/' . $sharedPath)) { + $defaultSource = $projectRoot . '/' . $this->config()->get('local.shared_dir') . '/' . $sharedPath; + } + } + + $applications = LocalApplication::getApplications($projectRoot, $this->config()); + $appPath = $projectRoot; + foreach ($applications as $path => $candidateApp) { + if ($candidateApp->getName() === $appName) { + $appPath = $path; + break; + } + } + if (is_dir($appPath . '/' . $mountPath)) { + $defaultSource = $appPath . '/' . $mountPath; + } + } + + if (empty($source) && $input->isInteractive()) { + $questionText = 'Source directory'; + if ($defaultSource !== null) { + $formattedDefaultSource = $fs->formatPathForDisplay($defaultSource); + $questionText .= ' <question>[' . $formattedDefaultSource . ']</question>'; + } + $questionText .= ': '; + $source = $questionHelper->ask($input, $this->stdErr, new Question($questionText, $defaultSource)); + } + + if (empty($source)) { + $this->stdErr->writeln('The source directory must be specified.'); + + return 1; + } + + $this->validateDirectory($source); + + $confirmText = "\nThis will <options=bold>add, replace, and delete</> files in the remote mount '<info>$mountPath</info>'." + . "\n\nAre you sure you want to continue?"; + if (!$questionHelper->confirm($confirmText)) { + return 1; + } + + $this->runSync($sshUrl, $mountPath, $source, true, (bool) $input->getOption('delete')); + + return 0; + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.20.5/src/Console/CustomTextDescriptor.php new/platformsh-cli-3.21.0/src/Console/CustomTextDescriptor.php --- old/platformsh-cli-3.20.5/src/Console/CustomTextDescriptor.php 2017-10-13 10:57:00.000000000 +0200 +++ new/platformsh-cli-3.21.0/src/Console/CustomTextDescriptor.php 2017-10-18 10:41:52.000000000 +0200 @@ -103,7 +103,7 @@ if ($describedNamespace) { $this->writeText( - sprintf("<comment>Available commands for the \"%s\" namespace:</comment>", $describedNamespace), + sprintf("<comment>Available commands for the \"%s\" namespace:</comment>", $application->findNamespace($describedNamespace)), $options ); } else { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.20.5/src/Service/Filesystem.php new/platformsh-cli-3.21.0/src/Service/Filesystem.php --- old/platformsh-cli-3.20.5/src/Service/Filesystem.php 2017-10-13 10:57:00.000000000 +0200 +++ new/platformsh-cli-3.21.0/src/Service/Filesystem.php 2017-10-18 10:41:52.000000000 +0200 @@ -252,6 +252,23 @@ } /** + * Format a path for display (use the relative path if it's simpler). + * + * @param string $path + * + * @return string + */ + public function formatPathForDisplay($path) + { + $relative = $this->makePathRelative($path, getcwd()); + if (strpos($relative, '../..') === false && strlen($relative) < strlen($path)) { + return $relative; + } + + return rtrim(trim($path), '/'); + } + + /** * Check if a filename is in the blacklist. * * @param string $filename diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/platformsh-cli-3.20.5/src/Service/Shell.php new/platformsh-cli-3.21.0/src/Service/Shell.php --- old/platformsh-cli-3.20.5/src/Service/Shell.php 2017-10-13 10:57:00.000000000 +0200 +++ new/platformsh-cli-3.21.0/src/Service/Shell.php 2017-10-18 10:41:52.000000000 +0200 @@ -19,8 +19,6 @@ /** @var OutputInterface */ protected $stdErr; - protected $defaultTimeout = 3600; - public function __construct(OutputInterface $output = null) { $this->setOutput($output ?: new NullOutput()); @@ -78,6 +76,7 @@ * @param bool $mustRun * @param bool $quiet * @param array $env + * @param int|null $timeout * * @throws \Exception * If $mustRun is enabled and the command fails. @@ -86,11 +85,16 @@ * False if the command fails, true if it succeeds with no output, or a * string if it succeeds with output. */ - public function execute(array $args, $dir = null, $mustRun = false, $quiet = true, array $env = []) + public function execute(array $args, $dir = null, $mustRun = false, $quiet = true, array $env = [], $timeout = 3600) { $builder = new ProcessBuilder($args); $process = $builder->getProcess(); - $process->setTimeout($this->defaultTimeout); + + if ($timeout === null) { + set_time_limit(0); + } + + $process->setTimeout($timeout); $this->stdErr->writeln( "Running command: <info>" . $process->getCommandLine() . "</info>", ++++++ 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 2017-10-13 16:15:40.586800666 +0200 +++ new/vendor/autoload.php 2017-10-18 15:52:30.650536485 +0200 @@ -4,4 +4,4 @@ require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInit8f904e37181a69f3b56037fd804558c1::getLoader(); +return ComposerAutoloaderInit94be79301dbcc61b3879e411153f86b2::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 2017-10-13 16:15:40.586800666 +0200 +++ new/vendor/composer/autoload_real.php 2017-10-18 15:52:30.650536485 +0200 @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit8f904e37181a69f3b56037fd804558c1 +class ComposerAutoloaderInit94be79301dbcc61b3879e411153f86b2 { private static $loader; @@ -19,15 +19,15 @@ return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit8f904e37181a69f3b56037fd804558c1', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInit94be79301dbcc61b3879e411153f86b2', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); - spl_autoload_unregister(array('ComposerAutoloaderInit8f904e37181a69f3b56037fd804558c1', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit94be79301dbcc61b3879e411153f86b2', '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\ComposerStaticInit8f904e37181a69f3b56037fd804558c1::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInit94be79301dbcc61b3879e411153f86b2::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\ComposerStaticInit8f904e37181a69f3b56037fd804558c1::$files; + $includeFiles = Composer\Autoload\ComposerStaticInit94be79301dbcc61b3879e411153f86b2::$files; } else { $includeFiles = require __DIR__ . '/autoload_files.php'; } foreach ($includeFiles as $fileIdentifier => $file) { - composerRequire8f904e37181a69f3b56037fd804558c1($fileIdentifier, $file); + composerRequire94be79301dbcc61b3879e411153f86b2($fileIdentifier, $file); } return $loader; } } -function composerRequire8f904e37181a69f3b56037fd804558c1($fileIdentifier, $file) +function composerRequire94be79301dbcc61b3879e411153f86b2($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 2017-10-13 16:15:40.586800666 +0200 +++ new/vendor/composer/autoload_static.php 2017-10-18 15:52:30.650536485 +0200 @@ -4,7 +4,7 @@ namespace Composer\Autoload; -class ComposerStaticInit8f904e37181a69f3b56037fd804558c1 +class ComposerStaticInit94be79301dbcc61b3879e411153f86b2 { public static $files = array ( '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', @@ -188,9 +188,9 @@ public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInit8f904e37181a69f3b56037fd804558c1::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInit8f904e37181a69f3b56037fd804558c1::$prefixDirsPsr4; - $loader->classMap = ComposerStaticInit8f904e37181a69f3b56037fd804558c1::$classMap; + $loader->prefixLengthsPsr4 = ComposerStaticInit94be79301dbcc61b3879e411153f86b2::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit94be79301dbcc61b3879e411153f86b2::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit94be79301dbcc61b3879e411153f86b2::$classMap; }, null, ClassLoader::class); }
