Hello community,

here is the log from the commit of package platformsh-cli for openSUSE:Factory 
checked in at 2020-08-14 10:05:11
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/platformsh-cli (Old)
 and      /work/SRC/openSUSE:Factory/.platformsh-cli.new.3399 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "platformsh-cli"

Fri Aug 14 10:05:11 2020 rev:103 rq:826562 version:3.61.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/platformsh-cli/platformsh-cli.changes    
2020-07-24 09:59:45.685632153 +0200
+++ /work/SRC/openSUSE:Factory/.platformsh-cli.new.3399/platformsh-cli.changes  
2020-08-14 10:05:34.221355028 +0200
@@ -1,0 +2,41 @@
+Thu Aug 13 18:32:04 UTC 2020 - [email protected]
+
+- Update to version 3.61.0:
+  * Release v3.61.0
+  * Add proxy support to the installer (#953)
+  * Provide autocomplete choice of projects if there are many
+  * Use --globoff by default in curl commands
+  * Disable use of SSH agent unless explicitly enabled in the user config file
+  * Explicitly request the offline_access scope when logging in
+  * Add a comment to auto-generated temporary SSH keys (#970)
+  * Release v3.60.4
+  * Restore PLATFORMSH_CLI_ACCOUNTS_API environment variable option for 
backwards compatibility
+  * Release v3.60.3
+  * Fix locating projects that are not in the user's list
+  * Release v3.60.2
+  * Ignore project-level variable activities (#968)
+  * Detect the SSH version and show appropriate messages (#969)
+  * Refine --no-refresh help in the ssh-cert:info command
+  * Fix extra prompt for input in mongodump command
+  * Add hidden ssh-cert:info utility/debug command
+  * Revamp config to no longer use a service.accounts_url nor an 
api.accounts_api_url
+  * Fix Match syntax with multiple hosts
+  * Release v3.60.1
+  * Add the SSH certificate to the SSH agent, if possible (#963)
+  * Clean up the "domain already claimed" error in the domain:add command
+  * Put host condition back on "Match ... exec"
+  * Use ed25519 for autogenerated SSH keys
+  * Always create a new SSH key when generating a certificate
+  * README: add extensions needed by cli (#958)
+  * Release v3.60.0
+  * The root SSH config file should have a Host line too
+  * Add comments to the session-specific SSH config
+  * Support multiple domain wildcards in SSH config
+  * SSH config: end the Host block explicitly with "Host *" (#960)
+  * Autoload SSH certificates on login or SSH
+  * Show the ssh-cert:load command in the list
+  * Avoid unnecessary SSH messages in verbose mode
+  * Installer: allow iconv as an alternative to mbstring
+  * Fix detection of internal vs external Git domains for platform:get (get) 
command errors
+
+-------------------------------------------------------------------

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

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

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

Other differences:
------------------
++++++ platformsh-cli.spec ++++++
--- /var/tmp/diff_new_pack.RZFlIo/_old  2020-08-14 10:05:36.465356165 +0200
+++ /var/tmp/diff_new_pack.RZFlIo/_new  2020-08-14 10:05:36.465356165 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           platformsh-cli
-Version:        3.59.1
+Version:        3.61.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.RZFlIo/_old  2020-08-14 10:05:36.497356182 +0200
+++ /var/tmp/diff_new_pack.RZFlIo/_new  2020-08-14 10:05:36.497356182 +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.59.1</param>
+    <param name="revision">refs/tags/v3.61.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.RZFlIo/_old  2020-08-14 10:05:36.517356192 +0200
+++ /var/tmp/diff_new_pack.RZFlIo/_new  2020-08-14 10:05:36.517356192 +0200
@@ -1,6 +1,6 @@
 <servicedata>
   <service name="tar_scm">
     <param name="url">git://github.com/platformsh/platformsh-cli.git</param>
-    <param 
name="changesrevision">3854cfd67303add357cece3c5f330ac9e443bf33</param>
+    <param 
name="changesrevision">8e6262a3ef2ec7b2b16a6675fb34fc2300489f46</param>
   </service>
 </servicedata>

++++++ licenses.txt ++++++
--- /var/tmp/diff_new_pack.RZFlIo/_old  2020-08-14 10:05:36.557356212 +0200
+++ /var/tmp/diff_new_pack.RZFlIo/_new  2020-08-14 10:05:36.557356212 +0200
@@ -16,7 +16,7 @@
 padraic/phar-updater                v1.0.6   BSD-3-Clause  
 paragonie/random_compat             v2.0.18  MIT           
 pjcdawkins/guzzle-oauth2-plugin     v2.3.1   MIT           
-platformsh/client                   v0.35.2  MIT           
+platformsh/client                   v0.37.2  MIT           
 platformsh/console-form             v0.0.24  MIT           
 psr/container                       1.0.0    MIT           
 psr/log                             1.1.3    MIT           

++++++ platformsh-cli-3.59.1.tar.xz -> platformsh-cli-3.61.0.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/README.md 
new/platformsh-cli-3.61.0/README.md
--- old/platformsh-cli-3.59.1/README.md 2020-07-21 15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/README.md 2020-08-13 13:27:12.000000000 +0200
@@ -5,7 +5,8 @@
 ## Requirements
 
 * Operating system: Linux, OS X, or Windows 10
-* PHP 5.5.9 or higher, with cURL support
+* PHP 5.5.9 or higher, with the following extensions: `curl`, `json`,
+ `mbstring`, `pcre`, and `phar`. The installation command will check for these.
 * Git
 * A Bash-like shell:
   * On OS X or Linux/Unix: SH, Bash, Dash or ZSH - usually the built-in shell 
will work.
@@ -182,6 +183,8 @@
   service:mongo:restore (mongorestore)      Restore a binary archive dump of 
data into MongoDB
   service:mongo:shell (mongo)               Use the MongoDB shell
   service:redis-cli (redis)                 Access the Redis CLI
+ssh-cert
+  ssh-cert:load                             Generate an SSH certificate
 ssh-key
   ssh-key:add                               Add a new SSH key
   ssh-key:delete                            Delete an SSH key
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/composer.json 
new/platformsh-cli-3.61.0/composer.json
--- old/platformsh-cli-3.59.1/composer.json     2020-07-21 15:02:11.000000000 
+0200
+++ new/platformsh-cli-3.61.0/composer.json     2020-08-13 13:27:12.000000000 
+0200
@@ -8,7 +8,7 @@
         "guzzlehttp/guzzle": "^5.3",
         "guzzlehttp/ringphp": "^1.1",
         "platformsh/console-form": ">=0.0.24 <2.0",
-        "platformsh/client": ">=0.35.2 <2.0",
+        "platformsh/client": ">=0.37.2 <2.0",
         "symfony/console": "^3.0 >=3.2",
         "symfony/yaml": "^3.0 || ^2.6",
         "symfony/finder": "^3.0",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/composer.lock 
new/platformsh-cli-3.61.0/composer.lock
--- old/platformsh-cli-3.59.1/composer.lock     2020-07-21 15:02:11.000000000 
+0200
+++ new/platformsh-cli-3.61.0/composer.lock     2020-08-13 13:27:12.000000000 
+0200
@@ -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": "34395bedceaf33b0af253532c389a178",
+    "content-hash": "622a98f40b55807f6d8bb25538914875",
     "packages": [
         {
             "name": "cocur/slugify",
@@ -665,16 +665,16 @@
         },
         {
             "name": "platformsh/client",
-            "version": "v0.35.2",
+            "version": "v0.37.2",
             "source": {
                 "type": "git",
                 "url": 
"https://github.com/platformsh/platformsh-client-php.git";,
-                "reference": "ce7f949dc6788ee90b8172d991845a5a66788531"
+                "reference": "2f63f43d16faa370895f90f1e6febd5519e2664d"
             },
             "dist": {
                 "type": "zip",
-                "url": 
"https://api.github.com/repos/platformsh/platformsh-client-php/zipball/ce7f949dc6788ee90b8172d991845a5a66788531";,
-                "reference": "ce7f949dc6788ee90b8172d991845a5a66788531",
+                "url": 
"https://api.github.com/repos/platformsh/platformsh-client-php/zipball/2f63f43d16faa370895f90f1e6febd5519e2664d";,
+                "reference": "2f63f43d16faa370895f90f1e6febd5519e2664d",
                 "shasum": ""
             },
             "require": {
@@ -704,7 +704,7 @@
                 }
             ],
             "description": "Platform.sh API client",
-            "time": "2020-07-21T07:54:05+00:00"
+            "time": "2020-08-05T09:08:05+00:00"
         },
         {
             "name": "platformsh/console-form",
@@ -3448,5 +3448,6 @@
     "platform-dev": [],
     "platform-overrides": {
         "php": "5.5.9"
-    }
+    },
+    "plugin-api-version": "1.1.0"
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/config.yaml 
new/platformsh-cli-3.61.0/config.yaml
--- old/platformsh-cli-3.59.1/config.yaml       2020-07-21 15:02:11.000000000 
+0200
+++ new/platformsh-cli-3.61.0/config.yaml       2020-08-13 13:27:12.000000000 
+0200
@@ -85,7 +85,10 @@
   applications_config_file: '.platform/applications.yaml'
   docs_url: 'https://docs.platform.sh'
   docs_search_url: 
'https://www.google.com/search?q=site%3Adocs.platform.sh%20{{ terms }}'
-  accounts_url: 'https://accounts.platform.sh'
+  api_tokens_url: 'https://accounts.platform.sh/user/api-tokens'
+  register_url: 'https://auth.api.platform.sh'
+  reset_password_url: 'https://auth.api.platform.sh/reset-password'
+  console_url: 'https://console.platform.sh'
   pricing_url: 'https://platform.sh/pricing'
   api_token_help_url: 
'https://docs.platform.sh/gettingstarted/cli/api-tokens.html'
   available_regions:
@@ -113,9 +116,6 @@
   # Overridden by {application.env_prefix}API_URL env var.
   base_url: 'https://api.platform.sh/'
 
-  # Overridden by {application.env_prefix}ACCOUNTS_API env var.
-  accounts_api_url: 'https://accounts.platform.sh/api/v1/'
-
   # Overridden by {application.env_prefix}OAUTH2_AUTH_URL env var.
   oauth2_auth_url:  'https://auth.api.platform.sh/oauth2/authorize'
 
@@ -141,13 +141,17 @@
   # Overridden by {application.env_prefix}CERTIFIER_URL env var.
   certifier_url: 'https://ssh.api.platform.sh'
 
-  # Wildcard domain for SSH configuration.
-  # Overridden by {application.env_prefix}SSH_DOMAIN_WILDCARD env var.
-  ssh_domain_wildcard: '*.platform.sh'
+  # Wildcard domains for SSH configuration.
+  # Can be replaced by a single value using the 
{application.env_prefix}SSH_DOMAIN_WILDCARD env var.
+  ssh_domain_wildcards: ['*.platform.sh']
+
+  # Whether auto-generated SSH certificate identities should be added to the 
SSH agent.
+  # Enabling this may be useful for those who use agent forwarding.
+  add_to_ssh_agent: false
 
   # Whether to auto-load an SSH certificate on login and SSH commands.
   # Overridden by {application.env_prefix}AUTO_LOAD_SSH_CERT env var
-  auto_load_ssh_cert: false
+  auto_load_ssh_cert: true
 
   # Whether the API supports project invitations.
   invitations: true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/dist/installer.php 
new/platformsh-cli-3.61.0/dist/installer.php
--- old/platformsh-cli-3.59.1/dist/installer.php        2020-07-21 
15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/dist/installer.php        2020-08-13 
13:27:12.000000000 +0200
@@ -52,6 +52,7 @@
     private $configDir;
     private $executable;
     private $cliName;
+    private $userAgent;
     private $pharName;
     private $argv;
 
@@ -69,6 +70,12 @@
         $this->configDir = '.platformsh';
         $this->executable = 'platform';
         $this->cliName = 'Platform.sh CLI';
+        $this->userAgent = sprintf(
+            'platformsh-cli-installer (%s; %s; PHP %s)',
+            php_uname('s'),
+            php_uname('r'),
+            PHP_VERSION
+        );
         $this->pharName = $this->executable . '.phar';
     }
 
@@ -120,8 +127,6 @@
         );
 
         $required_extensions = [
-            // Either mbstring or iconv is required by Symfony Console (even 
though this is not enforced in its composer.json).
-            'mbstring',
             'openssl',
             'pcre',
         ];
@@ -135,6 +140,15 @@
             );
         }
 
+        // Either mbstring or iconv is required by Symfony Console (even 
though this is not enforced in its composer.json).
+        $this->check(
+            'One or both of the "mbstring" or "iconv" PHP extensions is 
installed.',
+            'One or both of the "mbstring" or "iconv" PHP extensions is 
required.',
+            function () {
+                return \extension_loaded('mbstring') || 
\extension_loaded('iconv');
+            }
+        );
+
         $this->check(
             'The "curl" PHP extension is installed.',
             'The "curl" PHP extension is strongly recommended.',
@@ -212,7 +226,7 @@
                 $url = str_replace($removePath, '/' . ltrim($url, '/'), 
$this->manifestUrl);
             }
 
-            if (!file_put_contents($this->pharName, file_get_contents($url))) {
+            if (!file_put_contents($this->pharName, file_get_contents($url, 
false, $this->getStreamContext(300)))) {
                 return TaskResult::failure('The download failed');
             }
 
@@ -301,7 +315,7 @@
      * @return TaskResult
      */
     private function findLatestVersion($manifestUrl) {
-        $manifest = file_get_contents($manifestUrl);
+        $manifest = file_get_contents($manifestUrl, false, 
$this->getStreamContext(15));
         if ($manifest === false) {
             return TaskResult::failure('Failed to download manifest file: ' . 
$manifestUrl);
         }
@@ -521,6 +535,48 @@
 
         return false;
     }
+
+    /**
+     * Constructs a stream context for downloading files.
+     *
+     * @param int $timeout
+     *
+     * @return resource
+     */
+    private function getStreamContext($timeout) {
+        $opts = [
+            'http' => [
+                'method' => 'GET',
+                'follow_location' => 1,
+                'timeout' => $timeout,
+                'user_agent' => $this->userAgent,
+            ],
+        ];
+        if ($proxy = $this->getProxy()) {
+            $opts['http']['proxy'] = $proxy;
+        }
+
+        return stream_context_create($opts);
+    }
+
+    /**
+     * Finds a proxy address based on the https_proxy or http_proxy 
environment variable.
+     *
+     * @return string|null
+     */
+    private function getProxy() {
+        // The proxy variables should be ignored in a non-CLI context.
+        // This check has probably already been run, but it's important.
+        if (PHP_SAPI !== 'cli') {
+            return null;
+        }
+        foreach (['https', 'http'] as $scheme) {
+            if ($proxy = getenv($scheme . '_proxy')) {
+                return $proxy;
+            }
+        }
+        return null;
+    }
 }
 
 class TaskResult {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/dist/manifest.json 
new/platformsh-cli-3.61.0/dist/manifest.json
--- old/platformsh-cli-3.59.1/dist/manifest.json        2020-07-21 
15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/dist/manifest.json        2020-08-13 
13:27:12.000000000 +0200
@@ -17,10 +17,10 @@
     },
     {
         "name": "platform.phar",
-        "sha1": "be6d7919c5a776dbcbdc7887c47d27651f5f49f3",
-        "sha256": 
"cd367d27beb28c7ee9852c5fbe5ae808f1dbff448d1ff8fed7ceab9a792b6839",
-        "url": 
"https://github.com/platformsh/platformsh-cli/releases/download/v3.59.1/platform.phar";,
-        "version": "3.59.1",
+        "sha1": "93f8a19ff692c19e60e07494a484ea613affef24",
+        "sha256": 
"6f5114c2a27a3d4631d857cf938eda184bbb431568bfbf006958a6dd3526323f",
+        "url": 
"https://github.com/platformsh/platformsh-cli/releases/download/v3.61.0/platform.phar";,
+        "version": "3.61.0",
         "php": {
             "min": "5.5.9"
         },
@@ -284,6 +284,16 @@
                 "notes": "* Support new user invitations\n* Installer 
improvements:\n  - Create a .zshrc file if it doesn't already exist, on ZSH.\n  
- Require the mbstring extension.",
                 "show from": "3.58.0",
                 "hide from": "3.59.0"
+            },
+            {
+                "notes": "New features:\n\n* Autoload SSH certificates on 
login or SSH, by default.\n    When supported by all relevant servers (soon), 
SSH certificates will:\n    - Enable support for multi-factor authentication on 
projects that require it for SSH access.\n    - Make it no longer necessary to 
add or administer SSH keys manually.\n\n  Note: SSH certificates can be checked 
or reloaded using the `ssh-cert:load` command.\n\nOther changes:\n\n* SSH 
configuration improvements:\n  - Denote top-level SSH configuration with a 
`Host` block, to avoid issues where a previous `Host` or `Match` configuration 
had not been closed.\n  - End the Host blocks explicitly with `Host *`\n* Fix 
detection of third-party Git domains for `project:get` (`get`) command errors.",
+                "show from": "3.59.0",
+                "hide from": "3.60.0"
+            },
+            {
+                "notes": "New features:\n\n* Project choice will now be via an 
autocomplete, rather than a list, if there\n  are too many to list (more than 
25, or more than the terminal height). Start\n  typing a project ID, or use the 
up and down arrows to scroll through the\n  autocomplete suggestions.\n* 
Similarly, the environment choice can now be via a list, if there are few\n  
enough to display like that, otherwise the older autocomplete will show.\n* The 
installer now supports a proxy defined with one of the `https_proxy` or\n  
`http_proxy` environment variables.\n\nOther changes:\n\n* Use --globoff by 
default in curl commands\n* Disable use of the SSH agent unless explicitly 
enabled in the user config file.\n  Add this to the user config file to 
automatically add keys/certificates to the SSH agent:\n  ```yaml\n  # 
~/.platformsh/config.yaml\n  api:\n    add_to_ssh_agent: true\n  ```\n* 
Explicitly request the `offline_access` scope when logging in.\n* Add a comment 
to auto-generated temporary SSH keys.",
+                "show from": "3.60.0",
+                "hide from": "3.61.0"
             }
         ]
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.59.1/resources/oauth-listener/index.php 
new/platformsh-cli-3.61.0/resources/oauth-listener/index.php
--- old/platformsh-cli-3.59.1/resources/oauth-listener/index.php        
2020-07-21 15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/resources/oauth-listener/index.php        
2020-08-13 13:27:12.000000000 +0200
@@ -12,6 +12,7 @@
     private $response;
     private $codeChallenge;
     private $prompt;
