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

Allow running only actions specified on the command line #137

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

ramsey
Copy link
Contributor

@ramsey ramsey commented May 31, 2021

This PR adds 3 new hook command options:

  • --list-actions
  • --action
  • --disable-plugins

--list-actions

This option allows you to list all the configured actions for a given hook:

$ ./bin/captainhook hook:pre-commit --list-actions
Listing pre-commit actions:
 - \CaptainHook\App\Hook\PHP\Action\Linting
 - tools/phpcs --standard=psr12 src
 - \CaptainHook\App\Hook\PHP\Action\TestCoverage
 - \CaptainHook\App\Hook\Composer\Action\CheckLockFile

--action

This option allows you to pass one or more actions to execute for a given hook. All other actions configured for the hook are skipped.

$ ./bin/captainhook hook:pre-commit --action="tools/phpcs --standard=psr12 src" --action="\CaptainHook\App\Hook\Composer\Action\CheckLockFile"
pre-commit: 
 - \CaptainHook\App\Hook\PHP\Action\Linting                          : skipped
 - tools/phpcs --standard=psr12 src                                  : done
 - \CaptainHook\App\Hook\PHP\Action\TestCoverage                     : skipped
 - \CaptainHook\App\Hook\Composer\Action\CheckLockFile               : done

--disable-plugins

This option is useful for combining with the --action option to disable any plugins while executing only the specified actions.

$ ./bin/captainhook hook:pre-commit --disable-plugins --action="tools/phpcs --standard=psr12 src" --action="\CaptainHook\App\Hook\Composer\Action\CheckLockFile"
pre-commit: 
Running with plugins disabled
 - \CaptainHook\App\Hook\PHP\Action\Linting                          : skipped
 - tools/phpcs --standard=psr12 src                                  : done
 - \CaptainHook\App\Hook\PHP\Action\TestCoverage                     : skipped
 - \CaptainHook\App\Hook\Composer\Action\CheckLockFile               : done

Rationale

As discussed in #122, I'm working on a DisallowActionChanges plugin that will extend the PreserveWorkingTree plugin to check whether any actions make changes and, if so, fail. As part of the failure message, I want to list the actions that made changes and instruct the user with a message like this:

Use the following to run this action and inspect the changes it makes:

  captainhook hook:pre-commit --disable-plugins --action="tools/phpcs --standard=psr12 src"

The --list-actions option is a helper option that I thought to include so a user can quickly list all actions configured for a hook. The names of the actions it lists are the same names that you would pass to the --action option.

IO::getOptions() and IO::getOption()

I've added these two methods to allow retrieving options from IO, just like you can retrieve arguments. However, I did not add these to the IO interface, since that would likely require a major version bump. For now, they're only on Console\IO\DefaultIO and Console\IO\Base. They should also be added to the Console\IO interface in version 6.0.0.

// Otherwise, use the verbosity setting from the configuration.
if (!$input->hasOption('verbose') || !$input->getOption('verbose')) {
$output->setVerbosity(IOUtil::mapConfigVerbosity($config->getVerbosity()));
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this because I found that captainhook didn't respect the verbosity option (i.e., -v, -vv, and -vvv) passed on the command line. It was always setting the output to whatever was configured in captainhook.json.

IMO, if you pass verbosity on the command line, it should override whatever is configured in captainhook.json.

@@ -92,6 +92,7 @@ public function execute(Config $config, IO $io, Repository $repository, Config\A
* Setup the action, reading and validating all config settings
*
* @param \CaptainHook\App\Config\Options $options
* @codeCoverageIgnore
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method does nothing here. It only has a comment in it, but it was showing up in coverage reports as an uncovered method.

public function isEnabled(): bool
{
return $this->isAnyConfigEnabled($this->getHookConfigsToHandle());
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows the hook command class access to know whether the hook is enabled.

public function getActions(): array
{
return $this->getActionsToExecute($this->getHookConfigsToHandle());
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows the hook command class access to the list of actions configured for the hook, so it may use these to list the actions, when --list-actions is provided.

$this->io->write(' - <fg=blue>' . $this->formatActionOutput($action->getAction()) . '</> : ', false);

if (!$this->doConditionsApply($action->getConditions())) {
$this->io->write('<comment>skipped</comment>', true);
if ($this->checkSkipAction($action)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this to reduce the amount of code in the handleAction() method and, hopefully, to simplify the logic, so it's easier to read and understand.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -114,12 +114,19 @@ public function run(): void
{
$hookConfigs = $this->getHookConfigsToHandle();

$this->io->write('<comment>' . $this->hook . ':</comment> ');
/** @var IO\Base $io */
$io = $this->io;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for the benefit of PHPStan, since getOption() is not on the Console\IO interface.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the getOption method. I think it would be better to handle the option stuff on the outside (inside the Command) and set an explicit setting on the runner with $runner->setActions or $runner->disablePlugins ...
This way we could get rid of the option methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll give this a try. I didn't like adding the getOption() method. 🙂

private function cliSkipAction(Config\Action $action): bool
{
/** @var IO\Base $io */
$io = $this->io;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for the benefit of PHPStan, since getOption() is not on the Console\IO interface.

@@ -449,10 +517,17 @@ private function getHookPlugins(): array
*/
private function executeHookPluginsFor(string $method, ?Config\Action $action = null): void
{
/** @var IO\Base $io */
$io = $this->io;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for the benefit of PHPStan, since getOption() is not on the Console\IO interface.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will not need this if we are not using the IO to make decision inside Runner
See previous comment on src/Runner/Hook.php

@sebastianfeldmann sebastianfeldmann self-assigned this May 31, 2021
Comment on lines +96 to +102
if ($input->getOption('list-actions') === true) {
foreach ($this->getDefinition()->getArguments() as $arg) {
if ($arg->isRequired() && $input->getArgument($arg->getName()) === null) {
$input->setArgument($arg->getName(), '');
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about creating three separate commands (e.g. hook:pre-commit:list-actions) instead of adding those as options with need for this magic?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of separating the list functionality from the execution functionality
I think one command is enough captainhook list-actions pre-commit :)
Then this can be done in a separate Runner\ListActions
Maybe we can move the getHookConfigsToHandle functionality to the Config class. This way we would not have duplicate code in Runner\Hook and Runner\ListActions

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ramsey have you seen this comment as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds good to me.

Copy link
Collaborator

@sebastianfeldmann sebastianfeldmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty nice, the only thing to discuss would be the ->getOption stuff.

@@ -114,12 +114,19 @@ public function run(): void
{
$hookConfigs = $this->getHookConfigsToHandle();

$this->io->write('<comment>' . $this->hook . ':</comment> ');
/** @var IO\Base $io */
$io = $this->io;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the getOption method. I think it would be better to handle the option stuff on the outside (inside the Command) and set an explicit setting on the runner with $runner->setActions or $runner->disablePlugins ...
This way we could get rid of the option methods.

$this->io->write(' - <fg=blue>' . $this->formatActionOutput($action->getAction()) . '</> : ', false);

if (!$this->doConditionsApply($action->getConditions())) {
$this->io->write('<comment>skipped</comment>', true);
if ($this->checkSkipAction($action)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants