From 001f0455251529949cebbe99c07b547be38ce3aa Mon Sep 17 00:00:00 2001 From: Patrick Dawkins Date: Wed, 27 May 2026 00:23:05 +0100 Subject: [PATCH] fix(env:deploy:type): update settings via direct PATCH The command set the deployment type with $settings->update(), which runs the client's "edit" operation. That operation requires an "#edit" HAL link on the /settings resource, but the API only returns a "self" link, so the command failed with "Operation not available: edit". This was introduced when the command switched from $environment->setManualDeployments() to $settings->update(). Replace the update() call with a direct PATCH request to the settings endpoint. The mock API's /settings response previously included a fabricated "#edit" link, hiding the regression. Remove it so the mock matches the real API, and the integration test now exercises the failing path. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Environment/EnvironmentDeployTypeCommand.php | 15 +++++++++++++-- pkg/mockapi/environments.go | 9 +++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/legacy/src/Command/Environment/EnvironmentDeployTypeCommand.php b/legacy/src/Command/Environment/EnvironmentDeployTypeCommand.php index 74d487a53..e7a7a06d7 100644 --- a/legacy/src/Command/Environment/EnvironmentDeployTypeCommand.php +++ b/legacy/src/Command/Environment/EnvironmentDeployTypeCommand.php @@ -2,13 +2,17 @@ namespace Platformsh\Cli\Command\Environment; +use GuzzleHttp\Utils; use Platformsh\Cli\Selector\SelectorConfig; use Symfony\Component\Console\Attribute\AsCommand; use Platformsh\Cli\Service\ActivityMonitor; +use Platformsh\Cli\Service\Api; use Platformsh\Cli\Service\QuestionHelper; use Platformsh\Cli\Selector\Selector; use Platformsh\Cli\Command\CommandBase; use Platformsh\Client\Model\Activity; +use Platformsh\Client\Model\Result; +use Platformsh\Client\Model\Settings; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -18,7 +22,7 @@ #[AsCommand(name: 'environment:deploy:type', description: 'Show or set the environment deployment type')] class EnvironmentDeployTypeCommand extends CommandBase { - public function __construct(private readonly ActivityMonitor $activityMonitor, private readonly QuestionHelper $questionHelper, private readonly Selector $selector) + public function __construct(private readonly ActivityMonitor $activityMonitor, private readonly Api $api, private readonly QuestionHelper $questionHelper, private readonly Selector $selector) { parent::__construct(); } @@ -89,7 +93,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int } } - $result = $settings->update(['enable_manual_deployments' => $newType === 'manual']); + // Update the setting with a direct PATCH request. The settings API + // resource does not advertise an "edit" operation link, so the client's + // update() method (which relies on that operation) cannot be used. + $httpClient = $this->api->getHttpClient(); + $response = $httpClient->request('PATCH', $settings->getUri(), [ + 'json' => ['enable_manual_deployments' => $newType === 'manual'], + ]); + $result = new Result((array) Utils::jsonDecode((string) $response->getBody(), true), $settings->getUri(), $httpClient, Settings::class); if ($result->getActivities() && $this->activityMonitor->shouldWait($input)) { $success = $this->activityMonitor->waitMultiple($result->getActivities(), $selection->getProject()); diff --git a/pkg/mockapi/environments.go b/pkg/mockapi/environments.go index 067c82acf..f5d064d04 100644 --- a/pkg/mockapi/environments.go +++ b/pkg/mockapi/environments.go @@ -73,9 +73,11 @@ func (h *Handler) handleGetEnvironmentSettings(w http.ResponseWriter, req *http. if env.settings != nil { settings = env.settings } + // The real API only exposes a "self" link on the settings resource; it does + // not advertise an "#edit" operation, so the client cannot run an "edit" + // operation against it. settings["_links"] = MakeHALLinks( - "self=/projects/"+env.Project+"/environments/"+env.ID+"/settings", - "#edit=/projects/"+env.Project+"/environments/"+env.ID+"/settings", + "self=/projects/" + env.Project + "/environments/" + env.ID + "/settings", ) _ = json.NewEncoder(w).Encode(settings) @@ -101,8 +103,7 @@ func (h *Handler) handleSetEnvironmentSettings(w http.ResponseWriter, req *http. env.settings[k] = v } settings["_links"] = MakeHALLinks( - "self=/projects/"+env.Project+"/environments/"+env.ID+"/settings", - "#edit=/projects/"+env.Project+"/environments/"+env.ID+"/settings", + "self=/projects/" + env.Project + "/environments/" + env.ID + "/settings", ) h.environments[env.ID] = env