474 lines
16 KiB
PHP
474 lines
16 KiB
PHP
<?php
|
|
use Symfony\Component\Finder\Finder;
|
|
|
|
class RoboFile extends \Robo\Tasks
|
|
{
|
|
/**
|
|
* Run the Robo unit tests.
|
|
*/
|
|
public function test(array $args, $options =
|
|
[
|
|
'coverage-html' => 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 --exclude=Squiz.Classes.ValidClassName {$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 --exclude=Squiz.Classes.ValidClassName {$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->checkPharReadonly();
|
|
|
|
$version = \Robo\Robo::VERSION;
|
|
$stable = !$opts['beta'];
|
|
if ($stable) {
|
|
$version = preg_replace('/-.*/', '', $version);
|
|
}
|
|
else {
|
|
$version = $this->incrementVersion($version, 'beta');
|
|
}
|
|
$this->writeVersion($version);
|
|
$this->yell("Releasing Robo $version");
|
|
|
|
$this->docs();
|
|
$this->taskGitStack()
|
|
->add('-A')
|
|
->commit("Robo release $version")
|
|
->pull()
|
|
->push()
|
|
->run();
|
|
|
|
if ($stable) {
|
|
$this->pharPublish();
|
|
}
|
|
$this->publish();
|
|
|
|
$this->taskGitStack()
|
|
->tag($version)
|
|
->push('origin master --tags')
|
|
->run();
|
|
|
|
if ($stable) {
|
|
$version = $this->incrementVersion($version) . '-dev';
|
|
$this->writeVersion($version);
|
|
|
|
$this->taskGitStack()
|
|
->add('-A')
|
|
->commit("Prepare for $version")
|
|
->push()
|
|
->run();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
$version = preg_replace('/-.*/', '', \Robo\Robo::VERSION);
|
|
return $this->taskChangelog()
|
|
->version($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.
|
|
* @option stage The version stage: dev, alpha, beta or rc. Use empty for stable.
|
|
*/
|
|
public function versionBump($version = '', $options = ['stage' => ''])
|
|
{
|
|
// If the user did not specify a version, then update the current version.
|
|
if (empty($version)) {
|
|
$version = $this->incrementVersion(\Robo\Robo::VERSION, $options['stage']);
|
|
}
|
|
return $this->writeVersion($version);
|
|
}
|
|
|
|
/**
|
|
* Write the specified version string back into the Robo.php file.
|
|
* @param string $version
|
|
*/
|
|
protected function writeVersion($version)
|
|
{
|
|
// Write the result to a file.
|
|
return $this->taskReplaceInFile(__DIR__.'/src/Robo.php')
|
|
->regex("#VERSION = '[^']*'#")
|
|
->to("VERSION = '".$version."'")
|
|
->run();
|
|
}
|
|
|
|
/**
|
|
* Advance to the next SemVer version.
|
|
*
|
|
* The behavior depends on the parameter $stage.
|
|
* - If $stage is empty, then the patch or minor version of $version is incremented
|
|
* - If $stage matches the current stage in the current version, then add one
|
|
* to the stage (e.g. alpha3 -> alpha4)
|
|
* - If $stage does not match the current stage in the current version, then
|
|
* reset to '1' (e.g. alpha4 -> beta1)
|
|
*
|
|
* @param string $version A SemVer version
|
|
* @param string $stage dev, alpha, beta, rc or an empty string for stable.
|
|
* @return string
|
|
*/
|
|
protected function incrementVersion($version, $stage = '')
|
|
{
|
|
$stable = empty($stage);
|
|
$versionStageNumber = '0';
|
|
preg_match('/-([a-zA-Z]*)([0-9]*)/', $version, $match);
|
|
$match += ['', '', ''];
|
|
$versionStage = $match[1];
|
|
$versionStageNumber = $match[2];
|
|
if ($versionStage != $stage) {
|
|
$versionStageNumber = 0;
|
|
}
|
|
$version = preg_replace('/-.*/', '', $version);
|
|
$versionParts = explode('.', $version);
|
|
if ($stable) {
|
|
$versionParts[count($versionParts)-1]++;
|
|
}
|
|
$version = implode('.', $versionParts);
|
|
if (!$stable) {
|
|
$version .= '-' . $stage;
|
|
if ($stage != 'dev') {
|
|
$versionStageNumber++;
|
|
$version .= $versionStageNumber;
|
|
}
|
|
}
|
|
return $version;
|
|
}
|
|
|
|
/**
|
|
* 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',
|
|
'setVerbosityThreshold',
|
|
'verbosityThreshold',
|
|
'setOutputAdapter',
|
|
'outputAdapter',
|
|
'hasOutputAdapter',
|
|
'verbosityMeetsThreshold',
|
|
'writeMessage',
|
|
'detectInteractive',
|
|
'background',
|
|
'timeout',
|
|
'idleTimeout',
|
|
'env',
|
|
'envVars',
|
|
'setInput',
|
|
'interactive',
|
|
'silent',
|
|
'printed',
|
|
'printOutput',
|
|
'printMetadata',
|
|
];
|
|
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()
|
|
{
|
|
$this->checkPharReadonly();
|
|
|
|
// 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)
|
|
->noScripts()
|
|
->printed(true)
|
|
->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();
|
|
}
|
|
|
|
protected function checkPharReadonly()
|
|
{
|
|
if (ini_get('phar.readonly')) {
|
|
throw new \Exception('Must set "phar.readonly = Off" in php.ini to build phars.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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();
|
|
}
|
|
}
|