Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
66 changes: 66 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Bug report
description: Report a problem with Acquia CLI.
labels: [bug]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to report a bug! Please fill out the fields below to help us reproduce and fix the issue.

If you have a question or need help from Acquia, visit the [Acquia Support Portal](https://acquia.my.site.com/s/) or the [discussions section](https://github.com/acquia/cli/discussions) instead.
- type: input
id: acli-version
attributes:
label: Acquia CLI version
description: Output of `acli --version`.
placeholder: Acquia CLI 2.x.x
validations:
required: true
- type: input
id: php-version
attributes:
label: PHP version
description: Output of `php --version`.
placeholder: PHP 8.3.x
validations:
required: true
- type: input
id: os
attributes:
label: Operating system
description: Your OS and version, and whether you're running in a special environment (Acquia Cloud IDE, WSL, Docker, etc.).
placeholder: macOS 15.5, Ubuntu 24.04, Windows 11 (WSL2), Acquia Cloud IDE
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce
description: The exact commands you ran and any relevant configuration.
placeholder: |
1. Run `acli ...`
2. ...
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
description: What you expected to happen.
validations:
required: true
- type: textarea
id: actual
attributes:
label: Actual behavior
description: What actually happened, including any error messages.
validations:
required: true
- type: textarea
id: verbose-output
attributes:
label: Verbose output
description: Re-run the failing command with maximum verbosity (`acli <command> -vvv`) and paste the output. Remove any sensitive information such as API keys or tokens.
render: shell
validations:
required: false
11 changes: 11 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
blank_issues_enabled: true
contact_links:
- name: Security vulnerability report
url: https://www.acquia.com/why-acquia/acquia-security
about: Please do not report security vulnerabilities in public issues. Report them privately via Acquia's security program.
- name: Acquia Support Portal
url: https://acquia.my.site.com/s/
about: Get support from Acquia for your subscription and products.
- name: GitHub Discussions
url: https://github.com/acquia/cli/discussions
about: Ask questions and discuss ideas with other Acquia CLI users.
29 changes: 29 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Feature request
description: Suggest a new feature or improvement for Acquia CLI.
labels: [enhancement]
body:
- type: markdown
attributes:
value: |
Thanks for suggesting an improvement! If you'd like to discuss an idea with other Acquia CLI users first, consider the [discussions section](https://github.com/acquia/cli/discussions).
- type: textarea
id: problem
attributes:
label: Problem statement
description: What problem are you trying to solve? What is currently difficult or impossible to do?
validations:
required: true
- type: textarea
id: proposed-solution
attributes:
label: Proposed solution
description: How would you like Acquia CLI to solve this? Include example commands or output if helpful.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: Any workarounds or alternative solutions you've considered.
validations:
required: false
71 changes: 71 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Acquia CLI

PHP 8.2+ Symfony Console application distributed as a PHAR (built with Box).
Commands talk to the Acquia Cloud Platform API and Acquia Cloud Site Factory
(ACSF) API.

## Commands

```shell
composer install # install dependencies
composer test # lint + cs + stan + unit
composer unit # full PHPUnit suite (serial group, then paratest)
composer cs # phpcs (acquia/coding-standards)
composer cbf # phpcbf autofix
composer stan # PHPStan (pass --memory-limit=2G if it OOMs)
composer mutation # Infection mutation testing
composer box-install && composer box-compile # build var/acli.phar
```

Run a single test class:

```shell
vendor/bin/phpunit --filter SomeCommandTest tests/phpunit/src/Commands/...
```

GrumPHP runs phpcs as a pre-commit hook; commits fail on style violations.

## Architecture

- `bin/acli` boots `src/Kernel.php` (Symfony DI container, services wired in
`config/`). Commands are services in `src/Command/`.
- `src/Command/CommandBase.php` is the base for all commands: telemetry,
authentication, alias→UUID conversion, update checks.
- `api:*` and `acsf:*` commands are NOT hand-written: they are generated at
runtime by `src/Command/Api/ApiCommandHelper.php` from the OpenAPI specs in
`assets/acquia-spec.json` and `assets/acsf-spec.yaml`. To change an API
command, change the helper or regenerate the spec (`composer
update-cloud-api-spec`), not individual command classes.
- `src/DataStore/` persists config: `CloudDataStore` (`~/.acquia/cloud_api.conf`,
contains API credentials — must stay chmod 0600) and `AcquiaCliDatastore`.
- Telemetry goes to Amplitude and errors to Bugsnag via
`src/Helpers/TelemetryHelper.php`. Anything derived from command input must
pass through `TelemetryHelper::redactSensitiveData()` before being sent.

## Conventions

- TDD: write or update the failing PHPUnit test before changing `src/`.
- Tests use prophecy mocks; base classes `tests/phpunit/src/TestBase.php` and
`CommandTestBase.php` provide mock helpers for the Cloud API client
(`mockRequest()` reads from the OpenAPI spec fixtures).
- PHPUnit metadata uses PHP attributes (`#[Group]`, `#[DataProvider]`), not
doc-comment annotations.
- Tests that mutate global state belong in the `serial` group
(`#[Group('serial')]`); everything else runs under paratest.
- `declare(strict_types=1);` in every file; strict comparisons; phpcs enforces
ordered use-statement imports.
- Process execution: always pass argument arrays to
`LocalMachineHelper::execute()`; never interpolate user input into
`executeFromCmd()` shell strings.
- SSH/rsync/git invocations use `-o StrictHostKeyChecking=accept-new`; do not
weaken to `=no`.

## Gotchas

- Symfony is pinned to 6.4: `typhonius/acquia-logstream` blocks Symfony 7.
- PHP_CodeSniffer is pinned to 3.x: `acquia/coding-standards` and
`drupal/coder` block phpcs 4.
- `minimum-stability: dev` is required by the `consolidation/self-update`
fork pin; `prefer-stable: true` keeps everything else on stable releases.
- The update check in `CommandBase::checkForNewVersion()` is cached for 24h
(`self:clear-caches` clears it).
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ Acquia CLI is not a local development environment. If you are looking for an int

Install instructions and official documentation are available at https://docs.acquia.com/acquia-cli/install/

### Shell completion

Acquia CLI supports tab completion for bash, zsh, and fish. To enable it, run
`acli completion --help` and follow the installation instructions for your
shell. For example, for bash:

```shell
acli completion bash > /etc/bash_completion.d/acli
```

## Contribution

See [CONTRIBUTING.md](CONTRIBUTING.md) for instructions on building, testing, and contributing to Acquia CLI.
Expand Down
9 changes: 7 additions & 2 deletions bin/acli
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use Acquia\Cli\Helpers\LocalMachineHelper;
use Dotenv\Dotenv;
use SelfUpdate\SelfUpdateCommand;
use SelfUpdate\SelfUpdateManager;
use Symfony\Component\Console\CommandLoader\FactoryCommandLoader;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
Expand Down Expand Up @@ -97,8 +98,12 @@ $application = $container->get(Application::class);
$output = $container->get(OutputInterface::class);
/** @var ApiCommandHelper $helper */
$helper = $container->get(ApiCommandHelper::class);
$application->addCommands($helper->getApiCommands( __DIR__ . '/../assets/acquia-spec.json', 'api', $container->get(ApiCommandFactory::class)));
$application->addCommands($helper->getApiCommands( __DIR__ . '/../assets/acsf-spec.json', 'acsf', $container->get(AcsfCommandFactory::class)));
// Register the spec-derived commands lazily so a normal invocation only builds
// the single command being run, rather than all ~500 API/ACSF commands.
$application->setCommandLoader(new FactoryCommandLoader(array_merge(
$helper->getApiCommandFactories(__DIR__ . '/../assets/acquia-spec.json', 'api', $container->get(ApiCommandFactory::class)),
$helper->getApiCommandFactories(__DIR__ . '/../assets/acsf-spec.json', 'acsf', $container->get(AcsfCommandFactory::class))
)));
try {
/** @var SelfUpdateManager $selfUpdateManager*/
$selfUpdateManager = $container->get(SelfUpdateManager::class);
Expand Down
13 changes: 6 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
"grasmash/expander": "^3.0.1",
"guzzlehttp/guzzle": "^7.4",
"http-interop/http-factory-guzzle": "^1.0",
"laminas/laminas-validator": "^2.20.0",
"league/csv": "^9.8",
"loophp/phposinfo": "^1.7.2",
"ltd-beget/dns-zone-configurator": "^1.4.0",
Expand All @@ -51,7 +50,7 @@
"symfony/process": "^6.4",
"symfony/validator": "^6.4",
"symfony/yaml": "^6.4",
"thecodingmachine/safe": "3.0.2",
"thecodingmachine/safe": "^3.4",
"typhonius/acquia-logstream": "^0.0.15",
"typhonius/acquia-php-sdk-v2": "^3.8.0",
"vlucas/phpdotenv": "^5.5",
Expand All @@ -61,17 +60,17 @@
"acquia/coding-standards": "^3.0.2",
"brianium/paratest": "^7",
"dealerdirect/phpcodesniffer-composer-installer": "^1.0.0",
"dominikb/composer-license-checker": "^2.4",
"infection/infection": "^0.31.2",
"jangregor/phpstan-prophecy": "^1.0",
"dominikb/composer-license-checker": "^3.0",
"infection/infection": "^0.32",
"jangregor/phpstan-prophecy": "^2.0",
"mikey179/vfsstream": "^1.6",
"overtrue/phplint": "^9.6.2",
"phpro/grumphp": "^2.9.0",
"phpspec/prophecy": "^1.17",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.0",
"phpstan/phpstan-deprecation-rules": "^1.0",
"phpstan/phpstan": "^2.0",
"phpstan/phpstan-deprecation-rules": "^2.0",
"phpunit/phpunit": "^11",
"slevomat/coding-standard": "^8.10",
"squizlabs/php_codesniffer": "^3.5",
Expand Down
Loading
Loading