+    private $scope;
 
     public function __construct() {
         $required = [
@@ -31,6 +32,7 @@
         $this->file = $_ENV['CLI_OAUTH_FILE'];
         $this->prompt = $_ENV['CLI_OAUTH_PROMPT'];
         $this->codeChallenge = $_ENV['CLI_OAUTH_CODE_CHALLENGE'];
+        $this->scope = isset($_ENV['CLI_OAUTH_SCOPE']) ? 
$_ENV['CLI_OAUTH_SCOPE'] : '';
         $this->localUrl = $localUrl = 'http://127.0.0.1:' . 
$_SERVER['SERVER_PORT'];
         $this->response = new Response();
     }
@@ -48,6 +50,7 @@
             'response_type' => 'code',
             'code_challenge' => $this->codeChallenge,
             'code_challenge_method' => 'S256',
+            'scope' => $this->scope,
         ], null, '&', PHP_QUERY_RFC3986);
     }
 
@@ -185,7 +188,7 @@
 }
 ?>
 <!DOCTYPE html>
-<html>
+<html lang="en">
 <head>
     <meta charset="utf-8">
     <title><?php echo $response->title; ?></title>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/src/Application.php 
new/platformsh-cli-3.61.0/src/Application.php
--- old/platformsh-cli-3.59.1/src/Application.php       2020-07-21 
15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Application.php       2020-08-13 
13:27:12.000000000 +0200
@@ -196,6 +196,7 @@
         $commands[] = new Command\Backup\BackupListCommand();
         $commands[] = new Command\Backup\BackupRestoreCommand();
         $commands[] = new Command\SourceOperation\RunCommand();
+        $commands[] = new Command\SshCert\SshCertInfoCommand();
         $commands[] = new Command\SshCert\SshCertLoadCommand();
         $commands[] = new Command\SshKey\SshKeyAddCommand();
         $commands[] = new Command\SshKey\SshKeyDeleteCommand();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.59.1/src/Command/Auth/ApiTokenLoginCommand.php 
new/platformsh-cli-3.61.0/src/Command/Auth/ApiTokenLoginCommand.php
--- old/platformsh-cli-3.59.1/src/Command/Auth/ApiTokenLoginCommand.php 
2020-07-21 15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Command/Auth/ApiTokenLoginCommand.php 
2020-08-13 13:27:12.000000000 +0200
@@ -17,7 +17,6 @@
     protected function configure()
     {
         $service = $this->config()->get('service.name');
-        $accountsUrl = $this->config()->get('service.accounts_url');
         $executable = $this->config()->get('application.executable');
 
         $this->setName('auth:api-token-login');
@@ -28,9 +27,9 @@
         $this->setDescription('Log in to ' . $service . ' using an API token');
 
         $help = 'Use this command to log in to your ' . $service . ' account 
using an API token.'
-            . "\n\nYou can create an account at:\n    <info>" . $accountsUrl . 
'</info>'
+            . "\n\nYou can create an account at:\n    <info>" . 
$this->config()->get('service.register_url') . '</info>'
             . "\n\nIf you have an account, but you do not already have an API 
token, you can create one here:\n    <info>"
-            . $accountsUrl . '/user/api-tokens</info>'
+            . $this->config()->get('service.api_tokens_url') . '</info>'
             . "\n\nAlternatively, to log in to the CLI with a browser, run:\n  
  <info>"
             . $executable . ' auth:browser-login</info>';
         $this->setHelp($help);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.59.1/src/Command/Auth/BrowserLoginCommand.php 
new/platformsh-cli-3.61.0/src/Command/Auth/BrowserLoginCommand.php
--- old/platformsh-cli-3.59.1/src/Command/Auth/BrowserLoginCommand.php  
2020-07-21 15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Command/Auth/BrowserLoginCommand.php  
2020-08-13 13:27:12.000000000 +0200
@@ -145,6 +145,7 @@
             'CLI_OAUTH_AUTH_URL' => 
$this->config()->get('api.oauth2_auth_url'),
             'CLI_OAUTH_CLIENT_ID' => 
$this->config()->get('api.oauth2_client_id'),
             'CLI_OAUTH_PROMPT' => $input->getOption('force') ? 'consent 
select_account' : 'consent',
+            'CLI_OAUTH_SCOPE' => 'offline_access',
             'CLI_OAUTH_FILE' => $responseFile,
         ] + $this->getParentEnv());
         $process->setTimeout(null);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.59.1/src/Command/Auth/PasswordLoginCommand.php 
