├── .editorconfig ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── RoboFile.php ├── codeception.yml ├── composer.json ├── composer.lock ├── data └── Task │ └── Development │ └── GeneratedWrapper.tmpl ├── dependencies.yml ├── phpunit.xml.dist ├── robo ├── robo.yml └── src ├── Application.php ├── ClassDiscovery ├── AbstractClassDiscovery.php ├── ClassDiscoveryInterface.php └── RelativeNamespaceDiscovery.php ├── Collection ├── CallableTask.php ├── Collection.php ├── CollectionBuilder.php ├── CollectionInterface.php ├── CollectionProcessHook.php ├── CompletionWrapper.php ├── Element.php ├── NestedCollectionInterface.php ├── TaskForEach.php ├── Tasks.php └── Temporary.php ├── Common ├── BuilderAwareTrait.php ├── CommandArguments.php ├── CommandReceiver.php ├── ConfigAwareTrait.php ├── DynamicParams.php ├── ExecCommand.php ├── ExecOneCommand.php ├── ExecTrait.php ├── IO.php ├── InflectionTrait.php ├── InputAwareTrait.php ├── OutputAdapter.php ├── OutputAwareTrait.php ├── ProcessExecutor.php ├── ProcessUtils.php ├── ProgressIndicator.php ├── ProgressIndicatorAwareTrait.php ├── ResourceExistenceChecker.php ├── TaskIO.php ├── TimeKeeper.php ├── Timer.php └── VerbosityThresholdTrait.php ├── Config.php ├── Config ├── Config.php └── GlobalOptionDefaultValuesInterface.php ├── Contract ├── BuilderAwareInterface.php ├── CommandInterface.php ├── CompletionInterface.php ├── ConfigAwareInterface.php ├── IOAwareInterface.php ├── InflectionInterface.php ├── OutputAdapterInterface.php ├── OutputAwareInterface.php ├── PrintedInterface.php ├── ProgressIndicatorAwareInterface.php ├── ProgressInterface.php ├── RollbackInterface.php ├── SimulatedInterface.php ├── TaskInterface.php ├── VerbosityThresholdInterface.php └── WrappedTaskInterface.php ├── Exception ├── AbortTasksException.php ├── TaskException.php └── TaskExitException.php ├── GlobalOptionsEventListener.php ├── LoadAllTasks.php ├── Log ├── ResultPrinter.php ├── RoboLogLevel.php ├── RoboLogStyle.php └── RoboLogger.php ├── Result.php ├── ResultData.php ├── Robo.php ├── Runner.php ├── Runtime └── Runner.php ├── State ├── Consumer.php ├── Data.php ├── StateAwareInterface.php └── StateAwareTrait.php ├── Symfony ├── ConsoleIO.php ├── ConsoleIOInjector.php └── SymfonyStyleInjector.php ├── Task ├── ApiGen │ ├── ApiGen.php │ └── Tasks.php ├── Archive │ ├── Extract.php │ ├── Pack.php │ └── Tasks.php ├── Assets │ ├── CssPreprocessor.php │ ├── ImageMinify.php │ ├── Less.php │ ├── Minify.php │ ├── Scss.php │ └── Tasks.php ├── Base │ ├── Exec.php │ ├── ExecStack.php │ ├── ParallelExec.php │ ├── Shortcuts.php │ ├── SymfonyCommand.php │ ├── Tasks.php │ └── Watch.php ├── BaseTask.php ├── Bower │ ├── Base.php │ ├── Install.php │ ├── Tasks.php │ └── Update.php ├── CommandStack.php ├── Composer │ ├── Base.php │ ├── CheckPlatformReqs.php │ ├── Config.php │ ├── CreateProject.php │ ├── DumpAutoload.php │ ├── Init.php │ ├── Install.php │ ├── Remove.php │ ├── RequireDependency.php │ ├── Tasks.php │ ├── Update.php │ └── Validate.php ├── Development │ ├── Changelog.php │ ├── GenerateMarkdownDoc.php │ ├── GenerateTask.php │ ├── GitHub.php │ ├── GitHubRelease.php │ ├── OpenBrowser.php │ ├── PackPhar.php │ ├── PhpServer.php │ ├── SemVer.php │ └── Tasks.php ├── Docker │ ├── Base.php │ ├── Build.php │ ├── Commit.php │ ├── Exec.php │ ├── Pull.php │ ├── Remove.php │ ├── Result.php │ ├── Run.php │ ├── Start.php │ ├── Stop.php │ └── Tasks.php ├── File │ ├── Concat.php │ ├── Replace.php │ ├── Tasks.php │ ├── TmpFile.php │ └── Write.php ├── Filesystem │ ├── BaseDir.php │ ├── CleanDir.php │ ├── CopyDir.php │ ├── DeleteDir.php │ ├── FilesystemStack.php │ ├── FlattenDir.php │ ├── MirrorDir.php │ ├── Shortcuts.php │ ├── Tasks.php │ ├── TmpDir.php │ └── WorkDir.php ├── Gulp │ ├── Base.php │ ├── Run.php │ └── Tasks.php ├── Logfile │ ├── BaseLogfile.php │ ├── RotateLog.php │ ├── Shortcuts.php │ ├── Tasks.php │ └── TruncateLog.php ├── Npm │ ├── Base.php │ ├── Install.php │ ├── Tasks.php │ └── Update.php ├── Remote │ ├── Rsync.php │ ├── Ssh.php │ └── Tasks.php ├── Simulator.php ├── StackBasedTask.php ├── Testing │ ├── Atoum.php │ ├── Behat.php │ ├── Codecept.php │ ├── PHPUnit.php │ ├── Phpspec.php │ └── Tasks.php └── Vcs │ ├── GitStack.php │ ├── HgStack.php │ ├── Shortcuts.php │ ├── SvnStack.php │ └── Tasks.php ├── TaskAccessor.php ├── TaskInfo.php └── Tasks.php /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [**.php] 13 | indent_style = space 14 | indent_size = 4 15 | 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Robo 2 | 3 | Thank you for your interest in contributing to Robo! Here are some of the guidelines you should follow to make the most of your efforts: 4 | 5 | ## Code Style Guidelines 6 | 7 | Robo adheres to the [PSR-2 Coding Style Guide](https://www.php-fig.org/psr/psr-2/) for PHP code. An `.editorconfig` file is included with the repository to help you get up and running quickly. Most modern editors support this standard, but if yours does not or you would like to configure your editor manually, follow the guidelines in the document linked above. 8 | 9 | You can run the PHP Codesniffer on your work using a convenient command built into this project's own `RoboFile.php`: 10 | ``` 11 | robo sniff src/Foo.php --autofix 12 | ``` 13 | The above will run the PHP Codesniffer on the `src/Foo.php` file and automatically correct variances from the PSR-2 standard. Please ensure all contributions are compliant _before_ submitting a pull request. 14 | 15 | ## Tests 16 | 17 | Note that in the past, Robo used Codeception / Aspect Mock etc. in its unit tests. These components proved to be difficult to maintain when testing on mutiple PHP versions, so they were removed. The tests formerly in tests/cli were all ported to straight phpunit tests in the tests/integration directory. Some of the unit tests from tests/unit were ported to tests/phpunit; however, a number of tests that still use AspectMock still exist in tests/unit, although these are not currently being used. 18 | 19 | Pull requests that touch parts of the code formerly tested by these disabled tests must also convert the AspectMock test to Prophecy or some other mocking system. Alternately, getting AspectMock working again on the master and 1.x branches is another option, if someone wants to stand up to do that work. 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2021 Codegyre Developers Team, Consolidation Team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | DEPENDENCY LICENSES: 23 | 24 | Name Version License 25 | consolidation/annotated-command 4.2.3 MIT 26 | consolidation/config 2.0.0 MIT 27 | consolidation/log 2.0.1 MIT 28 | consolidation/output-formatters 4.1.1 MIT 29 | consolidation/self-update 1.2.0 MIT 30 | dflydev/dot-access-data v1.1.0 MIT 31 | grasmash/expander 1.0.0 MIT 32 | league/container 3.3.3 MIT 33 | psr/container 1.0.0 MIT 34 | psr/log 1.1.3 MIT 35 | symfony/console v4.4.14 MIT 36 | symfony/event-dispatcher v4.4.14 MIT 37 | symfony/event-dispatcher-contracts v1.1.9 MIT 38 | symfony/filesystem v4.4.14 MIT 39 | symfony/finder v4.4.14 MIT 40 | symfony/polyfill-ctype v1.18.1 MIT 41 | symfony/polyfill-mbstring v1.18.1 MIT 42 | symfony/polyfill-php73 v1.18.1 MIT 43 | symfony/polyfill-php80 v1.18.1 MIT 44 | symfony/process v4.4.14 MIT 45 | symfony/service-contracts v1.1.9 MIT 46 | -------------------------------------------------------------------------------- /codeception.yml: -------------------------------------------------------------------------------- 1 | actor: Guy 2 | paths: 3 | tests: tests 4 | log: tests/_log 5 | data: tests/_data 6 | helpers: tests/_helpers 7 | bootstrap: _bootstrap.php 8 | settings: 9 | colors: true 10 | memory_limit: 1024M 11 | modules: 12 | config: 13 | Db: 14 | dsn: '' 15 | user: '' 16 | password: '' 17 | dump: tests/_data/dump.sql 18 | coverage: 19 | enabled: true 20 | include: 21 | - src/* 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "consolidation/robo", 3 | "description": "Modern task runner", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Davert", 8 | "email": "davert.php@resend.cc" 9 | } 10 | ], 11 | "autoload": { 12 | "psr-4": { 13 | "Robo\\": "src" 14 | } 15 | }, 16 | "autoload-dev": { 17 | "psr-4": { 18 | "Robo\\": "tests/src", 19 | "RoboExample\\": "examples/src" 20 | } 21 | }, 22 | "bin": [ 23 | "robo" 24 | ], 25 | "require": { 26 | "php": ">=8.2", 27 | "consolidation/annotated-command": "^4.8.1", 28 | "consolidation/config": "^3", 29 | "consolidation/log": "^3", 30 | "consolidation/output-formatters": "^4.1.2", 31 | "league/container": "^3.3.1 || ^4.0", 32 | "phpowermove/docblock": "^4.0", 33 | "symfony/console": "^6 || ^7", 34 | "symfony/event-dispatcher": "^6 || ^7", 35 | "symfony/filesystem": "^6 || ^7", 36 | "symfony/finder": "^6 || ^7", 37 | "symfony/process": "^6 || ^7", 38 | "symfony/yaml": "^6 || ^7" 39 | }, 40 | "require-dev": { 41 | "natxet/cssmin": "3.0.4", 42 | "patchwork/jsqueeze": "^2", 43 | "pear/archive_tar": "^1.4.4", 44 | "squizlabs/php_codesniffer": "^3.6", 45 | "phpunit/phpunit": "^7.5.20 || ^8 || ^9", 46 | "yoast/phpunit-polyfills": "^0.2.0" 47 | }, 48 | "scripts": { 49 | "cs": "./robo sniff", 50 | "unit": "phpunit", 51 | "lint": "find src tests/src -name '*.php' -print0 | xargs -0 -n1 -P4 -- php -l", 52 | "test": [ 53 | "@lint", 54 | "@unit", 55 | "@cs" 56 | ], 57 | "phpdoc": "build/tools/phpdoc", 58 | "install-tools": [ 59 | "if [[ ! -f build/tools/phpdoc ]] ; then mkdir -p build/tools && wget --output-document=build/tools/phpdoc https://phpdoc.org/phpDocumentor.phar && chmod +x build/tools/phpdoc; fi" 60 | ] 61 | }, 62 | "config": { 63 | "optimize-autoloader": true, 64 | "sort-packages": true, 65 | "platform": { 66 | "php": "8.2.18" 67 | } 68 | }, 69 | "suggest": { 70 | "pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively.", 71 | "totten/lurkerlite": "For monitoring filesystem changes in taskWatch", 72 | "patchwork/jsqueeze": "For minifying JS files in taskMinify", 73 | "natxet/cssmin": "For minifying CSS files in taskMinify", 74 | "consolidation/self-update": "For self-updating a phar-based app built with Robo" 75 | }, 76 | "conflict": { 77 | "codegyre/robo": "*" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /data/Task/Development/GeneratedWrapper.tmpl: -------------------------------------------------------------------------------- 1 | task{wrapperClassName}() 15 | * ... 16 | * ->run(); 17 | * 18 | * // one line 19 | * ... 20 | * 21 | * ?> 22 | * ``` 23 | * 24 | {methodList} 25 | */ 26 | class {wrapperClassName} extends StackBasedTask 27 | { 28 | protected $delegate; 29 | 30 | public function __construct() 31 | { 32 | $this->delegate = new {delegate}(); 33 | } 34 | 35 | protected function getDelegate() 36 | { 37 | return $this->delegate; 38 | }{immediateMethods}{methodImplementations} 39 | } 40 | -------------------------------------------------------------------------------- /dependencies.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | dependencies: 3 | - type: php 4 | settings: 5 | composer_options: "--no-dev" # used in "collection", overriden when actually making updates 6 | lockfile_updates: 7 | settings: 8 | composer_options: "" 9 | manifest_updates: 10 | settings: 11 | composer_options: "" 12 | filters: 13 | - name: ".*" 14 | versions: "L.Y.Y" 15 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | tests/integration 30 | 31 | 32 | tests/phpunit 33 | 34 | 35 | 36 | 37 | 38 | ./src 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /robo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 500000); 17 | 18 | // Non-phar autoloader paths 19 | $candidates = [ 20 | __DIR__.'/vendor/autoload.php', 21 | __DIR__.'/../../autoload.php', 22 | __DIR__.'/../autoload.php', 23 | ]; 24 | 25 | // Use our phar alias path 26 | if ($isPhar) { 27 | array_unshift($candidates, 'phar://robo.phar/vendor/autoload.php'); 28 | } 29 | 30 | $autoloaderPath = false; 31 | foreach ($candidates as $candidate) { 32 | if (file_exists($candidate)) { 33 | $autoloaderPath = $candidate; 34 | break; 35 | } 36 | } 37 | if (!$autoloaderPath) { 38 | die("Could not find autoloader. Run 'composer install'."); 39 | } 40 | $classLoader = require $autoloaderPath; 41 | $configFilePath = getenv('ROBO_CONFIG') ?: getenv('HOME') . '/.robo/robo.yml'; 42 | $runner = new \Robo\Runner(); 43 | $runner 44 | ->setRelativePluginNamespace('Robo\Plugin') 45 | ->setSelfUpdateRepository('consolidation/robo') 46 | ->setConfigurationFilename($configFilePath) 47 | ->setEnvConfigPrefix('ROBO') 48 | ->setClassLoader($classLoader); 49 | $statusCode = $runner->execute($_SERVER['argv']); 50 | exit($statusCode); 51 | -------------------------------------------------------------------------------- /robo.yml: -------------------------------------------------------------------------------- 1 | options: 2 | progress-delay: 2 3 | simulate: null 4 | command: 5 | try: 6 | config: 7 | options: 8 | opt: wow 9 | -------------------------------------------------------------------------------- /src/Application.php: -------------------------------------------------------------------------------- 1 | getDefinition() 22 | ->addOption( 23 | new InputOption('--simulate', null, InputOption::VALUE_NONE, 'Run in simulated mode (show what would have happened).') 24 | ); 25 | $this->getDefinition() 26 | ->addOption( 27 | new InputOption('--progress-delay', null, InputOption::VALUE_REQUIRED, 'Number of seconds before progress bar is displayed in long-running task collections. Default: 2s.', Config::DEFAULT_PROGRESS_DELAY) 28 | ); 29 | 30 | $this->getDefinition() 31 | ->addOption( 32 | new InputOption('--define', '-D', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Define a configuration item value.', []) 33 | ); 34 | } 35 | 36 | /** 37 | * @param string $roboFile 38 | * @param string $roboClass 39 | */ 40 | public function addInitRoboFileCommand($roboFile, $roboClass) 41 | { 42 | $createRoboFile = new Command('init'); 43 | $createRoboFile->setDescription("Intitalizes basic RoboFile in current dir"); 44 | $createRoboFile->setCode(function (InputInterface $input, OutputInterface $output) use ($roboClass, $roboFile) { 45 | $output->writeln(" ~~~ Welcome to Robo! ~~~~ "); 46 | $output->writeln(" " . basename($roboFile) . " will be created in the current directory "); 47 | file_put_contents( 48 | $roboFile, 49 | 'writeln(" Edit this file to add your commands! "); 58 | }); 59 | $this->add($createRoboFile); 60 | } 61 | 62 | /** 63 | * Add self update command, do nothing if null is provided 64 | * 65 | * @param string $repository 66 | * GitHub Repository for self update. 67 | */ 68 | public function addSelfUpdateCommand($repository = null) 69 | { 70 | if (!$repository || !class_exists('\SelfUpdate\SelfUpdateCommand') || empty(\Phar::running())) { 71 | return; 72 | } 73 | $selfUpdateCommand = new \SelfUpdate\SelfUpdateCommand($this->getName(), $this->getVersion(), $repository); 74 | $this->add($selfUpdateCommand); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/ClassDiscovery/AbstractClassDiscovery.php: -------------------------------------------------------------------------------- 1 | searchPattern = $searchPattern; 23 | 24 | return $this; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ClassDiscovery/ClassDiscoveryInterface.php: -------------------------------------------------------------------------------- 1 | classLoader = $classLoader; 33 | } 34 | 35 | /** 36 | * @param string $relativeNamespace 37 | * 38 | * @return $this 39 | */ 40 | public function setRelativeNamespace($relativeNamespace) 41 | { 42 | $this->relativeNamespace = $relativeNamespace; 43 | 44 | return $this; 45 | } 46 | 47 | /** 48 | * {@inheritDoc} 49 | */ 50 | public function getClasses() 51 | { 52 | $classes = []; 53 | $relativePath = $this->convertNamespaceToPath($this->relativeNamespace); 54 | 55 | foreach ($this->classLoader->getPrefixesPsr4() as $baseNamespace => $directories) { 56 | $directories = array_filter(array_map(function ($directory) use ($relativePath) { 57 | return $directory . $relativePath; 58 | }, $directories), 'is_dir'); 59 | 60 | if ($directories) { 61 | foreach ($this->search($directories, $this->searchPattern) as $file) { 62 | $relativePathName = $file->getRelativePathname(); 63 | $classes[] = $baseNamespace . $this->convertPathToNamespace($relativePath . '/' . $relativePathName); 64 | } 65 | } 66 | } 67 | 68 | return $classes; 69 | } 70 | 71 | /** 72 | * {@inheritdoc} 73 | */ 74 | public function getFile($class) 75 | { 76 | return $this->classLoader->findFile($class); 77 | } 78 | 79 | /** 80 | * @param string|array $directories 81 | * @param string $pattern 82 | * 83 | * @return \Symfony\Component\Finder\Finder 84 | */ 85 | protected function search($directories, $pattern) 86 | { 87 | $finder = new Finder(); 88 | $finder->files() 89 | ->name($pattern) 90 | ->in($directories); 91 | 92 | return $finder; 93 | } 94 | 95 | /** 96 | * @param string $path 97 | * 98 | * @return string 99 | */ 100 | protected function convertPathToNamespace($path) 101 | { 102 | return str_replace(['/', '.php'], ['\\', ''], trim($path, '/')); 103 | } 104 | 105 | /** 106 | * @param string $namespace 107 | * 108 | * @return string 109 | */ 110 | public function convertNamespaceToPath($namespace) 111 | { 112 | return '/' . str_replace("\\", '/', trim($namespace, '\\')); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/Collection/CallableTask.php: -------------------------------------------------------------------------------- 1 | fn = $fn; 32 | $this->reference = $reference; 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function run() 39 | { 40 | $result = call_user_func($this->fn, $this->getState()); 41 | // If the function returns no result, then count it 42 | // as a success. 43 | if (!isset($result)) { 44 | $result = Result::success($this->reference); 45 | } 46 | // If the function returns a result, it must either return 47 | // a \Robo\Result or an exit code. In the later case, we 48 | // convert it to a \Robo\Result. 49 | if (!$result instanceof Result) { 50 | $result = new Result($this->reference, $result); 51 | } 52 | 53 | return $result; 54 | } 55 | 56 | /** 57 | * @return \Robo\State\Data 58 | */ 59 | public function getState() 60 | { 61 | if ($this->reference instanceof StateAwareInterface) { 62 | return $this->reference->getState(); 63 | } 64 | return new Data(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Collection/CollectionProcessHook.php: -------------------------------------------------------------------------------- 1 | run(); 31 | } catch (\Exception $e) { 32 | return Result::fromException($result, $e); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Collection/CompletionWrapper.php: -------------------------------------------------------------------------------- 1 | collection = $collection; 60 | $this->task = ($task instanceof WrappedTaskInterface) ? $task->original() : $task; 61 | $this->rollbackTask = $rollbackTask; 62 | } 63 | 64 | /** 65 | * {@inheritdoc} 66 | */ 67 | public function original() 68 | { 69 | return $this->task; 70 | } 71 | 72 | /** 73 | * Before running this task, register its rollback and completion 74 | * handlers on its collection. The reason this class exists is to 75 | * defer registration of rollback and completion tasks until 'run()' time. 76 | * 77 | * @return \Robo\Result 78 | */ 79 | public function run() 80 | { 81 | if ($this->rollbackTask) { 82 | $this->collection->registerRollback($this->rollbackTask); 83 | } 84 | if ($this->task instanceof RollbackInterface) { 85 | $this->collection->registerRollback(new CallableTask([$this->task, 'rollback'], $this->task)); 86 | } 87 | if ($this->task instanceof CompletionInterface) { 88 | $this->collection->registerCompletion(new CallableTask([$this->task, 'complete'], $this->task)); 89 | } 90 | 91 | return $this->task->run(); 92 | } 93 | 94 | /** 95 | * Make this wrapper object act like the class it wraps. 96 | * 97 | * @param string $function 98 | * @param array $args 99 | * 100 | * @return mixed 101 | */ 102 | public function __call($function, $args) 103 | { 104 | return call_user_func_array(array($this->task, $function), $args); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Collection/Element.php: -------------------------------------------------------------------------------- 1 | task = $task; 35 | } 36 | 37 | /** 38 | * @param \Robo\Contract\TaskInterface|callable $before 39 | * @param string $name 40 | */ 41 | public function before($before, $name) 42 | { 43 | if ($name) { 44 | $this->before[$name] = $before; 45 | } else { 46 | $this->before[] = $before; 47 | } 48 | } 49 | 50 | /** 51 | * @param \Robo\Contract\TaskInterface|callable $after 52 | * @param string $name 53 | */ 54 | public function after($after, $name) 55 | { 56 | if ($name) { 57 | $this->after[$name] = $after; 58 | } else { 59 | $this->after[] = $after; 60 | } 61 | } 62 | 63 | /** 64 | * @return \Robo\Contract\TaskInterface[]|callable[] 65 | */ 66 | public function getBefore() 67 | { 68 | return $this->before; 69 | } 70 | 71 | /** 72 | * @return \Robo\Contract\TaskInterface[]|callable[] 73 | */ 74 | public function getAfter() 75 | { 76 | return $this->after; 77 | } 78 | 79 | /** 80 | * @return \Robo\Contract\TaskInterface 81 | */ 82 | public function getTask() 83 | { 84 | return $this->task; 85 | } 86 | 87 | /** 88 | * @return \Robo\Contract\TaskInterface[]|callable[] 89 | */ 90 | public function getTaskList() 91 | { 92 | return array_merge($this->getBefore(), [$this->getTask()], $this->getAfter()); 93 | } 94 | 95 | /** 96 | * @return int 97 | */ 98 | public function progressIndicatorSteps() 99 | { 100 | $steps = 0; 101 | foreach ($this->getTaskList() as $task) { 102 | if ($task instanceof WrappedTaskInterface) { 103 | $task = $task->original(); 104 | } 105 | // If the task is a ProgressIndicatorAwareInterface, then it 106 | // will advance the progress indicator a number of times. 107 | if ($task instanceof ProgressIndicatorAwareInterface) { 108 | $steps += $task->progressIndicatorSteps(); 109 | } 110 | // We also advance the progress indicator once regardless 111 | // of whether it is progress-indicator aware or not. 112 | $steps++; 113 | } 114 | return $steps; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Collection/NestedCollectionInterface.php: -------------------------------------------------------------------------------- 1 | task(TaskForEach::class, $collection); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Collection/Temporary.php: -------------------------------------------------------------------------------- 1 | get('collection'); 43 | register_shutdown_function(function () { 44 | static::complete(); 45 | }); 46 | } 47 | 48 | return static::$collection; 49 | } 50 | 51 | /** 52 | * Call the complete method of all of the registered objects. 53 | */ 54 | public static function complete() 55 | { 56 | // Run the collection of tasks. This will also run the 57 | // completion tasks. 58 | $collection = static::getCollection(); 59 | $collection->run(); 60 | // Make sure that our completion functions do not run twice. 61 | $collection->reset(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Common/BuilderAwareTrait.php: -------------------------------------------------------------------------------- 1 | builder = $builder; 26 | 27 | return $this; 28 | } 29 | 30 | /** 31 | * @see \Robo\Contract\BuilderAwareInterface::getBuilder() 32 | * 33 | * @return \Robo\Collection\CollectionBuilder 34 | */ 35 | public function getBuilder() 36 | { 37 | return $this->builder; 38 | } 39 | 40 | /** 41 | * @return \Robo\Collection\CollectionBuilder 42 | * 43 | * @param \Robo\Symfony\ConsoleIO $io 44 | */ 45 | protected function collectionBuilder($io = null) 46 | { 47 | // TODO: trigger_error if $io is null. Eventually this shim will be removed. 48 | if (!$io) { 49 | $io = new ConsoleIO(Robo::input(), Robo::output()); 50 | } 51 | return $this->getBuilder()->newBuilder()->inflect($this)->inflect($io); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Common/CommandArguments.php: -------------------------------------------------------------------------------- 1 | args($arg); 27 | } 28 | 29 | /** 30 | * Pass methods parameters as arguments to executable. Argument values 31 | * are automatically escaped. 32 | * 33 | * @param string|string[] $args 34 | * 35 | * @return $this 36 | */ 37 | public function args($args) 38 | { 39 | $func_args = func_get_args(); 40 | if (!is_array($args)) { 41 | $args = $func_args; 42 | } 43 | $this->arguments .= ' ' . implode(' ', array_map([static::class, 'escape'], $args)); 44 | return $this; 45 | } 46 | 47 | /** 48 | * Pass the provided string in its raw (as provided) form as an argument to executable. 49 | * 50 | * @param string $arg 51 | * 52 | * @return $this 53 | */ 54 | public function rawArg($arg) 55 | { 56 | $this->arguments .= " $arg"; 57 | 58 | return $this; 59 | } 60 | 61 | /** 62 | * Escape the provided value, unless it contains only alphanumeric 63 | * plus a few other basic characters. 64 | * 65 | * @param string $value 66 | * 67 | * @return string 68 | */ 69 | public static function escape($value) 70 | { 71 | if (preg_match('/^[a-zA-Z0-9\/\.@~_-]+$/', $value)) { 72 | return $value; 73 | } 74 | return ProcessUtils::escapeArgument($value); 75 | } 76 | 77 | /** 78 | * Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter. 79 | * Option values are automatically escaped. 80 | * 81 | * @param string $option 82 | * @param string $value 83 | * @param string $separator 84 | * 85 | * @return $this 86 | */ 87 | public function option($option, $value = null, $separator = ' ') 88 | { 89 | if ($option !== null and strpos($option, '-') !== 0) { 90 | $option = "--$option"; 91 | } 92 | $this->arguments .= null == $option ? '' : " " . $option; 93 | $this->arguments .= null == $value ? '' : $separator . static::escape($value); 94 | return $this; 95 | } 96 | 97 | /** 98 | * Pass multiple options to executable. The associative array contains 99 | * the key:value pairs that become `--key value`, for each item in the array. 100 | * Values are automatically escaped. 101 | * 102 | * @param array $options 103 | * @param string $separator 104 | * 105 | * @return $this 106 | */ 107 | public function options(array $options, $separator = ' ') 108 | { 109 | foreach ($options as $option => $value) { 110 | $this->option($option, $value, $separator); 111 | } 112 | return $this; 113 | } 114 | 115 | /** 116 | * Pass an option with multiple values to executable. Value can be a string or array. 117 | * Option values are automatically escaped. 118 | * 119 | * @param string $option 120 | * @param string|array $value 121 | * @param string $separator 122 | * 123 | * @return $this 124 | */ 125 | public function optionList($option, $value = array(), $separator = ' ') 126 | { 127 | if (is_array($value)) { 128 | foreach ($value as $item) { 129 | $this->optionList($option, $item, $separator); 130 | } 131 | } else { 132 | $this->option($option, $value, $separator); 133 | } 134 | 135 | return $this; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/Common/CommandReceiver.php: -------------------------------------------------------------------------------- 1 | getCommand(); 27 | } else { 28 | throw new TaskException($this, get_class($command) . " does not implement CommandInterface, so can't be passed into this task"); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Common/ConfigAwareTrait.php: -------------------------------------------------------------------------------- 1 | config = $config; 25 | 26 | return $this; 27 | } 28 | 29 | /** 30 | * Get the config management object. 31 | * 32 | * @return \Consolidation\Config\ConfigInterface 33 | */ 34 | public function getConfig() 35 | { 36 | return $this->config; 37 | } 38 | 39 | /** 40 | * Any class that uses ConfigAwareTrait SHOULD override this method 41 | * , and define a prefix for its configuration items. This is usually 42 | * done in a base class. When used, this method should return a string 43 | * that ends with a "."; see BaseTask::configPrefix(). 44 | * 45 | * @return string 46 | */ 47 | protected static function configPrefix() 48 | { 49 | return ''; 50 | } 51 | 52 | protected static function configClassIdentifier($classname) 53 | { 54 | $configIdentifier = strtr($classname, '\\', '.'); 55 | $configIdentifier = preg_replace('#^(.*\.Task\.|\.)#', '', $configIdentifier); 56 | 57 | return $configIdentifier; 58 | } 59 | 60 | protected static function configPostfix() 61 | { 62 | return ''; 63 | } 64 | 65 | /** 66 | * @param string $key 67 | * 68 | * @return string 69 | */ 70 | private static function getClassKey($key) 71 | { 72 | $configPrefix = static::configPrefix(); // task. 73 | $configClass = static::configClassIdentifier(static::class); // PARTIAL_NAMESPACE.CLASSNAME 74 | $configPostFix = static::configPostfix(); // .settings 75 | 76 | return sprintf('%s%s%s.%s', $configPrefix, $configClass, $configPostFix, $key); 77 | } 78 | 79 | /** 80 | * @param string $key 81 | * @param mixed $value 82 | * @param \Consolidation\Config\ConfigInterface|null $config 83 | */ 84 | public static function configure($key, $value, $config = null) 85 | { 86 | if (!$config) { 87 | $config = Robo::config(); 88 | } 89 | $config->setDefault(static::getClassKey($key), $value); 90 | } 91 | 92 | /** 93 | * @param string $key 94 | * @param mixed|null $default 95 | * 96 | * @return mixed|null 97 | */ 98 | protected function getConfigValue($key, $default = null) 99 | { 100 | if (!$this->getConfig()) { 101 | return $default; 102 | } 103 | return $this->getConfig()->get(static::getClassKey($key), $default); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/Common/DynamicParams.php: -------------------------------------------------------------------------------- 1 | $property))) { 29 | $this->$property = !$this->$property; 30 | return $this; 31 | } 32 | 33 | // append item to array 34 | if (is_array($this->$property)) { 35 | if (is_array($args[0])) { 36 | $this->$property = $args[0]; 37 | } else { 38 | array_push($this->$property, $args[0]); 39 | } 40 | return $this; 41 | } 42 | 43 | $this->$property = $args[0]; 44 | return $this; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Common/ExecOneCommand.php: -------------------------------------------------------------------------------- 1 | injectDependencies($this); 21 | } 22 | return $this; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Common/InputAwareTrait.php: -------------------------------------------------------------------------------- 1 | input = $input; 23 | } 24 | 25 | /** 26 | * @return \Symfony\Component\Console\Input\InputInterface 27 | */ 28 | protected function input() 29 | { 30 | if (!isset($this->input)) { 31 | $this->setInput(new ArgvInput()); 32 | } 33 | return $this->input; 34 | } 35 | 36 | /** 37 | * Backwards compatibility. 38 | * 39 | * @return \Symfony\Component\Console\Input\InputInterface 40 | * 41 | * @deprecated 42 | */ 43 | protected function getInput() 44 | { 45 | return $this->input(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Common/OutputAdapter.php: -------------------------------------------------------------------------------- 1 | OutputInterface::VERBOSITY_NORMAL, 22 | VerbosityThresholdInterface::VERBOSITY_VERBOSE => OutputInterface::VERBOSITY_VERBOSE, 23 | VerbosityThresholdInterface::VERBOSITY_VERY_VERBOSE => OutputInterface::VERBOSITY_VERY_VERBOSE, 24 | VerbosityThresholdInterface::VERBOSITY_DEBUG => OutputInterface::VERBOSITY_DEBUG, 25 | ]; 26 | 27 | /** 28 | * {@inheritdoc} 29 | */ 30 | public function verbosityMeetsThreshold($verbosityThreshold) 31 | { 32 | if (!isset($this->verbosityMap[$verbosityThreshold])) { 33 | return true; 34 | } 35 | $verbosityThreshold = $this->verbosityMap[$verbosityThreshold]; 36 | $verbosity = $this->output()->getVerbosity(); 37 | 38 | return $verbosity >= $verbosityThreshold; 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | */ 44 | public function writeMessage($message) 45 | { 46 | $this->output()->write($message); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Common/OutputAwareTrait.php: -------------------------------------------------------------------------------- 1 | output = $output; 26 | 27 | return $this; 28 | } 29 | 30 | /** 31 | * @return \Symfony\Component\Console\Output\OutputInterface 32 | */ 33 | protected function output() 34 | { 35 | if (!isset($this->output)) { 36 | $this->setOutput(new NullOutput()); 37 | } 38 | return $this->output; 39 | } 40 | 41 | /** 42 | * @return \Symfony\Component\Console\Output\OutputInterface 43 | */ 44 | protected function stderr() 45 | { 46 | $output = $this->output(); 47 | if ($output instanceof ConsoleOutputInterface) { 48 | $output = $output->getErrorOutput(); 49 | } 50 | return $output; 51 | } 52 | 53 | /** 54 | * Backwards compatibility 55 | * 56 | * @return \Symfony\Component\Console\Output\OutputInterface 57 | * 58 | * @deprecated 59 | */ 60 | protected function getOutput() 61 | { 62 | return $this->output(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Common/ProcessExecutor.php: -------------------------------------------------------------------------------- 1 | process = $process; 23 | } 24 | 25 | /** 26 | * @param \Psr\Container\ContainerInterface $container 27 | * @param \Symfony\Component\Process\Process $process 28 | * 29 | * @return static 30 | */ 31 | public static function create($container, $process) 32 | { 33 | $processExecutor = new self($process); 34 | 35 | $processExecutor->setLogger($container->get('logger')); 36 | $processExecutor->setProgressIndicator($container->get('progressIndicator')); 37 | $processExecutor->setConfig($container->get('config')); 38 | $processExecutor->setOutputAdapter($container->get('outputAdapter')); 39 | 40 | return $processExecutor; 41 | } 42 | 43 | /** 44 | * {@inheritdoc} 45 | */ 46 | protected function getCommandDescription() 47 | { 48 | return $this->process->getCommandLine(); 49 | } 50 | 51 | public function run() 52 | { 53 | return $this->execute($this->process); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Common/ProcessUtils.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | 8 | namespace Robo\Common; 9 | 10 | use Symfony\Component\Process\Exception\InvalidArgumentException; 11 | 12 | /** 13 | * ProcessUtils is a bunch of utility methods. We want to allow Robo 1.x 14 | * to work with Symfony 4.x while remaining backwards compatibility. This 15 | * requires us to replace some deprecated functionality removed in Symfony. 16 | */ 17 | class ProcessUtils 18 | { 19 | /** 20 | * This class should not be instantiated. 21 | */ 22 | private function __construct() 23 | { 24 | } 25 | 26 | /** 27 | * Escapes a string to be used as a shell argument. 28 | * 29 | * This method is a copy of a method that was deprecated by Symfony 3.3 and 30 | * removed in Symfony 4; it will be removed once there is an actual 31 | * replacement for escapeArgument. 32 | * 33 | * @param string $argument 34 | * The argument that will be escaped. 35 | * 36 | * @return string 37 | * The escaped argument. 38 | */ 39 | public static function escapeArgument($argument) 40 | { 41 | //Fix for PHP bug #43784 escapeshellarg removes % from given string 42 | //Fix for PHP bug #49446 escapeshellarg doesn't work on Windows 43 | //@see https://bugs.php.net/bug.php?id=43784 44 | //@see https://bugs.php.net/bug.php?id=49446 45 | if ('\\' === DIRECTORY_SEPARATOR) { 46 | if ('' === $argument) { 47 | return escapeshellarg($argument); 48 | } 49 | 50 | $escapedArgument = ''; 51 | $quote = false; 52 | foreach (preg_split('/(")/', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) { 53 | if ('"' === $part) { 54 | $escapedArgument .= '\\"'; 55 | } elseif (self::isSurroundedBy($part, '%')) { 56 | // Avoid environment variable expansion 57 | $escapedArgument .= '^%"' . substr($part, 1, -1) . '"^%'; 58 | } else { 59 | // escape trailing backslash 60 | if ('\\' === substr($part, -1)) { 61 | $part .= '\\'; 62 | } 63 | $quote = true; 64 | $escapedArgument .= $part; 65 | } 66 | } 67 | if ($quote) { 68 | $escapedArgument = '"' . $escapedArgument . '"'; 69 | } 70 | 71 | return $escapedArgument; 72 | } 73 | 74 | return "'" . str_replace("'", "'\\''", $argument) . "'"; 75 | } 76 | 77 | private static function isSurroundedBy($arg, $char) 78 | { 79 | return 2 < strlen($arg) && $char === $arg[0] && $char === $arg[strlen($arg) - 1]; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Common/ProgressIndicatorAwareTrait.php: -------------------------------------------------------------------------------- 1 | progressIndicator = $progressIndicator; 33 | 34 | return $this; 35 | } 36 | 37 | /** 38 | * @return null|bool 39 | */ 40 | protected function hideProgressIndicator() 41 | { 42 | if (!$this->progressIndicator) { 43 | return; 44 | } 45 | return $this->progressIndicator->hideProgressIndicator(); 46 | } 47 | 48 | protected function showProgressIndicator() 49 | { 50 | if (!$this->progressIndicator) { 51 | return; 52 | } 53 | $this->progressIndicator->showProgressIndicator(); 54 | } 55 | 56 | /** 57 | * @param bool $visible 58 | */ 59 | protected function restoreProgressIndicator($visible) 60 | { 61 | if (!$this->progressIndicator) { 62 | return; 63 | } 64 | $this->progressIndicator->restoreProgressIndicator($visible); 65 | } 66 | 67 | /** 68 | * @return int 69 | */ 70 | protected function getTotalExecutionTime() 71 | { 72 | if (!$this->progressIndicator) { 73 | return 0; 74 | } 75 | return $this->progressIndicator->getExecutionTime(); 76 | } 77 | 78 | protected function startProgressIndicator() 79 | { 80 | $this->startTimer(); 81 | if ($this instanceof VerbosityThresholdInterface 82 | && !$this->verbosityMeetsThreshold() 83 | ) { 84 | return; 85 | } 86 | if (!$this->progressIndicator) { 87 | return; 88 | } 89 | $totalSteps = $this->progressIndicatorSteps(); 90 | $this->progressIndicator->startProgressIndicator($totalSteps, $this); 91 | } 92 | 93 | /** 94 | * @return bool 95 | */ 96 | protected function inProgress() 97 | { 98 | if (!$this->progressIndicator) { 99 | return false; 100 | } 101 | return $this->progressIndicator->inProgress(); 102 | } 103 | 104 | protected function stopProgressIndicator() 105 | { 106 | $this->stopTimer(); 107 | if (!$this->progressIndicator) { 108 | return; 109 | } 110 | $this->progressIndicator->stopProgressIndicator($this); 111 | } 112 | 113 | protected function disableProgressIndicator() 114 | { 115 | $this->stopTimer(); 116 | if (!$this->progressIndicator) { 117 | return; 118 | } 119 | $this->progressIndicator->disableProgressIndicator(); 120 | } 121 | 122 | protected function detatchProgressIndicator() 123 | { 124 | $this->setProgressIndicator(null); 125 | } 126 | 127 | /** 128 | * @param int $steps 129 | */ 130 | protected function advanceProgressIndicator($steps = 1) 131 | { 132 | if (!$this->progressIndicator) { 133 | return; 134 | } 135 | $this->progressIndicator->advanceProgressIndicator($steps); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/Common/ResourceExistenceChecker.php: -------------------------------------------------------------------------------- 1 | printTaskError(sprintf('Invalid glob "%s"!', $resource), $this); 30 | $success = false; 31 | continue; 32 | } 33 | foreach ($glob as $resource) { 34 | if (!$this->checkResource($resource, $type)) { 35 | $success = false; 36 | } 37 | } 38 | } 39 | return $success; 40 | } 41 | 42 | /** 43 | * Checks a single resource, file or directory. 44 | * 45 | * It will print an error as well on the console. 46 | * 47 | * @param string $resource 48 | * File or folder. 49 | * @param string $type 50 | * Allowed values: "file", "dir", "fileAndDir". 51 | * 52 | * @return bool 53 | */ 54 | protected function checkResource($resource, $type) 55 | { 56 | switch ($type) { 57 | case 'file': 58 | if (!$this->isFile($resource)) { 59 | $this->printTaskError(sprintf('File "%s" does not exist!', $resource), $this); 60 | return false; 61 | } 62 | return true; 63 | case 'dir': 64 | if (!$this->isDir($resource)) { 65 | $this->printTaskError(sprintf('Directory "%s" does not exist!', $resource), $this); 66 | return false; 67 | } 68 | return true; 69 | case 'fileAndDir': 70 | if (!$this->isDir($resource) && !$this->isFile($resource)) { 71 | $this->printTaskError(sprintf('File or directory "%s" does not exist!', $resource), $this); 72 | return false; 73 | } 74 | return true; 75 | } 76 | } 77 | 78 | /** 79 | * Convenience method to check the often uses "source => target" file / folder arrays. 80 | * 81 | * @param string|array $resources 82 | */ 83 | protected function checkSourceAndTargetResource($resources) 84 | { 85 | if (is_string($resources)) { 86 | $resources = [$resources]; 87 | } 88 | $sources = []; 89 | $targets = []; 90 | foreach ($resources as $source => $target) { 91 | $sources[] = $source; 92 | $target[] = $target; 93 | } 94 | $this->checkResources($sources); 95 | $this->checkResources($targets); 96 | } 97 | 98 | /** 99 | * Wrapper method around phps is_dir() 100 | * 101 | * @param string $directory 102 | * 103 | * @return bool 104 | */ 105 | protected function isDir($directory) 106 | { 107 | return is_dir($directory); 108 | } 109 | 110 | /** 111 | * Wrapper method around phps file_exists() 112 | * 113 | * @param string $file 114 | * 115 | * @return bool 116 | */ 117 | protected function isFile($file) 118 | { 119 | return file_exists($file); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Common/TimeKeeper.php: -------------------------------------------------------------------------------- 1 | startedAt) { 24 | return; 25 | } 26 | // Get time in seconds as a float, accurate to the microsecond. 27 | $this->startedAt = microtime(true); 28 | } 29 | 30 | public function stop() 31 | { 32 | $this->finishedAt = microtime(true); 33 | } 34 | 35 | public function reset() 36 | { 37 | $this->startedAt = $this->finishedAt = null; 38 | } 39 | 40 | /** 41 | * @return float|null 42 | */ 43 | public function elapsed() 44 | { 45 | $finished = $this->finishedAt ? $this->finishedAt : microtime(true); 46 | if ($finished - $this->startedAt <= 0) { 47 | return null; 48 | } 49 | return $finished - $this->startedAt; 50 | } 51 | 52 | /** 53 | * Format a duration into a human-readable time. 54 | * 55 | * @param float $duration 56 | * Duration in seconds, with fractional component. 57 | * 58 | * @return string 59 | */ 60 | public static function formatDuration($duration) 61 | { 62 | if ($duration >= self::DAY * 2) { 63 | return gmdate('z \d\a\y\s H:i:s', (int) $duration); 64 | } 65 | if ($duration >= self::DAY) { 66 | return gmdate('\1 \d\a\y H:i:s', (int) $duration); 67 | } 68 | if ($duration >= self::HOUR) { 69 | return gmdate("H:i:s", (int) $duration); 70 | } 71 | if ($duration >= self::MINUTE) { 72 | return gmdate("i:s", (int) $duration); 73 | } 74 | return round($duration, 3) . 's'; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Common/Timer.php: -------------------------------------------------------------------------------- 1 | timer)) { 15 | $this->timer = new TimeKeeper(); 16 | } 17 | $this->timer->start(); 18 | } 19 | 20 | protected function stopTimer() 21 | { 22 | if (!isset($this->timer)) { 23 | return; 24 | } 25 | $this->timer->stop(); 26 | } 27 | 28 | protected function resetTimer() 29 | { 30 | $this->timer->reset(); 31 | } 32 | 33 | /** 34 | * @return float|null 35 | */ 36 | protected function getExecutionTime() 37 | { 38 | if (!isset($this->timer)) { 39 | return null; 40 | } 41 | return $this->timer->elapsed(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Common/VerbosityThresholdTrait.php: -------------------------------------------------------------------------------- 1 | verbosityThreshold = $verbosityThreshold; 44 | return $this; 45 | } 46 | 47 | /** 48 | * @return int 49 | */ 50 | public function verbosityThreshold() 51 | { 52 | return $this->verbosityThreshold; 53 | } 54 | 55 | public function setOutputAdapter(OutputAdapterInterface $outputAdapter) 56 | { 57 | $this->outputAdapter = $outputAdapter; 58 | } 59 | 60 | /** 61 | * @return \Robo\Contract\OutputAdapterInterface 62 | */ 63 | public function outputAdapter() 64 | { 65 | return $this->outputAdapter; 66 | } 67 | 68 | /** 69 | * @return bool 70 | */ 71 | public function hasOutputAdapter() 72 | { 73 | return isset($this->outputAdapter); 74 | } 75 | 76 | /** 77 | * @return bool 78 | */ 79 | public function verbosityMeetsThreshold() 80 | { 81 | if ($this->hasOutputAdapter()) { 82 | return $this->outputAdapter()->verbosityMeetsThreshold($this->verbosityThreshold()); 83 | } 84 | return true; 85 | } 86 | 87 | /** 88 | * Print a message if the selected verbosity level is over this task's 89 | * verbosity threshold. 90 | * 91 | * @param string $message 92 | */ 93 | public function writeMessage($message) 94 | { 95 | if (!$this->verbosityMeetsThreshold()) { 96 | return; 97 | } 98 | $this->outputAdapter()->writeMessage($message); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Config.php: -------------------------------------------------------------------------------- 1 | inflect($this) 14 | * ->initializer() 15 | * ->... 16 | * 17 | * Instead of: 18 | * 19 | * (new SomeTask($args)) 20 | * ->setLogger($this->logger) 21 | * ->initializer() 22 | * ->... 23 | * 24 | * The reason `inflect` is better than the more explicit alternative is 25 | * that subclasses of BaseTask that implement a new FooAwareInterface 26 | * can override injectDependencies() as explained below, and add more 27 | * dependencies that can be injected as needed. 28 | * 29 | * @param \Robo\Contract\InflectionInterface $parent 30 | */ 31 | public function inflect($parent); 32 | 33 | /** 34 | * Take all dependencies availble to this task and inject any that are 35 | * needed into the provided task. The general pattern is that, for every 36 | * FooAwareInterface that this class implements, it should test to see 37 | * if the child also implements the same interface, and if so, should call 38 | * $child->setFoo($this->foo). 39 | * 40 | * The benefits of this are pretty large. Any time an object that implements 41 | * InflectionInterface is created, just call `$child->inflect($this)`, and 42 | * any available optional dependencies will be hooked up via setter injection. 43 | * 44 | * The required dependencies of an object should be provided via constructor 45 | * injection, not inflection. 46 | * 47 | * @param mixed $child An object with one or more *AwareInterfaces implemented. 48 | * 49 | * @see https://mwop.net/blog/2016-04-26-on-locators.html 50 | */ 51 | public function injectDependencies($child); 52 | } 53 | -------------------------------------------------------------------------------- /src/Contract/OutputAdapterInterface.php: -------------------------------------------------------------------------------- 1 | logger->setErrorStream(null); 28 | $this->logger->setOutputStream($output); 29 | } 30 | 31 | /** 32 | * Log the result of a Robo task. 33 | * 34 | * Returns 'true' if the message is printed, or false if it isn't. 35 | * 36 | * @param \Robo\Result $result 37 | * 38 | * @return null|bool 39 | */ 40 | public function printResult(Result $result) 41 | { 42 | $task = $result->getTask(); 43 | if ($task instanceof VerbosityThresholdInterface && !$task->verbosityMeetsThreshold()) { 44 | return; 45 | } 46 | if (!$result->wasSuccessful()) { 47 | return $this->printError($result); 48 | } else { 49 | return $this->printSuccess($result); 50 | } 51 | } 52 | 53 | /** 54 | * Log that we are about to abort due to an error being encountered 55 | * in 'stop on fail' mode. 56 | * 57 | * @param \Robo\Result $result 58 | */ 59 | public function printStopOnFail($result) 60 | { 61 | $this->printMessage(LogLevel::NOTICE, 'Stopping on fail. Exiting....'); 62 | $this->printMessage(LogLevel::ERROR, 'Exit Code: {code}', ['code' => $result->getExitCode()]); 63 | } 64 | 65 | /** 66 | * Log the result of a Robo task that returned an error. 67 | * 68 | * @param \Robo\Result $result 69 | * 70 | * @return bool 71 | */ 72 | protected function printError(Result $result) 73 | { 74 | $task = $result->getTask(); 75 | $context = $result->getContext() + ['timer-label' => 'Time', '_style' => []]; 76 | $context['_style']['message'] = ''; 77 | 78 | $printOutput = true; 79 | if ($task instanceof PrintedInterface) { 80 | $printOutput = !$task->getPrinted(); 81 | } 82 | if ($printOutput) { 83 | $this->printMessage(LogLevel::ERROR, "{message}", $context); 84 | } 85 | $this->printMessage(LogLevel::ERROR, 'Exit code {code}', $context); 86 | return true; 87 | } 88 | 89 | /** 90 | * Log the result of a Robo task that was successful. 91 | * 92 | * @param \Robo\Result $result 93 | * 94 | * @return bool 95 | */ 96 | protected function printSuccess(Result $result) 97 | { 98 | $task = $result->getTask(); 99 | $context = $result->getContext() + ['timer-label' => 'in']; 100 | $time = $result->getExecutionTime(); 101 | if ($time) { 102 | $this->printMessage(RoboLogLevel::SUCCESS, 'Done', $context); 103 | } 104 | return false; 105 | } 106 | 107 | /** 108 | * @param string $level 109 | * @param string $message 110 | * @param array $context 111 | */ 112 | protected function printMessage($level, $message, $context = []) 113 | { 114 | $inProgress = $this->hideProgressIndicator(); 115 | $this->logger->log($level, $message, $context); 116 | if ($inProgress) { 117 | $this->restoreProgressIndicator($inProgress); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/Log/RoboLogLevel.php: -------------------------------------------------------------------------------- 1 | labelStyles += [ 27 | RoboLogLevel::SIMULATED_ACTION => self::TASK_STYLE_SIMULATED, 28 | RoboLogLevel::SUCCESS => self::TASK_STYLE_SUCCESS, 29 | ]; 30 | $this->messageStyles += [ 31 | RoboLogLevel::SIMULATED_ACTION => '', 32 | RoboLogLevel::SUCCESS => '', 33 | ]; 34 | } 35 | 36 | /** 37 | * Log style customization for Robo: replace the log level with 38 | * the task name. 39 | * 40 | * @param string $level 41 | * @param string $message 42 | * @param array $context 43 | * 44 | * @return string 45 | */ 46 | protected function formatMessageByLevel($level, $message, $context) 47 | { 48 | $label = $level; 49 | if (array_key_exists('name', $context)) { 50 | $label = $context['name']; 51 | } 52 | return $this->formatMessage($label, $message, $context, $this->labelStyles[$level], $this->messageStyles[$level]); 53 | } 54 | 55 | /** 56 | * Log style customization for Robo: add the time indicator to the 57 | * end of the log message if it exists in the context. 58 | * 59 | * @param string $label 60 | * @param string $message 61 | * @param array $context 62 | * @param string $taskNameStyle 63 | * @param string $messageStyle 64 | * 65 | * @return string 66 | */ 67 | protected function formatMessage($label, $message, $context, $taskNameStyle, $messageStyle = '') 68 | { 69 | $message = parent::formatMessage($label, $message, $context, $taskNameStyle, $messageStyle); 70 | 71 | if (array_key_exists('time', $context) && !empty($context['time']) && array_key_exists('timer-label', $context)) { 72 | $duration = TimeKeeper::formatDuration($context['time']); 73 | $message .= ' ' . $context['timer-label'] . ' ' . $this->wrapFormatString($duration, 'fg=yellow'); 74 | } 75 | 76 | return $message; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Log/RoboLogger.php: -------------------------------------------------------------------------------- 1 | OutputInterface::VERBOSITY_NORMAL, // Default is "verbose" 24 | RoboLogLevel::SUCCESS => OutputInterface::VERBOSITY_NORMAL, // Same as "NOTICE" 25 | LogLevel::NOTICE => OutputInterface::VERBOSITY_NORMAL, // Default is "verbose" 26 | LogLevel::INFO => OutputInterface::VERBOSITY_VERBOSE, // Default is "very verbose" 27 | ]; 28 | parent::__construct($output, $roboVerbosityOverrides); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/ResultData.php: -------------------------------------------------------------------------------- 1 | exitCode = $exitCode; 37 | parent::__construct($message, $data); 38 | } 39 | 40 | /** 41 | * @param string $message 42 | * @param array $data 43 | * 44 | * @return static 45 | */ 46 | public static function message($message, $data = []) 47 | { 48 | return new self(self::EXITCODE_OK, $message, $data); 49 | } 50 | 51 | /** 52 | * @param string $message 53 | * @param array $data 54 | * 55 | * @return static 56 | */ 57 | public static function cancelled($message = '', $data = []) 58 | { 59 | return new ResultData(self::EXITCODE_USER_CANCEL, $message, $data); 60 | } 61 | 62 | /** 63 | * @return int 64 | */ 65 | public function getExitCode() 66 | { 67 | return $this->exitCode; 68 | } 69 | 70 | /** 71 | * @return null|string 72 | */ 73 | public function getOutputData() 74 | { 75 | if (!empty($this->message) && !isset($this['already-printed']) && isset($this['provide-outputdata'])) { 76 | return $this->message; 77 | } 78 | } 79 | 80 | /** 81 | * Indicate that the message in this data has already been displayed. 82 | */ 83 | public function alreadyPrinted() 84 | { 85 | $this['already-printed'] = true; 86 | } 87 | 88 | /** 89 | * Opt-in to providing the result message as the output data 90 | */ 91 | public function provideOutputdata() 92 | { 93 | $this['provide-outputdata'] = true; 94 | } 95 | 96 | /** 97 | * @return bool 98 | */ 99 | public function wasSuccessful() 100 | { 101 | return $this->exitCode === self::EXITCODE_OK; 102 | } 103 | 104 | /** 105 | * @return bool 106 | */ 107 | public function wasCancelled() 108 | { 109 | return $this->exitCode == self::EXITCODE_USER_CANCEL; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/State/Consumer.php: -------------------------------------------------------------------------------- 1 | state; 23 | } 24 | 25 | public function setState(Data $state) 26 | { 27 | $this->state = $state; 28 | } 29 | 30 | /** 31 | * @param int|string $key 32 | * @param mixed $value 33 | */ 34 | public function setStateValue($key, $value) 35 | { 36 | $this->state[$key] = $value; 37 | } 38 | 39 | public function updateState(Data $update) 40 | { 41 | $this->state->update($update); 42 | } 43 | 44 | public function resetState() 45 | { 46 | $this->state = new Data(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Symfony/ConsoleIO.php: -------------------------------------------------------------------------------- 1 | input = $input; 26 | $this->output = $output; 27 | parent::__construct($input, $output); 28 | } 29 | 30 | public function input() 31 | { 32 | return $this->input; 33 | } 34 | 35 | public function output() 36 | { 37 | return $this->output; 38 | } 39 | 40 | /** 41 | * @param string $text 42 | */ 43 | public function say($text) 44 | { 45 | $char = $this->decorationCharacter('>', '➜'); 46 | $this->writeln("$char $text"); 47 | } 48 | 49 | /** 50 | * @param string $text 51 | * @param int $length 52 | * @param string $color 53 | */ 54 | public function yell($text, $length = 40, $color = 'green') 55 | { 56 | $char = $this->decorationCharacter(' ', '➜'); 57 | $format = "$char %s"; 58 | $this->formattedOutput($text, $length, $format); 59 | } 60 | 61 | /** 62 | * @param string $text 63 | * @param int $length 64 | * @param string $format 65 | */ 66 | protected function formattedOutput($text, $length, $format) 67 | { 68 | $lines = explode("\n", trim($text, "\n")); 69 | $maxLineLength = array_reduce(array_map('strlen', $lines), 'max'); 70 | $length = max($length, $maxLineLength); 71 | $len = $length + 2; 72 | $space = str_repeat(' ', $len); 73 | $this->writeln(sprintf($format, $space)); 74 | foreach ($lines as $line) { 75 | $line = str_pad($line, $length, ' ', STR_PAD_BOTH); 76 | $this->writeln(sprintf($format, " $line ")); 77 | } 78 | $this->writeln(sprintf($format, $space)); 79 | } 80 | 81 | /** 82 | * @param string $nonDecorated 83 | * @param string $decorated 84 | * 85 | * @return string 86 | */ 87 | protected function decorationCharacter($nonDecorated, $decorated) 88 | { 89 | if (!$this->output()->isDecorated() || (strncasecmp(PHP_OS, 'WIN', 3) == 0)) { 90 | return $nonDecorated; 91 | } 92 | return $decorated; 93 | } 94 | 95 | /** 96 | * {@inheritdoc} 97 | */ 98 | public function lightText($message) 99 | { 100 | $this->block($message, '', 'fg=gray', '', true); 101 | } 102 | 103 | /** 104 | * {@inheritdoc} 105 | */ 106 | public function injectDependencies($child) 107 | { 108 | if ($child instanceof InputAwareInterface) { 109 | $child->setInput($this->input()); 110 | } 111 | if ($child instanceof OutputAwareInterface) { 112 | $child->setOutput($this->output()); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/Symfony/ConsoleIOInjector.php: -------------------------------------------------------------------------------- 1 | input(), $commandData->output()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Symfony/SymfonyStyleInjector.php: -------------------------------------------------------------------------------- 1 | input(), $commandData->output()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Task/ApiGen/Tasks.php: -------------------------------------------------------------------------------- 1 | task(ApiGen::class, $pathToApiGen); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Task/Archive/Tasks.php: -------------------------------------------------------------------------------- 1 | task(Pack::class, $filename); 15 | } 16 | 17 | /** 18 | * @param string $filename 19 | * 20 | * @return \Robo\Task\Archive\Extract|\Robo\Collection\CollectionBuilder 21 | */ 22 | protected function taskExtract($filename) 23 | { 24 | return $this->task(Extract::class, $filename); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Task/Assets/Less.php: -------------------------------------------------------------------------------- 1 | taskLess([ 13 | * 'less/default.less' => 'css/default.css' 14 | * ]) 15 | * ->run(); 16 | * ?> 17 | * ``` 18 | * 19 | * Use one of both less compilers in your project: 20 | * 21 | * ``` 22 | * "leafo/lessphp": "~0.5", 23 | * "oyejorge/less.php": "~1.5" 24 | * ``` 25 | * 26 | * Specify directory (string or array) for less imports lookup: 27 | * 28 | * ```php 29 | * taskLess([ 31 | * 'less/default.less' => 'css/default.css' 32 | * ]) 33 | * ->importDir('less') 34 | * ->compiler('lessphp') 35 | * ->run(); 36 | * ?> 37 | * ``` 38 | * 39 | * You can implement additional compilers by extending this task and adding a 40 | * method named after them and overloading the lessCompilers() method to 41 | * inject the name there. 42 | */ 43 | class Less extends CssPreprocessor 44 | { 45 | const FORMAT_NAME = 'less'; 46 | 47 | /** 48 | * @var string[] 49 | */ 50 | protected $compilers = [ 51 | 'less', // https://github.com/oyejorge/less.php 52 | 'lessphp', //https://github.com/leafo/lessphp 53 | ]; 54 | 55 | /** 56 | * lessphp compiler 57 | * @link https://github.com/leafo/lessphp 58 | * 59 | * @param string $file 60 | * 61 | * @return string 62 | */ 63 | protected function lessphp($file) 64 | { 65 | if (!class_exists('\lessc')) { 66 | return Result::errorMissingPackage($this, 'lessc', 'leafo/lessphp'); 67 | } 68 | 69 | $lessCode = file_get_contents($file); 70 | 71 | $less = new \lessc(); 72 | if (isset($this->compilerOptions['importDirs'])) { 73 | $less->setImportDir($this->compilerOptions['importDirs']); 74 | } 75 | 76 | return $less->compile($lessCode); 77 | } 78 | 79 | /** 80 | * less compiler 81 | * @link https://github.com/oyejorge/less.php 82 | * 83 | * @param string $file 84 | * 85 | * @return string 86 | */ 87 | protected function less($file) 88 | { 89 | if (!class_exists('\Less_Parser')) { 90 | return Result::errorMissingPackage($this, 'Less_Parser', 'oyejorge/less.php'); 91 | } 92 | 93 | $lessCode = file_get_contents($file); 94 | 95 | $parser = new \Less_Parser(); 96 | $parser->SetOptions($this->compilerOptions); 97 | if (isset($this->compilerOptions['importDirs'])) { 98 | $importDirs = []; 99 | foreach ($this->compilerOptions['importDirs'] as $dir) { 100 | $importDirs[$dir] = $dir; 101 | } 102 | $parser->SetImportDirs($importDirs); 103 | } 104 | 105 | $parser->parse($lessCode); 106 | 107 | return $parser->getCss(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Task/Assets/Scss.php: -------------------------------------------------------------------------------- 1 | taskScss([ 13 | * 'scss/default.scss' => 'css/default.css' 14 | * ]) 15 | * ->importDir('assets/styles') 16 | * ->run(); 17 | * ?> 18 | * ``` 19 | * 20 | * Use the following scss compiler in your project: 21 | * 22 | * ``` 23 | * "scssphp/scssphp ": "~1.0.0", 24 | * ``` 25 | * 26 | * You can implement additional compilers by extending this task and adding a 27 | * method named after them and overloading the scssCompilers() method to 28 | * inject the name there. 29 | */ 30 | class Scss extends CssPreprocessor 31 | { 32 | const FORMAT_NAME = 'scss'; 33 | 34 | /** 35 | * @var string[] 36 | */ 37 | protected $compilers = [ 38 | 'scssphp', // https://github.com/scssphp/scssphp 39 | ]; 40 | 41 | /** 42 | * scssphp compiler 43 | * @link https://github.com/scssphp/scssphp 44 | * 45 | * @param string $file 46 | * 47 | * @return string 48 | */ 49 | protected function scssphp($file) 50 | { 51 | if (!class_exists('\ScssPhp\ScssPhp\Compiler')) { 52 | return Result::errorMissingPackage($this, 'scssphp', 'scssphp/scssphp'); 53 | } 54 | 55 | $scssCode = file_get_contents($file); 56 | $scss = new \ScssPhp\ScssPhp\Compiler(); 57 | 58 | // set options for the scssphp compiler 59 | if (isset($this->compilerOptions['importDirs'])) { 60 | $scss->setImportPaths($this->compilerOptions['importDirs']); 61 | } 62 | 63 | if (isset($this->compilerOptions['formatter'])) { 64 | $scss->setFormatter($this->compilerOptions['formatter']); 65 | } 66 | 67 | return $scss->compile($scssCode); 68 | } 69 | 70 | /** 71 | * Sets the formatter for scssphp 72 | * 73 | * The method setFormatter($formatterName) sets the current formatter to $formatterName, 74 | * the name of a class as a string that implements the formatting interface. See the source 75 | * for ScssPhp\ScssPhp\Formatter\Expanded for an example. 76 | * 77 | * Five formatters are included with scssphp/scssphp: 78 | * - ScssPhp\ScssPhp\Formatter\Expanded 79 | * - ScssPhp\ScssPhp\Formatter\Nested (default) 80 | * - ScssPhp\ScssPhp\Formatter\Compressed 81 | * - ScssPhp\ScssPhp\Formatter\Compact 82 | * - ScssPhp\ScssPhp\Formatter\Crunched 83 | * 84 | * @link https://scssphp.github.io/scssphp/docs/#output-formatting 85 | * 86 | * @param string $formatterName 87 | * 88 | * @return $this 89 | */ 90 | public function setFormatter($formatterName) 91 | { 92 | return parent::setFormatter($formatterName); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/Task/Assets/Tasks.php: -------------------------------------------------------------------------------- 1 | task(Minify::class, $input); 15 | } 16 | 17 | /** 18 | * @param string|string[] $input 19 | * 20 | * @return \Robo\Task\Assets\ImageMinify|\Robo\Collection\CollectionBuilder 21 | */ 22 | protected function taskImageMinify($input) 23 | { 24 | return $this->task(ImageMinify::class, $input); 25 | } 26 | 27 | /** 28 | * @param array $input 29 | * 30 | * @return \Robo\Task\Assets\Less|\Robo\Collection\CollectionBuilder 31 | */ 32 | protected function taskLess($input) 33 | { 34 | return $this->task(Less::class, $input); 35 | } 36 | 37 | /** 38 | * @param array $input 39 | * 40 | * @return \Robo\Task\Assets\Scss|\Robo\Collection\CollectionBuilder 41 | */ 42 | protected function taskScss($input) 43 | { 44 | return $this->task(Scss::class, $input); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Task/Base/Exec.php: -------------------------------------------------------------------------------- 1 | taskExec('compass')->arg('watch')->run(); 21 | * // or use shortcut 22 | * $this->_exec('compass watch'); 23 | * 24 | * $this->taskExec('compass watch')->background()->run(); 25 | * 26 | * if ($this->taskExec('phpunit .')->run()->wasSuccessful()) { 27 | * $this->say('tests passed'); 28 | * } 29 | * 30 | * ?> 31 | * ``` 32 | */ 33 | class Exec extends BaseTask implements CommandInterface, PrintedInterface, SimulatedInterface 34 | { 35 | use CommandReceiver; 36 | use ExecOneCommand; 37 | 38 | /** 39 | * @var static[] 40 | */ 41 | protected static $instances = []; 42 | 43 | /** 44 | * @var string|\Robo\Contract\CommandInterface 45 | */ 46 | protected $command; 47 | 48 | private static $isSetupStopRunningJob = false; 49 | 50 | /** 51 | * @param string|\Robo\Contract\CommandInterface $command 52 | */ 53 | public function __construct($command) 54 | { 55 | $this->command = $this->receiveCommand($command); 56 | 57 | $this->setupStopRunningJobs(); 58 | } 59 | 60 | private function setupStopRunningJobs() 61 | { 62 | if (self::$isSetupStopRunningJob === true) { 63 | return; 64 | } 65 | 66 | $stopRunningJobs = Closure::fromCallable(self::class.'::stopRunningJobs'); 67 | 68 | if (function_exists('pcntl_signal')) { 69 | pcntl_signal(SIGTERM, $stopRunningJobs); 70 | } 71 | 72 | register_shutdown_function($stopRunningJobs); 73 | 74 | self::$isSetupStopRunningJob = true; 75 | } 76 | 77 | public function __destruct() 78 | { 79 | $this->stop(); 80 | } 81 | 82 | /** 83 | * Executes command in background mode (asynchronously) 84 | * 85 | * @param bool $arg 86 | * 87 | * @return $this 88 | */ 89 | public function background($arg = true) 90 | { 91 | self::$instances[] = $this; 92 | $this->background = $arg; 93 | return $this; 94 | } 95 | 96 | /** 97 | * {@inheritdoc} 98 | */ 99 | protected function getCommandDescription() 100 | { 101 | return $this->getCommand(); 102 | } 103 | /** 104 | * {@inheritdoc} 105 | */ 106 | public function getCommand() 107 | { 108 | return trim($this->command . $this->arguments); 109 | } 110 | 111 | /** 112 | * {@inheritdoc} 113 | */ 114 | public function simulate($context) 115 | { 116 | $this->printAction($context); 117 | } 118 | 119 | public static function stopRunningJobs() 120 | { 121 | foreach (self::$instances as $instance) { 122 | if ($instance) { 123 | unset($instance); 124 | } 125 | } 126 | } 127 | 128 | /** 129 | * {@inheritdoc} 130 | */ 131 | public function run() 132 | { 133 | $this->hideProgressIndicator(); 134 | // TODO: Symfony 4 requires that we supply the working directory. 135 | $result_data = $this->execute(Process::fromShellCommandline($this->getCommand(), getcwd())); 136 | $result = new Result( 137 | $this, 138 | $result_data->getExitCode(), 139 | $result_data->getMessage(), 140 | $result_data->getData() 141 | ); 142 | $this->showProgressIndicator(); 143 | return $result; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/Task/Base/ExecStack.php: -------------------------------------------------------------------------------- 1 | taskExecStack() 14 | * ->stopOnFail() 15 | * ->exec('mkdir site') 16 | * ->exec('cd site') 17 | * ->run(); 18 | * 19 | * ?> 20 | * ``` 21 | */ 22 | class ExecStack extends CommandStack 23 | { 24 | } 25 | -------------------------------------------------------------------------------- /src/Task/Base/Shortcuts.php: -------------------------------------------------------------------------------- 1 | taskExec($command)->run(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Task/Base/SymfonyCommand.php: -------------------------------------------------------------------------------- 1 | taskSymfonyCommand(new \Codeception\Command\Run('run')) 18 | * ->arg('suite','acceptance') 19 | * ->opt('debug') 20 | * ->run(); 21 | * 22 | * // Artisan Command 23 | * $this->taskSymfonyCommand(new ModelGeneratorCommand()) 24 | * ->arg('name', 'User') 25 | * ->run(); 26 | * ?> 27 | * ``` 28 | */ 29 | class SymfonyCommand extends BaseTask 30 | { 31 | /** 32 | * @var \Symfony\Component\Console\Command\Command 33 | */ 34 | protected $command; 35 | 36 | /** 37 | * @var string[] 38 | */ 39 | protected $input; 40 | 41 | public function __construct(Command $command) 42 | { 43 | $this->command = $command; 44 | $this->input = []; 45 | } 46 | 47 | /** 48 | * @param string $arg 49 | * @param string $value 50 | * 51 | * @return $this 52 | */ 53 | public function arg($arg, $value) 54 | { 55 | $this->input[$arg] = $value; 56 | return $this; 57 | } 58 | 59 | public function opt($option, $value = null) 60 | { 61 | $this->input["--$option"] = $value; 62 | return $this; 63 | } 64 | 65 | /** 66 | * {@inheritdoc} 67 | */ 68 | public function run() 69 | { 70 | $this->printTaskInfo('Running command {command}', ['command' => $this->command->getName()]); 71 | return new Result( 72 | $this, 73 | $this->command->run(new ArrayInput($this->input), $this->output()) 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Task/Base/Tasks.php: -------------------------------------------------------------------------------- 1 | task(Exec::class, $command); 15 | } 16 | 17 | /** 18 | * @return \Robo\Task\Base\ExecStack|\Robo\Collection\CollectionBuilder 19 | */ 20 | protected function taskExecStack() 21 | { 22 | return $this->task(ExecStack::class); 23 | } 24 | 25 | /** 26 | * @return \Robo\Task\Base\ParallelExec|\Robo\Collection\CollectionBuilder 27 | */ 28 | protected function taskParallelExec() 29 | { 30 | return $this->task(ParallelExec::class); 31 | } 32 | 33 | /** 34 | * @param \Symfony\Component\Console\Command\Command $command 35 | * 36 | * @return \Robo\Task\Base\SymfonyCommand|\Robo\Collection\CollectionBuilder 37 | */ 38 | protected function taskSymfonyCommand($command) 39 | { 40 | return $this->task(SymfonyCommand::class, $command); 41 | } 42 | 43 | /** 44 | * @return \Robo\Task\Base\Watch|\Robo\Collection\CollectionBuilder 45 | */ 46 | protected function taskWatch() 47 | { 48 | return $this->task(Watch::class, $this); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Task/Base/Watch.php: -------------------------------------------------------------------------------- 1 | taskWatch() 18 | * ->monitor( 19 | * 'composer.json', 20 | * function() { 21 | * $this->taskComposerUpdate()->run(); 22 | * } 23 | * )->monitor( 24 | * 'src', 25 | * function() { 26 | * $this->taskExec('phpunit')->run(); 27 | * }, 28 | * \Lurker\Event\FilesystemEvent::ALL 29 | * )->monitor( 30 | * 'migrations', 31 | * function() { 32 | * //do something 33 | * }, 34 | * [ 35 | * \Lurker\Event\FilesystemEvent::CREATE, 36 | * \Lurker\Event\FilesystemEvent::DELETE 37 | * ] 38 | * )->run(); 39 | * ?> 40 | * ``` 41 | * 42 | * Pass through the changed file to the callable function 43 | * 44 | * ``` 45 | * $this 46 | * ->taskWatch() 47 | * ->monitor( 48 | * 'filename', 49 | * function ($event) { 50 | * $resource = $event->getResource(); 51 | * ... do something with (string)$resource ... 52 | * }, 53 | * FilesystemEvent::ALL 54 | * ) 55 | * ->run(); 56 | * ``` 57 | * 58 | * The $event parameter is a [standard Symfony file resource object](https://api.symfony.com/3.1/Symfony/Component/Config/Resource/FileResource.html) 59 | */ 60 | class Watch extends BaseTask 61 | { 62 | /** 63 | * @var \Closure 64 | */ 65 | protected $closure; 66 | 67 | /** 68 | * @var array 69 | */ 70 | protected $monitor = []; 71 | 72 | /** 73 | * @var object 74 | */ 75 | protected $bindTo; 76 | 77 | /** 78 | * @param $bindTo 79 | */ 80 | public function __construct($bindTo) 81 | { 82 | $this->bindTo = $bindTo; 83 | } 84 | 85 | /** 86 | * @param string|string[] $paths 87 | * @param \Closure $callable 88 | * @param int|int[] $events 89 | * 90 | * @return $this 91 | */ 92 | public function monitor($paths, \Closure $callable, $events = 2) 93 | { 94 | $this->monitor[] = [(array)$paths, $callable, (array)$events]; 95 | return $this; 96 | } 97 | 98 | /** 99 | * {@inheritdoc} 100 | */ 101 | public function run() 102 | { 103 | if (!class_exists('Lurker\\ResourceWatcher')) { 104 | return Result::errorMissingPackage($this, 'ResourceWatcher', 'totten/lurkerlite'); 105 | } 106 | 107 | $watcher = new ResourceWatcher(); 108 | 109 | foreach ($this->monitor as $k => $monitor) { 110 | /** @var \Closure $closure */ 111 | $closure = $monitor[1]; 112 | $closure->bindTo($this->bindTo); 113 | foreach ($monitor[0] as $i => $dir) { 114 | foreach ($monitor[2] as $j => $event) { 115 | $watcher->track("fs.$k.$i.$j", $dir, $event); 116 | $watcher->addListener("fs.$k.$i.$j", $closure); 117 | } 118 | $this->printTaskInfo('Watching {dir} for changes...', ['dir' => $dir]); 119 | } 120 | } 121 | 122 | $watcher->start(); 123 | return Result::success($this); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Task/BaseTask.php: -------------------------------------------------------------------------------- 1 | logger) { 52 | $child->setLogger($this->logger); 53 | } 54 | if ($child instanceof OutputAwareInterface) { 55 | $child->setOutput($this->output()); 56 | } 57 | if ($child instanceof ProgressIndicatorAwareInterface && $this->progressIndicator) { 58 | $child->setProgressIndicator($this->progressIndicator); 59 | } 60 | if ($child instanceof ConfigAwareInterface && $this->getConfig()) { 61 | $child->setConfig($this->getConfig()); 62 | } 63 | if ($child instanceof VerbosityThresholdInterface && $this->outputAdapter()) { 64 | $child->setOutputAdapter($this->outputAdapter()); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Task/Bower/Base.php: -------------------------------------------------------------------------------- 1 | option('allow-root'); 36 | return $this; 37 | } 38 | 39 | /** 40 | * adds `force-latest` option to bower 41 | * 42 | * @return $this 43 | */ 44 | public function forceLatest() 45 | { 46 | $this->option('force-latest'); 47 | return $this; 48 | } 49 | 50 | /** 51 | * adds `production` option to bower 52 | * 53 | * @return $this 54 | */ 55 | public function noDev() 56 | { 57 | $this->option('production'); 58 | return $this; 59 | } 60 | 61 | /** 62 | * adds `offline` option to bower 63 | * 64 | * @return $this 65 | */ 66 | public function offline() 67 | { 68 | $this->option('offline'); 69 | return $this; 70 | } 71 | 72 | /** 73 | * Base constructor. 74 | * 75 | * @param null|string $pathToBower 76 | * 77 | * @throws \Robo\Exception\TaskException 78 | */ 79 | public function __construct($pathToBower = null) 80 | { 81 | $this->command = $pathToBower; 82 | if (!$this->command) { 83 | $this->command = $this->findExecutable('bower'); 84 | } 85 | if (!$this->command) { 86 | throw new TaskException(__CLASS__, "Bower executable not found."); 87 | } 88 | } 89 | 90 | /** 91 | * @return string 92 | */ 93 | public function getCommand() 94 | { 95 | return "{$this->command} {$this->action}{$this->arguments}"; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Task/Bower/Install.php: -------------------------------------------------------------------------------- 1 | taskBowerInstall()->run(); 14 | * 15 | * // prefer dist with custom path 16 | * $this->taskBowerInstall('path/to/my/bower') 17 | * ->noDev() 18 | * ->run(); 19 | * ?> 20 | * ``` 21 | */ 22 | class Install extends Base implements CommandInterface 23 | { 24 | /** 25 | * {@inheritdoc} 26 | */ 27 | protected $action = 'install'; 28 | 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function run() 33 | { 34 | $this->printTaskInfo('Install Bower packages: {arguments}', ['arguments' => $this->arguments]); 35 | return $this->executeCommand($this->getCommand()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Task/Bower/Tasks.php: -------------------------------------------------------------------------------- 1 | task(Install::class, $pathToBower); 15 | } 16 | 17 | /** 18 | * @param null|string $pathToBower 19 | * 20 | * @return \Robo\Task\Bower\Update|\Robo\Collection\CollectionBuilder 21 | */ 22 | protected function taskBowerUpdate($pathToBower = null) 23 | { 24 | return $this->task(Update::class, $pathToBower); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Task/Bower/Update.php: -------------------------------------------------------------------------------- 1 | taskBowerUpdate->run(); 12 | * 13 | * // prefer dist with custom path 14 | * $this->taskBowerUpdate('path/to/my/bower') 15 | * ->noDev() 16 | * ->run(); 17 | * ?> 18 | * ``` 19 | */ 20 | class Update extends Base 21 | { 22 | /** 23 | * {@inheritdoc} 24 | */ 25 | protected $action = 'update'; 26 | 27 | /** 28 | * {@inheritdoc} 29 | */ 30 | public function run() 31 | { 32 | $this->printTaskInfo('Update Bower packages: {arguments}', ['arguments' => $this->arguments]); 33 | return $this->executeCommand($this->getCommand()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Task/Composer/CheckPlatformReqs.php: -------------------------------------------------------------------------------- 1 | taskComposerValidate()->run(); 12 | * ?> 13 | * ``` 14 | */ 15 | class CheckPlatformReqs extends Base 16 | { 17 | /** 18 | * {@inheritdoc} 19 | */ 20 | protected $action = 'check-platform-reqs'; 21 | 22 | /** 23 | * {@inheritdoc} 24 | */ 25 | public function run() 26 | { 27 | $command = $this->getCommand(); 28 | $this->printTaskInfo('Checking platform requirements: {command}', ['command' => $command]); 29 | return $this->executeCommand($command); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Task/Composer/Config.php: -------------------------------------------------------------------------------- 1 | taskComposerConfig()->set('bin-dir', 'bin/')->run(); 12 | * ?> 13 | * ``` 14 | */ 15 | class Config extends Base 16 | { 17 | /** 18 | * {@inheritdoc} 19 | */ 20 | protected $action = 'config'; 21 | 22 | /** 23 | * Set a configuration value. 24 | * 25 | * @param string $key 26 | * @param string $value 27 | * 28 | * @return $this 29 | */ 30 | public function set($key, $value) 31 | { 32 | $this->arg($key); 33 | $this->arg($value); 34 | return $this; 35 | } 36 | 37 | /** 38 | * Operate on the global repository 39 | * 40 | * @param bool $useGlobal 41 | * 42 | * @return $this 43 | */ 44 | public function useGlobal($useGlobal = true) 45 | { 46 | if ($useGlobal) { 47 | $this->option('global'); 48 | } 49 | return $this; 50 | } 51 | 52 | /** 53 | * @param string $id 54 | * @param string $uri 55 | * @param string $repoType 56 | * 57 | * @return $this 58 | */ 59 | public function repository($id, $uri, $repoType = 'vcs') 60 | { 61 | $this->arg("repositories.$id"); 62 | $this->arg($repoType); 63 | $this->arg($uri); 64 | return $this; 65 | } 66 | 67 | /** 68 | * @param string $id 69 | * 70 | * @return $this 71 | */ 72 | public function removeRepository($id) 73 | { 74 | $this->option('unset', "repositories.$id"); 75 | return $this; 76 | } 77 | 78 | /** 79 | * @param string $id 80 | * 81 | * @return $this 82 | */ 83 | public function disableRepository($id) 84 | { 85 | $this->arg("repositories.$id"); 86 | $this->arg('false'); 87 | return $this; 88 | } 89 | 90 | /** 91 | * @param string $id 92 | * 93 | * @return $this 94 | */ 95 | public function enableRepository($id) 96 | { 97 | $this->arg("repositories.$id"); 98 | $this->arg('true'); 99 | return $this; 100 | } 101 | 102 | /** 103 | * {@inheritdoc} 104 | */ 105 | public function run() 106 | { 107 | $command = $this->getCommand(); 108 | $this->printTaskInfo('Configuring composer.json: {command}', ['command' => $command]); 109 | return $this->executeCommand($command); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/Task/Composer/CreateProject.php: -------------------------------------------------------------------------------- 1 | taskComposerCreateProject()->source('foo/bar')->target('myBar')->run(); 12 | * ?> 13 | * ``` 14 | */ 15 | class CreateProject extends Base 16 | { 17 | /** 18 | * {@inheritdoc} 19 | */ 20 | protected $action = 'create-project'; 21 | 22 | /** 23 | * @var 24 | */ 25 | protected $source; 26 | 27 | /** 28 | * @var string 29 | */ 30 | protected $target = ''; 31 | 32 | /** 33 | * @var string 34 | */ 35 | protected $version = ''; 36 | 37 | /** 38 | * @param string $source 39 | * 40 | * @return $this 41 | */ 42 | public function source($source) 43 | { 44 | $this->source = $source; 45 | return $this; 46 | } 47 | 48 | /** 49 | * @param string $target 50 | * 51 | * @return $this 52 | */ 53 | public function target($target) 54 | { 55 | $this->target = $target; 56 | return $this; 57 | } 58 | 59 | /** 60 | * @param string $version 61 | * 62 | * @return $this 63 | */ 64 | public function version($version) 65 | { 66 | $this->version = $version; 67 | return $this; 68 | } 69 | 70 | /** 71 | * @param bool $keep 72 | * 73 | * @return $this 74 | */ 75 | public function keepVcs($keep = true) 76 | { 77 | if ($keep) { 78 | $this->option('--keep-vcs'); 79 | } 80 | return $this; 81 | } 82 | 83 | /** 84 | * @param bool $noInstall 85 | * 86 | * @return $this 87 | */ 88 | public function noInstall($noInstall = true) 89 | { 90 | if ($noInstall) { 91 | $this->option('--no-install'); 92 | } 93 | return $this; 94 | } 95 | 96 | /** 97 | * @param string $repository 98 | * 99 | * @return $this 100 | */ 101 | public function repository($repository) 102 | { 103 | if (!empty($repository)) { 104 | $this->option('repository', $repository); 105 | } 106 | return $this; 107 | } 108 | 109 | /** 110 | * @param string $stability 111 | * 112 | * @return $this 113 | */ 114 | public function stability($stability) 115 | { 116 | if (!empty($stability)) { 117 | $this->option('stability', $stability); 118 | } 119 | return $this; 120 | } 121 | 122 | /** 123 | * {@inheritdoc} 124 | */ 125 | public function buildCommand() 126 | { 127 | $this->arg($this->source); 128 | if (!empty($this->target)) { 129 | $this->arg($this->target); 130 | } 131 | if (!empty($this->version)) { 132 | $this->arg($this->version); 133 | } 134 | 135 | return parent::buildCommand(); 136 | } 137 | 138 | /** 139 | * {@inheritdoc} 140 | */ 141 | public function run() 142 | { 143 | $command = $this->getCommand(); 144 | $this->printTaskInfo('Creating project: {command}', ['command' => $command]); 145 | return $this->executeCommand($command); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/Task/Composer/DumpAutoload.php: -------------------------------------------------------------------------------- 1 | taskComposerDumpAutoload()->run(); 12 | * 13 | * // dump auto loader with custom path 14 | * $this->taskComposerDumpAutoload('path/to/my/composer.phar') 15 | * ->preferDist() 16 | * ->run(); 17 | * 18 | * // optimize autoloader dump with custom path 19 | * $this->taskComposerDumpAutoload('path/to/my/composer.phar') 20 | * ->optimize() 21 | * ->run(); 22 | * 23 | * // optimize autoloader dump with custom path and no dev 24 | * $this->taskComposerDumpAutoload('path/to/my/composer.phar') 25 | * ->optimize() 26 | * ->noDev() 27 | * ->run(); 28 | * ?> 29 | * ``` 30 | */ 31 | class DumpAutoload extends Base 32 | { 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | protected $action = 'dump-autoload'; 37 | 38 | /** 39 | * @var string 40 | */ 41 | protected $optimize; 42 | 43 | /** 44 | * @param bool $optimize 45 | * 46 | * @return $this 47 | */ 48 | public function optimize($optimize = true) 49 | { 50 | if ($optimize) { 51 | $this->option("--optimize"); 52 | } 53 | return $this; 54 | } 55 | 56 | /** 57 | * {@inheritdoc} 58 | */ 59 | public function run() 60 | { 61 | $command = $this->getCommand(); 62 | $this->printTaskInfo('Dumping Autoloader: {command}', ['command' => $command]); 63 | return $this->executeCommand($command); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Task/Composer/Init.php: -------------------------------------------------------------------------------- 1 | taskComposerInit()->run(); 12 | * ?> 13 | * ``` 14 | */ 15 | class Init extends Base 16 | { 17 | /** 18 | * {@inheritdoc} 19 | */ 20 | protected $action = 'init'; 21 | 22 | /** 23 | * @param string $projectName 24 | * 25 | * @return $this 26 | */ 27 | public function projectName($projectName) 28 | { 29 | $this->option('name', $projectName); 30 | return $this; 31 | } 32 | 33 | /** 34 | * @param string $description 35 | * 36 | * @return $this 37 | */ 38 | public function description($description) 39 | { 40 | $this->option('description', $description); 41 | return $this; 42 | } 43 | 44 | /** 45 | * @param string $author 46 | * 47 | * @return $this 48 | */ 49 | public function author($author) 50 | { 51 | $this->option('author', $author); 52 | return $this; 53 | } 54 | 55 | /** 56 | * @param string $type 57 | * 58 | * @return $this 59 | */ 60 | public function projectType($type) 61 | { 62 | $this->option('type', $type); 63 | return $this; 64 | } 65 | 66 | /** 67 | * @param string $homepage 68 | * 69 | * @return $this 70 | */ 71 | public function homepage($homepage) 72 | { 73 | $this->option('homepage', $homepage); 74 | return $this; 75 | } 76 | 77 | /** 78 | * 'require' is a keyword, so it cannot be a method name. 79 | * 80 | * @param string $project 81 | * @param null|string $version 82 | * 83 | * @return $this 84 | */ 85 | public function dependency($project, $version = null) 86 | { 87 | if (isset($version)) { 88 | $project .= ":$version"; 89 | } 90 | $this->option('require', $project); 91 | return $this; 92 | } 93 | 94 | /** 95 | * @param string $stability 96 | * 97 | * @return $this 98 | */ 99 | public function stability($stability) 100 | { 101 | $this->option('stability', $stability); 102 | return $this; 103 | } 104 | 105 | /** 106 | * @param string $license 107 | * 108 | * @return $this 109 | */ 110 | public function license($license) 111 | { 112 | $this->option('license', $license); 113 | return $this; 114 | } 115 | 116 | /** 117 | * @param string $repository 118 | * 119 | * @return $this 120 | */ 121 | public function repository($repository) 122 | { 123 | $this->option('repository', $repository); 124 | return $this; 125 | } 126 | 127 | /** 128 | * {@inheritdoc} 129 | */ 130 | public function run() 131 | { 132 | $command = $this->getCommand(); 133 | $this->printTaskInfo('Creating composer.json: {command}', ['command' => $command]); 134 | return $this->executeCommand($command); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/Task/Composer/Install.php: -------------------------------------------------------------------------------- 1 | taskComposerInstall()->run(); 12 | * 13 | * // prefer dist with custom path 14 | * $this->taskComposerInstall('path/to/my/composer.phar') 15 | * ->preferDist() 16 | * ->run(); 17 | * 18 | * // optimize autoloader with custom path 19 | * $this->taskComposerInstall('path/to/my/composer.phar') 20 | * ->optimizeAutoloader() 21 | * ->run(); 22 | * ?> 23 | * ``` 24 | */ 25 | class Install extends Base 26 | { 27 | /** 28 | * {@inheritdoc} 29 | */ 30 | protected $action = 'install'; 31 | 32 | /** 33 | * adds `no-suggest` option to composer 34 | * 35 | * @param bool $noSuggest 36 | * 37 | * @return $this 38 | */ 39 | public function noSuggest($noSuggest = true) 40 | { 41 | $this->option('--no-suggest'); 42 | return $this; 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | */ 48 | public function run() 49 | { 50 | $command = $this->getCommand(); 51 | $this->printTaskInfo('Installing Packages: {command}', ['command' => $command]); 52 | return $this->executeCommand($command); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Task/Composer/Remove.php: -------------------------------------------------------------------------------- 1 | taskComposerRemove()->run(); 12 | * ?> 13 | * ``` 14 | */ 15 | class Remove extends Base 16 | { 17 | /** 18 | * {@inheritdoc} 19 | */ 20 | protected $action = 'remove'; 21 | 22 | /** 23 | * 'remove' is a keyword, so it cannot be a method name. 24 | * 25 | * @param array|string $project 26 | * 27 | * @return $this 28 | */ 29 | public function dependency(array|string $project): self 30 | { 31 | $project = (array)$project; 32 | $this->args($project); 33 | return $this; 34 | } 35 | 36 | /** 37 | * @param bool $dev 38 | * 39 | * @return $this 40 | */ 41 | public function dev($dev = true) 42 | { 43 | if ($dev) { 44 | $this->option('--dev'); 45 | } 46 | return $this; 47 | } 48 | 49 | /** 50 | * @param bool $noProgress 51 | * 52 | * @return $this 53 | */ 54 | public function noProgress($noProgress = true) 55 | { 56 | if ($noProgress) { 57 | $this->option('--no-progress'); 58 | } 59 | return $this; 60 | } 61 | 62 | /** 63 | * @param bool $noUpdate 64 | * 65 | * @return $this 66 | */ 67 | public function noUpdate($noUpdate = true) 68 | { 69 | if ($noUpdate) { 70 | $this->option('--no-update'); 71 | } 72 | return $this; 73 | } 74 | 75 | /** 76 | * @param bool $updateNoDev 77 | * 78 | * @return $this 79 | */ 80 | public function updateNoDev($updateNoDev = true) 81 | { 82 | if ($updateNoDev) { 83 | $this->option('--update-no-dev'); 84 | } 85 | return $this; 86 | } 87 | 88 | /** 89 | * @param bool $updateWithDependencies 90 | * 91 | * @return $this 92 | */ 93 | public function noUpdateWithDependencies($updateWithDependencies = true) 94 | { 95 | if ($updateWithDependencies) { 96 | $this->option('--no-update-with-dependencies'); 97 | } 98 | return $this; 99 | } 100 | 101 | /** 102 | * {@inheritdoc} 103 | */ 104 | public function run() 105 | { 106 | $command = $this->getCommand(); 107 | $this->printTaskInfo('Removing packages: {command}', ['command' => $command]); 108 | return $this->executeCommand($command); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Task/Composer/RequireDependency.php: -------------------------------------------------------------------------------- 1 | taskComposerRequire()->dependency('foo/bar', '^.2.4.8')->run(); 12 | * ?> 13 | * ``` 14 | */ 15 | class RequireDependency extends Base 16 | { 17 | /** 18 | * {@inheritdoc} 19 | */ 20 | protected $action = 'require'; 21 | 22 | /** 23 | * 'require' is a keyword, so it cannot be a method name. 24 | * 25 | * @param string $project 26 | * @param null|string $version 27 | * 28 | * @return $this 29 | */ 30 | public function dependency($project, $version = null) 31 | { 32 | $project = (array)$project; 33 | 34 | if (isset($version)) { 35 | $project = array_map( 36 | function ($item) use ($version) { 37 | return "$item:$version"; 38 | }, 39 | $project 40 | ); 41 | } 42 | $this->args($project); 43 | return $this; 44 | } 45 | 46 | /** 47 | * adds `no-suggest` option to composer 48 | * 49 | * @param bool $noSuggest 50 | * 51 | * @return $this 52 | */ 53 | public function noSuggest($noSuggest = true) 54 | { 55 | $this->option('--no-suggest'); 56 | return $this; 57 | } 58 | 59 | /** 60 | * adds `no-update` option to composer 61 | * 62 | * @return $this 63 | */ 64 | public function noUpdate(): self 65 | { 66 | $this->option('--no-update'); 67 | return $this; 68 | } 69 | 70 | /** 71 | * {@inheritdoc} 72 | */ 73 | public function run() 74 | { 75 | $command = $this->getCommand(); 76 | $this->printTaskInfo('Requiring packages: {command}', ['command' => $command]); 77 | return $this->executeCommand($command); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Task/Composer/Tasks.php: -------------------------------------------------------------------------------- 1 | task(Install::class, $pathToComposer); 15 | } 16 | 17 | /** 18 | * @param null|string $pathToComposer 19 | * 20 | * @return \Robo\Task\Composer\Update|\Robo\Collection\CollectionBuilder 21 | */ 22 | protected function taskComposerUpdate($pathToComposer = null) 23 | { 24 | return $this->task(Update::class, $pathToComposer); 25 | } 26 | 27 | /** 28 | * @param null|string $pathToComposer 29 | * 30 | * @return \Robo\Task\Composer\DumpAutoload|\Robo\Collection\CollectionBuilder 31 | */ 32 | protected function taskComposerDumpAutoload($pathToComposer = null) 33 | { 34 | return $this->task(DumpAutoload::class, $pathToComposer); 35 | } 36 | 37 | /** 38 | * @param null|string $pathToComposer 39 | * 40 | * @return \Robo\Task\Composer\Init|\Robo\Collection\CollectionBuilder 41 | */ 42 | protected function taskComposerInit($pathToComposer = null) 43 | { 44 | return $this->task(Init::class, $pathToComposer); 45 | } 46 | 47 | /** 48 | * @param null|string $pathToComposer 49 | * 50 | * @return \Robo\Task\Composer\Config|\Robo\Collection\CollectionBuilder 51 | */ 52 | protected function taskComposerConfig($pathToComposer = null) 53 | { 54 | return $this->task(Config::class, $pathToComposer); 55 | } 56 | 57 | /** 58 | * @param null|string $pathToComposer 59 | * 60 | * @return \Robo\Task\Composer\Validate|\Robo\Collection\CollectionBuilder 61 | */ 62 | protected function taskComposerValidate($pathToComposer = null) 63 | { 64 | return $this->task(Validate::class, $pathToComposer); 65 | } 66 | 67 | /** 68 | * @param null|string $pathToComposer 69 | * 70 | * @return \Robo\Task\Composer\Remove|\Robo\Collection\CollectionBuilder 71 | */ 72 | protected function taskComposerRemove($pathToComposer = null) 73 | { 74 | return $this->task(Remove::class, $pathToComposer); 75 | } 76 | 77 | /** 78 | * @param null|string $pathToComposer 79 | * 80 | * @return \Robo\Task\Composer\RequireDependency|\Robo\Collection\CollectionBuilder 81 | */ 82 | protected function taskComposerRequire($pathToComposer = null) 83 | { 84 | return $this->task(RequireDependency::class, $pathToComposer); 85 | } 86 | 87 | /** 88 | * @param null|string $pathToComposer 89 | * 90 | * @return \Robo\Task\Composer\CreateProject|\Robo\Collection\CollectionBuilder 91 | */ 92 | protected function taskComposerCreateProject($pathToComposer = null) 93 | { 94 | return $this->task(CreateProject::class, $pathToComposer); 95 | } 96 | 97 | /** 98 | * @param null|string $pathToComposer 99 | * 100 | * @return \Robo\Task\Composer\CreateProject|\Robo\Collection\CollectionBuilder 101 | */ 102 | protected function taskCheckPlatformReqs($pathToComposer = null) 103 | { 104 | return $this->task(CheckPlatformReqs::class, $pathToComposer); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Task/Composer/Update.php: -------------------------------------------------------------------------------- 1 | taskComposerUpdate()->run(); 12 | * 13 | * // prefer dist with custom path 14 | * $this->taskComposerUpdate('path/to/my/composer.phar') 15 | * ->preferDist() 16 | * ->run(); 17 | * 18 | * // optimize autoloader with custom path 19 | * $this->taskComposerUpdate('path/to/my/composer.phar') 20 | * ->optimizeAutoloader() 21 | * ->run(); 22 | * ?> 23 | * ``` 24 | */ 25 | class Update extends Base 26 | { 27 | /** 28 | * {@inheritdoc} 29 | */ 30 | protected $action = 'update'; 31 | 32 | /** 33 | * adds `no-suggest` option to composer 34 | * 35 | * @param bool $noSuggest 36 | * 37 | * @return $this 38 | */ 39 | public function noSuggest($noSuggest = true) 40 | { 41 | $this->option('--no-suggest'); 42 | return $this; 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | */ 48 | public function run() 49 | { 50 | $command = $this->getCommand(); 51 | $this->printTaskInfo('Updating Packages: {command}', ['command' => $command]); 52 | return $this->executeCommand($command); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Task/Composer/Validate.php: -------------------------------------------------------------------------------- 1 | taskComposerValidate()->run(); 12 | * ?> 13 | * ``` 14 | */ 15 | class Validate extends Base 16 | { 17 | /** 18 | * {@inheritdoc} 19 | */ 20 | protected $action = 'validate'; 21 | 22 | /** 23 | * @param bool $noCheckAll 24 | * 25 | * @return $this 26 | */ 27 | public function noCheckAll($noCheckAll = true) 28 | { 29 | if ($noCheckAll) { 30 | $this->option('--no-check-all'); 31 | } 32 | return $this; 33 | } 34 | 35 | /** 36 | * @param bool $noCheckLock 37 | * 38 | * @return $this 39 | */ 40 | public function noCheckLock($noCheckLock = true) 41 | { 42 | if ($noCheckLock) { 43 | $this->option('--no-check-lock'); 44 | } 45 | return $this; 46 | } 47 | 48 | /** 49 | * @param bool $noCheckPublish 50 | * 51 | * @return $this 52 | */ 53 | public function noCheckPublish($noCheckPublish = true) 54 | { 55 | if ($noCheckPublish) { 56 | $this->option('--no-check-publish'); 57 | } 58 | return $this; 59 | } 60 | 61 | /** 62 | * @param bool $withDependencies 63 | * 64 | * @return $this 65 | */ 66 | public function withDependencies($withDependencies = true) 67 | { 68 | if ($withDependencies) { 69 | $this->option('--with-dependencies'); 70 | } 71 | return $this; 72 | } 73 | 74 | /** 75 | * @param bool $strict 76 | * 77 | * @return $this 78 | */ 79 | public function strict($strict = true) 80 | { 81 | if ($strict) { 82 | $this->option('--strict'); 83 | } 84 | return $this; 85 | } 86 | 87 | /** 88 | * {@inheritdoc} 89 | */ 90 | public function run() 91 | { 92 | $command = $this->getCommand(); 93 | $this->printTaskInfo('Validating composer.json: {command}', ['command' => $command]); 94 | return $this->executeCommand($command); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Task/Development/GitHub.php: -------------------------------------------------------------------------------- 1 | repo = $repo; 45 | return $this; 46 | } 47 | 48 | /** 49 | * @param string $owner 50 | * 51 | * @return $this 52 | */ 53 | public function owner($owner) 54 | { 55 | $this->owner = $owner; 56 | return $this; 57 | } 58 | 59 | /** 60 | * @param string $uri 61 | * 62 | * @return $this 63 | */ 64 | public function uri($uri) 65 | { 66 | list($this->owner, $this->repo) = explode('/', $uri); 67 | return $this; 68 | } 69 | 70 | /** 71 | * @return string 72 | */ 73 | protected function getUri() 74 | { 75 | return $this->owner . '/' . $this->repo; 76 | } 77 | 78 | /** 79 | * @param string $user 80 | * 81 | * @return $this 82 | */ 83 | public function user($user) 84 | { 85 | $this->user = $user; 86 | return $this; 87 | } 88 | 89 | /** 90 | * @param string $password 91 | * 92 | * @return $this 93 | */ 94 | public function password($password) 95 | { 96 | $this->password = $password; 97 | return $this; 98 | } 99 | 100 | /** 101 | * @param string $token 102 | * 103 | * @return $this 104 | */ 105 | public function accessToken($token) 106 | { 107 | $this->accessToken = $token; 108 | return $this; 109 | } 110 | 111 | /** 112 | * @param string $uri 113 | * @param array $params 114 | * @param string $method 115 | * 116 | * @return array 117 | * 118 | * @throws \Robo\Exception\TaskException 119 | */ 120 | protected function sendRequest($uri, $params = [], $method = 'POST') 121 | { 122 | if (!$this->owner or !$this->repo) { 123 | throw new TaskException($this, 'Repo URI is not set'); 124 | } 125 | 126 | $ch = curl_init(); 127 | $url = sprintf('%s/repos/%s/%s', self::GITHUB_URL, $this->getUri(), $uri); 128 | $this->printTaskInfo($url); 129 | $this->printTaskInfo('{method} {url}', ['method' => $method, 'url' => $url]); 130 | 131 | if (!empty($this->user)) { 132 | curl_setopt($ch, CURLOPT_USERPWD, $this->user . ':' . $this->password); 133 | } 134 | 135 | if (!empty($this->accessToken)) { 136 | curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: ' . sprintf('token %s', $this->accessToken)]); 137 | } 138 | 139 | curl_setopt_array( 140 | $ch, 141 | array( 142 | CURLOPT_URL => $url, 143 | CURLOPT_RETURNTRANSFER => true, 144 | CURLOPT_POST => $method != 'GET', 145 | CURLOPT_POSTFIELDS => json_encode($params), 146 | CURLOPT_FOLLOWLOCATION => true, 147 | CURLOPT_USERAGENT => "Robo" 148 | ) 149 | ); 150 | 151 | $output = curl_exec($ch); 152 | $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 153 | $response = json_decode($output); 154 | 155 | $this->printTaskInfo($output); 156 | return [$code, $response]; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/Task/Development/OpenBrowser.php: -------------------------------------------------------------------------------- 1 | taskOpenBrowser('http://localhost') 17 | * ->run(); 18 | * 19 | * // open two browser windows 20 | * $this->taskOpenBrowser([ 21 | * 'http://localhost/mysite', 22 | * 'http://localhost/mysite2' 23 | * ]) 24 | * ->run(); 25 | * ``` 26 | */ 27 | class OpenBrowser extends BaseTask 28 | { 29 | /** 30 | * @var string[] 31 | */ 32 | protected $urls = []; 33 | 34 | /** 35 | * @param string|string[] $url 36 | */ 37 | public function __construct($url) 38 | { 39 | $this->urls = (array) $url; 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function run() 46 | { 47 | $openCommand = $this->getOpenCommand(); 48 | 49 | if (empty($openCommand)) { 50 | return Result::error($this, 'no suitable browser opening command found'); 51 | } 52 | 53 | foreach ($this->urls as $url) { 54 | passthru(sprintf($openCommand, ProcessUtils::escapeArgument($url))); 55 | $this->printTaskInfo('Opened {url}', ['url' => $url]); 56 | } 57 | 58 | return Result::success($this); 59 | } 60 | 61 | /** 62 | * @return null|string 63 | */ 64 | private function getOpenCommand() 65 | { 66 | if (defined('PHP_WINDOWS_VERSION_MAJOR')) { 67 | return 'start "web" explorer "%s"'; 68 | } 69 | 70 | passthru('which xdg-open', $linux); 71 | passthru('which open', $osx); 72 | 73 | if (0 === $linux) { 74 | return 'xdg-open %s'; 75 | } 76 | 77 | if (0 === $osx) { 78 | return 'open %s'; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Task/Development/PhpServer.php: -------------------------------------------------------------------------------- 1 | taskServer(8000) 14 | * ->dir('public') 15 | * ->run(); 16 | * 17 | * // run with IP 0.0.0.0 18 | * $this->taskServer(8000) 19 | * ->host('0.0.0.0') 20 | * ->run(); 21 | * 22 | * // execute server in background 23 | * $this->taskServer(8000) 24 | * ->background() 25 | * ->run(); 26 | * ?> 27 | * ``` 28 | */ 29 | class PhpServer extends Exec 30 | { 31 | /** 32 | * @var int 33 | */ 34 | protected $port; 35 | 36 | /** 37 | * @var string 38 | */ 39 | protected $host = '127.0.0.1'; 40 | 41 | /** 42 | * {@inheritdoc} 43 | */ 44 | protected $command = 'php -S %s:%d '; 45 | 46 | /** 47 | * @param int $port 48 | */ 49 | public function __construct($port) 50 | { 51 | $this->port = $port; 52 | 53 | if (strtolower(PHP_OS) === 'linux') { 54 | $this->command = 'exec php -S %s:%d '; 55 | } 56 | } 57 | 58 | /** 59 | * @param string $host 60 | * 61 | * @return $this 62 | */ 63 | public function host($host) 64 | { 65 | $this->host = $host; 66 | return $this; 67 | } 68 | 69 | /** 70 | * @param string $path 71 | * 72 | * @return $this 73 | */ 74 | public function dir($path) 75 | { 76 | $this->command .= "-t $path"; 77 | return $this; 78 | } 79 | 80 | /** 81 | * {@inheritdoc} 82 | */ 83 | public function getCommand() 84 | { 85 | return sprintf($this->command . $this->arguments, $this->host, $this->port); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Task/Development/Tasks.php: -------------------------------------------------------------------------------- 1 | task(Changelog::class, $filename); 15 | } 16 | 17 | /** 18 | * @param string $filename 19 | * 20 | * @return \Robo\Task\Development\GenerateMarkdownDoc|\Robo\Collection\CollectionBuilder 21 | */ 22 | protected function taskGenDoc($filename) 23 | { 24 | return $this->task(GenerateMarkdownDoc::class, $filename); 25 | } 26 | 27 | /** 28 | * @param string $className 29 | * @param string $wrapperClassName 30 | * 31 | * @return \Robo\Task\Development\GenerateTask|\Robo\Collection\CollectionBuilder 32 | */ 33 | protected function taskGenTask($className, $wrapperClassName = '') 34 | { 35 | return $this->task(GenerateTask::class, $className, $wrapperClassName); 36 | } 37 | 38 | /** 39 | * @param string $pathToSemVer 40 | * 41 | * @return \Robo\Task\Development\SemVer|\Robo\Collection\CollectionBuilder 42 | */ 43 | protected function taskSemVer($pathToSemVer = '.semver') 44 | { 45 | return $this->task(SemVer::class, $pathToSemVer); 46 | } 47 | 48 | /** 49 | * @param int $port 50 | * 51 | * @return \Robo\Task\Development\PhpServer|\Robo\Collection\CollectionBuilder 52 | */ 53 | protected function taskServer($port = 8000) 54 | { 55 | return $this->task(PhpServer::class, $port); 56 | } 57 | 58 | /** 59 | * @param string $filename 60 | * 61 | * @return \Robo\Task\Development\PackPhar|\Robo\Collection\CollectionBuilder 62 | */ 63 | protected function taskPackPhar($filename) 64 | { 65 | return $this->task(PackPhar::class, $filename); 66 | } 67 | 68 | /** 69 | * @param string $tag 70 | * 71 | * @return \Robo\Task\Development\GitHubRelease|\Robo\Collection\CollectionBuilder 72 | */ 73 | protected function taskGitHubRelease($tag) 74 | { 75 | return $this->task(GitHubRelease::class, $tag); 76 | } 77 | 78 | /** 79 | * @param string|array $url 80 | * 81 | * @return \Robo\Task\Development\OpenBrowser|\Robo\Collection\CollectionBuilder 82 | */ 83 | protected function taskOpenBrowser($url) 84 | { 85 | return $this->task(OpenBrowser::class, $url); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Task/Docker/Base.php: -------------------------------------------------------------------------------- 1 | getCommand(); 25 | return $this->executeCommand($command); 26 | } 27 | 28 | abstract public function getCommand(); 29 | } 30 | -------------------------------------------------------------------------------- /src/Task/Docker/Build.php: -------------------------------------------------------------------------------- 1 | taskDockerBuild()->run(); 11 | * 12 | * $this->taskDockerBuild('path/to/dir') 13 | * ->tag('database') 14 | * ->run(); 15 | * 16 | * ?> 17 | * 18 | * ``` 19 | * 20 | * Class Build 21 | * @package Robo\Task\Docker 22 | */ 23 | class Build extends Base 24 | { 25 | /** 26 | * @var string 27 | */ 28 | protected $path; 29 | 30 | /** 31 | * @var bool 32 | */ 33 | protected $buildKit = false; 34 | 35 | /** 36 | * @param string $path 37 | */ 38 | public function __construct($path = '.') 39 | { 40 | $this->command = "docker build"; 41 | $this->path = $path; 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | */ 47 | public function getCommand() 48 | { 49 | $command = $this->command; 50 | if ($this->buildKit) { 51 | $command = 'DOCKER_BUILDKIT=1 ' . $command; 52 | } 53 | return $command . ' ' . $this->arguments . ' ' . $this->path; 54 | } 55 | 56 | /** 57 | * @param string $tag 58 | * 59 | * @return $this 60 | */ 61 | public function tag($tag) 62 | { 63 | return $this->option('-t', $tag); 64 | } 65 | 66 | /** 67 | * @return $this 68 | */ 69 | public function enableBuildKit() 70 | { 71 | $this->buildKit = true; 72 | return $this; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Task/Docker/Commit.php: -------------------------------------------------------------------------------- 1 | taskDockerCommit($containerId) 10 | * ->name('my/database') 11 | * ->run(); 12 | * 13 | * // alternatively you can take the result from DockerRun task: 14 | * 15 | * $result = $this->taskDockerRun('db') 16 | * ->exec('./prepare_database.sh') 17 | * ->run(); 18 | * 19 | * $task->dockerCommit($result) 20 | * ->name('my/database') 21 | * ->run(); 22 | * ``` 23 | */ 24 | class Commit extends Base 25 | { 26 | /** 27 | * @var string 28 | */ 29 | protected $command = "docker commit"; 30 | 31 | /** 32 | * @var string 33 | */ 34 | protected $name; 35 | 36 | /** 37 | * @var string 38 | */ 39 | protected $cid; 40 | 41 | /** 42 | * @param string|\Robo\Task\Docker\Result $cidOrResult 43 | */ 44 | public function __construct($cidOrResult) 45 | { 46 | $this->cid = $cidOrResult instanceof Result ? $cidOrResult->getCid() : $cidOrResult; 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | public function getCommand() 53 | { 54 | return $this->command . ' ' . $this->cid . ' ' . $this->name . ' ' . $this->arguments; 55 | } 56 | 57 | /** 58 | * @param string $name 59 | * 60 | * @return $this 61 | */ 62 | public function name($name) 63 | { 64 | $this->name = $name; 65 | return $this; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Task/Docker/Exec.php: -------------------------------------------------------------------------------- 1 | taskDockerRun('test_env') 13 | * ->detached() 14 | * ->run(); 15 | * 16 | * $this->taskDockerExec($test) 17 | * ->interactive() 18 | * ->exec('./runtests') 19 | * ->run(); 20 | * 21 | * // alternatively use commands from other tasks 22 | * 23 | * $this->taskDockerExec($test) 24 | * ->interactive() 25 | * ->exec($this->taskCodecept()->suite('acceptance')) 26 | * ->run(); 27 | * ?> 28 | * ``` 29 | * 30 | */ 31 | class Exec extends Base 32 | { 33 | use CommandReceiver; 34 | 35 | /** 36 | * @var string 37 | */ 38 | protected $command = "docker exec"; 39 | 40 | /** 41 | * @var string 42 | */ 43 | protected $cid; 44 | 45 | /** 46 | * @var string 47 | */ 48 | protected $run = ''; 49 | 50 | /** 51 | * @param string|\Robo\Result $cidOrResult 52 | */ 53 | public function __construct($cidOrResult) 54 | { 55 | $this->cid = $cidOrResult instanceof Result ? $cidOrResult->getCid() : $cidOrResult; 56 | } 57 | 58 | /** 59 | * @return $this 60 | */ 61 | public function detached() 62 | { 63 | $this->option('-d'); 64 | return $this; 65 | } 66 | 67 | /** 68 | * {@inheritdoc} 69 | */ 70 | public function interactive($interactive = true) 71 | { 72 | if ($interactive) { 73 | $this->option('-i'); 74 | } 75 | return parent::interactive($interactive); 76 | } 77 | 78 | /** 79 | * @param string|\Robo\Contract\CommandInterface $command 80 | * 81 | * @return $this 82 | */ 83 | public function exec($command) 84 | { 85 | $this->run = $this->receiveCommand($command); 86 | return $this; 87 | } 88 | 89 | /** 90 | * {@inheritdoc} 91 | */ 92 | public function getCommand() 93 | { 94 | return $this->command . ' ' . $this->arguments . ' ' . $this->cid . ' ' . $this->run; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Task/Docker/Pull.php: -------------------------------------------------------------------------------- 1 | taskDockerPull('wordpress') 11 | * ->run(); 12 | * 13 | * ?> 14 | * ``` 15 | * 16 | */ 17 | class Pull extends Base 18 | { 19 | /** 20 | * @param string $image 21 | */ 22 | public function __construct($image) 23 | { 24 | $this->command = "docker pull $image "; 25 | } 26 | 27 | /** 28 | * {@inheritdoc} 29 | */ 30 | public function getCommand() 31 | { 32 | return $this->command . ' ' . $this->arguments; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Task/Docker/Remove.php: -------------------------------------------------------------------------------- 1 | taskDockerRemove($container) 11 | * ->run(); 12 | * ?> 13 | * ``` 14 | * 15 | */ 16 | class Remove extends Base 17 | { 18 | /** 19 | * @param string $container 20 | */ 21 | public function __construct($container) 22 | { 23 | $this->command = "docker rm $container "; 24 | } 25 | 26 | /** 27 | * {@inheritdoc} 28 | */ 29 | public function getCommand() 30 | { 31 | return $this->command . ' ' . $this->arguments; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Task/Docker/Result.php: -------------------------------------------------------------------------------- 1 | taskDockerStart($cidOrResult) 11 | * ->run(); 12 | * ?> 13 | * ``` 14 | */ 15 | class Start extends Base 16 | { 17 | /** 18 | * @var string 19 | */ 20 | protected $command = "docker start"; 21 | 22 | /** 23 | * @var null|string 24 | */ 25 | protected $cid; 26 | 27 | /** 28 | * @param string|\Robo\Task\Docker\Result $cidOrResult 29 | */ 30 | public function __construct($cidOrResult) 31 | { 32 | $this->cid = $cidOrResult instanceof Result ? $cidOrResult->getCid() : $cidOrResult; 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function getCommand() 39 | { 40 | return $this->command . ' ' . $this->arguments . ' ' . $this->cid; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Task/Docker/Stop.php: -------------------------------------------------------------------------------- 1 | taskDockerStop($cidOrResult) 11 | * ->run(); 12 | * ?> 13 | * ``` 14 | */ 15 | class Stop extends Base 16 | { 17 | /** 18 | * @var string 19 | */ 20 | protected $command = "docker stop"; 21 | 22 | /** 23 | * @var null|string 24 | */ 25 | protected $cid; 26 | 27 | /** 28 | * @param string|\Robo\Task\Docker\Result $cidOrResult 29 | */ 30 | public function __construct($cidOrResult) 31 | { 32 | $this->cid = $cidOrResult instanceof Result ? $cidOrResult->getCid() : $cidOrResult; 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function getCommand() 39 | { 40 | return $this->command . ' ' . $this->arguments . ' ' . $this->cid; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Task/Docker/Tasks.php: -------------------------------------------------------------------------------- 1 | task(Run::class, $image); 15 | } 16 | 17 | /** 18 | * @param string $image 19 | * 20 | * @return \Robo\Task\Docker\Pull|\Robo\Collection\CollectionBuilder 21 | */ 22 | protected function taskDockerPull($image) 23 | { 24 | return $this->task(Pull::class, $image); 25 | } 26 | 27 | /** 28 | * @param string $path 29 | * 30 | * @return \Robo\Task\Docker\Build|\Robo\Collection\CollectionBuilder 31 | */ 32 | protected function taskDockerBuild($path = '.') 33 | { 34 | return $this->task(Build::class, $path); 35 | } 36 | 37 | /** 38 | * @param string|\Robo\Task\Docker\Result $cidOrResult 39 | * 40 | * @return \Robo\Task\Docker\Stop|\Robo\Collection\CollectionBuilder 41 | */ 42 | protected function taskDockerStop($cidOrResult) 43 | { 44 | return $this->task(Stop::class, $cidOrResult); 45 | } 46 | 47 | /** 48 | * @param string|\Robo\Task\Docker\Result $cidOrResult 49 | * 50 | * @return \Robo\Task\Docker\Commit|\Robo\Collection\CollectionBuilder 51 | */ 52 | protected function taskDockerCommit($cidOrResult) 53 | { 54 | return $this->task(Commit::class, $cidOrResult); 55 | } 56 | 57 | /** 58 | * @param string|\Robo\Task\Docker\Result $cidOrResult 59 | * 60 | * @return \Robo\Task\Docker\Start|\Robo\Collection\CollectionBuilder 61 | */ 62 | protected function taskDockerStart($cidOrResult) 63 | { 64 | return $this->task(Start::class, $cidOrResult); 65 | } 66 | 67 | /** 68 | * @param string|\Robo\Task\Docker\Result $cidOrResult 69 | * 70 | * @return \Robo\Task\Docker\Remove|\Robo\Collection\CollectionBuilder 71 | */ 72 | protected function taskDockerRemove($cidOrResult) 73 | { 74 | return $this->task(Remove::class, $cidOrResult); 75 | } 76 | 77 | /** 78 | * @param string|\Robo\Task\Docker\Result $cidOrResult 79 | * 80 | * @return \Robo\Task\Docker\Exec|\Robo\Collection\CollectionBuilder 81 | */ 82 | protected function taskDockerExec($cidOrResult) 83 | { 84 | return $this->task(Exec::class, $cidOrResult); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Task/File/Concat.php: -------------------------------------------------------------------------------- 1 | taskConcat([ 16 | * 'web/assets/screen.css', 17 | * 'web/assets/print.css', 18 | * 'web/assets/theme.css' 19 | * ]) 20 | * ->to('web/assets/style.css') 21 | * ->run() 22 | * ?> 23 | * ``` 24 | */ 25 | class Concat extends BaseTask 26 | { 27 | use ResourceExistenceChecker; 28 | 29 | /** 30 | * @var array|\Iterator 31 | */ 32 | protected $files; 33 | 34 | /** 35 | * @var string 36 | */ 37 | protected $dst; 38 | 39 | /** 40 | * Constructor. 41 | * 42 | * @param array|\Iterator $files 43 | */ 44 | public function __construct($files) 45 | { 46 | $this->files = $files; 47 | } 48 | 49 | /** 50 | * set the destination file 51 | * 52 | * @param string $dst 53 | * 54 | * @return $this 55 | */ 56 | public function to($dst) 57 | { 58 | $this->dst = $dst; 59 | 60 | return $this; 61 | } 62 | 63 | /** 64 | * {@inheritdoc} 65 | */ 66 | public function run() 67 | { 68 | if (is_null($this->dst) || "" === $this->dst) { 69 | return Result::error($this, 'You must specify a destination file with to() method.'); 70 | } 71 | 72 | if (!$this->checkResources($this->files, 'file')) { 73 | return Result::error($this, 'Source files are missing!'); 74 | } 75 | 76 | if (file_exists($this->dst) && !is_writable($this->dst)) { 77 | return Result::error($this, 'Destination already exists and cannot be overwritten.'); 78 | } 79 | 80 | $dump = ''; 81 | 82 | foreach ($this->files as $path) { 83 | foreach (glob($path) as $file) { 84 | $dump .= file_get_contents($file) . "\n"; 85 | } 86 | } 87 | 88 | $this->printTaskInfo('Writing {destination}', ['destination' => $this->dst]); 89 | 90 | $dst = $this->dst . '.part'; 91 | $write_result = file_put_contents($dst, $dump); 92 | 93 | if (false === $write_result) { 94 | @unlink($dst); 95 | return Result::error($this, 'File write failed.'); 96 | } 97 | // Cannot be cross-volume; should always succeed. 98 | @rename($dst, $this->dst); 99 | 100 | return Result::success($this); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Task/File/Tasks.php: -------------------------------------------------------------------------------- 1 | task(Concat::class, $files); 15 | } 16 | 17 | /** 18 | * @param string $file 19 | * 20 | * @return \Robo\Task\File\Replace|\Robo\Collection\CollectionBuilder 21 | */ 22 | protected function taskReplaceInFile($file) 23 | { 24 | return $this->task(Replace::class, $file); 25 | } 26 | 27 | /** 28 | * @param string $file 29 | * 30 | * @return \Robo\Task\File\Write|\Robo\Collection\CollectionBuilder 31 | */ 32 | protected function taskWriteToFile($file) 33 | { 34 | return $this->task(Write::class, $file); 35 | } 36 | 37 | /** 38 | * @param string $filename 39 | * @param string $extension 40 | * @param string $baseDir 41 | * @param bool $includeRandomPart 42 | * 43 | * @return \Robo\Task\File\TmpFile|\Robo\Collection\CollectionBuilder 44 | */ 45 | protected function taskTmpFile($filename = 'tmp', $extension = '', $baseDir = '', $includeRandomPart = true) 46 | { 47 | return $this->task(TmpFile::class, $filename, $extension, $baseDir, $includeRandomPart); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Task/File/TmpFile.php: -------------------------------------------------------------------------------- 1 | collectionBuilder(); 19 | * $tmpFilePath = $collection->taskTmpFile() 20 | * ->line('-----') 21 | * ->line(date('Y-m-d').' '.$title) 22 | * ->line('----') 23 | * ->getPath(); 24 | * $collection->run(); 25 | * ?> 26 | * ``` 27 | */ 28 | class TmpFile extends Write implements CompletionInterface 29 | { 30 | /** 31 | * @param string $filename 32 | * @param string $extension 33 | * @param string $baseDir 34 | * @param bool $includeRandomPart 35 | */ 36 | public function __construct($filename = 'tmp', $extension = '', $baseDir = '', $includeRandomPart = true) 37 | { 38 | if (empty($baseDir)) { 39 | $baseDir = sys_get_temp_dir(); 40 | } 41 | if ($includeRandomPart) { 42 | $random = static::randomString(); 43 | $filename = "{$filename}_{$random}"; 44 | } 45 | $filename .= $extension; 46 | parent::__construct("{$baseDir}/{$filename}"); 47 | } 48 | 49 | /** 50 | * Generate a suitably random string to use as the suffix for our 51 | * temporary file. 52 | * 53 | * @param int $length 54 | * 55 | * @return string 56 | */ 57 | private static function randomString($length = 12) 58 | { 59 | return substr(str_shuffle('23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'), 0, $length); 60 | } 61 | 62 | /** 63 | * Delete this file when our collection completes. 64 | * If this temporary file is not part of a collection, 65 | * then it will be deleted when the program terminates, 66 | * presuming that it was created by taskTmpFile() or _tmpFile(). 67 | */ 68 | public function complete() 69 | { 70 | unlink($this->getPath()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Task/Filesystem/BaseDir.php: -------------------------------------------------------------------------------- 1 | dirs = $dirs 27 | : $this->dirs[] = $dirs; 28 | 29 | $this->fs = new sfFilesystem(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Task/Filesystem/CleanDir.php: -------------------------------------------------------------------------------- 1 | taskCleanDir(['tmp','logs'])->run(); 14 | * // as shortcut 15 | * $this->_cleanDir('app/cache'); 16 | * ?> 17 | * ``` 18 | */ 19 | class CleanDir extends BaseDir 20 | { 21 | use ResourceExistenceChecker; 22 | 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function run() 27 | { 28 | if (!$this->checkResources($this->dirs, 'dir')) { 29 | return Result::error($this, 'Source directories are missing!'); 30 | } 31 | foreach ($this->dirs as $dir) { 32 | $this->emptyDir($dir); 33 | $this->printTaskInfo("Cleaned {dir}", ['dir' => $dir]); 34 | } 35 | return Result::success($this); 36 | } 37 | 38 | /** 39 | * @param string $path 40 | */ 41 | protected function emptyDir($path) 42 | { 43 | $iterator = new \RecursiveIteratorIterator( 44 | new \RecursiveDirectoryIterator($path), 45 | \RecursiveIteratorIterator::CHILD_FIRST 46 | ); 47 | 48 | foreach ($iterator as $path) { 49 | if ($path->isDir()) { 50 | $dir = (string)$path; 51 | if (basename($dir) === '.' || basename($dir) === '..') { 52 | continue; 53 | } 54 | $this->fs->remove($dir); 55 | } else { 56 | $file = (string)$path; 57 | if (basename($file) === '.gitignore' || basename($file) === '.gitkeep') { 58 | continue; 59 | } 60 | $this->fs->remove($file); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Task/Filesystem/DeleteDir.php: -------------------------------------------------------------------------------- 1 | taskDeleteDir('tmp')->run(); 14 | * // as shortcut 15 | * $this->_deleteDir(['tmp', 'log']); 16 | * ?> 17 | * ``` 18 | */ 19 | class DeleteDir extends BaseDir 20 | { 21 | use ResourceExistenceChecker; 22 | 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function run() 27 | { 28 | if (!$this->checkResources($this->dirs, 'dir')) { 29 | return Result::error($this, 'Source directories are missing!'); 30 | } 31 | foreach ($this->dirs as $dir) { 32 | $this->fs->remove($dir); 33 | $this->printTaskInfo("Deleted {dir}...", ['dir' => $dir]); 34 | } 35 | return Result::success($this); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Task/Filesystem/MirrorDir.php: -------------------------------------------------------------------------------- 1 | taskMirrorDir(['dist/config/' => 'config/'])->run(); 13 | * // or use shortcut 14 | * $this->_mirrorDir('dist/config/', 'config/'); 15 | * 16 | * ?> 17 | * ``` 18 | */ 19 | class MirrorDir extends BaseDir 20 | { 21 | /** 22 | * {@inheritdoc} 23 | */ 24 | public function run() 25 | { 26 | foreach ($this->dirs as $src => $dst) { 27 | $this->fs->mirror( 28 | $src, 29 | $dst, 30 | null, 31 | [ 32 | 'override' => true, 33 | 'copy_on_windows' => true, 34 | 'delete' => true 35 | ] 36 | ); 37 | $this->printTaskInfo("Mirrored from {source} to {destination}", ['source' => $src, 'destination' => $dst]); 38 | } 39 | return Result::success($this); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Task/Filesystem/Tasks.php: -------------------------------------------------------------------------------- 1 | task(CleanDir::class, $dirs); 15 | } 16 | 17 | /** 18 | * @param string|string[] $dirs 19 | * 20 | * @return \Robo\Task\Filesystem\DeleteDir|\Robo\Collection\CollectionBuilder 21 | */ 22 | protected function taskDeleteDir($dirs) 23 | { 24 | return $this->task(DeleteDir::class, $dirs); 25 | } 26 | 27 | /** 28 | * @param string $prefix 29 | * @param string $base 30 | * @param bool $includeRandomPart 31 | * 32 | * @return \Robo\Task\Filesystem\WorkDir|\Robo\Collection\CollectionBuilder 33 | */ 34 | protected function taskTmpDir($prefix = 'tmp', $base = '', $includeRandomPart = true) 35 | { 36 | return $this->task(TmpDir::class, $prefix, $base, $includeRandomPart); 37 | } 38 | 39 | /** 40 | * @param string $finalDestination 41 | * 42 | * @return \Robo\Task\Filesystem\TmpDir|\Robo\Collection\CollectionBuilder 43 | */ 44 | protected function taskWorkDir($finalDestination) 45 | { 46 | return $this->task(WorkDir::class, $finalDestination); 47 | } 48 | 49 | /** 50 | * @param string|string[] $dirs 51 | * 52 | * @return \Robo\Task\Filesystem\CopyDir|\Robo\Collection\CollectionBuilder 53 | */ 54 | protected function taskCopyDir($dirs) 55 | { 56 | return $this->task(CopyDir::class, $dirs); 57 | } 58 | 59 | /** 60 | * @param string|string[] $dirs 61 | * 62 | * @return \Robo\Task\Filesystem\MirrorDir|\Robo\Collection\CollectionBuilder 63 | */ 64 | protected function taskMirrorDir($dirs) 65 | { 66 | return $this->task(MirrorDir::class, $dirs); 67 | } 68 | 69 | /** 70 | * @param string|string[] $dirs 71 | * 72 | * @return \Robo\Task\Filesystem\FlattenDir|\Robo\Collection\CollectionBuilder 73 | */ 74 | protected function taskFlattenDir($dirs) 75 | { 76 | return $this->task(FlattenDir::class, $dirs); 77 | } 78 | 79 | /** 80 | * @return \Robo\Task\Filesystem\FilesystemStack|\Robo\Collection\CollectionBuilder 81 | */ 82 | protected function taskFilesystemStack() 83 | { 84 | return $this->task(FilesystemStack::class); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Task/Gulp/Base.php: -------------------------------------------------------------------------------- 1 | option('silent'); 37 | return $this; 38 | } 39 | 40 | /** 41 | * adds `--no-color` option to gulp 42 | * 43 | * @return $this 44 | */ 45 | public function noColor() 46 | { 47 | $this->option('no-color'); 48 | return $this; 49 | } 50 | 51 | /** 52 | * adds `--color` option to gulp 53 | * 54 | * @return $this 55 | */ 56 | public function color() 57 | { 58 | $this->option('color'); 59 | return $this; 60 | } 61 | 62 | /** 63 | * adds `--tasks-simple` option to gulp 64 | * 65 | * @return $this 66 | */ 67 | public function simple() 68 | { 69 | $this->option('tasks-simple'); 70 | return $this; 71 | } 72 | 73 | /** 74 | * @param string $task 75 | * @param null|string $pathToGulp 76 | * 77 | * @throws \Robo\Exception\TaskException 78 | */ 79 | public function __construct($task, $pathToGulp = null) 80 | { 81 | $this->task = $task; 82 | $this->command = $pathToGulp; 83 | if (!$this->command) { 84 | $this->command = $this->findExecutable('gulp'); 85 | } 86 | if (!$this->command) { 87 | throw new TaskException(__CLASS__, "Gulp executable not found."); 88 | } 89 | } 90 | 91 | /** 92 | * @return string 93 | */ 94 | public function getCommand() 95 | { 96 | return "{$this->command} " . ProcessUtils::escapeArgument($this->task) . "{$this->arguments}"; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Task/Gulp/Run.php: -------------------------------------------------------------------------------- 1 | taskGulpRun()->run(); 14 | * 15 | * // run task 'clean' with --silent option 16 | * $this->taskGulpRun('clean') 17 | * ->silent() 18 | * ->run(); 19 | * ?> 20 | * ``` 21 | */ 22 | class Run extends Base implements CommandInterface 23 | { 24 | /** 25 | * {@inheritdoc} 26 | */ 27 | public function run() 28 | { 29 | if (strlen($this->arguments)) { 30 | $this->printTaskInfo('Running Gulp task: {gulp_task} with arguments: {arguments}', ['gulp_task' => $this->task, 'arguments' => $this->arguments]); 31 | } else { 32 | $this->printTaskInfo('Running Gulp task: {gulp_task} without arguments', ['gulp_task' => $this->task]); 33 | } 34 | return $this->executeCommand($this->getCommand()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Task/Gulp/Tasks.php: -------------------------------------------------------------------------------- 1 | task(Run::class, $task, $pathToGulp); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Task/Logfile/BaseLogfile.php: -------------------------------------------------------------------------------- 1 | logfiles = $logfiles 32 | : $this->logfiles[] = $logfiles; 33 | 34 | $this->filesystem = new Filesystem(); 35 | } 36 | 37 | /** 38 | * @param int $chmod 39 | * @return $this 40 | */ 41 | public function chmod(int $chmod) 42 | { 43 | $this->chmod = $chmod; 44 | 45 | return $this; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Task/Logfile/Shortcuts.php: -------------------------------------------------------------------------------- 1 | taskRotateLog($logfile)->run(); 17 | } 18 | 19 | /** 20 | * @param string|string[] $logfile 21 | * 22 | * @return \Robo\Result 23 | */ 24 | protected function _truncateLog($logfile): Result 25 | { 26 | return $this->taskTruncateLog($logfile)->run(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Task/Logfile/Tasks.php: -------------------------------------------------------------------------------- 1 | task(RotateLog::class, $logfile); 17 | } 18 | 19 | /** 20 | * @param string|string[] $logfile 21 | * 22 | * @return \Robo\Task\Logfile\TruncateLog|\Robo\Collection\CollectionBuilder 23 | */ 24 | protected function taskTruncateLog($logfile): CollectionBuilder 25 | { 26 | return $this->task(TruncateLog::class, $logfile); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Task/Logfile/TruncateLog.php: -------------------------------------------------------------------------------- 1 | taskTruncateLog(['logfile.log'])->run(); 13 | * // or use shortcut 14 | * $this->_truncateLog(['logfile.log']); 15 | * 16 | * ?> 17 | * ``` 18 | */ 19 | class TruncateLog extends BaseLogfile 20 | { 21 | /** 22 | * {@inheritdoc} 23 | */ 24 | public function run(): Result 25 | { 26 | foreach ($this->logfiles as $logfile) { 27 | $this->filesystem->dumpFile($logfile, false); 28 | if ($this->chmod) { 29 | $this->filesystem->chmod($logfile, $this->chmod); 30 | } 31 | $this->printTaskInfo("Truncated {logfile}", ['logfile' => $logfile]); 32 | } 33 | 34 | return Result::success($this); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Task/Npm/Base.php: -------------------------------------------------------------------------------- 1 | option('production'); 36 | return $this; 37 | } 38 | 39 | /** 40 | * @param null|string $pathToNpm 41 | * 42 | * @throws \Robo\Exception\TaskException 43 | */ 44 | public function __construct($pathToNpm = null) 45 | { 46 | $this->command = $pathToNpm; 47 | if (!$this->command) { 48 | $this->command = $this->findExecutable('npm'); 49 | } 50 | if (!$this->command) { 51 | throw new TaskException(__CLASS__, "Npm executable not found."); 52 | } 53 | } 54 | 55 | /** 56 | * @return string 57 | */ 58 | public function getCommand() 59 | { 60 | return "{$this->command} {$this->action}{$this->arguments}"; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Task/Npm/Install.php: -------------------------------------------------------------------------------- 1 | taskNpmInstall()->run(); 14 | * 15 | * // prefer dist with custom path 16 | * $this->taskNpmInstall('path/to/my/npm') 17 | * ->noDev() 18 | * ->run(); 19 | * ?> 20 | * ``` 21 | */ 22 | class Install extends Base implements CommandInterface 23 | { 24 | /** 25 | * {@inheritdoc} 26 | */ 27 | protected $action = 'install'; 28 | 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function run() 33 | { 34 | $this->printTaskInfo('Install Npm packages: {arguments}', ['arguments' => $this->arguments]); 35 | return $this->executeCommand($this->getCommand()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Task/Npm/Tasks.php: -------------------------------------------------------------------------------- 1 | task(Install::class, $pathToNpm); 15 | } 16 | 17 | /** 18 | * @param null|string $pathToNpm 19 | * 20 | * @return \Robo\Task\Npm\Update|\Robo\Collection\CollectionBuilder 21 | */ 22 | protected function taskNpmUpdate($pathToNpm = null) 23 | { 24 | return $this->task(Update::class, $pathToNpm); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Task/Npm/Update.php: -------------------------------------------------------------------------------- 1 | taskNpmUpdate()->run(); 12 | * 13 | * // prefer dist with custom path 14 | * $this->taskNpmUpdate('path/to/my/npm') 15 | * ->noDev() 16 | * ->run(); 17 | * ?> 18 | * ``` 19 | */ 20 | class Update extends Base 21 | { 22 | /** 23 | * {@inheritdoc} 24 | */ 25 | protected $action = 'update'; 26 | 27 | /** 28 | * {@inheritdoc} 29 | */ 30 | public function run() 31 | { 32 | $this->printTaskInfo('Update Npm packages: {arguments}', ['arguments' => $this->arguments]); 33 | return $this->executeCommand($this->getCommand()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Task/Remote/Tasks.php: -------------------------------------------------------------------------------- 1 | task(Rsync::class); 13 | } 14 | 15 | /** 16 | * @param null|string $hostname 17 | * @param null|string $user 18 | * 19 | * @return \Robo\Task\Remote\Ssh|\Robo\Collection\CollectionBuilder 20 | */ 21 | protected function taskSshExec($hostname = null, $user = null) 22 | { 23 | return $this->task(Ssh::class, $hostname, $user); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Task/Testing/Phpspec.php: -------------------------------------------------------------------------------- 1 | taskPhpspec() 16 | * ->format('pretty') 17 | * ->noInteraction() 18 | * ->run(); 19 | * ?> 20 | * ``` 21 | * 22 | */ 23 | class Phpspec extends BaseTask implements CommandInterface, PrintedInterface 24 | { 25 | use ExecOneCommand; 26 | 27 | /** 28 | * @var string 29 | */ 30 | protected $command; 31 | 32 | /** 33 | * @var string[] $formaters 34 | * Available formaters for format option. 35 | */ 36 | protected $formaters = ['progress', 'html', 'pretty', 'junit', 'dot', 'tap']; 37 | 38 | /** 39 | * @var array $verbose_levels 40 | * Available verbose levels. 41 | */ 42 | protected $verbose_levels = ['v', 'vv', 'vvv']; 43 | 44 | /** 45 | * Phpspec constructor. 46 | * 47 | * @param null|string $pathToPhpspec 48 | * 49 | * @throws \Robo\Exception\TaskException 50 | */ 51 | public function __construct($pathToPhpspec = null) 52 | { 53 | $this->command = $pathToPhpspec; 54 | if (!$this->command) { 55 | $this->command = $this->findExecutable('phpspec'); 56 | } 57 | if (!$this->command) { 58 | throw new \Robo\Exception\TaskException(__CLASS__, "Neither composer nor phar installation of Phpspec found"); 59 | } 60 | $this->arg('run'); 61 | } 62 | 63 | public function stopOnFail() 64 | { 65 | $this->option('stop-on-failure'); 66 | return $this; 67 | } 68 | 69 | public function noCodeGeneration() 70 | { 71 | $this->option('no-code-generation'); 72 | return $this; 73 | } 74 | 75 | public function quiet() 76 | { 77 | $this->option('quiet'); 78 | return $this; 79 | } 80 | 81 | /** 82 | * @param string $level 83 | * 84 | * @return $this 85 | */ 86 | public function verbose($level = 'v') 87 | { 88 | if (!in_array($level, $this->verbose_levels)) { 89 | throw new \InvalidArgumentException('expected ' . implode(',', $this->verbose_levels)); 90 | } 91 | $this->option('-' . $level); 92 | return $this; 93 | } 94 | 95 | /** 96 | * @return $this 97 | */ 98 | public function noAnsi() 99 | { 100 | $this->option('no-ansi'); 101 | return $this; 102 | } 103 | 104 | /** 105 | * @return $this 106 | */ 107 | public function noInteraction() 108 | { 109 | $this->option('no-interaction'); 110 | return $this; 111 | } 112 | 113 | /** 114 | * @param string $config_file 115 | * 116 | * @return $this 117 | */ 118 | public function config($config_file) 119 | { 120 | $this->option('config', $config_file); 121 | return $this; 122 | } 123 | 124 | /** 125 | * @param string $formater 126 | * 127 | * @return $this 128 | */ 129 | public function format($formater) 130 | { 131 | if (!in_array($formater, $this->formaters)) { 132 | throw new \InvalidArgumentException('expected ' . implode(',', $this->formaters)); 133 | } 134 | $this->option('format', $formater); 135 | return $this; 136 | } 137 | 138 | /** 139 | * {@inheritdoc} 140 | */ 141 | public function getCommand() 142 | { 143 | return $this->command . $this->arguments; 144 | } 145 | 146 | /** 147 | * {@inheritdoc} 148 | */ 149 | public function run() 150 | { 151 | $this->printTaskInfo('Running phpspec {arguments}', ['arguments' => $this->arguments]); 152 | return $this->executeCommand($this->getCommand()); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/Task/Testing/Tasks.php: -------------------------------------------------------------------------------- 1 | task(Codecept::class, $pathToCodeception); 15 | } 16 | 17 | /** 18 | * @param null|string $pathToPhpUnit 19 | * 20 | * @return \Robo\Task\Testing\PHPUnit|\Robo\Collection\CollectionBuilder 21 | */ 22 | protected function taskPhpUnit($pathToPhpUnit = null) 23 | { 24 | return $this->task(PHPUnit::class, $pathToPhpUnit); 25 | } 26 | 27 | /** 28 | * @param null|string $pathToPhpspec 29 | * 30 | * @return \Robo\Task\Testing\Phpspec|\Robo\Collection\CollectionBuilder 31 | */ 32 | protected function taskPhpspec($pathToPhpspec = null) 33 | { 34 | return $this->task(Phpspec::class, $pathToPhpspec); 35 | } 36 | 37 | /** 38 | * @param null|string $pathToAtoum 39 | * 40 | * @return \Robo\Task\Testing\Atoum|\Robo\Collection\CollectionBuilder 41 | */ 42 | protected function taskAtoum($pathToAtoum = null) 43 | { 44 | return $this->task(Atoum::class, $pathToAtoum); 45 | } 46 | 47 | /** 48 | * @param null|string $pathToBehat 49 | * 50 | * @return \Robo\Task\Testing\Behat|\Robo\Collection\CollectionBuilder 51 | */ 52 | protected function taskBehat($pathToBehat = null) 53 | { 54 | return $this->task(Behat::class, $pathToBehat); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Task/Vcs/HgStack.php: -------------------------------------------------------------------------------- 1 | hgStack 13 | * ->cloneRepo('https://bitbucket.org/durin42/hgsubversion') 14 | * ->pull() 15 | * ->add() 16 | * ->commit('changed') 17 | * ->push() 18 | * ->tag('0.6.0') 19 | * ->push('0.6.0') 20 | * ->run(); 21 | * ?> 22 | * ``` 23 | */ 24 | class HgStack extends CommandStack 25 | { 26 | 27 | /** 28 | * @param string $pathToHg 29 | */ 30 | public function __construct($pathToHg = 'hg') 31 | { 32 | $this->executable = $pathToHg; 33 | } 34 | 35 | /** 36 | * Executes `hg clone` 37 | * 38 | * @param string $repo 39 | * @param string $to 40 | * 41 | * @return $this 42 | */ 43 | public function cloneRepo($repo, $to = '') 44 | { 45 | return $this->exec(['clone', $repo, $to]); 46 | } 47 | 48 | /** 49 | * Executes `hg add` command with files to add by pattern 50 | * 51 | * @param string $include 52 | * @param string $exclude 53 | * 54 | * @return $this 55 | */ 56 | public function add($include = '', $exclude = '') 57 | { 58 | if (strlen($include) > 0) { 59 | $include = "-I {$include}"; 60 | } 61 | 62 | if (strlen($exclude) > 0) { 63 | $exclude = "-X {$exclude}"; 64 | } 65 | 66 | return $this->exec([__FUNCTION__, $include, $exclude]); 67 | } 68 | 69 | /** 70 | * Executes `hg commit` command with a message 71 | * 72 | * @param string $message 73 | * @param string $options 74 | * 75 | * @return $this 76 | */ 77 | public function commit($message, $options = '') 78 | { 79 | return $this->exec([__FUNCTION__, "-m '{$message}'", $options]); 80 | } 81 | 82 | /** 83 | * Executes `hg pull` command. 84 | * 85 | * @param string $branch 86 | * 87 | * @return $this 88 | */ 89 | public function pull($branch = '') 90 | { 91 | if (strlen($branch) > 0) { 92 | $branch = "-b '{$branch}''"; 93 | } 94 | 95 | return $this->exec([__FUNCTION__, $branch]); 96 | } 97 | 98 | /** 99 | * Executes `hg push` command 100 | * 101 | * @param string $branch 102 | * 103 | * @return $this 104 | */ 105 | public function push($branch = '') 106 | { 107 | if (strlen($branch) > 0) { 108 | $branch = "-b '{$branch}'"; 109 | } 110 | 111 | return $this->exec([__FUNCTION__, $branch]); 112 | } 113 | 114 | /** 115 | * Performs hg merge 116 | * 117 | * @param string $revision 118 | * 119 | * @return $this 120 | */ 121 | public function merge($revision = '') 122 | { 123 | if (strlen($revision) > 0) { 124 | $revision = "-r {$revision}"; 125 | } 126 | 127 | return $this->exec([__FUNCTION__, $revision]); 128 | } 129 | 130 | /** 131 | * Executes `hg tag` command 132 | * 133 | * @param string $tag_name 134 | * @param string $message 135 | * 136 | * @return $this 137 | */ 138 | public function tag($tag_name, $message = '') 139 | { 140 | if ($message !== '') { 141 | $message = "-m '{$message}'"; 142 | } 143 | return $this->exec([__FUNCTION__, $message, $tag_name]); 144 | } 145 | 146 | /** 147 | * {@inheritdoc} 148 | */ 149 | public function run() 150 | { 151 | $this->printTaskInfo('Running hg commands...'); 152 | return parent::run(); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/Task/Vcs/Shortcuts.php: -------------------------------------------------------------------------------- 1 | taskSvnStack()->checkout($url)->run(); 15 | } 16 | 17 | /** 18 | * @param string $url 19 | * 20 | * @return \Robo\Result 21 | */ 22 | protected function _gitClone($url) 23 | { 24 | return $this->taskGitStack()->cloneRepo($url)->run(); 25 | } 26 | 27 | /** 28 | * @param string $url 29 | * 30 | * @return \Robo\Result 31 | */ 32 | protected function _hgClone($url) 33 | { 34 | return $this->taskHgStack()->cloneRepo($url)->run(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Task/Vcs/SvnStack.php: -------------------------------------------------------------------------------- 1 | taskSvnStack() 15 | * ->checkout('http://svn.collab.net/repos/svn/trunk') 16 | * ->run() 17 | * 18 | * // alternatively 19 | * $this->_svnCheckout('http://svn.collab.net/repos/svn/trunk'); 20 | * 21 | * $this->taskSvnStack('username', 'password') 22 | * ->stopOnFail() 23 | * ->update() 24 | * ->add('doc/*') 25 | * ->commit('doc updated') 26 | * ->run(); 27 | * ?> 28 | * ``` 29 | */ 30 | class SvnStack extends CommandStack implements CommandInterface 31 | { 32 | /** 33 | * @var bool 34 | */ 35 | protected $stopOnFail = false; 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | protected $result; 41 | 42 | /** 43 | * @param string $username 44 | * @param string $password 45 | * @param string $pathToSvn 46 | */ 47 | public function __construct($username = '', $password = '', $pathToSvn = 'svn') 48 | { 49 | $this->executable = $pathToSvn; 50 | if (!empty($username)) { 51 | $this->executable .= " --username $username"; 52 | } 53 | if (!empty($password)) { 54 | $this->executable .= " --password $password"; 55 | } 56 | $this->result = Result::success($this); 57 | } 58 | 59 | /** 60 | * Updates `svn update` command 61 | * 62 | * @param string $path 63 | * 64 | * @return $this 65 | */ 66 | public function update($path = '') 67 | { 68 | return $this->exec("update $path"); 69 | } 70 | 71 | /** 72 | * Executes `svn add` command with files to add pattern 73 | * 74 | * @param string $pattern 75 | * 76 | * @return $this 77 | */ 78 | public function add($pattern = '') 79 | { 80 | return $this->exec("add $pattern"); 81 | } 82 | 83 | /** 84 | * Executes `svn commit` command with a message 85 | * 86 | * @param string $message 87 | * @param string $options 88 | * 89 | * @return $this 90 | */ 91 | public function commit($message, $options = "") 92 | { 93 | return $this->exec("commit -m '$message' $options"); 94 | } 95 | 96 | /** 97 | * Executes `svn checkout` command 98 | * 99 | * @param string $branch 100 | * 101 | * @return $this 102 | */ 103 | public function checkout($branch) 104 | { 105 | return $this->exec("checkout $branch"); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Task/Vcs/Tasks.php: -------------------------------------------------------------------------------- 1 | task(SvnStack::class, $username, $password, $pathToSvn); 17 | } 18 | 19 | /** 20 | * @param string $pathToGit 21 | * 22 | * @return \Robo\Task\Vcs\GitStack|\Robo\Collection\CollectionBuilder 23 | */ 24 | protected function taskGitStack($pathToGit = 'git') 25 | { 26 | return $this->task(GitStack::class, $pathToGit); 27 | } 28 | 29 | /** 30 | * @param string $pathToHg 31 | * 32 | * @return \Robo\Task\Vcs\HgStack|\Robo\Collection\CollectionBuilder 33 | */ 34 | protected function taskHgStack($pathToHg = 'hg') 35 | { 36 | return $this->task(HgStack::class, $pathToHg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/TaskAccessor.php: -------------------------------------------------------------------------------- 1 | task(Foo::class, $a, $b); 31 | * 32 | * instead of: 33 | * 34 | * $this->taskFoo($a, $b); 35 | * 36 | * The later form is preferred. 37 | * 38 | * @return \Robo\Collection\CollectionBuilder 39 | */ 40 | protected function task() 41 | { 42 | $args = func_get_args(); 43 | $name = array_shift($args); 44 | 45 | $collectionBuilder = $this->collectionBuilder(); 46 | return $collectionBuilder->build($name, $args); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/TaskInfo.php: -------------------------------------------------------------------------------- 1 | TaskInfo::formatTaskName($task), 18 | 'task' => $task, 19 | ]; 20 | } 21 | 22 | /** 23 | * @param object $task 24 | * 25 | * @return string 26 | */ 27 | public static function formatTaskName($task) 28 | { 29 | $name = get_class($task); 30 | $name = preg_replace('~Stack^~', '', $name); 31 | $name = str_replace('Robo\\Task\Base\\', '', $name); 32 | $name = str_replace('Robo\\Task\\', '', $name); 33 | $name = str_replace('Robo\\Collection\\', '', $name); 34 | return $name; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Tasks.php: -------------------------------------------------------------------------------- 1 |