false, 'coverage' => false ]) { $taskCodecept = $this->taskCodecept() ->args($args); if ($options['coverage']) { $taskCodecept->coverageXml('../../build/logs/clover.xml'); } if ($options['coverage-html']) { $taskCodecept->coverageHtml('../../build/logs/coverage'); } return $taskCodecept->run(); } /** * Code sniffer. * * Run the PHP Codesniffer on a file or directory. * * @param string $file * A file or directory to analyze. * @option $autofix Whether to run the automatic fixer or not. * @option $strict Show warnings as well as errors. * Default is to show only errors. */ public function sniff( $file = 'src/', $options = [ 'autofix' => false, 'strict' => false, ] ) { $strict = $options['strict'] ? '' : '-n'; $result = $this->taskExec("./vendor/bin/phpcs --standard=PSR2 {$strict} {$file}")->run(); if (!$result->wasSuccessful()) { if (!$options['autofix']) { $options['autofix'] = $this->confirm('Would you like to run phpcbf to fix the reported errors?'); } if ($options['autofix']) { $result = $this->taskExec("./vendor/bin/phpcbf --standard=PSR2 {$file}")->run(); } } return $result; } /** * Generate a new Robo task that wraps an existing utility class. * * @param $className The name of the existing utility class to wrap. * @param $wrapperClassName The name of the wrapper class to create. Optional. * @usage generate:task 'Symfony\Component\Filesystem\Filesystem' FilesystemStack */ public function generateTask($className, $wrapperClassName = "") { return $this->taskGenTask($className, $wrapperClassName)->run(); } /** * Release Robo. */ public function release($opts = ['beta' => false]) { $this->yell("Releasing Robo"); $stable = true; if ($opts['beta']) { $stable = false; $this->say('non-stable release'); } $this->docs(); $this->taskGitStack() ->add('-A') ->commit("auto-update") ->pull() ->push() ->run(); if ($stable) $this->pharPublish(); $this->publish(); $this->taskGitStack() ->tag(\Robo\Robo::VERSION) ->push('origin master --tags') ->run(); if ($stable) $this->versionBump(); } /** * Update changelog. * * Add an entry to the Robo CHANGELOG.md file. * * @param string $addition The text to add to the change log. */ public function changed($addition) { return $this->taskChangelog() ->version(\Robo\Robo::VERSION) ->change($addition) ->run(); } /** * Update the version of Robo. * * @param string $version The new verison for Robo. * Defaults to the next minor (bugfix) version after the current relelase. */ public function versionBump($version = '') { if (empty($version)) { $versionParts = explode('.', \Robo\Robo::VERSION); $versionParts[count($versionParts)-1]++; $version = implode('.', $versionParts); } return $this->taskReplaceInFile(__DIR__.'/src/Robo.php') ->from("VERSION = '".\Robo\Robo::VERSION."'") ->to("VERSION = '".$version."'") ->run(); } /** * Generate the Robo documentation files. */ public function docs() { $collection = $this->collectionBuilder(); $collection->progressMessage('Generate documentation from source code.'); $files = Finder::create()->files()->name('*.php')->in('src/Task'); $docs = []; foreach ($files as $file) { if ($file->getFileName() == 'loadTasks.php') { continue; } if ($file->getFileName() == 'loadShortcuts.php') { continue; } $ns = $file->getRelativePath(); if (!$ns) { continue; } $class = basename(substr($file, 0, -4)); class_exists($class = "Robo\\Task\\$ns\\$class"); $docs[$ns][] = $class; } ksort($docs); foreach ($docs as $ns => $tasks) { $taskGenerator = $collection->taskGenDoc("docs/tasks/$ns.md"); $taskGenerator->filterClasses(function (\ReflectionClass $r) { return !($r->isAbstract() || $r->isTrait()) && $r->implementsInterface('Robo\Contract\TaskInterface'); })->prepend("# $ns Tasks"); sort($tasks); foreach ($tasks as $class) { $taskGenerator->docClass($class); } $taskGenerator->filterMethods( function (\ReflectionMethod $m) { if ($m->isConstructor() || $m->isDestructor() || $m->isStatic()) { return false; } $undocumentedMethods = [ '', 'run', '__call', 'inflect', 'injectDependencies', 'getCommand', 'getPrinted', 'getConfig', 'setConfig', 'logger', 'setLogger', 'setProgressIndicator', 'progressIndicatorSteps', 'setBuilder', 'getBuilder', 'collectionBuilder', ]; return !in_array($m->name, $undocumentedMethods) && $m->isPublic(); // methods are not documented } )->processClassSignature( function ($c) { return "## " . preg_replace('~Task$~', '', $c->getShortName()) . "\n"; } )->processClassDocBlock( function (\ReflectionClass $c, $doc) { $doc = preg_replace('~@method .*?(.*?)\)~', '* `$1)` ', $doc); $doc = str_replace('\\'.$c->name, '', $doc); return $doc; } )->processMethodSignature( function (\ReflectionMethod $m, $text) { return str_replace('#### *public* ', '* `', $text) . '`'; } )->processMethodDocBlock( function (\ReflectionMethod $m, $text) { return $text ? ' ' . trim(strtok($text, "\n"), "\n") : ''; } ); } $collection->progressMessage('Documentation generation complete.'); return $collection->run(); } /** * Publish Robo. * * Builds a site in gh-pages branch. Uses mkdocs */ public function publish() { $current_branch = exec('git rev-parse --abbrev-ref HEAD'); return $this->collectionBuilder() ->taskGitStack() ->checkout('site') ->merge('master') ->completion($this->taskGitStack()->checkout($current_branch)) ->taskFilesystemStack() ->copy('CHANGELOG.md', 'docs/changelog.md') ->completion($this->taskFilesystemStack()->remove('docs/changelog.md')) ->taskExec('mkdocs gh-deploy') ->run(); } /** * Build the Robo phar executable. */ public function pharBuild() { // Create a collection builder to hold the temporary // directory until the pack phar task runs. $collection = $this->collectionBuilder(); $workDir = $collection->tmpDir(); $roboBuildDir = "$workDir/robo"; // Before we run `composer install`, we will remove the dev // dependencies that we only use in the unit tests. Any dev dependency // that is in the 'suggested' section is used by a core task; // we will include all of those in the phar. $devProjectsToRemove = $this->devDependenciesToRemoveFromPhar(); // We need to create our work dir and run `composer install` // before we prepare the pack phar task, so create a separate // collection builder to do this step in. $prepTasks = $this->collectionBuilder(); $preparationResult = $prepTasks ->taskFilesystemStack() ->mkdir($workDir) ->taskRsync() ->fromPath( [ __DIR__ . '/composer.json', __DIR__ . '/scripts', __DIR__ . '/src', __DIR__ . '/data' ] ) ->toPath($roboBuildDir) ->recursive() ->progress() ->stats() ->taskComposerRemove() ->dir($roboBuildDir) ->dev() ->noUpdate() ->args($devProjectsToRemove) ->taskComposerInstall() ->dir($roboBuildDir) ->printed(false) ->run(); // Exit if the preparation step failed if (!$preparationResult->wasSuccessful()) { return $preparationResult; } // Decide which files we're going to pack $files = Finder::create()->ignoreVCS(true) ->files() ->name('*.php') ->name('*.exe') // for 1symfony/console/Resources/bin/hiddeninput.exe ->name('GeneratedWrapper.tmpl') ->path('src') ->path('vendor') ->notPath('docs') ->notPath('/vendor\/.*\/[Tt]est/') ->in(is_dir($roboBuildDir) ? $roboBuildDir : __DIR__); // Build the phar return $collection ->taskPackPhar('robo.phar') ->addFiles($files) ->addFile('robo', 'robo') ->executable('robo') ->taskFilesystemStack() ->chmod('robo.phar', 0777) ->run(); } /** * The phar:build command removes the project requirements from the * 'require-dev' section that are not in the 'suggest' section. * * @return array */ protected function devDependenciesToRemoveFromPhar() { $composerInfo = (array) json_decode(file_get_contents(__DIR__ . '/composer.json')); $devDependencies = array_keys((array)$composerInfo['require-dev']); $suggestedProjects = array_keys((array)$composerInfo['suggest']); return array_diff($devDependencies, $suggestedProjects); } /** * Install Robo phar. * * Installs the Robo phar executable in /usr/bin. Uses 'sudo'. */ public function pharInstall() { return $this->taskExec('sudo cp') ->arg('robo.phar') ->arg('/usr/bin/robo') ->run(); } /** * Publish Robo phar. * * Commits the phar executable to Robo's GitHub pages site. */ public function pharPublish() { $this->pharBuild(); $this->collectionBuilder() ->taskFilesystemStack() ->rename('robo.phar', 'robo-release.phar') ->taskGitStack() ->checkout('site') ->pull('origin site') ->taskFilesystemStack() ->remove('robotheme/robo.phar') ->rename('robo-release.phar', 'robotheme/robo.phar') ->taskGitStack() ->add('robotheme/robo.phar') ->commit('Update robo.phar to ' . \Robo\Robo::VERSION) ->push('origin site') ->checkout('master') ->run(); } }