├── .editorconfig
├── .env.example
├── .envrc
├── .github
├── PULL_REQUEST_TEMPLATE.md
├── dependabot.yml
├── release-drafter.yml
└── workflows
│ ├── autolabel.yml
│ ├── automerge.yml
│ ├── ccb-ticket.twig
│ ├── ci.yml
│ ├── create-ccb-ticket.sh
│ ├── integration-tests.yml
│ ├── jira.yml
│ ├── mutation.yml
│ ├── publish-release.yml
│ ├── release-drafter.yml
│ ├── release.yml
│ └── upload-s3.yml
├── .gitignore
├── .phplint.yml
├── .phpstorm.meta.php
├── CONTRIBUTING.md
├── LICENSE.txt
├── README.md
├── assets
├── acquia-spec.json
└── acsf-spec.json
├── bin
├── acli
└── acli.bat
├── box.json
├── box.json.md
├── codecov.yml
├── composer.json
├── composer.lock
├── config
├── dev
│ └── services.yml
├── from_d7_config.json
├── from_d7_recommendations.json
└── prod
│ └── services.yml
├── docker
├── Dockerfile
└── docker-entrypoint.sh
├── grumphp.yml
├── infection.json5
├── phpcs.xml.dist
├── phpstan.neon.dist
├── phpunit.xml.dist
├── src
├── AcsfApi
│ ├── AcsfClient.php
│ ├── AcsfClientService.php
│ ├── AcsfConnector.php
│ ├── AcsfConnectorFactory.php
│ └── AcsfCredentials.php
├── ApiCredentialsInterface.php
├── Application.php
├── Attribute
│ ├── RequireAuth.php
│ ├── RequireLocalDb.php
│ └── RequireRemoteDb.php
├── CloudApi
│ ├── AccessTokenConnector.php
│ ├── ClientService.php
│ ├── CloudCredentials.php
│ └── ConnectorFactory.php
├── Command
│ ├── Acsf
│ │ ├── AcsfCommandFactory.php
│ │ ├── AcsfListCommand.php
│ │ └── AcsfListCommandBase.php
│ ├── Api
│ │ ├── ApiBaseCommand.php
│ │ ├── ApiCommandFactory.php
│ │ ├── ApiCommandHelper.php
│ │ ├── ApiListCommand.php
│ │ └── ApiListCommandBase.php
│ ├── App
│ │ ├── AppOpenCommand.php
│ │ ├── AppVcsInfo.php
│ │ ├── From
│ │ │ ├── Composer
│ │ │ │ └── ProjectBuilder.php
│ │ │ ├── Configuration.php
│ │ │ ├── JsonResourceParserTrait.php
│ │ │ ├── Recommendation
│ │ │ │ ├── AbandonmentRecommendation.php
│ │ │ │ ├── DefinedRecommendation.php
│ │ │ │ ├── NoRecommendation.php
│ │ │ │ ├── NormalizableInterface.php
│ │ │ │ ├── RecommendationInterface.php
│ │ │ │ ├── Recommendations.php
│ │ │ │ ├── Resolver.php
│ │ │ │ └── UniversalRecommendation.php
│ │ │ ├── Safety
│ │ │ │ ├── ArrayValidationTrait.php
│ │ │ │ └── StructuredArrayValidator.php
│ │ │ └── SourceSite
│ │ │ │ ├── Drupal7Extension.php
│ │ │ │ ├── Drupal7SiteInspector.php
│ │ │ │ ├── ExportedDrupal7ExtensionsInspector.php
│ │ │ │ ├── ExtensionInterface.php
│ │ │ │ ├── SiteInspectorBase.php
│ │ │ │ └── SiteInspectorInterface.php
│ │ ├── LinkCommand.php
│ │ ├── LogTailCommand.php
│ │ ├── NewCommand.php
│ │ ├── NewFromDrupal7Command.php
│ │ ├── TaskWaitCommand.php
│ │ └── UnlinkCommand.php
│ ├── Archive
│ │ └── ArchiveExportCommand.php
│ ├── Auth
│ │ ├── AuthAcsfLoginCommand.php
│ │ ├── AuthAcsfLogoutCommand.php
│ │ ├── AuthLoginCommand.php
│ │ └── AuthLogoutCommand.php
│ ├── CodeStudio
│ │ ├── CodeStudioCiCdVariables.php
│ │ ├── CodeStudioCommandTrait.php
│ │ ├── CodeStudioPhpVersionCommand.php
│ │ ├── CodeStudioPipelinesMigrateCommand.php
│ │ ├── CodeStudioWizardCommand.php
│ │ ├── cs_icon.png
│ │ └── drupal_icon.png
│ ├── CommandBase.php
│ ├── DocsCommand.php
│ ├── Env
│ │ ├── EnvCertCreateCommand.php
│ │ ├── EnvCopyCronCommand.php
│ │ ├── EnvCreateCommand.php
│ │ ├── EnvDeleteCommand.php
│ │ └── EnvMirrorCommand.php
│ ├── HelloWorldCommand.php
│ ├── Ide
│ │ ├── IdeCommandBase.php
│ │ ├── IdeCreateCommand.php
│ │ ├── IdeDeleteCommand.php
│ │ ├── IdeInfoCommand.php
│ │ ├── IdeListCommand.php
│ │ ├── IdeListMineCommand.php
│ │ ├── IdeOpenCommand.php
│ │ ├── IdePhpVersionCommand.php
│ │ ├── IdeServiceRestartCommand.php
│ │ ├── IdeServiceStartCommand.php
│ │ ├── IdeServiceStopCommand.php
│ │ ├── IdeShareCommand.php
│ │ ├── IdeXdebugToggleCommand.php
│ │ └── Wizard
│ │ │ ├── IdeWizardCommandBase.php
│ │ │ ├── IdeWizardCreateSshKeyCommand.php
│ │ │ └── IdeWizardDeleteSshKeyCommand.php
│ ├── Pull
│ │ ├── PullCodeCommand.php
│ │ ├── PullCommand.php
│ │ ├── PullCommandBase.php
│ │ ├── PullDatabaseCommand.php
│ │ ├── PullFilesCommand.php
│ │ └── PullScriptsCommand.php
│ ├── Push
│ │ ├── PushArtifactCommand.php
│ │ ├── PushCodeCommand.php
│ │ ├── PushCommandBase.php
│ │ ├── PushDatabaseCommand.php
│ │ └── PushFilesCommand.php
│ ├── Remote
│ │ ├── AliasListCommand.php
│ │ ├── AliasesDownloadCommand.php
│ │ ├── DrushCommand.php
│ │ ├── SshBaseCommand.php
│ │ └── SshCommand.php
│ ├── Self
│ │ ├── ClearCacheCommand.php
│ │ ├── ListCommand.php
│ │ ├── MakeDocsCommand.php
│ │ ├── SelfInfoCommand.php
│ │ ├── TelemetryCommand.php
│ │ ├── TelemetryDisableCommand.php
│ │ └── TelemetryEnableCommand.php
│ ├── Ssh
│ │ ├── SshKeyCommandBase.php
│ │ ├── SshKeyCreateCommand.php
│ │ ├── SshKeyCreateUploadCommand.php
│ │ ├── SshKeyDeleteCommand.php
│ │ ├── SshKeyInfoCommand.php
│ │ ├── SshKeyListCommand.php
│ │ └── SshKeyUploadCommand.php
│ └── WizardCommandBase.php
├── CommandFactoryInterface.php
├── Config
│ ├── AcquiaCliConfig.php
│ └── CloudDataConfig.php
├── ConnectorFactoryInterface.php
├── DataStore
│ ├── AcquiaCliDatastore.php
│ ├── CloudDataStore.php
│ ├── DataStoreInterface.php
│ ├── Datastore.php
│ ├── JsonDataStore.php
│ └── YamlStore.php
├── EventListener
│ ├── ComposerScriptsListener.php
│ └── ExceptionListener.php
├── Exception
│ └── AcquiaCliException.php
├── Helpers
│ ├── AliasCache.php
│ ├── DataStoreContract.php
│ ├── IdeCommandTrait.php
│ ├── LocalMachineHelper.php
│ ├── LoopHelper.php
│ ├── SshCommandTrait.php
│ ├── SshHelper.php
│ └── TelemetryHelper.php
├── Kernel.php
└── Output
│ ├── Checklist.php
│ └── Spinner
│ └── Spinner.php
├── symfony.lock
├── tests
├── fixtures
│ ├── acquia-pipelines.yml
│ ├── acsf_db_response.json
│ ├── drupal7
│ │ ├── drush_to_extensions_test_file_format.sh
│ │ ├── training.acquia.com
│ │ │ ├── expected.json
│ │ │ └── extensions.json
│ │ ├── www.standard-profile.com
│ │ │ ├── expected.json
│ │ │ └── extensions.json
│ │ ├── www.webchick.net
│ │ │ ├── expected.json
│ │ │ └── extensions.json
│ │ └── www.wimleers.com
│ │ │ ├── expected.json
│ │ │ └── extensions.json
│ ├── drush-aliases
│ │ ├── .acquia
│ │ │ └── cloudapi.conf
│ │ ├── .drush
│ │ │ └── eemgrasmick.aliases.drushrc.php
│ │ ├── README-acquiacloud.txt
│ │ └── sites
│ │ │ └── devcloud2.site.yml
│ ├── git_config
│ ├── github-releases.json
│ ├── multisite-config.json
│ ├── no-multisite-config.json
│ ├── test.phar
│ └── xdebug.ini
├── integration
│ └── testcases
│ │ ├── __init__.py
│ │ └── test_acli_integration.py
└── phpunit
│ └── src
│ ├── AcsfApi
│ ├── AcsfServiceTest.php
│ └── EnvVarAcsfAuthenticationTest.php
│ ├── Application
│ ├── ComposerScriptsListenerTest.php
│ ├── ExceptionApplicationTest.php
│ ├── HelpApplicationTest.php
│ └── KernelTest.php
│ ├── ApplicationTestBase.php
│ ├── CloudApi
│ ├── AccessTokenConnectorTest.php
│ ├── AcsfClientServiceTest.php
│ ├── ClientServiceTest.php
│ └── EnvVarAuthenticationTest.php
│ ├── CommandTestBase.php
│ ├── Commands
│ ├── Acsf
│ │ ├── AcsfApiCommandTest.php
│ │ ├── AcsfAuthLoginCommandTest.php
│ │ ├── AcsfAuthLogoutCommandTest.php
│ │ ├── AcsfCommandTestBase.php
│ │ └── AcsfListCommandTest.php
│ ├── Api
│ │ ├── ApiBaseCommandTest.php
│ │ ├── ApiCommandTest.php
│ │ └── ApiListCommandTest.php
│ ├── App
│ │ ├── AppOpenCommandTest.php
│ │ ├── AppVcsInfoTest.php
│ │ ├── From
│ │ │ ├── AbandonmentRecommendationTest.php
│ │ │ ├── ConfigurationTest.php
│ │ │ ├── DefinedRecommendationTest.php
│ │ │ ├── ProjectBuilderTest.php
│ │ │ ├── RecommendationsTest.php
│ │ │ ├── TestRecommendation.php
│ │ │ └── TestSiteInspector.php
│ │ ├── LinkCommandTest.php
│ │ ├── LogTailCommandTest.php
│ │ ├── NewCommandTest.php
│ │ ├── NewFromDrupal7CommandTest.php
│ │ ├── TaskWaitCommandTest.php
│ │ └── UnlinkCommandTest.php
│ ├── Archive
│ │ └── ArchiveExporterCommandTest.php
│ ├── Auth
│ │ ├── AuthLoginCommandTest.php
│ │ └── AuthLogoutCommandTest.php
│ ├── CodeStudio
│ │ ├── CodeStudioCiCdVariablesTest.php
│ │ ├── CodeStudioPhpVersionCommandTest.php
│ │ ├── CodeStudioPipelinesMigrateCommandTest.php
│ │ └── CodeStudioWizardCommandTest.php
│ ├── CommandBaseTest.php
│ ├── DocsCommandTest.php
│ ├── Env
│ │ ├── EnvCertCreateCommandTest.php
│ │ ├── EnvCopyCronCommandTest.php
│ │ ├── EnvCreateCommandTest.php
│ │ ├── EnvDeleteCommandTest.php
│ │ └── EnvMirrorCommandTest.php
│ ├── Ide
│ │ ├── IdeCreateCommandTest.php
│ │ ├── IdeDeleteCommandTest.php
│ │ ├── IdeHelper.php
│ │ ├── IdeInfoCommandTest.php
│ │ ├── IdeListCommandMineTest.php
│ │ ├── IdeListCommandTest.php
│ │ ├── IdeOpenCommandTest.php
│ │ ├── IdePhpVersionCommandTest.php
│ │ ├── IdeRequiredTestTrait.php
│ │ ├── IdeServiceRestartCommandTest.php
│ │ ├── IdeServiceStartCommandTest.php
│ │ ├── IdeServiceStopCommandTest.php
│ │ ├── IdeShareCommandTest.php
│ │ ├── IdeXdebugToggleCommandTest.php
│ │ └── Wizard
│ │ │ ├── IdeWizardCreateSshKeyCommandTest.php
│ │ │ ├── IdeWizardDeleteSshKeyCommandTest.php
│ │ │ └── IdeWizardTestBase.php
│ ├── InferApplicationTest.php
│ ├── Pull
│ │ ├── PullCodeCommandTest.php
│ │ ├── PullCommandTest.php
│ │ ├── PullCommandTestBase.php
│ │ ├── PullDatabaseCommandTest.php
│ │ ├── PullFilesCommandTest.php
│ │ └── PullScriptsCommandTest.php
│ ├── Push
│ │ ├── PushArtifactCommandTest.php
│ │ ├── PushCodeCommandTest.php
│ │ ├── PushDatabaseCommandTest.php
│ │ └── PushFilesCommandTest.php
│ ├── Remote
│ │ ├── AliasesDownloadCommandTest.php
│ │ ├── AliasesListCommandTest.php
│ │ ├── DrushCommandTest.php
│ │ ├── SshCommandTest.php
│ │ └── SshCommandTestBase.php
│ ├── Self
│ │ ├── ClearCacheCommandTest.php
│ │ ├── MakeDocsCommandTest.php
│ │ ├── SelfInfoCommandTest.php
│ │ ├── TelemetryCommandTest.php
│ │ ├── TelemetryDisableCommandTest.php
│ │ └── TelemetryEnableCommandTest.php
│ ├── Ssh
│ │ ├── SshKeyCreateCommandTest.php
│ │ ├── SshKeyCreateUploadCommandTest.php
│ │ ├── SshKeyDeleteCommandTest.php
│ │ ├── SshKeyInfoCommandTest.php
│ │ ├── SshKeyListCommandTest.php
│ │ └── SshKeyUploadCommandTest.php
│ ├── UpdateCommandTest.php
│ └── WizardTestBase.php
│ ├── Misc
│ ├── ApiSpecTest.php
│ ├── ChecklistTest.php
│ ├── EnvDbCredsTest.php
│ ├── ExceptionListenerTest.php
│ ├── LocalMachineHelperTest.php
│ └── TelemetryHelperTest.php
│ └── TestBase.php
└── var
└── .gitkeep
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 4
8 | end_of_line = lf
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.xml.dist]
13 | indent_size = 2
14 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | BUGSNAG_KEY=abcd1234
2 | AMPLITUDE_KEY=acbd1234
3 | ACLI_VERSION=1.0.0
4 |
--------------------------------------------------------------------------------
/.envrc:
--------------------------------------------------------------------------------
1 | export PATH="$PATH:${PWD}/vendor/bin"
2 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | **Motivation**
2 |
3 | Fixes #NNN
4 |
5 | **Proposed changes**
6 |
7 |
8 | **Alternatives considered**
9 |
10 |
11 | **Testing steps**
12 |
13 |
14 | 1. Follow the [contribution guide](https://github.com/acquia/cli/blob/HEAD/CONTRIBUTING.md#building-and-testing) to set up your development environment or [download a pre-built acli.phar](https://github.com/acquia/cli/blob/HEAD/CONTRIBUTING.md#automatic-dev-builds) for this PR.
15 | 2. If running from source, clear the kernel cache to pick up new and changed commands: `./bin/acli ckc`
16 | 3. Check for regressions: (add specific steps for this pr)
17 | 4. Check new functionality: (add specific steps for this pr)
18 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: 'composer'
4 | directory: '/'
5 | versioning-strategy: lockfile-only
6 | schedule:
7 | interval: 'weekly'
8 | groups:
9 | dependencies:
10 | patterns:
11 | - '*'
12 | - package-ecosystem: 'github-actions'
13 | directory: '/'
14 | schedule:
15 | interval: 'weekly'
16 | ignore:
17 | - dependency-name: "release-drafter/release-drafter"
18 |
--------------------------------------------------------------------------------
/.github/release-drafter.yml:
--------------------------------------------------------------------------------
1 | name-template: '$RESOLVED_VERSION'
2 | tag-template: '$RESOLVED_VERSION'
3 | categories:
4 | - title: '🚨 Major changes'
5 | label: 'breaking change'
6 | - title: '🚀 Enhancements'
7 | label: 'enhancement'
8 | - title: '🐛 Bug Fixes'
9 | label: 'bug'
10 | - title: '🧰 Maintenance'
11 | label: 'chore'
12 | - title: '🧰 Dependency updates'
13 | label: 'dependencies'
14 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
15 | change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
16 | version-resolver:
17 | major:
18 | labels:
19 | - 'breaking change'
20 | minor:
21 | labels:
22 | - 'enhancement'
23 | patch:
24 | labels:
25 | - 'bug'
26 | default: patch
27 | template: |
28 | ## What's new since $PREVIOUS_TAG
29 |
30 | $CHANGES
31 | prerelease: true
32 |
--------------------------------------------------------------------------------
/.github/workflows/autolabel.yml:
--------------------------------------------------------------------------------
1 | name: Auto-label pull requests
2 | on:
3 | pull_request_target:
4 | types: [opened, synchronize, reopened, labeled, unlabeled]
5 | permissions:
6 | pull-requests: write
7 | jobs:
8 | autolabel:
9 | uses: acquia/.github/.github/workflows/autolabel.yml@main
10 | secrets:
11 | github-token: ${{ secrets.GITHUB_TOKEN }}
12 |
--------------------------------------------------------------------------------
/.github/workflows/automerge.yml:
--------------------------------------------------------------------------------
1 | name: Auto-merge dependency updates
2 | on:
3 | workflow_run:
4 | types:
5 | - 'completed'
6 | workflows:
7 | - 'CI'
8 | branches:
9 | - 'dependabot/**'
10 | permissions:
11 | contents: write
12 | pull-requests: write
13 | jobs:
14 | automerge:
15 | uses: acquia/.github/.github/workflows/automerge.yml@main
16 | secrets:
17 | github-token: ${{ secrets.GITHUB_TOKEN }}
18 |
--------------------------------------------------------------------------------
/.github/workflows/create-ccb-ticket.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | render('ccb-ticket.twig', [
11 | // @see https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
12 | 'GITHUB_RELEASE_BODY' => $argv[1],
13 | 'GITHUB_RELEASE_NAME' => $argv[2],
14 | 'GITHUB_RELEASE_URL' => $argv[3],
15 | 'GITHUB_ACTIONS_RUN_URL' => $argv[4],
16 | 'JIRA_BASE_URL' => $argv[5],
17 | ]);
18 | $body = htmlspecialchars($body);
19 | $body = preg_replace(
20 | '/[\x{1F600}-\x{1F64F}\x{2700}-\x{27BF}\x{1F680}-\x{1F6FF}\x{24C2}-\x{1F251}\x{1F30D}-\x{1F567}\x{1F900}-\x{1F9FF}\x{1F300}-\x{1F5FF}]/u',
21 | '[emoji-removed]',
22 | $body
23 | );
24 | $body = str_replace('## ', 'h4. ', $body);
25 | $body = str_replace(''', ''', $body);
26 |
27 | echo $body;
--------------------------------------------------------------------------------
/.github/workflows/integration-tests.yml:
--------------------------------------------------------------------------------
1 | name: Run Tests on Release
2 |
3 | on:
4 | release:
5 | types: [created]
6 |
7 | jobs:
8 | test:
9 | runs-on: ubuntu-22.04
10 | steps:
11 | - name: Check out repository code
12 | uses: actions/checkout@v4
13 |
14 | - uses: shivammathur/setup-php@v2
15 | with:
16 | coverage: none
17 | php-version: '8.1'
18 |
19 | - name: Build
20 | run: |
21 | composer install --no-dev --optimize-autoloader
22 | composer box-install
23 | # Warm the symfony cache so it gets bundled with phar.
24 | ./bin/acli
25 | composer box-compile
26 |
27 | - name: Set up Python
28 | uses: actions/setup-python@v5
29 | with:
30 | python-version: '3.8'
31 |
32 | - name: Run tests
33 | env:
34 | APPLICATION_UUID: ${{ secrets.APPLICATION_UUID }}
35 | APPLICATION_NAME: ${{ secrets.APPLICATION_NAME }}
36 | ACLI_AUTH_TOKEN: ${{ secrets.ACLI_AUTH_TOKEN }}
37 | ACLI_AUTH_SECRET: ${{ secrets.ACLI_AUTH_SECRET }}
38 | # Add more secret environment variables as needed
39 | run: python3 -m unittest discover -s tests/integration/testcases
40 |
--------------------------------------------------------------------------------
/.github/workflows/jira.yml:
--------------------------------------------------------------------------------
1 | name: Sync GitHub issues to Jira
2 | on:
3 | issues:
4 | types:
5 | - opened
6 | - closed
7 | pull_request_target:
8 | types:
9 | - opened
10 | permissions:
11 | issues: write
12 | pull-requests: read
13 | jobs:
14 | jira:
15 | uses: acquia/.github/.github/workflows/jira.yml@main
16 | secrets:
17 | jira-api-token: ${{ secrets.JIRA_API_TOKEN }}
18 | github-token: ${{ secrets.GITHUB_TOKEN }}
19 | jira-user-email: ${{ secrets.JIRA_USER_EMAIL }}
20 | with:
21 | project_key: CLI
22 |
--------------------------------------------------------------------------------
/.github/workflows/mutation.yml:
--------------------------------------------------------------------------------
1 | name: Mutation Testing
2 |
3 | on:
4 | push:
5 | # Prevent duplicate jobs on Dependabot PRs that interfere with automerge.
6 | branches-ignore:
7 | - 'dependabot/**'
8 | pull_request:
9 |
10 | jobs:
11 | tests:
12 | runs-on: ubuntu-22.04
13 |
14 | name: Mutation Testing
15 | steps:
16 | - name: Checkout code
17 | uses: actions/checkout@v4
18 |
19 | - name: Setup PHP
20 | uses: shivammathur/setup-php@v2
21 | with:
22 | php-version: 8.2
23 | tools: composer:v2
24 | coverage: pcov
25 |
26 | - name: Install dependencies
27 | run: |
28 | composer install --no-progress --no-suggest --no-interaction
29 |
30 | - name: Run Infection for added files only
31 | if: github.event_name == 'pull_request'
32 | run: |
33 | git fetch --depth=1 origin $GITHUB_BASE_REF
34 | # nproc returns 4 threads on GitHub Actions and this seems to provide the best performance.
35 | composer mutation-diff-lines
36 |
37 | - name: Run Infection for all files
38 | if: github.event_name == 'push'
39 | env:
40 | INFECTION_DASHBOARD_API_KEY: ${{ secrets.INFECTION_DASHBOARD_API_KEY }}
41 | run: composer mutation
42 |
--------------------------------------------------------------------------------
/.github/workflows/publish-release.yml:
--------------------------------------------------------------------------------
1 | name: Publish release on CCB approval
2 | on:
3 | workflow_dispatch:
4 | schedule:
5 | - cron: '0 0 * * *'
6 | jobs:
7 | publish-release:
8 | runs-on: ubuntu-22.04
9 | name: Publish release on CCB approval
10 | permissions:
11 | contents: write
12 | steps:
13 | - name: Get reviewed release
14 | run: |
15 | ISSUES=$(curl --request GET \
16 | --url "$JIRA_BASE_URL/rest/api/3/search?jql=project%20%3D%20CLI%20AND%20issuetype%20%3D%20Release%20AND%20status%20%3D%20Reviewed" \
17 | --user "$JIRA_USER_EMAIL:$JIRA_API_TOKEN" \
18 | --header 'Accept: application/json')
19 | echo "FIX_VERSION=$(printf '%s' $ISSUES | jq -r '.issues[0].fields.fixVersions[0].name' | sed 's/AcquiaCLI//')" >> $GITHUB_ENV
20 | echo "ISSUE_KEY=$(printf '%s' $ISSUES | jq -r '.issues[0].key')" >> $GITHUB_ENV
21 | env:
22 | JIRA_BASE_URL: ${{ vars.JIRA_BASE_URL }}
23 | JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
24 | JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
25 | - name: Login to Jira
26 | if: env.FIX_VERSION != 'null' && env.ISSUE_KEY != 'null'
27 | uses: acquia/gajira-login@master
28 | env:
29 | JIRA_BASE_URL: ${{ vars.JIRA_BASE_URL }}
30 | JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
31 | JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
32 | - name: Start release
33 | if: env.FIX_VERSION != 'null' && env.ISSUE_KEY != 'null'
34 | uses: acquia/gajira-transition@master
35 | with:
36 | issue: ${{ env.ISSUE_KEY }}
37 | transition: Start Release
38 | - name: Publish release
39 | if: env.FIX_VERSION != 'null' && env.ISSUE_KEY != 'null'
40 | run: |
41 | gh release edit $FIX_VERSION --prerelease=false --latest --repo acquia/cli
42 | env:
43 | GH_TOKEN: ${{ github.token }}
44 | - name: Close release
45 | if: env.FIX_VERSION != 'null' && env.ISSUE_KEY != 'null'
46 | uses: acquia/gajira-transition@master
47 | with:
48 | issue: ${{ env.ISSUE_KEY }}
49 | transition: Released
50 |
--------------------------------------------------------------------------------
/.github/workflows/release-drafter.yml:
--------------------------------------------------------------------------------
1 | name: Release Drafter
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | workflow_run:
8 | types:
9 | - 'completed'
10 | workflows:
11 | - 'Auto-label pull requests'
12 | - 'Auto-merge dependency updates'
13 |
14 | jobs:
15 | update_release_draft:
16 | runs-on: ubuntu-22.04
17 | permissions:
18 | contents: write
19 | pull-requests: write
20 | steps:
21 | - uses: release-drafter/release-drafter@v6.0.0
22 | env:
23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ###> symfony/framework-bundle ###
2 | /var/
3 | /vendor/
4 | ###< symfony/framework-bundle ###
5 |
6 | drupal
7 | .idea
8 | *.bak
9 | .DS_STORE
10 |
11 | ###> squizlabs/php_codesniffer ###
12 | /phpcs.xml
13 | ###< squizlabs/php_codesniffer ###
14 |
15 | ###> phpunit/phpunit ###
16 | /phpunit.xml
17 | ###< phpunit/phpunit ###
18 |
19 | cx-api-spec
20 | gardener
21 | .env
22 |
23 | // Artifacts from mutation testing
24 | *.cache
25 |
--------------------------------------------------------------------------------
/.phplint.yml:
--------------------------------------------------------------------------------
1 | path: ./
2 | jobs: 10
3 | cache: var/phplint.cache
4 | extensions:
5 | - php
6 | exclude:
7 | - var
8 | - vendor
9 | warning: false
10 |
--------------------------------------------------------------------------------
/.phpstorm.meta.php:
--------------------------------------------------------------------------------
1 | AccountResponse::class,
16 | 'getApplications' => ApplicationsResponse::class,
17 | 'getApplicationByUuid' => ApplicationResponse::class,
18 | 'getApplicationEnvironments' => EnvironmentsResponse::class,
19 | 'getCron' => CronResponse::class,
20 | 'getCronJobsByEnvironmentId' => CronsResponse::class,
21 | 'getEnvironment' => EnvironmentResponse::class,
22 | 'getEnvironmentsDatabases' => DatabasesResponse::class,
23 | 'getIde' => IdeResponse::class
24 | ]));
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |  [](https://codecov.io/github/acquia/cli) [](https://dashboard.stryker-mutator.io/reports/github.com/acquia/cli/main)
2 | # Acquia CLI
3 |
4 | The official command-line tool for interacting with the Acquia Cloud Platform and services. Acquia CLI (acli) helps you run [Drush](http://www.drush.org/) commands and tail logs from your Acquia-hosted applications, manage [Acquia Cloud IDEs](https://docs.acquia.com/dev-studio/ide/), create and manage teams and applications via the [Cloud Platform API](https://cloudapi-docs.acquia.com/), and much more!
5 |
6 | Acquia CLI is not a local development environment. If you are looking for an integrated development environment, consider [Acquia Cloud IDE](https://docs.acquia.com/dev-studio/ide/) or third-party tools such as [Lando](https://lando.dev/).
7 |
8 |
9 | ## Installation and usage
10 |
11 | Install instructions and official documentation are available at https://docs.acquia.com/acquia-cli/install/
12 |
13 | ## Contribution
14 |
15 | See [CONTRIBUTING.md](CONTRIBUTING.md) for instructions on building, testing, and contributing to Acquia CLI.
16 |
17 | ## Support
18 |
19 | - To receive support from Acquia, visit the [Acquia Support Portal](https://acquia.my.site.com/s/).
20 | - To receive support from and discuss ideas with other Acquia CLI users, visit the [discussions section](https://github.com/acquia/cli/discussions) on GitHub.
21 |
--------------------------------------------------------------------------------
/bin/acli.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 | REM Running this file is equivalent to running `php acli`
3 | setlocal DISABLEDELAYEDEXPANSION
4 | SET BIN_TARGET=%~dp0acli
5 | php "%BIN_TARGET%" %*
6 |
--------------------------------------------------------------------------------
/box.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://raw.githubusercontent.com/box-project/box/4.3.7/res/schema.json",
3 | "directories": [
4 | "config",
5 | "var/cache"
6 | ],
7 | "files": [
8 | ".env"
9 | ],
10 | "compactors": [
11 | "KevinGH\\Box\\Compactor\\Php",
12 | "KevinGH\\Box\\Compactor\\Json"
13 | ],
14 | "output": "var/acli.phar",
15 | "exclude-composer-files": false,
16 | "force-autodiscovery": true
17 | }
18 |
--------------------------------------------------------------------------------
/box.json.md:
--------------------------------------------------------------------------------
1 | ## Box configuration
2 |
3 | box.json is largely based on the template provided here: https://github.com/humbug/box/blob/master/fixtures/build/dir012/box.json.dist
4 |
5 | This particular configuration is necessary to support Symfony Console. Specifically:
6 | - Must include composer files, since Symfony uses these to determine the root directory
7 | - Must force autodisovery, since Symfony won't be able to find service classes otherwise
8 | - Must force include of config and var directories, since Symfony uses these for cache and config
9 |
10 | See also: https://github.com/humbug/box/blob/master/doc/symfony.md
11 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | ignore:
2 | # It's not feasible to spin up a working Drupal 7 site as part of ACLI's test suite.
3 | - src/Command/App/From/SourceSite/Drupal7SiteInspector.php
4 | coverage:
5 | status:
6 | project:
7 | default:
8 | only_pulls: true
9 |
--------------------------------------------------------------------------------
/config/dev/services.yml:
--------------------------------------------------------------------------------
1 | imports:
2 | - { resource: '../prod/services.yml' }
3 |
4 | services:
5 | Symfony\Component\Console\Output\BufferedOutput: ~
6 | Symfony\Component\Console\Output\OutputInterface:
7 | alias: Symfony\Component\Console\Output\BufferedOutput
8 | public: true
9 | Symfony\Component\Console\Input\ArrayInput: ~
10 | Symfony\Component\Console\Input\InputInterface:
11 | alias: Symfony\Component\Console\Input\ArrayInput
12 | public: true
13 |
--------------------------------------------------------------------------------
/config/from_d7_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "rootPackageDefinition": {
3 | "type": "project",
4 | "description": "Generated by Acquia CLI's app:new:from:drupal7",
5 | "repositories": [
6 | {
7 | "type": "composer",
8 | "url": "https://packages.drupal.org/8"
9 | }
10 | ],
11 | "require": {
12 | "composer/installers": "^1.9",
13 | "cweagans/composer-patches": "^1.7",
14 | "drupal/core-composer-scaffold": "9.0.1",
15 | "drupal/core-project-message": "9.0.1",
16 | "drupal/core-recommended": "9.0.1",
17 | "drush/drush": "*"
18 | },
19 | "config": {
20 | "sort-packages": true,
21 | "allow-plugins": {
22 | "composer/installers": true,
23 | "cweagans/composer-patches": true,
24 | "drupal/core-composer-scaffold": true,
25 | "drupal/core-project-message": true
26 | }
27 | },
28 | "conflict": {
29 | "drupal/drupal": "*"
30 | },
31 | "minimum-stability": "dev",
32 | "prefer-stable": true,
33 | "extra": {
34 | "enable-patching": true,
35 | "patchLevel": {
36 | "drupal/core": "-p2"
37 | },
38 | "drupal-scaffold": {
39 | "locations": {
40 | "web-root": "docroot/"
41 | }
42 | },
43 | "installer-paths": {
44 | "docroot/core": [
45 | "type:drupal-core"
46 | ],
47 | "docroot/libraries/{$name}": [
48 | "type:drupal-library"
49 | ],
50 | "docroot/modules/contrib/{$name}": [
51 | "type:drupal-module"
52 | ],
53 | "docroot/profiles/contrib/{$name}": [
54 | "type:drupal-profile"
55 | ],
56 | "docroot/themes/contrib/{$name}": [
57 | "type:drupal-theme"
58 | ],
59 | "drush/Commands/contrib/{$name}": [
60 | "type:drupal-drush"
61 | ],
62 | "docroot/modules/custom/{$name}": [
63 | "type:drupal-custom-module"
64 | ],
65 | "docroot/profiles/custom/{$name}": [
66 | "type:drupal-custom-profile"
67 | ],
68 | "docroot/themes/custom/{$name}": [
69 | "type:drupal-custom-theme"
70 | ]
71 | }
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:8-alpine
2 |
3 | RUN set -eux ; \
4 | apk add --no-cache \
5 | tini
6 |
7 | RUN curl https://github.com/acquia/cli/releases/latest/download/acli.phar -L -o /usr/local/bin/acli \
8 | && chmod +x /usr/local/bin/acli
9 |
10 | COPY docker-entrypoint.sh /docker-entrypoint.sh
11 |
12 | ENTRYPOINT ["/docker-entrypoint.sh"]
13 |
14 | CMD ["acli"]
15 |
--------------------------------------------------------------------------------
/docker/docker-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | isCommand() {
4 | # Retain backwards compatibility with common CI providers,
5 | # see: https://github.com/composer/docker/issues/107
6 | if [ "$1" = "sh" ]; then
7 | return 1
8 | fi
9 |
10 | acli help --no-interaction "$1" > /dev/null 2>&1
11 | }
12 |
13 | # check if the first argument passed in looks like a flag
14 | if [ "${1#-}" != "$1" ]; then
15 | set -- /sbin/tini -- acli "$@"
16 | # check if the first argument passed in is acli
17 | elif [ "$1" = 'acli' ]; then
18 | set -- /sbin/tini -- "$@"
19 | # check if the first argument passed in matches a known command
20 | elif isCommand "$1"; then
21 | set -- /sbin/tini -- acli "$@"
22 | fi
23 |
24 | exec "$@"
25 |
--------------------------------------------------------------------------------
/grumphp.yml:
--------------------------------------------------------------------------------
1 | grumphp:
2 | tasks:
3 | phpcs: ~
4 | fixer:
5 | fix_by_default: true
6 | ascii:
7 | failed: ~
8 | succeeded: ~
9 |
--------------------------------------------------------------------------------
/infection.json5:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "vendor/infection/infection/resources/schema.json",
3 | "source": {
4 | "directories": [
5 | "src"
6 | ]
7 | },
8 | "logs": {
9 | "stryker": {
10 | "report": "main"
11 | },
12 | "html": "var/infection.html"
13 | },
14 | "mutators": {
15 | "@default": true,
16 | "global-ignoreSourceCodeByRegex": [
17 | "\\$this->logger.*"
18 | ]
19 | },
20 | "timeout": 30,
21 | "testFrameworkOptions": "--exclude-group=serial"
22 | }
23 |
--------------------------------------------------------------------------------
/phpcs.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 | Acquia CLI PHP CodeSniffer configuration.
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | src
18 | tests
19 |
20 |
21 |
22 |
23 | tests/fixtures/*
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/phpstan.neon.dist:
--------------------------------------------------------------------------------
1 | parameters:
2 | level: 1
3 | paths:
4 | - src
5 | - tests
6 | - bin
7 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | tests/phpunit
11 |
12 |
13 |
14 |
15 |
16 | src
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/AcsfApi/AcsfClient.php:
--------------------------------------------------------------------------------
1 | getBody();
16 | $body = json_decode((string) $bodyJson, false, 512, JSON_THROW_ON_ERROR);
17 |
18 | // ACSF sometimes returns an array rather than an object.
19 | if (is_array($body)) {
20 | return $body;
21 | }
22 |
23 | if (property_exists($body, '_embedded') && property_exists($body->_embedded, 'items')) {
24 | return $body->_embedded->items;
25 | }
26 |
27 | if (property_exists($body, 'error') && property_exists($body, 'message')) {
28 | throw new ApiErrorException($body);
29 | }
30 | // Throw error for 4xx and 5xx responses.
31 | if (
32 | property_exists($body, 'message') && in_array(substr((string) $response->getStatusCode(), 0, 1), [
33 | '4',
34 | '5',
35 | ], true)
36 | ) {
37 | $body->error = $response->getStatusCode();
38 | throw new ApiErrorException($body);
39 | }
40 |
41 | return $body;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/AcsfApi/AcsfClientService.php:
--------------------------------------------------------------------------------
1 | connector);
20 | $this->configureClient($client);
21 |
22 | return $client;
23 | }
24 |
25 | protected function checkAuthentication(): bool
26 | {
27 | return ($this->credentials->getCloudKey() && $this->credentials->getCloudSecret());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/AcsfApi/AcsfConnector.php:
--------------------------------------------------------------------------------
1 | $config
15 | */
16 | public function __construct(array $config, ?string $baseUri = null, ?string $urlAccessToken = null)
17 | {
18 | parent::__construct($config, $baseUri, $urlAccessToken);
19 |
20 | $this->client = new GuzzleClient([
21 | 'auth' => [
22 | $config['key'],
23 | $config['secret'],
24 | ],
25 | 'base_uri' => $this->getBaseUri(),
26 | ]);
27 | }
28 |
29 | /**
30 | * @param array $options
31 | */
32 | public function sendRequest(string $verb, string $path, array $options): ResponseInterface
33 | {
34 | return $this->client->request($verb, $path, $options);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/AcsfApi/AcsfConnectorFactory.php:
--------------------------------------------------------------------------------
1 | $config
13 | */
14 | public function __construct(protected array $config, protected ?string $baseUri = null)
15 | {
16 | }
17 |
18 | public function createConnector(): AcsfConnector
19 | {
20 | return new AcsfConnector($this->config, $this->baseUri);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/AcsfApi/AcsfCredentials.php:
--------------------------------------------------------------------------------
1 | getCurrentFactory()) && $activeUser = $this->getFactoryActiveUser($currentFactory)) {
26 | return $activeUser['username'];
27 | }
28 |
29 | return null;
30 | }
31 |
32 | /**
33 | * @param array $factory
34 | */
35 | public function getFactoryActiveUser(array $factory): mixed
36 | {
37 | if (array_key_exists('active_user', $factory)) {
38 | $activeUser = $factory['active_user'];
39 | if (array_key_exists($activeUser, $factory['users'])) {
40 | return $factory['users'][$activeUser];
41 | }
42 | }
43 |
44 | return null;
45 | }
46 |
47 | private function getCurrentFactory(): mixed
48 | {
49 | if (($factory = $this->datastoreCloud->get('acsf_active_factory')) && ($acsfFactories = $this->datastoreCloud->get('acsf_factories')) && array_key_exists($factory, $acsfFactories)) {
50 | return $acsfFactories[$factory];
51 | }
52 | return null;
53 | }
54 |
55 | public function getCloudSecret(): ?string
56 | {
57 | if (getenv('ACSF_KEY')) {
58 | return getenv('ACSF_KEY');
59 | }
60 |
61 | if (($currentFactory = $this->getCurrentFactory()) && $activeUser = $this->getFactoryActiveUser($currentFactory)) {
62 | return $activeUser['key'];
63 | }
64 |
65 | return null;
66 | }
67 |
68 | public function getBaseUri(): ?string
69 | {
70 | if (getenv('ACSF_FACTORY_URI')) {
71 | return getenv('ACSF_FACTORY_URI');
72 | }
73 | if ($factory = $this->datastoreCloud->get('acsf_active_factory')) {
74 | return $factory;
75 | }
76 |
77 | return null;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/ApiCredentialsInterface.php:
--------------------------------------------------------------------------------
1 | helpMessages;
26 | }
27 |
28 | public function setHelpMessages(array $helpMessages): void
29 | {
30 | $this->helpMessages = $helpMessages;
31 | }
32 |
33 | public function renderThrowable(
34 | Throwable $e,
35 | OutputInterface $output
36 | ): void {
37 | parent::renderThrowable($e, $output);
38 |
39 | if ($this->getHelpMessages()) {
40 | $io = new SymfonyStyle(new ArrayInput([]), $output);
41 | $outputStyle = new OutputFormatterStyle('white', 'blue');
42 | $output->getFormatter()->setStyle('help', $outputStyle);
43 | $io->block($this->getHelpMessages(), 'help', 'help', ' ', true, false);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Attribute/RequireAuth.php:
--------------------------------------------------------------------------------
1 | $config
23 | */
24 | public function __construct(array $config, ?string $baseUri = null, ?string $urlAccessToken = null)
25 | {
26 | $this->accessToken = new AccessToken(['access_token' => $config['access_token']]);
27 | parent::__construct($config, $baseUri, $urlAccessToken);
28 | }
29 |
30 | public function createRequest(string $verb, string $path): RequestInterface
31 | {
32 | if ($file = getenv('ACLI_ACCESS_TOKEN_FILE')) {
33 | if (!file_exists($file)) {
34 | throw new AcquiaCliException('Access token file not found at {file}', ['file' => $file]);
35 | }
36 | $this->accessToken = new AccessToken(['access_token' => trim(file_get_contents($file), "\"\n")]);
37 | }
38 | return $this->provider->getAuthenticatedRequest(
39 | $verb,
40 | $this->getBaseUri() . $path,
41 | $this->accessToken
42 | );
43 | }
44 |
45 | public function setProvider(
46 | GenericProvider $provider
47 | ): void {
48 | $this->provider = $provider;
49 | }
50 |
51 | public function getAccessToken(): AccessToken
52 | {
53 | return $this->accessToken;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/CloudApi/ConnectorFactory.php:
--------------------------------------------------------------------------------
1 | $config
15 | */
16 | public function __construct(protected array $config, protected ?string $baseUri = null, protected ?string $accountsUri = null)
17 | {
18 | }
19 |
20 | /**
21 | * @return \Acquia\Cli\CloudApi\AccessTokenConnector|\AcquiaCloudApi\Connector\Connector
22 | */
23 | public function createConnector(): Connector|AccessTokenConnector
24 | {
25 | // A defined key & secret takes priority.
26 | if ($this->config['key'] && $this->config['secret']) {
27 | return new Connector($this->config, $this->baseUri, $this->accountsUri);
28 | }
29 |
30 | // Fall back to a valid access token.
31 | if ($this->config['accessToken']) {
32 | $accessToken = $this->createAccessToken();
33 | if (!$accessToken->hasExpired()) {
34 | // @todo Add debug log entry indicating that access token is being used.
35 | return new AccessTokenConnector([
36 | 'access_token' => $accessToken,
37 | 'key' => null,
38 | 'secret' => null,
39 | ], $this->baseUri, $this->accountsUri);
40 | }
41 | }
42 |
43 | // Fall back to an unauthenticated request.
44 | return new Connector($this->config, $this->baseUri, $this->accountsUri);
45 | }
46 |
47 | private function createAccessToken(): AccessToken
48 | {
49 | return new AccessToken([
50 | 'access_token' => $this->config['accessToken'],
51 | 'expires' => $this->config['accessTokenExpiry'],
52 | ]);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Command/Acsf/AcsfCommandFactory.php:
--------------------------------------------------------------------------------
1 | localMachineHelper,
40 | $this->datastoreCloud,
41 | $this->datastoreAcli,
42 | $this->cloudCredentials,
43 | $this->telemetryHelper,
44 | $this->projectDir,
45 | $this->cloudApiClientService,
46 | $this->sshHelper,
47 | $this->sshDir,
48 | $this->logger,
49 | $this->selfUpdateManager,
50 | );
51 | }
52 |
53 | public function createListCommand(): AcsfListCommand
54 | {
55 | return new AcsfListCommand(
56 | $this->localMachineHelper,
57 | $this->datastoreCloud,
58 | $this->datastoreAcli,
59 | $this->cloudCredentials,
60 | $this->telemetryHelper,
61 | $this->projectDir,
62 | $this->cloudApiClientService,
63 | $this->sshHelper,
64 | $this->sshDir,
65 | $this->logger,
66 | $this->selfUpdateManager,
67 | );
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Command/Acsf/AcsfListCommand.php:
--------------------------------------------------------------------------------
1 | namespace = $namespace;
19 | }
20 |
21 | protected function execute(InputInterface $input, OutputInterface $output): int
22 | {
23 | $commands = $this->getApplication()->all();
24 | foreach ($commands as $command) {
25 | if (
26 | $command->getName() !== $this->namespace
27 | // E.g., if the namespace is acsf:api, show all acsf:api:* commands.
28 | && str_contains($command->getName(), $this->namespace . ':')
29 | // This is a lazy way to exclude api:base and acsf:base.
30 | && $command->getDescription()
31 | ) {
32 | $command->setHidden(false);
33 | } else {
34 | $command->setHidden();
35 | }
36 | }
37 |
38 | $command = $this->getApplication()->find('list');
39 | $arguments = [
40 | 'command' => 'list',
41 | 'namespace' => 'acsf',
42 | ];
43 | $listInput = new ArrayInput($arguments);
44 |
45 | return $command->run($listInput, $output);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Command/Api/ApiCommandFactory.php:
--------------------------------------------------------------------------------
1 | localMachineHelper,
39 | $this->datastoreCloud,
40 | $this->datastoreAcli,
41 | $this->cloudCredentials,
42 | $this->telemetryHelper,
43 | $this->projectDir,
44 | $this->cloudApiClientService,
45 | $this->sshHelper,
46 | $this->sshDir,
47 | $this->logger,
48 | $this->selfUpdateManager,
49 | );
50 | }
51 |
52 | public function createListCommand(): ApiListCommand
53 | {
54 | return new ApiListCommand(
55 | $this->localMachineHelper,
56 | $this->datastoreCloud,
57 | $this->datastoreAcli,
58 | $this->cloudCredentials,
59 | $this->telemetryHelper,
60 | $this->projectDir,
61 | $this->cloudApiClientService,
62 | $this->sshHelper,
63 | $this->sshDir,
64 | $this->logger,
65 | $this->selfUpdateManager,
66 | );
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/Command/Api/ApiListCommand.php:
--------------------------------------------------------------------------------
1 | namespace = $namespace;
19 | }
20 |
21 | protected function execute(InputInterface $input, OutputInterface $output): int
22 | {
23 | $commands = $this->getApplication()->all();
24 | foreach ($commands as $command) {
25 | if (
26 | $command->getName() !== $this->namespace
27 | && str_contains($command->getName(), $this->namespace . ':')
28 | // This is a lazy way to exclude api:base and acsf:base.
29 | && $command->getDescription()
30 | ) {
31 | $command->setHidden(false);
32 | } else {
33 | $command->setHidden();
34 | }
35 | }
36 |
37 | $command = $this->getApplication()->find('list');
38 | $arguments = [
39 | 'command' => 'list',
40 | 'namespace' => 'api',
41 | ];
42 | $listInput = new ArrayInput($arguments);
43 |
44 | return $command->run($listInput, $output);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Command/App/AppOpenCommand.php:
--------------------------------------------------------------------------------
1 | acceptApplicationUuid();
26 | }
27 |
28 | protected function execute(InputInterface $input, OutputInterface $output): int
29 | {
30 | if (!$this->localMachineHelper->isBrowserAvailable()) {
31 | throw new AcquiaCliException('No browser is available on this machine');
32 | }
33 | $applicationUuid = $this->determineCloudApplication();
34 | $this->localMachineHelper->startBrowser('https://cloud.acquia.com/a/applications/' . $applicationUuid);
35 |
36 | return Command::SUCCESS;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Command/App/From/Configuration.php:
--------------------------------------------------------------------------------
1 |
21 | */
22 | protected array $array;
23 |
24 | /**
25 | * Configuration constructor.
26 | *
27 | * @param array $config
28 | * An array of configuration, usually parsed from a configuration file.
29 | */
30 | protected function __construct(array $config)
31 | {
32 | $this->array = static::schema([
33 | 'rootPackageDefinition' => 'is_array',
34 | ])($config);
35 | }
36 |
37 | /**
38 | * Creates a configuration object from configuration given as a PHP
39 | * resource.
40 | *
41 | * The given PHP resource is usually obtained by calling fopen($location).
42 | *
43 | * @param resource $configuration_resource
44 | * Configuration to be parse; given as a PHP resource.
45 | * @return \Acquia\Cli\Command\App\From\Configuration
46 | * A new configuration object.
47 | */
48 | public static function createFromResource($configuration_resource): Configuration
49 | {
50 | return new static(static::parseJsonResource($configuration_resource));
51 | }
52 |
53 | /**
54 | * Gets an basic root composer package definition for a Drupal 9+ project.
55 | *
56 | * @return array
57 | * An array representing a root composer package definition. From this
58 | * starting point, additional dependencies and metadata can be added until
59 | * an acceptable project is defined for migrating a source site to Drupal
60 | * 9+.
61 | */
62 | public function getRootPackageDefinition(): array
63 | {
64 | return $this->array['rootPackageDefinition'];
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Command/App/From/JsonResourceParserTrait.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | public function normalize(): array;
19 | }
20 |
--------------------------------------------------------------------------------
/src/Command/App/From/Recommendation/Recommendations.php:
--------------------------------------------------------------------------------
1 | extensions;
44 | }
45 |
46 | public function getPublicFilePath(): string
47 | {
48 | return 'sites/default/files';
49 | }
50 |
51 | public function getPrivateFilePath(): ?string
52 | {
53 | return null;
54 | }
55 |
56 | /**
57 | * Reads an extensions resource into extensions objects.
58 | *
59 | * @param resource $extensions_resource
60 | * A serialized extensions resource from which to parse extensions.
61 | * @return \Acquia\Cli\Command\App\From\SourceSite\Drupal7Extension[]
62 | * An array of extensions.
63 | */
64 | protected static function parseExtensionsFromResource($extensions_resource): array
65 | {
66 | return array_map(function (array $extension) {
67 | $extension['status'] = $extension['enabled'];
68 | return Drupal7Extension::createFromStdClass((object) $extension);
69 | }, static::parseJsonResource($extensions_resource));
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/Command/App/From/SourceSite/ExtensionInterface.php:
--------------------------------------------------------------------------------
1 | readExtensions(), function (ExtensionInterface $extension) use ($state_flags, $type_flags) {
17 | // Generate a flag for the extension's enabled/disabled state.
18 | $has = $extension->isEnabled() ? SiteInspectorInterface::FLAG_EXTENSION_ENABLED : SiteInspectorInterface::FLAG_EXTENSION_DISABLED;
19 | // Incorporate the extension's type.
20 | $has = $has | ($extension->isModule() ? SiteInspectorInterface::FLAG_EXTENSION_MODULE : 0);
21 | $has = $has | ($extension->isTheme() ? SiteInspectorInterface::FLAG_EXTENSION_THEME : 0);
22 | // TRUE if the extension has a flag in $type_flags AND a flag in
23 | // $state_flags, FALSE otherwise.
24 | return ($has & $type_flags) && ($has & $state_flags);
25 | });
26 | }
27 |
28 | /**
29 | * Returns a list of extensions discovered on the inspected site.
30 | *
31 | * @return \Acquia\Cli\Command\App\From\SourceSite\ExtensionInterface[]
32 | * An array of extensions discovered on the inspected source site.
33 | */
34 | abstract protected function readExtensions(): array;
35 | }
36 |
--------------------------------------------------------------------------------
/src/Command/App/LinkCommand.php:
--------------------------------------------------------------------------------
1 | acceptApplicationUuid();
21 | }
22 |
23 | protected function execute(InputInterface $input, OutputInterface $output): int
24 | {
25 | $this->validateCwdIsValidDrupalProject();
26 | if ($cloudApplicationUuid = $this->getCloudUuidFromDatastore()) {
27 | $cloudApplication = $this->getCloudApplication($cloudApplicationUuid);
28 | $output->writeln('This repository is already linked to Cloud application ' . $cloudApplication->name . '>. Run acli unlink> to unlink it.');
29 | return 1;
30 | }
31 | $this->determineCloudApplication(true);
32 |
33 | return Command::SUCCESS;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Command/App/TaskWaitCommand.php:
--------------------------------------------------------------------------------
1 | addArgument('notification-uuid', InputArgument::REQUIRED, 'The task notification UUID or Cloud Platform API response containing a linked notification')
23 | ->setHelp('Accepts either a notification UUID or Cloud Platform API response as JSON string. The JSON string must contain the _links->notification->href property.')
24 | ->addUsage('"$(acli api:environments:domain-clear-caches [environmentId] [domain])"');
25 | }
26 |
27 | protected function execute(InputInterface $input, OutputInterface $output): int
28 | {
29 | $notificationUuid = $input->getArgument('notification-uuid');
30 | $success = $this->waitForNotificationToComplete($this->cloudApiClientService->getClient(), $notificationUuid, "Waiting for task $notificationUuid to complete");
31 | return $success ? Command::SUCCESS : Command::FAILURE;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Command/App/UnlinkCommand.php:
--------------------------------------------------------------------------------
1 | validateCwdIsValidDrupalProject();
20 |
21 | $projectDir = $this->projectDir;
22 | if (!$this->getCloudUuidFromDatastore()) {
23 | throw new AcquiaCliException('There is no Cloud Platform application linked to {projectDir}', ['projectDir' => $projectDir]);
24 | }
25 |
26 | $application = $this->getCloudApplication($this->datastoreAcli->get('cloud_app_uuid'));
27 | $this->datastoreAcli->set('cloud_app_uuid', null);
28 | $output->writeln("Unlinked $projectDir> from Cloud application $application->name>");
29 |
30 | return Command::SUCCESS;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Command/Auth/AuthAcsfLogoutCommand.php:
--------------------------------------------------------------------------------
1 | datastoreCloud->get('acsf_factories');
19 | if (empty($factories)) {
20 | $this->io->error(['You are not logged into any factories.']);
21 | return Command::FAILURE;
22 | }
23 | foreach ($factories as $url => $factory) {
24 | $factories[$url]['url'] = $url;
25 | }
26 | $factory = $this->promptChooseFromObjectsOrArrays($factories, 'url', 'url', 'Choose a Factory to logout of');
27 | $factoryUrl = $factory['url'];
28 |
29 | /** @var \Acquia\Cli\AcsfApi\AcsfCredentials $cloudCredentials */
30 | $cloudCredentials = $this->cloudCredentials;
31 | $activeUser = $cloudCredentials->getFactoryActiveUser($factory);
32 | // @todo Only show factories the user is logged into.
33 | if (!$activeUser) {
34 | $this->io->error("You're already logged out of $factoryUrl");
35 | return 1;
36 | }
37 | $answer = $this->io->confirm("Are you sure you'd like to logout the user {$activeUser['username']} from $factoryUrl?");
38 | if (!$answer) {
39 | return Command::SUCCESS;
40 | }
41 | $factories[$factoryUrl]['active_user'] = null;
42 | $this->datastoreCloud->set('acsf_factories', $factories);
43 | $this->datastoreCloud->remove('acsf_active_factory');
44 |
45 | $output->writeln("Logged {$activeUser['username']} out of $factoryUrl");
46 |
47 | return Command::SUCCESS;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Command/Auth/AuthLogoutCommand.php:
--------------------------------------------------------------------------------
1 | addOption('delete', null, InputOption::VALUE_NEGATABLE, 'Delete the active Cloud Platform API credentials');
21 | }
22 |
23 | protected function execute(InputInterface $input, OutputInterface $output): int
24 | {
25 | $keys = $this->datastoreCloud->get('keys');
26 | $activeKey = $this->datastoreCloud->get('acli_key');
27 | if (!$activeKey) {
28 | throw new AcquiaCliException('There is no active Cloud Platform API key');
29 | }
30 | $activeKeyLabel = $keys[$activeKey]['label'];
31 | $output->writeln("The key $activeKeyLabel> will be deactivated on this machine. However, the credentials will remain on disk and can be reactivated by running acli auth:login> unless you also choose to delete them.");
32 | $delete = $this->determineOption('delete', false, null, null, false);
33 | $this->datastoreCloud->remove('acli_key');
34 | $action = 'deactivated';
35 | if ($delete) {
36 | $this->datastoreCloud->remove("keys.$activeKey");
37 | $action = 'deleted';
38 | }
39 | $output->writeln("The active Cloud Platform API credentials were $action");
40 | $output->writeln('No Cloud Platform API key is active. Run acli auth:login> to continue using the Cloud Platform API.');
41 |
42 | return Command::SUCCESS;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Command/CodeStudio/cs_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/acquia/cli/29236b4ec1b1e81e16fb19373e9641556dfdf76d/src/Command/CodeStudio/cs_icon.png
--------------------------------------------------------------------------------
/src/Command/CodeStudio/drupal_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/acquia/cli/29236b4ec1b1e81e16fb19373e9641556dfdf76d/src/Command/CodeStudio/drupal_icon.png
--------------------------------------------------------------------------------
/src/Command/Env/EnvDeleteCommand.php:
--------------------------------------------------------------------------------
1 | acceptEnvironmentId();
24 | }
25 |
26 | protected function execute(InputInterface $input, OutputInterface $output): int
27 | {
28 | $this->output = $output;
29 | $cloudAppUuid = $this->determineCloudApplication(true);
30 | $acquiaCloudClient = $this->cloudApiClientService->getClient();
31 | $environmentsResource = new Environments($acquiaCloudClient);
32 | $environment = $this->determineEnvironmentCde($environmentsResource, $cloudAppUuid);
33 | $environmentsResource->delete($environment->uuid);
34 |
35 | $this->io->success([
36 | "The $environment->label environment is being deleted",
37 | ]);
38 |
39 | return Command::SUCCESS;
40 | }
41 |
42 | private function determineEnvironmentCde(Environments $environmentsResource, string $cloudAppUuid): EnvironmentResponse
43 | {
44 | if ($this->input->getArgument('environmentId')) {
45 | // @todo Validate.
46 | $environmentId = $this->input->getArgument('environmentId');
47 | return $environmentsResource->get($environmentId);
48 | }
49 | $environments = $environmentsResource->getAll($cloudAppUuid);
50 | $cdes = [];
51 | foreach ($environments as $environment) {
52 | if ($environment->flags->cde) {
53 | $cdes[] = $environment;
54 | }
55 | }
56 | if (!$cdes) {
57 | throw new AcquiaCliException('There are no existing CDEs for Application ' . $cloudAppUuid);
58 | }
59 | return $this->promptChooseFromObjectsOrArrays($cdes, 'uuid', 'label', "Which Continuous Delivery Environment (CDE) do you want to delete?");
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Command/HelloWorldCommand.php:
--------------------------------------------------------------------------------
1 | io->success('Hello world!');
18 |
19 | return Command::SUCCESS;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Command/Ide/IdeDeleteCommand.php:
--------------------------------------------------------------------------------
1 | acceptApplicationUuid();
25 | // @todo make this an argument
26 | $this->addOption('uuid', null, InputOption::VALUE_OPTIONAL, 'UUID of the IDE to delete');
27 | }
28 |
29 | protected function execute(InputInterface $input, OutputInterface $output): int
30 | {
31 | $acquiaCloudClient = $this->cloudApiClientService->getClient();
32 | $idesResource = new Ides($acquiaCloudClient);
33 |
34 | $ideUuid = $input->getOption('uuid');
35 | if ($ideUuid) {
36 | $ide = $idesResource->get($ideUuid);
37 | } else {
38 | $cloudApplicationUuid = $this->determineCloudApplication();
39 | $ide = $this->promptIdeChoice("Select the IDE you'd like to delete:", $idesResource, $cloudApplicationUuid);
40 | $answer = $this->io->confirm("Are you sure you want to delete $ide->label>");
41 | if (!$answer) {
42 | $this->io->writeln('Ok, never mind.');
43 | return Command::FAILURE;
44 | }
45 | }
46 | $response = $idesResource->delete($ide->uuid);
47 | $this->io->writeln($response->message);
48 |
49 | // Check to see if an SSH key for this IDE exists on Cloud.
50 | $cloudKey = $this->findIdeSshKeyOnCloud($ide->label, $ide->uuid);
51 | if ($cloudKey) {
52 | $answer = $this->io->confirm('Would you like to delete the SSH key associated with this IDE from your Cloud Platform account?');
53 | if ($answer) {
54 | $this->deleteSshKeyFromCloud($output, $cloudKey);
55 | }
56 | }
57 |
58 | return Command::SUCCESS;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Command/Ide/IdeInfoCommand.php:
--------------------------------------------------------------------------------
1 | acceptApplicationUuid();
22 | }
23 |
24 | protected function execute(InputInterface $input, OutputInterface $output): int
25 | {
26 | $applicationUuid = $this->determineCloudApplication();
27 |
28 | $acquiaCloudClient = $this->cloudApiClientService->getClient();
29 | $idesResource = new Ides($acquiaCloudClient);
30 |
31 | $ide = $this->promptIdeChoice("Select an IDE to get more information:", $idesResource, $applicationUuid);
32 | $response = $idesResource->get($ide->uuid);
33 | $this->io->definitionList(
34 | ['IDE property' => 'IDE value'],
35 | new TableSeparator(),
36 | ['UUID' => $response->uuid],
37 | ['Label' => $response->label],
38 | ['Owner name' => $response->owner->first_name . ' ' . $response->owner->last_name],
39 | ['Owner username' => $response->owner->username],
40 | ['Owner email' => $response->owner->mail],
41 | ['Cloud application' => $response->links->application->href],
42 | ['IDE URL' => $response->links->ide->href],
43 | ['Web URL' => $response->links->web->href]
44 | );
45 |
46 | return Command::SUCCESS;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Command/Ide/IdeListCommand.php:
--------------------------------------------------------------------------------
1 | acceptApplicationUuid();
23 | }
24 |
25 | protected function execute(InputInterface $input, OutputInterface $output): int
26 | {
27 | $applicationUuid = $this->determineCloudApplication();
28 |
29 | $acquiaCloudClient = $this->cloudApiClientService->getClient();
30 | $idesResource = new Ides($acquiaCloudClient);
31 | $applicationIdes = $idesResource->getAll($applicationUuid);
32 |
33 | if ($applicationIdes->count()) {
34 | $table = new Table($output);
35 | $table->setStyle('borderless');
36 | $table->setHeaders(['IDEs']);
37 | foreach ($applicationIdes as $ide) {
38 | $table->addRows([
39 | ["$ide->label ({$ide->owner->mail})"],
40 | ["IDE URL: links->ide->href}>{$ide->links->ide->href}>"],
41 | ["Web URL: links->web->href}>{$ide->links->web->href}>"],
42 | new TableSeparator(),
43 | ]);
44 | }
45 | $table->render();
46 | } else {
47 | $output->writeln('No IDE exists for this application.');
48 | }
49 |
50 | return Command::SUCCESS;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Command/Ide/IdeListMineCommand.php:
--------------------------------------------------------------------------------
1 | cloudApiClientService->getClient();
24 | $ides = new Ides($acquiaCloudClient);
25 | $accountIdes = $ides->getMine();
26 | $applicationResource = new Applications($acquiaCloudClient);
27 |
28 | if (count($accountIdes)) {
29 | $table = new Table($output);
30 | $table->setStyle('borderless');
31 | $table->setHeaders(['IDEs']);
32 | foreach ($accountIdes as $ide) {
33 | $appUrlParts = explode('/', $ide->links->application->href);
34 | $appUuid = end($appUrlParts);
35 | $application = $applicationResource->get($appUuid);
36 | $applicationUrl = str_replace('/api', '/a', $application->links->self->href);
37 |
38 | $table->addRows([
39 | ["$ide->label"],
40 | ["UUID: $ide->uuid"],
41 | ["Application: $application->name>"],
42 | ["Subscription: {$application->subscription->name}"],
43 | ["IDE URL: links->ide->href}>{$ide->links->ide->href}>"],
44 | ["Web URL: links->web->href}>{$ide->links->web->href}>"],
45 | new TableSeparator(),
46 | ]);
47 | }
48 | $table->render();
49 | } else {
50 | $output->writeln('No IDE exists for your account.');
51 | }
52 |
53 | return Command::SUCCESS;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Command/Ide/IdeOpenCommand.php:
--------------------------------------------------------------------------------
1 | setHidden(AcquiaDrupalEnvironmentDetector::isAhIdeEnv());
23 | $this->acceptApplicationUuid();
24 | // @todo Add option to accept an ide UUID.
25 | }
26 |
27 | protected function execute(InputInterface $input, OutputInterface $output): int
28 | {
29 | $acquiaCloudClient = $this->cloudApiClientService->getClient();
30 | $cloudApplicationUuid = $this->determineCloudApplication();
31 | $idesResource = new Ides($acquiaCloudClient);
32 | $ide = $this->promptIdeChoice("Select the IDE you'd like to open:", $idesResource, $cloudApplicationUuid);
33 |
34 | $this->output->writeln('');
35 | $this->output->writeln("Your IDE URL: links->ide->href}>{$ide->links->ide->href}>");
36 | $this->output->writeln("Your Drupal Site URL: links->web->href}>{$ide->links->web->href}>");
37 | $this->output->writeln('Opening your IDE in browser...');
38 |
39 | $this->localMachineHelper->startBrowser($ide->links->ide->href);
40 |
41 | return Command::SUCCESS;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Command/Ide/IdePhpVersionCommand.php:
--------------------------------------------------------------------------------
1 | addArgument('version', InputArgument::REQUIRED, 'The PHP version')
24 | ->setHidden(!AcquiaDrupalEnvironmentDetector::isAhIdeEnv());
25 | }
26 |
27 | protected function execute(InputInterface $input, OutputInterface $output): int
28 | {
29 | $this->requireCloudIdeEnvironment();
30 | $version = $input->getArgument('version');
31 | $this->validatePhpVersion($version);
32 | $this->localMachineHelper->getFilesystem()
33 | ->dumpFile($this->getIdePhpVersionFilePath(), $version);
34 | $this->restartService('php-fpm');
35 |
36 | return Command::SUCCESS;
37 | }
38 |
39 | private function getIdePhpFilePathPrefix(): string
40 | {
41 | if (!isset($this->idePhpFilePathPrefix)) {
42 | $this->idePhpFilePathPrefix = '/usr/local/php';
43 | }
44 | return $this->idePhpFilePathPrefix;
45 | }
46 |
47 | public function setIdePhpFilePathPrefix(string $path): void
48 | {
49 | $this->idePhpFilePathPrefix = $path;
50 | }
51 |
52 | protected function validatePhpVersion(string $version): string
53 | {
54 | parent::validatePhpVersion($version);
55 | $phpFilepath = $this->getIdePhpFilePathPrefix() . $version;
56 | if (!$this->localMachineHelper->getFilesystem()->exists($phpFilepath)) {
57 | throw new AcquiaCliException('The specified PHP version does not exist on this machine.');
58 | }
59 |
60 | return $version;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Command/Ide/IdeShareCommand.php:
--------------------------------------------------------------------------------
1 |
21 | */
22 | private array $shareCodeFilepaths;
23 |
24 | protected function configure(): void
25 | {
26 | $this
27 | ->addOption('regenerate', '', InputOption::VALUE_NONE, 'regenerate the share code')
28 | ->setHidden(!AcquiaDrupalEnvironmentDetector::isAhIdeEnv());
29 | }
30 |
31 | protected function execute(InputInterface $input, OutputInterface $output): int
32 | {
33 | $this->requireCloudIdeEnvironment();
34 |
35 | if ($input->getOption('regenerate')) {
36 | $this->regenerateShareCode();
37 | }
38 |
39 | $shareUuid = $this->localMachineHelper->readFile($this->getShareCodeFilepaths()[0]);
40 | $webUrl = self::getThisCloudIdeWebUrl();
41 |
42 | $this->output->writeln('');
43 | $this->output->writeln("Your IDE Share URL: https://$webUrl?share=$shareUuid>");
44 |
45 | return Command::SUCCESS;
46 | }
47 |
48 | public function setShareCodeFilepaths(array $filePath): void
49 | {
50 | $this->shareCodeFilepaths = $filePath;
51 | }
52 |
53 | /**
54 | * @return array
55 | */
56 | private function getShareCodeFilepaths(): array
57 | {
58 | if (!isset($this->shareCodeFilepaths)) {
59 | $this->shareCodeFilepaths = [
60 | '/usr/local/share/ide/.sharecode',
61 | '/home/ide/.sharecode',
62 | ];
63 | }
64 | return $this->shareCodeFilepaths;
65 | }
66 |
67 | private function regenerateShareCode(): void
68 | {
69 | $newShareCode = (string) Uuid::uuid4();
70 | foreach ($this->getShareCodeFilepaths() as $path) {
71 | $this->localMachineHelper->writeFile($path, $newShareCode);
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Command/Ide/Wizard/IdeWizardCommandBase.php:
--------------------------------------------------------------------------------
1 | setSshKeyFilepath(self::getSshKeyFilename($this::getThisCloudIdeUuid()));
29 | $this->passphraseFilepath = $this->localMachineHelper->getLocalFilepath('~/.passphrase');
30 | }
31 |
32 | public static function getSshKeyFilename(mixed $ideUuid): string
33 | {
34 | return 'id_rsa_acquia_ide_' . $ideUuid;
35 | }
36 |
37 | protected function validateEnvironment(): void
38 | {
39 | $this->requireCloudIdeEnvironment();
40 | }
41 |
42 | protected function getSshKeyLabel(): string
43 | {
44 | return $this::getIdeSshKeyLabel(self::getThisCloudIdeLabel(), self::getThisCloudIdeUuid());
45 | }
46 |
47 | protected function deleteThisSshKeyFromCloud(mixed $output): void
48 | {
49 | if ($cloudKey = $this->findIdeSshKeyOnCloud($this::getThisCloudIdeLabel(), $this::getThisCloudIdeUuid())) {
50 | $this->deleteSshKeyFromCloud($output, $cloudKey);
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Command/Ide/Wizard/IdeWizardDeleteSshKeyCommand.php:
--------------------------------------------------------------------------------
1 | setHidden(!CommandBase::isAcquiaCloudIde());
26 | }
27 |
28 | protected function execute(InputInterface $input, OutputInterface $output): int
29 | {
30 | $this->requireCloudIdeEnvironment();
31 |
32 | $cloudKey = $this->findIdeSshKeyOnCloud($this::getThisCloudIdeLabel(), $this::getThisCloudIdeUuid());
33 | if (!$cloudKey) {
34 | throw new AcquiaCliException('Could not find an SSH key on the Cloud Platform matching any local key in this IDE.');
35 | }
36 |
37 | $this->deleteSshKeyFromCloud($output, $cloudKey);
38 | $this->deleteLocalSshKey();
39 |
40 | $this->output->writeln("Deleted local files $this->publicSshKeyFilepath> and $this->privateSshKeyFilepath>");
41 |
42 | return Command::SUCCESS;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Command/Pull/PullCodeCommand.php:
--------------------------------------------------------------------------------
1 | acceptEnvironmentId()
25 | ->addOption('dir', null, InputArgument::OPTIONAL, 'The directory containing the Drupal project to be refreshed')
26 | ->addOption(
27 | 'no-scripts',
28 | null,
29 | InputOption::VALUE_NONE,
30 | 'Do not run any additional scripts after code is pulled. E.g., composer install , drush cache-rebuild, etc.'
31 | );
32 | }
33 |
34 | protected function execute(InputInterface $input, OutputInterface $output): int
35 | {
36 | $this->setDirAndRequireProjectCwd($input);
37 | $clone = $this->determineCloneProject($output);
38 | $sourceEnvironment = $this->determineEnvironment($input, $output, true);
39 | $this->pullCode($input, $output, $clone, $sourceEnvironment);
40 | $this->checkEnvironmentPhpVersions($sourceEnvironment);
41 | $this->matchIdePhpVersion($output, $sourceEnvironment);
42 | if (!$input->getOption('no-scripts')) {
43 | $outputCallback = $this->getOutputCallback($output, $this->checklist);
44 | $this->runComposerScripts($outputCallback, $this->checklist);
45 | $this->runDrushCacheClear($outputCallback, $this->checklist);
46 | }
47 |
48 | return Command::SUCCESS;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Command/Pull/PullFilesCommand.php:
--------------------------------------------------------------------------------
1 | acceptEnvironmentId()
21 | ->acceptSite();
22 | }
23 |
24 | protected function execute(InputInterface $input, OutputInterface $output): int
25 | {
26 | $this->setDirAndRequireProjectCwd($input);
27 | $sourceEnvironment = $this->determineEnvironment($input, $output, true);
28 | $this->pullFiles($input, $output, $sourceEnvironment);
29 |
30 | return Command::SUCCESS;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Command/Pull/PullScriptsCommand.php:
--------------------------------------------------------------------------------
1 | acceptEnvironmentId()
24 | ->addOption('dir', null, InputArgument::OPTIONAL, 'The directory containing the Drupal project to be refreshed');
25 | }
26 |
27 | protected function initialize(InputInterface $input, OutputInterface $output): void
28 | {
29 | parent::initialize($input, $output);
30 | $this->checklist = new Checklist($output);
31 | }
32 |
33 | protected function execute(InputInterface $input, OutputInterface $output): int
34 | {
35 | $this->setDirAndRequireProjectCwd($input);
36 | $this->executeAllScripts($this->getOutputCallback($output, $this->checklist), $this->checklist);
37 |
38 | return Command::SUCCESS;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Command/Push/PushCodeCommand.php:
--------------------------------------------------------------------------------
1 | setHidden(!AcquiaDrupalEnvironmentDetector::isAhIdeEnv() && !self::isLandoEnv());
22 | }
23 |
24 | protected function execute(InputInterface $input, OutputInterface $output): int
25 | {
26 | $output->writeln("Use git> to push code changes upstream.");
27 |
28 | return Command::SUCCESS;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Command/Push/PushCommandBase.php:
--------------------------------------------------------------------------------
1 | acceptEnvironmentId()
23 | ->acceptSite();
24 | }
25 |
26 | protected function execute(InputInterface $input, OutputInterface $output): int
27 | {
28 | $this->setDirAndRequireProjectCwd($input);
29 | $destinationEnvironment = $this->determineEnvironment($input, $output);
30 | $chosenSite = $input->getArgument('site');
31 | if (!$chosenSite) {
32 | $chosenSite = $this->promptChooseDrupalSite($destinationEnvironment);
33 | }
34 | $answer = $this->io->confirm("Overwrite the public files directory on $destinationEnvironment->name> with a copy of the files from the current machine?");
35 | if (!$answer) {
36 | return Command::SUCCESS;
37 | }
38 |
39 | $this->checklist = new Checklist($output);
40 | $this->checklist->addItem('Pushing public files directory to remote machine');
41 | $this->rsyncFilesToCloud($destinationEnvironment, $this->getOutputCallback($output, $this->checklist), $chosenSite);
42 | $this->checklist->completePreviousItem();
43 |
44 | return Command::SUCCESS;
45 | }
46 |
47 | private function rsyncFilesToCloud(EnvironmentResponse $chosenEnvironment, ?callable $outputCallback = null, ?string $site = null): void
48 | {
49 | $sourceDir = $this->getLocalFilesDir($site);
50 | $destinationDir = $chosenEnvironment->sshUrl . ':' . $this->getCloudFilesDir($chosenEnvironment, $site);
51 |
52 | $this->rsyncFiles($sourceDir, $destinationDir, $outputCallback);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Command/Remote/DrushCommand.php:
--------------------------------------------------------------------------------
1 | setHelp('Pay close attention to the argument syntax! Note the usage of --> to separate the drush command arguments and options.>')
27 | ->acceptEnvironmentId()
28 | ->addArgument('drush_command', InputArgument::IS_ARRAY, 'Drush command')
29 | ->addUsage('. -- ')
30 | ->addUsage('myapp.dev -- uli 1')
31 | ->addUsage('myapp.dev -- status --fields=db-status');
32 | }
33 |
34 | /**
35 | * @throws \Acquia\Cli\Exception\AcquiaCliException
36 | */
37 | protected function execute(InputInterface $input, OutputInterface $output): ?int
38 | {
39 | $environment = $this->determineEnvironment($input, $output, true);
40 | $alias = self::getEnvironmentAlias($environment);
41 | $acliArguments = $input->getArguments();
42 | $drushArguments = (array) $acliArguments['drush_command'];
43 | // When available, provide the default domain to drush.
44 | if (!empty($environment->default_domain)) {
45 | // Insert at the beginning so a user-supplied --uri arg will override.
46 | array_unshift($drushArguments, "--uri=http://$environment->default_domain");
47 | }
48 | $drushCommandArguments = [
49 | "cd /var/www/html/$alias/docroot; ",
50 | 'drush',
51 | implode(' ', $drushArguments),
52 | ];
53 |
54 | return $this->sshHelper->executeCommand($environment->sshUrl, $drushCommandArguments)
55 | ->getExitCode();
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Command/Remote/SshBaseCommand.php:
--------------------------------------------------------------------------------
1 | addArgument('alias', InputArgument::REQUIRED, 'Alias for application & environment in the format `app-name.env`')
25 | ->addArgument('ssh_command', InputArgument::IS_ARRAY, 'Command to run via SSH (if not provided, opens a shell in the site directory)')
26 | ->addUsage("myapp.dev # open a shell in the myapp.dev environment")
27 | ->addUsage("myapp.dev -- ls -al # list files in the myapp.dev environment and return");
28 | }
29 |
30 | protected function execute(InputInterface $input, OutputInterface $output): ?int
31 | {
32 | $alias = $input->getArgument('alias');
33 | $alias = $this->normalizeAlias($alias);
34 | $alias = self::validateEnvironmentAlias($alias);
35 | $environment = $this->getEnvironmentFromAliasArg($alias);
36 | if (!isset($environment->sshUrl)) {
37 | throw new AcquiaCliException('Cannot determine environment SSH URL. Check that you have SSH permissions on this environment.');
38 | }
39 | $sshCommand = [
40 | 'cd /var/www/html/' . $alias,
41 | ];
42 | $arguments = $input->getArguments();
43 | if (empty($arguments['ssh_command'])) {
44 | $sshCommand[] = 'exec $SHELL -l';
45 | } else {
46 | $sshCommand[] = implode(' ', $arguments['ssh_command']);
47 | }
48 | $sshCommand = (array) implode('; ', $sshCommand);
49 | return $this->sshHelper->executeCommand($environment->sshUrl, $sshCommand)
50 | ->getExitCode();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Command/Self/ClearCacheCommand.php:
--------------------------------------------------------------------------------
1 | writeln('Acquia CLI caches were cleared.');
25 |
26 | return Command::SUCCESS;
27 | }
28 |
29 | /**
30 | * Clear caches.
31 | */
32 | public static function clearCaches(): void
33 | {
34 | $cache = self::getAliasCache();
35 | $cache->clear();
36 | $systemCacheDir = Path::join(sys_get_temp_dir(), 'symphony-cache');
37 | $fs = new Filesystem();
38 | $fs->remove([$systemCacheDir]);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Command/Self/SelfInfoCommand.php:
--------------------------------------------------------------------------------
1 | createTable($output, 'Acquia CLI information', ['Property', 'Value']);
21 | $table->addRow(['Version', $this->getApplication()->getVersion()]);
22 | $table->addRow(['Cloud datastore', $this->datastoreCloud->filepath]);
23 | $table->addRow(['ACLI datastore', $this->datastoreAcli->filepath]);
24 | $table->addRow(['Telemetry enabled', var_export($this->telemetryHelper->telemetryEnabled(), true)]);
25 | $table->addRow(['User ID', $this->telemetryHelper->getUserId()]);
26 | foreach ($this->telemetryHelper->getTelemetryUserData() as $key => $value) {
27 | $table->addRow([$key, $value]);
28 | }
29 | $table->render();
30 | return Command::SUCCESS;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Command/Self/TelemetryCommand.php:
--------------------------------------------------------------------------------
1 | datastoreCloud;
20 | if ($datastore->get(DataStoreContract::SEND_TELEMETRY)) {
21 | $datastore->set(DataStoreContract::SEND_TELEMETRY, false);
22 | $this->io->success('Telemetry has been disabled.');
23 | } else {
24 | $datastore->set(DataStoreContract::SEND_TELEMETRY, true);
25 | $this->io->success('Telemetry has been enabled.');
26 | }
27 | $oppositeVerb = $datastore->get(DataStoreContract::SEND_TELEMETRY) ? 'disable' : 'enable';
28 | $this->io->writeln("Run this command again to $oppositeVerb telemetry");
29 |
30 | return Command::SUCCESS;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Command/Self/TelemetryDisableCommand.php:
--------------------------------------------------------------------------------
1 | datastoreCloud;
20 | $datastore->set(DataStoreContract::SEND_TELEMETRY, false);
21 | $this->io->success('Telemetry has been disabled.');
22 |
23 | return Command::SUCCESS;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Command/Self/TelemetryEnableCommand.php:
--------------------------------------------------------------------------------
1 | datastoreCloud;
20 | $datastore->set(DataStoreContract::SEND_TELEMETRY, true);
21 | $this->io->success('Telemetry has been enabled.');
22 |
23 | return Command::SUCCESS;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Command/Ssh/SshKeyCreateCommand.php:
--------------------------------------------------------------------------------
1 | addOption('filename', null, InputOption::VALUE_REQUIRED, 'The filename of the SSH key')
22 | ->addOption('password', null, InputOption::VALUE_REQUIRED, 'The password for the SSH key');
23 | }
24 |
25 | protected function execute(InputInterface $input, OutputInterface $output): int
26 | {
27 | $filename = $this->determineFilename();
28 | $password = $this->determinePassword();
29 | $this->createSshKey($filename, $password);
30 | $output->writeln('Created new SSH key. ' . $this->publicSshKeyFilepath);
31 |
32 | return Command::SUCCESS;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Command/Ssh/SshKeyCreateUploadCommand.php:
--------------------------------------------------------------------------------
1 | addOption('filename', null, InputOption::VALUE_REQUIRED, 'The filename of the SSH key')
22 | ->addOption('password', null, InputOption::VALUE_REQUIRED, 'The password for the SSH key')
23 | ->addOption('label', null, InputOption::VALUE_REQUIRED, 'The SSH key label to be used with the Cloud Platform')
24 | ->addOption('no-wait', null, InputOption::VALUE_NONE, "Don't wait for the SSH key to be uploaded to the Cloud Platform");
25 | }
26 |
27 | protected function execute(InputInterface $input, OutputInterface $output): int
28 | {
29 | $filename = $this->determineFilename();
30 | $password = $this->determinePassword();
31 | $this->createSshKey($filename, $password);
32 | $publicKey = $this->localMachineHelper->readFile($this->publicSshKeyFilepath);
33 | $chosenLocalKey = basename($this->privateSshKeyFilepath);
34 | $label = $this->determineSshKeyLabel();
35 | $this->uploadSshKey($label, $publicKey);
36 | $this->io->success("Uploaded $chosenLocalKey to the Cloud Platform with label $label");
37 |
38 | return Command::SUCCESS;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Command/Ssh/SshKeyDeleteCommand.php:
--------------------------------------------------------------------------------
1 | addOption('cloud-key-uuid', 'uuid', InputOption::VALUE_REQUIRED);
24 | }
25 |
26 | protected function execute(InputInterface $input, OutputInterface $output): int
27 | {
28 | return $this->deleteSshKeyFromCloud($output, $input->getOption('cloud-key-uuid'));
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Command/Ssh/SshKeyUploadCommand.php:
--------------------------------------------------------------------------------
1 | addOption('filepath', null, InputOption::VALUE_REQUIRED, 'The filepath of the public SSH key to upload')
22 | ->addOption('label', null, InputOption::VALUE_REQUIRED, 'The SSH key label to be used with the Cloud Platform')
23 | ->addOption('no-wait', null, InputOption::VALUE_NONE, "Don't wait for the SSH key to be uploaded to the Cloud Platform");
24 | }
25 |
26 | protected function execute(InputInterface $input, OutputInterface $output): int
27 | {
28 | [$chosenLocalKey, $publicKey] = $this->determinePublicSshKey();
29 | $label = $this->determineSshKeyLabel();
30 | $this->uploadSshKey($label, $publicKey);
31 | $this->io->success("Uploaded $chosenLocalKey to the Cloud Platform with label $label");
32 |
33 | return Command::SUCCESS;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/CommandFactoryInterface.php:
--------------------------------------------------------------------------------
1 | getRootNode()
22 | ->children()
23 | ->scalarNode('cloud_app_uuid')->end()
24 | ->arrayNode('push')
25 | ->children()
26 | ->arrayNode('artifact')
27 | ->children()
28 | ->arrayNode('destination_git_urls')
29 | ->scalarPrototype()->end()
30 | ->end()
31 | ->end()
32 | ->end()
33 | ->end()
34 | ->end()
35 | ->end();
36 | return $treeBuilder;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/ConnectorFactoryInterface.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | protected array $config;
16 |
17 | public function __construct(
18 | protected LocalMachineHelper $localMachineHelper,
19 | AcquiaCliConfig $configDefinition,
20 | string $acliConfigFilepath
21 | ) {
22 | $filePath = $localMachineHelper->getLocalFilepath($acliConfigFilepath);
23 | parent::__construct($filePath, $configDefinition);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/DataStore/CloudDataStore.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | protected array $config;
16 |
17 | public function __construct(
18 | protected LocalMachineHelper $localMachineHelper,
19 | CloudDataConfig $cloudDataConfig,
20 | string $cloudConfigFilepath
21 | ) {
22 | parent::__construct($cloudConfigFilepath, $cloudDataConfig);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/DataStore/DataStoreInterface.php:
--------------------------------------------------------------------------------
1 | fileSystem = new Filesystem();
30 | $this->filepath = $path;
31 | $this->expander = new Expander();
32 | $this->expander->setStringifier(new Stringifier());
33 | $this->data = new Data();
34 | }
35 |
36 | public function set(string $key, mixed $value): void
37 | {
38 | $this->data->set($key, $value);
39 | $this->dump();
40 | }
41 |
42 | public function get(string $key): mixed
43 | {
44 | try {
45 | return $this->data->get($key);
46 | } catch (MissingPathException) {
47 | return null;
48 | }
49 | }
50 |
51 | public function remove(string $key): void
52 | {
53 | $this->data->remove($key);
54 | $this->dump();
55 | }
56 |
57 | public function exists(string $key): bool
58 | {
59 | return $this->data->has($key);
60 | }
61 |
62 | /**
63 | * @param string $path Path to the datastore on disk.
64 | * @return array
65 | */
66 | protected function processConfig(array $config, ConfigurationInterface $definition, string $path): array
67 | {
68 | try {
69 | return (new Processor())->processConfiguration(
70 | $definition,
71 | [$definition->getName() => $config],
72 | );
73 | } catch (InvalidConfigurationException $e) {
74 | throw new AcquiaCliException(
75 | 'Configuration file at the following path contains invalid keys: {path} {error}',
76 | ['path' => $path, 'error' => $e->getMessage()]
77 | );
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/DataStore/JsonDataStore.php:
--------------------------------------------------------------------------------
1 | fileSystem->exists($path)) {
18 | $array = json_decode(file_get_contents($path), true, 512, JSON_THROW_ON_ERROR);
19 | $array = $this->expander->expandArrayProperties($array);
20 | $cleaned = $this->cleanLegacyConfig($array);
21 |
22 | if ($configDefinition) {
23 | $array = $this->processConfig($array, $configDefinition, $path);
24 | }
25 | $this->data->import($array);
26 |
27 | // Dump the new values to disk.
28 | if ($cleaned) {
29 | $this->dump();
30 | }
31 | }
32 | }
33 |
34 | public function dump(): void
35 | {
36 | $this->fileSystem->dumpFile($this->filepath, json_encode($this->data->export(), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT));
37 | }
38 |
39 | protected function cleanLegacyConfig(array &$array): bool
40 | {
41 | // Legacy format of credential storage.
42 | $dump = false;
43 | if (array_key_exists('key', $array) || array_key_exists('secret', $array)) {
44 | unset($array['key'], $array['secret']);
45 | $dump = true;
46 | }
47 | return $dump;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/DataStore/YamlStore.php:
--------------------------------------------------------------------------------
1 | fileSystem->exists($path)) {
19 | $array = Yaml::parseFile($path);
20 | $array = $this->expander->expandArrayProperties($array);
21 | if ($configDefinition) {
22 | $array = $this->processConfig($array, $configDefinition, $path);
23 | }
24 | $this->data->import($array);
25 | }
26 | }
27 |
28 | public function dump(): void
29 | {
30 | $this->fileSystem->dumpFile($this->filepath, Yaml::dump($this->data->export()));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Exception/AcquiaCliException.php:
--------------------------------------------------------------------------------
1 | $code,
25 | 'message' => $rawMessage,
26 | ];
27 | Amplitude::getInstance()
28 | ->queueEvent('Threw exception', $eventProperties);
29 |
30 | parent::__construct($this->interpolateString($rawMessage, $replacements), $code);
31 | }
32 |
33 | /**
34 | * Returns the replacements context array.
35 | *
36 | * @return string $this->replacements
37 | */
38 | public function getRawMessage(): string
39 | {
40 | return $this->rawMessage;
41 | }
42 |
43 | /**
44 | * Replace the variables into the message string.
45 | *
46 | * @param string $message The raw, uninterpolated message string.
47 | * @param array $replacements The values to replace into the message.
48 | */
49 | protected function interpolateString(string $message, array $replacements): string
50 | {
51 | $tr = [];
52 | foreach ($replacements as $key => $val) {
53 | $tr['{' . $key . '}'] = $val;
54 | }
55 |
56 | return strtr($message, $tr);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Helpers/AliasCache.php:
--------------------------------------------------------------------------------
1 | localMachineHelper->readFile($this->getIdePhpVersionFilePath()));
17 | } catch (FilesystemException) {
18 | return null;
19 | }
20 | }
21 |
22 | public function setPhpVersionFilePath(string $path): void
23 | {
24 | $this->phpVersionFilePath = $path;
25 | }
26 |
27 | protected function getIdePhpVersionFilePath(): string
28 | {
29 | if (!isset($this->phpVersionFilePath)) {
30 | $this->phpVersionFilePath = '/home/ide/configs/php/.version';
31 | }
32 | return $this->phpVersionFilePath;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Helpers/LoopHelper.php:
--------------------------------------------------------------------------------
1 | setMessage($spinnerMessage);
23 | $spinner->start();
24 |
25 | $cancelTimers = static function () use (&$timers, $spinner): void {
26 | // @infection-ignore-all
27 | array_map('\React\EventLoop\Loop::cancelTimer', $timers);
28 | $timers = [];
29 | $spinner->finish();
30 | };
31 | $periodicCallback = static function () use ($statusCallback, $doneCallback, $cancelTimers): void {
32 | // @infection-ignore-all
33 | if ($statusCallback()) {
34 | $cancelTimers();
35 | $doneCallback();
36 | }
37 | };
38 |
39 | // Spinner timer.
40 | $timers[] = Loop::addPeriodicTimer(
41 | $spinner->interval(),
42 | static function () use ($spinner): void {
43 | $spinner->advance();
44 | }
45 | );
46 |
47 | // Primary timer checking for result status.
48 | $timers[] = Loop::addPeriodicTimer(5, $periodicCallback);
49 | // Initial timer to speed up tests.
50 | $timers[] = Loop::addTimer(0.1, $periodicCallback);
51 |
52 | // Watchdog timer.
53 | $timers[] = Loop::addTimer(45 * 60, static function () use ($io, $doneCallback, $cancelTimers): void {
54 | $cancelTimers();
55 | $io->error("Timed out after 45 minutes!");
56 | $doneCallback();
57 | });
58 |
59 | // Manually run the loop. React EventLoop advises against this and suggests
60 | // using autorun instead, but I'm not sure how to pass the correct exit code
61 | // to Symfony if this isn't blocking.
62 | Loop::run();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/tests/fixtures/drupal7/drush_to_extensions_test_file_format.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | set -e
4 |
5 | jq_filter='to_entries | [.[] | {
6 | name: .key,
7 | humanName: (.value.name | capture("(?.+)(? \\(.*\\))") | .humanName),
8 | type: (if .value.type == "Module" then "module" else "theme" end),
9 | enabled: (.value.status == "Enabled"),
10 | version: .value.version,
11 | }] | sort_by(.name)'
12 |
13 | main () {
14 | which jq >/dev/null || jq_not_installed=true
15 | if test $jq_not_installed; then
16 | echo "You must have jq installed and available in your \$PATH. See https://stedolan.github.io/jq/."
17 | fi
18 | jq "$jq_filter" < /dev/stdin
19 | }
20 |
21 | main $@
22 |
--------------------------------------------------------------------------------
/tests/fixtures/drush-aliases/.acquia/cloudapi.conf:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/tests/fixtures/drush-aliases/README-acquiacloud.txt:
--------------------------------------------------------------------------------
1 | This folder contains two hidden folders named .acquia and .drush. Both of these folders should be placed in the $HOME directory on your system. The .acquia folder contains your Cloud Platform API credentials and the .drush folder contains your Drush aliases for the sites you currently have access to on the Cloud Platform.
2 |
3 | Refer to "About Drush on the Cloud Platform" (https://docs.acquia.com/acquia-cloud/manage/ssh/drush/) for more information."
4 |
--------------------------------------------------------------------------------
/tests/fixtures/drush-aliases/sites/devcloud2.site.yml:
--------------------------------------------------------------------------------
1 | # Application 'eemgrasmick', environment 'prod'.
2 | prod:
3 | root: /var/www/html/eemgrasmick.prod/docroot
4 | ac-site: eemgrasmick
5 | ac-env: prod
6 | ac-realm: prod
7 | uri: eemgrasmick.prod.acquia-sites.com
8 | prod.livedev:
9 | parent: '@eemgrasmick.prod'
10 | root: /mnt/gfs/eemgrasmick.prod/livedev/docroot
11 | host: eemgrasmick.ssh.prod.acquia-sites.com
12 | user: eemgrasmick.prod
13 | paths:
14 | drush-script: drush9
15 |
--------------------------------------------------------------------------------
/tests/fixtures/git_config:
--------------------------------------------------------------------------------
1 | [core]
2 | repositoryformatversion = 0
3 | filemode = true
4 | bare = false
5 | logallrefupdates = true
6 | ignorecase = true
7 | precomposeunicode = true
8 | [branch "master"]
9 | [remote "acquia"]
10 | url = site@svn-3.hosted.acquia-sites.com:site.git
11 | fetch = +refs/heads/*:refs/remotes/acquia/*
12 | [branch "develop"]
13 | remote = acquia
14 | merge = refs/heads/develop
15 |
--------------------------------------------------------------------------------
/tests/fixtures/multisite-config.json:
--------------------------------------------------------------------------------
1 | {"cloud":{"site":"profserv2","env":"01dev"},"memcache_inc":"profiles\/gardens\/modules\/acquia\/memcache\/memcache.inc",
2 | "sites": {
3 | "oracletest1.dev-profserv2.acsitefactory.com": {"name":"jxr5000596dev","flags":{"preferred_domain":true},"conf":{"gardens_site_id":5000596,"gardens_db_name":"jxr5000596dev","acsf_site_id":5000596,"acsf_db_name":"jxr5000596dev"}},
4 | "oracletest3.dev-profserv2.acsitefactory.com": {"name":"jxr5000606dev","flags":{"preferred_domain":true},"conf":{"gardens_site_id":5000606,"gardens_db_name":"jxr5000606dev","acsf_site_id":5000606,"acsf_db_name":"jxr5000606dev"}},
5 | "oracletest4.dev-profserv2.acsitefactory.com": {"name":"jxr5000611dev","flags":{"preferred_domain":true},"conf":{"gardens_site_id":5000611,"gardens_db_name":"jxr5000611dev","acsf_site_id":5000611,"acsf_db_name":"jxr5000611dev"}},
6 | "jeff1.dev-profserv2.acsitefactory.com": {"name":"jxr5000616dev","flags":{"preferred_domain":true},"conf":{"gardens_site_id":5000616,"gardens_db_name":"jxr5000616dev","acsf_site_id":5000616,"acsf_db_name":"jxr5000616dev"}},
7 | "stephen.dev-profserv2.acsitefactory.com": {"name":"jxr5000621dev","flags":{"preferred_domain":true},"conf":{"gardens_site_id":5000621,"gardens_db_name":"jxr5000621dev","acsf_site_id":5000621,"acsf_db_name":"jxr5000621dev"}},
8 | "oracletest5.dev-profserv2.acsitefactory.com": {"name":"jxr5000626dev","flags":{"preferred_domain":true},"conf":{"gardens_site_id":5000626,"gardens_db_name":"jxr5000626dev","acsf_site_id":5000626,"acsf_db_name":"jxr5000626dev"}},
9 | "oracletest2.dev-profserv2.acsitefactory.com": {"name":"jxr5000631dev","flags":{"preferred_domain":true},"conf":{"gardens_site_id":5000631,"gardens_db_name":"jxr5000631dev","acsf_site_id":5000631,"acsf_db_name":"jxr5000631dev"}}
10 | }}
11 |
--------------------------------------------------------------------------------
/tests/fixtures/no-multisite-config.json:
--------------------------------------------------------------------------------
1 | {"cloud":{"site":"profserv2","env":"01dev"},"memcache_inc":"profiles\/gardens\/modules\/acquia\/memcache\/memcache.inc",
2 | "sites": {
3 | }}
4 |
--------------------------------------------------------------------------------
/tests/fixtures/test.phar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/acquia/cli/29236b4ec1b1e81e16fb19373e9641556dfdf76d/tests/fixtures/test.phar
--------------------------------------------------------------------------------
/tests/fixtures/xdebug.ini:
--------------------------------------------------------------------------------
1 | ; Acquia Hosting XDebug defaults
2 | ; This file configures the default settings for xdebug.
3 | [xdebug]
4 | ;zend_extension=xdebug.so
5 | xdebug.remote_enable=1
6 | xdebug.remote_autostart=1
7 | xdebug.remote_port=9001
8 | xdebug.auto_trace=Off
9 | xdebug.default_enable=Off
10 | xdebug.max_nesting_level=2000
11 | xdebug.profiler_enable=Off
12 |
--------------------------------------------------------------------------------
/tests/integration/testcases/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/acquia/cli/29236b4ec1b1e81e16fb19373e9641556dfdf76d/tests/integration/testcases/__init__.py
--------------------------------------------------------------------------------
/tests/phpunit/src/AcsfApi/AcsfServiceTest.php:
--------------------------------------------------------------------------------
1 | cloudCredentials = new AcsfCredentials($this->datastoreCloud);
19 | }
20 |
21 | /**
22 | * @return array
23 | */
24 | public static function providerTestIsMachineAuthenticated(): array
25 | {
26 | return [
27 | [
28 | ['ACSF_USERNAME' => 'key', 'ACSF_KEY' => 'secret'],
29 | true,
30 | ],
31 | [
32 | ['ACSF_USERNAME' => 'key', 'ACSF_KEY' => 'secret'],
33 | true,
34 | ],
35 | [
36 | ['ACSF_USERNAME' => null, 'ACSF_KEY' => null],
37 | false,
38 | ],
39 | [
40 | ['ACSF_USERNAME' => 'key', 'ACSF_KEY' => null],
41 | false,
42 | ],
43 | ];
44 | }
45 |
46 | /**
47 | * @dataProvider providerTestIsMachineAuthenticated
48 | */
49 | public function testIsMachineAuthenticated(array $envVars, bool $isAuthenticated): void
50 | {
51 | self::setEnvVars($envVars);
52 | $clientService = new AcsfClientService(new AcsfConnectorFactory([
53 | 'key' => null,
54 | 'secret' => null,
55 | ]), $this->prophet->prophesize(Application::class)
56 | ->reveal(), $this->cloudCredentials);
57 | $this->assertEquals($isAuthenticated, $clientService->isMachineAuthenticated());
58 | self::unsetEnvVars($envVars);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/tests/phpunit/src/AcsfApi/EnvVarAcsfAuthenticationTest.php:
--------------------------------------------------------------------------------
1 | cloudCredentials = new AcsfCredentials($this->datastoreCloud);
18 | putenv('ACSF_USERNAME=' . self::$key);
19 | putenv('ACSF_KEY=' . self::$secret);
20 | putenv('ACSF_FACTORY_URI=' . self::$acsfCurrentFactoryUrl);
21 | }
22 |
23 | protected function tearDown(): void
24 | {
25 | parent::tearDown();
26 | putenv('ACSF_USERNAME');
27 | putenv('ACSF_KEY');
28 | }
29 |
30 | public function testKeyAndSecret(): void
31 | {
32 | $this->removeMockCloudConfigFile();
33 | self::assertEquals(self::$key, $this->cloudCredentials->getCloudKey());
34 | self::assertEquals(self::$secret, $this->cloudCredentials->getCloudSecret());
35 | self::assertEquals(self::$acsfCurrentFactoryUrl, $this->cloudCredentials->getBaseUri());
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Application/ExceptionApplicationTest.php:
--------------------------------------------------------------------------------
1 | setInput([
26 | 'applicationUuid' => '2ed281d4-9dec-4cc3-ac63-691c3ba002c2',
27 | 'command' => 'aliases',
28 | ]);
29 | $this->mockUnauthorizedRequest();
30 | $buffer = $this->runApp();
31 | // This is sensitive to the display width of the test environment, so that's fun.
32 | self::assertStringContainsString('Your Cloud Platform API credentials are invalid.', $buffer);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Application/HelpApplicationTest.php:
--------------------------------------------------------------------------------
1 | setInput([
24 | 'command' => 'help',
25 | 'command_name' => 'app:link',
26 | ]);
27 | $buffer = $this->runApp();
28 | $this->assertStringContainsString('The Cloud Platform application UUID or alias (i.e. an application name optionally prefixed with the realm)', $buffer);
29 | $this->assertStringContainsString('Usage:
30 | app:link []
31 | link
32 | app:link []
33 | app:link myapp
34 | app:link prod:myapp
35 | app:link abcd1234-1111-2222-3333-0e02b2c3d470', $buffer);
36 | }
37 |
38 | /**
39 | * @group serial
40 | */
41 | public function testEnvironmentAliasHelp(): void
42 | {
43 | $this->setInput([
44 | 'command' => 'help',
45 | 'command_name' => 'log:tail',
46 | ]);
47 | $buffer = $this->runApp();
48 | $this->assertStringContainsString('The Cloud Platform environment ID or alias (i.e. an application and environment name optionally prefixed with the realm)', $buffer);
49 | $this->assertStringContainsString('Usage:
50 | app:log:tail []
51 | tail
52 | log:tail
53 | app:log:tail []
54 | app:log:tail myapp.dev
55 | app:log:tail prod:myapp.dev
56 | app:log:tail 12345-abcd1234-1111-2222-3333-0e02b2c3d470', $buffer);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/tests/phpunit/src/ApplicationTestBase.php:
--------------------------------------------------------------------------------
1 | kernel = new Kernel('dev', false);
27 | $this->kernel->boot();
28 | $this->kernel->getContainer()
29 | ->set(CloudDataStore::class, $this->datastoreCloud);
30 | $this->kernel->getContainer()
31 | ->set(ClientService::class, $this->clientServiceProphecy->reveal());
32 | $output = new BufferedOutput();
33 | $this->kernel->getContainer()->set(OutputInterface::class, $output);
34 | }
35 |
36 | protected function runApp(): string
37 | {
38 | putenv("ACLI_REPO_ROOT=" . $this->projectDir);
39 | putenv("ACLI_VERSION=" . 'dev-unknown');
40 | $input = $this->kernel->getContainer()->get(InputInterface::class);
41 | $output = $this->kernel->getContainer()->get(OutputInterface::class);
42 | /** @var Application $application */
43 | $application = $this->kernel->getContainer()->get(Application::class);
44 | $application->setAutoExit(false);
45 | $application->run($input, $output);
46 | return $output->fetch();
47 | }
48 |
49 | protected function setInput(array $args): void
50 | {
51 | // ArrayInput requires command to be the first parameter.
52 | if (array_key_exists('command', $args)) {
53 | $newArgs = [];
54 | $newArgs['command'] = $args['command'];
55 | unset($args['command']);
56 | $args = array_merge($newArgs, $args);
57 | }
58 | $input = new ArrayInput($args);
59 | $input->setInteractive(false);
60 | $this->kernel->getContainer()->set(InputInterface::class, $input);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/tests/phpunit/src/CloudApi/ClientServiceTest.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | public static function providerTestIsMachineAuthenticated(): array
19 | {
20 | return [
21 | [
22 | [
23 | 'ACLI_ACCESS_TOKEN' => 'token',
24 | 'ACLI_KEY' => 'key',
25 | 'ACLI_SECRET' => 'secret',
26 | ],
27 | true,
28 | ],
29 | [
30 | [
31 | 'ACLI_ACCESS_TOKEN' => null,
32 | 'ACLI_KEY' => 'key',
33 | 'ACLI_SECRET' => 'secret',
34 | ],
35 | true,
36 | ],
37 | [
38 | [
39 | 'ACLI_ACCESS_TOKEN' => null,
40 | 'ACLI_KEY' => null,
41 | 'ACLI_SECRET' => null,
42 | ],
43 | false,
44 | ],
45 | [
46 | [
47 | 'ACLI_ACCESS_TOKEN' => null,
48 | 'ACLI_KEY' => 'key',
49 | 'ACLI_SECRET' => null,
50 | ],
51 | false,
52 | ],
53 | ];
54 | }
55 |
56 | /**
57 | * @dataProvider providerTestIsMachineAuthenticated
58 | */
59 | public function testIsMachineAuthenticated(array $envVars, bool $isAuthenticated): void
60 | {
61 | self::setEnvVars($envVars);
62 | $cloudDatastore = $this->prophet->prophesize(CloudDataStore::class);
63 | $clientService = new ClientService(new ConnectorFactory([
64 | 'accessToken' => null,
65 | 'key' => null,
66 | 'secret' => null,
67 | ]), $this->application, new CloudCredentials($cloudDatastore->reveal()));
68 | $this->assertEquals($isAuthenticated, $clientService->isMachineAuthenticated());
69 | self::unsetEnvVars($envVars);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/tests/phpunit/src/CloudApi/EnvVarAuthenticationTest.php:
--------------------------------------------------------------------------------
1 | cloudApiBaseUri);
19 | }
20 |
21 | protected function tearDown(): void
22 | {
23 | parent::tearDown();
24 | putenv('ACLI_KEY');
25 | putenv('ACLI_SECRET');
26 | }
27 |
28 | public function testKeyAndSecret(): void
29 | {
30 | $this->removeMockCloudConfigFile();
31 | self::assertEquals(self::$key, $this->cloudCredentials->getCloudKey());
32 | self::assertEquals(self::$secret, $this->cloudCredentials->getCloudSecret());
33 | self::assertEquals($this->cloudApiBaseUri, $this->cloudCredentials->getBaseUri());
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Acsf/AcsfCommandTestBase.php:
--------------------------------------------------------------------------------
1 |
33 | */
34 | protected function getApiCommands(): array
35 | {
36 | $apiCommandHelper = new ApiCommandHelper($this->logger);
37 | $commandFactory = $this->getCommandFactory();
38 | return $apiCommandHelper->getApiCommands(self::$apiSpecFixtureFilePath, $this->apiCommandPrefix, $commandFactory);
39 | }
40 |
41 | /**
42 | * @return array
43 | */
44 | protected static function getAcsfCredentialsFileContents(): array
45 | {
46 | return [
47 | 'acsf_active_factory' => self::$acsfCurrentFactoryUrl,
48 | 'acsf_factories' => [
49 | self::$acsfCurrentFactoryUrl => [
50 | 'active_user' => self::$acsfActiveUser,
51 | 'url' => self::$acsfCurrentFactoryUrl,
52 | 'users' => [
53 | self::$acsfUsername => [
54 | 'key' => self::$acsfKey,
55 | 'username' => self::$acsfUsername,
56 | ],
57 | ],
58 | ],
59 | ],
60 | DataStoreContract::SEND_TELEMETRY => false,
61 | ];
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Acsf/AcsfListCommandTest.php:
--------------------------------------------------------------------------------
1 | application->addCommands($this->getApiCommands());
21 | }
22 |
23 | protected function createCommand(): CommandBase
24 | {
25 | return $this->injectCommand(AcsfListCommand::class);
26 | }
27 |
28 | /**
29 | * @throws \Exception
30 | */
31 | public function testAcsfListCommand(): void
32 | {
33 | $this->executeCommand();
34 | $output = $this->getDisplay();
35 | $this->assertStringContainsString('acsf:api', $output);
36 | $this->assertStringContainsString('acsf:api:ping', $output);
37 | $this->assertStringContainsString('acsf:info:audit-events-find', $output);
38 | }
39 |
40 | /**
41 | * @throws \Exception
42 | */
43 | public function testApiNamespaceListCommand(): void
44 | {
45 | $this->command = $this->injectCommand(AcsfListCommandBase::class);
46 | $name = 'acsf:api';
47 | $this->command->setName($name);
48 | $this->command->setNamespace($name);
49 | $this->executeCommand();
50 | $output = $this->getDisplay();
51 | $this->assertStringContainsString('acsf:api:ping', $output);
52 | $this->assertStringNotContainsString('acsf:groups', $output);
53 | }
54 |
55 | /**
56 | * @throws \Exception
57 | */
58 | public function testListCommand(): void
59 | {
60 | $this->command = $this->injectCommand(ListCommand::class);
61 | $this->executeCommand();
62 | $output = $this->getDisplay();
63 | $this->assertStringContainsString('acsf:api', $output);
64 | $this->assertStringNotContainsString('acsf:api:ping', $output);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Api/ApiBaseCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(ApiBaseCommand::class);
17 | }
18 |
19 | public function testApiBaseCommand(): void
20 | {
21 | $this->expectException(AcquiaCliException::class);
22 | $this->expectExceptionMessage('api:base is not a valid command');
23 | $this->executeCommand();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Api/ApiListCommandTest.php:
--------------------------------------------------------------------------------
1 | application->addCommands($this->getApiCommands());
22 | }
23 |
24 | protected function createCommand(): CommandBase
25 | {
26 | return $this->injectCommand(ApiListCommand::class);
27 | }
28 |
29 | public function testApiListCommand(): void
30 | {
31 | $this->executeCommand();
32 | $output = $this->getDisplay();
33 | $this->assertStringContainsString(' api:accounts:ssh-keys-list', $output);
34 | }
35 |
36 | public function testApiNamespaceListCommand(): void
37 | {
38 | $this->command = $this->injectCommand(ApiListCommandBase::class);
39 | $name = 'api:accounts';
40 | $this->command->setName($name);
41 | $this->command->setNamespace($name);
42 | $this->executeCommand();
43 | $output = $this->getDisplay();
44 | $this->assertStringContainsString('api:accounts:', $output);
45 | $this->assertStringContainsString('api:accounts:ssh-keys-list', $output);
46 | $this->assertStringNotContainsString('api:subscriptions', $output);
47 | }
48 |
49 | public function testListCommand(): void
50 | {
51 | $this->command = $this->injectCommand(ListCommand::class);
52 | $this->executeCommand();
53 | $output = $this->getDisplay();
54 | $this->assertStringContainsString(' api:accounts', $output);
55 | $this->assertStringNotContainsString(' api:accounts:ssh-keys-list', $output);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/App/AppOpenCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(AppOpenCommand::class);
20 | }
21 |
22 | public function testAppOpenCommand(): void
23 | {
24 | $applicationUuid = 'a47ac10b-58cc-4372-a567-0e02b2c3d470';
25 | $localMachineHelper = $this->mockLocalMachineHelper();
26 | $localMachineHelper->startBrowser('https://cloud.acquia.com/a/applications/' . $applicationUuid)
27 | ->shouldBeCalled();
28 | $localMachineHelper->isBrowserAvailable()->willReturn(true);
29 | $this->mockRequest('getApplicationByUuid', $applicationUuid);
30 | $this->executeCommand(['applicationUuid' => $applicationUuid]);
31 | }
32 |
33 | public function testAppOpenNoBrowser(): void
34 | {
35 | $localMachineHelper = $this->mockLocalMachineHelper();
36 | $localMachineHelper->isBrowserAvailable()->willReturn(false);
37 |
38 | $this->expectException(AcquiaCliException::class);
39 | $this->expectExceptionMessage('No browser is available on this machine');
40 | $this->executeCommand();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/App/From/ConfigurationTest.php:
--------------------------------------------------------------------------------
1 | expectExceptionObject($expected_exception);
31 | Configuration::createFromResource($test_stream);
32 | }
33 |
34 | /**
35 | * @return array
36 | */
37 | public static function getTestConfigurations(): array
38 | {
39 | return [
40 | 'bad JSON in configuration file' => [
41 | '{,}',
42 | new JsonException('Syntax error', JSON_ERROR_SYNTAX),
43 | ],
44 | 'empty configuration file' => [
45 | json_encode((object) []),
46 | new DomainException('Missing required key: rootPackageDefinition'),
47 | ],
48 | ];
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/App/From/TestRecommendation.php:
--------------------------------------------------------------------------------
1 | packageName = $package_name ?: self::ABANDON;
29 | }
30 |
31 | public function applies(ExtensionInterface $extension): bool
32 | {
33 | return $this->shouldApply;
34 | }
35 |
36 | public function getPackageName(): string
37 | {
38 | return $this->packageName;
39 | }
40 |
41 | public function getVersionConstraint(): string
42 | {
43 | return $this->versionConstraint;
44 | }
45 |
46 | public function hasModulesToInstall(): bool
47 | {
48 | return !empty($this->install);
49 | }
50 |
51 | /**
52 | * {@inheritDoc}
53 | */
54 | public function getModulesToInstall(): array
55 | {
56 | return $this->install;
57 | }
58 |
59 | public function isVetted(): bool
60 | {
61 | return $this->vetted;
62 | }
63 |
64 | public function hasPatches(): bool
65 | {
66 | return !empty($this->patches);
67 | }
68 |
69 | /**
70 | * {@inheritDoc}
71 | */
72 | public function getPatches(): array
73 | {
74 | return $this->patches;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/App/From/TestSiteInspector.php:
--------------------------------------------------------------------------------
1 | extensions;
36 | }
37 |
38 | public function getPublicFilePath(): string
39 | {
40 | return $this->filePublicPath;
41 | }
42 |
43 | public function getPrivateFilePath(): ?string
44 | {
45 | return $this->filePrivatePath;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/App/UnlinkCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(UnlinkCommand::class);
20 | }
21 |
22 | public function testUnlinkCommand(): void
23 | {
24 | $applicationsResponse = self::getMockResponseFromSpec(
25 | '/applications',
26 | 'get',
27 | '200'
28 | );
29 | $cloudApplication = $applicationsResponse->{'_embedded'}->items[0];
30 | $cloudApplicationUuid = $cloudApplication->uuid;
31 | $this->createMockAcliConfigFile($cloudApplicationUuid);
32 | $this->createDataStores();
33 | $this->command = $this->injectCommand(UnlinkCommand::class);
34 | $this->mockApplicationRequest();
35 |
36 | // Assert we set it correctly.
37 | $this->assertEquals($applicationsResponse->{'_embedded'}->items[0]->uuid, $this->datastoreAcli->get('cloud_app_uuid'));
38 |
39 | $this->executeCommand();
40 | $output = $this->getDisplay();
41 |
42 | // Assert it's been unset.
43 | $this->assertNull($this->datastoreAcli->get('cloud_app_uuid'));
44 | $this->assertStringContainsString("Unlinked $this->projectDir from Cloud application " . $cloudApplication->name, $output);
45 | }
46 |
47 | public function testUnlinkCommandInvalidDir(): void
48 | {
49 | $this->expectException(AcquiaCliException::class);
50 | $this->expectExceptionMessage('There is no Cloud Platform application linked to ' . $this->projectDir);
51 | $this->executeCommand();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Auth/AuthLogoutCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(AuthLogoutCommand::class);
20 | }
21 |
22 | public function testAuthLogoutCommand(): void
23 | {
24 | $this->executeCommand();
25 | $output = $this->getDisplay();
26 | $this->assertFileExists($this->cloudConfigFilepath);
27 | $this->assertStringContainsString('The key Test Key will be deactivated on this machine.', $output);
28 | $this->assertStringContainsString('Do you want to delete the active Cloud Platform API credentials (option --delete)? (yes/no) [no]:', $output);
29 | $this->assertStringContainsString('The active Cloud Platform API credentials were deactivated', $output);
30 | }
31 |
32 | public function testAuthLogoutInvalidDatastore(): void
33 | {
34 | $this->clientServiceProphecy->isMachineAuthenticated()
35 | ->willReturn(false);
36 | $this->removeMockCloudConfigFile();
37 | $data = [
38 | 'acli_key' => 'key2',
39 | 'keys' => [
40 | 'key1' => [
41 | 'label' => 'foo',
42 | 'secret' => 'foo',
43 | 'uuid' => 'foo',
44 | ],
45 | ],
46 | ];
47 | $this->fs->dumpFile($this->cloudConfigFilepath, json_encode($data));
48 | $this->expectException(AcquiaCliException::class);
49 | $this->expectExceptionMessage("Configuration file at the following path contains invalid keys: $this->cloudConfigFilepath Invalid configuration for path \"cloud_api\": acli_key must exist in keys");
50 | $this->createDataStores();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/CodeStudio/CodeStudioCiCdVariablesTest.php:
--------------------------------------------------------------------------------
1 | getDefaultsForNode();
16 | $this->testBooleanValues($variables);
17 | $variables = $codeStudioCiCdVariablesObj->getDefaultsForPhp();
18 | $this->testBooleanValues($variables);
19 | }
20 |
21 | protected function testBooleanValues(array $variables): void
22 | {
23 | foreach ($variables as $variable) {
24 | if ($variable['key'] !== "MYSQL_VERSION" && $variable['key'] !== "PHP_VERSION" && $variable['key'] !== "NODE_VERSION" && $variable['key'] !== "NODE_HOSTING_TYPE") {
25 | $maskedValue = $variable['masked'];
26 | $this->assertEquals(true, $maskedValue);
27 | } else {
28 | $maskedValue = $variable['masked'];
29 | $this->assertEquals(false, $maskedValue);
30 | }
31 | $protectedValue = $variable['protected'];
32 | $this->assertEquals(false, $protectedValue);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Ide/IdeHelper.php:
--------------------------------------------------------------------------------
1 |
27 | */
28 | public static function getEnvVars(): array
29 | {
30 | return [
31 | 'ACQUIA_USER_UUID' => '4acf8956-45df-3cf4-5106-065b62cf1ac8',
32 | 'AH_SITE_ENVIRONMENT' => 'IDE',
33 | 'REMOTEIDE_LABEL' => self::$remoteIdeLabel,
34 | 'REMOTEIDE_UUID' => self::$remoteIdeUuid,
35 | ];
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Ide/IdeInfoCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(IdeInfoCommand::class);
19 | }
20 |
21 | /**
22 | * @group brokenProphecy
23 | */
24 | public function testIdeInfoCommand(): void
25 | {
26 | $applications = $this->mockRequest('getApplications');
27 | $this->mockRequest('getApplicationByUuid', $applications[0]->uuid);
28 | $ides = $this->mockRequest('getApplicationIdes', $applications[0]->uuid);
29 | $this->mockRequest('getIde', $ides[0]->uuid);
30 | $inputs = [
31 | // Would you like Acquia CLI to search for a Cloud application that matches your local git config?
32 | 'n',
33 | // Select the application.
34 | 0,
35 | // Would you like to link the project at ... ?
36 | 'y',
37 | // Select an IDE ...
38 | 0,
39 | ];
40 | $this->executeCommand([], $inputs);
41 |
42 | // Assert.
43 | $output = $this->getDisplay();
44 | $this->assertStringContainsString('Select a Cloud Platform application:', $output);
45 | $this->assertStringContainsString('[0] Sample application 1', $output);
46 | $this->assertStringContainsString('[1] Sample application 2', $output);
47 | $this->assertStringContainsString('IDE property IDE value', $output);
48 | $this->assertStringContainsString('UUID 215824ff-272a-4a8c-9027-df32ed1d68a9', $output);
49 | $this->assertStringContainsString('Label Example IDE', $output);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Ide/IdeOpenCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(IdeOpenCommand::class);
19 | }
20 |
21 | /**
22 | * @group brokenProphecy
23 | */
24 | public function testIdeOpenCommand(): void
25 | {
26 | $applications = $this->mockRequest('getApplications');
27 | $this->mockRequest('getApplicationByUuid', $applications[0]->uuid);
28 | $this->mockRequest('getApplicationIdes', $applications[0]->uuid);
29 | $localMachineHelper = $this->mockLocalMachineHelper();
30 | $localMachineHelper->isBrowserAvailable()->willReturn(true);
31 | $localMachineHelper->startBrowser('https://9a83c081-ef78-4dbd-8852-11cc3eb248f7.ides.acquia.com')
32 | ->willReturn(true);
33 |
34 | $inputs = [
35 | // Would you like Acquia CLI to search for a Cloud application that matches your local git config?
36 | 'n',
37 | // Select a Cloud Platform application:
38 | 0,
39 | // Would you like to link the project at ... ?
40 | 'y',
41 | // Select the IDE you'd like to open:
42 | 0,
43 | ];
44 | $this->executeCommand([], $inputs);
45 |
46 | // Assert.
47 | $output = $this->getDisplay();
48 | $this->assertStringContainsString('Select a Cloud Platform application:', $output);
49 | $this->assertStringContainsString('[0] Sample application 1', $output);
50 | $this->assertStringContainsString('Select the IDE you\'d like to open:', $output);
51 | $this->assertStringContainsString('[0] IDE Label 1', $output);
52 | $this->assertStringContainsString('Your IDE URL: https://9a83c081-ef78-4dbd-8852-11cc3eb248f7.ides.acquia.com', $output);
53 | $this->assertStringContainsString('Your Drupal Site URL: https://9a83c081-ef78-4dbd-8852-11cc3eb248f7.web.ahdev.cloud', $output);
54 | $this->assertStringContainsString('Opening your IDE in browser...', $output);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Ide/IdeRequiredTestTrait.php:
--------------------------------------------------------------------------------
1 | injectCommand(IdeServiceRestartCommand::class);
22 | }
23 |
24 | public function testIdeServiceRestartCommand(): void
25 | {
26 | $localMachineHelper = $this->mockLocalMachineHelper();
27 | $this->mockRestartPhp($localMachineHelper);
28 |
29 | $this->executeCommand(['service' => 'php'], []);
30 |
31 | // Assert.
32 | $output = $this->getDisplay();
33 | $this->assertStringContainsString('Restarted php', $output);
34 | }
35 |
36 | /**
37 | * @group brokenProphecy
38 | */
39 | public function testIdeServiceRestartCommandInvalid(): void
40 | {
41 | $localMachineHelper = $this->mockLocalMachineHelper();
42 | $this->mockRestartPhp($localMachineHelper);
43 |
44 | $this->expectException(ValidatorException::class);
45 | $this->expectExceptionMessage('Specify a valid service name');
46 | $this->executeCommand(['service' => 'rambulator'], []);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Ide/IdeServiceStartCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(IdeServiceStartCommand::class);
22 | }
23 |
24 | public function testIdeServiceStartCommand(): void
25 | {
26 | $localMachineHelper = $this->mockLocalMachineHelper();
27 | $this->mockStartPhp($localMachineHelper);
28 |
29 | $this->executeCommand(['service' => 'php'], []);
30 |
31 | // Assert.
32 | $output = $this->getDisplay();
33 | $this->assertStringContainsString('Starting php', $output);
34 | }
35 |
36 | /**
37 | * @group brokenProphecy
38 | */
39 | public function testIdeServiceStartCommandInvalid(): void
40 | {
41 | $localMachineHelper = $this->mockLocalMachineHelper();
42 | $this->mockStartPhp($localMachineHelper);
43 |
44 | $this->expectException(ValidatorException::class);
45 | $this->expectExceptionMessage('Specify a valid service name');
46 | $this->executeCommand(['service' => 'rambulator'], []);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Ide/IdeServiceStopCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(IdeServiceStopCommand::class);
22 | }
23 |
24 | public function testIdeServiceStopCommand(): void
25 | {
26 | $localMachineHelper = $this->mockLocalMachineHelper();
27 | $this->mockStopPhp($localMachineHelper);
28 |
29 | $this->executeCommand(['service' => 'php'], []);
30 |
31 | // Assert.
32 | $output = $this->getDisplay();
33 | $this->assertStringContainsString('Stopping php', $output);
34 | }
35 |
36 | /**
37 | * @group brokenProphecy
38 | */
39 | public function testIdeServiceStopCommandInvalid(): void
40 | {
41 | $localMachineHelper = $this->mockLocalMachineHelper();
42 | $this->mockStopPhp($localMachineHelper);
43 |
44 | $this->expectException(ValidatorException::class);
45 | $this->expectExceptionMessage('Specify a valid service name');
46 | $this->executeCommand(['service' => 'rambulator'], []);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Ide/IdeShareCommandTest.php:
--------------------------------------------------------------------------------
1 |
21 | */
22 | private array $shareCodeFilepaths;
23 |
24 | private string $shareCode;
25 |
26 | /**
27 | * This method is called before each test.
28 | */
29 | public function setUp(?OutputInterface $output = null): void
30 | {
31 | parent::setUp();
32 | $this->shareCode = 'a47ac10b-58cc-4372-a567-0e02b2c3d470';
33 | $shareCodeFilepath = $this->fs->tempnam(sys_get_temp_dir(), 'acli_share_uuid_');
34 | $this->fs->dumpFile($shareCodeFilepath, $this->shareCode);
35 | $this->command->setShareCodeFilepaths([$shareCodeFilepath]);
36 | IdeHelper::setCloudIdeEnvVars();
37 | }
38 |
39 | protected function createCommand(): CommandBase
40 | {
41 | return $this->injectCommand(IdeShareCommand::class);
42 | }
43 |
44 | public function testIdeShareCommand(): void
45 | {
46 | $this->executeCommand();
47 | $output = $this->getDisplay();
48 | $this->assertStringContainsString('Your IDE Share URL: ', $output);
49 | $this->assertStringContainsString($this->shareCode, $output);
50 | }
51 |
52 | public function testIdeShareRegenerateCommand(): void
53 | {
54 | $this->executeCommand(['--regenerate' => true]);
55 | $output = $this->getDisplay();
56 | $this->assertStringContainsString('Your IDE Share URL: ', $output);
57 | $this->assertStringNotContainsString($this->shareCode, $output);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Ide/Wizard/IdeWizardCreateSshKeyCommandTest.php:
--------------------------------------------------------------------------------
1 | mockApplicationRequest();
22 | $this->mockListSshKeysRequest();
23 | $this->mockRequest('getAccount');
24 | $this->mockPermissionsRequest($applicationResponse);
25 | $this->sshKeyFileName = IdeWizardCreateSshKeyCommand::getSshKeyFilename(IdeHelper::$remoteIdeUuid);
26 | }
27 |
28 | /**
29 | * @return \Acquia\Cli\Command\Ide\Wizard\IdeWizardCreateSshKeyCommand
30 | */
31 | protected function createCommand(): CommandBase
32 | {
33 | return $this->injectCommand(IdeWizardCreateSshKeyCommand::class);
34 | }
35 |
36 | public function testCreate(): void
37 | {
38 | $this->runTestCreate();
39 | }
40 |
41 | /**
42 | * @group brokenProphecy
43 | */
44 | public function testSshKeyAlreadyUploaded(): void
45 | {
46 | $this->runTestSshKeyAlreadyUploaded();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Ide/Wizard/IdeWizardDeleteSshKeyCommandTest.php:
--------------------------------------------------------------------------------
1 | mockListSshKeysRequestWithIdeKey(IdeHelper::$remoteIdeLabel, IdeHelper::$remoteIdeUuid);
20 |
21 | $this->mockDeleteSshKeyRequest($mockBody->{'_embedded'}->items[0]->uuid);
22 |
23 | // Create the file so it can be deleted.
24 | $sshKeyFilename = $this->command::getSshKeyFilename(IdeHelper::$remoteIdeUuid);
25 | $this->fs->touch($this->sshDir . '/' . $sshKeyFilename);
26 | $this->fs->dumpFile($this->sshDir . '/' . $sshKeyFilename . '.pub', $mockBody->{'_embedded'}->items[0]->public_key);
27 |
28 | // Run it!
29 | $this->executeCommand();
30 |
31 | $this->assertFileDoesNotExist($this->sshDir . '/' . $sshKeyFilename);
32 | }
33 |
34 | /**
35 | * @return \Acquia\Cli\Command\Ide\Wizard\IdeWizardCreateSshKeyCommand
36 | */
37 | protected function createCommand(): CommandBase
38 | {
39 | return $this->injectCommand(IdeWizardDeleteSshKeyCommand::class);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Ide/Wizard/IdeWizardTestBase.php:
--------------------------------------------------------------------------------
1 | injectCommand(PullScriptsCommand::class);
19 | }
20 |
21 | public function testRefreshScripts(): void
22 | {
23 | touch(Path::join($this->projectDir, 'composer.json'));
24 | $localMachineHelper = $this->mockLocalMachineHelper();
25 | $process = $this->mockProcess();
26 |
27 | // Composer.
28 | $this->mockExecuteComposerExists($localMachineHelper);
29 | $this->mockExecuteComposerInstall($localMachineHelper, $process);
30 |
31 | // Drush.
32 | $this->mockExecuteDrushExists($localMachineHelper);
33 | $this->mockExecuteDrushStatus($localMachineHelper, $this->projectDir);
34 | $this->mockExecuteDrushCacheRebuild($localMachineHelper, $process);
35 | $this->mockExecuteDrushSqlSanitize($localMachineHelper, $process);
36 |
37 | $inputs = [
38 | // Would you like Acquia CLI to search for a Cloud application that matches your local git config?
39 | 'n',
40 | // Select a Cloud Platform application:
41 | 0,
42 | // Would you like to link the project at ... ?
43 | 'n',
44 | // Choose an Acquia environment:
45 | 0,
46 | ];
47 |
48 | $this->executeCommand([
49 | '--dir' => $this->projectDir,
50 | ], $inputs);
51 |
52 | $this->getDisplay();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Push/PushCodeCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(PushCodeCommand::class);
19 | }
20 |
21 | public function testPushCode(): void
22 | {
23 | $this->executeCommand();
24 |
25 | $output = $this->getDisplay();
26 |
27 | $this->assertStringContainsString('Use git to push code changes upstream.', $output);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Remote/AliasesListCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(AliasListCommand::class);
19 | }
20 |
21 | /**
22 | * @throws \Exception
23 | */
24 | public function testRemoteAliasesListCommand(): void
25 | {
26 | $applicationsResponse = $this->mockApplicationsRequest();
27 | $this->mockApplicationRequest();
28 | $this->mockEnvironmentsRequest($applicationsResponse);
29 |
30 | $inputs = [
31 | // Would you like Acquia CLI to search for a Cloud application that matches your local git config?
32 | 'n',
33 | // Select a Cloud Platform application:
34 | '0',
35 | // Would you like to link the project at ...
36 | 'n',
37 | ];
38 | $this->executeCommand([], $inputs);
39 |
40 | // Assert.
41 | $output = $this->getDisplay();
42 |
43 | $this->assertStringContainsString('Environments for Sample application 1', $output);
44 | $this->assertStringContainsString('| Alias | UUID | SSH URL |', $output);
45 | $this->assertStringContainsString('| devcloud2.dev | 24-a47ac10b-58cc-4372-a567-0e02b2c3d470 | site.dev@sitedev.ssh.hosted.acquia-sites.com', $output);
46 | $this->assertStringContainsString('Run acli api:environments:find to get more information about a specific environment', $output);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Remote/DrushCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(DrushCommand::class);
20 | }
21 |
22 | /**
23 | * @return array>>
24 | */
25 | public static function providerTestRemoteDrushCommand(): array
26 | {
27 | return [
28 | [
29 | [
30 | 'drush_command' => 'status --fields=db-status',
31 | ],
32 | ],
33 | [
34 | [
35 | 'drush_command' => 'status --fields=db-status',
36 | ],
37 | ],
38 | ];
39 | }
40 |
41 | /**
42 | * @dataProvider providerTestRemoteDrushCommand
43 | */
44 | public function testRemoteDrushCommand(array $args): void
45 | {
46 | $this->mockGetEnvironment();
47 | [$process, $localMachineHelper] = $this->mockForExecuteCommand();
48 | $localMachineHelper->checkRequiredBinariesExist(['ssh'])
49 | ->shouldBeCalled();
50 | $sshCommand = [
51 | 'ssh',
52 | 'site.dev@sitedev.ssh.hosted.acquia-sites.com',
53 | '-t',
54 | '-o StrictHostKeyChecking=no',
55 | '-o AddressFamily inet',
56 | '-o LogLevel=ERROR',
57 | 'cd /var/www/html/site.dev/docroot; ',
58 | 'drush',
59 | '--uri=http://sitedev.hosted.acquia-sites.com status --fields=db-status',
60 | ];
61 | $localMachineHelper
62 | ->execute($sshCommand, Argument::type('callable'), null, true, null)
63 | ->willReturn($process->reveal())
64 | ->shouldBeCalled();
65 |
66 | $this->command->sshHelper = new SshHelper($this->output, $localMachineHelper->reveal(), $this->logger);
67 | $this->executeCommand($args, self::inputChooseEnvironment());
68 |
69 | // Assert.
70 | $this->getDisplay();
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Remote/SshCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(SshCommand::class);
21 | }
22 |
23 | /**
24 | * @group serial
25 | */
26 | public function testRemoteAliasesDownloadCommand(): void
27 | {
28 | ClearCacheCommand::clearCaches();
29 | $this->mockForGetEnvironmentFromAliasArg();
30 | [$process, $localMachineHelper] = $this->mockForExecuteCommand();
31 | $localMachineHelper->checkRequiredBinariesExist(['ssh'])
32 | ->shouldBeCalled();
33 | $sshCommand = [
34 | 'ssh',
35 | 'site.dev@sitedev.ssh.hosted.acquia-sites.com',
36 | '-t',
37 | '-o StrictHostKeyChecking=no',
38 | '-o AddressFamily inet',
39 | '-o LogLevel=ERROR',
40 | 'cd /var/www/html/devcloud2.dev; exec $SHELL -l',
41 | ];
42 | $localMachineHelper
43 | ->execute($sshCommand, Argument::type('callable'), null, true, null)
44 | ->willReturn($process->reveal())
45 | ->shouldBeCalled();
46 |
47 | $this->command->sshHelper = new SshHelper($this->output, $localMachineHelper->reveal(), $this->logger);
48 |
49 | $args = [
50 | 'alias' => 'devcloud2.dev',
51 | ];
52 | $this->executeCommand($args);
53 |
54 | // Assert.
55 | $output = $this->getDisplay();
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Remote/SshCommandTestBase.php:
--------------------------------------------------------------------------------
1 | mockApplicationsRequest(1);
16 | $this->mockEnvironmentsRequest($applicationsResponse);
17 | $this->clientProphecy->addQuery('filter', 'hosting=@*:devcloud2')
18 | ->shouldBeCalled();
19 | $this->mockRequest('getAccount');
20 | }
21 |
22 | /**
23 | * @return array
24 | */
25 | protected function mockForExecuteCommand(): array
26 | {
27 | $process = $this->prophet->prophesize(Process::class);
28 | $process->isSuccessful()->willReturn(true);
29 | $process->getExitCode()->willReturn(0);
30 | $localMachineHelper = $this->prophet->prophesize(LocalMachineHelper::class);
31 | $localMachineHelper->useTty()->willReturn(false)->shouldBeCalled();
32 | return [$process, $localMachineHelper];
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Self/MakeDocsCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(MakeDocsCommand::class);
20 | }
21 |
22 | public function testMakeDocsCommand(): void
23 | {
24 | $this->executeCommand();
25 | $output = $this->getDisplay();
26 | $this->assertStringContainsString('Console Tool', $output);
27 | $this->assertStringContainsString('============', $output);
28 | $this->assertStringContainsString('- `help`_', $output);
29 | }
30 |
31 | public function testMakeDocsCommandDump(): void
32 | {
33 | $vfs = vfsStream::setup('root');
34 | $this->executeCommand(['--dump' => $vfs->url()]);
35 | $this->assertStringContainsString('The completion command dumps', $vfs->getChild('completion.json')->getContent());
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Self/SelfInfoCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(SelfInfoCommand::class);
19 | }
20 |
21 | /**
22 | * @throws \Exception
23 | */
24 | public function testSelfInfoCommand(): void
25 | {
26 | $this->mockRequest('getAccount');
27 | $this->executeCommand();
28 | $output = $this->getDisplay();
29 | $this->assertStringContainsString('Property', $output);
30 | $this->assertStringContainsString('--------', $output);
31 | $this->assertStringContainsString('Version', $output);
32 | $this->assertStringContainsString('Cloud datastore', $output);
33 | $this->assertStringContainsString('ACLI datastore', $output);
34 | $this->assertStringContainsString('Telemetry enabled', $output);
35 | $this->assertStringContainsString('User ID', $output);
36 | $this->assertStringContainsString('is_acquian', $output);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Self/TelemetryDisableCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(TelemetryDisableCommand::class);
19 | }
20 |
21 | public function testTelemetryDisableCommand(): void
22 | {
23 | $this->executeCommand();
24 | $output = $this->getDisplay();
25 | $this->assertStringContainsString('Telemetry has been disabled.', $output);
26 |
27 | $settings = json_decode(file_get_contents($this->cloudConfigFilepath), true);
28 | $this->assertFalse($settings['send_telemetry']);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Self/TelemetryEnableCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(TelemetryEnableCommand::class);
21 | }
22 |
23 | public function testTelemetryEnableCommand(): void
24 | {
25 | $this->executeCommand();
26 | $output = $this->getDisplay();
27 | $this->assertStringContainsString('Telemetry has been enabled.', $output);
28 |
29 | $settings = json_decode(file_get_contents($this->cloudConfigFilepath), true);
30 | $this->assertTrue($settings['send_telemetry']);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Ssh/SshKeyInfoCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(SshKeyInfoCommand::class);
16 | }
17 |
18 | public function setUp(): void
19 | {
20 | parent::setUp();
21 | $this->setupFsFixture();
22 | $this->command = $this->createCommand();
23 | }
24 |
25 | public function testInfo(): void
26 | {
27 | $this->mockListSshKeysRequest();
28 |
29 | $inputs = [
30 | // Choose key.
31 | '0',
32 | ];
33 | $this->executeCommand([], $inputs);
34 |
35 | // Assert.
36 | $output = $this->getDisplay();
37 | $this->assertStringContainsString('Choose an SSH key to view', $output);
38 | $this->assertStringContainsString('SSH key property SSH key value', $output);
39 | $this->assertStringContainsString('UUID 02905393-65d7-4bef-873b-24593f73d273', $output);
40 | $this->assertStringContainsString('Label PC Home', $output);
41 | $this->assertStringContainsString('Fingerprint (md5) 5d:23:fb:45:70:df:ef:ad:ca:bf:81:93:cd:50:26:28', $output);
42 | $this->assertStringContainsString('Created at 2017-05-09T20:30:35.000Z', $output);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/Ssh/SshKeyListCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(SshKeyListCommand::class);
19 | }
20 |
21 | public function setUp(): void
22 | {
23 | parent::setUp();
24 | $this->setupFsFixture();
25 | $this->command = $this->createCommand();
26 | }
27 |
28 | public function testList(): void
29 | {
30 |
31 | $mockBody = self::getMockResponseFromSpec('/account/ssh-keys', 'get', '200');
32 | $this->clientProphecy->request('get', '/account/ssh-keys')
33 | ->willReturn($mockBody->{'_embedded'}->items)
34 | ->shouldBeCalled();
35 | $mockRequestArgs = self::getMockRequestBodyFromSpec('/account/ssh-keys');
36 | $tempFileName = $this->createLocalSshKey($mockRequestArgs['public_key']);
37 | $baseFilename = basename($tempFileName);
38 | $this->executeCommand();
39 |
40 | // Assert.
41 | $output = $this->getDisplay();
42 | $this->assertStringContainsString('Local filename', $output);
43 | $this->assertStringContainsString('Cloud Platform label', $output);
44 | $this->assertStringContainsString('Fingerprint', $output);
45 | $this->assertStringContainsString($baseFilename, $output);
46 | $this->assertStringContainsString($mockBody->_embedded->items[0]->label, $output);
47 | $this->assertStringContainsString($mockBody->_embedded->items[1]->label, $output);
48 | $this->assertStringContainsString($mockBody->_embedded->items[2]->label, $output);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Commands/UpdateCommandTest.php:
--------------------------------------------------------------------------------
1 | injectCommand(HelloWorldCommand::class);
20 | }
21 |
22 | public function testSelfUpdate(): void
23 | {
24 | $this->application->setVersion($this->startVersion);
25 | $this->mockSelfUpdateCommand();
26 | $this->executeCommand();
27 | self::assertEquals(0, $this->getStatusCode());
28 | self::assertStringContainsString("Acquia CLI $this->endVersion is available", $this->getDisplay());
29 | }
30 |
31 | public function testBadResponseFailsSilently(): void
32 | {
33 | $this->application->setVersion($this->startVersion);
34 | $this->mockSelfUpdateCommand(true);
35 | $this->executeCommand();
36 | self::assertEquals(0, $this->getStatusCode());
37 | self::assertStringNotContainsString("Acquia CLI $this->endVersion is available", $this->getDisplay());
38 | }
39 |
40 | /**
41 | * @throws \GuzzleHttp\Exception\GuzzleException
42 | */
43 | private function mockSelfUpdateCommand(bool $exception = false): void
44 | {
45 | $selfUpdateManagerProphecy = $this->prophet->prophesize(SelfUpdateManager::class);
46 | if ($exception) {
47 | $selfUpdateManagerProphecy->isUpToDate()->willThrow(new Exception())->shouldBeCalled();
48 | } else {
49 | $selfUpdateManagerProphecy->isUpToDate()
50 | ->willReturn(false)
51 | ->shouldBeCalled();
52 | $selfUpdateManagerProphecy->getLatestReleaseFromGithub()
53 | ->willReturn(['tag_name' => $this->endVersion])
54 | ->shouldBeCalled();
55 | }
56 | $this->command->selfUpdateManager = $selfUpdateManagerProphecy->reveal();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Misc/ApiSpecTest.php:
--------------------------------------------------------------------------------
1 | assertFileExists($apiSpecFile);
16 | $apiSpec = file_get_contents($apiSpecFile);
17 | $this->assertStringNotContainsString('x-internal', $apiSpec);
18 | $this->assertStringNotContainsString('cloud.acquia.dev', $apiSpec);
19 | $this->assertStringNotContainsString('network.acquia-sites.com', $apiSpec);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Misc/ChecklistTest.php:
--------------------------------------------------------------------------------
1 | section()
20 | // method which is only available for ConsoleOutput. Could make a custom testing
21 | // output class with the method.
22 | parent::setUp();
23 | $this->output = new ConsoleOutput();
24 | }
25 |
26 | /**
27 | * @group serial
28 | */
29 | public function testSpinner(): void
30 | {
31 | putenv('PHPUNIT_RUNNING=1');
32 | $checklist = new Checklist($this->output);
33 | $checklist->addItem('Testing!');
34 | $items = $checklist->getItems();
35 | $progressBar = $items[0]['spinner']->getProgressBar();
36 | $this->assertEquals(' ', $progressBar->getMessage('detail'));
37 |
38 | // Make the spinner spin with some output.
39 | $outputCallback = static function (string $type, string $buffer) use ($checklist): void {
40 | $checklist->updateProgressBar($buffer);
41 | };
42 | $this->localMachineHelper->execute([
43 | 'echo',
44 | 'hello world',
45 | ], $outputCallback, null, false);
46 |
47 | // Complete the item.
48 | $checklist->completePreviousItem();
49 | $items = $checklist->getItems();
50 | /** @var \Symfony\Component\Console\Helper\ProgressBar $progressBar */
51 | $progressBar = $items[0]['spinner']->getProgressBar();
52 | $this->assertEquals('Testing!', $progressBar->getMessage());
53 | $this->assertEquals('✔', $progressBar->getBarCharacter());
54 | $this->assertEquals('[38;5;202m⢸[0m', $progressBar->getProgressCharacter());
55 | $this->assertEquals('⌛', $progressBar->getEmptyBarCharacter());
56 | $this->assertEquals(1, $progressBar->getBarWidth());
57 | $this->assertEquals(' ', $progressBar->getMessage('detail'));
58 |
59 | putenv('PHPUNIT_RUNNING');
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/tests/phpunit/src/Misc/EnvDbCredsTest.php:
--------------------------------------------------------------------------------
1 | dbUser = 'myuserisgood';
25 | $this->dbPassword = 'mypasswordisgreat';
26 | $this->dbName = 'mynameisgrand';
27 | $this->dbHost = 'myhostismeh';
28 | self::setEnvVars($this->getEnvVars());
29 | parent::setUp();
30 | }
31 |
32 | public function tearDown(): void
33 | {
34 | parent::tearDown();
35 | self::unsetEnvVars($this->getEnvVars());
36 | }
37 |
38 | /**
39 | * @return array
40 | */
41 | protected function getEnvVars(): array
42 | {
43 | return [
44 | 'ACLI_DB_HOST' => $this->dbHost,
45 | 'ACLI_DB_NAME' => $this->dbName,
46 | 'ACLI_DB_PASSWORD' => $this->dbPassword,
47 | 'ACLI_DB_USER' => $this->dbUser,
48 | ];
49 | }
50 |
51 | protected function createCommand(): CommandBase
52 | {
53 | return $this->injectCommand(ClearCacheCommand::class);
54 | }
55 |
56 | public function testEnvDbCreds(): void
57 | {
58 | $this->assertEquals($this->dbUser, $this->command->getLocalDbUser());
59 | $this->assertEquals($this->dbPassword, $this->command->getLocalDbPassword());
60 | $this->assertEquals($this->dbName, $this->command->getLocalDbName());
61 | $this->assertEquals($this->dbHost, $this->command->getLocalDbHost());
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/var/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/acquia/cli/29236b4ec1b1e81e16fb19373e9641556dfdf76d/var/.gitkeep
--------------------------------------------------------------------------------