new/platformsh-cli-3.61.0/src/Command/Auth/PasswordLoginCommand.php
--- old/platformsh-cli-3.59.1/src/Command/Auth/PasswordLoginCommand.php 
2020-07-21 15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Command/Auth/PasswordLoginCommand.php 
2020-08-13 13:27:12.000000000 +0200
@@ -15,7 +15,6 @@
     protected function configure()
     {
         $service = $this->config()->get('service.name');
-        $accountsUrl = $this->config()->get('service.accounts_url');
         $executable = $this->config()->get('application.executable');
 
         $this->setName('auth:password-login');
@@ -27,9 +26,9 @@
         $this->setDescription('Log in to ' . $service . ' using a username and 
password');
 
         $help = 'Use this command to log in to your ' . $service . ' account 
in the terminal.'
-            . "\n\nYou can create an account at:\n    <info>" . $accountsUrl . 
'</info>'
+            . "\n\nYou can create an account at:\n    <info>" . 
$this->config()->get('service.register_url') . '</info>'
             . "\n\nIf you have an account, but you do not already have a 
password, you can set one here:\n    <info>"
-            . $accountsUrl . '/user/password</info>'
+            . $this->config()->get('service.reset_password_url') . '</info>'
             . "\n\nAlternatively, to log in to the CLI with a browser, run:\n  
  <info>"
             . $executable . ' auth:browser-login</info>'
             . "\n\n" . $this->getNonInteractiveAuthHelp();
@@ -134,7 +133,7 @@
                     '<error>Login failed. Please check your 
credentials.</error>',
                     '',
                     "Forgot your password? Or don't have a password yet? 
Visit:",
-                    '  <comment>' . 
$this->config()->get('service.accounts_url') . '/user/password</comment>',
+                    '  <comment>' . 
$this->config()->get('service.reset_password_url') . '</comment>',
                     '',
                 ]);
                 $this->configureAccount($input, $output);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/src/Command/CommandBase.php 
new/platformsh-cli-3.61.0/src/Command/CommandBase.php
--- old/platformsh-cli-3.59.1/src/Command/CommandBase.php       2020-07-21 
15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Command/CommandBase.php       2020-08-13 
13:27:12.000000000 +0200
@@ -27,6 +27,7 @@
 use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Output\ConsoleOutputInterface;
 use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Terminal;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
 
@@ -533,6 +534,7 @@
                 $this->debug('Selected environment ' . 
$this->api()->getEnvironmentLabel($environment) . ' based on branch name: ' . 
$currentBranch);
                 return $environment;
             }
+            $this->debug('No environment was found to match the current Git 
branch: ' . $currentBranch);
         }
 
         return false;
