Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI-1227: Implement PHP 8 attributes #1642

Merged
merged 12 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export PATH="$PATH:${PWD}/vendor/bin"
13 changes: 13 additions & 0 deletions src/Attribute/RequireAuth.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types = 1);

namespace Acquia\Cli\Attribute;

/**
* Specify that a command requires authentication.
*/
#[\Attribute(\Attribute::TARGET_CLASS)]
class RequireAuth {

}
13 changes: 13 additions & 0 deletions src/Attribute/RequireDb.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types = 1);

namespace Acquia\Cli\Attribute;

/**
* Specify that a command requires authentication.
*/
#[\Attribute(\Attribute::TARGET_CLASS)]
class RequireDb {

}
3 changes: 2 additions & 1 deletion src/Command/Acsf/AcsfApiBaseCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Acquia\Cli\Command\Acsf;

use Acquia\Cli\Attribute\RequireAuth;
use Acquia\Cli\Command\Api\ApiBaseCommand;
use Acquia\Cli\Exception\AcquiaCliException;
use Symfony\Component\Console\Attribute\AsCommand;
Expand All @@ -13,7 +14,7 @@
class AcsfApiBaseCommand extends ApiBaseCommand {

protected function checkAuthentication(): void {
if ($this->commandRequiresAuthentication() && !$this->cloudApiClientService->isMachineAuthenticated()) {
if ((new \ReflectionClass(static::class))->getAttributes(RequireAuth::class) && !$this->cloudApiClientService->isMachineAuthenticated()) {

Check warning on line 17 in src/Command/Acsf/AcsfApiBaseCommand.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "LogicalNot": --- Original +++ New @@ @@ { protected function checkAuthentication() : void { - if ((new \ReflectionClass(static::class))->getAttributes(RequireAuth::class) && !$this->cloudApiClientService->isMachineAuthenticated()) { + if ((new \ReflectionClass(static::class))->getAttributes(RequireAuth::class) && $this->cloudApiClientService->isMachineAuthenticated()) { throw new AcquiaCliException('This machine is not yet authenticated with the Acquia Cloud Site Factory. Run `acli auth:acsf-login`'); } }

Check warning on line 17 in src/Command/Acsf/AcsfApiBaseCommand.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "LogicalAnd": --- Original +++ New @@ @@ { protected function checkAuthentication() : void { - if ((new \ReflectionClass(static::class))->getAttributes(RequireAuth::class) && !$this->cloudApiClientService->isMachineAuthenticated()) { + if ((new \ReflectionClass(static::class))->getAttributes(RequireAuth::class) || !$this->cloudApiClientService->isMachineAuthenticated()) { throw new AcquiaCliException('This machine is not yet authenticated with the Acquia Cloud Site Factory. Run `acli auth:acsf-login`'); } }

Check warning on line 17 in src/Command/Acsf/AcsfApiBaseCommand.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "LogicalAndSingleSubExprNegation": --- Original +++ New @@ @@ { protected function checkAuthentication() : void { - if ((new \ReflectionClass(static::class))->getAttributes(RequireAuth::class) && !$this->cloudApiClientService->isMachineAuthenticated()) { + if (!(new \ReflectionClass(static::class))->getAttributes(RequireAuth::class) && !$this->cloudApiClientService->isMachineAuthenticated()) { throw new AcquiaCliException('This machine is not yet authenticated with the Acquia Cloud Site Factory. Run `acli auth:acsf-login`'); } }
throw new AcquiaCliException('This machine is not yet authenticated with the Acquia Cloud Site Factory. Run `acli auth:acsf-login`');
}
}
Expand Down
11 changes: 4 additions & 7 deletions src/Command/Acsf/AcsfListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@

namespace Acquia\Cli\Command\Acsf;

use Acquia\Cli\Attribute\RequireAuth;
use Symfony\Component\Console\Attribute\AsCommand;

#[AsCommand(name: 'acsf:list')]
class AcsfListCommand extends AcsfListCommandBase {
#[RequireAuth]
#[AsCommand(name: 'acsf:list', description: 'List all Acquia Cloud Site Factory commands', aliases: ['acsf'])]
final class AcsfListCommand extends AcsfListCommandBase {

protected string $namespace = 'acsf';

protected function configure(): void {
$this->setDescription("List all Acquia Cloud Site Factory commands")
->setAliases(['acsf']);
}

}
8 changes: 0 additions & 8 deletions src/Command/Acsf/AcsfListCommandBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,6 @@ public function setNamespace(string $namespace): void {
$this->namespace = $namespace;
}

/**
* Indicates whether the command requires the machine to be authenticated with the Cloud Platform.
*/
protected function commandRequiresAuthentication(): bool {
// Assume commands require authentication unless they opt out by overriding this method.
return FALSE;
}

protected function execute(InputInterface $input, OutputInterface $output): int {
$commands = $this->getApplication()->all();
foreach ($commands as $command) {
Expand Down
9 changes: 3 additions & 6 deletions src/Command/Api/ApiBaseCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Acquia\Cli\Command\Api;

use Acquia\Cli\Attribute\RequireAuth;
use Acquia\Cli\Command\CommandBase;
use AcquiaCloudApi\Connector\Client;
use AcquiaCloudApi\Exception\ApiErrorException;
Expand All @@ -21,7 +22,8 @@
use Symfony\Component\Validator\Exception\ValidatorException;
use Symfony\Component\Validator\Validation;

#[AsCommand(name: 'api:base')]
#[RequireAuth]
#[AsCommand(name: 'api:base', hidden: TRUE)]
class ApiBaseCommand extends CommandBase {

protected string $method;
Expand Down Expand Up @@ -53,11 +55,6 @@ class ApiBaseCommand extends CommandBase {
*/
private array $pathParams = [];

protected function configure(): void {
$this->setHidden();
parent::configure();
}

protected function interact(InputInterface $input, OutputInterface $output): void {
$params = array_merge($this->queryParams, $this->postParams, $this->pathParams);
foreach ($this->getDefinition()->getArguments() as $argument) {
Expand Down
11 changes: 4 additions & 7 deletions src/Command/Api/ApiListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@

namespace Acquia\Cli\Command\Api;

use Acquia\Cli\Attribute\RequireAuth;
use Symfony\Component\Console\Attribute\AsCommand;

#[AsCommand(name: 'api:list')]
class ApiListCommand extends ApiListCommandBase {
#[RequireAuth]
#[AsCommand(name: 'api:list', description: 'List all API commands', aliases: ['api'])]
final class ApiListCommand extends ApiListCommandBase {

protected string $namespace = 'api';

protected function configure(): void {
$this->setDescription("List all API commands")
->setAliases(['api']);
}

}
11 changes: 6 additions & 5 deletions src/Command/App/AppOpenCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@

namespace Acquia\Cli\Command\App;

use Acquia\Cli\Attribute\RequireAuth;
use Acquia\Cli\Command\CommandBase;
use Acquia\Cli\Exception\AcquiaCliException;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(name: 'app:open')]
class AppOpenCommand extends CommandBase {
#[RequireAuth]
#[AsCommand(name: 'app:open', description: 'Opens your browser to view a given Cloud application', aliases: ['open', 'o'])]
final class AppOpenCommand extends CommandBase {

protected function configure(): void {
$this->setDescription('Opens your browser to view a given Cloud application')
->acceptApplicationUuid()
->setAliases(['open', 'o']);
$this

Check warning on line 20 in src/Command/App/AppOpenCommand.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ { protected function configure() : void { - $this->acceptApplicationUuid(); + } protected function execute(InputInterface $input, OutputInterface $output) : int {
->acceptApplicationUuid();
}

protected function execute(InputInterface $input, OutputInterface $output): int {
Expand Down
6 changes: 4 additions & 2 deletions src/Command/App/AppVcsInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Acquia\Cli\Command\App;

use Acquia\Cli\Attribute\RequireAuth;
use Acquia\Cli\Command\CommandBase;
use Acquia\Cli\Exception\AcquiaCliException;
use AcquiaCloudApi\Endpoints\Code;
Expand All @@ -14,11 +15,12 @@
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(name: 'app:vcs:info')]
#[RequireAuth]
#[AsCommand(name: 'app:vcs:info', description: 'Get all branches and tags of the application with the deployment status')]
class AppVcsInfo extends CommandBase {

protected function configure(): void {
$this->setDescription('Get all branches and tags of the application with the deployment status')
$this

Check warning on line 23 in src/Command/App/AppVcsInfo.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ { protected function configure() : void { - $this->addOption('deployed', NULL, InputOption::VALUE_OPTIONAL, 'Show only deployed branches and tags')->addUsage('[<applicationAlias>] --deployed'); + $this->acceptApplicationUuid(); } protected function execute(InputInterface $input, OutputInterface $output) : int
->addOption('deployed', NULL, InputOption::VALUE_OPTIONAL, 'Show only deployed branches and tags')
->addUsage('[<applicationAlias>] --deployed');
$this->acceptApplicationUuid();
Expand Down
8 changes: 4 additions & 4 deletions src/Command/App/LinkCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@

namespace Acquia\Cli\Command\App;

use Acquia\Cli\Attribute\RequireAuth;
use Acquia\Cli\Command\CommandBase;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(name: 'app:link')]
class LinkCommand extends CommandBase {
#[RequireAuth]
#[AsCommand(name: 'app:link', description: 'Associate your project with a Cloud Platform application', aliases: ['link'])]
final class LinkCommand extends CommandBase {

protected function configure(): void {
$this->setDescription('Associate your project with a Cloud Platform application')
->setAliases(['link']);
$this->acceptApplicationUuid();
}

Expand Down
11 changes: 6 additions & 5 deletions src/Command/App/LogTailCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@

namespace Acquia\Cli\Command\App;

use Acquia\Cli\Attribute\RequireAuth;
use Acquia\Cli\Command\CommandBase;
use AcquiaCloudApi\Endpoints\Logs;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(name: 'app:log:tail')]
class LogTailCommand extends CommandBase {
#[RequireAuth]
#[AsCommand(name: 'app:log:tail', description: 'Tail the logs from your environments', aliases: ['tail', 'log:tail'])]
final class LogTailCommand extends CommandBase {

protected function configure(): void {
$this->setDescription('Tail the logs from your environments')
->acceptEnvironmentId()
->setAliases(['tail', 'log:tail']);
$this
->acceptEnvironmentId();
}

protected function execute(InputInterface $input, OutputInterface $output): int {
Expand Down
13 changes: 4 additions & 9 deletions src/Command/App/NewCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Path;

#[AsCommand(name: 'app:new:local')]
class NewCommand extends CommandBase {
#[AsCommand(name: 'app:new:local', description: 'Create a new Drupal or Next.js project', aliases: ['new'])]
final class NewCommand extends CommandBase {

protected function configure(): void {
$this->setDescription('Create a new Drupal or Next.js project')
->addArgument('directory', InputArgument::OPTIONAL, 'The destination directory')
->setAliases(['new']);
$this
->addArgument('directory', InputArgument::OPTIONAL, 'The destination directory');
}

protected function execute(InputInterface $input, OutputInterface $output): int {
Expand Down Expand Up @@ -65,10 +64,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return Command::SUCCESS;
}

protected function commandRequiresAuthentication(): bool {
return FALSE;
}

private function createNextJsProject(string $dir): void {
$process = $this->localMachineHelper->execute([
'npx',
Expand Down
23 changes: 9 additions & 14 deletions src/Command/App/NewFromDrupal7Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,13 @@
use Symfony\Component\Filesystem\Path;
use Symfony\Component\Validator\Exception\ValidatorException;

#[AsCommand(name: 'app:new:from:drupal7')]
class NewFromDrupal7Command extends CommandBase {
#[AsCommand(name: 'app:new:from:drupal7', description: 'Generate a new Drupal 9+ project from a Drupal 7 application using the default Acquia Migrate Accelerate recommendations.', aliases: [
// Currently only "from Drupal 7", more to potentially follow.
'from:d7',
// A nod to its roots.
'ama',
])]
final class NewFromDrupal7Command extends CommandBase {

/**
* Exit code raised when the URI flag does not correspond to configuration.
Expand All @@ -42,18 +47,12 @@ class NewFromDrupal7Command extends CommandBase {
public const ERR_INDETERMINATE_SITE = 4;

protected function configure(): void {
$this->setDescription('Generate a new Drupal 9+ project from a Drupal 7 application using the default Acquia Migrate Accelerate recommendations.')
$this
->addOption('drupal7-directory', 'source', InputOption::VALUE_OPTIONAL, 'The root of the Drupal 7 application')
->addOption('drupal7-uri', 'uri', InputOption::VALUE_OPTIONAL, 'Only necessary in case of a multisite. If a single site, this will be computed automatically.')
->addOption('stored-analysis', 'analysis', InputOption::VALUE_OPTIONAL, 'As an alternative to drupal7-directory, it is possible to pass a stored analysis.')
->addOption('recommendations', 'recommendations', InputOption::VALUE_OPTIONAL, 'Overrides the default recommendations.')
->addOption('directory', 'destination', InputOption::VALUE_OPTIONAL, 'The directory where to generate the new application.')
->setAliases([
// Currently only "from Drupal 7", more to potentially follow.
'from:d7',
// A nod to its roots.
'ama',
]);
->addOption('directory', 'destination', InputOption::VALUE_OPTIONAL, 'The directory where to generate the new application.');
}

private function getInspector(InputInterface $input): SiteInspectorInterface {
Expand Down Expand Up @@ -215,10 +214,6 @@ function (mixed $path): string {
return Command::SUCCESS;
}

protected function commandRequiresAuthentication(): bool {
return FALSE;
}

private function initializeGitRepository(string $dir): void {
if ($this->localMachineHelper->getFilesystem()->exists(Path::join($dir, '.git'))) {
$this->logger->debug('.git directory detected, skipping Git repo initialization');
Expand Down
8 changes: 5 additions & 3 deletions src/Command/App/TaskWaitCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@

namespace Acquia\Cli\Command\App;

use Acquia\Cli\Attribute\RequireAuth;
use Acquia\Cli\Command\CommandBase;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(name: 'app:task-wait')]
class TaskWaitCommand extends CommandBase {
#[RequireAuth]
#[AsCommand(name: 'app:task-wait', description: 'Wait for a task to complete')]
final class TaskWaitCommand extends CommandBase {

protected function configure(): void {
$this->setDescription('Wait for a task to complete')
$this
->addArgument('notification-uuid', InputArgument::REQUIRED, 'The task notification UUID or Cloud Platform API response containing a linked notification')
->setHelp('Accepts either a notification UUID or Cloud Platform API response as JSON string. The JSON string must contain the _links->notification->href property.')
->addUsage('"$(acli api:environments:domain-clear-caches [environmentId] [domain])"');
Expand Down
13 changes: 2 additions & 11 deletions src/Command/App/UnlinkCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,8 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(name: 'app:unlink')]
class UnlinkCommand extends CommandBase {

protected function configure(): void {
$this->setDescription('Remove local association between your project and a Cloud Platform application')
->setAliases(['unlink']);
}

protected function commandRequiresAuthentication(): bool {
return FALSE;
}
#[AsCommand(name: 'app:unlink', description: 'Remove local association between your project and a Cloud Platform application', aliases: ['unlink'])]
final class UnlinkCommand extends CommandBase {

protected function execute(InputInterface $input, OutputInterface $output): int {
$this->validateCwdIsValidDrupalProject();
Expand Down
15 changes: 8 additions & 7 deletions src/Command/Archive/ArchiveExportCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@

namespace Acquia\Cli\Command\Archive;

use Acquia\Cli\Attribute\RequireAuth;
use Acquia\Cli\Attribute\RequireDb;
use Acquia\Cli\Command\CommandBase;
use Acquia\Cli\Exception\AcquiaCliException;
use Acquia\Cli\Output\Checklist;
use Acquia\DrupalEnvironmentDetector\AcquiaDrupalEnvironmentDetector;
use Closure;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
Expand All @@ -17,7 +20,10 @@
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Path;

class ArchiveExportCommand extends CommandBase {
#[RequireAuth]
#[RequireDb]
#[AsCommand(name: 'archive:export', description: 'Export an archive of the Drupal application including code, files, and database')]
final class ArchiveExportCommand extends CommandBase {

protected Checklist $checklist;

Expand All @@ -30,13 +36,8 @@ class ArchiveExportCommand extends CommandBase {

private const PUBLIC_FILES_DIR = '/docroot/sites/default/files';

protected function commandRequiresDatabase(): bool {
return TRUE;
}

protected function configure(): void {
$this->setName('archive:export');
$this->setDescription('Export an archive of the Drupal application including code, files, and database')
$this
->addArgument('destination-dir', InputArgument::REQUIRED, 'The destination directory for the archive file')
->addOption('source-dir', 'dir', InputOption::VALUE_REQUIRED, 'The directory containing the Drupal project to be pushed')
->addOption('no-files', NULL, InputOption::VALUE_NONE, 'Exclude public files directory from archive')
Expand Down
Loading