Skip to content
Closed
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
08f5c6f
CLI-1711: [NCI] Database and File commands support
jigar-shah-acquia Feb 3, 2026
fbc4ac8
CLI-1711: [NCI] Database and File commands support
jigar-shah-acquia Feb 4, 2026
a71835a
CLI-1711: Changes for codecoverage
jigar-shah-acquia Feb 4, 2026
def8283
CLI-1711: Added testcase for mutation
jigar-shah-acquia Feb 5, 2026
c3c15fc
CLI-1711: Added testcase for mutation
jigar-shah-acquia Feb 5, 2026
0959898
CLI-1711: Added testcase for mutation
jigar-shah-acquia Feb 5, 2026
4d4c528
CLI-1711: Added testcase for mutation
jigar-shah-acquia Feb 5, 2026
5de16b1
CLI-1711: Added testcase for mutation
jigar-shah-acquia Feb 5, 2026
6a13b0c
CLI-1711: Added testcase for mutation
jigar-shah-acquia Feb 5, 2026
dc09340
Merge branch 'main' into CLI-1711
jigar-shah-acquia Feb 5, 2026
81ae54c
CLI-1711: Added testcase for mutation
jigar-shah-acquia Feb 6, 2026
c5ac8a1
CLI-1711: Removed 8 functions from acquia.json
jigar-shah-acquia Feb 6, 2026
3f8b93b
CLI-1711: Removed 8 functions from acquia.json
jigar-shah-acquia Feb 6, 2026
39b9e49
CLI-1711: Removed 8 functions from acquia.json
jigar-shah-acquia Feb 9, 2026
8646956
CLI-1711: Changes as per Danes feedback
jigar-shah-acquia Feb 10, 2026
b97317d
CLI-1711: Reverted code as not require these changes
jigar-shah-acquia Feb 10, 2026
62b6839
CLI-1711: Reverted code as not require these changes
jigar-shah-acquia Feb 10, 2026
7521e34
CLI-1711: Reverted code as not require these changes
jigar-shah-acquia Feb 10, 2026
1e2ad8c
CLI-1711: Solved mutation errors.
jigar-shah-acquia Feb 10, 2026
3c0776d
CLI-1711: Solved mutation errors.
jigar-shah-acquia Feb 10, 2026
d391324
CLI-1711: Solved mutation errors.
jigar-shah-acquia Feb 10, 2026
67966cd
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
3df6a0e
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
3e41648
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
37fb0bf
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
dd31223
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
08c2079
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
092f3f5
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
e15eec9
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
ba1d84b
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
d51cd7a
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
d83b5b9
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
7d2e2bb
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
36a271b
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
be9fbc6
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
4611dbe
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
f158208
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
97db815
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
15019a5
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
027049a
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
de19db5
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
60f9b0f
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
3a739cf
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
d8cb766
CLI-1711: ci.yml changes
jigar-shah-acquia Feb 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ jobs:
echo AMPLITUDE_KEY=${{ secrets.AMPLITUDE_KEY }} >> .env
echo ACLI_VERSION=${{ steps.acli-version.outputs.ACLI_VERSION }} >> .env
echo ACLI_BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") >> .env
- name: 'Extract additional API specs from secrets'
run: |
# Extract additional acquia-spec if available
if [ -n "${{ secrets.ACQUIA_SPEC_ADDITIONAL }}" ]; then
echo "${{ secrets.ACQUIA_SPEC_ADDITIONAL }}" > assets/acquia-spec-additional.json
echo "ACLI_ADDITIONAL_SPEC_FILE_ACQUIA_SPEC=$(pwd)/assets/acquia-spec-additional.json" >> $GITHUB_ENV
fi
- name: Build
run: |
composer install --no-dev --optimize-autoloader
Expand Down
1,612 changes: 198 additions & 1,414 deletions assets/acquia-spec.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/acquia-spec.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
c1a6c6733a1ec7a8d3344cc2a2390f694ab73335
4d2927c3f9fa1ec1770717ba363d164d9cac5e94
40 changes: 30 additions & 10 deletions src/Command/Api/ApiBaseCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,28 +130,48 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$exitCode = 1;
}

