2 use Symfony\Component\Finder\Finder;
4 class RoboFile extends \Robo\Tasks
7 * Run the Robo unit tests.
9 public function test(array $args, $options =
11 'coverage-html' => false,
15 $taskCodecept = $this->taskCodecept()
18 if ($options['coverage']) {
19 $taskCodecept->coverageXml('../../build/logs/clover.xml');
21 if ($options['coverage-html']) {
22 $taskCodecept->coverageHtml('../../build/logs/coverage');
25 return $taskCodecept->run();
31 * Run the PHP Codesniffer on a file or directory.
34 * A file or directory to analyze.
35 * @option $autofix Whether to run the automatic fixer or not.
36 * @option $strict Show warnings as well as errors.
37 * Default is to show only errors.
39 public function sniff(
46 $strict = $options['strict'] ? '' : '-n';
47 $result = $this->taskExec("./vendor/bin/phpcs --standard=PSR2 --exclude=Squiz.Classes.ValidClassName {$strict} {$file}")->run();
48 if (!$result->wasSuccessful()) {
49 if (!$options['autofix']) {
50 $options['autofix'] = $this->confirm('Would you like to run phpcbf to fix the reported errors?');
52 if ($options['autofix']) {
53 $result = $this->taskExec("./vendor/bin/phpcbf --standard=PSR2 --exclude=Squiz.Classes.ValidClassName {$file}")->run();
60 * Generate a new Robo task that wraps an existing utility class.
62 * @param $className The name of the existing utility class to wrap.
63 * @param $wrapperClassName The name of the wrapper class to create. Optional.
64 * @usage generate:task 'Symfony\Component\Filesystem\Filesystem' FilesystemStack
66 public function generateTask($className, $wrapperClassName = "")
68 return $this->taskGenTask($className, $wrapperClassName)->run();
74 public function release($opts = ['beta' => false])
76 $this->checkPharReadonly();
78 $version = \Robo\Robo::VERSION;
79 $stable = !$opts['beta'];
81 $version = preg_replace('/-.*/', '', $version);
84 $version = $this->incrementVersion($version, 'beta');
86 $this->writeVersion($version);
87 $this->yell("Releasing Robo $version");
92 ->commit("Robo release $version")
102 $this->taskGitStack()
104 ->push('origin master --tags')
108 $version = $this->incrementVersion($version) . '-dev';
109 $this->writeVersion($version);
111 $this->taskGitStack()
113 ->commit("Prepare for $version")
122 * Add an entry to the Robo CHANGELOG.md file.
124 * @param string $addition The text to add to the change log.
126 public function changed($addition)
128 $version = preg_replace('/-.*/', '', \Robo\Robo::VERSION);
129 return $this->taskChangelog()
136 * Update the version of Robo.
138 * @param string $version The new verison for Robo.
139 * Defaults to the next minor (bugfix) version after the current relelase.
140 * @option stage The version stage: dev, alpha, beta or rc. Use empty for stable.
142 public function versionBump($version = '', $options = ['stage' => ''])
144 // If the user did not specify a version, then update the current version.
145 if (empty($version)) {
146 $version = $this->incrementVersion(\Robo\Robo::VERSION, $options['stage']);
148 return $this->writeVersion($version);
152 * Write the specified version string back into the Robo.php file.
153 * @param string $version
155 protected function writeVersion($version)
157 // Write the result to a file.
158 return $this->taskReplaceInFile(__DIR__.'/src/Robo.php')
159 ->regex("#VERSION = '[^']*'#")
160 ->to("VERSION = '".$version."'")
165 * Advance to the next SemVer version.
167 * The behavior depends on the parameter $stage.
168 * - If $stage is empty, then the patch or minor version of $version is incremented
169 * - If $stage matches the current stage in the current version, then add one
170 * to the stage (e.g. alpha3 -> alpha4)
171 * - If $stage does not match the current stage in the current version, then
172 * reset to '1' (e.g. alpha4 -> beta1)
174 * @param string $version A SemVer version
175 * @param string $stage dev, alpha, beta, rc or an empty string for stable.
178 protected function incrementVersion($version, $stage = '')
180 $stable = empty($stage);
181 $versionStageNumber = '0';
182 preg_match('/-([a-zA-Z]*)([0-9]*)/', $version, $match);
183 $match += ['', '', ''];
184 $versionStage = $match[1];
185 $versionStageNumber = $match[2];
186 if ($versionStage != $stage) {
187 $versionStageNumber = 0;
189 $version = preg_replace('/-.*/', '', $version);
190 $versionParts = explode('.', $version);
192 $versionParts[count($versionParts)-1]++;
194 $version = implode('.', $versionParts);
196 $version .= '-' . $stage;
197 if ($stage != 'dev') {
198 $versionStageNumber++;
199 $version .= $versionStageNumber;
206 * Generate the Robo documentation files.
208 public function docs()
210 $collection = $this->collectionBuilder();
211 $collection->progressMessage('Generate documentation from source code.');
212 $files = Finder::create()->files()->name('*.php')->in('src/Task');
214 foreach ($files as $file) {
215 if ($file->getFileName() == 'loadTasks.php') {
218 if ($file->getFileName() == 'loadShortcuts.php') {
221 $ns = $file->getRelativePath();
225 $class = basename(substr($file, 0, -4));
226 class_exists($class = "Robo\\Task\\$ns\\$class");
227 $docs[$ns][] = $class;
231 foreach ($docs as $ns => $tasks) {
232 $taskGenerator = $collection->taskGenDoc("docs/tasks/$ns.md");
233 $taskGenerator->filterClasses(function (\ReflectionClass $r) {
234 return !($r->isAbstract() || $r->isTrait()) && $r->implementsInterface('Robo\Contract\TaskInterface');
235 })->prepend("# $ns Tasks");
237 foreach ($tasks as $class) {
238 $taskGenerator->docClass($class);
241 $taskGenerator->filterMethods(
242 function (\ReflectionMethod $m) {
243 if ($m->isConstructor() || $m->isDestructor() || $m->isStatic()) {
246 $undocumentedMethods =
252 'injectDependencies',
259 'setProgressIndicator',
260 'progressIndicatorSteps',
264 'setVerbosityThreshold',
265 'verbosityThreshold',
269 'verbosityMeetsThreshold',
284 return !in_array($m->name, $undocumentedMethods) && $m->isPublic(); // methods are not documented
286 )->processClassSignature(
288 return "## " . preg_replace('~Task$~', '', $c->getShortName()) . "\n";
290 )->processClassDocBlock(
291 function (\ReflectionClass $c, $doc) {
292 $doc = preg_replace('~@method .*?(.*?)\)~', '* `$1)` ', $doc);
293 $doc = str_replace('\\'.$c->name, '', $doc);
296 )->processMethodSignature(
297 function (\ReflectionMethod $m, $text) {
298 return str_replace('#### *public* ', '* `', $text) . '`';
300 )->processMethodDocBlock(
301 function (\ReflectionMethod $m, $text) {
303 return $text ? ' ' . trim(strtok($text, "\n"), "\n") : '';
307 $collection->progressMessage('Documentation generation complete.');
308 return $collection->run();
314 * Builds a site in gh-pages branch. Uses mkdocs
316 public function publish()
318 $current_branch = exec('git rev-parse --abbrev-ref HEAD');
320 return $this->collectionBuilder()
324 ->completion($this->taskGitStack()->checkout($current_branch))
325 ->taskFilesystemStack()
326 ->copy('CHANGELOG.md', 'docs/changelog.md')
327 ->completion($this->taskFilesystemStack()->remove('docs/changelog.md'))
328 ->taskExec('mkdocs gh-deploy')
333 * Build the Robo phar executable.
335 public function pharBuild()
337 $this->checkPharReadonly();
339 // Create a collection builder to hold the temporary
340 // directory until the pack phar task runs.
341 $collection = $this->collectionBuilder();
343 $workDir = $collection->tmpDir();
344 $roboBuildDir = "$workDir/robo";
346 // Before we run `composer install`, we will remove the dev
347 // dependencies that we only use in the unit tests. Any dev dependency
348 // that is in the 'suggested' section is used by a core task;
349 // we will include all of those in the phar.
350 $devProjectsToRemove = $this->devDependenciesToRemoveFromPhar();
352 // We need to create our work dir and run `composer install`
353 // before we prepare the pack phar task, so create a separate
354 // collection builder to do this step in.
355 $prepTasks = $this->collectionBuilder();
357 $preparationResult = $prepTasks
358 ->taskFilesystemStack()
363 __DIR__ . '/composer.json',
364 __DIR__ . '/scripts',
369 ->toPath($roboBuildDir)
373 ->taskComposerRemove()
377 ->args($devProjectsToRemove)
378 ->taskComposerInstall()
384 // Exit if the preparation step failed
385 if (!$preparationResult->wasSuccessful()) {
386 return $preparationResult;
389 // Decide which files we're going to pack
390 $files = Finder::create()->ignoreVCS(true)
393 ->name('*.exe') // for 1symfony/console/Resources/bin/hiddeninput.exe
394 ->name('GeneratedWrapper.tmpl')
398 ->notPath('/vendor\/.*\/[Tt]est/')
399 ->in(is_dir($roboBuildDir) ? $roboBuildDir : __DIR__);
403 ->taskPackPhar('robo.phar')
405 ->addFile('robo', 'robo')
407 ->taskFilesystemStack()
408 ->chmod('robo.phar', 0777)
412 protected function checkPharReadonly()
414 if (ini_get('phar.readonly')) {
415 throw new \Exception('Must set "phar.readonly = Off" in php.ini to build phars.');
420 * The phar:build command removes the project requirements from the
421 * 'require-dev' section that are not in the 'suggest' section.
425 protected function devDependenciesToRemoveFromPhar()
427 $composerInfo = (array) json_decode(file_get_contents(__DIR__ . '/composer.json'));
429 $devDependencies = array_keys((array)$composerInfo['require-dev']);
430 $suggestedProjects = array_keys((array)$composerInfo['suggest']);
432 return array_diff($devDependencies, $suggestedProjects);
438 * Installs the Robo phar executable in /usr/bin. Uses 'sudo'.
440 public function pharInstall()
442 return $this->taskExec('sudo cp')
444 ->arg('/usr/bin/robo')
451 * Commits the phar executable to Robo's GitHub pages site.
453 public function pharPublish()
457 $this->collectionBuilder()
458 ->taskFilesystemStack()
459 ->rename('robo.phar', 'robo-release.phar')
462 ->pull('origin site')
463 ->taskFilesystemStack()
464 ->remove('robotheme/robo.phar')
465 ->rename('robo-release.phar', 'robotheme/robo.phar')
467 ->add('robotheme/robo.phar')
468 ->commit('Update robo.phar to ' . \Robo\Robo::VERSION)
469 ->push('origin site')