diff --git a/doc/tasks.md b/doc/tasks.md index 4618d83bf..3984770e0 100644 --- a/doc/tasks.md +++ b/doc/tasks.md @@ -22,6 +22,7 @@ parameters: gulp: ~ jsonlint: ~ make: ~ + npm_script: ~ phing: ~ php7cc: ~ phpcpd: ~ @@ -58,6 +59,7 @@ Every task has it's own default configuration. It is possible to overwrite the p - [Gulp](tasks/gulp.md) - [JsonLint](tasks/jsonlint.md) - [Make](tasks/make.md) +- [NPM script](tasks/npm_script.md) - [Phing](tasks/phing.md) - [Php7cc](tasks/php7cc.md) - [PhpCpd](tasks/phpcpd.md) diff --git a/doc/tasks/npm_script.md b/doc/tasks/npm_script.md new file mode 100644 index 000000000..44ecbfa11 --- /dev/null +++ b/doc/tasks/npm_script.md @@ -0,0 +1,41 @@ +# NPM script + +The NPM script task will run your configured npm script. +It lives under the `npm_script` namespace and has following configurable parameters: + +```yaml +# grumphp.yml +parameters: + tasks: + npm_script: + script: ~ + triggered_by: [js, jsx, coffee, ts, less, sass, scss] + working_directory: "./" +``` + +**script** + +*Default: null* + +This option specifies which NPM script you want to run. +This option is set to null by default. +This means that grumphp will stop immediately. +Note that this script should be used to verify things. +It is also possible to alter code during commit, +but this is surely NOT recommended! + + +**triggered_by** + +*Default: [js, jsx, coffee, ts, less, sass, scss]* + +This option will specify which file extensions will trigger the NPM script. +By default NPM script will be triggered by altering any file. +You can overwrite this option to whatever file you want to use! + + +**working_directory** + +*Default: "./"* + +This option specifies in which directory the NPM script should be run. diff --git a/resources/config/tasks.yml b/resources/config/tasks.yml index 96f401618..217ba0ac8 100644 --- a/resources/config/tasks.yml +++ b/resources/config/tasks.yml @@ -121,6 +121,15 @@ services: tags: - {name: grumphp.task, config: make} + task.npm.script: + class: GrumPHP\Task\NpmScript + arguments: + - '@config' + - '@process_builder' + - '@formatter.raw_process' + tags: + - {name: grumphp.task, config: npm_script} + task.phing: class: GrumPHP\Task\Phing arguments: diff --git a/spec/GrumPHP/Task/NpmScriptSpec.php b/spec/GrumPHP/Task/NpmScriptSpec.php new file mode 100644 index 000000000..0ef795e62 --- /dev/null +++ b/spec/GrumPHP/Task/NpmScriptSpec.php @@ -0,0 +1,111 @@ +getTaskConfiguration('npm_script')->willReturn(array('script' => 'test', 'working_directory' => './')); + $this->beConstructedWith($grumPHP, $processBuilder, $formatter); + } + + function it_is_initializable() + { + $this->shouldHaveType('GrumPHP\Task\NpmScript'); + } + + function it_should_have_a_name() + { + $this->getName()->shouldBe('npm_script'); + } + + function it_should_have_configurable_options() + { + $options = $this->getConfigurableOptions(); + $options->shouldBeAnInstanceOf('Symfony\Component\OptionsResolver\OptionsResolver'); + $options->getDefinedOptions()->shouldContain('script'); + $options->getDefinedOptions()->shouldContain('triggered_by'); + $options->getDefinedOptions()->shouldContain('working_directory'); + } + + function it_should_run_in_git_pre_commit_context(GitPreCommitContext $context) + { + $this->canRunInContext($context)->shouldReturn(true); + } + + function it_should_run_in_run_context(RunContext $context) + { + $this->canRunInContext($context)->shouldReturn(true); + } + + function it_does_not_do_anything_if_there_are_no_files(ProcessBuilder $processBuilder, ContextInterface $context) + { + $processBuilder->createArgumentsForCommand('npm')->shouldNotBeCalled(); + $processBuilder->buildProcess()->shouldNotBeCalled(); + $context->getFiles()->willReturn(new FilesCollection()); + + $result = $this->run($context); + $result->shouldBeAnInstanceOf('GrumPHP\Runner\TaskResultInterface'); + $result->getResultCode()->shouldBe(TaskResult::SKIPPED); + } + + function it_runs_the_suite(ProcessBuilder $processBuilder, Process $process, ContextInterface $context) + { + $arguments = new ProcessArgumentsCollection(); + $processBuilder->createArgumentsForCommand('npm')->willReturn($arguments); + $processBuilder->buildProcess($arguments)->willReturn($process); + + $process->run()->shouldBeCalled(); + $process->setWorkingDirectory(getcwd())->shouldBeCalled(); + $process->isSuccessful()->willReturn(true); + + $context->getFiles()->willReturn(new FilesCollection(array( + new SplFileInfo('test.js', '.', 'test.js') + ))); + + $result = $this->run($context); + $result->shouldBeAnInstanceOf('GrumPHP\Runner\TaskResultInterface'); + $result->isPassed()->shouldBe(true); + } + + function it_throws_exception_if_the_process_fails( + ProcessBuilder $processBuilder, + Process $process, + ContextInterface $context + ) { + $arguments = new ProcessArgumentsCollection(); + $processBuilder->createArgumentsForCommand('npm')->willReturn($arguments); + $processBuilder->buildProcess($arguments)->willReturn($process); + + $process->run()->shouldBeCalled(); + $process->setWorkingDirectory(getcwd())->shouldBeCalled(); + $process->isSuccessful()->willReturn(false); + + $context->getFiles()->willReturn(new FilesCollection(array( + new SplFileInfo('test.js', '.', 'test.js') + ))); + + $result = $this->run($context); + $result->shouldBeAnInstanceOf('GrumPHP\Runner\TaskResultInterface'); + $result->isPassed()->shouldBe(false); + } +} diff --git a/src/GrumPHP/Task/NpmScript.php b/src/GrumPHP/Task/NpmScript.php new file mode 100644 index 000000000..babf22365 --- /dev/null +++ b/src/GrumPHP/Task/NpmScript.php @@ -0,0 +1,75 @@ +setDefaults(array( + 'script' => null, + 'triggered_by' => array('js', 'jsx', 'coffee', 'ts', 'less', 'sass', 'scss'), + 'working_directory' => './', + )); + + $resolver->addAllowedTypes('script', array('string')); + $resolver->addAllowedTypes('triggered_by', array('array')); + $resolver->addAllowedTypes('working_directory', array('string')); + + return $resolver; + } + + /** + * {@inheritdoc} + */ + public function canRunInContext(ContextInterface $context) + { + return ($context instanceof GitPreCommitContext || $context instanceof RunContext); + } + + /** + * {@inheritdoc} + */ + public function run(ContextInterface $context) + { + $config = $this->getConfiguration(); + $files = $context->getFiles()->extensions($config['triggered_by']); + if (0 === count($files)) { + return TaskResult::createSkipped($this, $context); + } + + $arguments = $this->processBuilder->createArgumentsForCommand('npm'); + $arguments->addRequiredArgument('%s', $config['script']); + + $process = $this->processBuilder->buildProcess($arguments); + $process->setWorkingDirectory(realpath($config['working_directory'])); + $process->run(); + + if (!$process->isSuccessful()) { + return TaskResult::createFailed($this, $context, $this->formatter->format($process)); + } + + return TaskResult::createPassed($this, $context); + } +}