Skip to content
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
17 changes: 9 additions & 8 deletions commands/list_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,14 +213,15 @@ type Application struct {
}

type Command struct {
Name CommandName `json:"name"`
Usage []string `json:"usage"`
Aliases []string `json:"aliases"`
Description CleanString `json:"description"`
Help CleanString `json:"help"`
Examples []Example `json:"examples"`
Definition Definition `json:"definition"`
Hidden bool `json:"hidden"`
Name CommandName `json:"name"`
Usage []string `json:"usage"`
Aliases []string `json:"aliases"`
HiddenAliases []string `json:"hidden_aliases,omitempty"`
Description CleanString `json:"description"`
Help CleanString `json:"help"`
Examples []Example `json:"examples"`
Definition Definition `json:"definition"`
Hidden bool `json:"hidden"`
}

// indentLines adds a number of spaces to each line of the given text.
Expand Down
59 changes: 59 additions & 0 deletions integration-tests/hidden_aliases_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package tests

import (
"encoding/json"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestHiddenAliases(t *testing.T) {
f := newCommandFactory(t, "", "")

cases := []struct {
alias string
command string
}{
{"snapshots", "backup:list"},
{"logs", "environment:logs"},
{"user:role", "user:get"},
{"environment:sql", "db:sql"},
}
for _, c := range cases {
t.Run(c.alias, func(t *testing.T) {
var data struct {
Name string `json:"name"`
}
require.NoError(t, json.Unmarshal([]byte(f.Run("help", c.alias, "--format", "json")), &data))
assert.Equal(t, c.command, data.Name)
})
}

help := f.Run("help", "snapshots")
assert.Contains(t, help, "Aliases: backups")
assert.NotContains(t, help, "snapshot")

assert.NotContains(t, f.Run("list", "backup"), "snapshot")

// Namespaces only used by hidden aliases are not listed.
_, stdErr, err := f.RunCombinedOutput("snapshot")
assert.Error(t, err)
assert.Contains(t, stdErr, `Command "snapshot" is not defined.`)

var list struct {
Commands []struct {
Name string `json:"name"`
HiddenAliases []string `json:"hidden_aliases"`
} `json:"commands"`
}
require.NoError(t, json.Unmarshal([]byte(f.Run("list", "--format", "json")), &list))
var found bool
for _, c := range list.Commands {
if c.Name == "backup:list" {
found = true
assert.Equal(t, []string{"snapshots", "snapshot:list"}, c.HiddenAliases)
}
}
assert.True(t, found)
}
32 changes: 1 addition & 31 deletions legacy/phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -282,12 +282,6 @@ parameters:
count: 1
path: src/Application.php

-
message: '#^Parameter \#1 \$value of class Symfony\\Component\\Console\\Completion\\Suggestion constructor expects string, mixed given\.$#'
identifier: argument.type
count: 1
path: src/Application.php

-
message: '#^Access to an undefined property Platformsh\\Client\\Model\\ApiResourceBase\:\:\$created_at\.$#'
identifier: property.notFound
Expand Down Expand Up @@ -1056,24 +1050,6 @@ parameters:
count: 1
path: src/Command/CommandBase.php

-
message: '#^Method Platformsh\\Cli\\Command\\CommandBase\:\:getVisibleAliases\(\) should return array\<string\> but returns array\.$#'
identifier: return.type
count: 1
path: src/Command/CommandBase.php

-
message: '#^Parameter \#1 \$aliases of method Symfony\\Component\\Console\\Command\\Command\:\:setAliases\(\) expects iterable\<string\>, array given\.$#'
identifier: argument.type
count: 1
path: src/Command/CommandBase.php

-
message: '#^Parameter \#1 \$array of function array_diff expects an array of values castable to string, array given\.$#'
identifier: argument.type
count: 1
path: src/Command/CommandBase.php

-
message: '#^Parameter \#1 \$array of function array_intersect expects array, mixed given\.$#'
identifier: argument.type
Expand Down Expand Up @@ -4716,12 +4692,6 @@ parameters:
count: 1
path: src/Console/CustomMarkdownDescriptor.php

-
message: '#^Parameter \#2 \$array of function implode expects array\<string\>, array given\.$#'
identifier: argument.type
count: 1
path: src/Console/CustomMarkdownDescriptor.php

-
message: '#^Parameter \#2 \$namespace of method Platformsh\\Cli\\Console\\DescriptorUtils\:\:describeNamespaces\(\) expects string\|null, mixed given\.$#'
identifier: argument.type
Expand Down Expand Up @@ -4821,7 +4791,7 @@ parameters:
-
message: '#^Parameter \#2 \$array of function implode expects array\<string\>, array given\.$#'
identifier: argument.type
count: 2
count: 1
path: src/Console/CustomTextDescriptor.php

-
Expand Down
11 changes: 11 additions & 0 deletions legacy/phpstan-stubs/Command.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Symfony\Component\Console\Command;

class Command
{
/**
* @return string[]
*/
public function getAliases(): array {}
}
1 change: 1 addition & 0 deletions legacy/phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ parameters:
- dist/installer.php
stubFiles:
- phpstan-stubs/Activity.stub
- phpstan-stubs/Command.stub

includes:
- phpstan-baseline.neon
53 changes: 46 additions & 7 deletions legacy/src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
use Platformsh\Cli\Command\WelcomeCommand;
use Platformsh\Cli\Command\MultiAwareInterface;
use Platformsh\Cli\Console\EventSubscriber;
use Platformsh\Cli\Console\HiddenAliasesCommandLoader;
use Platformsh\Cli\Console\HiddenAliasesPass;
use Platformsh\Cli\Console\HiddenInputOption;
use Platformsh\Cli\Service\Config;
use Platformsh\Cli\Service\LegacyMigration;
Expand Down Expand Up @@ -54,6 +56,11 @@ class Application extends ParentApplication

private bool $runningViaMulti = false;

/** @var string[] */
private array $hiddenAliases = [];

private ?CommandLoaderInterface $commandLoader = null;

public function __construct(?Config $config = null)
{
// Initialize configuration (from config.yaml).
Expand All @@ -75,7 +82,13 @@ public function __construct(?Config $config = null)
// services tagged with "console.command").
/** @var CommandLoaderInterface $loader */
$loader = $this->container()->get('console.command_loader');
$this->setCommandLoader($loader);
/** @var string[] $hiddenAliases */
$hiddenAliases = $this->container()->hasParameter(HiddenAliasesPass::PARAMETER)
? $this->container()->getParameter(HiddenAliasesPass::PARAMETER)
: [];
$this->hiddenAliases = $hiddenAliases;
$this->commandLoader = new HiddenAliasesCommandLoader($loader, $hiddenAliases);
$this->setCommandLoader($this->commandLoader);

// Set "welcome" as the default command.
$this->setDefaultCommand(WelcomeCommand::getDefaultName());
Expand Down Expand Up @@ -110,15 +123,39 @@ public static function warmCaches(): void
* {@inheritdoc}
*
* Prevent commands being enabled, according to config.yaml configuration.
*
* Hidden aliases are removed, so that they are not used for abbreviations
* or namespaces.
*
* @see self::get()
*/
public function add(ConsoleCommand $command): ?ConsoleCommand
public function addCommand(callable|ConsoleCommand $command): ?ConsoleCommand
{
if (!$this->config->isCommandEnabled($command->getName())) {
$command->setApplication(null);
return null;
if ($command instanceof ConsoleCommand) {
if (!$this->config->isCommandEnabled($command->getName())) {
$command->setApplication(null);
return null;
}
if (array_intersect($command->getAliases(), $this->hiddenAliases)) {
$command->setAliases(array_values(array_diff($command->getAliases(), $this->hiddenAliases)));
}
}

return parent::addCommand($command);
}

/**
* {@inheritdoc}
*
* Gets commands by their hidden aliases, which only work in full.
*/
public function get(string $name): ConsoleCommand
{
if (in_array($name, $this->hiddenAliases, true) && $this->commandLoader?->has($name)) {
$name = $this->commandLoader->get($name)->getName() ?? $name;
}

return parent::add($command);
return parent::get($name);
}

/**
Expand Down Expand Up @@ -153,6 +190,7 @@ private function container(bool $recompile = false): ContainerInterface
$e->getMessage(),
));
}
$this->container->addCompilerPass(new HiddenAliasesPass());
$this->container->addCompilerPass(new AddConsoleCommandPass());
$this->container->compile();
$dumper = new PhpDumper($this->container);
Expand Down Expand Up @@ -231,7 +269,8 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti
continue;
}
$suggestions->suggestValue(new Suggestion($name, $command->getDescription()));
foreach ($command->getAliases() as $alias) {
$aliases = $command instanceof CommandBase ? $command->getVisibleAliases() : $command->getAliases();
foreach ($aliases as $alias) {
$suggestions->suggestValue(new Suggestion($alias, $command->getDescription()));
}
}
Expand Down
3 changes: 2 additions & 1 deletion legacy/src/Command/Backup/BackupCreateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Platformsh\Cli\Console\HiddenAliases;

#[AsCommand(name: 'backup:create', description: 'Make a backup of an environment', aliases: ['backup'])]
#[HiddenAliases(['snapshot:create', 'environment:backup'])]
class BackupCreateCommand extends CommandBase
{
public function __construct(private readonly ActivityMonitor $activityMonitor, private readonly Api $api, private readonly Config $config, private readonly Io $io, private readonly QuestionHelper $questionHelper, private readonly Selector $selector)
Expand All @@ -46,7 +48,6 @@ protected function configure(): void
$this->addCompleter($this->selector);
$this->activityMonitor->addWaitOptions($this->getDefinition());
$this->addHiddenOption('unsafe', null, InputOption::VALUE_NONE, 'Deprecated option: use --live instead');
$this->setHiddenAliases(['snapshot:create', 'environment:backup']);
$this->addExample('Make a backup of the current environment');
$this->addExample('Request a backup (and exit quickly)', '--no-wait');
$this->addExample('Make a backup avoiding downtime (but risking inconsistency)', '--live');
Expand Down
3 changes: 2 additions & 1 deletion legacy/src/Command/Backup/BackupListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Platformsh\Cli\Console\HiddenAliases;

#[AsCommand(name: 'backup:list', description: 'List available backups of an environment', aliases: ['backups'])]
#[HiddenAliases(['snapshots', 'snapshot:list'])]
class BackupListCommand extends CommandBase
{
/** @var array<string, string> */
Expand Down Expand Up @@ -50,7 +52,6 @@ protected function configure(): void
$this->selector->addProjectOption($this->getDefinition());
$this->selector->addEnvironmentOption($this->getDefinition());
$this->addCompleter($this->selector);
$this->setHiddenAliases(['snapshots', 'snapshot:list']);
$this->addExample('Display backups including the "live" and "commit_id" columns', '-c+live,commit_id');
}

Expand Down
3 changes: 2 additions & 1 deletion legacy/src/Command/Backup/BackupRestoreCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Platformsh\Cli\Console\HiddenAliases;

#[AsCommand(name: 'backup:restore', description: 'Restore an environment backup')]
#[HiddenAliases(['environment:restore', 'snapshot:restore'])]
class BackupRestoreCommand extends CommandBase
{
/** @var string[] */
Expand All @@ -49,7 +51,6 @@ protected function configure(): void
$this->selector->addEnvironmentOption($this->getDefinition());
$this->addCompleter($this->selector);
$this->activityMonitor->addWaitOptions($this->getDefinition());
$this->setHiddenAliases(['environment:restore', 'snapshot:restore']);
$this->addExample('Restore the most recent backup');
$this->addExample('Restore a specific backup', '92c9a4b2aa75422efb3d');
}
Expand Down
8 changes: 2 additions & 6 deletions legacy/src/Command/ClearCacheCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,16 @@
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Platformsh\Cli\Console\HiddenAliases;

#[AsCommand(name: 'clear-cache', description: 'Clear the CLI cache', aliases: ['cc'])]
#[HiddenAliases(['clearcache'])]
class ClearCacheCommand extends CommandBase
{
public function __construct(private readonly CacheProvider $cacheProvider)
{
parent::__construct();
}
protected function configure(): void
{
$this
->setHiddenAliases(['clearcache']);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$cache = $this->cacheProvider;
Expand Down
24 changes: 7 additions & 17 deletions legacy/src/Command/CommandBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Platformsh\Cli\Command;

use Platformsh\Cli\Console\CompleterInterface;
use Platformsh\Cli\Console\HiddenAliases;
use Platformsh\Cli\Console\HiddenInputOption;
use Platformsh\Cli\Service\Config;
use Symfony\Component\Console\Command\Command;
Expand Down Expand Up @@ -42,12 +43,6 @@ abstract class CommandBase extends Command implements MultiAwareInterface
protected bool $canBeRunMultipleTimes = true;
protected bool $runningViaMulti = false;

/**
* @var string[]
* @see self::setHiddenAliases()
*/
private array $hiddenAliases = [];

/**
* The command synopsis.
* @var array<string, string>
Expand Down Expand Up @@ -117,20 +112,15 @@ protected function addHiddenOption(string $name, ?string $shortcut = null, ?int
}

/**
* Add aliases that should be hidden from help.
*
* @see parent::setAliases()
* Get aliases that should be hidden from help.
*
* @param string[] $hiddenAliases
*
* @return static
* @return string[]
*/
protected function setHiddenAliases(array $hiddenAliases): static
public function getHiddenAliases(): array
{
$this->hiddenAliases = $hiddenAliases;
$this->setAliases(array_merge($this->getAliases(), $hiddenAliases));
$attribute = (new \ReflectionClass($this))->getAttributes(HiddenAliases::class)[0] ?? null;

return $this;
return $attribute?->newInstance()->aliases ?? [];
}

/**
Expand All @@ -140,7 +130,7 @@ protected function setHiddenAliases(array $hiddenAliases): static
*/
public function getVisibleAliases(): array
{
return array_diff($this->getAliases(), $this->hiddenAliases);
return array_values(array_diff($this->getAliases(), $this->getHiddenAliases()));
}

/**
Expand Down
Loading
Loading