@@ -756,7 +758,7 @@
         $this->project = $detectCurrent ? $this->getCurrentProject() : false;
         if (!$this->project && isset($this->input) && 
$this->input->isInteractive()) {
             $projects = $this->api()->getProjects();
-            if (count($projects) > 0 && count($projects) < 25) {
+            if (count($projects) > 0) {
                 $this->debug('No project specified: offering a choice...');
                 $projectId = $this->offerProjectChoice($projects);
 
@@ -849,6 +851,7 @@
         }
 
         if ($selectDefaultEnv) {
+            $this->debug('No environment specified or detected: finding a 
default...');
             $environments = $this->api()->getEnvironments($this->project);
             $defaultId = $this->api()->getDefaultEnvironmentId($environments);
             if ($defaultId && isset($environments[$defaultId])) {
@@ -858,7 +861,7 @@
         }
 
         if ($required && isset($this->input) && $this->input->isInteractive()) 
{
-            $this->debug('No environment specified: offering a choice...');
+            $this->debug('No environment specified or detected: offering a 
choice...');
             $this->environment = 
$this->offerEnvironmentChoice($this->api()->getEnvironments($this->project));
             return;
         }
@@ -1101,35 +1104,52 @@
      * @return string
      *   The chosen project ID.
      */
-    protected final function offerProjectChoice(array $projects, $text = 
'Enter a number to choose a project:')
+    final protected function offerProjectChoice(array $projects, $text = 
'Enter a number to choose a project:')
     {
         if (!isset($this->input) || !isset($this->output) || 
!$this->input->isInteractive()) {
             throw new \BadMethodCallException('Not interactive: a project 
choice cannot be offered.');
         }
 
-        // Build and sort a list of project options.
+        /** @var \Platformsh\Cli\Service\QuestionHelper $questionHelper */
+        $questionHelper = $this->getService('question_helper');
+
+        if (count($projects) >= 25 || count($projects) > (new 
Terminal())->getHeight() - 3) {
+            $autocomplete = [];
+            foreach ($projects as $project) {
+                if ($project->title) {
+                    $autocomplete[$project->id] = $project->id . ' - 
<question>' . $project->title . '</question>';
+                } else {
+                    $autocomplete[$project->id] = $project->id;
+                }
+            }
+            asort($autocomplete, SORT_NATURAL | SORT_FLAG_CASE);
+            return $questionHelper->askInput('Enter a project ID', null, 
array_values($autocomplete), function ($value) use ($autocomplete) {
+                list($id, ) = explode(' - ', $value);
+                if (!isset($autocomplete[$id]) && 
!$this->api()->getProject($id)) {
+                    throw new \RuntimeException('Project not found: ' . $id);
+                }
+                return $id;
+            });
+        }
+
         $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');
-
-        $id = $questionHelper->choose($projectList, $text, null, false);
-
-        return $id;
+        return $questionHelper->choose($projectList, $text, null, false);
     }
 
     /**
      * Offers a choice of environments.
      *
      * @param Environment[] $environments
+     * @param string $text
      *
      * @return Environment
      */
-    protected final function offerEnvironmentChoice(array $environments)
+    final protected function offerEnvironmentChoice(array $environments, $text 
= 'Enter a number to choose an environment:')
     {
         if (!isset($this->input) || !isset($this->output) || 
!$this->input->isInteractive()) {
             throw new \BadMethodCallException('Not interactive: an environment 
choice cannot be offered.');
@@ -1139,19 +1159,30 @@
         $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);
+        if (count($environments) > (new Terminal())->getHeight() / 2) {
+            $ids = array_keys($environments);
+            sort($ids, SORT_NATURAL | SORT_FLAG_CASE);
+
+            $id = $questionHelper->askInput('Enter an environment ID', 
$default, array_keys($environments), function ($value) use ($environments) {
+                if (!isset($environments[$value])) {
+                    throw new \RuntimeException('Environment not found: ' . 
$value);
+                }
 
-        $id = $questionHelper->askInput('Environment ID', $default, 
array_keys($environments), function ($value) use ($environments) {
-            if (!isset($environments[$value])) {
-                throw new \RuntimeException('Environment not found: ' . 
$value);
+                return $value;
+            });
+        } else {
+            $environmentList = [];
+            foreach ($environments as $environment) {
+                $environmentList[$environment->id] = 
$this->api()->getEnvironmentLabel($environment, false);
             }
+            asort($environmentList, SORT_NATURAL | SORT_FLAG_CASE);
 
-            return $value;
-        });
+            if ($default) {
+                $text .= "\n" . 'Default: <question>' . $default . 
'</question>';
+            }
 
-        $this->stdErr->writeln('');
+            $id = $questionHelper->choose($environmentList, $text, $default);
+        }
 
         return $environments[$id];
     }
@@ -1633,9 +1664,11 @@
         $this->stdErr->writeln('You are logged in.');
 
         // Generate a new certificate from the certifier API.
+        /** @var \Platformsh\Cli\Service\SshConfig $sshConfig */
+        $sshConfig = $this->getService('ssh_config');
         /** @var \Platformsh\Cli\SshCert\Certifier $certifier */
         $certifier = $this->getService('certifier');
-        if ($certifier->isAutoLoadEnabled()) {
+        if ($certifier->isAutoLoadEnabled() && 
$sshConfig->checkRequiredVersion()) {
             $this->stdErr->writeln('');
             $this->stdErr->writeln('Generating SSH certificate...');
             try {
@@ -1650,8 +1683,6 @@
         // Write SSH configuration.
         /** @var \Platformsh\Cli\Service\QuestionHelper $questionHelper */
         $questionHelper = $this->getService('question_helper');
-        /** @var \Platformsh\Cli\Service\SshConfig $sshConfig */
-        $sshConfig = $this->getService('ssh_config');
         if ($sshConfig->configureSessionSsh()) {
             $sshConfig->addUserSshConfig($questionHelper);
         }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.59.1/src/Command/Domain/DomainCommandBase.php 
new/platformsh-cli-3.61.0/src/Command/Domain/DomainCommandBase.php
--- old/platformsh-cli-3.59.1/src/Command/Domain/DomainCommandBase.php  
2020-07-21 15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Command/Domain/DomainCommandBase.php  
2020-08-13 13:27:12.000000000 +0200
@@ -84,16 +84,26 @@
     protected function handleApiException(ClientException $e, Project $project)
     {
         $response = $e->getResponse();
-        if ($response !== null && $response->getStatusCode() === 403) {
+        if (!$response) {
+            throw $e;
+        }
+        if ($response->getStatusCode() === 403) {
             $project->ensureFull();
             $data = $project->getData();
             if (!$project->hasLink('#manage-domains')
                 && !empty($data['subscription']['plan'])
                 && $data['subscription']['plan'] === 'development') {
                 $this->stdErr->writeln('This project is on a Development plan. 
Upgrade the plan to add domains.');
+                return;
+            }
+        }
+        if ($response->getStatusCode() === 400) {
+            $data = $response->json();
+            if (isset($data['detail']) && strpos($data['detail'], 'already 
claimed') !== false) {
+                $this->stdErr->writeln('This domain is already claimed by 
another service.');
+                return;
             }
-        } else {
-            throw $e;
         }
+        throw $e;
     }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.59.1/src/Command/Project/ProjectGetCommand.php 
new/platformsh-cli-3.61.0/src/Command/Project/ProjectGetCommand.php
--- old/platformsh-cli-3.59.1/src/Command/Project/ProjectGetCommand.php 
2020-07-21 15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Command/Project/ProjectGetCommand.php 
2020-08-13 13:27:12.000000000 +0200
@@ -304,8 +304,17 @@
      */
     protected function suggestSshRemedies($gitUrl)
     {
-        $internalDomain = $this->config()->get('detection.git_domain');
-        $isInternal = substr($gitUrl, -strlen($internalDomain)) === 
$internalDomain;
+        // Remove the path from the git URI to get the SSH part.
+        $gitSshUri = '';
+        if (strpos($gitUrl, ':') !== false) {
+            list($gitSshUri,) = explode(':', $gitUrl, 2);
+        }
+
+        // Determine whether the URL is for an internal Git repository, as
+        // opposed to a third-party one (like GitLab/GitHub).
+        $internalDomainSuffix = $this->config()->get('detection.git_domain');
+        $isInternal = substr($gitSshUri, -strlen($internalDomainSuffix)) === 
$internalDomainSuffix;
+
         if (!$isInternal) {
             $this->stdErr->writeln('');
             $this->stdErr->writeln(
@@ -333,11 +342,10 @@
             $this->config()->get('application.executable')
         ));
 
-        if (strpos($gitUrl, ':') !== false) {
-            list($gitSshUrl,) = explode(':', $gitUrl, 2);
+        if ($gitSshUri !== '') {
             $this->stdErr->writeln('');
             $this->stdErr->writeln('You can test your connection to the Git 
server by running:');
-            $this->stdErr->writeln(sprintf('<comment>ssh -v %s</comment>', 
escapeshellarg($gitSshUrl)));
+            $this->stdErr->writeln(sprintf('<comment>ssh -v %s</comment>', 
escapeshellarg($gitSshUri)));
         }
     }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.59.1/src/Command/Project/Variable/ProjectVariableSetCommand.php
 
new/platformsh-cli-3.61.0/src/Command/Project/Variable/ProjectVariableSetCommand.php
--- 
old/platformsh-cli-3.59.1/src/Command/Project/Variable/ProjectVariableSetCommand.php
        2020-07-21 15:02:11.000000000 +0200
+++ 
new/platformsh-cli-3.61.0/src/Command/Project/Variable/ProjectVariableSetCommand.php
        2020-08-13 13:27:12.000000000 +0200
@@ -62,21 +62,14 @@
         }
 
         // Set the variable to a new value.
-        $result = $this->getSelectedProject()
+        $this->getSelectedProject()
                        ->setVariable($variableName, $variableValue, $json, 
!$supressBuild, !$supressRuntime);
 
         $this->stdErr->writeln("Variable <info>$variableName</info> set to: 
$variableValue");
 
-        $success = true;
-        if (!$result->countActivities()) {
-            $this->redeployWarning();
-        } elseif ($this->shouldWait($input)) {
-            /** @var \Platformsh\Cli\Service\ActivityMonitor $activityMonitor 
*/
-            $activityMonitor = $this->getService('activity_monitor');
-            $success = 
$activityMonitor->waitMultiple($result->getActivities(), 
$this->getSelectedProject());
-        }
+        $this->redeployWarning();
 
-        return $success ? 0 : 1;
+        return 0;
     }
 
     /**
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.59.1/src/Command/Service/MongoDB/MongoDumpCommand.php 
new/platformsh-cli-3.61.0/src/Command/Service/MongoDB/MongoDumpCommand.php
--- old/platformsh-cli-3.59.1/src/Command/Service/MongoDB/MongoDumpCommand.php  
2020-07-21 15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Command/Service/MongoDB/MongoDumpCommand.php  
2020-08-13 13:27:12.000000000 +0200
@@ -40,7 +40,6 @@
         $host = $this->selectHost($input, getenv($envPrefix . 'RELATIONSHIPS') 
!== false);
 
         if ($host instanceof RemoteHost) {
-            $this->validateInput($input);
             $appName = $this->selectApp($input);
         } else {
             $appName = getenv($envPrefix . 'APPLICATION_NAME');
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.59.1/src/Command/SshCert/SshCertInfoCommand.php 
new/platformsh-cli-3.61.0/src/Command/SshCert/SshCertInfoCommand.php
--- old/platformsh-cli-3.59.1/src/Command/SshCert/SshCertInfoCommand.php        
1970-01-01 01:00:00.000000000 +0100
+++ new/platformsh-cli-3.61.0/src/Command/SshCert/SshCertInfoCommand.php        
2020-08-13 13:27:12.000000000 +0200
@@ -0,0 +1,69 @@
+<?php
+namespace Platformsh\Cli\Command\SshCert;
+
+use Platformsh\Cli\Command\CommandBase;
+use Platformsh\Cli\Service\PropertyFormatter;
+use Platformsh\Cli\SshCert\Certificate;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class SshCertInfoCommand extends CommandBase
+{
+    protected $hiddenInList = true;
+
+    protected function configure()
+    {
+        $this
+            ->setName('ssh-cert:info')
+            ->setDescription('Display information about the current SSH 
certificate')
+            ->addOption('no-refresh', null, InputOption::VALUE_NONE, 'Do not 
refresh the certificate if it is invalid')
+            ->addOption('property', 'P', InputOption::VALUE_REQUIRED, 'The 
certificate property to display');
+        PropertyFormatter::configureInput($this->getDefinition());
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        // Initialize the API service to ensure event listeners etc.
+        $this->api();
+
+        /** @var \Platformsh\Cli\SshCert\Certifier $certifier */
+        $certifier = $this->getService('certifier');
+        /** @var \Platformsh\Cli\Service\SshConfig $sshConfig */
+        $sshConfig = $this->getService('ssh_config');
+
+        $cert = $certifier->getExistingCertificate();
+        if (!$cert || !$this->isValid($cert)) {
+            if ($input->getOption('no-refresh')) {
+                $this->stdErr->writeln('No valid SSH certificate found.');
+                $this->stdErr->writeln('To generate a certificate, run this 
command again without the <comment>--no-refresh</comment> option.');
+                return 1;
+            }
+            if (!$sshConfig->checkRequiredVersion()) {
+                return 1;
+            }
+            // Generate a new certificate.
+            $cert = $certifier->generateCertificate();
+        }
+
+        /** @var \Platformsh\Cli\Service\PropertyFormatter $formatter */
+        $formatter = $this->getService('property_formatter');
+        $properties = [
+            'filename' => $cert->certificateFilename(),
+            'key_filename' => $cert->privateKeyFilename(),
+            'key_id' => $cert->metadata()->getKeyId(),
+            'key_type' => $cert->metadata()->getKeyType(),
+            'valid_after' => 
$formatter->formatDate($cert->metadata()->getValidAfter()),
+            'valid_before' => 
$formatter->formatDate($cert->metadata()->getValidBefore()),
+            'extensions' => $cert->metadata()->getExtensions(),
+        ];
+
+        $formatter->displayData($output, $properties, 
$input->getOption('property'));
+
+        return 0;
+    }
+
+    private function isValid(Certificate $cert) {
+        return !$cert->hasExpired(0) && $cert->metadata()->getKeyId() === 
$this->api()->getMyAccount()['id'];
+    }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.59.1/src/Command/SshCert/SshCertLoadCommand.php 
new/platformsh-cli-3.61.0/src/Command/SshCert/SshCertLoadCommand.php
--- old/platformsh-cli-3.59.1/src/Command/SshCert/SshCertLoadCommand.php        
2020-07-21 15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Command/SshCert/SshCertLoadCommand.php        
2020-08-13 13:27:12.000000000 +0200
@@ -9,20 +9,20 @@
 
 class SshCertLoadCommand extends CommandBase
 {
-    protected $hiddenInList = true;
-
     protected function configure()
     {
         $this
             ->setName('ssh-cert:load')
             ->addOption('refresh-only', null, InputOption::VALUE_NONE, 'Only 
refresh the certificate, if necessary (do not write SSH config)')
             ->addOption('new', null, InputOption::VALUE_NONE, 'Force the 
certificate to be refreshed')
-            ->addOption('new-key', null, InputOption::VALUE_NONE, 'Force the 
certificate to be refreshed with a new SSH key pair')
-            ->setDescription('Generate a new certificate from the certifier 
API');
+            ->addOption('new-key', null, InputOption::VALUE_NONE, 
'[Deprecated] Use --new instead')
+            ->setDescription('Generate an SSH certificate');
     }
 
     protected function execute(InputInterface $input, OutputInterface $output)
     {
+        $this->warnAboutDeprecatedOptions(['new-key'], 'The --new-key option 
is deprecated. Use --new instead.');
+
         // Initialize the API service to ensure event listeners etc.
         $this->api();
 
@@ -42,15 +42,19 @@
             $refresh = false;
         }
 
+        /** @var \Platformsh\Cli\Service\SshConfig $sshConfig */
+        $sshConfig = $this->getService('ssh_config');
+
         if ($refresh) {
+            if (!$sshConfig->checkRequiredVersion()) {
+                return 1;
+            }
             $this->stdErr->writeln('Generating SSH certificate...');
-            $sshCert = 
$certifier->generateCertificate($input->getOption('new-key'));
+            $sshCert = $certifier->generateCertificate();
             $this->displayCertificate($sshCert);
         }
 
-        /** @var \Platformsh\Cli\Service\SshConfig $sshConfig */
-        $sshConfig = $this->getService('ssh_config');
-        $sshConfig->configureSessionSsh();
+        $hasSessionConfig = $sshConfig->configureSessionSsh();
 
         if ($input->getOption('refresh-only')) {
             return 0;
@@ -58,7 +62,7 @@
 
         /** @var \Platformsh\Cli\Service\QuestionHelper $questionHelper */
         $questionHelper = $this->getService('question_helper');
-        $success = $sshConfig->addUserSshConfig($questionHelper);
+        $success = !$hasSessionConfig || 
$sshConfig->addUserSshConfig($questionHelper);
 
         return $success ? 0 : 1;
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.59.1/src/Command/Variable/VariableCreateCommand.php 
new/platformsh-cli-3.61.0/src/Command/Variable/VariableCreateCommand.php
--- old/platformsh-cli-3.59.1/src/Command/Variable/VariableCreateCommand.php    
2020-07-21 15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Command/Variable/VariableCreateCommand.php    
2020-08-13 13:27:12.000000000 +0200
@@ -155,7 +155,7 @@
         $this->displayVariable($result->getEntity());
 
         $success = true;
-        if (!$result->countActivities()) {
+        if (!$result->countActivities() || $level === self::LEVEL_PROJECT) {
             $this->redeployWarning();
         } elseif ($this->shouldWait($input)) {
             /** @var \Platformsh\Cli\Service\ActivityMonitor $activityMonitor 
*/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.59.1/src/Command/Variable/VariableDeleteCommand.php 
new/platformsh-cli-3.61.0/src/Command/Variable/VariableDeleteCommand.php
--- old/platformsh-cli-3.59.1/src/Command/Variable/VariableDeleteCommand.php    
2020-07-21 15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Command/Variable/VariableDeleteCommand.php    
2020-08-13 13:27:12.000000000 +0200
@@ -81,7 +81,7 @@
         $this->stdErr->writeln("Deleted variable <info>$variableName</info>");
 
         $success = true;
-        if (!$result->countActivities()) {
+        if (!$result->countActivities() || $level === self::LEVEL_PROJECT) {
             $this->redeployWarning();
         } elseif ($this->shouldWait($input)) {
             /** @var \Platformsh\Cli\Service\ActivityMonitor $activityMonitor 
*/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/platformsh-cli-3.59.1/src/Command/Variable/VariableUpdateCommand.php 
new/platformsh-cli-3.61.0/src/Command/Variable/VariableUpdateCommand.php
--- old/platformsh-cli-3.59.1/src/Command/Variable/VariableUpdateCommand.php    
2020-07-21 15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Command/Variable/VariableUpdateCommand.php    
2020-08-13 13:27:12.000000000 +0200
@@ -84,7 +84,8 @@
         $this->displayVariable($variable);
 
         $success = true;
-        if (!$result->countActivities()) {
+
+        if (!$result->countActivities() || $level === self::LEVEL_PROJECT) {
             $this->redeployWarning();
         } elseif ($this->shouldWait($input)) {
             /** @var \Platformsh\Cli\Service\ActivityMonitor $activityMonitor 
*/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/src/Command/WebCommand.php 
new/platformsh-cli-3.61.0/src/Command/WebCommand.php
--- old/platformsh-cli-3.59.1/src/Command/WebCommand.php        2020-07-21 
15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Command/WebCommand.php        2020-08-13 
13:27:12.000000000 +0200
@@ -48,7 +48,7 @@
                 }
             }
         } else {
-            $url = $this->config()->get('service.accounts_url');
+            $url = $this->config()->getWithDefault('service.console_url', 
$this->config()->get('service.accounts_url'));
         }
 
         /** @var \Platformsh\Cli\Service\Url $urlService */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/src/Command/WelcomeCommand.php 
new/platformsh-cli-3.61.0/src/Command/WelcomeCommand.php
--- old/platformsh-cli-3.59.1/src/Command/WelcomeCommand.php    2020-07-21 
15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Command/WelcomeCommand.php    2020-08-13 
13:27:12.000000000 +0200
@@ -90,9 +90,7 @@
             $messages = [];
             $messages[] = '<comment>This project is suspended.</comment>';
             if ($project->owner === $this->api()->getMyAccount()['id']) {
-                $messages[] = '<comment>Update your payment details to 
re-activate it: '
-                    . $this->config()->get('service.accounts_url')
-                    . '</comment>';
+                $messages[] = '<comment>Update your payment details to 
re-activate it</comment>';
             }
             $messages[] = '';
             $this->stdErr->writeln($messages);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/src/Service/Api.php 
new/platformsh-cli-3.61.0/src/Service/Api.php
--- old/platformsh-cli-3.59.1/src/Service/Api.php       2020-07-21 
15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Service/Api.php       2020-08-13 
13:27:12.000000000 +0200
@@ -246,8 +246,10 @@
      */
     private function getConnectorOptions() {
         $connectorOptions = [];
-        $connectorOptions['accounts'] = 
rtrim($this->config->get('api.accounts_api_url'), '/') . '/';
         $connectorOptions['api_url'] = 
$this->config->getWithDefault('api.base_url', '');
+        if ($this->config->has('api.accounts_api_url')) {
+            $connectorOptions['accounts'] = 
$this->config->get('api.accounts_api_url');
+        }
         $connectorOptions['certifier_url'] = 
$this->config->get('api.certifier_url');
         $connectorOptions['verify'] = !$this->config->get('api.skip_ssl');
         $connectorOptions['debug'] = $this->config->get('api.debug') ? STDERR 
: false;
@@ -728,7 +730,7 @@
     {
         $data = $this->getMyAccount($reset);
 
-        return SshKey::wrapCollection($data['ssh_keys'], 
rtrim($this->config->get('api.accounts_api_url'), '/') . '/', 
$this->getHttpClient());
+        return SshKey::wrapCollection($data['ssh_keys'], 
rtrim($this->config->get('api.base_url'), '/') . '/', $this->getHttpClient());
     }
 
     /**
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/src/Service/Config.php 
new/platformsh-cli-3.61.0/src/Service/Config.php
--- old/platformsh-cli-3.59.1/src/Service/Config.php    2020-07-21 
15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Service/Config.php    2020-08-13 
13:27:12.000000000 +0200
@@ -308,15 +308,13 @@
             'DRUSH' => 'local.drush_executable',
             'SESSION_ID' => 'api.session_id',
             'SKIP_SSL' => 'api.skip_ssl',
-            'API_URL' => 'api.base_url',
             'ACCOUNTS_API' => 'api.accounts_api_url',
-            'ACCOUNTS_API_URL' => 'api.accounts_api_url',
+            'API_URL' => 'api.base_url',
             'OAUTH2_AUTH_URL' => 'api.oauth2_auth_url',
             'OAUTH2_TOKEN_URL' => 'api.oauth2_token_url',
             'OAUTH2_REVOKE_URL' => 'api.oauth2_revoke_url',
             'CERTIFIER_URL' => 'api.certifier_url',
             'AUTO_LOAD_SSH_CERT' => 'api.auto_load_ssh_cert',
-            'SSH_DOMAIN_WILDCARD' => 'api.ssh_domain_wildcard',
             'UPDATES_CHECK' => 'updates.check',
         ];
 
@@ -326,6 +324,11 @@
                 NestedArrayUtil::setNestedArrayValue($this->config, 
explode('.', $key), $value, true);
             }
         }
+
+        // Special case: replace the list api.ssh_domain_wildcards with the 
value of {PREFIX}SSH_DOMAIN_WILDCARD.
+        if (($value = $this->getEnv('SSH_DOMAIN_WILDCARD')) !== false) {
+            $this->config['api']['ssh_domain_wildcards'] = [$value];
+        }
     }
 
     /**
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/src/Service/CurlCli.php 
new/platformsh-cli-3.61.0/src/Service/CurlCli.php
--- old/platformsh-cli-3.59.1/src/Service/CurlCli.php   2020-07-21 
15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Service/CurlCli.php   2020-08-13 
13:27:12.000000000 +0200
@@ -25,6 +25,7 @@
         $definition->addOption(new InputOption('include', 'i', 
InputOption::VALUE_NONE, 'Include headers in the output'));
         $definition->addOption(new InputOption('head', 'I', 
InputOption::VALUE_NONE, 'Fetch headers only'));
         $definition->addOption(new InputOption('disable-compression', null, 
InputOption::VALUE_NONE, 'Do not use the curl --compressed flag'));
+        $definition->addOption(new InputOption('enable-glob', null, 
InputOption::VALUE_NONE, 'Enable curl globbing (remove the --globoff flag)'));
         $definition->addOption(new InputOption('header', 'H', 
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Extra header(s)'));
     }
 
@@ -77,6 +78,10 @@
             $commandline .= ' --compressed';
         }
 
+        if (!$input->getOption('enable-glob')) {
+            $commandline .= ' --globoff';
+        }
+
         foreach ($input->getOption('header') as $header) {
             $commandline .= ' --header ' . escapeshellarg($header);
         }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/src/Service/QuestionHelper.php 
new/platformsh-cli-3.61.0/src/Service/QuestionHelper.php
--- old/platformsh-cli-3.59.1/src/Service/QuestionHelper.php    2020-07-21 
15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Service/QuestionHelper.php    2020-08-13 
13:27:12.000000000 +0200
@@ -127,7 +127,7 @@
     public function askInput($questionText, $default = null, array 
$autoCompleterValues = [], callable $validator = null)
     {
         if ($default !== null) {
-            $questionText .= ' <question>[' . $default . ']</question>';
+            $questionText .= ' (default: <question>' . $default . 
'</question>)';
         }
         $questionText .= ': ';
         $question = new Question($questionText, $default);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/src/Service/Ssh.php 
new/platformsh-cli-3.61.0/src/Service/Ssh.php
--- old/platformsh-cli-3.59.1/src/Service/Ssh.php       2020-07-21 
15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Service/Ssh.php       2020-08-13 
13:27:12.000000000 +0200
@@ -84,8 +84,9 @@
             // Inject the SSH certificate.
             $sshCert = $this->certifier->getExistingCertificate();
             if ($sshCert || $this->certifier->isAutoLoadEnabled()) {
-                if (!$sshCert || $sshCert->hasExpired()) {
-                    $stdErr = $this->output instanceof ConsoleOutputInterface 
? $this->output->getErrorOutput() : $this->output;
+                $stdErr = $this->output instanceof ConsoleOutputInterface ? 
$this->output->getErrorOutput() : $this->output;
+
+                if ((!$sshCert || $sshCert->hasExpired()) && 
$this->sshConfig->checkRequiredVersion()) {
                     $stdErr->writeln('Generating SSH certificate...', 
OutputInterface::VERBOSITY_VERBOSE);
                     try {
                         $sshCert = $this->certifier->generateCertificate();
@@ -96,7 +97,9 @@
                 }
 
                 if ($sshCert) {
-                    $options['CertificateFile'] = 
$sshCert->certificateFilename();
+                    if ($this->sshConfig->supportsCertificateFile()) {
+                        $options['CertificateFile'] = 
$sshCert->certificateFilename();
+                    }
                     $options['IdentityFile'] = 
[$sshCert->privateKeyFilename()];
                     foreach 
($this->sshConfig->getUserDefaultSshIdentityFiles() as $identityFile) {
                         $options['IdentityFile'][] = $identityFile;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/src/Service/SshConfig.php 
new/platformsh-cli-3.61.0/src/Service/SshConfig.php
--- old/platformsh-cli-3.59.1/src/Service/SshConfig.php 2020-07-21 
15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Service/SshConfig.php 2020-08-13 
13:27:12.000000000 +0200
@@ -8,6 +8,7 @@
 use Symfony\Component\Console\Output\ConsoleOutputInterface;
 use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\Filesystem\Exception\IOException;
+use Symfony\Component\Process\Process;
 
 class SshConfig {
     private $config;
@@ -15,6 +16,7 @@
     private $stdErr;
     private $sshKey;
     private $certifier;
+    private $openSshVersion;
 
     public function __construct(Config $config, Filesystem $fs, 
OutputInterface $output, SshKey $sshKey, Certifier $certifier)
     {
@@ -33,12 +35,18 @@
      */
     public function configureSessionSsh()
     {
+        if (!$this->supportsInclude()) {
+            return false;
+        }
+
         // Backwards compatibility: delete the old SSH configuration file.
         $legacy = $this->getCliSshDir() . DIRECTORY_SEPARATOR . 
'sess-cli-default.config';
         if (\file_exists($legacy)) {
             $this->fs->remove($legacy);
         }
 
+        $domainWildcards = $this->config->get('api.ssh_domain_wildcards');
+
         $lines = [];
 
         if ($certificate = $this->certifier->getExistingCertificate()) {
@@ -47,14 +55,31 @@
             if (!OsUtil::isWindows()) {
                 $refreshCommand .= ' 2>/dev/null';
             }
-            $lines[] = sprintf('Match host %s exec "%s"', 
$this->config->get('api.ssh_domain_wildcard'), $refreshCommand);
-            $lines[] = sprintf('  CertificateFile %s', 
$certificate->certificateFilename());
-            $lines[] = sprintf('  IdentityFile %s', 
$certificate->privateKeyFilename());
+            // Use Match solely to run the refresh command.
+            $lines[] = '# Auto-refresh the SSH certificate:';
+            if ($domainWildcards) {
+                $lines[] = sprintf('Match host "%s" exec "%s"', \implode(',', 
$domainWildcards), $refreshCommand);
+            } else {
+                $lines[] = sprintf('Match exec "%s"', $refreshCommand);
+            }
+            $lines[] = '';
+
+            // Indentation in the SSH config is for readability (it has no 
other effect).
+            $lines[] = '# Include the certificate and its key:';
+            $lines[] = sprintf('CertificateFile %s', 
$certificate->certificateFilename());
+            $lines[] = sprintf('IdentityFile %s', 
$certificate->privateKeyFilename());
+            $lines[] = '';
+        }
+
+        if ($domainWildcards) {
+            $lines[] = 'Host ' . implode(' ', $domainWildcards);
         }
 
         $sessionIdentityFile = $this->sshKey->selectIdentity();
         if ($sessionIdentityFile !== null) {
+            $lines[] = '# This SSH key was detected as corresponding to the 
session:';
             $lines[] = sprintf('IdentityFile %s', $sessionIdentityFile);
+            $lines[] = '';
         }
 
         $sessionSpecificFilename = $this->getSessionSshDir() . 
DIRECTORY_SEPARATOR . 'config';
@@ -67,24 +92,31 @@
         }
 
         // Add default files if there is no preferred session identity file.
-        if ($sessionIdentityFile === null) {
-            $defaultFiles = $this->getUserDefaultSshIdentityFiles();
+        if ($sessionIdentityFile === null && ($defaultFiles = 
$this->getUserDefaultSshIdentityFiles())) {
+            $lines[] = '# Include SSH "default" identity files:';
             foreach ($defaultFiles as $identityFile) {
                 $lines[] = sprintf('IdentityFile %s', $identityFile);
             }
+            $lines[] = '';
         }
 
         $this->writeSshIncludeFile($sessionSpecificFilename, $lines);
-        $this->writeSshIncludeFile(
-            $includerFilename,
-            [
-                '# This file is included from your SSH config file 
(~/.ssh/config).',
-                '# In turn, it includes the configuration for the currently 
active CLI session.',
-                '# It is updated automatically when certain CLI commands are 
run.',
-                'Host ' . $this->config->get('api.ssh_domain_wildcard'),
-                '  Include ' . $sessionSpecificFilename,
-            ]
-        );
+
+        $includerLines = [
+            '# This file is included from your SSH config file 
(~/.ssh/config).',
+            '# In turn, it includes the configuration for the currently active 
CLI session.',
+            '# It is updated automatically when certain CLI commands are run.',
+        ];
+
+        $wildcards = $this->config->get('api.ssh_domain_wildcards');
+        if (count($wildcards)) {
+            $includerLines[] = 'Host ' . implode(' ', $wildcards);
+            $includerLines[] = '  Include ' . $sessionSpecificFilename;
+            $this->writeSshIncludeFile(
+                $includerFilename,
+                $includerLines
+            );
+        }
 
         return true;
     }
@@ -111,7 +143,7 @@
             $this->stdErr->writeln(sprintf('Updating SSH configuration include 
file: <info>%s</info>', $filename), OutputInterface::VERBOSITY_VERBOSE);
             $this->fs->writeFile($filename, $contents, false);
         } else {
-            $this->stdErr->writeln(sprintf('Validated SSH configuration 
include file: <info>%s</info>', $filename), OutputInterface::VERBOSITY_VERBOSE);
+            $this->stdErr->writeln(sprintf('Validated SSH configuration 
include file: <info>%s</info>', $filename), 
OutputInterface::VERBOSITY_VERY_VERBOSE);
         }
         $this->chmod($filename, 0600);
     }
@@ -135,9 +167,22 @@
      */
     public function addUserSshConfig(QuestionHelper $questionHelper)
     {
+        if (!$this->supportsInclude()) {
+            return false;
+        }
+
         $filename = $this->getUserSshConfigFilename();
 
-        $suggestedConfig = 'Include ' . $this->getCliSshDir() . 
DIRECTORY_SEPARATOR . '*.config';
+        $wildcards = $this->config->get('api.ssh_domain_wildcards');
+        if (!$wildcards) {
+            return true;
+        }
+
+        $suggestedConfig = \implode("\n", [
+            'Host ' . \implode(' ', $wildcards),
+            '  Include ' . $this->getCliSshDir() . DIRECTORY_SEPARATOR . 
'*.config',
+            'Host *',
+        ]);
 
         $manualMessage = 'To configure SSH manually, add the following lines 
to: <comment>' . $filename . '</comment>'
             . "\n" . $suggestedConfig;
@@ -321,4 +366,96 @@
             $this->stdErr->writeln('The configuration could not be 
automatically removed: ' . $e->getMessage());
         }
     }
+
+    /**
+     * Finds the locally installed OpenSSH version.
+     *
+     * @param bool $reset
+     *
+     * @return string|false
+     */
+    private function findVersion($reset = false)
+    {
+        if (isset($this->openSshVersion) && !$reset) {
+            return $this->openSshVersion;
+        }
+        $this->openSshVersion = false;
+        $process = new Process('ssh -V');
+        $process->run();
+        $errorOutput = $process->getErrorOutput();
+        if (!$process->isSuccessful()) {
+            if ($this->stdErr->isVerbose()) {
+                $this->stdErr->writeln('Unable to determine the installed 
OpenSSH version. The command output was:');
+                $this->stdErr->writeln($errorOutput);
+            }
+            return false;
+        }
+        if (\preg_match('/OpenSSH_([0-9.]+[^ ,]*)/', $errorOutput, $matches)) {
+            $this->openSshVersion = $matches[1];
+        }
+        return $this->openSshVersion;
+    }
+
+    /**
+     * Checks if the installed OpenSSH version is below a given value.
+     *
+     * @param string $test
+     *
+     * @return bool
+     *   True if the version is determined and it is lower than the $test 
value, false otherwise.
+     */
+    private function versionIsBelow($test)
+    {
+        $version = $this->findVersion();
+        if (!$version) {
+            return false;
+        }
+        return \version_compare($version, $test, '<');
+    }
+
+    /**
+     * Checks if the installed OpenSSH version supports the 'Include' syntax.
+     *
+     * @return bool
+     */
+    public function supportsInclude() {
+        return !$this->versionIsBelow('7.3');
+    }
+
+    /**
+     * Checks if the installed OpenSSH version supports the 'CertificateFile' 
syntax.
+     *
+     * @return bool
+     */
+    public function supportsCertificateFile() {
+        return !$this->versionIsBelow('7.2');
+    }
+
+    /**
+     * Checks if the required version is supported, and prints a warning.
+     *
+     * @return bool
+     *   False if an incorrect version is installed, true otherwise.
+     */
+    public function checkRequiredVersion()
+    {
+        $version = $this->findVersion();
+        if (!$version) {
+            return true;
+        }
+        if (\version_compare($version, '6.5', '<')) {
+            $this->stdErr->writeln(\sprintf(
+                'OpenSSH version <error>%s</error> is installed. Version 6.5 
or above is required. Some features depend on version 7.3 or above.',
+                $version
+            ));
+            return false;
+        }
+        if (\version_compare($version, '7.3', '<')) {
+            $this->stdErr->writeln(\sprintf(
+                'OpenSSH version <comment>%s</comment> is installed. Some 
features depend on version 7.3 or above.',
+                $version
+            ));
+        }
+        return true;
+    }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/src/Service/SshKey.php 
new/platformsh-cli-3.61.0/src/Service/SshKey.php
--- old/platformsh-cli-3.59.1/src/Service/SshKey.php    2020-07-21 
15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/Service/SshKey.php    2020-08-13 
13:27:12.000000000 +0200
@@ -29,11 +29,20 @@
      * accounts, and (2) if the user has only one key, but it is in a
      * non-standard location.
      *
+     * @param bool $reset
+     *
      * @return string|null
      *   An absolute filename of an SSH private key, or null if there is no
      *   selected key.
      */
-    public function selectIdentity() {
+    public function selectIdentity($reset = false) {
+        // Cache, mainly to avoid repetition of the output message.
+        static $selectedIdentity = false;
+        if (!$reset && $selectedIdentity !== false) {
+            return $selectedIdentity;
+        }
+        $selectedIdentity = null;
+
         $accountKeyFingerprints = $this->listAccountKeyFingerprints();
         if (!$accountKeyFingerprints) {
             return null;
@@ -52,7 +61,7 @@
 
         if ($key = 
$this->findIdentityMatchingPublicKeys($accountKeyFingerprints)) {
             $this->stdErr->writeln(sprintf('Automatically selected SSH 
identity: <info>%s</info>', $key), OutputInterface::VERBOSITY_VERBOSE);
-            return $key;
+            return $selectedIdentity = $key;
         }
 
         return null;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/platformsh-cli-3.59.1/src/SshCert/Certifier.php 
new/platformsh-cli-3.61.0/src/SshCert/Certifier.php
--- old/platformsh-cli-3.59.1/src/SshCert/Certifier.php 2020-07-21 
15:02:11.000000000 +0200
+++ new/platformsh-cli-3.61.0/src/SshCert/Certifier.php 2020-08-13 
13:27:12.000000000 +0200
@@ -11,7 +11,8 @@
 
 class Certifier
 {
-    const PRIVATE_KEY_FILENAME = 'id_rsa';
+    const KEY_ALGORITHM = 'ed25519';
+    const PRIVATE_KEY_FILENAME = 'id_ed25519';
 
     private $api;
     private $config;
@@ -41,16 +42,24 @@
     /**
      * Generates a new certificate.
      *
-     * @param bool $newKeyPair Whether to recreate the SSH key pair.
-     *
      * @return Certificate
      */
-    public function generateCertificate($newKeyPair = false)
+    public function generateCertificate()
     {
         $dir = $this->config->getSessionDir(true) . DIRECTORY_SEPARATOR . 
'ssh';
         $this->fs->mkdir($dir, 0700);
 
-        $sshPair = $this->generateSshKey($dir, $newKeyPair);
+        // Remove the old certificate and key from the SSH agent.
+        if ($this->config->getWithDefault('api.add_to_ssh_agent', false)) {
+            $this->shell->execute(['ssh-add', '-d', $dir . DIRECTORY_SEPARATOR 
. self::PRIVATE_KEY_FILENAME], null, false, !$this->stdErr->isVeryVerbose());
+        }
+
+        // Ensure the user is logged in to the API, so that an auto-login will
+        // not be triggered after we have generated keys (auto-login triggers a
+        // logout, which wipes keys).
+        $apiClient = $this->api->getClient();
+
+        $sshPair = $this->generateSshKey($dir, true);
         $publicContents = file_get_contents($sshPair['public']);
         if (!$publicContents) {
             throw new \RuntimeException('Failed to read public key file: ' . 
$publicContents);
@@ -64,13 +73,21 @@
         }
 
         $this->stdErr->writeln('Requesting certificate from the API', 
OutputInterface::VERBOSITY_VERBOSE);
-        $certificate = 
$this->api->getClient()->getSshCertificate($publicContents);
+        $certificate = $apiClient->getSshCertificate($publicContents);
 
         $this->fs->writeFile($certificateFilename, $certificate);
         $this->chmod($certificateFilename, 0600);
 
         $certificate = new Certificate($certificateFilename, 
$sshPair['private']);
 
+        // Add the key to the SSH agent, if possible, silently.
+        // In verbose mode the full command will be printed, so the user can
+        // re-run it to check error details.
+        if ($this->config->getWithDefault('api.add_to_ssh_agent', false)) {
+            $lifetime = ($certificate->metadata()->getValidBefore() - time()) 
?: 3600;
+            $this->shell->execute(['ssh-add', '-t', $lifetime, 
$sshPair['private']], null, false, !$this->stdErr->isVerbose());
+        }
+
         return $certificate;
     }
 
@@ -116,7 +133,14 @@
             $this->fs->remove($sshInfo);
         }
         // Generate new keys and set permissions.
-        $this->shell->execute(['ssh-keygen', '-t', 'rsa', '-N', '', '-f', 
$sshInfo['private']], null, true);
+        $args = [
+            'ssh-keygen',
+            '-t', self::KEY_ALGORITHM,
+            '-f', $sshInfo['private'],
+            '-N', '', // No passphrase
+            '-C', $this->config->get('application.slug') . '-temporary-cert', 
// Key comment
+        ];
+        $this->shell->execute($args, null, true);
         $this->chmod($sshInfo['private'], 0600);
         $this->chmod($sshInfo['public'], 0600);
 

++++++ 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     2020-07-21 21:13:49.465291866 +0200
+++ new/vendor/autoload.php     2020-08-13 20:32:07.793394032 +0200
@@ -4,4 +4,4 @@
 
 require_once __DIR__ . '/composer/autoload_real.php';
 
-return ComposerAutoloaderInitc6c1301a1038b3fa5191b9699022ee9b::getLoader();
+return ComposerAutoloaderInitf33106493991023d7e9aa3a168d1ea0f::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       2020-07-21 21:13:49.465291866 
+0200
+++ new/vendor/composer/autoload_real.php       2020-08-13 20:32:07.793394032 
+0200
@@ -2,7 +2,7 @@
 
 // autoload_real.php @generated by Composer
 
-class ComposerAutoloaderInitc6c1301a1038b3fa5191b9699022ee9b
+class ComposerAutoloaderInitf33106493991023d7e9aa3a168d1ea0f
 {
     private static $loader;
 
@@ -22,15 +22,15 @@
             return self::$loader;
         }
 
-        
spl_autoload_register(array('ComposerAutoloaderInitc6c1301a1038b3fa5191b9699022ee9b',
 'loadClassLoader'), true, true);
+        
spl_autoload_register(array('ComposerAutoloaderInitf33106493991023d7e9aa3a168d1ea0f',
 'loadClassLoader'), true, true);
         self::$loader = $loader = new \Composer\Autoload\ClassLoader();
-        
spl_autoload_unregister(array('ComposerAutoloaderInitc6c1301a1038b3fa5191b9699022ee9b',
 'loadClassLoader'));
+        
spl_autoload_unregister(array('ComposerAutoloaderInitf33106493991023d7e9aa3a168d1ea0f',
 '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\ComposerStaticInitc6c1301a1038b3fa5191b9699022ee9b::getInitializer($loader));
+            
call_user_func(\Composer\Autoload\ComposerStaticInitf33106493991023d7e9aa3a168d1ea0f::getInitializer($loader));
         } else {
             $map = require __DIR__ . '/autoload_namespaces.php';
             foreach ($map as $namespace => $path) {
@@ -51,19 +51,19 @@
         $loader->register(true);
 
         if ($useStaticLoader) {
-            $includeFiles = 
Composer\Autoload\ComposerStaticInitc6c1301a1038b3fa5191b9699022ee9b::$files;
+            $includeFiles = 
Composer\Autoload\ComposerStaticInitf33106493991023d7e9aa3a168d1ea0f::$files;
         } else {
             $includeFiles = require __DIR__ . '/autoload_files.php';
         }
         foreach ($includeFiles as $fileIdentifier => $file) {
-            composerRequirec6c1301a1038b3fa5191b9699022ee9b($fileIdentifier, 
$file);
+            composerRequiref33106493991023d7e9aa3a168d1ea0f($fileIdentifier, 
$file);
         }
 
         return $loader;
     }
 }
 
-function composerRequirec6c1301a1038b3fa5191b9699022ee9b($fileIdentifier, 
$file)
+function composerRequiref33106493991023d7e9aa3a168d1ea0f($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     2020-07-21 21:13:49.465291866 
+0200
+++ new/vendor/composer/autoload_static.php     2020-08-13 20:32:07.793394032 
+0200
@@ -4,7 +4,7 @@
 
 namespace Composer\Autoload;
 
-class ComposerStaticInitc6c1301a1038b3fa5191b9699022ee9b
+class ComposerStaticInitf33106493991023d7e9aa3a168d1ea0f
 {
     public static $files = array (
         '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . 
'/symfony/polyfill-ctype/bootstrap.php',
@@ -193,9 +193,9 @@
     public static function getInitializer(ClassLoader $loader)
     {
         return \Closure::bind(function () use ($loader) {
-            $loader->prefixLengthsPsr4 = 
ComposerStaticInitc6c1301a1038b3fa5191b9699022ee9b::$prefixLengthsPsr4;
-            $loader->prefixDirsPsr4 = 
ComposerStaticInitc6c1301a1038b3fa5191b9699022ee9b::$prefixDirsPsr4;
-            $loader->classMap = 
ComposerStaticInitc6c1301a1038b3fa5191b9699022ee9b::$classMap;
+            $loader->prefixLengthsPsr4 = 
ComposerStaticInitf33106493991023d7e9aa3a168d1ea0f::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = 
ComposerStaticInitf33106493991023d7e9aa3a168d1ea0f::$prefixDirsPsr4;
+            $loader->classMap = 
ComposerStaticInitf33106493991023d7e9aa3a168d1ea0f::$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  2020-07-21 21:13:49.021289253 +0200
+++ new/vendor/composer/installed.json  2020-08-13 20:32:07.141387931 +0200
@@ -682,17 +682,17 @@
     },
     {
         "name": "platformsh/client",
-        "version": "v0.35.2",
-        "version_normalized": "0.35.2.0",
+        "version": "v0.37.2",
+        "version_normalized": "0.37.2.0",
         "source": {
             "type": "git",
             "url": "https://github.com/platformsh/platformsh-client-php.git";,
-            "reference": "ce7f949dc6788ee90b8172d991845a5a66788531"
+            "reference": "2f63f43d16faa370895f90f1e6febd5519e2664d"
         },
         "dist": {
             "type": "zip",
-            "url": 
"https://api.github.com/repos/platformsh/platformsh-client-php/zipball/ce7f949dc6788ee90b8172d991845a5a66788531";,
-            "reference": "ce7f949dc6788ee90b8172d991845a5a66788531",
+            "url": 
"https://api.github.com/repos/platformsh/platformsh-client-php/zipball/2f63f43d16faa370895f90f1e6febd5519e2664d";,
+            "reference": "2f63f43d16faa370895f90f1e6febd5519e2664d",
             "shasum": ""
         },
         "require": {
@@ -706,7 +706,7 @@
         "require-dev": {
             "phpunit/phpunit": "~4.5"
         },
-        "time": "2020-07-21T07:54:05+00:00",
+        "time": "2020-08-05T09:08:05+00:00",
         "type": "library",
         "installation-source": "dist",
         "autoload": {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/platformsh/client/src/Connection/Connector.php 
new/vendor/platformsh/client/src/Connection/Connector.php
--- old/vendor/platformsh/client/src/Connection/Connector.php   2020-07-21 
09:54:05.000000000 +0200
+++ new/vendor/platformsh/client/src/Connection/Connector.php   2020-08-05 
11:08:05.000000000 +0200
@@ -8,7 +8,6 @@
 use GuzzleHttp\Client;
 use GuzzleHttp\ClientInterface;
 use GuzzleHttp\Collection;
-use GuzzleHttp\Exception\ClientException;
 use GuzzleHttp\Exception\RequestException;
 use GuzzleHttp\Subscriber\Cache\CacheSubscriber;
 use GuzzleHttp\Url;
@@ -36,7 +35,10 @@
     /**
      * @param array            $config
      *     Possible configuration keys are:
-     *     - accounts (string): The endpoint URL for the accounts API.
+     *     - api_url (string): The API base URL.
+     *     - token_url (string): The OAuth 2.0 token URL.
+     *     - revoke_url (string): The OAuth 2.0 revocation URL.
+     *     - certifier_url (string): The SSH certificate issuer URL.
      *     - client_id (string): The OAuth2 client ID for this client.
      *     - debug (bool): Whether or not Guzzle debugging should be enabled
      *       (default: false).
@@ -56,9 +58,13 @@
      */
     public function __construct(array $config = [], SessionInterface $session 
= null)
     {
+        if (isset($config['accounts'])) {
+            \trigger_error('The "accounts" URL option is deprecated. APIs are 
accessed based on the "api_url" and OAuth 2.0 URL options instead.', 
E_USER_DEPRECATED);
+        }
+
         $defaults = [
-          'accounts' => 'https://accounts.platform.sh/api/v1/',
           'api_url' => 'https://api.platform.sh',
+          'accounts' => 'https://api.platform.sh/',
           'client_id' => 'platformsh-client-php',
           'client_secret' => '',
           'debug' => false,
@@ -124,6 +130,10 @@
     /**
      * Get the configured accounts endpoint URL.
      *
+     * @deprecated Use ConnectorInterface::getApiUrl() instead
+     *
+     * @see ConnectorInterface::getApiUrl()
+     *
      * @return string
      */
     public function getAccountsEndpoint()
@@ -132,9 +142,7 @@
     }
 
     /**
-     * Get the configured API gateway URL.
-     *
-     * @return string
+     * {@inheritDoc}
      */
     public function getApiUrl()
     {
@@ -165,6 +173,27 @@
     }
 
     /**
+     * Get a configured OAuth 2.0 URL.
+     *
+     * @param string $key Either 'token_url' or 'revoke_url'
+     *
+     * @return string
+     */
+    private function getOAuthUrl($key)
+    {
+        $url = $this->config[$key];
+
+        // Backwards compatibility.
+        if (strpos($url, '//') === false) {
+            $url = Url::fromString($this->config['accounts'])
+                ->combine($this->config[$key])
+                ->__toString();
+        }
+
+        return $url;
+    }
+
+    /**
      * Revokes the access and refresh tokens saved in the session.
      */
     private function revokeTokens()
@@ -173,9 +202,7 @@
             'refresh_token' => $this->session->get('refreshToken'),
             'access_token' => $this->session->get('accessToken'),
         ]);
-        $url = Url::fromString($this->config['accounts'])
-            ->combine($this->config['revoke_url'])
-            ->__toString();
+        $url = $this->getOAuthUrl('revoke_url');
         foreach ($revocations as $type => $token) {
             $options = [
                 'body' => [
@@ -186,17 +213,7 @@
                 ],
                 'auth' => false,
             ];
-            try {
-                $this->getClient()->post($url, $options);
-            }  catch (ClientException $e) {
-                // Ignore unsupported token type errors.
-                if ($e->getResponse()) {
-                    $data = $e->getResponse()->json();
-                    if ($data['error'] !== 'unsupported_token_type') {
-                        throw $e;
-                    }
-                }
-            }
+            $this->getClient()->post($url, $options);
         }
     }
 
@@ -218,7 +235,6 @@
         }
         $this->logOut();
         $client = $this->getGuzzleClient([
-          'base_url' => $this->config['accounts'],
           'defaults' => [
             'debug' => $this->config['debug'],
             'verify' => $this->config['verify'],
@@ -231,7 +247,7 @@
             'client_secret' => $this->config['client_secret'],
             'username' => $username,
             'password' => $password,
-            'token_url' => $this->config['token_url'],
+            'token_url' => $this->getOAuthUrl('token_url'),
           ]
         );
         if (isset($totp)) {
@@ -314,7 +330,6 @@
             }
 
             $options = [
-              'base_url' => $this->config['accounts'],
               'defaults' => [
                 'headers' => ['User-Agent' => $this->config['user_agent']],
                 'debug' => $this->config['debug'],
@@ -331,7 +346,7 @@
                     'client_secret' => $this->config['client_secret'],
                     'api_token' => $this->config['api_token'],
                     'refresh_token' => $this->session->get('refreshToken'),
-                    'token_url' => $this->config['token_url'],
+                    'token_url' => $this->getOAuthUrl('token_url'),
                   ]
                 );
             }
@@ -341,7 +356,7 @@
                     'client_id' => $this->config['client_id'],
                     'client_secret' => $this->config['client_secret'],
                     'refresh_token' => $this->session->get('refreshToken'),
-                    'token_url' => $this->config['token_url'],
+                    'token_url' => $this->getOAuthUrl('token_url'),
                   ]
                 );
             }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/platformsh/client/src/Connection/ConnectorInterface.php 
new/vendor/platformsh/client/src/Connection/ConnectorInterface.php
--- old/vendor/platformsh/client/src/Connection/ConnectorInterface.php  
2020-07-21 09:54:05.000000000 +0200
+++ new/vendor/platformsh/client/src/Connection/ConnectorInterface.php  
2020-08-05 11:08:05.000000000 +0200
@@ -50,11 +50,11 @@
     public function getClient();
 
     /**
-     * Get the configured accounts endpoint URL.
+     * Get the configured API gateway URL (without trailing slash).
      *
      * @return string
      */
-    public function getAccountsEndpoint();
+    public function getApiUrl();
 
     /**
      * Set the API token to use for Platform.sh requests.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/platformsh/client/src/PlatformClient.php 
new/vendor/platformsh/client/src/PlatformClient.php
--- old/vendor/platformsh/client/src/PlatformClient.php 2020-07-21 
09:54:05.000000000 +0200
+++ new/vendor/platformsh/client/src/PlatformClient.php 2020-08-05 
11:08:05.000000000 +0200
@@ -22,9 +22,6 @@
     /** @var ConnectorInterface */
     protected $connector;
 
-    /** @var string */
-    protected $accountsEndpoint;
-
     /** @var array */
     protected $accountInfo;
 
@@ -34,7 +31,6 @@
     public function __construct(ConnectorInterface $connector = null)
     {
         $this->connector = $connector ?: new Connector();
-        $this->accountsEndpoint = $this->connector->getAccountsEndpoint();
     }
 
     /**
@@ -46,6 +42,14 @@
     }
 
     /**
+     * Returns the base URL of the API, without trailing slash.
+     */
+    private function apiUrl()
+    {
+        return $this->connector->getApiUrl() ?: 
rtrim($this->connector->getAccountsEndpoint(), '/');
+    }
+
+    /**
      * Get a single project by its ID.
      *
      * @param string $id
@@ -116,7 +120,7 @@
     {
         if (!isset($this->accountInfo) || $reset) {
             $client = $this->connector->getClient();
-            $url = $this->accountsEndpoint . 'me';
+            $url = $this->apiUrl() . '/me';
             try {
                 $this->accountInfo = (array) $client->get($url)->json();
             }
@@ -164,7 +168,7 @@
     protected function locateProject($id)
     {
         $client = $this->connector->getClient();
-        $url = $this->accountsEndpoint . 'projects/' . rawurlencode($id);
+        $url = $this->apiUrl() . '/projects/' . rawurlencode($id);
         try {
             $result = (array) $client->get($url)->json();
         }
@@ -178,7 +182,13 @@
             throw ApiResponseException::create($e->getRequest(), 
$e->getResponse(), $e->getPrevious());
         }
 
-        return isset($result['endpoint']) ? $result['endpoint'] : false;
+        if (isset($result['endpoint'])) {
+            return $result['endpoint'];
+        }
+        if (isset($result['_links']['self']['href'])) {
+            return $result['_links']['self']['href'];
+        }
+        return false;
     }
 
     /**
@@ -192,7 +202,7 @@
     {
         $data = $this->getAccountInfo($reset);
 
-        return SshKey::wrapCollection($data['ssh_keys'], 
$this->accountsEndpoint, $this->connector->getClient());
+        return SshKey::wrapCollection($data['ssh_keys'], $this->apiUrl() . 
'/ssh_keys', $this->connector->getClient());
     }
 
     /**
@@ -204,7 +214,7 @@
      */
     public function getSshKey($id)
     {
-        $url = $this->accountsEndpoint . 'ssh_keys';
+        $url = $this->apiUrl() . '/ssh_keys';
 
         return SshKey::get($id, $url, $this->connector->getClient());
     }
@@ -220,7 +230,7 @@
     public function addSshKey($value, $title = null)
     {
         $values = $this->cleanRequest(['value' => $value, 'title' => $title]);
-        $url = $this->accountsEndpoint . 'ssh_keys';
+        $url = $this->apiUrl() . '/ssh_keys';
 
         return SshKey::create($values, $url, $this->connector->getClient());
     }
@@ -262,7 +272,7 @@
     public function createSubscription($region, $plan = 'development', $title 
= null, $storage = null, $environments = null, array $activationCallback = 
null, $catalog = null)
     {
 
-        $url = $this->accountsEndpoint . 'subscriptions';
+        $url = $this->apiUrl() . '/subscriptions';
         $values = $this->cleanRequest([
           'project_region' => $region,
           'plan' => $plan,
@@ -283,7 +293,7 @@
      */
     public function getSubscriptions()
     {
-        $url = $this->accountsEndpoint . 'subscriptions';
+        $url = $this->apiUrl() . '/subscriptions';
         return Subscription::getCollection($url, 0, [], 
$this->connector->getClient());
     }
 
@@ -296,7 +306,7 @@
      */
     public function getSubscription($id)
     {
-        $url = $this->accountsEndpoint . 'subscriptions';
+        $url = $this->apiUrl() . '/subscriptions';
         return Subscription::get($id, $url, $this->connector->getClient());
     }
 
@@ -326,7 +336,7 @@
         try {
             $response = $this->connector
                 ->getClient()
-                ->get($this->accountsEndpoint . 'subscriptions/estimate', 
$options);
+                ->get($this->apiUrl() . '/subscriptions/estimate', $options);
         } catch (BadResponseException $e) {
             throw ApiResponseException::create($e->getRequest(), 
$e->getResponse(), $e->getPrevious());
         }
@@ -341,7 +351,7 @@
      */
     public function getPlans()
     {
-        return Plan::getCollection($this->accountsEndpoint . 'plans', 0, [], 
$this->getConnector()->getClient());
+        return Plan::getCollection($this->apiUrl() . '/plans', 0, [], 
$this->getConnector()->getClient());
     }
 
     /**
@@ -351,7 +361,7 @@
      */
     public function getRegions()
     {
-        return Region::getCollection($this->accountsEndpoint . 'regions', 0, 
[], $this->getConnector()->getClient());
+        return Region::getCollection($this->apiUrl() . '/regions', 0, [], 
$this->getConnector()->getClient());
     }
 
     /**
@@ -385,7 +395,7 @@
      */
     public function getCatalog()
     {
-        return Catalog::create([], $this->accountsEndpoint . 'setup/catalog', 
$this->getConnector()->getClient());
+        return Catalog::create([], $this->apiUrl() . '/setup/catalog', 
$this->getConnector()->getClient());
     }
 
     /**
@@ -401,7 +411,7 @@
      */
     public function getSetupOptions($vendor = NULL, $plan = NULL, $options_url 
= NULL, $username = NULL, $organization = NULL)
     {
-        $url = $this->accountsEndpoint . 'setup/options';
+        $url = $this->apiUrl() . '/setup/options';
         $options = $this->cleanRequest([
           'vendor' => $vendor,
           'plan' => $plan,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/platformsh/client/src/SshCert/Metadata.php 
new/vendor/platformsh/client/src/SshCert/Metadata.php
--- old/vendor/platformsh/client/src/SshCert/Metadata.php       2020-07-21 
09:54:05.000000000 +0200
+++ new/vendor/platformsh/client/src/SshCert/Metadata.php       2020-08-05 
11:08:05.000000000 +0200
@@ -3,27 +3,17 @@
 namespace Platformsh\Client\SshCert;
 
 /**
- * Parses an OpenSSH RSA certificate.
+ * Parses an OpenSSH certificate (RSA or ED25519).
  *
  * @see 
https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD
  */
 class Metadata {
 
-    private $keyType;
-    private $nonce;
-    private $rsaExponent;
-    private $publicModulus;
-    private $serial;
-    private $type;
     private $keyId;
-    private $validPrincipals;
+    private $keyType;
     private $validAfter;
     private $validBefore;
-    private $criticalOptions;
     private $extensions;
-    private $reserved;
-    private $signatureKey;
-    private $signature;
 
     /**
      * Constructor
@@ -33,7 +23,7 @@
     public function __construct($string)
     {
         list($type, $cert) = \explode(' ', $string);
-        if ($type !== '[email protected]') {
+        if (!\in_array($type, ['[email protected]', 
'[email protected]'], true)) {
             throw new \InvalidArgumentException('Unsupported key type: ' . 
$type);
         }
         $bytes = \base64_decode($cert, true);
@@ -41,20 +31,23 @@
             throw new \InvalidArgumentException('Unable to decode SSH 
certificate');
         }
         $this->keyType = $this->readString($bytes);
-        $this->nonce = $this->readString($bytes);
-        $this->rsaExponent = $this->readString($bytes);
-        $this->publicModulus = $this->readString($bytes);
-        $this->serial = $this->readUint64($bytes);
-        $this->type = $this->readUint32($bytes);
+        $this->readString($bytes); // ignore nonce
+        // @todo refactor this?
+        if ($type === '[email protected]') {
+            $this->readString($bytes); // ignore ED25519 public key
+        } else {
+            $this->readString($bytes); // ignore RSA exponent
+            $this->readString($bytes); // ignore RSA modulus
+        }
+        $this->readUint64($bytes); // ignore serial number
+        $this->readUint32($bytes); // ignore certificate type (1 for user, 2 
for host)
         $this->keyId = $this->readString($bytes);
-        $this->validPrincipals = $this->readArray($bytes);
+        $this->readArray($bytes); // ignore valid principals
         $this->validAfter = $this->readUint64($bytes);
         $this->validBefore = $this->readUint64($bytes);
-        $this->criticalOptions = $this->readTuples($bytes);
+        $this->readTuples($bytes); // ignore critical options
         $this->extensions = $this->readTuples($bytes);
-        $this->reserved = $this->readString($bytes);
-        $this->signatureKey = $this->readString($bytes);
-        $this->signature = $this->readString($bytes);
+        // ignore the reserved, signature and signature key fields
     }
 
     /**
@@ -179,4 +172,15 @@
     public function getKeyId() {
         return $this->keyId;
     }
+
+    /**
+     * Return's the certificate key type.
+     *
+     * This will be an identifier such as [email protected] or 
[email protected].
+     *
+     * @return string
+     */
+    public function getKeyType() {
+        return $this->keyType;
+    }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/vendor/platformsh/client/tests/PlatformClientTest.php 
new/vendor/platformsh/client/tests/PlatformClientTest.php
--- old/vendor/platformsh/client/tests/PlatformClientTest.php   2020-07-21 
09:54:05.000000000 +0200
+++ new/vendor/platformsh/client/tests/PlatformClientTest.php   2020-08-05 
11:08:05.000000000 +0200
@@ -44,18 +44,13 @@
         $this->connector->setMockResult(['projects' => [$testProject]]);
         $projects = $this->client->getProjects();
         $this->assertEquals($testProject['name'], $projects[0]['name']);
-        $this->assertEquals($this->apiUrl . '/projects/test', 
$projects[0]['endpoint']);
+        $this->assertEquals($testProject['endpoint'], 
$projects[0]['endpoint']);
+        $this->assertEquals($this->apiUrl . '/projects/test/invitations', 
$projects[0]->getLink('invitations'));
 
         $project = $this->client->getProject('test');
         $this->assertEquals($testProject['name'], $project['name']);
-        $this->assertEquals($this->apiUrl . '/projects/test', 
$project['endpoint']);
-
-        // Test endpoint without an API gateway URL configured.
-        $connector = new MockConnector(['api_url' => '']);
-        $connector->setMockResult(['projects' => [$testProject]]);
-        $project = (new PlatformClient($connector))->getProject('test');
-        $this->assertEquals($testProject['name'], $project['name']);
         $this->assertEquals($testProject['endpoint'], $project['endpoint']);
+        $this->assertEquals($this->apiUrl . '/projects/test/invitations', 
$project->getLink('invitations'));
     }
 
     public function testGetProjectDirect()


Reply via email to