if (substr($this->path, 0, 12) === '/translation') {
$this->mungeResponse($response);
// Extract notification UUID before removing _links if task-wait is enabled.
$notificationUuid = null;
if (!$exitCode && $this->getParamFromInput($input, 'task-wait')) {
$notificationUuid = CommandBase::getNotificationUuidFromResponse($response);
}

if ($exitCode || !$this->getParamFromInput($input, 'task-wait')) {
// Remove _links from response for all commands.
$this->mungeResponse($response);

if ($exitCode || !$notificationUuid) {
$contents = json_encode($response, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT);
$this->output->writeln($contents);
return $exitCode;
}
$notificationUuid = CommandBase::getNotificationUuidFromResponse($response);
$success = $this->waitForNotificationToComplete($this->cloudApiClientService->getClient(), $notificationUuid, "Waiting for task $notificationUuid to complete");
return $success ? Command::SUCCESS : Command::FAILURE;
}

private function mungeResponse(mixed $response): void
private function mungeResponse(mixed &$response): void
{
if (is_object($response) && property_exists($response, '_links')) {
unset($response->_links);
if (!is_object($response) && !is_array($response)) {
throw new \InvalidArgumentException('mungeResponse() expects an object or array, got ' . gettype($response));
}
foreach ($response as $value) {
if (property_exists($value, '_links')) {
unset($value->_links);

if (is_object($response)) {
if (property_exists($response, '_links')) {
unset($response->_links);
}
// Recursively remove _links from nested objects.
foreach ($response as $key => $value) {
if (is_object($value) || is_array($value)) {
$this->mungeResponse($value);
}
}
} elseif (is_array($response)) {
if (array_key_exists('_links', $response)) {
unset($response['_links']);
}
foreach ($response as $key => $value) {
if (is_object($value) || is_array($value)) {
$this->mungeResponse($response[$key]);
}
}
}
}
Expand Down
137 changes: 120 additions & 17 deletions src/Command/Api/ApiCommandHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -326,29 +326,132 @@ private function getCloudApiSpec(string $specFilePath): array

// When running the phar, the original file may not exist. In that case, always use the cache.
if (!file_exists($specFilePath) && $cacheItemSpec->isHit()) {
return $cacheItemSpec->get();
$spec = $cacheItemSpec->get();
} else {
// Otherwise, only use cache when it is valid.
$checksum = md5_file($specFilePath);
if (
$this->useCloudApiSpecCache()
&& $this->isApiSpecChecksumCacheValid($cacheItemChecksum, $checksum) && $cacheItemSpec->isHit()
) {
$spec = $cacheItemSpec->get();
} else {
// Parse file. This can take a long while!
$this->logger->debug("Rebuilding caches...");
$spec = json_decode(file_get_contents($specFilePath), true);

$cache->warmUp([
$cacheKey => $spec,
$cacheKey . '.checksum' => $checksum,
]);
}
}

// Otherwise, only use cache when it is valid.
$checksum = md5_file($specFilePath);
// @infection-ignore-all
if (
$this->useCloudApiSpecCache()
&& $this->isApiSpecChecksumCacheValid($cacheItemChecksum, $checksum) && $cacheItemSpec->isHit()
) {
return $cacheItemSpec->get();
// Merge additional specs from environment variables or files.
$spec = $this->mergeAdditionalSpecs($spec, $specFilePath);

return $spec;
}

/**
* Merge additional API specs from environment variables or files.
*
* Additional specs can be provided via:
* - Environment variable ACLI_ADDITIONAL_SPEC_FILE_<SPECNAME> pointing to a JSON file
* - Environment variable ACLI_ADDITIONAL_SPEC_JSON_<SPECNAME> containing JSON directly
*
* @param array<mixed> $baseSpec The base API spec
* @param string $baseSpecFilePath Path to the base spec file (used for determining env var name)
* @return array<mixed> The merged spec
*/
private function mergeAdditionalSpecs(array $baseSpec, string $baseSpecFilePath): array
{
$specName = basename($baseSpecFilePath, '.json');
// Normalize spec name: acquia-spec -> ACQUIA_SPEC, acsf-spec -> ACSF_SPEC.
$specNameNormalized = strtoupper(str_replace('-', '_', $specName));
$envVarFile = 'ACLI_ADDITIONAL_SPEC_FILE_' . $specNameNormalized;
$envVarJson = 'ACLI_ADDITIONAL_SPEC_JSON_' . $specNameNormalized;

$additionalSpec = null;

// Try to load from file path in environment variable.
$envFileValue = getenv($envVarFile);
if ($envFileValue && file_exists($envFileValue)) {
$this->logger->debug("Loading additional spec from file: $envFileValue");
$additionalSpecContent = file_get_contents($envFileValue);
$additionalSpec = json_decode($additionalSpecContent, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$this->logger->warning("Failed to parse additional spec from file: " . json_last_error_msg());
$additionalSpec = null;
}
}

// Parse file. This can take a long while!
$this->logger->debug("Rebuilding caches...");
$spec = json_decode(file_get_contents($specFilePath), true);
// Try to load from JSON in environment variable.
if (!$additionalSpec) {
$envJsonValue = getenv($envVarJson);
if ($envJsonValue) {
$this->logger->debug("Loading additional spec from environment variable: $envVarJson");
$additionalSpec = json_decode($envJsonValue, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$this->logger->warning("Failed to parse additional spec from env var: " . json_last_error_msg());
$additionalSpec = null;
}
}
}

$cache->warmUp([
$cacheKey => $spec,
$cacheKey . '.checksum' => $checksum,
]);
if (!is_array($additionalSpec) || $additionalSpec === []) {
return $baseSpec;
}
$keyCount = count($additionalSpec);
if ($keyCount === 0) {
throw new \LogicException('Unexpected empty additionalSpec');
}

return $spec;
// Merge paths from additional spec into base spec.
if (isset($additionalSpec['paths']) && is_array($additionalSpec['paths'])) {
if (!isset($baseSpec['paths'])) {
$baseSpec['paths'] = [];
}
foreach ($additionalSpec['paths'] as $path => $endpoint) {
if (!is_array($endpoint)) {
continue;
}
// Mark all commands from additional specs as deprecated.
foreach ($endpoint as $method => $schema) {
if (is_array($schema)) {
$schema['deprecated'] = true;
$endpoint[$method] = $schema;
}
}
// If path already exists, merge methods; otherwise add new path.
if (isset($baseSpec['paths'][$path])) {
$baseSpec['paths'][$path] = array_merge($baseSpec['paths'][$path], $endpoint);
} else {
$baseSpec['paths'][$path] = $endpoint;
}
}
$this->logger->debug("Merged " . count($additionalSpec['paths']) . " additional paths into spec (marked as deprecated)");
}

// Merge components if present.
if (isset($additionalSpec['components']) && is_array($additionalSpec['components'])) {
if (!isset($baseSpec['components'])) {
$baseSpec['components'] = [];
}
foreach ($additionalSpec['components'] as $componentType => $components) {
if (!isset($baseSpec['components'][$componentType])) {
$baseSpec['components'][$componentType] = [];
}
if (is_array($components)) {
$baseSpec['components'][$componentType] = array_merge(
$baseSpec['components'][$componentType],
$components
);
}
}
}

return $baseSpec;
}

/**
Expand Down
3 changes: 0 additions & 3 deletions src/EventListener/ExceptionListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,6 @@ public function onConsoleError(ConsoleErrorEvent $event): void
}

if ($error instanceof ApiErrorException) {
if (($command = $event->getCommand()) && $error->getResponseBody()->error === 'not_found' && $command->getName() === 'api:environments:log-download') {
$this->helpMessages[] = "You must create logs (api:environments:log-create) prior to downloading them";
}
switch ($errorMessage) {
case "There are no available Cloud IDEs for this application.\n":
$this->helpMessages[] = "Delete an existing IDE via <bg=$this->messagesBgColor;fg=$this->messagesFgColor;options=bold>acli ide:delete</> or contact your Account Manager or Acquia Sales to purchase additional IDEs.";
Expand Down
Loading