Hello community,

here is the log from the commit of package platformsh-cli for openSUSE:Factory 
checked in at 2019-01-10 15:23:35
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/platformsh-cli (Old)
 and      /work/SRC/openSUSE:Factory/.platformsh-cli.new.28833 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "platformsh-cli"

Thu Jan 10 15:23:35 2019 rev:60 rq:664154 version:3.38.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/platformsh-cli/platformsh-cli.changes    
2018-11-29 23:01:18.391439396 +0100
+++ /work/SRC/openSUSE:Factory/.platformsh-cli.new.28833/platformsh-cli.changes 
2019-01-10 15:23:37.638312692 +0100
@@ -1,0 +2,49 @@
+Wed Jan 09 18:04:55 UTC 2019 - [email protected]
+
+- Update to version 3.38.0:
+  * Release v3.38.0
+  * Fix addOption arguments in snapshot:restore
+  * Allow restoring snapshots to another environment (#735)
+  * Sort interactive project/environment choices alphabetically
+  * Update README.md [skip changelog]
+  * Deduplicate role option description [skip changelog]
+  * Separate user:add and user:update command help
+  * [environment:list] Define "created" and "updated" columns
+  * [environment:list] Fix "Name" column should always have been "Title"
+  * Also support environment titles of 0
+  * Fix: environments with an empty name (e.g. '0') not being recognized
+  * [certificate:list] Add --pipe-domains option
+  * [certificate:list] Add --exclude-domain option
+  * [certificate:list] Add certs alias
+  * Remove interactivity check from installer (#765)
+  * [certificate:list] Define `domains` column
+  * [certificate:list] Filter on --no-expired by default; add --ignore-expiry 
option for previous behavior
+  * Fix inline documentation in user:list command
+  * Run tests in PHP 7.3 (Travis) (#764)
+
+-------------------------------------------------------------------
+Fri Dec 28 23:29:16 UTC 2018 - [email protected]
+
+- Update to version 3.37.2:
+  * Release v3.37.2
+  * [worker:list] Display project and environment name/ID
+  * [snapshot:list] Display project and environment name/ID
+  * [route:list] Display project name/ID
+  * [domain:list] Display project name/ID
+  * [activity:list] use api() method [skip changelog]
+  * [user:list] Display project name/ID
+  * Alias "python3" dependencies type to use pip
+  * Define "path" column in mount:list command
+  * Fix table CSV test
+  * Revert csv/tsv output to use LF instead of CRLF line breaks
+  * [activity:list] Better explanation of the selected environment/project
+  * Parse project URLs from the beta unified-UI (#761)
+
+-------------------------------------------------------------------
+Thu Nov 29 13:53:32 UTC 2018 - [email protected]
+
+- Update to version 3.37.1:
+  * Release v3.37.1
+  * [mount:size] Fix capacity calculation (should show % of total space, 
instead of % of "available")
+
+-------------------------------------------------------------------

Old:
----
  platformsh-cli-3.37.0.tar.xz

New:
----
  platformsh-cli-3.38.0.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ platformsh-cli.spec ++++++
--- /var/tmp/diff_new_pack.Wzr31p/_old  2019-01-10 15:23:38.154312159 +0100
+++ /var/tmp/diff_new_pack.Wzr31p/_new  2019-01-10 15:23:38.154312159 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package platformsh-cli
 #
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,7 +17,7 @@
 
 
 Name:           platformsh-cli
-Version:        3.37.0
+Version:        3.38.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.Wzr31p/_old  2019-01-10 15:23:38.186312126 +0100
+++ /var/tmp/diff_new_pack.Wzr31p/_new  2019-01-10 15:23:38.186312126 +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.37.0</param>
+    <param name="revision">refs/tags/v3.38.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.Wzr31p/_old  2019-01-10 15:23:38.202312109 +0100
+++ /var/tmp/diff_new_pack.Wzr31p/_new  2019-01-10 15:23:38.202312109 +0100
@@ -1,6 +1,6 @@
 <servicedata>
   <service name="tar_scm">
     <param name="url">git://github.com/platformsh/platformsh-cli.git</param>
-    <param 
name="changesrevision">6ad5381f1888c5601a44c37b85ca2d00e17faf57</param>
+    <param 
name="changesrevision">89037ac97082af56f5346c0fc5dfeb3d8a2d0061</param>
   </service>
 </servicedata>

++++++ licenses.txt ++++++
--- /var/tmp/diff_new_pack.Wzr31p/_old  2019-01-10 15:23:38.242312068 +0100
+++ /var/tmp/diff_new_pack.Wzr31p/_new  2019-01-10 15:23:38.242312068 +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.22.2  MIT           
+platformsh/client                   v0.23.0  MIT           
 platformsh/console-form             v0.0.23  MIT           
 psr/container                       1.0.0    MIT           
 psr/log                             1.1.0    MIT           

++++++ platformsh-cli-3.37.0.tar.xz -> platformsh-cli-3.38.0.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.37.0/.travis.yml 
new/platformsh-cli-3.38.0/.travis.yml
--- old/platformsh-cli-3.37.0/.travis.yml       2018-11-28 10:48:36.000000000 
+0100
+++ new/platformsh-cli-3.38.0/.travis.yml       2019-01-09 15:57:31.000000000 
+0100
@@ -8,7 +8,7 @@
   include:
     - php: 5.6
       env: TEST_SCRIPT=scripts/test/security.sh
-    - php: 7.2
+    - php: 7.3
       env: TEST_SCRIPT=scripts/test/security.sh
 
     - php: 5.6
@@ -22,10 +22,12 @@
       env: TEST_SCRIPT=scripts/test/unit.sh
     - php: 7.2
       env: TEST_SCRIPT=scripts/test/unit.sh
+    - php: 7.3
+      env: TEST_SCRIPT=scripts/test/unit.sh
     - php: nightly
       env: TEST_SCRIPT=scripts/test/unit.sh
 
-    - php: 7.2
+    - php: 7.3
       env: TEST_SCRIPT=scripts/test/slow.sh
 
   allow_failures:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.37.0/README.md 
new/platformsh-cli-3.38.0/README.md
--- old/platformsh-cli-3.37.0/README.md 2018-11-28 10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/README.md 2019-01-09 15:57:31.000000000 +0100
@@ -95,7 +95,7 @@
   certificate:add                           Add an SSL certificate to the 
project
   certificate:delete                        Delete a certificate from the 
project
   certificate:get                           View a certificate
-  certificate:list (certificates)           List project certificates
+  certificate:list (certificates, certs)    List project certificates
 commit
   commit:get                                Show commit details
   commit:list (commits)                     List commits
@@ -132,6 +132,7 @@
   integration:get                           View details of an integration
   integration:list (integrations)           View a list of project 
integration(s)
   integration:update                        Update an integration
+  integration:validate                      Validate an existing integration
 local
   local:build (build)                       Build the current project locally
   local:dir (dir)                           Find the local project root
@@ -184,10 +185,11 @@
   tunnel:list (tunnels)                     List SSH tunnels
   tunnel:open                               Open SSH tunnels to an app's 
relationships
 user
-  user:add (user:update)                    Add a user to the project, or set 
their role(s)
+  user:add                                  Add a user to the project
   user:delete                               Delete a user from the project
   user:get                                  View a user's role(s)
   user:list (users)                         List project users
+  user:update                               Update user role(s) on a project
 variable
   variable:create                           Create a variable
   variable:delete                           Delete a variable
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.37.0/composer.json 
new/platformsh-cli-3.38.0/composer.json
--- old/platformsh-cli-3.37.0/composer.json     2018-11-28 10:48:36.000000000 
+0100
+++ new/platformsh-cli-3.38.0/composer.json     2019-01-09 15:57:31.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.22.2 <2.0",
+        "platformsh/client": ">=0.23.0 <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.37.0/composer.lock 
new/platformsh-cli-3.38.0/composer.lock
--- old/platformsh-cli-3.37.0/composer.lock     2018-11-28 10:48:36.000000000 
+0100
+++ new/platformsh-cli-3.38.0/composer.lock     2019-01-09 15:57:31.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": "2b5dbc63521744801a54eb2cd2c5a14a",
+    "content-hash": "7316086542b599673424191e18406f6a",
     "packages": [
         {
             "name": "cocur/slugify",
@@ -712,16 +712,16 @@
         },
         {
             "name": "platformsh/client",
-            "version": "v0.22.2",
+            "version": "v0.23.0",
             "source": {
                 "type": "git",
                 "url": 
"https://github.com/platformsh/platformsh-client-php.git";,
-                "reference": "e487f9fbb9f79b404dcfd19e633d13cade7bce6b"
+                "reference": "9b0fc3004d5abdc3cc54b0bea80f779e23660289"
             },
             "dist": {
                 "type": "zip",
-                "url": 
"https://api.github.com/repos/platformsh/platformsh-client-php/zipball/e487f9fbb9f79b404dcfd19e633d13cade7bce6b";,
-                "reference": "e487f9fbb9f79b404dcfd19e633d13cade7bce6b",
+                "url": 
"https://api.github.com/repos/platformsh/platformsh-client-php/zipball/9b0fc3004d5abdc3cc54b0bea80f779e23660289";,
+                "reference": "9b0fc3004d5abdc3cc54b0bea80f779e23660289",
                 "shasum": ""
             },
             "require": {
@@ -757,7 +757,7 @@
                 }
             ],
             "description": "Platform.sh API client",
-            "time": "2018-10-24T20:17:25+00:00"
+            "time": "2019-01-09T14:36:49+00:00"
         },
         {
             "name": "platformsh/console-form",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.37.0/config.yaml 
new/platformsh-cli-3.38.0/config.yaml
--- old/platformsh-cli-3.37.0/config.yaml       2018-11-28 10:48:36.000000000 
+0100
+++ new/platformsh-cli-3.38.0/config.yaml       2019-01-09 15:57:31.000000000 
+0100
@@ -1,7 +1,7 @@
 # Metadata about the CLI application itself.
 application:
   name: 'Platform.sh CLI'
-  version: '3.37.0'
+  version: '3.38.0'
   executable: 'platform'
   package_name: 'platformsh/cli'
   installer_url: 'https://platform.sh/cli/installer'
@@ -104,6 +104,9 @@
   git_domain: 'platform.sh'
   site_domains: ['platform.sh', 'platformsh.site']
 
+  # Domain of a unified-UI instance (beta).
+  ui_domain: 'ui.platform.sh'
+
 # Automatic updates.
 # This can be overridden in the user config file.
 updates:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.37.0/dist/installer.php 
new/platformsh-cli-3.38.0/dist/installer.php
--- old/platformsh-cli-3.37.0/dist/installer.php        2018-11-28 
10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/dist/installer.php        2019-01-09 
15:57:31.000000000 +0100
@@ -222,10 +222,7 @@
 
 output(PHP_EOL . '  Running self:install command...' . PHP_EOL);
 putenv('CLICOLOR_FORCE=' . (is_ansi() ? '1' : '0'));
-$commandline = 'php ' . $pharPath . ' self:install';
-if (!is_interactive()) {
-    $commandline .= ' --yes';
-}
+$commandline = 'php ' . $pharPath . ' self:install --yes';
 $process = proc_open($commandline, [STDIN, STDOUT, STDERR], $pipes);
 $result = proc_close($process);
 
@@ -311,25 +308,6 @@
 }
 
 /**
- * Returns whether the terminal is interactive.
- *
- * @return bool
- */
-function is_interactive()
-{
-    global $argv;
-    if (!empty($argv) && array_intersect(['--no-interaction', '-y', '--yes'], 
$argv)) {
-        return false;
-    }
-
-    if (function_exists('posix_isatty')) {
-        return posix_isatty(STDOUT) && posix_isatty(STDERR);
-    }
-
-    return true;
-}
-
-/**
  * Sets up the STDIN, STDOUT and STDERR constants.
  *
  * Due to a PHP bug, these constants are not available when the PHP script is
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.37.0/dist/manifest.json 
new/platformsh-cli-3.38.0/dist/manifest.json
--- old/platformsh-cli-3.37.0/dist/manifest.json        2018-11-28 
10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/dist/manifest.json        2019-01-09 
15:57:31.000000000 +0100
@@ -1,10 +1,10 @@
 [
     {
         "name": "platform.phar",
-        "sha1": "99793bba2729e51a4ea425cbb069e9e0227d67b5",
-        "sha256": 
"6fcc456af03881579bdc67a53fcb7724f43e51c6723988a92f2a276d8323a9fb",
-        "url": 
"https://github.com/platformsh/platformsh-cli/releases/download/v3.37.0/platform.phar";,
-        "version": "3.37.0",
+        "sha1": "5144fb0283e09583c7f5933324168194b6df4e61",
+        "sha256": 
"f6aa40652c031374338fdedc2b5fe45419477cb8d8c688639819f8cac11ddd13",
+        "url": 
"https://github.com/platformsh/platformsh-cli/releases/download/v3.38.0/platform.phar";,
+        "version": "3.38.0",
         "php": {
             "min": "5.5.9"
         },
@@ -152,6 +152,16 @@
                 "notes": "New features:\n\n* Add --columns option for all 
tables (to filter and order table columns).\n* Add non-default \"disk\" and 
\"size\" columns to app:list command.\n* Add non-default \"updated_at\" column 
to domains:list command.\n* Add non-default \"host\" column to project:list 
command.\n* Allow loading more than 10 snapshots via --limit in snapshot:list 
command.\n\nOther changes:\n\n* Change CSV/TSV table output to make it cleaner. 
CSV/TSV cells will now only\n  be enclosed in quote marks if necessary, 
conforming to RFC 4180.\n* Fail if password input cannot be hidden in 
auth:password-login command.\n* Set the remote only for new branches in 
environment:branch command.\n* Avoid wrapping dates in commit:list command.\n* 
Update list of allowed SSH key algorithms for ssh-key:add command.\n* Remove 
confirmation step in logout command.",
                 "show from": "3.36.0",
                 "hide from": "3.37.0"
+            },
+            {
+                "notes": "* Revert csv/tsv table output to use LF instead of 
CRLF line breaks.\n* Display project and environment title/ID in the following 
commands:\n  `activity:list` (`activities`), `domain:list` (`domains`),\n  
`route:list` (`routes`), `snapshot:list` (`snapshots`),\n  `user:list` 
(`users`), `worker:list` (`workers`) \n* Local build: handle `python3` 
dependencies type (same as `python`).\n* Define the `path` column in the 
`mount:list` (`mounts`) command.\n* Parse project URLs from the beta 
unified-UI.",
+                "show from": "3.37.0",
+                "hide from": "3.37.2"
+            },
+            {
+                "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"
             }
         ]
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.37.0/src/Application.php 
new/platformsh-cli-3.38.0/src/Application.php
--- old/platformsh-cli-3.37.0/src/Application.php       2018-11-28 
10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Application.php       2019-01-09 
15:57:31.000000000 +0100
@@ -194,6 +194,7 @@
         $commands[] = new Command\User\UserDeleteCommand();
         $commands[] = new Command\User\UserListCommand();
         $commands[] = new Command\User\UserGetCommand();
+        $commands[] = new Command\User\UserUpdateCommand();
         $commands[] = new Command\Variable\VariableCreateCommand();
         $commands[] = new Command\Variable\VariableDeleteCommand();
         $commands[] = new Command\Variable\VariableDisableCommand();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/Activity/ActivityListCommand.php 
new/platformsh-cli-3.38.0/src/Command/Activity/ActivityListCommand.php
--- old/platformsh-cli-3.37.0/src/Command/Activity/ActivityListCommand.php      
2018-11-28 10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Command/Activity/ActivityListCommand.php      
2019-01-09 15:57:31.000000000 +0100
@@ -96,19 +96,16 @@
 
         if (!$table->formatIsMachineReadable()) {
             if ($environmentSpecific) {
-                $this->stdErr->writeln(
-                    sprintf(
-                        'Activities for the environment <info>%s</info>:',
-                        $apiResource->id
-                    )
-                );
+                $this->stdErr->writeln(sprintf(
+                    'Activities on the project %s, environment %s:',
+                    $this->api()->getProjectLabel($project),
+                    $this->api()->getEnvironmentLabel($apiResource)
+                ));
             } else {
-                $this->stdErr->writeln(
-                    sprintf(
-                        'Activities for the project <info>%s</info>:',
-                        $this->api()->getProjectLabel($project)
-                    )
-                );
+                $this->stdErr->writeln(sprintf(
+                    'Activities on the project %s:',
+                    $this->api()->getProjectLabel($project)
+                ));
             }
         }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/Certificate/CertificateListCommand.php 
new/platformsh-cli-3.38.0/src/Command/Certificate/CertificateListCommand.php
--- 
old/platformsh-cli-3.37.0/src/Command/Certificate/CertificateListCommand.php    
    2018-11-28 10:48:36.000000000 +0100
+++ 
new/platformsh-cli-3.38.0/src/Command/Certificate/CertificateListCommand.php    
    2019-01-09 15:57:31.000000000 +0100
@@ -16,24 +16,34 @@
     {
         $this
             ->setName('certificate:list')
-            ->setAliases(['certificates'])
+            ->setAliases(['certificates', 'certs'])
             ->setDescription('List project certificates');
         $this->addOption('domain', null, InputOption::VALUE_REQUIRED, 'Filter 
by domain name (case-insensitive search)');
+        $this->addOption('exclude-domain', null, InputOption::VALUE_REQUIRED, 
'Exclude certificates, matching by domain name (case-insensitive search)');
         $this->addOption('issuer', null, InputOption::VALUE_REQUIRED, 'Filter 
by issuer');
         $this->addOption('only-auto', null, InputOption::VALUE_NONE, 'Show 
only auto-provisioned certificates');
         $this->addOption('no-auto', null, InputOption::VALUE_NONE, 'Show only 
manually added certificates');
+        $this->addOption('ignore-expiry', null, InputOption::VALUE_NONE, 'Show 
both expired and non-expired certificates');
         $this->addOption('only-expired', null, InputOption::VALUE_NONE, 'Show 
only expired certificates');
-        $this->addOption('no-expired', null, InputOption::VALUE_NONE, 'Show 
only non-expired certificates');
+        $this->addOption('no-expired', null, InputOption::VALUE_NONE, 'Show 
only non-expired certificates (default)');
+        $this->addOption('pipe-domains', null, InputOption::VALUE_NONE, 'Only 
return a list of domain names covered by the certificates');
         PropertyFormatter::configureInput($this->getDefinition());
         Table::configureInput($this->getDefinition());
         $this->addProjectOption();
+        $this->addExample('Output a list of domains covered by valid 
certificates', '--pipe-domains --no-expired');
     }
 
     protected function execute(InputInterface $input, OutputInterface $output)
     {
         $this->validateInput($input);
 
-        $filterOptions = ['domain', 'issuer', 'only-auto', 'no-auto', 
'only-expired', 'no-expired'];
+        // Set --no-expired by default, if --ignore-expiry and --only-expired
+        // are not supplied.
+        if (!$input->getOption('ignore-expiry') && 
!$input->getOption('only-expired')) {
+            $input->setOption('no-expired', true);
+        }
+
+        $filterOptions = ['domain', 'exclude-domain', 'issuer', 'only-auto', 
'no-auto', 'only-expired', 'no-expired'];
         $filters = array_filter(array_intersect_key($input->getOptions(), 
array_flip($filterOptions)));
 
         $project = $this->getSelectedProject();
@@ -42,11 +52,12 @@
 
         $this->filterCerts($certs, $filters);
 
-        if (!empty($filters)) {
+        if (!empty($filters) && !$input->getOption('pipe-domains')) {
             $filtersUsed = '<comment>--'
                 . implode('</comment>, <comment>--', array_keys($filters))
                 . '</comment>';
             $this->stdErr->writeln(sprintf('Filters in use: %s', 
$filtersUsed));
+            $this->stdErr->writeln('');
         }
 
         if (empty($certs)) {
@@ -55,17 +66,27 @@
             return 0;
         }
 
+        if ($input->getOption('pipe-domains')) {
+            foreach ($certs as $cert) {
+                foreach ($cert->domains as $domain) {
+                    $output->writeln($domain);
+                }
+            }
+
+            return 0;
+        }
+
         /** @var \Platformsh\Cli\Service\Table $table */
         $table = $this->getService('table');
         /** @var \Platformsh\Cli\Service\PropertyFormatter $propertyFormatter 
*/
         $propertyFormatter = $this->getService('property_formatter');
 
-        $header = ['ID', 'Domain(s)', 'Created', 'Expires', 'Issuer'];
+        $header = ['ID', 'domains' => 'Domain(s)', 'Created', 'Expires', 
'Issuer'];
         $rows = [];
         foreach ($certs as $cert) {
             $rows[] = [
                 $cert->id,
-                implode("\n", $cert->domains),
+                'domains' => implode("\n", $cert->domains),
                 $propertyFormatter->format($cert->created_at, 'created_at'),
                 $propertyFormatter->format($cert->expires_at, 'expires_at'),
                 $this->getCertificateIssuerByAlias($cert, 'commonName') ?: '',
@@ -94,14 +115,16 @@
         foreach ($filters as $filter => $value) {
             switch ($filter) {
                 case 'domain':
-                    $certs = array_filter($certs, function (Certificate $cert) 
use ($value) {
+                case 'exclude-domain':
+                    $include = $filter === 'domain';
+                    $certs = array_filter($certs, function (Certificate $cert) 
use ($value, $include) {
                         foreach ($cert->domains as $domain) {
                             if (stripos($domain, $value) !== false) {
-                                return true;
+                                return $include;
                             }
                         }
 
-                        return false;
+                        return !$include;
                     });
                     break;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.37.0/src/Command/CommandBase.php 
new/platformsh-cli-3.38.0/src/Command/CommandBase.php
--- old/platformsh-cli-3.37.0/src/Command/CommandBase.php       2018-11-28 
10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Command/CommandBase.php       2019-01-09 
15:57:31.000000000 +0100
@@ -759,7 +759,7 @@
             ), OutputInterface::VERBOSITY_VERBOSE);
         }
 
-        if (!empty($environmentId)) {
+        if ($environmentId !== null) {
             $environment = $this->api()->getEnvironment($environmentId, 
$this->project, null, true);
             if (!$environment) {
                 throw new ConsoleInvalidArgumentException('Specified 
environment not found: ' . $environmentId);
@@ -865,10 +865,12 @@
             throw new \BadMethodCallException('Not interactive: a project 
choice cannot be offered.');
         }
 
+        // Build and sort a list of project options.
         $projectList = [];
         foreach ($projects as $project) {
             $projectList[$project->id] = 
$this->api()->getProjectLabel($project, false);
         }
+        asort($projectList, SORT_NATURAL | SORT_FLAG_CASE);
 
         /** @var \Platformsh\Cli\Service\QuestionHelper $questionHelper */
         $questionHelper = $this->getService('question_helper');
@@ -897,6 +899,10 @@
         $questionHelper = $this->getService('question_helper');
         $default = $this->api()->getDefaultEnvironmentId($environments);
 
+        // Build and sort a list of options (environment IDs).
+        $ids = array_keys($environments);
+        sort($ids, SORT_NATURAL | SORT_FLAG_CASE);
+
         $id = $questionHelper->askInput('Environment ID', $default, 
array_keys($environments), function ($value) use ($environments) {
             if (!isset($environments[$value])) {
                 throw new \RuntimeException('Environment not found: ' . 
$value);
@@ -968,8 +974,8 @@
 
         // Select the environment.
         $envOptionName = 'environment';
-        if ($input->hasArgument($this->envArgName) && 
$input->getArgument($this->envArgName)) {
-            if ($input->hasOption($envOptionName) && 
$input->getOption($envOptionName)) {
+        if ($input->hasArgument($this->envArgName) && 
$input->getArgument($this->envArgName) !== null) {
+            if ($input->hasOption($envOptionName) && 
$input->getOption($envOptionName) !== null) {
                 throw new ConsoleInvalidArgumentException(
                     sprintf(
                         'You cannot use both the <%s> argument and the --%s 
option',
@@ -987,7 +993,9 @@
                 $this->selectEnvironment($argument, true, $selectDefaultEnv);
             }
         } elseif ($input->hasOption($envOptionName)) {
-            $environmentId = $input->getOption($envOptionName) ?: 
$environmentId;
+            if ($input->getOption($envOptionName) !== null) {
+                $environmentId = $input->getOption($envOptionName);
+            }
             $this->selectEnvironment($environmentId, !$envNotRequired, 
$selectDefaultEnv);
         }
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/Domain/DomainListCommand.php 
new/platformsh-cli-3.38.0/src/Command/Domain/DomainListCommand.php
--- old/platformsh-cli-3.37.0/src/Command/Domain/DomainListCommand.php  
2018-11-28 10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Command/Domain/DomainListCommand.php  
2019-01-09 15:57:31.000000000 +0100
@@ -86,15 +86,23 @@
             return 0;
         }
 
-        $this->stdErr->writeln("Your domains are: ");
+        if (!$table->formatIsMachineReadable()) {
+            $this->stdErr->writeln(sprintf(
+                'Domains on the project %s:',
+                $this->api()->getProjectLabel($project)
+            ));
+        }
+
         $table->render($rows, $header, $defaultColumns);
 
-        $this->stdErr->writeln('');
-        $this->stdErr->writeln([
-            'To add a new domain, run: <info>' . $executable . ' domain:add 
[domain-name]</info>',
-            'To view a domain, run: <info>' . $executable . ' domain:get 
[domain-name]</info>',
-            'To delete a domain, run: <info>' . $executable . ' domain:delete 
[domain-name]</info>',
-        ]);
+        if (!$table->formatIsMachineReadable()) {
+            $this->stdErr->writeln('');
+            $this->stdErr->writeln([
+                'To add a new domain, run: <info>' . $executable . ' 
domain:add [domain-name]</info>',
+                'To view a domain, run: <info>' . $executable . ' domain:get 
[domain-name]</info>',
+                'To delete a domain, run: <info>' . $executable . ' 
domain:delete [domain-name]</info>',
+            ]);
+        }
 
         return 0;
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/Environment/EnvironmentBranchCommand.php 
new/platformsh-cli-3.38.0/src/Command/Environment/EnvironmentBranchCommand.php
--- 
old/platformsh-cli-3.37.0/src/Command/Environment/EnvironmentBranchCommand.php  
    2018-11-28 10:48:36.000000000 +0100
+++ 
new/platformsh-cli-3.38.0/src/Command/Environment/EnvironmentBranchCommand.php  
    2019-01-09 15:57:31.000000000 +0100
@@ -47,7 +47,7 @@
         $parentEnvironment = $this->getSelectedEnvironment();
 
         $branchName = $input->getArgument('id');
-        if (empty($branchName)) {
+        if ($branchName === null) {
             if ($input->isInteractive()) {
                 // List environments.
                 return $this->runOtherCommand(
@@ -117,11 +117,11 @@
             return 1;
         }
 
-        $title = $input->getOption('title') ?: $branchName;
+        $title = $input->getOption('title') !== null ? 
$input->getOption('title') : $branchName;
 
         $this->stdErr->writeln(sprintf(
             'Creating a new environment %s, branched from %s',
-            $title && $title !== $branchName
+            strlen($title) > 0 && $title !== $branchName
                 ? '<info>' . $title . '</info> (' . $branchName . ')'
                 : '<info>' . $branchName . '</info>',
             $this->api()->getEnvironmentLabel($parentEnvironment)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/Environment/EnvironmentCheckoutCommand.php
 
new/platformsh-cli-3.38.0/src/Command/Environment/EnvironmentCheckoutCommand.php
--- 
old/platformsh-cli-3.37.0/src/Command/Environment/EnvironmentCheckoutCommand.php
    2018-11-28 10:48:36.000000000 +0100
+++ 
new/platformsh-cli-3.38.0/src/Command/Environment/EnvironmentCheckoutCommand.php
    2019-01-09 15:57:31.000000000 +0100
@@ -36,10 +36,10 @@
         }
 
         $branch = $input->getArgument('id');
-        if (empty($branch)) {
+        if ($branch === null) {
             if ($input->isInteractive()) {
                 $branch = $this->offerBranchChoice($project, $projectRoot);
-                if (empty($branch)) {
+                if ($branch === null) {
                     return 1;
                 }
             } else {
@@ -115,7 +115,10 @@
         }
         $environmentList = [];
         foreach ($environments as $id => $environment) {
-            if ($currentEnvironment && $id == $currentEnvironment->id) {
+            // The $id will be an integer for numeric environment names (as
+            // it was assigned to an array key), so it's cast back to a
+            // string for this comparison.
+            if ($currentEnvironment && (string) $id === 
$currentEnvironment->id) {
                 continue;
             }
             $environmentList[$id] = 
$this->api()->getEnvironmentLabel($environment, false);
@@ -146,7 +149,11 @@
         // If there's more than one choice, present the user with a list.
         if (count($environmentList) > 1) {
             $chooseEnvironmentText = "Enter a number to check out another 
environment:";
-            return $helper->choose($environmentList, $chooseEnvironmentText);
+
+            // The environment ID will be an integer if it was numeric
+            // (because PHP does that with array keys), so it's cast back to
+            // a string here.
+            return (string) $helper->choose($environmentList, 
$chooseEnvironmentText);
         }
 
         // If there's only one choice, QuestionHelper::choose() does not
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/Environment/EnvironmentListCommand.php 
new/platformsh-cli-3.38.0/src/Command/Environment/EnvironmentListCommand.php
--- 
old/platformsh-cli-3.37.0/src/Command/Environment/EnvironmentListCommand.php    
    2018-11-28 10:48:36.000000000 +0100
+++ 
new/platformsh-cli-3.38.0/src/Command/Environment/EnvironmentListCommand.php    
    2019-01-09 15:57:31.000000000 +0100
@@ -18,6 +18,9 @@
     protected $currentEnvironment;
     protected $mapping = [];
 
+    /** @var \Platformsh\Cli\Service\PropertyFormatter */
+    protected $formatter;
+
     /**
      * {@inheritdoc}
      */
@@ -107,6 +110,9 @@
 
             $row[] = $this->formatEnvironmentStatus($environment->status);
 
+            $row[] = $this->formatter->format($environment->created_at, 
'created_at');
+            $row[] = $this->formatter->format($environment->updated_at, 
'updated_at');
+
             $rows[] = $row;
             if (isset($this->children[$environment->id])) {
                 $childRows = $this->buildEnvironmentRows(
@@ -173,20 +179,24 @@
             $this->children['master'] = [];
         }
 
-        $headers = ['ID', 'Name', 'Status'];
+        $headers = ['ID', 'Title', 'Status', 'Created', 'Updated'];
+        $defaultColumns = ['id', 'title', 'status'];
 
         /** @var \Platformsh\Cli\Service\Table $table */
         $table = $this->getService('table');
 
+        /** @var \Platformsh\Cli\Service\PropertyFormatter $formatter */
+        $this->formatter = $this->getService('property_formatter');
+
         if ($table->formatIsMachineReadable()) {
-            $table->render($this->buildEnvironmentRows($tree, false, false), 
$headers);
+            $table->render($this->buildEnvironmentRows($tree, false, false), 
$headers, $defaultColumns);
 
             return;
         }
 
         $this->stdErr->writeln("Your environments are: ");
 
-        $table->render($this->buildEnvironmentRows($tree), $headers);
+        $table->render($this->buildEnvironmentRows($tree), $headers, 
$defaultColumns);
 
         if (!$this->currentEnvironment) {
             return;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/Mount/MountListCommand.php 
new/platformsh-cli-3.38.0/src/Command/Mount/MountListCommand.php
--- old/platformsh-cli-3.37.0/src/Command/Mount/MountListCommand.php    
2018-11-28 10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Command/Mount/MountListCommand.php    
2019-01-09 15:57:31.000000000 +0100
@@ -51,12 +51,12 @@
             return 0;
         }
 
-        $header = ['Mount path', 'Definition'];
+        $header = ['path' => 'Mount path', 'definition' => 'Definition'];
         $rows = [];
         /** @var \Platformsh\Cli\Service\PropertyFormatter $formatter */
         $formatter = $this->getService('property_formatter');
         foreach ($mounts as $path => $definition) {
-            $rows[] = [$path, $formatter->format($definition)];
+            $rows[] = ['path' => $path, 'definition' => 
$formatter->format($definition)];
         }
 
         /** @var \Platformsh\Cli\Service\Table $table */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/Mount/MountSizeCommand.php 
new/platformsh-cli-3.38.0/src/Command/Mount/MountSizeCommand.php
--- old/platformsh-cli-3.37.0/src/Command/Mount/MountSizeCommand.php    
2018-11-28 10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Command/Mount/MountSizeCommand.php    
2019-01-09 15:57:31.000000000 +0100
@@ -203,15 +203,13 @@
                 $results[$filesystem]['mounts'][] = $mountPath;
                 continue;
             }
-            $available = $this->getDfColumn($line, 'available');
-            $used = $this->getDfColumn($line, 'used');
             $results[$filesystem] = [
                 'total' => $this->getDfColumn($line, 'total'),
-                'used' => $used,
-                'available' => $available,
+                'used' => $this->getDfColumn($line, 'used'),
+                'available' => $this->getDfColumn($line, 'available'),
                 'mounts' => [$mountPath],
-                'percent_used' => $used / $available * 100,
             ];
+            $results[$filesystem]['percent_used'] = 
$results[$filesystem]['used'] / $results[$filesystem]['total'] * 100;
         }
 
         return $results;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/Project/ProjectGetCommand.php 
new/platformsh-cli-3.38.0/src/Command/Project/ProjectGetCommand.php
--- old/platformsh-cli-3.37.0/src/Command/Project/ProjectGetCommand.php 
2018-11-28 10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Command/Project/ProjectGetCommand.php 
2019-01-09 15:57:31.000000000 +0100
@@ -216,7 +216,7 @@
             $result = $identifier->identify($projectId);
             $projectId = $result['projectId'];
             $host = $host ?: $result['host'];
-            $environmentId = $environmentId ?: $result['environmentId'];
+            $environmentId = $environmentId !== null ? $environmentId : 
$result['environmentId'];
         }
 
         $project = $this->selectProject($projectId, $host);
@@ -245,7 +245,7 @@
         }
         $this->projectRoot = $parent . '/' . basename($directory);
 
-        if (!$environmentId) {
+        if ($environmentId === null) {
             $environments = $this->api()->getEnvironments($project);
             $environmentId = isset($environments['master']) ? 'master' : 
key($environments);
             if (count($environments) > 1) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/Project/ProjectListCommand.php 
new/platformsh-cli-3.38.0/src/Command/Project/ProjectListCommand.php
--- old/platformsh-cli-3.37.0/src/Command/Project/ProjectListCommand.php        
2018-11-28 10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Command/Project/ProjectListCommand.php        
2019-01-09 15:57:31.000000000 +0100
@@ -40,7 +40,7 @@
         if ($host = $input->getOption('host')) {
             $filters['host'] = $host;
         }
-        if ($title = $input->getOption('title')) {
+        if (($title = $input->getOption('title')) !== null) {
             $filters['title'] = $title;
         }
         if ($input->getOption('my')) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/Route/RouteListCommand.php 
new/platformsh-cli-3.38.0/src/Command/Route/RouteListCommand.php
--- old/platformsh-cli-3.37.0/src/Command/Route/RouteListCommand.php    
2018-11-28 10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Command/Route/RouteListCommand.php    
2019-01-09 15:57:31.000000000 +0100
@@ -53,7 +53,11 @@
         }
 
         if (!$table->formatIsMachineReadable()) {
-            $this->stdErr->writeln("Routes for the environment 
<info>{$environment->id}</info>:");
+            $this->stdErr->writeln(sprintf(
+                'Routes on the project %s, environment %s:',
+                $this->api()->getProjectLabel($this->getSelectedProject()),
+                $this->api()->getEnvironmentLabel($environment)
+            ));
         }
 
         $table->render($rows, $header);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/Snapshot/SnapshotListCommand.php 
new/platformsh-cli-3.38.0/src/Command/Snapshot/SnapshotListCommand.php
--- old/platformsh-cli-3.37.0/src/Command/Snapshot/SnapshotListCommand.php      
2018-11-28 10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Command/Snapshot/SnapshotListCommand.php      
2019-01-09 15:57:31.000000000 +0100
@@ -46,10 +46,6 @@
         /** @var \Platformsh\Cli\Service\PropertyFormatter $formatter */
         $formatter = $this->getService('property_formatter');
 
-        if (!$table->formatIsMachineReadable()) {
-            $this->stdErr->writeln("Finding snapshots for the environment 
<info>{$environment->id}</info>");
-        }
-
         /** @var \Platformsh\Cli\Service\ActivityLoader $loader */
         $loader = $this->getService('activity_loader');
         $activities = $loader->load($environment, $input->getOption('limit'), 
'environment.backup', $startsAt);
@@ -71,6 +67,14 @@
             ];
         }
 
+        if (!$table->formatIsMachineReadable()) {
+            $this->stdErr->writeln(sprintf(
+                'Snapshots on the project %s, environment %s:',
+                $this->api()->getProjectLabel($this->getSelectedProject()),
+                $this->api()->getEnvironmentLabel($environment)
+            ));
+        }
+
         $table->render($rows, $headers);
         return 0;
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/Snapshot/SnapshotRestoreCommand.php 
new/platformsh-cli-3.38.0/src/Command/Snapshot/SnapshotRestoreCommand.php
--- old/platformsh-cli-3.37.0/src/Command/Snapshot/SnapshotRestoreCommand.php   
2018-11-28 10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Command/Snapshot/SnapshotRestoreCommand.php   
2019-01-09 15:57:31.000000000 +0100
@@ -5,6 +5,7 @@
 use Platformsh\Client\Model\Activity;
 use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Output\OutputInterface;
 
 class SnapshotRestoreCommand extends CommandBase
@@ -15,7 +16,9 @@
         $this
             ->setName('snapshot:restore')
             ->setDescription('Restore an environment snapshot')
-            ->addArgument('snapshot', InputArgument::OPTIONAL, 'The name of 
the snapshot. Defaults to the most recent one');
+            ->addArgument('snapshot', InputArgument::OPTIONAL, 'The name of 
the snapshot. Defaults to the most recent one')
+            ->addOption('target', null, InputOption::VALUE_REQUIRED, "The 
environment to restore to. Defaults to the snapshot's current environment")
+            ->addOption('branch-from', null, InputOption::VALUE_REQUIRED, 'If 
the --target does not yet exist, this specifies the parent of the new 
environment');
         $this->addProjectOption()
              ->addEnvironmentOption()
              ->addWaitOptions();
@@ -72,19 +75,36 @@
             return 1;
         }
 
+        // Validate the --branch-from option.
+        $branchFrom = $input->getOption('branch-from');
+        if ($branchFrom !== null && !$this->api()->getEnvironment($branchFrom, 
$this->getSelectedProject())) {
+            $this->stdErr->writeln(sprintf('Environment not found (in 
--branch-from): <error>%s</error>', $branchFrom));
+
+            return 1;
+        }
+
+        // Process the --target option.
+        $target = $input->getOption('target');
+        $targetEnvironment = $target !== null
+            ? $this->api()->getEnvironment($target, 
$this->getSelectedProject())
+            : $environment;
+        $targetLabel = $targetEnvironment
+            ? $this->api()->getEnvironmentLabel($targetEnvironment)
+            : '<info>' . $target . '</info>';
+
         /** @var \Platformsh\Cli\Service\QuestionHelper $questionHelper */
         $questionHelper = $this->getService('question_helper');
         $name = $selectedActivity['payload']['backup_name'];
         $date = date('c', strtotime($selectedActivity['created_at']));
         if (!$questionHelper->confirm(
-            "Are you sure you want to restore the snapshot 
<comment>$name</comment> from <comment>$date</comment>?"
+            "Are you sure you want to restore the snapshot 
<comment>$name</comment> from <comment>$date</comment> to environment 
$targetLabel?"
         )) {
             return 1;
         }
 
-        $this->stdErr->writeln("Restoring snapshot <info>$name</info>");
+        $this->stdErr->writeln("Restoring snapshot <info>$name</info> to 
$targetLabel");
 
-        $activity = $selectedActivity->restore();
+        $activity = $selectedActivity->restore($target, $branchFrom);
         if ($this->shouldWait($input)) {
             $this->stdErr->writeln('Waiting for the restore to complete...');
             /** @var \Platformsh\Cli\Service\ActivityMonitor $activityMonitor 
*/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/User/UserAddCommand.php 
new/platformsh-cli-3.38.0/src/Command/User/UserAddCommand.php
--- old/platformsh-cli-3.37.0/src/Command/User/UserAddCommand.php       
2018-11-28 10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Command/User/UserAddCommand.php       
2019-01-09 15:57:31.000000000 +0100
@@ -20,16 +20,29 @@
     {
         $this
             ->setName('user:add')
-            ->setAliases(['user:update'])
-            ->setDescription('Add a user to the project, or set their role(s)')
-            ->addArgument('email', InputArgument::OPTIONAL, "The user's email 
address")
-            ->addOption('role', 'r', InputOption::VALUE_REQUIRED | 
InputOption::VALUE_IS_ARRAY, "The user's project role ('admin' or 'viewer') or 
environment-specific role (e.g. 'master:contributor' or 'stage:viewer').\nThe 
character % can be used as a wildcard in the environment ID e.g. 
'%:viewer'.\nThe role can be abbreviated, e.g. 'master:c'.");
+            ->setDescription('Add a user to the project')
+            ->addArgument('email', InputArgument::OPTIONAL, "The user's email 
address");
+
+        $this->addRoleOption();
         $this->addProjectOption();
         $this->addWaitOptions();
+
         $this->addExample('Add Alice as a project admin', '[email protected] 
-r admin');
-        $this->addExample('Make Bob an admin on the "develop" and "stage" 
environments', '[email protected] -r develop:a,stage:a');
-        $this->addExample('Make Charlie a contributor on all existing 
environments', '[email protected] -r %:c');
-        $this->addExample('Make Damien an admin on "master" and all (existing) 
environments starting with "pr-"', '[email protected] -r master:a -r pr-%:a');
+    }
+
+    /**
+     * Adds the --role (-r) option to the command.
+     */
+    protected function addRoleOption()
+    {
+        $this->addOption(
+            'role',
+            'r',
+            InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
+            "The user's project role ('admin' or 'viewer') or 
environment-specific role (e.g. 'master:contributor' or 'stage:viewer')."
+            . "\nThe character % can be used as a wildcard in the environment 
ID e.g. '%:viewer'."
+            . "\nThe role can be abbreviated, e.g. 'master:c'."
+        );
     }
 
     protected function execute(InputInterface $input, OutputInterface $output)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/User/UserListCommand.php 
new/platformsh-cli-3.38.0/src/Command/User/UserListCommand.php
--- old/platformsh-cli-3.37.0/src/Command/User/UserListCommand.php      
2018-11-28 10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Command/User/UserListCommand.php      
2019-01-09 15:57:31.000000000 +0100
@@ -44,13 +44,22 @@
 
         ksort($rows);
 
+        if (!$table->formatIsMachineReadable()) {
+            $this->stdErr->writeln(sprintf(
+                'Users on the project %s:',
+                $this->api()->getProjectLabel($project)
+            ));
+        }
+
         $table->render(array_values($rows), ['email' => 'Email address', 
'Name', 'role' => 'Project role', 'ID']);
 
         if (!$table->formatIsMachineReadable()) {
             $this->stdErr->writeln('');
             $executable = $this->config()->get('application.executable');
+            $this->stdErr->writeln("To add a new user to the project, run: 
<info>$executable user:add [email]</info>");
+            $this->stdErr->writeln('');
             $this->stdErr->writeln("To view a user's role(s), run: 
<info>$executable user:get [email]</info>");
-            $this->stdErr->writeln("To change a user's role(s), run: 
<info>$executable user:add [email]</info>");
+            $this->stdErr->writeln("To change a user's role(s), run: 
<info>$executable user:update [email]</info>");
         }
 
         return 0;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/User/UserUpdateCommand.php 
new/platformsh-cli-3.38.0/src/Command/User/UserUpdateCommand.php
--- old/platformsh-cli-3.37.0/src/Command/User/UserUpdateCommand.php    
1970-01-01 01:00:00.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Command/User/UserUpdateCommand.php    
2019-01-09 15:57:31.000000000 +0100
@@ -0,0 +1,27 @@
+<?php
+namespace Platformsh\Cli\Command\User;
+
+use Symfony\Component\Console\Input\InputArgument;
+
+/**
+ * This command is the same as user:add, with different documentation.
+ */
+class UserUpdateCommand extends UserAddCommand
+{
+
+    protected function configure()
+    {
+        $this
+            ->setName('user:update')
+            ->setDescription('Update user role(s) on a project')
+            ->addArgument('email', InputArgument::OPTIONAL, "The user's email 
address");
+
+        $this->addRoleOption();
+        $this->addProjectOption();
+        $this->addWaitOptions();
+
+        $this->addExample('Make Bob an admin on the "develop" and "stage" 
environments', '[email protected] -r develop:a,stage:a');
+        $this->addExample('Make Charlie a contributor on all existing 
environments', '[email protected] -r %:c');
+        $this->addExample('Make Damien an admin on "master" and all (existing) 
environments starting with "pr-"', '[email protected] -r master:a -r pr-%:a');
+    }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.37.0/src/Command/WebCommand.php 
new/platformsh-cli-3.38.0/src/Command/WebCommand.php
--- old/platformsh-cli-3.37.0/src/Command/WebCommand.php        2018-11-28 
10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Command/WebCommand.php        2019-01-09 
15:57:31.000000000 +0100
@@ -39,9 +39,9 @@
 
         if ($this->hasSelectedProject()) {
             $url = $this->getSelectedProject()->getLink('#ui');
-            if (!empty($environmentId)) {
-                // New (alpha) UI links lack the /environments path component.
-                if (strpos($url, 'https://ui.') === 0) {
+            if ($environmentId !== null) {
+                // Unified-UI links lack the /environments path component.
+                if ($this->config()->has('detection.ui_domain') && 
parse_url($url, PHP_URL_HOST) === $this->config()->get('detection.ui_domain')) {
                     $url .= '/' . rawurlencode($environmentId);
                 } else {
                     $url .= '/environments/' . rawurlencode($environmentId);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Command/Worker/WorkerListCommand.php 
new/platformsh-cli-3.38.0/src/Command/Worker/WorkerListCommand.php
--- old/platformsh-cli-3.37.0/src/Command/Worker/WorkerListCommand.php  
2018-11-28 10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Command/Worker/WorkerListCommand.php  
2019-01-09 15:57:31.000000000 +0100
@@ -49,6 +49,15 @@
 
         /** @var \Platformsh\Cli\Service\Table $table */
         $table = $this->getService('table');
+
+        if (!$table->formatIsMachineReadable()) {
+            $this->stdErr->writeln(sprintf(
+                'Workers on the project <info>%s</info>, environment 
<info>%s</info>:',
+                $this->api()->getProjectLabel($this->getSelectedProject()),
+                
$this->api()->getEnvironmentLabel($this->getSelectedEnvironment())
+            ));
+        }
+
         $table->render($rows, ['Name', 'Type', 'Commands']);
 
         return 0;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/src/Local/DependencyInstaller.php 
new/platformsh-cli-3.38.0/src/Local/DependencyInstaller.php
--- old/platformsh-cli-3.37.0/src/Local/DependencyInstaller.php 2018-11-28 
10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Local/DependencyInstaller.php 2019-01-09 
15:57:31.000000000 +0100
@@ -108,6 +108,7 @@
         $stacks = [
             'nodejs' => new DependencyManager\Npm($this->shell),
             'python' => new DependencyManager\Pip($this->shell),
+            'python3' => new DependencyManager\Pip($this->shell),
             'ruby' => new DependencyManager\Bundler($this->shell),
             'php' => new DependencyManager\Composer($this->shell),
         ];
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.37.0/src/Service/Api.php 
new/platformsh-cli-3.38.0/src/Service/Api.php
--- old/platformsh-cli-3.37.0/src/Service/Api.php       2018-11-28 
10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Service/Api.php       2019-01-09 
15:57:31.000000000 +0100
@@ -623,9 +623,9 @@
     public function getProjectLabel(Project $project, $tag = 'info')
     {
         $title = $project->title;
-        $pattern = $title ? '%2$s (%3$s)' : '%3$s';
+        $pattern = strlen($title) > 0 ? '%2$s (%3$s)' : '%3$s';
         if ($tag !== false) {
-            $pattern = $title ? '<%1$s>%2$s</%1$s> (%3$s)' : 
'<%1$s>%3$s</%1$s>';
+            $pattern = strlen($title) > 0 ? '<%1$s>%2$s</%1$s> (%3$s)' : 
'<%1$s>%3$s</%1$s>';
         }
 
         return sprintf($pattern, $tag, $title, $project->id);
@@ -643,7 +643,7 @@
     {
         $id = $environment->id;
         $title = $environment->title;
-        $use_title = $title && $title !== $id;
+        $use_title = strlen($title) > 0 && $title !== $id;
         $pattern = $use_title ? '%2$s (%3$s)' : '%3$s';
         if ($tag !== false) {
             $pattern = $use_title ? '<%1$s>%2$s</%1$s> (%3$s)' : 
'<%1$s>%3$s</%1$s>';
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.37.0/src/Service/Identifier.php 
new/platformsh-cli-3.38.0/src/Service/Identifier.php
--- old/platformsh-cli-3.37.0/src/Service/Identifier.php        2018-11-28 
10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Service/Identifier.php        2019-01-09 
15:57:31.000000000 +0100
@@ -89,13 +89,8 @@
 
         $site_domains_pattern = '(' . implode('|', array_map('preg_quote', 
$this->config->get('detection.site_domains'))) . ')';
         $site_pattern = '/\-\w+\.[a-z]{2}(\-[0-9])?\.' . $site_domains_pattern 
. '$/';
-        if (strpos($path, '/projects/') !== false || strpos($fragment, 
'/projects/') !== false) {
-            $result['host'] = $host;
-            $result['projectId'] = 
basename(preg_replace('#/projects(/\w+)/?.*$#', '$1', $url));
-            if (preg_match('#/environments(/[^/]+)/?.*$#', $url, $matches)) {
-                $result['environmentId'] = rawurldecode(basename($matches[1]));
-            }
-        } elseif (preg_match($site_pattern, $host)) {
+
+        if (preg_match($site_pattern, $host)) {
             list($env_project_app,) = explode('.', $host, 2);
             if (($tripleDashPos = strrpos($env_project_app, '---')) !== false) 
{
                 $env_project_app = substr($env_project_app, $tripleDashPos + 
3);
@@ -110,6 +105,29 @@
                 $result['projectId'] = substr($env_project, $dashPos + 1);
                 $result['environmentId'] = substr($env_project, 0, $dashPos);
             }
+
+            return $result;
+        }
+
+        if (strpos($path, '/projects/') !== false || strpos($fragment, 
'/projects/') !== false) {
+            $result['host'] = $host;
+            $result['projectId'] = 
basename(preg_replace('#/projects(/\w+)/?.*$#', '$1', $url));
+            if (preg_match('#/environments(/[^/]+)/?.*$#', $url, $matches)) {
+                $result['environmentId'] = rawurldecode(basename($matches[1]));
+            }
+
+            return $result;
+        }
+
+        if ($this->config->has('detection.ui_domain')
+            && $host === $this->config->get('detection.ui_domain')
+            && preg_match('#^/[a-z0-9-]+/([a-z0-9-]+)(/([^/]+))?#', $path, 
$matches)) {
+            $result['projectId'] = $matches[1];
+            if (isset($matches[3])) {
+                $result['environmentId'] = rawurldecode($matches[3]);
+            }
+
+            return $result;
         }
 
         return $result;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.37.0/src/Service/QuestionHelper.php 
new/platformsh-cli-3.38.0/src/Service/QuestionHelper.php
--- old/platformsh-cli-3.37.0/src/Service/QuestionHelper.php    2018-11-28 
10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Service/QuestionHelper.php    2019-01-09 
15:57:31.000000000 +0100
@@ -85,12 +85,12 @@
             return key($items);
         }
         $itemList = array_values($items);
-        $defaultKey = $default !== null ? array_search($default, $itemList) : 
null;
+        $defaultKey = $default !== null ? array_search($default, $itemList, 
true) : null;
         $question = new ChoiceQuestion($text, $itemList, $defaultKey);
         $question->setMaxAttempts(5);
 
         $choice = $this->ask($this->input, $this->output, $question);
-        $choiceKey = array_search($choice, $items);
+        $choiceKey = array_search($choice, $items, true);
         if ($choiceKey === false) {
             throw new \RuntimeException("Invalid value: $choice");
         }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.37.0/src/Service/Table.php 
new/platformsh-cli-3.38.0/src/Service/Table.php
--- old/platformsh-cli-3.37.0/src/Service/Table.php     2018-11-28 
10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/src/Service/Table.php     2019-01-09 
15:57:31.000000000 +0100
@@ -223,7 +223,11 @@
         if (!empty($header)) {
             array_unshift($rows, $header);
         }
-        $this->output->write((new Csv($delimiter))->format($rows));
+        // RFC 4180 (the closest thing to a CSV standard) asks for CRLF line
+        // breaks, but these do not play nicely with POSIX shells whose
+        // default internal field separator (IFS) does not account for CR. So
+        // the line break character is forced as LF.
+        $this->output->write((new Csv($delimiter, "\n"))->format($rows));
     }
 
     /**
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/tests/Service/IdentifierTest.php 
new/platformsh-cli-3.38.0/tests/Service/IdentifierTest.php
--- old/platformsh-cli-3.37.0/tests/Service/IdentifierTest.php  1970-01-01 
01:00:00.000000000 +0100
+++ new/platformsh-cli-3.38.0/tests/Service/IdentifierTest.php  2019-01-09 
15:57:31.000000000 +0100
@@ -0,0 +1,100 @@
+<?php
+
+namespace Platformsh\Cli\Tests;
+
+use Platformsh\Cli\Service\Identifier;
+
+class IdentifierTest extends \PHPUnit_Framework_TestCase
+{
+
+    public function testIdentify()
+    {
+        $identifier = new Identifier();
+
+        $url = 'https://master-4jkbdba6zde2i.eu-2.platformsh.site';
+        $expected = [
+            'projectId' => '4jkbdba6zde2i',
+            'environmentId' => 'master',
+            'host' => null,
+            'appId' => null,
+        ];
+        $this->assertEquals($expected, $identifier->identify($url));
+
+        $url = 'https://master-4jkbdba6zde2i--foo.eu-2.platformsh.site';
+        $expected = [
+            'projectId' => '4jkbdba6zde2i',
+            'environmentId' => 'master',
+            'host' => null,
+            'appId' => 'foo',
+        ];
+        $this->assertEquals($expected, $identifier->identify($url));
+
+        $url = 'https://www---master-4jkbdba6zde2i.eu-2.platformsh.site';
+        $expected = [
+            'projectId' => '4jkbdba6zde2i',
+            'environmentId' => 'master',
+            'host' => null,
+            'appId' => null,
+        ];
+        $this->assertEquals($expected, $identifier->identify($url));
+
+        $url = 'https://eu-2.platform.sh/projects/4jkbdba6zde2i';
+        $expected = [
+            'projectId' => '4jkbdba6zde2i',
+            'environmentId' => null,
+            'host' => 'eu-2.platform.sh',
+            'appId' => null,
+        ];
+        $this->assertEquals($expected, $identifier->identify($url));
+
+        $url = 
'https://eu-2.platform.sh/projects/4jkbdba6zde2i/environments/bar';
+        $expected = [
+            'projectId' => '4jkbdba6zde2i',
+            'environmentId' => 'bar',
+            'host' => 'eu-2.platform.sh',
+            'appId' => null,
+        ];
+        $this->assertEquals($expected, $identifier->identify($url));
+
+        $url = 'https://ui.platform.sh/foo/4jkbdba6zde2i';
+        $expected = [
+            'projectId' => '4jkbdba6zde2i',
+            'environmentId' => null,
+            'host' => null,
+            'appId' => null,
+        ];
+        $this->assertEquals($expected, $identifier->identify($url));
+
+        $url = 'https://ui.platform.sh/foo/4jkbdba6zde2i/bar';
+        $expected = [
+            'projectId' => '4jkbdba6zde2i',
+            'environmentId' => 'bar',
+            'host' => null,
+            'appId' => null,
+        ];
+        $this->assertEquals($expected, $identifier->identify($url));
+    }
+
+    public function testIdentifyWithEnvironmentIdOf0()
+    {
+        $identifier = new Identifier();
+
+        $url = 
'https://eu-2.platform.sh/projects/4jkbdba6zde2i/environments/0';
+        $expected = [
+            'projectId' => '4jkbdba6zde2i',
+            'environmentId' => '0',
+            'host' => 'eu-2.platform.sh',
+            'appId' => null,
+        ];
+        $this->assertEquals($expected, $identifier->identify($url));
+
+        $url = 'https://ui.platform.sh/foo/4jkbdba6zde2i/0';
+        $expected = [
+            'projectId' => '4jkbdba6zde2i',
+            'environmentId' => '0',
+            'host' => null,
+            'appId' => null,
+        ];
+        $this->assertEquals($expected, $identifier->identify($url));
+    }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.37.0/tests/Service/TableServiceTest.php 
new/platformsh-cli-3.38.0/tests/Service/TableServiceTest.php
--- old/platformsh-cli-3.37.0/tests/Service/TableServiceTest.php        
2018-11-28 10:48:36.000000000 +0100
+++ new/platformsh-cli-3.38.0/tests/Service/TableServiceTest.php        
2019-01-09 15:57:31.000000000 +0100
@@ -29,7 +29,7 @@
             ['foo', 1, 2, 3],
             ['bar', 4, 5, 6],
         ];
-        $expected = (new Csv())->format([
+        $expected = (new Csv(',', "\n"))->format([
             ['Value 2', 'Name'],
             ['2', 'foo'],
             ['5', 'bar'],

++++++ 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     2018-11-29 00:56:03.022636751 +0100
+++ new/vendor/autoload.php     2019-01-09 19:05:00.765710176 +0100
@@ -4,4 +4,4 @@
 
 require_once __DIR__ . '/composer/autoload_real.php';
 
-return ComposerAutoloaderInit5a63cef765f5d3c7c18e50d3aff43b1c::getLoader();
+return ComposerAutoloaderInit434b3fb8d6425b9c0fdd81e20c14d3f7::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       2018-11-29 00:56:03.022636751 
+0100
+++ new/vendor/composer/autoload_real.php       2019-01-09 19:05:00.765710176 
+0100
@@ -2,7 +2,7 @@
 
 // autoload_real.php @generated by Composer
 
-class ComposerAutoloaderInit5a63cef765f5d3c7c18e50d3aff43b1c
+class ComposerAutoloaderInit434b3fb8d6425b9c0fdd81e20c14d3f7
 {
     private static $loader;
 
@@ -19,15 +19,15 @@
             return self::$loader;
         }
 
-        
spl_autoload_register(array('ComposerAutoloaderInit5a63cef765f5d3c7c18e50d3aff43b1c',
 'loadClassLoader'), true, true);
+        
spl_autoload_register(array('ComposerAutoloaderInit434b3fb8d6425b9c0fdd81e20c14d3f7',
 'loadClassLoader'), true, true);
         self::$loader = $loader = new \Composer\Autoload\ClassLoader();
-        
spl_autoload_unregister(array('ComposerAutoloaderInit5a63cef765f5d3c7c18e50d3aff43b1c',
 'loadClassLoader'));
+        
spl_autoload_unregister(array('ComposerAutoloaderInit434b3fb8d6425b9c0fdd81e20c14d3f7',
 '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\ComposerStaticInit5a63cef765f5d3c7c18e50d3aff43b1c::getInitializer($loader));
+            
call_user_func(\Composer\Autoload\ComposerStaticInit434b3fb8d6425b9c0fdd81e20c14d3f7::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\ComposerStaticInit5a63cef765f5d3c7c18e50d3aff43b1c::$files;
+            $includeFiles = 
Composer\Autoload\ComposerStaticInit434b3fb8d6425b9c0fdd81e20c14d3f7::$files;
         } else {
             $includeFiles = require __DIR__ . '/autoload_files.php';
         }
         foreach ($includeFiles as $fileIdentifier => $file) {
-            composerRequire5a63cef765f5d3c7c18e50d3aff43b1c($fileIdentifier, 
$file);
+            composerRequire434b3fb8d6425b9c0fdd81e20c14d3f7($fileIdentifier, 
$file);
         }
 
         return $loader;
     }
 }
 
-function composerRequire5a63cef765f5d3c7c18e50d3aff43b1c($fileIdentifier, 
$file)
+function composerRequire434b3fb8d6425b9c0fdd81e20c14d3f7($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     2018-11-29 00:56:03.022636751 
+0100
+++ new/vendor/composer/autoload_static.php     2019-01-09 19:05:00.765710176 
+0100
@@ -4,7 +4,7 @@
 
 namespace Composer\Autoload;
 
-class ComposerStaticInit5a63cef765f5d3c7c18e50d3aff43b1c
+class ComposerStaticInit434b3fb8d6425b9c0fdd81e20c14d3f7
 {
     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 = 
ComposerStaticInit5a63cef765f5d3c7c18e50d3aff43b1c::$prefixLengthsPsr4;
-            $loader->prefixDirsPsr4 = 
ComposerStaticInit5a63cef765f5d3c7c18e50d3aff43b1c::$prefixDirsPsr4;
-            $loader->classMap = 
ComposerStaticInit5a63cef765f5d3c7c18e50d3aff43b1c::$classMap;
+            $loader->prefixLengthsPsr4 = 
ComposerStaticInit434b3fb8d6425b9c0fdd81e20c14d3f7::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = 
ComposerStaticInit434b3fb8d6425b9c0fdd81e20c14d3f7::$prefixDirsPsr4;
+            $loader->classMap = 
ComposerStaticInit434b3fb8d6425b9c0fdd81e20c14d3f7::$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  2018-11-29 00:56:02.630633386 +0100
+++ new/vendor/composer/installed.json  2019-01-09 19:05:00.345706414 +0100
@@ -731,17 +731,17 @@
     },
     {
         "name": "platformsh/client",
-        "version": "v0.22.2",
-        "version_normalized": "0.22.2.0",
+        "version": "v0.23.0",
+        "version_normalized": "0.23.0.0",
         "source": {
             "type": "git",
             "url": "https://github.com/platformsh/platformsh-client-php.git";,
-            "reference": "e487f9fbb9f79b404dcfd19e633d13cade7bce6b"
+            "reference": "9b0fc3004d5abdc3cc54b0bea80f779e23660289"
         },
         "dist": {
             "type": "zip",
-            "url": 
"https://api.github.com/repos/platformsh/platformsh-client-php/zipball/e487f9fbb9f79b404dcfd19e633d13cade7bce6b";,
-            "reference": "e487f9fbb9f79b404dcfd19e633d13cade7bce6b",
+            "url": 
"https://api.github.com/repos/platformsh/platformsh-client-php/zipball/9b0fc3004d5abdc3cc54b0bea80f779e23660289";,
+            "reference": "9b0fc3004d5abdc3cc54b0bea80f779e23660289",
             "shasum": ""
         },
         "require": {
@@ -754,7 +754,7 @@
         "require-dev": {
             "phpunit/phpunit": "~4.5"
         },
-        "time": "2018-10-24T20:17:25+00:00",
+        "time": "2019-01-09T14:36:49+00:00",
         "type": "library",
         "extra": {
             "patches": {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/platformsh/client/src/Model/Activity.php 
new/vendor/platformsh/client/src/Model/Activity.php
--- old/vendor/platformsh/client/src/Model/Activity.php 2018-10-24 
22:17:25.000000000 +0200
+++ new/vendor/platformsh/client/src/Model/Activity.php 2019-01-09 
15:36:49.000000000 +0100
@@ -104,9 +104,19 @@
     /**
      * Restore the backup associated with this activity.
      *
+     * @param string|null $target     The name of the target environment to
+     *                                which the backup should be restored (this
+     *                                could be the name of an existing
+     *                                environment, or a new environment). Leave
+     *                                this null to restore to the backup's
+     *                                original environment.
+     * @param string|null $branchFrom If a new environment will be created
+     *                                (depending on $target), this specifies
+     *                                the name of the parent branch.
+     *
      * @return Activity
      */
-    public function restore()
+    public function restore($target = null, $branchFrom = null)
     {
         if ($this->getProperty('type') !== 'environment.backup') {
             throw new \BadMethodCallException('Cannot restore activity (wrong 
type)');
@@ -115,7 +125,15 @@
             throw new \BadMethodCallException('Cannot restore backup (not 
complete)');
         }
 
-        return $this->runLongOperation('restore');
+        $options = [];
+        if ($target !== null) {
+            $options['environment_name'] = $target;
+        }
+        if ($branchFrom !== null) {
+            $options['branch_from'] = $branchFrom;
+        }
+
+        return $this->runLongOperation('restore', 'post', $options);
     }
 
     /**
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/platformsh/client/src/Session/Storage/File.php 
new/vendor/platformsh/client/src/Session/Storage/File.php
--- old/vendor/platformsh/client/src/Session/Storage/File.php   2018-10-24 
22:17:25.000000000 +0200
+++ new/vendor/platformsh/client/src/Session/Storage/File.php   2019-01-09 
15:36:49.000000000 +0100
@@ -13,16 +13,62 @@
     protected $directory;
 
     /**
-     * @param string $directory A directory where session files will be saved
-     *                          (default: ~/.platformsh/.session)
+     * @param string|null $directory
+     *   A writable directory where session files will be saved. Leave null
+     *   to use the default.
      */
     public function __construct($directory = null)
     {
-        $this->directory = $directory ?: $this->getHomeDirectory() . 
'/.platformsh/.session';
+        $this->directory = $directory ?: $this->getDefaultDirectory();
     }
 
     /**
-     * @throws \Exception
+     * Get the default directory for session files.
+     *
+     * @return string
+     */
+    protected function getDefaultDirectory()
+    {
+        // Default to ~/.platformsh/.session, but if it's not writable, fall
+        // back to the temporary directory.
+        $default = $this->getHomeDirectory() . '/.platformsh/.session';
+        if ($this->canWrite($default)) {
+            return $default;
+        }
+        $temp = sys_get_temp_dir() . '/.platformsh-client/.session';
+        if ($this->canWrite($temp)) {
+            return $temp;
+        }
+
+        throw new \RuntimeException('Unable to find a writable session storage 
directory');
+    }
+
+    /**
+     * Tests whether a file path is writable (even if it doesn't exist).
+     *
+     * @param string $path
+     *
+     * @return bool
+     */
+    protected function canWrite($path)
+    {
+        if (is_writable($path)) {
+            return true;
+        }
+
+        $current = $path;
+        while (!file_exists($current) && ($parent = dirname($current)) && 
$parent !== $current) {
+            if (is_writable($parent)) {
+                return true;
+            }
+            $current = $parent;
+        }
+
+        return false;
+    }
+
+    /**
+     * Finds the user's home directory.
      *
      * @return string
      */
@@ -33,7 +79,7 @@
             $home = $userProfile;
         }
         if (!$home || !is_dir($home)) {
-            throw new \Exception('Could not determine home directory');
+            throw new \RuntimeException('Could not determine home directory');
         }
 
         return $home;


Reply via email to