Add pear modules, mail and net_smtp via composer (#93)

Add pear modules, mail and net_smtp via composer, remove php 5.6 build due to phpunit 6
This commit is contained in:
Thilina Hasantha
2018-01-08 23:13:43 +01:00
committed by GitHub
parent 359e3f8382
commit e7792e7d79
2349 changed files with 117270 additions and 83170 deletions
@@ -1,11 +0,0 @@
### Steps to reproduce
What did you do?
### Expected behavior
Tell us what should happen
### Actual behavior
Tell us what happens instead
### System Configuration
Which O.S. and PHP version are you using?
@@ -1,13 +0,0 @@
### Overview
This pull request:
- [ ] Fixes a bug
- [ ] Adds a feature
- [ ] Breaks backwards compatibility
- [ ] Needs tests
### Summary
Short overview of what changed.
### Description
Any additional information.
@@ -1,9 +0,0 @@
vendor/
.idea/
build
site/
robotheme/
tests/_log/*
tests/_helpers/_generated/*
composer.phar
robo.phar
@@ -1,3 +0,0 @@
filter:
excluded_paths:
- "RoboFile.php"
-47
View File
@@ -1,47 +0,0 @@
language: php
branches:
# Only test the master branch and SemVer tags.
only:
- master
- /^[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+.*$/
matrix:
include:
- php: 7.0
env: dependencies=highest
- php: 5.6
- php: 5.5
- php: 5.5
env: dependencies=lowest
sudo: false
cache:
directories:
- $HOME/.composer/cache
before_script:
- if [ -z "$dependencies" ]; then composer install --prefer-dist; fi;
- if [ "$dependencies" = "lowest" ]; then composer update --prefer-dist --prefer-lowest -n; fi;
- if [ "$dependencies" = "highest" ]; then composer update --prefer-dist -n; fi;
script: "./robo test --coverage"
after_success:
- travis_retry php vendor/bin/coveralls -v
# Prior to a deploy, build a fresh robo.phar
before_deploy:
- ./robo phar:build
# Deploy instructions set up via `travis setup releases` per
# https://docs.travis-ci.com/user/deployment/releases
deploy:
provider: releases
api_key:
secure: EdmB1nW5gj5nggYfmHv20enSgvRIAl1PIWV5GKmkxAJwuummh3UqdI7z0ecTGdw2IBgJx9lizNvqhcWjXbpNhE9VaaT1sHFCKv4Zust6sLb9bneK3oLRdJk2wemfrrZQpdH900zA0o7b3CHVth8UhkrCB4FXVDjUW13K061EXG8=
file: robo.phar
skip_cleanup: true
on:
tags: true
+80 -11
View File
@@ -1,10 +1,79 @@
# Changelog
#### 1.0.4
### 1.2.1 12/28/2017
* Fixes to tests / build only.
### 1.2.0 12/12/2017
* Support Symfony 4 Components (#651)
* Test multiple composer dependency permutations with https://github.com/greg-1-anderson/composer-test-scenarios
### 1.1.5 10/25/2017
* Load option default values from $input for all options defined in the Application's input definition (#642)
* BUGFIX: Store global options in 'options' namespace rather than at the top level of config.
### 1.1.4 10/16/2017
* Update order of command event hooks so that the option settings are injected prior to configuration being injected, so that dynamic options are available for config injection. (#636)
* Add shallow clone method to GithubStack task. by Stefan Lange (#633)
* Make Changelog task more flexible. by Matthew Grasmick(#631)
* Adding accessToken() to GitHub task. by Matthew Grasmick (#630)
### 1.1.3 09/23/2017
* Add self:update command to update Robo phar distributions to the latest available version on GitHub. by Alexander Menk
* Fix Robo\Task\Docker\Base to implement CommandInterface. by Alexei Gorobet (#625)
* Add overwrite argument to Robo\Task\Filesystem\loadShortcuts.php::_rename by Alexei Gorobets (#624)
* Add failGroup() method for Codeception run command. by Max Gorovenko (#622)
* Set up composer-lock-updater on cron. (#618)
* Fix robo.yml loader by exporting processor instead of loader. By thomscode (#612)
### 1.1.2 07/28/2017
* Inject option default values in help (#607)
* Add noRebuild() method for Codeception run command. By Max Gorovenko (#603)
### 1.1.1 07/07/2017
* Add an option to wait an interval of time between parallel processes. By Gemma Pou #601
* Do not print dire messages about Robo bootstrap problems when a valid command (e.g. help, list, init, --version) runs. #502
### 1.1.0 06/29/2017
* Configuration for multiple commands or multiple tasks may now be shared by attaching the configuration values to the task namespace or the command group. #597
* *Breaking* Task configuration taken from property `task.PARTIAL_NAMESPACE.CLASSNAME.settings` instead of `task.CLASSNAME.settings`. Breaks backwards compatibility only with experimental configuration features introduced in version 1.0.6. Config is now stable, as of this release; there will be no more breaking config changes until Robo 2.0. #596
### 1.0.8 06/02/2017
* Fix regression in 1.0.7: Allow tasks to return results of types other than \Robo\Result. #585
* Allow Copydir exclude method to specify subfolders by Alex Skrypnyk #590
* Add composer init task, and general rounding out of composer tasks. #586
* Enhance SemVer task so that it can be used with files or strings. #589
#### 1.0.7 05/30/2017
* Add a state system for collections to allow tasks to pass state to later tasks.
* Ensure that task results are returned when in stopOnFail() mode.
* Make rawArg() and detectInteractive chainable. By Matthew Grasmick #553 #558
* [CopyDir] Use Symfony Filesystem. By malikkotob #555
* [Composer] Implement CommandInterface. By Ivan Borzenkov #561
#### 1.0.6 03/31/2017
* Add configuration features to inject values into commandline option and task setter methods. Experimental; incompatible changes may be introduced prior to the stable release of configuration in version 1.1.0.
#### 1.0.5 11/23/2016
* Incorporate word-wrapping from output-formatters 3.1.5
* Incorporate custom event handlers from annotated-command 2.2.0
#### 1.0.4 11/15/2016
* Updated to latest changes in `master` branch. Phar and tag issues.
#### 1.0.0
#### 1.0.0 10/10/2016
* [Collection] Add tasks to a collection, and implement them as a group with rollback
* Tasks may be added to a collection via `$collection->add($task);`
@@ -45,7 +114,7 @@
* Rename 'FileSystem' to 'Filesystem' wherever it occurs.
* Current directory is changed with `chdir` only if specified via the `--load-from` option (RC2)
#### 0.6.0
#### 0.6.0 10/30/2015
* Added `--load-from` option to make Robo start RoboFiles from other directories. Use it like `robo --load-from /path/to/where/RobFile/located`.
* Robo will not ask to create RoboFile if it does not exist, `init` command should be used.
@@ -54,7 +123,7 @@
* [FlattenDir] task added by @gabor-udvari
* Robo Runner can easily extended for custom runner by passing RoboClass and RoboFile parameters to constructor. By @rdeutz See #232
#### 0.5.4
#### 0.5.4 08/31/2015
* [WriteToFile] Fixed by @gabor-udvari: always writing to file regardless whether any changes were made or not. This can bring the taskrunner into an inifinite loop if a replaced file is being watched.
* [Scss] task added, requires `leafo/scssphp` library to compile by @gabor-udvari
@@ -63,7 +132,7 @@
* [Less] fixed passing closure as compiler by @pr0nbaer
* [Sass] task added by *2015-08-31*
#### 0.5.3
#### 0.5.3 07/15/2015
* [Rsync] Ability to use remote shell with identity file by @Mihailoff
* [Less] Task added by @burzum
@@ -75,7 +144,7 @@
* [Minify] Making it possible to pass options to the JS minification @burzum *2015-03-05*
* [CopyDir] Create destination recursively @boedah *2015-02-28*
#### 0.5.2
#### 0.5.2 02/24/2015
* [Phar] do not compress phar if more than 1000 files included (causes internal PHP error) *2015-02-24*
* _copyDir and _mirrorDir shortcuts fixed by @boedah *2015-02-24*
@@ -85,14 +154,14 @@
* [PhpServer] fixed passing arguments to server *2015-02-24*
#### 0.5.1
#### 0.5.1 01/27/2015
* [Exec] fixed execution of background jobs, processes persist till the end of PHP script *2015-01-27*
* [Ssh] Fixed SSH task by @Butochnikov *2015-01-27*
* [CopyDir] fixed shortcut usage by @boedah *2015-01-27*
* Added default value options for Configuration trait by @TamasBarta *2015-01-27*
#### 0.5.0
#### 0.5.0 01/22/2015
Refactored core
@@ -112,7 +181,7 @@ Refactored core
* [Docker] Tasks added
* [Gulp] Task added by @schorsch3000
#### 0.4.7
#### 0.4.7 12/26/2014
* [Minify] Task added by @Rarst. Requires additional dependencies installed *2014-12-26*
* [Help command is populated from annotation](https://github.com/consolidation-org/Robo/pull/71) by @jonsa *2014-12-26*
@@ -129,7 +198,7 @@ Refactored core
* [Rsync] Shell escape rsync exclude pattern by @boedah. Fixes #77 (BC break) *2014-12-26*
* [Npm] Task added by @AAlakkad *2014-12-26*
#### 0.4.6
#### 0.4.6 10/17/2014
* [Exec] Output from buffer is not spoiled by special chars *2014-10-17*
* [PHPUnit] detect PHPUnit on Windows or when is globally installed with Composer *2014-10-17*
@@ -139,7 +208,7 @@ Refactored core
* [ExecTask] now uses Executable trait with printed, dir, arg, option methods added *2014-08-12*
#### 0.4.5
#### 0.4.5 08/05/2014
* [Watch] bugfix: Watch only tracks last file if given array of files #46 *2014-08-05*
* All executable tasks can configure working directory with `dir` option
+2 -1
View File
@@ -10,6 +10,7 @@
[![License](https://poser.pugx.org/consolidation/robo/license.png)](https://www.versioneye.com/user/projects/57c4a6fe968d64004d97620a?child=57c4a6fe968d64004d97620a#tab-licenses)
[![Build Status](https://travis-ci.org/consolidation/Robo.svg?branch=master)](https://travis-ci.org/consolidation/Robo)
[![Windows CI](https://ci.appveyor.com/api/projects/status/0823hnh06pw8ir4d?svg=true)](https://ci.appveyor.com/project/greg-1-anderson/robo)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/consolidation/Robo/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/consolidation/Robo/?branch=master)
[![Dependency Status](https://www.versioneye.com/user/projects/57c4a6fe968d64004d97620a/badge.svg?style=flat-square)](https://www.versioneye.com/user/projects/57c4a6fe968d64004d97620a)
@@ -88,7 +89,7 @@ class RoboFile extends \Robo\Tasks
```
If you execute `robo` you will see this task added to list of available task with name: `test:acceptance`.
To execute it you shoud run `robo test:acceptance`. You may change path to selenium server by passing new path as a argument:
To execute it you should run `robo test:acceptance`. You may change path to selenium server by passing new path as a argument:
```
robo test:acceptance "C:\Downloads\selenium.jar"
+105 -20
View File
@@ -1,7 +1,5 @@
<?php
use Symfony\Component\Finder\Finder;
use Robo\Result;
use Robo\Collection\CollectionBuilder;
class RoboFile extends \Robo\Tasks
{
@@ -46,13 +44,13 @@ class RoboFile extends \Robo\Tasks
]
) {
$strict = $options['strict'] ? '' : '-n';
$result = $this->taskExec("./vendor/bin/phpcs --standard=PSR2 {$strict} {$file}")->run();
$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 {$file}")->run();
$result = $this->taskExec("./vendor/bin/phpcbf --standard=PSR2 --exclude=Squiz.Classes.ValidClassName {$file}")->run();
}
}
return $result;
@@ -75,30 +73,45 @@ class RoboFile extends \Robo\Tasks
*/
public function release($opts = ['beta' => false])
{
$this->yell("Releasing Robo");
$stable = true;
if ($opts['beta']) {
$stable = false;
$this->say('non-stable release');
$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("auto-update")
->commit("Robo release $version")
->pull()
->push()
->run();
if ($stable) $this->pharPublish();
if ($stable) {
$this->pharPublish();
}
$this->publish();
$this->taskGitStack()
->tag(\Robo\Robo::VERSION)
->tag($version)
->push('origin master --tags')
->run();
if ($stable) $this->versionBump();
if ($stable) {
$version = $this->incrementVersion($version) . '-dev';
$this->writeVersion($version);
$this->taskGitStack()
->add('-A')
->commit("Prepare for $version")
->push()
->run();
}
}
/**
@@ -110,8 +123,9 @@ class RoboFile extends \Robo\Tasks
*/
public function changed($addition)
{
$version = preg_replace('/-.*/', '', \Robo\Robo::VERSION);
return $this->taskChangelog()
->version(\Robo\Robo::VERSION)
->version($version)
->change($addition)
->run();
}
@@ -121,20 +135,71 @@ class RoboFile extends \Robo\Tasks
*
* @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 = '')
public function versionBump($version = '', $options = ['stage' => ''])
{
// If the user did not specify a version, then update the current version.
if (empty($version)) {
$versionParts = explode('.', \Robo\Robo::VERSION);
$versionParts[count($versionParts)-1]++;
$version = implode('.', $versionParts);
$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')
->from("VERSION = '".\Robo\Robo::VERSION."'")
->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.
*/
@@ -194,6 +259,25 @@ class RoboFile extends \Robo\Tasks
'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
}
@@ -289,7 +373,8 @@ class RoboFile extends \Robo\Tasks
->args($devProjectsToRemove)
->taskComposerInstall()
->dir($roboBuildDir)
->printed(false)
->noScripts()
->printed(true)
->run();
// Exit if the preparation step failed
+44 -23
View File
@@ -9,9 +9,6 @@
}
],
"autoload":{
"classmap": [
"scripts/composer/ScriptHandler.php"
],
"psr-4":{
"Robo\\":"src"
}
@@ -26,43 +23,67 @@
"php": ">=5.5.0",
"league/container": "^2.2",
"consolidation/log": "~1",
"consolidation/annotated-command": "^2.0.1",
"consolidation/output-formatters": "^2.1.2|~3",
"symfony/finder": "~2.5|~3.0",
"symfony/console": "~2.8|~3.0",
"symfony/process": "~2.5|~3.0",
"symfony/filesystem": "~2.5|~3.0",
"symfony/event-dispatcher": "~2.5|~3.0"
"consolidation/config": "^1.0.1",
"consolidation/annotated-command": "^2.8.2",
"consolidation/output-formatters": "^3.1.13",
"grasmash/yaml-expander": "^1.3",
"symfony/finder": "^2.5|^3|^4",
"symfony/console": "^2.8|^3|^4",
"symfony/process": "^2.5|^3|^4",
"symfony/filesystem": "^2.5|^3|^4",
"symfony/event-dispatcher": "^2.5|^3|^4"
},
"require-dev": {
"patchwork/jsqueeze": "~2",
"henrikbjorn/lurker": "~1",
"natxet/CssMin": "~3",
"natxet/CssMin": "3.0.4",
"pear/archive_tar": "^1.4.2",
"codeception/base": "^2.2.6",
"codeception/base": "^2.3.7",
"codeception/verify": "^0.3.2",
"codeception/aspect-mock": "~1",
"satooshi/php-coveralls": "~1",
"squizlabs/php_codesniffer": "~2",
"phpunit/php-code-coverage": "~2|~4"
"codeception/aspect-mock": "^1|^2.1.1",
"greg-1-anderson/composer-test-scenarios": "^1",
"satooshi/php-coveralls": "^2",
"phpunit/php-code-coverage": "~2|~4",
"squizlabs/php_codesniffer": "^2.8"
},
"scripts": {
"pre-install-cmd": [
"Robo\\composer\\ScriptHandler::checkDependencies"
"cs": "./robo sniff",
"unit": "./robo test",
"lint": [
"find src -name '*.php' -print0 | xargs -0 -n1 php -l",
"find tests/src -name '*.php' -print0 | xargs -0 -n1 php -l"
],
"cs": "/robo sniff",
"test": "./robo test"
"test": [
"@lint",
"@unit",
"@cs"
],
"scenario": "scenarios/install",
"pre-install-cmd": [
"Robo\\composer\\ScriptHandler::checkDependencies"
],
"post-update-cmd": [
"create-scenario symfony4 'symfony/console:^4.0' --platform-php '7.1.3'",
"create-scenario symfony2 'symfony/console:^2.8' --platform-php '5.5.9' --no-lockfile"
]
},
"config": {
"optimize-autoloader": true,
"sort-packages": true,
"platform": {
"php": "5.6.3"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
"dev-master": "1.x-dev",
"dev-state": "1.x-dev"
}
},
"suggest": {
"pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively.",
"henrikbjorn/lurker": "For monitoring filesystem changes in taskWatch",
"patchwork/jsqueeze": "For minifying JS files in taskMinify",
"natxet/CssMin": "For minifying JS files in taskMinify"
"natxet/CssMin": "For minifying CSS files in taskMinify"
},
"replace": {
"codegyre/robo": "< 1.0"
+1085 -737
View File
File diff suppressed because it is too large Load Diff
@@ -1,250 +0,0 @@
# Collection Builders
Robo provides task collections as a means of making error detection and recovery easier. When Robo tasks are added to a collection, their execution is deferred until the `$collection->run()` method is called. If one of the tasks fail, then the operation will be aborted; rollback tasks may also be defined to restore the system to its original condition.
When using collections, a Robo script will go through three phases:
1. Determine which tasks will need to be run, and create a task builder.
- Assign values to variables.
- Do not alter the state of the system.
2. Create the necessary tasks via the task builder.
- Use variables calculated in the first phase in task parameters.
3. Run the tasks via the `run()` method.
- Check and report errors once after `run()` returns.
Following this pattern will keep your code linear and easy to understand.
## Collections API
Collections are made up of a combination of tasks and/or `callable` functions / method pointers, such as:
- A task (implements TaskInterface)
- A function name (string)
- A closure (inline function)
- A method reference (array with object and method name)
Examples of adding different kinds of tasks to a collection are provided below.
### TaskInterface Objects
```php
<?php
$collection->add(
$this->taskExec('ls')
);
?>
```
### Functions
```php
<?php
$collection->addCode('mytaskfunction');
?>
```
### Closures
```php
<?php
$collection->addCode(
function() use ($work)
{
// do something with $work
});
?>
```
### Methods
```php
<?php
$collection->addCode([$myobject, 'mymethod']);
?>
```
## Using a Collection Builder
To manage a collection of tasks, a collection builder. Collection builders allow tasks to be created via chained methods. All of the tasks created by the same builder are added to a collection; when the `run()` method is called, all of the tasks in the collection run.
The 'publish' command from Robo's own RoboFile is shown below. It uses a collection builder to run some git and filesystem operations. The "completion" tasks are run after all other tasks complete, or during rollback processing when an operation fails.
``` php
<?php
class RoboFile extends \Robo\Tasks
{
public function publish()
{
$current_branch = exec('git rev-parse --abbrev-ref HEAD');
$collection = $this->collectionBuilder();
$collection->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');
return $collection;
}
}
?>
```
The example above also adds a couple of tasks as "completions"; these are run when the collection completes execution, as explained below.
## Rollbacks and Completions
Robo also provides rollbacks and completions, special tasks that are eligible to run only if all of the tasks added to the collection before them succeed. The section below explains the circumstances under which these tasks will run.
### Completion Tasks
Completions run whenever their collection completes or fails, but only if all of the tasks that come before it succeed. An example of this is shown in the first example above. A filesystem stack task copies CHANDELOG.md to docs/changelog.md; after this task is added to the collection, another filesystem stack task is added as a completion to delete docs/changelog.md. This is done because docs/changelog.md is only intended to exist long enough to be used by the `mkdocs` task, which is added later.
### Rollback Tasks
In addition to completions, Robo also supports rollbacks. Rollback tasks can be used to clean up after failures, so the state of the system does not change when execution is interrupted by an error. A rollback task is executed if all of the tasks that come before it succeed, and at least one of the tasks that come after it fails. If all tasks succeed, then no rollback tasks are executed.
### Rollback and Completion Methods
Any task may also implement \Robo\Contract\RollbackInterface; if this is done, then its `rollback()` method will be called if the task is `run()` on a collection that later fails.
Use `addAsCompletion($collection)` in place of `addAsRollback($collection)`, or implement \Robo\Contract\CompletionInterface. Completions otherwise work exactly like rollbacks.
## Temporary Objects
Since the concept of temporary objects that are cleaned up on failure is a common pattern, Robo provides built-in support for them. Temporary directories and files are provided out of the box; other kinds of temporary objects can be easily created using the Temporary global collection.
### Temporary Directories
It is recommended that operations that perform multiple filesystem operations should, whenever possible, do most of their work in a temporary directory. Temporary directories are created by `$this->taskTmpDir()`, and are automatically be removed when the collection completes or rolls back. As an added convenience, the CollectionBuilder class has a `tmpDir()` method that creates a temporary directory via `taskTmpDir()`, and then returns the path to the temporary directory.
``` php
<?php
class RoboFile extends \Robo\Tasks
{
function myOperation()
{
$collection = $this->collectionBuilder();
// Create a temporary directory, and fetch its path.
$work = $collection->tmpDir();
$collection
->taskWriteToFile("$work/README.md")
->line('-----')
->line(date('Y-m-d').' Generated file: do not edit.')
->line('----');
// If all of the preceding tasks succeed, then rename the temporary
// directory to its final name.
$collection->taskFilesystemStack()
->rename($work, 'destination');
return $collection->run();
}
}
?>
```
In the previous example, the path to the temporary directory is stored in the variable `$work`, and is passed as needed to the parameters of the other tasks as they are added to the collection. After the task collection is run, the temporary directory will be automatically deleted. In the example above, the temporary directory is renamed by the last task in the collection. This allows the working directory to persist; the collection will still attempt to remove the working directory, but no errors will be thrown if it no longer exists in its original location. Following this pattern allows Robo scripts to easily and safely do work that cleans up after itself on failure, without introducing a lot of branching or additional error recovery code. This paradigm is common enough to warrant a shortcut method of accomplishing the same thing. The example below is identical to the one above, save for the fact that it uses the `workDir()` method instead of `tmpDir()`. `workDir()` renames the temporary directory to its final name if the collection completes; any directory that exists in the same location will be overwritten at that time, but will persist if the collection roles back.
``` php
<?php
class RoboFile extends \Robo\Tasks
{
function myOperation()
{
$collection = $this->collectionBuilder();
// Create a temporary directory, and fetch its path.
// If all of the tasks succeed, then rename the temporary directory
// to its final name.
$work = $collection->workDir('destination');
$collection
->taskWriteToFile("$work/README.md")
->line('-----')
->line(date('Y-m-d').' Generated file: do not edit.')
->line('----');
return $collection->run();
}
}
?>
```
Temporary directories may also be created via the shortcut `$this->_tmpDir();`. Temporary directories created in this way are deleted when the script terminates.
### Temporary Files
Robo also provides an API for creating temporary files. They may be created via `$this->taskTmpFile()`; they are used exactly like `$this->taskWrite()`, except they are given a random name on creation, and are deleted when their collection completes. If they are not added to a collection, then they are deleted when the script terminates.
### The Temporary Global Collection
Robo maintains a special collection called the Temporary global collection. This collection is used to keep track of temporary objects that are not part of any collection. For example, Robo temporary directories and temporary files are managed by the Temporary global collection. These temporary objects are cleaned up automatically when the script terminates.
It is easy to create your own temporary tasks that behave in the same way as the provided temporary directory and temporary file tasks. There are two steps required:
- Implement \Robo\Contract\CompletionInterface
- Wrap the task via Temporary::wrap()
For example, the implementation of taskTmpFile() looks like this:
``` php
<?php
protected function taskTmpFile($filename = 'tmp', $extension = '', $baseDir = '', $includeRandomPart = true)
{
return Temporary::wrap(new TmpFile($filename, $extension, $baseDir, $includeRandomPart));
}
?>
```
The `complete()` method of the task will be called once the Collection the temporary object is attached to finishes running. If the temporary is not added to a collection, then its `complete()` method will be called when the script terminates.
## Named Tasks
It is also possible to provide names for the tasks added to a collection. This has two primary benefits:
1. Any result data returned from a named task is stored in the Result object under the task name.
2. It is possible for other code to add more tasks before or after any named task.
This feature is useful if you have functions that create task collections, and return them as a function results. The original caller can then use the `$collection->before()` or `$collection->after()` to insert sequenced tasks into the set of operations to be performed. One reason this might be done would be to define a base set of operations to perform (e.g. in a deploy), and then apply modifications for other environments (e.g. dev or stage).
```php
<?php
$collection->addCode(
function() use ($work)
{
// do something with $work
},
"taskname");
?>
```
Given a collection with named tasks, it is possible to insert more tasks before or after a task of a given name.
```php
<?php
$collection->after("taskname",
function() use ($work)
{
// do something with $work after "taskname" executes, if it succeeds.
});
?>
```
```php
<?php
$collection->before("taskname",
function() use ($work)
{
// do something with $work before "taskname" executes.
});
?>
```
It is recommended that named tasks be avoided unless specifically needed.
-191
View File
@@ -1,191 +0,0 @@
# Extending
Robo tasks can be added to your Robo application by using Composer to suppliment the set of built-in tasks that Robo provides by default. To find existing Robo task extensions, search in Packagist for projects of type [robo-tasks](https://packagist.org/search/?type=robo-tasks).
The convention used to add new tasks for use in your RoboFiles is to create a wrapper trait named `loadTasks` that instantiates the implementation class for each task. Each task method in the trait should start with the prefix `task`, and should use **chained method calls** for configuration. Task execution should be triggered by the method `run`.
To include additional tasks in your RoboFile, you must `use` the appropriate `loadTasks` in your RoboFile. See the section [Including Additional Tasks](#including-additional-tasks) below. To create your own Robo extension that provides tasks for use in RoboFiles, then you must write your own class that implements TaskInterface, and create a `loadTasks` trait for it as described in the section [Creating a Robo Extension](#creating-a-robo-extension).
## Including Additional Tasks
Additional tasks may be installed into projects that have included Robo via Composer. For example:
```
$ cd myproject
$ composer require boedah/robo-drush
```
If any of the tasks you include require external Composer projects themselves, then you must `composer require` these as well. See the `suggests` section of Robo's composer.json file for a list of some projects you might need to require.
Once the extension you wish to use has been added to your vendor directory, you may then include it from your RoboFile:
``` php
class RoboFile extends \Robo\Tasks
{
use \Boedah\Robo\Task\Drush\loadTasks;
public function test()
{
// ...
}
}
```
Once you have done this, all of the tasks defined in the extension you selected will be available for use in your commands.
Note that at the moment, it is not possible to extend Robo when using the robo.phar. This capability may be added in the future via [embedded composer](https://github.com/dflydev/dflydev-embedded-composer).
## Creating a Robo Extension
A Robo tasks extension is created by advertising a Composer package of type `robo-tasks` on [Packagist](https://packagist.org/). For an overview on how this is done, see the article [Creating your very own Composer Package](https://knpuniversity.com/screencast/question-answer-day/create-composer-package). Specific instructions for creating Robo task extensions are provided below.
### Create your composer.json File
Your composer.json file should look something like the example below:
```
{
"name": "boedah/robo-drush",
"description": "Drush CommandStack for Robo Task Runner",
"type": "robo-tasks",
"autoload": {
"psr-4": {
"Boedah\\Robo\\Task\\Drush\\": "src"
}
},
"require": {
"php": ">=5.5.0",
"consolidation/robo": "~1"
}
}
```
Customize the name and autoload paths as necessary, and add any additional required projects needed by the tasks that your extensions will provide. The type of your project should always be `robo-tasks`. Robo only supports php >= 5.5.0; you may require a higher version of php if necessary.
### Create the loadTasks.php Trait
It is recommended to place your trait-loading task in a `loadTasks` file in the same namespace as the task implementation.
```
namespace Boedah\Robo\Task\Drush;
use Robo\Container\SimpleServiceProvider;
trait loadTasks
{
/**
* @param string $pathToDrush
* @return DrushStack
*/
protected function taskDrushStack($pathToDrush = 'drush')
{
return $this->task(__FUNCTION__, $pathToDrush);
}
}
```
Note that the name of the service for a given task must start with the word "task", and must have the same name as the function used to call the task. `$this->task()` looks up the service by name; using the PHP built-in constant __FUNCTION__ for this parameter ensures that the names of these items remain in alignment.
### Task implementation
The implementation of each task class should extend \Robo\Task\BaseTask, or some class that extends the same, and should used chained initializer methods and defer all operations that alter the state of the system until its `run()` method. If you follow these patterns, then your task extensions will be usable via Robo collection builders, as explained in the [collections](collections.md) documentation.
There are many examples of task implementations in the Robo\Task namespace. A very basic task example is provided below. The namespace is `MyAssetTasks`, and the example task is `CompileAssets`. To customize to your purposes, choose an appropriate namespace, and then define as many tasks as you need.
``` php
<?php
namespace MyAssetTasks;
trait loadTasks
{
/**
* Example task to compile assets
*
* @param string $pathToCompileAssets
* @return \MyAssetTasks\CompileAssets
*/
protected function taskCompileAssets($path = null)
{
// Always construct your tasks with the `task()` task builder.
return $this->task(CompileAssets::class, $path);
}
}
class CompileAssets implements \Robo\Contract\TaskInterface
{
// configuration params
protected $path;
protected $to;
function __construct($path)
{
$this->path = $path;
}
function to($filename)
{
$this->to = $filename;
// must return $this
return $this;
}
// must implement Run
function run()
{
//....
}
}
?>
```
To use the tasks you define in a RoboFile, use its `loadTasks` trait as explained in the section [Including Additional Tasks](#including-additional-tasks), above.
### TaskIO
To allow tasks access IO, use the `Robo\Common\TaskIO` trait, or inherit your task class from `Robo\Task\BaseTask` (recommended).
Inside tasks you should print process details with `printTaskInfo`, `printTaskSuccess`, and `printTaskError`.
```
$this->printTaskInfo('Processing...');
```
The Task IO methods send all output through a PSR-3 logger. Tasks should use task IO exclusively; methods such as 'say' and 'ask' should reside in the command method. This allows tasks to be usable in any context that has a PSR-3 logger, including background or server processes where it is not possible to directly query the user.
### Tasks That Use Tasks
If one task implementation needs to use other tasks while it is running, it should do so via a `CollectionBuilder` object, as explained in the [Collections](collections.md) documentation.
To obtain access to a `CollectionBuilder`, a task should implement `BuilderAwareInterface` and use `BuilderAwareTrait`. It will then have access to a collection builder via the `$this->collectionBuilder()` method.
### Testing Extensions
If you wish to use the `task()` methods from your `loadTasks` trait in your unit tests, it is necessary to also use the Robo `TaskAccessor` trait, and define a `collectionBuilder()` method to provide a builder. Collection builders are used to initialize all Robo tasks. The easiest way to get a usable collection builder in your tests is to initialize Robo's default dependency injection container, and use it to request a new builder.
An example of how to do this in a PHPUnit test is shown below.
```
use League\Container\ContainerAwareInterface;
use League\Container\ContainerAwareTrait;
use Symfony\Component\Console\Output\NullOutput;
use Robo\TaskAccessor;
use Robo\Robo;
class DrushStackTest extends \PHPUnit_Framework_TestCase implements ContainerAwareInterface
{
use \Boedah\Robo\Task\Drush\loadTasks;
use TaskAccessor;
use ContainerAwareTrait;
// Set up the Robo container so that we can create tasks in our tests.
function setup()
{
$container = Robo::createDefaultContainer(null, new NullOutput());
$this->setContainer($container);
}
// Scaffold the collection builder
public function collectionBuilder()
{
$emptyRobofile = new \Robo\Tasks;
return $this->getContainer()->get('collectionBuilder', [$emptyRobofile]);
}
public function testYesIsAssumed()
{
$command = $this->taskDrushStack()
->drush('command')
->getCommand();
$this->assertEquals('drush command -y', $command);
}
}
```
To assert that the output of a command contains some value, use a `Symfony\Component\Console\Output\BufferedOutput` in place of null output when calling Robo::createDefaultContainer().
@@ -1,89 +0,0 @@
# Robo as a Framework
There are multiple ways to use and package Robo scripts; a few of the alternatives are presented below.
## Creating a Standalone Phar with Robo
It is possible to create a standalone phar that is implemented with Robo; doing this does not require the RoboFile to be located in the current working directory, or any particular location within your project. To achieve this, first set up your project as shown in the section [Implementing Composer Scripts with Robo](getting-started.md#implementing-composer-scripts-with-robo). Use of the "scripts" section is optional.
Next, add an "autoload" section to your composer.json to provide a namespace for your Robo commands:
```
{
"name": "myorg/myproject",
"require": {
"consolidation/Robo": "~1"
},
"autoload":{
"psr-4":{
"MyProject\\":"src"
}
}
}
```
Create a new file for your Robo commands, e.g. `class RoboFile` in `namespace MyProject\Commands;` in the file `src\Commands\RoboFile.php`. Optionally, add more task libraries as described in the [extending](extending.md) document.
Create a startup script similar to the one below, and add it to the root of your project, or some other location of your choosing:
``` php
#!/usr/bin/env php
<?php
/**
* If we're running from phar load the phar autoload file.
*/
$pharPath = \Phar::running(true);
if ($pharPath) {
require_once "$pharPath/vendor/autoload.php";
} else {
if (file_exists(__DIR__.'/vendor/autoload.php')) {
require_once __DIR__.'/vendor/autoload.php';
} elseif (file_exists(__DIR__.'/../../autoload.php')) {
require_once __DIR__ . '/../../autoload.php';
}
}
$commandClasses = [ \MyProject\Commands\RoboFile::class ];
$statusCode = \Robo\Robo::run(
$_SERVER['argv'],
$commandClasses,
'MyAppName',
'0.0.0-alpha0'
);
exit($statusCode);
```
When using Robo as a framework, the Robo file should be included in the autoloader, as Robo does not include a `RoboFile.php` file when used in this mode. Instead, specify the class or classes to load as a parameter to the Robo::run() method. By default, all output will be sent to a Symfony ConsoleOutput() that Robo will create for you. If you would like to use some other OutputInterface to capture the output, it may be specified via an optional fifth parameter.
Use [box-project/box2](https://github.com/box-project/box2) to create a phar for your application. Note that if you use Robo's taskPackPhar to create your phar, then `\Phar::running()` will always return an empty string due to a bug in this phar builder. If you encounter any problems with this, then hardcode the path to your autoload file. See the [robo](https://github.com/consolidation-org/Robo/blob/master/robo) script for details.
## Using Multiple RoboFiles in a Standalone Application
It is possible to provide as many command classes as you wish to the Robo `Runner()` constructor. You might wish to separate your Robo command implementations into separate Robo files if you have a lot of commands, or if you wish to group similar commands together in the same source file. If you do this, you can simply add more class references to the `$commandClasses` variable shown above.
```
$commandClasses = [
\MyProject\Commands\BuildCommands::class,
\MyProject\Commands\DeployCommands::class
];
```
If your application has a large number of command files, or if it supports command extensions, then you might wish to use the Command Discovery class to locate your files. The `CommandFileDiscovery` class will use the Symfony Finder class to search for all filenames matching the provided search pattern. It will return a list of class names using the provided base namespace.
``` php
$discovery = new \Consolidation\AnnotatedCommand\CommandFileDiscovery();
$discovery->setSearchPattern('*Command.php');
$commandClasses = $discovery->discover('php/MyProject/Commands', '\MyProject\Commands');
```
Pass the resulting `$commandClasses` to the `Runner()` constructor as shown above. See the annotated-commands project for more information about the different options that the discovery command takes.
## Using Your Own Dependency Injection Container with Robo (Advanced)
It is also possible to completely replace the Robo application with your own. To do this, set up your project as described in the sections above, but replace the Robo runner with your own main event loop.
Create the Robo dependency injection container:
```
use League\Container\Container;
$input = new \Symfony\Component\Console\Input\ArgvInput($argv);
$output = new \Symfony\Component\Console\Output\ConsoleOutput();
$conf = new \Robo\Config(); \\ or use your own subclass
$app = new \My\Application();
$container = \Robo\Robo::createDefaultContainer($input, $output, $app, $conf);
```
If you are using League\Container (recommended), then you may simply add and share your own classes to the same container. If you are using some other DI container, then you should use [delegate lookup](https://github.com/container-interop/fig-standards/blob/master/proposed/container.md#14-additional-feature-delegate-lookup) to combine them.
@@ -1,418 +0,0 @@
# Getting Started
To begin you need to create a RoboFile. Just run `robo init` in your project directory:
```
cd myproject
robo init
```
Your project directory may start out empty; Robo will create a new `RoboFile.php` for you. There will be RoboFile class which extends `\Robo\Tasks`, which includes all bundled tasks of Robo.
``` php
<?php
class RoboFile extends \Robo\Tasks
{
}
?>
```
## Commands
All public methods of the RoboFile class will be treated as **commands**. You can run them from the CLI and pass arguments.
``` php
<?php
class RoboFile extends \Robo\Tasks
{
function hello($world)
{
$this->say("Hello, $world");
}
}
?>
```
When we run:
```
robo hello davert
➜ Hello, davert
```
Method names should be camelCased. In CLI `camelCased` method will be available as `camel:cased` command.
`longCamelCased` method will be transformed to `long:camel-cased` command.
**Note:** This assumes you have installed Robo by downloading the [robo.phar](http://robo.li/robo.phar) file and copied it to a directory in your $PATH. For example, `cp robo.phar ~/bin/robo`.
### Arguments
All method parameters without default values are treated as required arguments. In our example command `hello` requires one argument.
If you pass a default value to parameter the argument becomes optional:
``` php
<?php
function hello($world = 'world')
{
$this->say("Hello, $world");
}
?>
```
```
robo hello
➜ Hello, world
```
To accept multiple, variable arguments, define a parameter as an `array`; Robo will then pass all CLI arguments in this variable:
``` php
<?php
function hello(array $world)
{
$this->say("Hello, " . implode(', ', $world));
}
?>
```
```
robo hello davert jon bill bob
➜ Hello, davert, jon, bill, bob
```
### Options
To define command options you should define the last method parameter as an associative array where the keys define the option names and the values provide each option's default values:
``` php
<?php
function hello($opts = ['silent' => false])
{
if (!$opts['silent']) $this->say("Hello, world");
}
?>
```
```
robo hello
➜ Hello, world
robo hello --silent
```
A one-character shortcut can be specified for option:
``` php
<?php
function hello($opts = ['silent|s' => false])
{
if (!$opts['silent']) $this->say("Hello, world");
}
?>
```
Now command can be executed with '-s' to run in silent mode:
```
robo hello -s
```
### Load From Other Directories
Robo can execute commands from a RoboFile located in different directory.
You can specify the path to another RoboFile by including the `--load-from` option:
```
robo run --load-from /path/to/my/other/project
```
### Pass-Through Arguments
Sometimes you need to pass arguments from your command into a task. A command line after the `--` characters is treated as one argument.
Any special character like `-` will be passed into without change.
``` php
<?php
function ls($args)
{
$this->taskExec('ls')->args($args)->run();
}
?>
```
```
robo ls -- Robo -c --all
[Robo\Task\ExecTask] running ls Robo -c --all
. .. CHANGELOG.md codeception.yml composer.json composer.lock docs .git .gitignore .idea LICENSE README.md robo RoboFile.php robo.phar src tests .travis.yml vendor
```
### Help
The help text for a command in a RoboFile may be provided in Doc-Block comments. An example help Doc-Block comment is shown below:
``` php
<?php
/**
* Calculate the fibonacci sequence between two numbers.
*
* Graphic output will look like
* +----+---+-------------+
* | | | |
* | |-+-| |
* |----+-+-+ |
* | | |
* | | |
* | | |
* +--------+-------------+
*
* @param int $start Number to start from
* @param int $steps Number of steps to perform
* @param array $opts
* @option $graphic Display the sequence graphically using cube
* representation
*/
public function fibonacci($start, $steps, $opts = ['graphic' => false])
{
}
?>
```
The corresponding help text produced is:
```
robo fibonacci --help
Usage:
fibonacci [--graphic] start steps
Arguments:
start Number to start from
steps Number of steps to perform
Options:
--graphic Display the sequence graphically using cube representation
Help:
Graphic output will look like
+----+---+-------------+
| | | |
| |-+-| |
|----+-+-+ |
| | |
| | |
| | |
+--------+-------------+
```
Arguments and options are populated from annotations.
Initially added with [PR by @jonsa](https://github.com/consolidation/Robo/pull/71); now provided by the [consolidation/annotated-command](https://github.com/consolidation/annotated-command) project, which was factored out from Robo.
### Ignored methods
Robo ignores any method of your RoboFile that begins with `get` or `set`. These methods are presumed to be data accessors, not commands. To implement a command whose name contains `get` or `set`, use the `@command` annotation.
``` php
<?php
/**
* @command set-alignment
*/
function setAlignment($value)
{
...
}
?>
```
## Tasks
Robo commands typically divide the work they need to accomplish into **tasks**. The command first determines what needs to be done, inspecting current state if necessary, and then sets up and executes one or more tasks that make the actual changes needed by the command. (See also the documentation on [Collections](collections.md), which allow you to combine groups of tasks which can provide rollback functions to recover from failure situations.)
For details on how to add custom tasks to Robo, see the [extending](extending.md) document.
### Shortcuts
Some tasks may have shortcuts. If a task does not require multi-step configuration, it can be executed with a single line:
```php
<?php
$this->_exec('ps aux');
$this->_copy('config/env.example.yml','config/env.yml');
?>
```
### Result
Each task must return an instance of `Robo\Result`. A Robo Result contains the task instance, exit code, message, and any variable data that the task may wish to return.
The `run` method of `CompileAssets` class may look like this:
```
return new Robo\Result($this, $exitCode, "Assets compiled");
```
or
```
return Robo\Result::success($this, "Assets compiled");
return Robo\Result::error($this, "Failed to compile assets");
```
You can use this results to check if execution was successful, either using the `wasSuccessful()` method, or via the `invoke` shortcut. We will use the `Exec` task in next example to illustrate this:
``` php
<?php
class RoboFile
{
use Robo\Task\Base\loadShortcuts;
function test()
{
$res1 = $this->_exec('phpunit tests/integration');
$res2 = $this->_exec('phpunit tests/unit');
// print message when tests passed
if ($res1->wasSuccessful() and $res2->wasSuccessful()) $this->say("All tests passed");
}
}
?>
```
When making multi-step commands that call one task after another, it is best to use a collection to group the tasks together. The collection will handle error detection and rollback, and will return a single Result object when done. For more information, see the [Collections](collections.md) documentation.
Some tasks may also attach data to the Result object. If this is done, the data may be accessed as an array; for example, `$result['path'];`. This is not common.
Commands should return a Result object obtained from a task; this will ensure that the command exit code is set correctly. If a command does not have a Result object available, then it may use a ResultData object. ResultData objects are just like Result objects, except the do not contain a reference to a task.
return new Robo\ResultData($exitcode, 'Error message.');
If the command returns a TaskInterface instead of a result, then the task will be executed, and the result from that task will be used as the final result of the command. See also `Formatters`, below.
### Stack
Some tasks contain `Stack` in their name. These are called "stack" tasks, and they execute similar tasks one after the other. Each of the primary methods in a stack class executes an operation.
Stack tasks also contain a `stopOnFail` method which can be used to stop task execution if one of its commands was unsuccessful.
### Global StopOnFail
There is a global `stopOnFail` method as well, that can be used to stop a command on first failure of a task.
```
$this->stopOnFail(true);
```
### IO
As you noticed, you can print text via the `say` method, which is taken from the `Robo\Output` trait.
```
$this->say("Hello");
```
Also, you can ask for input from console:
```
$name = $this->ask("What is your name?");
```
There are also `askDefault`, `askHidden`, and `confirm` methods.
In addition, Robo makes all of the methods of Symfony Style available throgh the `io()` method:
$this->io()->title("Build all site assets");
This allows Robo scripts to follow the [Symfony Console Style Guide](http://symfony.com/blog/new-in-symfony-2-8-console-style-guide) if desired.
### Formatters
It is preferable for commands that look up and display information should avoid doing IO directly, and should instead return the data they wish to display as an array. This data can then be converted into different data formats, such as "table" and "json". The user may select which formatter to use via the --format option. For details on formatters, see the [consolidation/output-formatters](https://github.com/consolidation/output-formatters) project.
### Progress
Robo supports progress indicators via the Symfony ProgressBar class. Long-running tasks that wish to display the progress indicator may do so via four simple steps:
- Override the `progressIndicatorSteps()` method and return the number of "steps" in the operation.
- Call `$this->startProgressIndicator()` to begin the progress indicator running.
- Call `$this->advanceProgressIndicator()` a number of times equal to the result returned by `progressIndicatorSteps()`
- Call `$this->stopProgressIndicator()` when the operation is completed.
An example of this is shown below:
``` php
<?php
class MyTask extends BaseTask
{
protected $steps = 10;
public function progressIndicatorSteps()
{
return $this->steps;
}
public function run()
{
$exitCode = 0;
$errorMessage = "";
$this->startProgressIndicator();
for ($i = 0; $i < $this->steps; ++$i) {
$this->advanceProgressIndicator();
}
$this->stopProgressIndicator();
return new Result($this, $exitCode, $errorMessage, ['time' => $this->getExecutionTime()]);
}
}
?>
```
Tasks should not attempt to use a specific progress indicator (e.g. the Symfony ProgressBar class) directly, as the ProgressIndicatorAwareTrait allows for an appropriate progress indicator to be used (or omitted) as best suits the application.
Note that when using [Collections](collections.md), the progress bar will automatically be shown if the collection takes longer than two seconds to run. Each task in the collection will count for one "step"; if the task supports progress indicators as shown above, then it will add an additional number of steps as indicated by its `progressIndicatorSteps()` method.
## Working with Composer
### Adding a RoboFile to your Project
Robo is designed to work well with Composer. To use Robo scripts in your Composer-based project, simply add `robo` to your composer.json file:
```
$ cd myproject
$ composer require consolidation/Robo:~1
$ ./vendor/bin/robo mycommand
```
If you do not want to type the whole path to Robo, you may add `./vendor/bin` to your $PATH (relative paths work), or use `composer exec` to find and run Robo:
```
$ composer exec robo mycommand
```
### Implementing Composer Scripts with Robo
When using Robo in your project, it is convenient to define Composer scripts that call your Robo commands. Simply add the following to your composer.json file:
```
{
"name": "myorg/myproject",
"require": {
"consolidation/Robo": "~1"
},
"scripts": {
"test": "composer robo test",
"phar": "composer robo phar:build",
"robo": "robo --ansi --load-from $(pwd)/scripts/BuildCommands.php"
}
}
```
*Note*: When you include Robo as a library like this, some external projects used by certain core Robo tasks are not automatically included in your project. See the `"suggest":` section of Robo's composer.json for a list of external projects you might also want to require in your project.
Once you have set up your composer.json file (and ran `composer update` if you manually changed the `require` or `require-dev` sections), Composer will ensure that your project-local copy of Robo in the `vendor/bin` dir is in your $PATH when you run the additional Composer scripts that you declared:
```
$ cd myproject
$ composer test
$ composer phar
```
This will call the public methods `test()` and `phar()` in your RoboFile.php when using `composer test` and `composer phar`, respectively.
Advertising your build commands as Composer scripts is a useful way to provide the key commands used for testing, building or packaging your application. Also, if your application should happen to provide a commandline tool to perform the operations of the application itself, then defining your build commands in their own RoboFile provides desirable separation, keeping your build commands out of the help and list commands of your primary script.
If you would like to simplify the output of your script (e.g. when running on a CI service), replace the `--ansi` option in the example above with `--no-ansi`, and colored terminal output and progress bars will be disabled.
## Robo as a Framework
For an overview on how to turn your Robo scripts into standalone tools, see the example [robo.script](https://github.com/consolidation/Robo/blob/master/examples/robo.script), and the section [Robo as a Framework](framework.md).
-24
View File
@@ -1,24 +0,0 @@
# Robo Documentation
* [Getting Started](getting-started.md)
* [Collections](collections.md)
* [Extending](extending.md)
* [Robo as a Framework](framework.md)
## Tasks
* [ApiGen](tasks/ApiGen.md)
* [Archive](tasks/Archive.md)
* [Assets](tasks/Assets.md)
* [Base](tasks/Base.md)
* [Bower](tasks/Bower.md)
* [Composer](tasks/Composer.md)
* [Development](tasks/Development.md)
* [Docker](tasks/Docker.md)
* [File](tasks/File.md)
* [Filesystem](tasks/Filesystem.md)
* [Gulp](tasks/Gulp.md)
* [Npm](tasks/Npm.md)
* [Remote](tasks/Remote.md)
* [Testing](tasks/Testing.md)
* [Vcs](tasks/Vcs.md)
@@ -1,56 +0,0 @@
# ApiGen Tasks
## ApiGen
Executes ApiGen command to generate documentation
``` php
<?php
// ApiGen Command
$this->taskApiGen('./apigen.neon')
->templateConfig('vendor/apigen/apigen/templates/bootstrap/config.neon')
->wipeout(true)
->run();
?>
```
* `config($config)` * `param string` $config
* `source($src)` * `param array|string|Traversable` $src one or more source values
* `destination($dest)` * `param string` $dest
* `extensions($exts)` * `param array|string` $exts one or more extensions
* `exclude($exclude)` * `param array|string` $exclude one or more exclusions
* `skipDocPath($path)` * `param array|string|Traversable` $path one or more skip-doc-path values
* `skipDocPrefix($prefix)` * `param array|string|Traversable` $prefix one or more skip-doc-prefix values
* `charset($charset)` * `param array|string` $charset one or more charsets
* `mainProjectNamePrefix($name)` * `param string` $name
* `title($title)` * `param string` $title
* `baseUrl($baseUrl)` * `param string` $baseUrl
* `googleCseId($id)` * `param string` $id
* `googleAnalytics($trackingCode)` * `param string` $trackingCode
* `templateConfig($templateConfig)` * `param mixed` $templateConfig
* `allowedHtml($tags)` * `param array|string` $tags one or more supported html tags
* `groups($groups)` * `param string` $groups
* `autocomplete($types)` * `param array|string` $types or more supported autocomplete types
* `accessLevels($levels)` * `param array|string` $levels one or more access levels
* `internal($internal)` * `param boolean|string` $internal 'yes' or true if internal, 'no' or false if not
* `php($php)` * `param boolean|string` $php 'yes' or true to generate documentation for internal php classes,
* `tree($tree)` * `param bool|string` $tree 'yes' or true to generate a tree view of classes, 'no' or false otherwise
* `deprecated($dep)` * `param bool|string` $dep 'yes' or true to generate documentation for deprecated classes, 'no' or false otherwise
* `todo($todo)` * `param bool|string` $todo 'yes' or true to document tasks, 'no' or false otherwise
* `sourceCode($src)` * `param bool|string` $src 'yes' or true to generate highlighted source code, 'no' or false otherwise
* `download($zipped)` * `param bool|string` $zipped 'yes' or true to generate downloadable documentation, 'no' or false otherwise
* `report($path)`
* `wipeout($wipeout)` * `param bool|string` $wipeout 'yes' or true to clear out the destination directory, 'no' or false otherwise
* `quiet($quiet)` * `param bool|string` $quiet 'yes' or true for quiet, 'no' or false otherwise
* `progressbar($bar)` * `param bool|string` $bar 'yes' or true to display a progress bar, 'no' or false otherwise
* `colors($colors)` * `param bool|string` $colors 'yes' or true colorize the output, 'no' or false otherwise
* `updateCheck($check)` * `param bool|string` $check 'yes' or true to check for updates, 'no' or false otherwise
* `debug($debug)` * `param bool|string` $debug 'yes' or true to enable debug mode, 'no' or false otherwise
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
@@ -1,52 +0,0 @@
# Archive Tasks
## Extract
Extracts an archive.
Note that often, distributions are packaged in tar or zip archives
where the topmost folder may contain variable information, such as
the release date, or the version of the package. This information
is very useful when unpacking by hand, but arbitrarily-named directories
are much less useful to scripts. Therefore, by default, Extract will
remove the top-level directory, and instead store all extracted files
into the directory specified by $archivePath.
To keep the top-level directory when extracting, use
`preserveTopDirectory(true)`.
``` php
<?php
$this->taskExtract($archivePath)
->to($destination)
->preserveTopDirectory(false) // the default
->run();
?>
```
* `to(string)` location to store extracted files
* `to($to)` Location to store extracted files.
* `preserveTopDirectory($preserve = null)` * `param bool` $preserve
## Pack
Creates a zip or tar archive.
``` php
<?php
$this->taskPack(
<archiveFile>)
->add('README') // Puts file 'README' in archive at the root
->add('project') // Puts entire contents of directory 'project' in archinve inside 'project'
->addFile('dir/file.txt', 'file.txt') // Takes 'file.txt' from cwd and puts it in archive inside 'dir'.
->run();
?>
```
* `archiveFile($archiveFile)` * `param string` $archiveFile
* `addFile($placementLocation, $filesystemLocation)` Add an item to the archive. Like file_exists(), the parameter
* `addDir($placementLocation, $filesystemLocation)` Alias for addFile, in case anyone has angst about using
* `add($item)` Add a file or directory, or list of same to the archive.
@@ -1,163 +0,0 @@
# Assets Tasks
## ImageMinify
Minifies images. When the required minifier is not installed on the system
the task will try to download it from the [imagemin](https://github.com/imagemin) repository.
When the task is run without any specified minifier it will compress the images
based on the extension.
```php
$this->taskImageMinify('assets/images/*')
->to('dist/images/')
->run();
```
This will use the following minifiers:
- PNG: optipng
- GIF: gifsicle
- JPG, JPEG: jpegtran
- SVG: svgo
When the minifier is specified the task will use that for all the input files. In that case
it is useful to filter the files with the extension:
```php
$this->taskImageMinify('assets/images/*.png')
->to('dist/images/')
->minifier('pngcrush');
->run();
```
The task supports the following minifiers:
- optipng
- pngquant
- advpng
- pngout
- zopflipng
- pngcrush
- gifsicle
- jpegoptim
- jpeg-recompress
- jpegtran
- svgo (only minification, no downloading)
You can also specifiy extra options for the minifiers:
```php
$this->taskImageMinify('assets/images/*.jpg')
->to('dist/images/')
->minifier('jpegtran', ['-progressive' => null, '-copy' => 'none'])
->run();
```
This will execute as:
`jpegtran -copy none -progressive -optimize -outfile "dist/images/test.jpg" "/var/www/test/assets/images/test.jpg"`
* `to($target)` Sets the target directory where the files will be copied to.
* `minifier($minifier, array $options = Array ( ) )` Sets the minifier.
## Less
Compiles less files.
```php
<?php
$this->taskLess([
'less/default.less' => 'css/default.css'
])
->run();
?>
```
Use one of both less compilers in your project:
```
"leafo/lessphp": "~0.5",
"oyejorge/less.php": "~1.5"
```
Specify directory (string or array) for less imports lookup:
```php
<?php
$this->taskLess([
'less/default.less' => 'css/default.css'
])
->importDir('less')
->compiler('lessphp')
->run();
?>
```
You can implement additional compilers by extending this task and adding a
method named after them and overloading the lessCompilers() method to
inject the name there.
* `importDir($dirs)` Sets import directories
* `addImportPath($dir)` Adds import directory
* `setImportPaths($dirs)` Sets import directories
* `setFormatter($formatterName)` * `param string` $formatterName
* `compiler($compiler, array $options = Array ( ) )` Sets the compiler.
## Minify
Minifies asset file (CSS or JS).
``` php
<?php
$this->taskMinify( 'web/assets/theme.css' )
->run()
?>
```
Please install additional dependencies to use:
```
"patchwork/jsqueeze": "~1.0",
"natxet/CssMin": "~3.0"
```
* `to($dst)` Sets destination. Tries to guess type from it.
* `type($type)` Sets type with validation.
* `singleLine($singleLine)` Single line option for the JS minimisation.
* `keepImportantComments($keepImportantComments)` keepImportantComments option for the JS minimisation.
* `specialVarRx($specialVarRx)` specialVarRx option for the JS minimisation.
* `__toString()` @return string
## Scss
Compiles scss files.
```php
<?php
$this->taskScss([
'scss/default.scss' => 'css/default.css'
])
->importDir('assets/styles')
->run();
?>
```
Use the following scss compiler in your project:
```
"leafo/scssphp": "~0.1",
```
You can implement additional compilers by extending this task and adding a
method named after them and overloading the scssCompilers() method to
inject the name there.
* `setFormatter($formatterName)` Sets the formatter for scssphp
* `importDir($dirs)` Sets import directories
* `addImportPath($dir)` Adds import directory
* `setImportPaths($dirs)` Sets import directories
* `compiler($compiler, array $options = Array ( ) )` Sets the compiler.
@@ -1,126 +0,0 @@
# Base Tasks
## Exec
Executes shell script. Closes it when running in background mode.
``` php
<?php
$this->taskExec('compass')->arg('watch')->run();
// or use shortcut
$this->_exec('compass watch');
$this->taskExec('compass watch')->background()->run();
if ($this->taskExec('phpunit .')->run()->wasSuccessful()) {
$this->say('tests passed');
}
?>
```
* `background()` Executes command in background mode (asynchronously)
* `timeout($timeout)` Stop command if it runs longer then $timeout in seconds
* `idleTimeout($timeout)` Stops command if it does not output something for a while
* `env(array $env)` Sets the environment variables for the command
* `simulate($context)` {@inheritdoc}
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## ExecStack
Execute commands one by one in stack.
Stack can be stopped on first fail if you call `stopOnFail()`.
```php
<?php
$this->taskExecStack()
->stopOnFail()
->exec('mkdir site')
->exec('cd site')
->run();
?>
```
* `$this stopOnFail()`
* `executable($executable)` * `param string` $executable
* `exec($command)` * `param string|string[]` $command
* `stopOnFail($stopOnFail = null)` * `param bool` $stopOnFail
* `result($result)`
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
## ParallelExec
Class ParallelExecTask
``` php
<?php
$this->taskParallelExec()
->process('php ~/demos/script.php hey')
->process('php ~/demos/script.php hoy')
->process('php ~/demos/script.php gou')
->run();
?>
```
* ` timeout(int $timeout)` stops process if it runs longer then `$timeout` (seconds)
* ` idleTimeout(int $timeout)` stops process if it does not output for time longer then `$timeout` (seconds)
* `printed($isPrinted = null)` * `param bool` $isPrinted
* `process($command)` * `param string|\Robo\Contract\CommandInterface` $command
* `timeout($timeout)` * `param int` $timeout
* `idleTimeout($idleTimeout)` * `param int` $idleTimeout
## SymfonyCommand
Executes Symfony Command
``` php
<?php
// Symfony Command
$this->taskSymfonyCommand(new \Codeception\Command\Run('run'))
->arg('suite','acceptance')
->opt('debug')
->run();
// Artisan Command
$this->taskSymfonyCommand(new ModelGeneratorCommand())
->arg('name', 'User')
->run();
?>
```
* `arg($arg, $value)` * `param string` $arg
* `opt($option, $value = null)`
## Watch
Runs task when specified file or dir was changed.
Uses Lurker library.
``` php
<?php
$this->taskWatch()
->monitor('composer.json', function() {
$this->taskComposerUpdate()->run();
})->monitor('src', function() {
$this->taskExec('phpunit')->run();
})->run();
?>
```
* `monitor($paths, $callable)` * `param string|string[]` $paths
@@ -1,60 +0,0 @@
# Bower Tasks
## Install
Bower Install
``` php
<?php
// simple execution
$this->taskBowerInstall()->run();
// prefer dist with custom path
$this->taskBowerInstall('path/to/my/bower')
->noDev()
->run();
?>
```
* `allowRoot()` adds `allow-root` option to bower
* `forceLatest()` adds `force-latest` option to bower
* `noDev()` adds `production` option to bower
* `offline()` adds `offline` option to bower
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Update
Bower Update
``` php
<?php
// simple execution
$this->taskBowerUpdate->run();
// prefer dist with custom path
$this->taskBowerUpdate('path/to/my/bower')
->noDev()
->run();
?>
```
* `allowRoot()` adds `allow-root` option to bower
* `forceLatest()` adds `force-latest` option to bower
* `noDev()` adds `production` option to bower
* `offline()` adds `offline` option to bower
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
@@ -1,179 +0,0 @@
# Composer Tasks
## DumpAutoload
Composer Dump Autoload
``` php
<?php
// simple execution
$this->taskComposerDumpAutoload()->run();
// dump auto loader with custom path
$this->taskComposerDumpAutoload('path/to/my/composer.phar')
->preferDist()
->run();
// optimize autoloader dump with custom path
$this->taskComposerDumpAutoload('path/to/my/composer.phar')
->optimize()
->run();
// optimize autoloader dump with custom path and no dev
$this->taskComposerDumpAutoload('path/to/my/composer.phar')
->optimize()
->noDev()
->run();
?>
```
* `optimize()` * `return` $this
* `preferDist()` adds `prefer-dist` option to composer
* `preferSource()` adds `prefer-source` option to composer
* `noDev()` adds `no-dev` option to composer
* `noAnsi()` adds `no-ansi` option to composer
* `ansi()` adds `ansi` option to composer
* `optimizeAutoloader()` adds `optimize-autoloader` option to composer
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Install
Composer Install
``` php
<?php
// simple execution
$this->taskComposerInstall()->run();
// prefer dist with custom path
$this->taskComposerInstall('path/to/my/composer.phar')
->preferDist()
->run();
// optimize autoloader with custom path
$this->taskComposerInstall('path/to/my/composer.phar')
->optimizeAutoloader()
->run();
?>
```
* `preferDist()` adds `prefer-dist` option to composer
* `preferSource()` adds `prefer-source` option to composer
* `noDev()` adds `no-dev` option to composer
* `noAnsi()` adds `no-ansi` option to composer
* `ansi()` adds `ansi` option to composer
* `optimizeAutoloader()` adds `optimize-autoloader` option to composer
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Remove
Composer Validate
``` php
<?php
// simple execution
$this->taskComposerValidate()->run();
?>
```
* `dev()` * `return` $this
* `noProgress()` * `return` $this
* `noUpdate()` * `return` $this
* `updateNoDev()` * `return` $this
* `noUpdateWithDependencies()` * `return` $this
* `preferDist()` adds `prefer-dist` option to composer
* `preferSource()` adds `prefer-source` option to composer
* `noDev()` adds `no-dev` option to composer
* `noAnsi()` adds `no-ansi` option to composer
* `ansi()` adds `ansi` option to composer
* `optimizeAutoloader()` adds `optimize-autoloader` option to composer
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Update
Composer Update
``` php
<?php
// simple execution
$this->taskComposerUpdate()->run();
// prefer dist with custom path
$this->taskComposerUpdate('path/to/my/composer.phar')
->preferDist()
->run();
// optimize autoloader with custom path
$this->taskComposerUpdate('path/to/my/composer.phar')
->optimizeAutoloader()
->run();
?>
```
* `preferDist()` adds `prefer-dist` option to composer
* `preferSource()` adds `prefer-source` option to composer
* `noDev()` adds `no-dev` option to composer
* `noAnsi()` adds `no-ansi` option to composer
* `ansi()` adds `ansi` option to composer
* `optimizeAutoloader()` adds `optimize-autoloader` option to composer
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Validate
Composer Validate
``` php
<?php
// simple execution
$this->taskComposerValidate()->run();
?>
```
* `noCheckAll()` * `return` $this
* `noCheckLock()` * `return` $this
* `noCheckPublish()` * `return` $this
* `withDependencies()` * `return` $this
* `strict()` * `return` $this
* `preferDist()` adds `prefer-dist` option to composer
* `preferSource()` adds `prefer-source` option to composer
* `noDev()` adds `no-dev` option to composer
* `noAnsi()` adds `no-ansi` option to composer
* `ansi()` adds `ansi` option to composer
* `optimizeAutoloader()` adds `optimize-autoloader` option to composer
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
@@ -1,282 +0,0 @@
# Development Tasks
## Changelog
Helps to manage changelog file.
Creates or updates `changelog.md` file with recent changes in current version.
``` php
<?php
$version = "0.1.0";
$this->taskChangelog()
->version($version)
->change("released to github")
->run();
?>
```
Changes can be asked from Console
``` php
<?php
$this->taskChangelog()
->version($version)
->askForChanges()
->run();
?>
```
* `Development\Changelog filename(string $filename)`
* `Development\Changelog anchor(string $anchor)`
* `Development\Changelog version(string $version)`
* `filename($filename)` * `param string` $filename
* `log($item)` * `param string` $item
* `anchor($anchor)` * `param string` $anchor
* `version($version)` * `param string` $version
* `changes(array $data)` * `param array` $data
* `change($change)` * `param string` $change
* `getChanges()` @return array
## GenerateMarkdownDoc
Simple documentation generator from source files.
Takes classes, properties and methods with their docblocks and writes down a markdown file.
``` php
<?php
$this->taskGenDoc('models.md')
->docClass('Model\User') // take class Model\User
->docClass('Model\Post') // take class Model\Post
->filterMethods(function(\ReflectionMethod $r) {
return $r->isPublic() or $r->isProtected(); // process public and protected methods
})->processClass(function(\ReflectionClass $r, $text) {
return "Class ".$r->getName()."\n\n$text\n\n###Methods\n";
})->run();
```
By default this task generates a documentation for each public method of a class.
It combines method signature with a docblock. Both can be post-processed.
``` php
<?php
$this->taskGenDoc('models.md')
->docClass('Model\User')
->processClassSignature(false) // false can be passed to not include class signature
->processClassDocBlock(function(\ReflectionClass $r, $text) {
return "[This is part of application model]\n" . $text;
})->processMethodSignature(function(\ReflectionMethod $r, $text) {
return "#### {$r->name}()";
})->processMethodDocBlock(function(\ReflectionMethod $r, $text) {
return strpos($r->name, 'save')===0 ? "[Saves to the database]\n" . $text : $text;
})->run();
```
* ` docClass(string $classname)` put a class you want to be documented
* ` filterMethods(\Closure $func)` using callback function filter out methods that won't be documented
* ` filterClasses(\Closure $func)` using callback function filter out classes that won't be documented
* ` filterProperties(\Closure $func)` using callback function filter out properties that won't be documented
* ` processClass(\Closure $func)` post-process class documentation
* ` processClassSignature(\Closure $func)` post-process class signature. Provide *false* to skip.
* ` processClassDocBlock(\Closure $func)` post-process class docblock contents. Provide *false* to skip.
* ` processMethod(\Closure $func)` post-process method documentation. Provide *false* to skip.
* ` processMethodSignature(\Closure $func)` post-process method signature. Provide *false* to skip.
* ` processMethodDocBlock(\Closure $func)` post-process method docblock contents. Provide *false* to skip.
* ` processProperty(\Closure $func)` post-process property documentation. Provide *false* to skip.
* ` processPropertySignature(\Closure $func)` post-process property signature. Provide *false* to skip.
* ` processPropertyDocBlock(\Closure $func)` post-process property docblock contents. Provide *false* to skip.
* ` reorder(\Closure $func)` use a function to reorder classes
* ` reorderMethods(\Closure $func)` use a function to reorder methods in class
* ` prepend($text)` inserts text into beginning of markdown file
* ` append($text)` inserts text in the end of markdown file
* `docClass($item)` * `param string` $item
* `filterMethods($filterMethods)` * `param callable` $filterMethods
* `filterClasses($filterClasses)` * `param callable` $filterClasses
* `filterProperties($filterProperties)` * `param callable` $filterProperties
* `processClass($processClass)` * `param callable` $processClass
* `processClassSignature($processClassSignature)` * `param callable|false` $processClassSignature
* `processClassDocBlock($processClassDocBlock)` * `param callable|false` $processClassDocBlock
* `processMethod($processMethod)` * `param callable|false` $processMethod
* `processMethodSignature($processMethodSignature)` * `param callable|false` $processMethodSignature
* `processMethodDocBlock($processMethodDocBlock)` * `param callable|false` $processMethodDocBlock
* `processProperty($processProperty)` * `param callable|false` $processProperty
* `processPropertySignature($processPropertySignature)` * `param callable|false` $processPropertySignature
* `processPropertyDocBlock($processPropertyDocBlock)` * `param callable|false` $processPropertyDocBlock
* `reorder($reorder)` * `param callable` $reorder
* `reorderMethods($reorderMethods)` * `param callable` $reorderMethods
* `reorderProperties($reorderProperties)` * `param callable` $reorderProperties
* `filename($filename)` * `param string` $filename
* `prepend($prepend)` * `param string` $prepend
* `append($append)` * `param string` $append
* `text($text)` * `param string` $text
* `textForClass($item)` * `param string` $item
## Generate
Generate a Robo Task that is a wrapper around an existing class.
``` php
<?php
$this->taskGenerateTask('Symfony\Component\Filesystem\Filesystem', 'FilesystemStack')
->run();
```
## GitHubRelease
Publishes new GitHub release.
``` php
<?php
$this->taskGitHubRelease('0.1.0')
->uri('consolidation-org/Robo')
->description('Add stuff people need.')
->change('Fix #123')
->change('Add frobulation method to all widgets')
->run();
?>
```
* `tag($tag)` * `param string` $tag
* `draft($draft)` * `param bool` $draft
* `name($name)` * `param string` $name
* `description($description)` * `param string` $description
* `prerelease($prerelease)` * `param bool` $prerelease
* `comittish($comittish)` * `param string` $comittish
* `appendDescription($description)` * `param string` $description
* `changes(array $changes)`
* `change($change)` * `param string` $change
* `repo($repo)` * `param string` $repo
* `owner($owner)` * `param string` $owner
* `uri($uri)` * `param string` $uri
* `user($user)` * `param string` $user
* `password($password)` * `param` $password
## OpenBrowser
Opens the default's user browser
code inspired from openBrowser() function in https://github.com/composer/composer/blob/master/src/Composer/Command/HomeCommand.php
``` php
<?php
// open one browser window
$this->taskOpenBrowser('http://localhost')
->run();
// open two browser windows
$this->taskOpenBrowser([
'http://localhost/mysite',
'http://localhost/mysite2'
])
->run();
```
## PackPhar
Creates Phar.
``` php
<?php
$pharTask = $this->taskPackPhar('package/codecept.phar')
->compress()
->stub('package/stub.php');
$finder = Finder::create()
->name('*.php')
->in('src');
foreach ($finder as $file) {
$pharTask->addFile('src/'.$file->getRelativePathname(), $file->getRealPath());
}
$finder = Finder::create()->files()
->name('*.php')
->in('vendor');
foreach ($finder as $file) {
$pharTask->addStripped('vendor/'.$file->getRelativePathname(), $file->getRealPath());
}
$pharTask->run();
// verify Phar is packed correctly
$code = $this->_exec('php package/codecept.phar');
?>
```
* `compress($compress = null)` * `param bool` $compress
* `stub($stub)` * `param string` $stub
* `addStripped($path, $file)` * `param string` $path
* `addFile($path, $file)` * `param string` $path
* `addFiles($files)` * `param \Symfony\Component\Finder\SplFileInfo[]` $files
* `executable($file)` * `param string` $file
## PhpServer
Runs PHP server and stops it when task finishes.
``` php
<?php
// run server in /public directory
$this->taskServer(8000)
->dir('public')
->run();
// run with IP 0.0.0.0
$this->taskServer(8000)
->host('0.0.0.0')
->run();
// execute server in background
$this->taskServer(8000)
->background()
->run();
?>
```
* `host($host)` * `param string` $host
* `dir($path)` * `param string` $path
* `background()` Executes command in background mode (asynchronously)
* `timeout($timeout)` Stop command if it runs longer then $timeout in seconds
* `idleTimeout($timeout)` Stops command if it does not output something for a while
* `env(array $env)` Sets the environment variables for the command
* `simulate($context)` {@inheritdoc}
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## SemVer
Helps to maintain `.semver` file.
```php
<?php
$this->taskSemVer('.semver')
->increment()
->run();
?>
```
* `__toString()` @return string
* `setFormat($format)` * `param string` $format
* `setMetadataSeparator($separator)` * `param string` $separator
* `setPrereleaseSeparator($separator)` * `param string` $separator
* `increment($what = null)` * `param string` $what
* `prerelease($tag = null)` * `param string` $tag
* `metadata($data)` * `param array|string` $data
@@ -1,249 +0,0 @@
# Docker Tasks
## Build
Builds Docker image
```php
<?php
$this->taskDockerBuild()->run();
$this->taskDockerBuild('path/to/dir')
->tag('database')
->run();
?>
```
Class Build
@package Robo\Task\Docker
* `tag($tag)` * `param string` $tag
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Commit
Commits docker container to an image
```
$this->taskDockerCommit($containerId)
->name('my/database')
->run();
// alternatively you can take the result from DockerRun task:
$result = $this->taskDockerRun('db')
->exec('./prepare_database.sh')
->run();
$task->dockerCommit($result)
->name('my/database')
->run();
```
* `name($name)` * `param` $name
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Exec
Executes command inside running Docker container
```php
<?php
$test = $this->taskDockerRun('test_env')
->detached()
->run();
$this->taskDockerExec($test)
->interactive()
->exec('./runtests')
->run();
// alternatively use commands from other tasks
$this->taskDockerExec($test)
->interactive()
->exec($this->taskCodecept()->suite('acceptance'))
->run();
?>
```
* `detached()` * `return` $this
* `interactive()` * `return` $this
* `exec($command)` * `param string|\Robo\Contract\CommandInterface` $command
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Pull
Pulls an image from DockerHub
```php
<?php
$this->taskDockerPull('wordpress')
->run();
?>
```
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Remove
Remove docker container
```php
<?php
$this->taskDockerRemove($container)
->run();
?>
```
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Run
Performs `docker run` on a container.
```php
<?php
$this->taskDockerRun('mysql')->run();
$result = $this->taskDockerRun('my_db_image')
->env('DB', 'database_name')
->volume('/path/to/data', '/data')
->detached()
->publish(3306)
->name('my_mysql')
->run();
// retrieve container's cid:
$this->say("Running container ".$result->getCid());
// execute script inside container
$result = $this->taskDockerRun('db')
->exec('prepare_test_data.sh')
->run();
$this->taskDockerCommit($result)
->name('test_db')
->run();
// link containers
$mysql = $this->taskDockerRun('mysql')
->name('wp_db') // important to set name for linked container
->env('MYSQL_ROOT_PASSWORD', '123456')
->run();
$this->taskDockerRun('wordpress')
->link($mysql)
->publish(80, 8080)
->detached()
->run();
?>
```
* `detached()` * `return` $this
* `interactive()` * `return` $this
* `exec($run)` * `param string|\Robo\Contract\CommandInterface` $run
* `volume($from, $to = null)` * `param string` $from
* `env($variable, $value = null)` * `param string` $variable
* `publish($port = null, $portTo = null)` * `param null|int` $port
* `containerWorkdir($dir)` * `param string` $dir
* `user($user)` * `param string` $user
* `privileged()` * `return` $this
* `name($name)` * `param string` $name
* `link($name, $alias)` * `param string|\Robo\Task\Docker\Result` $name
* `tmpDir($dir)` * `param string` $dir
* `getTmpDir()` @return string
* `getUniqId()` @return string
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Start
Starts Docker container
```php
<?php
$this->taskDockerStart($cidOrResult)
->run();
?>
```
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Stop
Stops Docker container
```php
<?php
$this->taskDockerStop($cidOrResult)
->run();
?>
```
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
@@ -1,129 +0,0 @@
# File Tasks
## Concat
Merges files into one. Used for preparing assets.
``` php
<?php
$this->taskConcat([
'web/assets/screen.css',
'web/assets/print.css',
'web/assets/theme.css'
])
->to('web/assets/style.css')
->run()
?>
```
* `to($dst)` set the destination file
## Replace
Performs search and replace inside a files.
``` php
<?php
$this->taskReplaceInFile('VERSION')
->from('0.2.0')
->to('0.3.0')
->run();
$this->taskReplaceInFile('README.md')
->from(date('Y')-1)
->to(date('Y'))
->run();
$this->taskReplaceInFile('config.yml')
->regex('~^service:~')
->to('services:')
->run();
$this->taskReplaceInFile('box/robo.txt')
->from(array('##dbname##', '##dbhost##'))
->to(array('robo', 'localhost'))
->run();
?>
```
* `regex(string)` regex to match string to be replaced
* `from(string|array)` string(s) to be replaced
* `to(string|array)` value(s) to be set as a replacement
* `filename($filename)` * `param string` $filename
* `from($from)` * `param string` $from
* `to($to)` * `param string` $to
* `regex($regex)` * `param string` $regex
## TmpFile
Create a temporary file that is automatically cleaned up
once the task collection is is part of completes. When created,
it is given a random filename.
This temporary file may be manipulated exacatly like taskWrite().
It is deleted as soon as the collection it is a part of completes
or rolls back.
``` php
<?php
$collection = $this->collectionBuilder();
$tmpFilePath = $collection->taskTmpFile()
->line('-----')
->line(date('Y-m-d').' '.$title)
->line('----')
->getPath();
$collection->run();
?>
```
* `complete()` Delete this file when our collection completes.
* `filename($filename)` * `param string` $filename
* `append($append = null)` * `param bool` $append
* `line($line)` add a line.
* `lines(array $lines)` add more lines.
* `text($text)` add a text.
* `textFromFile($filename)` add a text from a file.
* `place($name, $val)` substitute a placeholder with value, placeholder must be enclosed by `{}`.
* `replace($string, $replacement)` replace any string with value.
* `regexReplace($pattern, $replacement)` replace any string with value using regular expression.
* `appendIfMatches($pattern, $text)` Append the provided text to the end of the buffer if the provided
* `appendUnlessMatches($pattern, $text)` Append the provided text to the end of the buffer unless the provided
* `originalContents()` @return string
* `wouldChange()` @return bool
* `getPath()` @return string
## Write
Writes to file.
``` php
<?php
$this->taskWriteToFile('blogpost.md')
->line('-----')
->line(date('Y-m-d').' '.$title)
->line('----')
->run();
?>
```
* `append()`
* `filename($filename)` * `param string` $filename
* `append($append = null)` * `param bool` $append
* `line($line)` add a line.
* `lines(array $lines)` add more lines.
* `text($text)` add a text.
* `textFromFile($filename)` add a text from a file.
* `place($name, $val)` substitute a placeholder with value, placeholder must be enclosed by `{}`.
* `replace($string, $replacement)` replace any string with value.
* `regexReplace($pattern, $replacement)` replace any string with value using regular expression.
* `appendIfMatches($pattern, $text)` Append the provided text to the end of the buffer if the provided
* `appendUnlessMatches($pattern, $text)` Append the provided text to the end of the buffer unless the provided
* `originalContents()` @return string
* `wouldChange()` @return bool
* `getPath()` @return string
@@ -1,217 +0,0 @@
# Filesystem Tasks
## CleanDir
Deletes all files from specified dir, ignoring git files.
``` php
<?php
$this->taskCleanDir(['tmp','logs'])->run();
// as shortcut
$this->_cleanDir('app/cache');
?>
```
## CopyDir
Copies one dir into another
``` php
<?php
$this->taskCopyDir(['dist/config' => 'config'])->run();
// as shortcut
$this->_copyDir('dist/config', 'config');
?>
```
* `dirPermissions($value)` Sets the default folder permissions for the destination if it doesn't exist
* `exclude($exclude = null)` List files to exclude.
## DeleteDir
Deletes dir
``` php
<?php
$this->taskDeleteDir('tmp')->run();
// as shortcut
$this->_deleteDir(['tmp', 'log']);
?>
```
## FilesystemStack
Wrapper for [Symfony Filesystem](http://symfony.com/doc/current/components/filesystem.html) Component.
Comands are executed in stack and can be stopped on first fail with `stopOnFail` option.
``` php
<?php
$this->taskFilesystemStack()
->mkdir('logs')
->touch('logs/.gitignore')
->chgrp('www', 'www-data')
->symlink('/var/log/nginx/error.log', 'logs/error.log')
->run();
// one line
$this->_touch('.gitignore');
$this->_mkdir('logs');
?>
```
* `$this mkdir($dir)`
* `$this touch($file)`
* `$this copy($from, $to, $force = null)`
* `$this chmod($file, $permissions, $umask = null, $recursive = null)`
* `$this chgrp($file, $group, $recursive = null)`
* `$this chown($file, $user, $recursive = null)`
* `$this remove($file)`
* `$this rename($from, $to)`
* `$this symlink($from, $to)`
* `$this mirror($from, $to)`
* `stopOnFail($stop = null)` * `param bool` $stop
## FlattenDir
Searches for files in a nested directory structure and copies them to
a target directory with or without the parent directories. The task was
inspired by [gulp-flatten](https://www.npmjs.com/package/gulp-flatten).
Example directory structure:
```
└── assets
├── asset-library1
│ ├── README.md
│ └── asset-library1.min.js
└── asset-library2
├── README.md
└── asset-library2.min.js
```
The following code will search the `*.min.js` files and copy them
inside a new `dist` folder:
``` php
<?php
$this->taskFlattenDir(['assets/*.min.js' => 'dist'])->run();
// or use shortcut
$this->_flattenDir('assets/*.min.js', 'dist');
?>
```
You can also define the target directory with an additional method, instead of
key/value pairs. More similar to the gulp-flatten syntax:
``` php
<?php
$this->taskFlattenDir(['assets/*.min.js'])
->to('dist')
->run();
?>
```
You can also append parts of the parent directories to the target path. If you give
the value `1` to the `includeParents()` method, then the top parent will be appended
to the target directory resulting in a path such as `dist/assets/asset-library1.min.js`.
If you give a negative number, such as `-1` (the same as specifying `array(0, 1)` then
the bottom parent will be appended, resulting in a path such as
`dist/asset-library1/asset-library1.min.js`.
The top parent directory will always be starting from the relative path to the current
directory. You can override that with the `parentDir()` method. If in the above example
you would specify `assets`, then the top parent directory would be `asset-library1`.
``` php
<?php
$this->taskFlattenDir(['assets/*.min.js' => 'dist'])
->parentDir('assets')
->includeParents(1)
->run();
?>
```
* `dirPermissions($permission)` Sets the default folder permissions for the destination if it does not exist.
* `includeParents($parents)` Sets the value from which direction and how much parent dirs should be included.
* `parentDir($dir)` Sets the parent directory from which the relative parent directories will be calculated.
* `to($target)` Sets the target directory where the files will be copied to.
## MirrorDir
Mirrors a directory to another
``` php
<?php
$this->taskMirrorDir(['dist/config/' => 'config/'])->run();
// or use shortcut
$this->_mirrorDir('dist/config/', 'config/');
?>
```
## TmpDir
Create a temporary directory that is automatically cleaned up
once the task collection is is part of completes.
Use WorkDir if you do not want the directory to be deleted.
``` php
<?php
// Delete on rollback or on successful completion.
// Note that in this example, everything is deleted at
// the end of $collection->run().
$collection = $this->collectionBuilder();
$tmpPath = $collection->tmpDir()->getPath();
$collection->taskFilesystemStack()
->mkdir("$tmpPath/log")
->touch("$tmpPath/log/error.txt");
$collection->run();
// as shortcut (deleted when program exits)
$tmpPath = $this->_tmpDir();
?>
```
* `cwd($shouldChangeWorkingDirectory = null)` Flag that we should cwd to the temporary directory when it is
* `complete()` Delete this directory when our collection completes.
* `getPath()` Get a reference to the path to the temporary directory, so that
## WorkDir
Create a temporary working directory that is automatically renamed to its
final desired location if all of the tasks in the collection succeed. If
there is a rollback, then the working directory is deleted.
``` php
<?php
$collection = $this->collectionBuilder();
$workingPath = $collection->workDir("build")->getPath();
$collection->taskFilesystemStack()
->mkdir("$workingPath/log")
->touch("$workingPath/log/error.txt");
$collection->run();
?>
```
* `complete()` Move our working directory into its final destination once the
* `rollback()` Delete our working directory
* `getPath()` Get a reference to the path to the temporary directory, so that
* `cwd($shouldChangeWorkingDirectory = null)` Flag that we should cwd to the temporary directory when it is
@@ -1,31 +0,0 @@
# Gulp Tasks
## Run
Gulp Run
``` php
<?php
// simple execution
$this->taskGulpRun()->run();
// run task 'clean' with --silent option
$this->taskGulpRun('clean')
->silent()
->run();
?>
```
* `silent()` adds `silent` option to gulp
* `noColor()` adds `--no-color` option to gulp
* `color()` adds `--color` option to gulp
* `simple()` adds `--tasks-simple` option to gulp
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
@@ -1,54 +0,0 @@
# Npm Tasks
## Install
Npm Install
``` php
<?php
// simple execution
$this->taskNpmInstall()->run();
// prefer dist with custom path
$this->taskNpmInstall('path/to/my/npm')
->noDev()
->run();
?>
```
* `noDev()` adds `production` option to npm
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Update
Npm Update
```php
<?php
// simple execution
$this->taskNpmUpdate()->run();
// prefer dist with custom path
$this->taskNpmUpdate('path/to/my/npm')
->noDev()
->run();
?>
```
* `noDev()` adds `production` option to npm
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
@@ -1,143 +0,0 @@
# Remote Tasks
## Rsync
Executes rsync in a flexible manner.
``` php
$this->taskRsync()
->fromPath('src/')
->toHost('localhost')
->toUser('dev')
->toPath('/var/www/html/app/')
->remoteShell('ssh -i public_key')
->recursive()
->excludeVcs()
->checksum()
->wholeFile()
->verbose()
->progress()
->humanReadable()
->stats()
->run();
```
You could also clone the task and do a dry-run first:
``` php
$rsync = $this->taskRsync()
->fromPath('src/')
->toPath('example.com:/var/www/html/app/')
->archive()
->excludeVcs()
->progress()
->stats();
$dryRun = clone $rsync;
$dryRun->dryRun()->run();
if ('y' === $this->ask('Do you want to run (y/n)')) {
$rsync->run();
}
```
* ` fromUser(string $user)`
* ` fromHost(string $hostname)`
* ` toUser(string $user)`
* ` toHost(string $hostname)`
* `fromPath($path)` This can either be a full rsync path spec (user@host:path) or just a path.
* `toPath($path)` This can either be a full rsync path spec (user@host:path) or just a path.
* `fromUser($fromUser)` * `param string` $fromUser
* `fromHost($fromHost)` * `param string` $fromHost
* `toUser($toUser)` * `param string` $toUser
* `toHost($toHost)` * `param string` $toHost
* `progress()` * `return` $this
* `stats()` * `return` $this
* `recursive()` * `return` $this
* `verbose()` * `return` $this
* `checksum()` * `return` $this
* `archive()` * `return` $this
* `compress()` * `return` $this
* `owner()` * `return` $this
* `group()` * `return` $this
* `times()` * `return` $this
* `delete()` * `return` $this
* `timeout($seconds)` * `param int` $seconds
* `humanReadable()` * `return` $this
* `wholeFile()` * `return` $this
* `dryRun()` * `return` $this
* `itemizeChanges()` * `return` $this
* `excludeVcs()` Excludes .git, .svn and .hg items at any depth.
* `exclude($pattern)` * `param array|string` $pattern
* `excludeFrom($file)` * `param string` $file
* `includeFilter($pattern)` * `param array|string` $pattern
* `filter($pattern)` * `param array|string` $pattern
* `filesFrom($file)` * `param string` $file
* `remoteShell($command)` * `param string` $command
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Ssh
Runs multiple commands on a remote server.
Per default, commands are combined with &&, unless stopOnFail is false.
```php
<?php
$this->taskSshExec('remote.example.com', 'user')
->remoteDir('/var/www/html')
->exec('ls -la')
->exec('chmod g+x logs')
->run();
```
You can even exec other tasks (which implement CommandInterface):
```php
$gitTask = $this->taskGitStack()
->checkout('master')
->pull();
$this->taskSshExec('remote.example.com')
->remoteDir('/var/www/html/site')
->exec($gitTask)
->run();
```
You can configure the remote directory for all future calls:
```php
::configure('remoteDir', '/some-dir');
```
* `$this stopOnFail(bool $stopOnFail)` Whether or not to chain commands together with &&
and stop the chain if one command fails
* `$this remoteDir(string $remoteWorkingDirectory)` Changes to the given directory before running commands
* `hostname($hostname)` * `param string` $hostname
* `user($user)` * `param string` $user
* `stopOnFail($stopOnFail = null)` * `param bool` $stopOnFail
* `remoteDir($remoteDir)` * `param string` $remoteDir
* `identityFile($filename)` * `param string` $filename
* `port($port)` * `param int` $port
* `forcePseudoTty()` * `return` $this
* `quiet()` * `return` $this
* `verbose()` * `return` $this
* `exec($command)` * `param string|string[]|CommandInterface` $command
* `simulate($context)` {@inheritdoc}
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
@@ -1,171 +0,0 @@
# Testing Tasks
## Atoum
Runs [atoum](http://atoum.org/) tests
``` php
<?php
$this->taskAtoum()
->files('path/to/test.php')
->configFile('config/dev.php')
->run()
?>
```
* `tags($tags)` Tag or Tags to filter.
* `lightReport()` Display result using the light reporter.
* `tap()` Display result using the tap reporter.
* `bootstrap($file)` Path to the bootstrap file.
* `configFile($file)` Path to the config file.
* `debug()` Use atoum's debug mode.
* `files($files)` Test file or test files to run.
* `directories($directories)` Test directory or directories to run.
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Behat
Executes Behat tests
``` php
<?php
$this->taskBehat()
->format('pretty')
->noInteraction()
->run();
?>
```
* `stopOnFail()` * `return` $this
* `noInteraction()` * `return` $this
* `config($config_file)` * `param` $config_file
* `colors()` * `return` $this
* `noColors()` * `return` $this
* `suite($suite)` * `param string` $suite
* `verbose($level = null)` * `param string` $level
* `format($formater)` * `param string` $formater
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Codecept
Executes Codeception tests
``` php
<?php
// config
$this->taskCodecept()
->suite('acceptance')
->env('chrome')
->group('admin')
->xml()
->html()
->run();
?>
```
* `suite($suite)` * `param string` $suite
* `test($testName)` * `param string` $testName
* `group($group)` set group option. Can be called multiple times
* `excludeGroup($group)` * `param string` $group
* `json($file = null)` generate json report
* `xml($file = null)` generate xml JUnit report
* `html($dir = null)` Generate html report
* `tap($file = null)` generate tap report
* `configFile($file)` provides config file other then default `codeception.yml` with `-c` option
* `coverage($cov = null)` collect codecoverage in raw format. You may pass name of cov file to save results
* `silent()` execute in silent mode
* `coverageXml($xml = null)` collect code coverage in xml format. You may pass name of xml file to save results
* `coverageHtml($html = null)` collect code coverage and generate html report. You may pass
* `env($env)` * `param string` $env
* `debug()` * `return` $this
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## PHPUnit
Runs PHPUnit tests
``` php
<?php
$this->taskPHPUnit()
->group('core')
->bootstrap('test/bootstrap.php')
->run()
?>
```
* `filter($filter)` * `param string` $filter
* `group($group)` * `param string` $group
* `excludeGroup($group)` * `param string` $group
* `json($file = null)` adds `log-json` option to runner
* `xml($file = null)` adds `log-junit` option
* `tap($file = null)` * `param string` $file
* `bootstrap($file)` * `param string` $file
* `configFile($file)` * `param string` $file
* `debug()` * `return` $this
* `files($files)` Directory of test files or single test file to run.
* `file($file)` Test the provided file.
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
## Phpspec
Executes Phpspec tests
``` php
<?php
$this->taskPhpspec()
->format('pretty')
->noInteraction()
->run();
?>
```
* `stopOnFail()`
* `noCodeGeneration()`
* `quiet()`
* `verbose($level = null)`
* `noAnsi()`
* `noInteraction()`
* `config($config_file)`
* `format($formater)`
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
* `arg($arg)` Pass argument to executable. Its value will be automatically escaped.
* `args($args)` Pass methods parameters as arguments to executable. Argument values
* `rawArg($arg)` Pass the provided string in its raw (as provided) form as an argument to executable.
* `option($option, $value = null)` Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* `optionList($option, $value = null)` Pass multiple options to executable. Value can be a string or array.
-108
View File
@@ -1,108 +0,0 @@
# Vcs Tasks
## GitStack
Runs Git commands in stack. You can use `stopOnFail()` to point that stack should be terminated on first fail.
``` php
<?php
$this->taskGitStack()
->stopOnFail()
->add('-A')
->commit('adding everything')
->push('origin','master')
->tag('0.6.0')
->push('origin','0.6.0')
->run()
$this->taskGitStack()
->stopOnFail()
->add('doc/*')
->commit('doc updated')
->push()
->run();
?>
```
* `cloneRepo($repo, $to = null)` Executes `git clone`
* `add($pattern)` Executes `git add` command with files to add pattern
* `commit($message, $options = null)` Executes `git commit` command with a message
* `pull($origin = null, $branch = null)` Executes `git pull` command.
* `push($origin = null, $branch = null)` Executes `git push` command
* `merge($branch)` Performs git merge
* `checkout($branch)` Executes `git checkout` command
* `tag($tag_name, $message = null)` Executes `git tag` command
* `executable($executable)` * `param string` $executable
* `exec($command)` * `param string|string[]` $command
* `stopOnFail($stopOnFail = null)` * `param bool` $stopOnFail
* `result($result)`
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
## HgStack
Runs hg commands in stack. You can use `stopOnFail()` to point that stack should be terminated on first fail.
``` php
<?php
$this->hgStack
->cloneRepo('https://bitbucket.org/durin42/hgsubversion')
->pull()
->add()
->commit('changed')
->push()
->tag('0.6.0')
->push('0.6.0')
->run();
?>
```
* `cloneRepo($repo, $to = null)` Executes `hg clone`
* `add($include = null, $exclude = null)` Executes `hg add` command with files to add by pattern
* `commit($message, $options = null)` Executes `hg commit` command with a message
* `pull($branch = null)` Executes `hg pull` command.
* `push($branch = null)` Executes `hg push` command
* `merge($revision = null)` Performs hg merge
* `tag($tag_name, $message = null)` Executes `hg tag` command
* `executable($executable)` * `param string` $executable
* `exec($command)` * `param string|string[]` $command
* `stopOnFail($stopOnFail = null)` * `param bool` $stopOnFail
* `result($result)`
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
## SvnStack
Runs Svn commands in stack. You can use `stopOnFail()` to point that stack should be terminated on first fail.
``` php
<?php
$this->taskSvnStack()
->checkout('http://svn.collab.net/repos/svn/trunk')
->run()
// alternatively
$this->_svnCheckout('http://svn.collab.net/repos/svn/trunk');
$this->taskSvnStack('username', 'password')
->stopOnFail()
->update()
->add('doc/*')
->commit('doc updated')
->run();
?>
```
* `update($path = null)` Updates `svn update` command
* `add($pattern = null)` Executes `svn add` command with files to add pattern
* `commit($message, $options = null)` Executes `svn commit` command with a message
* `checkout($branch)` Executes `svn checkout` command
* `executable($executable)` * `param string` $executable
* `exec($command)` * `param string|string[]` $command
* `stopOnFail($stopOnFail = null)` * `param bool` $stopOnFail
* `result($result)`
* `dir($dir)` Changes working directory of command
* `printed($arg)` Should command output be printed
@@ -1,425 +0,0 @@
<?php
use Robo\Result;
use Robo\ResultData;
use Robo\Collection\CollectionBuilder;
use Consolidation\AnnotatedCommand\CommandData;
use Consolidation\OutputFormatters\Options\FormatterOptions;
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
use Consolidation\OutputFormatters\StructuredData\PropertyList;
/**
* Example RoboFile.
*
* To test:
*
* $ cd ROBO_PROJECT/examples
* $ ../robo try:success
*
* - or -
*
* $ cd ROBO_PROJECT
* $ ./robo -f examples try:formatters
*/
class RoboFile extends \Robo\Tasks
{
/**
* Watch a file.
*
* Demonstrates the 'watch' command. Runs 'composer update' any time
* composer.json changes.
*/
public function tryWatch()
{
$this->taskWatch()->monitor(['composer.json', 'composer.lock'], function () {
$this->taskComposerUpdate()->run();
})->run();
}
/**
* Demonstrates Robo input APIs.
*/
public function tryInput()
{
$answer = $this->ask('how are you?');
$this->say('You are '.$answer);
$yes = $this->confirm('Do you want one more question?');
if (!$yes) {
return Result::cancelled();
}
$lang = $this->askDefault('what is your favorite scripting language?', 'PHP');
$this->say($lang);
$pin = $this->askHidden('Ok, now tell your PIN code (it is hidden)');
$this->yell('Ha-ha, your pin code is: '.$pin);
$this->say('Bye!');
}
/**
* Demonstrates parallel execution.
*
* @option $printed Print the output of each process.
* @option $error Include an extra process that fails.
*/
public function tryPara($options = ['printed' => false, 'error' => false])
{
$dir = __DIR__;
$para = $this->taskParallelExec()
->printed($options['printed'])
->process("php $dir/tests/_data/parascript.php hey 4")
->process("php $dir/tests/_data/parascript.php hoy 3")
->process("php $dir/tests/_data/parascript.php gou 2")
->process("php $dir/tests/_data/parascript.php die 1");
if ($options['error']) {
$para->process("ls $dir/tests/_data/filenotfound");
}
return $para->run();
}
/**
* Demonstrates Robo argument passing.
*
* @param string $a The first parameter. Required.
* @param string $b The second parameter. Optional.
*/
public function tryArgs($a, $b = 'default')
{
$this->say("The parameter a is $a and b is $b");
}
/**
* Demonstrate Robo variable argument passing.
*
* @param $a A list of commandline parameters.
*/
public function tryArrayArgs(array $a)
{
$this->say("The parameters passed are:\n" . var_export($a, true));
}
/**
* Demonstrate Robo boolean options.
*
* @param $opts The options.
* @option boolean $silent Supress output.
*/
public function tryOptbool($opts = ['silent|s' => false])
{
if (!$opts['silent']) {
$this->say("Hello, world");
}
}
/**
* Demonstrate the use of the PHP built-in webserver.
*/
public function tryServer()
{
return $this->taskServer(8000)
->dir('site')
->arg('site/index.php')
->run();
}
/**
* Demonstrate the use of the Robo open-browser task.
*/
public function tryOpenBrowser()
{
return $this->taskOpenBrowser([
'http://robo.li',
'https://github.com/consolidation-org/Robo'
])->run();
}
/**
* Demonstrate Robo error output and command failure.
*/
public function tryError()
{
return $this->taskExec('ls xyzzy' . date('U'))->dir('/tmp')->run();
}
/**
* Demonstrate Robo standard output and command success.
*/
public function trySuccess()
{
return $this->_exec('pwd');
}
/**
* @field-labels
* name: Name
* species: Species
* legs: Legs
* food: Favorite Food
* id: Id
* @return PropertyList
*/
public function tryInfo()
{
$outputData = [
'name' => 'fluffy',
'species' => 'cat',
'legs' => 4,
'food' => 'salmon',
'id' => 389245032,
];
$data = new PropertyList($outputData);
// Add a render function to transform cell data when the output
// format is a table, or similar. This allows us to add color
// information to the output without modifying the data cells when
// using yaml or json output formats.
$data->addRendererFunction(
// n.b. There is a fourth parameter $rowData that may be added here.
function ($key, $cellData, FormatterOptions $options) {
if ($key == 'name') {
return "<info>$cellData</>";
}
return $cellData;
}
);
return $data;
}
/**
* Demonstrate Robo formatters. Default format is 'table'.
*
* @field-labels
* first: I
* second: II
* third: III
* @default-string-field second
* @usage try:formatters --format=yaml
* @usage try:formatters --format=csv
* @usage try:formatters --fields=first,third
* @usage try:formatters --fields=III,II
* @aliases tf
*
* @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields
*/
public function tryFormatters($somthing = 'default', $options = ['format' => 'table', 'fields' => ''])
{
$outputData = [
'en' => [ 'first' => 'One', 'second' => 'Two', 'third' => 'Three' ],
'de' => [ 'first' => 'Eins', 'second' => 'Zwei', 'third' => 'Drei' ],
'jp' => [ 'first' => 'Ichi', 'second' => 'Ni', 'third' => 'San' ],
'es' => [ 'first' => 'Uno', 'second' => 'Dos', 'third' => 'Tres' ],
];
return new RowsOfFields($outputData);
}
/**
* Demonstrate an alter hook with an option
*
* @hook alter try:formatters
* @option $french Add a row with French numbers.
* @usage try:formatters --french
*/
public function alterFormatters($result, CommandData $commandData)
{
if ($commandData->input()->getOption('french')) {
$result['fr'] = [ 'first' => 'Un', 'second' => 'Deux', 'third' => 'Trois' ];
}
return $result;
}
/**
* Demonstrate what happens when a command or a task
* throws an exception. Note that typically, Robo commands
* should return Result objects rather than throw exceptions.
*/
public function tryException($options = ['task' => false])
{
if (!$options['task']) {
throw new RuntimeException('Command failed with an exception.');
}
return new ExceptionTask('Task failed with an exception.');
}
/**
* Demonstrate deprecated task behavior.
*
* Demonstrate what happens when using a task that is created via
* direct instantiation, which omits initialization done by the
* container. Emits a warning message.
*/
public function tryDeprecated()
{
// Calling 'new' directly without manually setting
// up dependencies will result in a deprecation warning.
// @see RoboFile::trySuccess()
return (new \Robo\Task\Base\Exec('pwd'))->run();
}
/**
* Demonstrate the use of a collection builder to chain multiple tasks
* together into a collection, which is executed once constructed.
*
* For demonstration purposes only; this could, of course, be done
* with a single FilesystemStack.
*/
public function tryBuilder()
{
return $this->collectionBuilder()
->taskFilesystemStack()
->mkdir('a')
->touch('a/a.txt')
->taskFilesystemStack()
->mkdir('a/b')
->touch('a/b/b.txt')
->taskFilesystemStack()
->mkdir('a/b/c')
->touch('a/b/c/c.txt')
->run();
}
public function tryBuilderRollback()
{
// This example will create two builders, and add
// the first one as a child of the second in order
// to demonstrate nested rollbacks.
$collection = $this->collectionBuilder()
->taskFilesystemStack()
->mkdir('g')
->touch('g/g.txt')
->rollback(
$this->taskDeleteDir('g')
)
->taskFilesystemStack()
->mkdir('g/h')
->touch('g/h/h.txt')
->taskFilesystemStack()
->mkdir('g/h/i/c')
->touch('g/h/i/i.txt');
return $this->collectionBuilder()
->progressMessage('Start recursive collection')
->addTask($collection)
->progressMessage('Done with recursive collection')
->taskExec('ls xyzzy' . date('U'))
->dir('/tmp')
->run();
}
public function tryWorkdir()
{
// This example works like tryBuilderRollback,
// but does equivalent operations using a working
// directory. The working directory is deleted on rollback
$collection = $this->collectionBuilder();
$workdir = $collection->workDir('w');
$collection
->taskFilesystemStack()
->touch("$workdir/g.txt")
->taskFilesystemStack()
->mkdir("$workdir/h")
->touch("$workdir/h/h.txt")
->taskFilesystemStack()
->mkdir("$workdir/h/i/c")
->touch("$workdir/h/i/i.txt");
return $this->collectionBuilder()
->progressMessage('Start recursive collection')
->addTask($collection)
->progressMessage('Done with recursive collection')
->taskExec('ls xyzzy' . date('U'))
->dir('/tmp')
->run();
}
/**
* Demonstrates Robo temporary directory usage.
*/
public function tryTmpDir()
{
// Set up a collection to add tasks to
$collection = $this->collectionBuilder();
// Get a temporary directory to work in. Note that we get a path
// back, but the directory is not created until the task runs.
$tmpPath = $collection->tmpDir();
$result = $collection
->taskWriteToFile("$tmpPath/file.txt")
->line('Example file')
->run();
if (is_dir($tmpPath)) {
$this->say("The temporary directory at $tmpPath was not cleaned up after the collection completed.");
} else {
$this->say("The temporary directory at $tmpPath was automatically deleted.");
}
return $result;
}
/**
* Description
* @param $options
* @option delay Miliseconds delay
* @return type
*/
public function tryProgress($options = ['delay' => 500])
{
$delay = $options['delay'];
$delayUntilProgressStart = \Robo\Robo::config()->get(\Robo\Config::PROGRESS_BAR_AUTO_DISPLAY_INTERVAL);
$this->say("Progress bar will display after $delayUntilProgressStart seconds of activity.");
$processList = range(1, 10);
return $this->collectionBuilder()
->taskForEach($processList)
->iterationMessage('Processing {value}')
->call(
function ($value) use($delay) {
// TaskForEach::call should only be used to do
// non-Robo operations. To use Robo tasks in an
// iterator, @see TaskForEach::withBuilder.
usleep($delay * 1000); // delay units: msec, usleep units: usec
}
)
->run();
}
public function tryIter()
{
$workdir = 'build/iter-example';
$this->say("Creating sample direcories in $workdir.");
$processList = ['cats', 'dogs', 'sheep', 'fish', 'horses', 'cows'];
return $this->collectionBuilder()
->taskFilesystemStack()
->mkdir($workdir)
->taskCleanDir($workdir)
->taskForEach($processList)
->withBuilder(
function ($builder, $key, $value) use ($workdir) {
return $builder
->taskFilesystemStack()
->mkdir("$workdir/$value");
}
)
->run();
}
}
class ExceptionTask extends \Robo\Task\BaseTask
{
protected $message;
public function __construct($message)
{
$this->message = $message;
}
public function run()
{
throw new RuntimeException($this->message);
}
}
@@ -1,44 +0,0 @@
#!/usr/bin/env robo
<?php
/**
* Robo script.
*
* This file may be executed from the shell as if it were a bash script.
*
* Example:
*
* $ ./robo.script foo bar
* ➜ This is a Robo script, bar
*
* If the script has only one command, then you may remove the need to
* specify it on the commandline by naming it on the `#!` line.
*
* e.g. if the first line is `#!/usr/bin/env robo foo`:
*
* $ ./robo.script bar
* ➜ This is a Robo script, bar
*
* Note that in order for Robo scripts to work, the 'robo' application
* must be in your $PATH. Usually, this is done by installing
* Robo via `composer global require`, and placing ~/.composer/vendor/bin
* on your $PATH. Robo libraries that are also installed via `composer global
* require` will be available for use in your Robo scripts, provided that
* they are loaded via the `getServiceProviders` method of the script.
*
* See also: https://github.com/consolidation-org/cgr
*/
class MyRoboScript extends \Robo\Tasks
{
/**
* Foo
*
* A demonstration of a command in a Robo script.
*
* @param string $name a name that is printed.
*/
public function foo($name)
{
$this->say("This is a Robo script, $name");
}
}
+1
View File
@@ -17,5 +17,6 @@ if (strpos(basename(__FILE__), 'phar')) {
}
}
$runner = new \Robo\Runner();
$runner->setSelfUpdateRepository('consolidation/robo');
$statusCode = $runner->execute($_SERVER['argv']);
exit($statusCode);
+3
View File
@@ -0,0 +1,3 @@
options:
progress-delay: 2
simulate: null
+28
View File
@@ -0,0 +1,28 @@
#!/bin/bash
SCENARIO=$1
ACTION=${2-install}
dir=scenarios/${SCENARIO}
if [ -z "$SCENARIO" ] ; then
SCENARIO=default
dir=.
fi
if [ ! -d "$dir" ] ; then
echo "Requested scenario '${SCENARIO}' does not exist."
exit 1
fi
echo
echo "::"
echo ":: Switch to ${SCENARIO} scenario"
echo "::"
echo
set -ex
composer -n validate --working-dir=$dir --no-check-all --ansi
composer -n --working-dir=$dir ${ACTION} --prefer-dist --no-scripts
composer -n --working-dir=$dir info
@@ -0,0 +1,2 @@
vendor
composer.lock
@@ -0,0 +1,93 @@
{
"name": "consolidation/robo",
"description": "Modern task runner",
"license": "MIT",
"authors": [
{
"name": "Davert",
"email": "davert.php@resend.cc"
}
],
"autoload":{
"psr-4":{
"Robo\\":"src"
}
},
"autoload-dev":{
"psr-4":{
"Robo\\":"tests/src"
}
},
"bin":["robo"],
"require": {
"php": ">=5.5.0",
"league/container": "^2.2",
"consolidation/log": "~1",
"consolidation/config": "^1.0.1",
"consolidation/annotated-command": "^2.8.2",
"consolidation/output-formatters": "^3.1.13",
"grasmash/yaml-expander": "^1.3",
"symfony/finder": "^2.5|^3|^4",
"symfony/process": "^2.5|^3|^4",
"symfony/filesystem": "^2.5|^3|^4",
"symfony/event-dispatcher": "^2.5|^3|^4"
},
"require-dev": {
"codeception/aspect-mock": "^1|^2.1.1",
"codeception/base": "^2.3.7",
"codeception/verify": "^0.3.2",
"greg-1-anderson/composer-test-scenarios": "^1",
"natxet/CssMin": "3.0.4",
"patchwork/jsqueeze": "~2",
"pear/archive_tar": "^1.4.2",
"phpunit/php-code-coverage": "~2|~4",
"satooshi/php-coveralls": "^2",
"squizlabs/php_codesniffer": "^2.8",
"symfony/console": "^2.8"
},
"scripts": {
"cs": "./robo sniff",
"unit": "./robo test",
"lint": [
"find src -name '*.php' -print0 | xargs -0 -n1 php -l",
"find tests/src -name '*.php' -print0 | xargs -0 -n1 php -l"
],
"test": [
"@lint",
"@unit",
"@cs"
],
"scenario": "scenarios/install",
"pre-install-cmd": [
"Robo\\composer\\ScriptHandler::checkDependencies"
],
"post-update-cmd": [
"create-scenario symfony4 'symfony/console:^4.0' --platform-php '7.1.3'",
"create-scenario symfony2 'symfony/console:^2.8' --platform-php '5.5.9' --no-lockfile"
]
},
"config": {
"optimize-autoloader": true,
"sort-packages": true,
"platform": {
"php": "5.5.9"
},
"vendor-dir": "../../vendor"
},
"extra": {
"branch-alias": {
"dev-master": "1.x-dev",
"dev-state": "1.x-dev"
}
},
"suggest": {
"pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively.",
"henrikbjorn/lurker": "For monitoring filesystem changes in taskWatch",
"patchwork/jsqueeze": "For minifying JS files in taskMinify",
"natxet/CssMin": "For minifying CSS files in taskMinify"
},
"replace": {
"codegyre/robo": "< 1.0"
},
"minimum-stability": "stable"
}
+1
View File
@@ -0,0 +1 @@
../../src
@@ -0,0 +1 @@
~
@@ -0,0 +1 @@
vendor
@@ -0,0 +1 @@
~
@@ -0,0 +1,93 @@
{
"name": "consolidation/robo",
"description": "Modern task runner",
"license": "MIT",
"authors": [
{
"name": "Davert",
"email": "davert.php@resend.cc"
}
],
"autoload":{
"psr-4":{
"Robo\\":"src"
}
},
"autoload-dev":{
"psr-4":{
"Robo\\":"tests/src"
}
},
"bin":["robo"],
"require": {
"php": ">=5.5.0",
"league/container": "^2.2",
"consolidation/log": "~1",
"consolidation/config": "^1.0.1",
"consolidation/annotated-command": "^2.8.2",
"consolidation/output-formatters": "^3.1.13",
"grasmash/yaml-expander": "^1.3",
"symfony/finder": "^2.5|^3|^4",
"symfony/process": "^2.5|^3|^4",
"symfony/filesystem": "^2.5|^3|^4",
"symfony/event-dispatcher": "^2.5|^3|^4"
},
"require-dev": {
"codeception/aspect-mock": "^1|^2.1.1",
"codeception/base": "^2.3.7",
"codeception/verify": "^0.3.2",
"greg-1-anderson/composer-test-scenarios": "^1",
"natxet/CssMin": "3.0.4",
"patchwork/jsqueeze": "~2",
"pear/archive_tar": "^1.4.2",
"phpunit/php-code-coverage": "~2|~4",
"satooshi/php-coveralls": "^2",
"squizlabs/php_codesniffer": "^2.8",
"symfony/console": "^4.0"
},
"scripts": {
"cs": "./robo sniff",
"unit": "./robo test",
"lint": [
"find src -name '*.php' -print0 | xargs -0 -n1 php -l",
"find tests/src -name '*.php' -print0 | xargs -0 -n1 php -l"
],
"test": [
"@lint",
"@unit",
"@cs"
],
"scenario": "scenarios/install",
"pre-install-cmd": [
"Robo\\composer\\ScriptHandler::checkDependencies"
],
"post-update-cmd": [
"create-scenario symfony4 'symfony/console:^4.0' --platform-php '7.1.3'",
"create-scenario symfony2 'symfony/console:^2.8' --platform-php '5.5.9' --no-lockfile"
]
},
"config": {
"optimize-autoloader": true,
"sort-packages": true,
"platform": {
"php": "7.1.3"
},
"vendor-dir": "../../vendor"
},
"extra": {
"branch-alias": {
"dev-master": "1.x-dev",
"dev-state": "1.x-dev"
}
},
"suggest": {
"pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively.",
"henrikbjorn/lurker": "For monitoring filesystem changes in taskWatch",
"patchwork/jsqueeze": "For minifying JS files in taskMinify",
"natxet/CssMin": "For minifying CSS files in taskMinify"
},
"replace": {
"codegyre/robo": "< 1.0"
},
"minimum-stability": "stable"
}
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
../../src
+19 -2
View File
@@ -3,8 +3,6 @@ namespace Robo;
use Symfony\Component\Console\Application as SymfonyApplication;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
class Application extends SymfonyApplication
@@ -25,6 +23,11 @@ class Application extends SymfonyApplication
->addOption(
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)
);
$this->getDefinition()
->addOption(
new InputOption('--define', '-D', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Define a configuration item value.', [])
);
}
/**
@@ -53,4 +56,18 @@ class Application extends SymfonyApplication
});
$this->add($createRoboFile);
}
/**
* Add self update command, do nothing if null is provided
*
* @param string $repository GitHub Repository for self update
*/
public function addSelfUpdateCommand($repository = null)
{
if (!$repository) {
return;
}
$selfUpdateCommand = new SelfUpdateCommand($this->getName(), $this->getVersion(), $repository);
$this->add($selfUpdateCommand);
}
}
@@ -3,7 +3,8 @@ namespace Robo\Collection;
use Robo\Result;
use Robo\Contract\TaskInterface;
use Robo\Collection\Collection;
use Robo\State\StateAwareInterface;
use Robo\State\Data;
/**
* Creates a task wrapper that converts any Callable into an
@@ -35,7 +36,7 @@ class CallableTask implements TaskInterface
*/
public function run()
{
$result = call_user_func($this->fn);
$result = call_user_func($this->fn, $this->getState());
// If the function returns no result, then count it
// as a success.
if (!isset($result)) {
@@ -50,4 +51,12 @@ class CallableTask implements TaskInterface
return $result;
}
public function getState()
{
if ($this->reference instanceof StateAwareInterface) {
return $this->reference->getState();
}
return new Data();
}
}
@@ -2,6 +2,7 @@
namespace Robo\Collection;
use Robo\Result;
use Robo\State\Data;
use Psr\Log\LogLevel;
use Robo\Contract\TaskInterface;
use Robo\Task\StackBasedTask;
@@ -12,9 +13,9 @@ use Robo\Exception\TaskException;
use Robo\Exception\TaskExitException;
use Robo\Contract\CommandInterface;
use Robo\Common\ProgressIndicatorAwareTrait;
use Robo\Contract\InflectionInterface;
use Robo\State\StateAwareInterface;
use Robo\State\StateAwareTrait;
/**
* Group tasks into a collection that run together. Supports
@@ -30,8 +31,10 @@ use Robo\Contract\InflectionInterface;
* called. Here, taskDeleteDir is used to remove partial results
* of an unfinished task.
*/
class Collection extends BaseTask implements CollectionInterface, CommandInterface
class Collection extends BaseTask implements CollectionInterface, CommandInterface, StateAwareInterface
{
use StateAwareTrait;
/**
* @var \Robo\Collection\Element[]
*/
@@ -52,11 +55,22 @@ class Collection extends BaseTask implements CollectionInterface, CommandInterfa
*/
protected $parentCollection;
/**
* @var callable[]
*/
protected $deferredCallbacks = [];
/**
* @var string[]
*/
protected $messageStoreKeys = [];
/**
* Constructor.
*/
public function __construct()
{
$this->resetState();
}
public function setProgressBarAutoDisplayInterval($interval)
@@ -165,6 +179,7 @@ class Collection extends BaseTask implements CollectionInterface, CommandInterfa
$context += TaskInfo::getTaskContext($this);
return $this->addCode(
function () use ($level, $text, $context) {
$context += $this->getState()->getData();
$this->printTaskOutput($level, $text, $context);
}
);
@@ -552,6 +567,8 @@ class Collection extends BaseTask implements CollectionInterface, CommandInterfa
// the incremental results, if they wish.
$key = Result::isUnnamed($taskName) ? $name : $taskName;
$result->accumulate($key, $taskResult);
// The result message will be the message of the last task executed.
$result->setMessage($taskResult->getMessage());
}
} catch (TaskExitException $exitException) {
$this->fail();
@@ -632,10 +649,79 @@ class Collection extends BaseTask implements CollectionInterface, CommandInterfa
if ($original instanceof InflectionInterface) {
$original->inflect($this);
}
if ($original instanceof StateAwareInterface) {
$original->setState($this->getState());
}
$this->doDeferredInitialization($original);
$taskResult = $task->run();
$taskResult = Result::ensureResult($task, $taskResult);
$this->doStateUpdates($original, $taskResult);
return $taskResult;
}
protected function doStateUpdates($task, Data $taskResult)
{
$this->updateState($taskResult);
$key = spl_object_hash($task);
if (array_key_exists($key, $this->messageStoreKeys)) {
$state = $this->getState();
list($stateKey, $sourceKey) = $this->messageStoreKeys[$key];
$value = empty($sourceKey) ? $taskResult->getMessage() : $taskResult[$sourceKey];
$state[$stateKey] = $value;
}
}
public function storeState($task, $key, $source = '')
{
$this->messageStoreKeys[spl_object_hash($task)] = [$key, $source];
return $this;
}
public function deferTaskConfiguration($task, $functionName, $stateKey)
{
return $this->defer(
$task,
function ($task, $state) use ($functionName, $stateKey) {
$fn = [$task, $functionName];
$value = $state[$stateKey];
$fn($value);
}
);
}
/**
* Defer execution of a callback function until just before a task
* runs. Use this time to provide more settings for the task, e.g. from
* the collection's shared state, which is populated with the results
* of previous test runs.
*/
public function defer($task, $callback)
{
$this->deferredCallbacks[spl_object_hash($task)][] = $callback;
return $this;
}
protected function doDeferredInitialization($task)
{
// If the task is a state consumer, then call its receiveState method
if ($task instanceof \Robo\State\Consumer) {
$task->receiveState($this->getState());
}
// Check and see if there are any deferred callbacks for this task.
$key = spl_object_hash($task);
if (!array_key_exists($key, $this->deferredCallbacks)) {
return;
}
// Call all of the deferred callbacks
foreach ($this->deferredCallbacks[$key] as $fn) {
$fn($task, $this->getState());
}
}
/**
* @param TaskInterface|NestedCollectionInterface|WrappedTaskInterface $task
* @param $parentCollection
@@ -1,26 +1,22 @@
<?php
namespace Robo\Collection;
use Guzzle\Inflection\InflectorInterface;
use Robo\Config;
use Robo\Common\Timer;
use Consolidation\Config\Inject\ConfigForSetters;
use Robo\Config\Config;
use Psr\Log\LogLevel;
use Robo\Contract\InflectionInterface;
use Robo\Contract\TaskInterface;
use Robo\Contract\CompletionInterface;
use Robo\Contract\WrappedTaskInterface;
use Robo\Collection\NestedCollectionInterface;
use Robo\LoadAllTasks;
use Robo\Task\Simulator;
use Robo\Collection\CompletionWrapper;
use Robo\Collection\Temporary;
use Robo\Contract\ConfigAwareInterface;
use Robo\Common\ConfigAwareTrait;
use ReflectionClass;
use Robo\Task\BaseTask;
use Robo\Contract\BuilderAwareInterface;
use Robo\Contract\CommandInterface;
use Robo\Exception\TaskException;
use Robo\Contract\VerbosityThresholdInterface;
use Robo\State\StateAwareInterface;
use Robo\State\StateAwareTrait;
use Robo\Result;
/**
* Creates a collection, and adds tasks to it. The collection builder
@@ -49,8 +45,10 @@ use Robo\Exception\TaskException;
* In the example above, the `taskDeleteDir` will be called if
* ```
*/
class CollectionBuilder extends BaseTask implements NestedCollectionInterface, WrappedTaskInterface, CommandInterface
class CollectionBuilder extends BaseTask implements NestedCollectionInterface, WrappedTaskInterface, CommandInterface, StateAwareInterface
{
use StateAwareTrait;
/**
* @var \Robo\Tasks
*/
@@ -77,6 +75,19 @@ class CollectionBuilder extends BaseTask implements NestedCollectionInterface, W
public function __construct($commandFile)
{
$this->commandFile = $commandFile;
$this->resetState();
}
public static function create($container, $commandFile)
{
$builder = new self($commandFile);
$builder->setLogger($container->get('logger'));
$builder->setProgressIndicator($container->get('progressIndicator'));
$builder->setConfig($container->get('config'));
$builder->setOutputAdapter($container->get('outputAdapter'));
return $builder;
}
/**
@@ -147,9 +158,18 @@ class CollectionBuilder extends BaseTask implements NestedCollectionInterface, W
return $this;
}
public function addCode(callable $code)
/**
* Add arbitrary code to execute as a task.
*
* @see \Robo\Collection\CollectionInterface::addCode
*
* @param callable $code
* @param int|string $name
* @return $this
*/
public function addCode(callable $code, $name = \Robo\Collection\CollectionInterface::UNNAMEDTASK)
{
$this->getCollection()->addCode($code);
$this->getCollection()->addCode($code, $name);
return $this;
}
@@ -245,6 +265,51 @@ class CollectionBuilder extends BaseTask implements NestedCollectionInterface, W
return $this;
}
public function getState()
{
$collection = $this->getCollection();
return $collection->getState();
}
public function storeState($key, $source = '')
{
return $this->callCollectionStateFuntion(__FUNCTION__, func_get_args());
}
public function deferTaskConfiguration($functionName, $stateKey)
{
return $this->callCollectionStateFuntion(__FUNCTION__, func_get_args());
}
public function defer($callback)
{
return $this->callCollectionStateFuntion(__FUNCTION__, func_get_args());
}
protected function callCollectionStateFuntion($functionName, $args)
{
$currentTask = ($this->currentTask instanceof WrappedTaskInterface) ? $this->currentTask->original() : $this->currentTask;
array_unshift($args, $currentTask);
$collection = $this->getCollection();
$fn = [$collection, $functionName];
call_user_func_array($fn, $args);
return $this;
}
public function setVerbosityThreshold($verbosityThreshold)
{
$currentTask = ($this->currentTask instanceof WrappedTaskInterface) ? $this->currentTask->original() : $this->currentTask;
if ($currentTask) {
$currentTask->setVerbosityThreshold($verbosityThreshold);
return $this;
}
parent::setVerbosityThreshold($verbosityThreshold);
return $this;
}
/**
* Return the current task for this collection builder.
* TODO: Not needed?
@@ -266,6 +331,9 @@ class CollectionBuilder extends BaseTask implements NestedCollectionInterface, W
$collectionBuilder = new self($this->commandFile);
$collectionBuilder->inflect($this);
$collectionBuilder->simulated($this->isSimulated());
$collectionBuilder->setVerbosityThreshold($this->verbosityThreshold());
$collectionBuilder->setState($this->getState());
return $collectionBuilder;
}
@@ -355,6 +423,7 @@ class CollectionBuilder extends BaseTask implements NestedCollectionInterface, W
throw new RuntimeException("Can not construct task $name");
}
$task = $this->fixTask($task, $args);
$this->configureTask($name, $task);
return $this->addTaskToCollection($task);
}
@@ -366,10 +435,15 @@ class CollectionBuilder extends BaseTask implements NestedCollectionInterface, W
*/
protected function fixTask($task, $args)
{
$task->inflect($this);
if ($task instanceof InflectionInterface) {
$task->inflect($this);
}
if ($task instanceof BuilderAwareInterface) {
$task->setBuilder($this);
}
if ($task instanceof VerbosityThresholdInterface) {
$task->setVerbosityThreshold($this->verbosityThreshold());
}
// Do not wrap our wrappers.
if ($task instanceof CompletionWrapper || $task instanceof Simulator) {
@@ -402,6 +476,25 @@ class CollectionBuilder extends BaseTask implements NestedCollectionInterface, W
return $task;
}
/**
* Check to see if there are any setter methods defined in configuration
* for this task.
*/
protected function configureTask($taskClass, $task)
{
$taskClass = static::configClassIdentifier($taskClass);
$configurationApplier = new ConfigForSetters($this->getConfig(), $taskClass, 'task.');
$configurationApplier->apply($task, 'settings');
// TODO: If we counted each instance of $taskClass that was called from
// this builder, then we could also apply configuration from
// "task.{$taskClass}[$N].settings"
// TODO: If the builder knew what the current command name was,
// then we could also search for task configuration under
// command-specific keys such as "command.{$commandname}.task.{$taskClass}.settings".
}
/**
* When we run the collection builder, run everything in the collection.
*
@@ -413,6 +506,7 @@ class CollectionBuilder extends BaseTask implements NestedCollectionInterface, W
$result = $this->runTasks();
$this->stopTimer();
$result['time'] = $this->getExecutionTime();
$result->mergeData($this->getState()->getData());
return $result;
}
@@ -425,7 +519,8 @@ class CollectionBuilder extends BaseTask implements NestedCollectionInterface, W
protected function runTasks()
{
if (!$this->collection && $this->currentTask) {
return $this->currentTask->run();
$result = $this->currentTask->run();
return Result::ensureResult($this->currentTask, $result);
}
return $this->getCollection()->run();
}
@@ -464,6 +559,7 @@ class CollectionBuilder extends BaseTask implements NestedCollectionInterface, W
if (!isset($this->collection)) {
$this->collection = new Collection();
$this->collection->inflect($this);
$this->collection->setState($this->getState());
$this->collection->setProgressBarAutoDisplayInterval($this->getConfig()->get(Config::PROGRESS_BAR_AUTO_DISPLAY_INTERVAL));
if (isset($this->currentTask)) {
@@ -141,7 +141,8 @@ interface CollectionInterface extends NestedCollectionInterface
* message will not be printed.
*
* @param string $text Message to print.
* @param array $context Extra context data for use by the logger.
* @param array $context Extra context data for use by the logger. Note
* that the data from the collection state is merged with the provided context.
* @param \Psr\Log\LogLevel|string $level The log level to print the information at. Default is NOTICE.
*
* @return $this
@@ -1,9 +1,6 @@
<?php
namespace Robo\Collection;
use Psr\Log\LogLevel;
use Robo\Contract\TaskInterface;
interface NestedCollectionInterface
{
/**
@@ -1,11 +1,9 @@
<?php
namespace Robo\Collection;
use Robo\Collection\NestedCollectionInterface;
use Robo\Result;
use Robo\TaskInfo;
use Robo\Task\BaseTask;
use Robo\Contract\TaskInterface;
use Robo\Contract\BuilderAwareInterface;
use Robo\Common\BuilderAwareTrait;
@@ -2,8 +2,6 @@
namespace Robo\Collection;
use Robo\Contract\TaskInterface;
/**
* The temporary collection keeps track of the global collection of
* temporary cleanup tasks in instances where temporary-generating
@@ -2,7 +2,6 @@
namespace Robo\Common;
use Robo\Robo;
use Robo\Collection\CollectionBuilder;
trait BuilderAwareTrait
@@ -1,7 +1,7 @@
<?php
namespace Robo\Common;
use Symfony\Component\Process\ProcessUtils;
use Robo\Common\ProcessUtils;
/**
* Use this to add arguments and options to the $arguments property.
@@ -46,10 +46,14 @@ trait CommandArguments
* Pass the provided string in its raw (as provided) form as an argument to executable.
*
* @param string $arg
*
* @return $this
*/
public function rawArg($arg)
{
$this->arguments .= " $arg";
return $this;
}
/**
@@ -74,36 +78,51 @@ trait CommandArguments
*
* @param string $option
* @param string $value
* @param string $separator
*
* @return $this
*/
public function option($option, $value = null)
public function option($option, $value = null, $separator = ' ')
{
if ($option !== null and strpos($option, '-') !== 0) {
$option = "--$option";
}
$this->arguments .= null == $option ? '' : " " . $option;
$this->arguments .= null == $value ? '' : " " . static::escape($value);
$this->arguments .= null == $value ? '' : $separator . static::escape($value);
return $this;
}
/**
* Pass multiple options to executable. Value can be a string or array.
* Pass multiple options to executable. The associative array contains
* the key:value pairs that become `--key value`, for each item in the array.
* Values are automatically escaped.
*/
public function options(array $options, $separator = ' ')
{
foreach ($options as $option => $value) {
$this->option($option, $value, $separator);
}
return $this;
}
/**
* Pass an option with multiple values to executable. Value can be a string or array.
* Option values are automatically escaped.
*
* @param string $option
* @param string|array $value
* @param string $separator
*
* @return $this
*/
public function optionList($option, $value = array())
public function optionList($option, $value = array(), $separator = ' ')
{
if (is_array($value)) {
foreach ($value as $item) {
$this->optionList($option, $item);
$this->optionList($option, $item, $separator);
}
} else {
$this->option($option, $value);
$this->option($option, $value, $separator);
}
return $this;
@@ -3,25 +3,25 @@
namespace Robo\Common;
use Robo\Robo;
use Robo\Config;
use Consolidation\Config\ConfigInterface;
trait ConfigAwareTrait
{
/**
* @var \Robo\Config
* @var ConfigInterface
*/
protected $config;
/**
* Set the config management object.
*
* @param \Robo\Config $config
* @param ConfigInterface $config
*
* @return $this
*
* @see \Robo\Contract\ConfigAwareInterface::setConfig()
*/
public function setConfig(Config $config)
public function setConfig(ConfigInterface $config)
{
$this->config = $config;
@@ -31,7 +31,7 @@ trait ConfigAwareTrait
/**
* Get the config management object.
*
* @return \Robo\Config
* @return ConfigInterface
*
* @see \Robo\Contract\ConfigAwareInterface::getConfig()
*/
@@ -40,6 +40,32 @@ trait ConfigAwareTrait
return $this->config;
}
/**
* Any class that uses ConfigAwareTrait SHOULD override this method
* , and define a prefix for its configuration items. This is usually
* done in a base class. When used, this method should return a string
* that ends with a "."; see BaseTask::configPrefix().
*
* @return string
*/
protected static function configPrefix()
{
return '';
}
protected static function configClassIdentifier($classname)
{
$configIdentifier = strtr($classname, '\\', '.');
$configIdentifier = preg_replace('#^(.*\.Task\.|\.)#', '', $configIdentifier);
return $configIdentifier;
}
protected static function configPostfix()
{
return '';
}
/**
* @param string $key
*
@@ -47,18 +73,24 @@ trait ConfigAwareTrait
*/
private static function getClassKey($key)
{
return sprintf('%s.%s', get_called_class(), $key);
$configPrefix = static::configPrefix(); // task.
$configClass = static::configClassIdentifier(get_called_class()); // PARTIAL_NAMESPACE.CLASSNAME
$configPostFix = static::configPostfix(); // .settings
return sprintf('%s%s%s.%s', $configPrefix, $configClass, $configPostFix, $key);
}
/**
* @param string $key
* @param mixed $value
*
* @deprecated
* @param Config|null $config
*/
public static function configure($key, $value)
public static function configure($key, $value, $config = null)
{
Robo::config()->set(static::getClassKey($key), $value);
if (!$config) {
$config = Robo::config();
}
$config->setDefault(static::getClassKey($key), $value);
}
/**
@@ -11,15 +11,7 @@ use Symfony\Component\Process\Process;
*/
trait ExecCommand
{
/**
* @var bool
*/
protected $isPrinted = true;
/**
* @var string
*/
protected $workingDirectory;
use ExecTrait;
/**
* @var \Robo\Common\TimeKeeper
@@ -37,45 +29,6 @@ trait ExecCommand
return $this->execTimer;
}
/**
* Is command printing its output to screen
*
* @return bool
*/
public function getPrinted()
{
return $this->isPrinted;
}
/**
* Changes working directory of command
*
* @param string $dir
*
* @return $this
*/
public function dir($dir)
{
$this->workingDirectory = $dir;
return $this;
}
/**
* Should command output be printed
*
* @param bool $arg
*
* @return $this
*/
public function printed($arg)
{
if (is_bool($arg)) {
$this->isPrinted = $arg;
}
return $this;
}
/**
* Look for a "{$cmd}.phar" in the current working
* directory; return a string to exec it if it is
@@ -135,7 +88,8 @@ trait ExecCommand
*/
protected function findProjectBin()
{
$candidates = [ __DIR__ . '/../../vendor/bin', __DIR__ . '/../../bin' ];
$cwd = getcwd();
$candidates = [ __DIR__ . '/../../vendor/bin', __DIR__ . '/../../bin', $cwd . '/vendor/bin' ];
// If this project is inside a vendor directory, give highest priority
// to that directory.
@@ -170,6 +124,11 @@ trait ExecCommand
return $cmd;
}
protected function getCommandDescription()
{
return $this->process->getCommandLine();
}
/**
* @param string $command
*
@@ -177,21 +136,13 @@ trait ExecCommand
*/
protected function executeCommand($command)
{
$process = new Process($command);
$process->setTimeout(null);
if ($this->workingDirectory) {
$process->setWorkingDirectory($this->workingDirectory);
}
$this->getExecTimer()->start();
if ($this->isPrinted) {
$process->run(function ($type, $buffer) {
print $buffer;
});
} else {
$process->run();
}
$this->getExecTimer()->stop();
return new Result($this, $process->getExitCode(), $process->getOutput(), ['time' => $this->getExecTimer()->elapsed()]);
// TODO: Symfony 4 requires that we supply the working directory.
$result_data = $this->execute(new Process($command, getcwd()));
return new Result(
$this,
$result_data->getExitCode(),
$result_data->getMessage(),
$result_data->getData()
);
}
}
@@ -0,0 +1,408 @@
<?php
namespace Robo\Common;
use Robo\ResultData;
use Symfony\Component\Process\Process;
/**
* Class ExecTrait
* @package Robo\Common
*/
trait ExecTrait
{
/**
* @var bool
*/
protected $background = false;
/**
* @var null|int
*/
protected $timeout = null;
/**
* @var null|int
*/
protected $idleTimeout = null;
/**
* @var null|array
*/
protected $env = null;
/**
* @var Process
*/
protected $process;
/**
* @var resource|string
*/
protected $input;
/**
* @var boolean
*/
protected $interactive = null;
/**
* @var bool
*/
protected $isPrinted = true;
/**
* @var bool
*/
protected $isMetadataPrinted = true;
/**
* @var string
*/
protected $workingDirectory;
/**
* @return string
*/
abstract public function getCommandDescription();
/** Typically provided by Timer trait via ProgressIndicatorAwareTrait. */
abstract public function startTimer();
abstract public function stopTimer();
abstract public function getExecutionTime();
/**
* Typically provided by TaskIO Trait.
*/
abstract public function hideTaskProgress();
abstract public function showTaskProgress($inProgress);
abstract public function printTaskInfo($text, $context = null);
/**
* Typically provided by VerbosityThresholdTrait.
*/
abstract public function verbosityMeetsThreshold();
abstract public function writeMessage($message);
/**
* Sets $this->interactive() based on posix_isatty().
*
* @return $this
*/
public function detectInteractive()
{
// If the caller did not explicity set the 'interactive' mode,
// and output should be produced by this task (verbosityMeetsThreshold),
// then we will automatically set interactive mode based on whether
// or not output was redirected when robo was executed.
if (!isset($this->interactive) && function_exists('posix_isatty') && $this->verbosityMeetsThreshold()) {
$this->interactive = posix_isatty(STDOUT);
}
return $this;
}
/**
* Executes command in background mode (asynchronously)
*
* @return $this
*/
public function background($arg = true)
{
$this->background = $arg;
return $this;
}
/**
* Stop command if it runs longer then $timeout in seconds
*
* @param int $timeout
*
* @return $this
*/
public function timeout($timeout)
{
$this->timeout = $timeout;
return $this;
}
/**
* Stops command if it does not output something for a while
*
* @param int $timeout
*
* @return $this
*/
public function idleTimeout($timeout)
{
$this->idleTimeout = $timeout;
return $this;
}
/**
* Set a single environment variable, or multiple.
*/
public function env($env, $value = null)
{
if (!is_array($env)) {
$env = [$env => ($value ? $value : true)];
}
return $this->envVars($env);
}
/**
* Sets the environment variables for the command
*
* @param array $env
*
* @return $this
*/
public function envVars(array $env)
{
$this->env = $env;
return $this;
}
/**
* Pass an input to the process. Can be resource created with fopen() or string
*
* @param resource|string $input
*
* @return $this
*/
public function setInput($input)
{
$this->input = $input;
return $this;
}
/**
* Attach tty to process for interactive input
*
* @param $interactive bool
*
* @return $this
*/
public function interactive($interactive = true)
{
$this->interactive = $interactive;
return $this;
}
/**
* Is command printing its output to screen
*
* @return bool
*/
public function getPrinted()
{
return $this->isPrinted;
}
/**
* Changes working directory of command
*
* @param string $dir
*
* @return $this
*/
public function dir($dir)
{
$this->workingDirectory = $dir;
return $this;
}
/**
* Shortcut for setting isPrinted() and isMetadataPrinted() to false.
*
* @param bool $arg
*
* @return $this
*/
public function silent($arg)
{
if (is_bool($arg)) {
$this->isPrinted = !$arg;
$this->isMetadataPrinted = !$arg;
}
return $this;
}
/**
* Should command output be printed
*
* @param bool $arg
*
* @return $this
*
* @deprecated
*/
public function printed($arg)
{
$this->logger->warning("printed() is deprecated. Please use printOutput().");
return $this->printOutput($arg);
}
/**
* Should command output be printed
*
* @param bool $arg
*
* @return $this
*/
public function printOutput($arg)
{
if (is_bool($arg)) {
$this->isPrinted = $arg;
}
return $this;
}
/**
* Should command metadata be printed. I,e., command and timer.
*
* @param bool $arg
*
* @return $this
*/
public function printMetadata($arg)
{
if (is_bool($arg)) {
$this->isMetadataPrinted = $arg;
}
return $this;
}
/**
* @param Process $process
* @param callable $output_callback
*
* @return \Robo\ResultData
*/
protected function execute($process, $output_callback = null)
{
$this->process = $process;
if (!$output_callback) {
$output_callback = function ($type, $buffer) {
$progressWasVisible = $this->hideTaskProgress();
$this->writeMessage($buffer);
$this->showTaskProgress($progressWasVisible);
};
}
$this->detectInteractive();
if ($this->isMetadataPrinted) {
$this->printAction();
}
$this->process->setTimeout($this->timeout);
$this->process->setIdleTimeout($this->idleTimeout);
if ($this->workingDirectory) {
$this->process->setWorkingDirectory($this->workingDirectory);
}
if ($this->input) {
$this->process->setInput($this->input);
}
if ($this->interactive && $this->isPrinted) {
$this->process->setTty(true);
}
if (isset($this->env)) {
$this->process->setEnv($this->env);
}
if (!$this->background && !$this->isPrinted) {
$this->startTimer();
$this->process->run();
$this->stopTimer();
$output = rtrim($this->process->getOutput());
return new ResultData(
$this->process->getExitCode(),
$output,
$this->getResultData()
);
}
if (!$this->background && $this->isPrinted) {
$this->startTimer();
$this->process->run($output_callback);
$this->stopTimer();
return new ResultData(
$this->process->getExitCode(),
$this->process->getOutput(),
$this->getResultData()
);
}
try {
$this->process->start();
} catch (\Exception $e) {
return new ResultData(
$this->process->getExitCode(),
$e->getMessage(),
$this->getResultData()
);
}
return new ResultData($this->process->getExitCode());
}
/**
*
*/
protected function stop()
{
if ($this->background && isset($this->process) && $this->process->isRunning()) {
$this->process->stop();
$this->printTaskInfo(
"Stopped {command}",
['command' => $this->getCommandDescription()]
);
}
}
/**
* @param array $context
*/
protected function printAction($context = [])
{
$command = $this->getCommandDescription();
$formatted_command = $this->formatCommandDisplay($command);
$dir = $this->workingDirectory ? " in {dir}" : "";
$this->printTaskInfo("Running {command}$dir", [
'command' => $formatted_command,
'dir' => $this->workingDirectory
] + $context);
}
/**
* @param $command
*
* @return mixed
*/
protected function formatCommandDisplay($command)
{
$formatted_command = str_replace("&&", "&&\n", $command);
$formatted_command = str_replace("||", "||\n", $formatted_command);
return $formatted_command;
}
/**
* Gets the data array to be passed to Result().
*
* @return array
* The data array passed to Result().
*/
protected function getResultData()
{
if ($this->isMetadataPrinted) {
return ['time' => $this->getExecutionTime()];
}
return [];
}
}
+4 -9
View File
@@ -1,12 +1,7 @@
<?php
namespace Robo\Common;
use Robo\Robo;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;
@@ -76,7 +71,7 @@ trait IO
* @param int $length
* @param string $format
*/
private function formattedOutput($text, $length, $format)
protected function formattedOutput($text, $length, $format)
{
$lines = explode("\n", trim($text, "\n"));
$maxLineLength = array_reduce(array_map('strlen', $lines), 'max');
@@ -143,7 +138,7 @@ trait IO
*
* @return string
*/
private function doAsk(Question $question)
protected function doAsk(Question $question)
{
return $this->getDialog()->ask($this->input(), $this->output(), $question);
}
@@ -153,7 +148,7 @@ trait IO
*
* @return string
*/
private function formatQuestion($message)
protected function formatQuestion($message)
{
return "<question>? $message</question> ";
}
@@ -169,7 +164,7 @@ trait IO
/**
* @param $text
*/
private function writeln($text)
protected function writeln($text)
{
$this->output()->writeln($text);
}
@@ -0,0 +1,38 @@
<?php
namespace Robo\Common;
use Robo\Contract\OutputAdapterInterface;
use Robo\Contract\OutputAwareInterface;
use Robo\Contract\VerbosityThresholdInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Adapt OutputInterface or other output function to the VerbosityThresholdInterface.
*/
class OutputAdapter implements OutputAdapterInterface, OutputAwareInterface
{
use OutputAwareTrait;
protected $verbosityMap = [
VerbosityThresholdInterface::VERBOSITY_NORMAL => OutputInterface::VERBOSITY_NORMAL,
VerbosityThresholdInterface::VERBOSITY_VERBOSE => OutputInterface::VERBOSITY_VERBOSE,
VerbosityThresholdInterface::VERBOSITY_VERY_VERBOSE => OutputInterface::VERBOSITY_VERY_VERBOSE,
VerbosityThresholdInterface::VERBOSITY_DEBUG => OutputInterface::VERBOSITY_DEBUG,
];
public function verbosityMeetsThreshold($verbosityThreshold)
{
if (!isset($this->verbosityMap[$verbosityThreshold])) {
return true;
}
$verbosityThreshold = $this->verbosityMap[$verbosityThreshold];
$verbosity = $this->output()->getVerbosity();
return $verbosity >= $verbosityThreshold;
}
public function writeMessage($message)
{
$this->output()->write($message);
}
}
@@ -0,0 +1,51 @@
<?php
namespace Robo\Common;
use Psr\Log\LoggerAwareInterface;
use Robo\Contract\ConfigAwareInterface;
use Robo\Contract\OutputAwareInterface;
use Robo\Contract\VerbosityThresholdInterface;
use Symfony\Component\Process\Process;
class ProcessExecutor implements ConfigAwareInterface, LoggerAwareInterface, OutputAwareInterface, VerbosityThresholdInterface
{
use ExecTrait;
use TaskIO; // uses LoggerAwareTrait and ConfigAwareTrait
use ProgressIndicatorAwareTrait;
use OutputAwareTrait;
/**
* @param Process $process
* @return type
*/
public function __construct(Process $process)
{
$this->process = $process;
}
public static function create($container, $process)
{
$processExecutor = new self($process);
$processExecutor->setLogger($container->get('logger'));
$processExecutor->setProgressIndicator($container->get('progressIndicator'));
$processExecutor->setConfig($container->get('config'));
$processExecutor->setOutputAdapter($container->get('outputAdapter'));
return $processExecutor;
}
/**
* @return string
*/
protected function getCommandDescription()
{
return $this->process->getCommandLine();
}
public function run()
{
return $this->execute($this->process);
}
}
@@ -0,0 +1,79 @@
<?php
/*
* This file is derived from part of the Symfony package, which is
* (c) Fabien Potencier <fabien@symfony.com>
*/
namespace Robo\Common;
use Symfony\Component\Process\Exception\InvalidArgumentException;
/**
* ProcessUtils is a bunch of utility methods. We want to allow Robo 1.x
* to work with Symfony 4.x while remaining backwards compatibility. This
* requires us to replace some deprecated functionality removed in Symfony.
*/
class ProcessUtils
{
/**
* This class should not be instantiated.
*/
private function __construct()
{
}
/**
* Escapes a string to be used as a shell argument.
*
* @param string $argument The argument that will be escaped
*
* @return string The escaped argument
*
* @deprecated since version 3.3, to be removed in 4.0. Use a command line array or give env vars to the `Process::start/run()` method instead.
*/
public static function escapeArgument($argument)
{
@trigger_error('The '.__METHOD__.'() method is a copy of a method that was deprecated by Symfony 3.3 and removed in Symfony 4; it will be removed in Robo 2.0.', E_USER_DEPRECATED);
//Fix for PHP bug #43784 escapeshellarg removes % from given string
//Fix for PHP bug #49446 escapeshellarg doesn't work on Windows
//@see https://bugs.php.net/bug.php?id=43784
//@see https://bugs.php.net/bug.php?id=49446
if ('\\' === DIRECTORY_SEPARATOR) {
if ('' === $argument) {
return escapeshellarg($argument);
}
$escapedArgument = '';
$quote = false;
foreach (preg_split('/(")/', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) {
if ('"' === $part) {
$escapedArgument .= '\\"';
} elseif (self::isSurroundedBy($part, '%')) {
// Avoid environment variable expansion
$escapedArgument .= '^%"'.substr($part, 1, -1).'"^%';
} else {
// escape trailing backslash
if ('\\' === substr($part, -1)) {
$part .= '\\';
}
$quote = true;
$escapedArgument .= $part;
}
}
if ($quote) {
$escapedArgument = '"'.$escapedArgument.'"';
}
return $escapedArgument;
}
return "'".str_replace("'", "'\\''", $argument)."'";
}
private static function isSurroundedBy($arg, $char)
{
return 2 < strlen($arg) && $char === $arg[0] && $char === $arg[strlen($arg) - 1];
}
}
@@ -1,6 +1,9 @@
<?php
namespace Robo\Common;
use Robo\Contract\ProgressIndicatorAwareInterface;
use Robo\Contract\VerbosityThresholdInterface;
trait ProgressIndicatorAwareTrait
{
use Timer;
@@ -20,10 +23,14 @@ trait ProgressIndicatorAwareTrait
/**
* @param null|\Robo\Common\ProgressIndicator $progressIndicator
*
* @return ProgressIndicatorAwareInterface
*/
public function setProgressIndicator($progressIndicator)
{
$this->progressIndicator = $progressIndicator;
return $this;
}
/**
@@ -70,6 +77,10 @@ trait ProgressIndicatorAwareTrait
protected function startProgressIndicator()
{
$this->startTimer();
if ($this instanceof VerbosityThresholdInterface
&& !$this->verbosityMeetsThreshold()) {
return;
}
if (!$this->progressIndicator) {
return;
}
@@ -4,9 +4,7 @@ namespace Robo\Common;
use Robo\Robo;
use Robo\TaskInfo;
use Consolidation\Log\ConsoleLogLevel;
use Robo\Common\ConfigAwareTrait;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Robo\Contract\ProgressIndicatorAwareInterface;
@@ -21,6 +19,7 @@ trait TaskIO
{
use LoggerAwareTrait;
use ConfigAwareTrait;
use VerbosityThresholdTrait;
/**
* @return mixed|null|\Psr\Log\LoggerInterface
@@ -137,6 +136,9 @@ trait TaskIO
*/
protected function printTaskOutput($level, $text, $context)
{
if (!$this->verbosityMeetsThreshold()) {
return;
}
$logger = $this->logger();
if (!$logger) {
return;
@@ -0,0 +1,79 @@
<?php
namespace Robo\Common;
use Robo\Robo;
use Robo\TaskInfo;
use Robo\Contract\OutputAdapterInterface;
use Robo\Contract\VerbosityThresholdInterface;
use Consolidation\Log\ConsoleLogLevel;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LogLevel;
use Robo\Contract\ProgressIndicatorAwareInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Task input/output methods. TaskIO is 'used' in BaseTask, so any
* task that extends this class has access to all of the methods here.
* printTaskInfo, printTaskSuccess, and printTaskError are the three
* primary output methods that tasks are encouraged to use. Tasks should
* avoid using the IO trait output methods.
*/
trait VerbosityThresholdTrait
{
/** var OutputAdapterInterface */
protected $outputAdapter;
protected $verbosityThreshold = 0;
/**
* Required verbocity level before any TaskIO output will be produced.
* e.g. OutputInterface::VERBOSITY_VERBOSE
*/
public function setVerbosityThreshold($verbosityThreshold)
{
$this->verbosityThreshold = $verbosityThreshold;
return $this;
}
public function verbosityThreshold()
{
return $this->verbosityThreshold;
}
public function setOutputAdapter(OutputAdapterInterface $outputAdapter)
{
$this->outputAdapter = $outputAdapter;
}
/**
* @return OutputAdapterInterface
*/
public function outputAdapter()
{
return $this->outputAdapter;
}
public function hasOutputAdapter()
{
return isset($this->outputAdapter);
}
public function verbosityMeetsThreshold()
{
if ($this->hasOutputAdapter()) {
return $this->outputAdapter()->verbosityMeetsThreshold($this->verbosityThreshold());
}
return true;
}
/**
* Print a message if the selected verbosity level is over this task's
* verbosity threshhold.
*/
public function writeMessage($message)
{
if (!$this->verbosityMeetsThreshold()) {
return;
}
$this->outputAdapter()->writeMessage($message);
}
}
+4 -117
View File
@@ -1,122 +1,9 @@
<?php
namespace Robo;
class Config
/**
* @deprecated Use \Robo\Config\Config
*/
class Config extends \Robo\Config\Config
{
const PROGRESS_BAR_AUTO_DISPLAY_INTERVAL = 'progress-delay';
const DEFAULT_PROGRESS_DELAY = 2;
const SIMULATE = 'simulate';
const DECORATED = 'decorated';
/**
* @var array
*/
protected $config = [];
/**
* Fet a configuration value
*
* @param string $key Which config item to look up
* @param string|null $defaultOverride Override usual default value with a different default
*
* @return mixed
*/
public function get($key, $defaultOverride = null)
{
if (isset($this->config[$key])) {
return $this->config[$key];
}
return $this->getDefault($key, $defaultOverride);
}
/**
* Set a config value
*
* @param string $key
* @param mixed $value
*
* @return $this
*/
public function set($key, $value)
{
$this->config[$key] = $value;
return $this;
}
/**
* Return an associative array containing all of the global configuration
* options and their default values.
*
* @return array
*/
public function getGlobalOptionDefaultValues()
{
$globalOptions =
[
self::PROGRESS_BAR_AUTO_DISPLAY_INTERVAL => self::DEFAULT_PROGRESS_DELAY,
self::SIMULATE => false,
];
return $globalOptions;
}
/**
* Return the default value for a given configuration item.
*
* @param string $key
* @param mixed $defaultOverride
*
* @return mixed
*/
public function getDefault($key, $defaultOverride = null)
{
$globalOptions = $this->getGlobalOptionDefaultValues();
return isset($globalOptions[$key]) ? $globalOptions[$key] : $defaultOverride;
}
/**
* @return bool
*/
public function isSimulated()
{
return $this->get(self::SIMULATE);
}
/**
* @param bool $simulated
*
* @return $this
*/
public function setSimulated($simulated = true)
{
return $this->set(self::SIMULATE, $simulated);
}
/**
* @return bool
*/
public function isDecorated()
{
return $this->get(self::DECORATED);
}
/**
* @param bool $decorated
*
* @return $this
*/
public function setDecorated($decorated = true)
{
return $this->set(self::DECORATED, $decorated);
}
/**
* @param int $interval
*
* @return $this
*/
public function setProgressBarAutoDisplayInterval($interval)
{
return $this->set(self::PROGRESS_BAR_AUTO_DISPLAY_INTERVAL, $interval);
}
}
@@ -0,0 +1,130 @@
<?php
namespace Robo\Config;
class Config extends \Consolidation\Config\Config implements GlobalOptionDefaultValuesInterface
{
const PROGRESS_BAR_AUTO_DISPLAY_INTERVAL = 'options.progress-delay';
const DEFAULT_PROGRESS_DELAY = 2;
const SIMULATE = 'options.simulate';
// Read-only configuration properties; changing these has no effect.
const INTERACTIVE = 'options.interactive';
const DECORATED = 'options.decorated';
/**
* Create a new configuration object, and initialize it with
* the provided nested array containing configuration data.
*/
public function __construct(array $data = null)
{
parent::__construct($data);
$this->defaults = $this->getGlobalOptionDefaultValues();
}
/**
* Return an associative array containing all of the global configuration
* options and their default values.
*
* @return array
*/
public function getGlobalOptionDefaultValues()
{
$globalOptions =
[
self::PROGRESS_BAR_AUTO_DISPLAY_INTERVAL => self::DEFAULT_PROGRESS_DELAY,
self::SIMULATE => false,
];
return $this->trimPrefixFromGlobalOptions($globalOptions);
}
/**
* Remove the 'options.' prefix from the global options list.
*/
protected function trimPrefixFromGlobalOptions($globalOptions)
{
$result = [];
foreach ($globalOptions as $option => $value) {
$option = str_replace('options.', '', $option);
$result[$option] = $value;
}
return $result;
}
/**
* @deprecated Use $config->get(Config::SIMULATE)
*
* @return bool
*/
public function isSimulated()
{
return $this->get(self::SIMULATE);
}
/**
* @deprecated Use $config->set(Config::SIMULATE, true)
*
* @param bool $simulated
*
* @return $this
*/
public function setSimulated($simulated = true)
{
return $this->set(self::SIMULATE, $simulated);
}
/**
* @deprecated Use $config->get(Config::INTERACTIVE)
*
* @return bool
*/
public function isInteractive()
{
return $this->get(self::INTERACTIVE);
}
/**
* @deprecated Use $config->set(Config::INTERACTIVE, true)
*
* @param bool $interactive
*
* @return $this
*/
public function setInteractive($interactive = true)
{
return $this->set(self::INTERACTIVE, $interactive);
}
/**
* @deprecated Use $config->get(Config::DECORATED)
*
* @return bool
*/
public function isDecorated()
{
return $this->get(self::DECORATED);
}
/**
* @deprecated Use $config->set(Config::DECORATED, true)
*
* @param bool $decorated
*
* @return $this
*/
public function setDecorated($decorated = true)
{
return $this->set(self::DECORATED, $decorated);
}
/**
* @deprecated Use $config->set(Config::PROGRESS_BAR_AUTO_DISPLAY_INTERVAL, $interval)
*
* @param int $interval
*
* @return $this
*/
public function setProgressBarAutoDisplayInterval($interval)
{
return $this->set(self::PROGRESS_BAR_AUTO_DISPLAY_INTERVAL, $interval);
}
}
@@ -0,0 +1,17 @@
<?php
namespace Robo\Config;
/**
* @deprecated Use robo.yml instead
*
* robo.yml:
*
* options:
* simulated: false
* progress-delay: 2
*
* etc.
*/
interface GlobalOptionDefaultValuesInterface extends \Consolidation\Config\GlobalOptionDefaultValuesInterface
{
}
@@ -2,23 +2,23 @@
namespace Robo\Contract;
use Robo\Config;
use Consolidation\Config\ConfigInterface;
interface ConfigAwareInterface
{
/**
* Set the config reference
*
* @param \Robo\Config $config
* @param ConfigInterface $config
*
* @return $this
*/
public function setConfig(Config $config);
public function setConfig(ConfigInterface $config);
/**
* Get the config reference
*
* @return \Robo\Config
* @return ConfigInterface
*/
public function getConfig();
}
@@ -0,0 +1,11 @@
<?php
namespace Robo\Contract;
/**
* Adapt OutputInterface or other output function to the VerbosityThresholdInterface.
*/
interface OutputAdapterInterface
{
public function verbosityMeetsThreshold($verbosityThreshold);
public function writeMessage($message);
}
@@ -0,0 +1,24 @@
<?php
namespace Robo\Contract;
use Robo\Contract\OutputAdapterInterface;
/**
* Record and determine whether the current verbosity level exceeds the
* desired threshold level to produce output.
*/
interface VerbosityThresholdInterface
{
const VERBOSITY_NORMAL = 1;
const VERBOSITY_VERBOSE = 2;
const VERBOSITY_VERY_VERBOSE = 3;
const VERBOSITY_DEBUG = 4;
public function setVerbosityThreshold($verbosityThreshold);
public function verbosityThreshold();
public function setOutputAdapter(OutputAdapterInterface $outputAdapter);
public function outputAdapter();
public function hasOutputAdapter();
public function verbosityMeetsThreshold();
public function writeMessage($message);
}
@@ -3,21 +3,63 @@ namespace Robo;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Robo\Contract\ConfigAwareInterface;
use Robo\Common\ConfigAwareTrait;
use Robo\Config\GlobalOptionDefaultValuesInterface;
class GlobalOptionsEventListener implements EventSubscriberInterface, ConfigAwareInterface
{
use ConfigAwareTrait;
/** @var Application */
protected $application;
/** @var string */
protected $prefix;
/**
* GlobalOptionsEventListener listener
*/
public function __construct()
{
$this->prefix = 'options';
}
/**
* Add a reference to the Symfony Console application object.
*/
public function setApplication($application)
{
$this->application = $application;
return $this;
}
/**
* Stipulate the prefix to use for option injection.
* @param string $prefix
*/
public function setGlobalOptionsPrefix($prefix)
{
$this->prefix = $prefix;
return $this;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return [ConsoleEvents::COMMAND => 'setGlobalOptions'];
return [ConsoleEvents::COMMAND => 'handleCommandEvent'];
}
/**
* Run all of our individual operations when a command event is received.
*/
public function handleCommandEvent(ConsoleCommandEvent $event)
{
$this->setGlobalOptions($event);
$this->setConfigurationValues($event);
}
/**
@@ -31,15 +73,74 @@ class GlobalOptionsEventListener implements EventSubscriberInterface, ConfigAwar
{
$config = $this->getConfig();
$input = $event->getInput();
$globalOptions = $config->getGlobalOptionDefaultValues();
$globalOptions = $config->get($this->prefix, []);
if ($config instanceof \Consolidation\Config\GlobalOptionDefaultValuesInterface) {
$globalOptions += $config->getGlobalOptionDefaultValues();
}
$globalOptions += $this->applicationOptionDefaultValues();
// Set any config value that has a defined global option (e.g. --simulate)
foreach ($globalOptions as $option => $default) {
$value = $input->hasOption($option) ? $input->getOption($option) : null;
// Unfortunately, the `?:` operator does not differentate between `0` and `null`
if (!isset($value)) {
$value = $default;
}
$config->set($option, $value);
$config->set($this->prefix . '.' . $option, $value);
}
}
/**
* Examine the commandline --define / -D options, and apply the provided
* values to the active configuration.
*
* @param \Symfony\Component\Console\Event\ConsoleCommandEvent $event
*/
public function setConfigurationValues(ConsoleCommandEvent $event)
{
$config = $this->getConfig();
$input = $event->getInput();
// Also set any `-D config.key=value` options from the commandline.
if ($input->hasOption('define')) {
$configDefinitions = $input->getOption('define');
foreach ($configDefinitions as $value) {
list($key, $value) = $this->splitConfigKeyValue($value);
$config->set($key, $value);
}
}
}
/**
* Split up the key=value config setting into its component parts. If
* the input string contains no '=' character, then the value will be 'true'.
*
* @param string $value
* @return array
*/
protected function splitConfigKeyValue($value)
{
$parts = explode('=', $value, 2);
$parts[] = true;
return $parts;
}
/**
* Get default option values from the Symfony Console application, if
* it is available.
*/
protected function applicationOptionDefaultValues()
{
if (!$this->application) {
return [];
}
$result = [];
foreach ($this->application->getDefinition()->getOptions() as $key => $option) {
$result[$key] = $option->acceptValue() ? $option->getDefault() : null;
}
return $result;
}
}
@@ -2,13 +2,12 @@
namespace Robo\Log;
use Robo\Result;
use Robo\TaskInfo;
use Robo\Contract\PrintedInterface;
use Robo\Contract\ProgressIndicatorAwareInterface;
use Robo\Contract\VerbosityThresholdInterface;
use Robo\Common\ProgressIndicatorAwareTrait;
use Psr\Log\LogLevel;
use Psr\Log\LoggerInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Consolidation\Log\ConsoleLogLevel;
@@ -32,6 +31,10 @@ class ResultPrinter implements LoggerAwareInterface, ProgressIndicatorAwareInter
*/
public function printResult(Result $result)
{
$task = $result->getTask();
if ($task instanceof VerbosityThresholdInterface && !$task->verbosityMeetsThreshold()) {
return;
}
if (!$result->wasSuccessful()) {
return $this->printError($result);
} else {
@@ -3,8 +3,6 @@ namespace Robo\Log;
use Robo\Common\TimeKeeper;
use Consolidation\Log\LogOutputStyler;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\OutputStyle;
/**
* Robo Log Styler.
@@ -1,17 +1,10 @@
<?php
namespace Robo\Log;
use Robo\Result;
use Robo\TaskInfo;
use Robo\Contract\PrintedInterface;
use Robo\Contract\LogResultInterface;
use Consolidation\Log\ConsoleLogLevel;
use Consolidation\Log\Logger;
use Psr\Log\LogLevel;
use Psr\Log\AbstractLogger;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Logger\ConsoleLogger;
/**
* Robo's default logger
+26 -2
View File
@@ -2,8 +2,8 @@
namespace Robo;
use Robo\Contract\TaskInterface;
use Robo\Contract\LogResultInterface;
use Robo\Exception\TaskExitException;
use Robo\State\Data;
class Result extends ResultData
{
@@ -34,6 +34,30 @@ class Result extends ResultData
}
}
/**
* Tasks should always return a Result. However, they are also
* allowed to return NULL or an array to indicate success.
*/
public static function ensureResult($task, $result)
{
if ($result instanceof Result) {
return $result;
}
if (!isset($result)) {
return static::success($task);
}
if ($result instanceof Data) {
return static::success($task, $result->getMessage(), $result->getData());
}
if ($result instanceof ResultData) {
return new Result($task, $result->getExitCode(), $result->getMessage(), $result->getData());
}
if (is_array($result)) {
return static::success($task, '', $result);
}
throw new \Exception(sprintf('Task %s returned a %s instead of a \Robo\Result.', get_class($task), get_class($result)));
}
protected function printResult()
{
// For historic reasons, the Result constructor is responsible
@@ -45,7 +69,7 @@ class Result extends ResultData
$resultPrinter = Robo::resultPrinter();
if ($resultPrinter) {
if ($resultPrinter->printResult($this)) {
$this->data['already-printed'] = true;
$this->alreadyPrinted();
}
}
}
+15 -57
View File
@@ -1,22 +1,17 @@
<?php
namespace Robo;
use Robo\Contract\LogResultInterface;
use Consolidation\AnnotatedCommand\ExitCodeInterface;
use Consolidation\AnnotatedCommand\OutputDataInterface;
use Robo\State\Data;
class ResultData extends \ArrayObject implements ExitCodeInterface, OutputDataInterface
class ResultData extends Data implements ExitCodeInterface, OutputDataInterface
{
/**
* @var int
*/
protected $exitCode;
/**
* @var string
*/
protected $message;
const EXITCODE_OK = 0;
const EXITCODE_ERROR = 1;
/** Symfony Console handles these conditions; Robo returns the status
@@ -38,9 +33,7 @@ class ResultData extends \ArrayObject implements ExitCodeInterface, OutputDataIn
public function __construct($exitCode = self::EXITCODE_OK, $message = '', $data = [])
{
$this->exitCode = $exitCode;
$this->message = $message;
parent::__construct($data);
parent::__construct($message, $data);
}
/**
@@ -65,14 +58,6 @@ class ResultData extends \ArrayObject implements ExitCodeInterface, OutputDataIn
return new ResultData(self::EXITCODE_USER_CANCEL, $message, $data);
}
/**
* @return array
*/
public function getData()
{
return $this->getArrayCopy();
}
/**
* @return int
*/
@@ -86,17 +71,25 @@ class ResultData extends \ArrayObject implements ExitCodeInterface, OutputDataIn
*/
public function getOutputData()
{
if (!empty($this->message) && !isset($this['already-printed'])) {
if (!empty($this->message) && !isset($this['already-printed']) && isset($this['provide-outputdata'])) {
return $this->message;
}
}
/**
* @return string
* Indicate that the message in this data has already been displayed.
*/
public function getMessage()
public function alreadyPrinted()
{
return $this->message;
$this['already-printed'] = true;
}
/**
* Opt-in to providing the result message as the output data
*/
public function provideOutputdata()
{
$this['provide-outputdata'] = true;
}
/**
@@ -114,39 +107,4 @@ class ResultData extends \ArrayObject implements ExitCodeInterface, OutputDataIn
{
return $this->exitCode == self::EXITCODE_USER_CANCEL;
}
/**
* Merge another result into this result. Data already
* existing in this result takes precedence over the
* data in the Result being merged.
*
* @param \Robo\ResultData $result
*
* @return $this
*/
public function merge(ResultData $result)
{
$mergedData = $this->getArrayCopy() + $result->getArrayCopy();
$this->exchangeArray($mergedData);
return $this;
}
/**
* @return bool
*/
public function hasExecutionTime()
{
return isset($this['time']);
}
/**
* @return null|float
*/
public function getExecutionTime()
{
if (!$this->hasExecutionTime()) {
return null;
}
return $this['time'];
}
}
+70 -13
View File
@@ -3,8 +3,13 @@ namespace Robo;
use League\Container\Container;
use League\Container\ContainerInterface;
use Robo\Common\ProcessExecutor;
use Consolidation\Config\ConfigInterface;
use Consolidation\Config\Loader\ConfigProcessor;
use Consolidation\Config\Loader\YamlConfigLoader;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Application as SymfonyApplication;
use Symfony\Component\Process\Process;
/**
* Manages the container reference and other static data. Favor
@@ -14,7 +19,7 @@ use Symfony\Component\Console\Application as SymfonyApplication;
class Robo
{
const APPLICATION_NAME = 'Robo';
const VERSION = '1.0.4';
const VERSION = '1.2.1';
/**
* The currently active container object, or NULL if not initialized yet.
@@ -34,9 +39,10 @@ class Robo
*
* @return int
*/
public static function run($argv, $commandClasses, $appName = null, $appVersion = null, $output = null)
public static function run($argv, $commandClasses, $appName = null, $appVersion = null, $output = null, $repository = null)
{
$runner = new \Robo\Runner($commandClasses);
$runner->setSelfUpdateRepository($repository);
$statusCode = $runner->execute($argv, $appName, $appVersion, $output);
return $statusCode;
}
@@ -85,6 +91,33 @@ class Robo
return static::$container !== null;
}
/**
* Create a config object and load it from the provided paths.
*/
public static function createConfiguration($paths)
{
$config = new \Robo\Config\Config();
static::loadConfiguration($paths, $config);
return $config;
}
/**
* Use a simple config loader to load configuration values from specified paths
*/
public static function loadConfiguration($paths, $config = null)
{
if ($config == null) {
$config = static::config();
}
$loader = new YamlConfigLoader();
$processor = new ConfigProcessor();
$processor->add($config->export());
foreach ($paths as $path) {
$processor->extend($loader->load($path));
}
$config->import($processor->export());
}
/**
* Create a container and initiailze it. If you wish to *change*
* anything defined in the container, then you should call
@@ -93,7 +126,7 @@ class Robo
* @param null|\Symfony\Component\Console\Input\InputInterface $input
* @param null|\Symfony\Component\Console\Output\OutputInterface $output
* @param null|\Robo\Application $app
* @param null|\Robo\Config $config
* @param null|ConfigInterface $config
*
* @return \League\Container\Container|\League\Container\ContainerInterface
*/
@@ -109,7 +142,7 @@ class Robo
}
if (!$config) {
$config = new Config();
$config = new \Robo\Config\Config();
}
// Set up our dependency injection container.
@@ -139,11 +172,11 @@ class Robo
*
* @param \League\Container\ContainerInterface $container
* @param \Symfony\Component\Console\Application $app
* @param \Robo\Config $config
* @param ConfigInterface $config
* @param null|\Symfony\Component\Console\Input\InputInterface $input
* @param null|\Symfony\Component\Console\Output\OutputInterface $output
*/
public static function configureContainer(ContainerInterface $container, SymfonyApplication $app, Config $config, $input = null, $output = null)
public static function configureContainer(ContainerInterface $container, SymfonyApplication $app, ConfigInterface $config, $input = null, $output = null)
{
// Self-referential container refernce for the inflector
$container->add('container', $container);
@@ -156,12 +189,14 @@ class Robo
if (!$output) {
$output = new \Symfony\Component\Console\Output\ConsoleOutput();
}
$config->setDecorated($output->isDecorated());
$config->set(Config::DECORATED, $output->isDecorated());
$config->set(Config::INTERACTIVE, $input->isInteractive());
$container->share('application', $app);
$container->share('config', $config);
$container->share('input', $input);
$container->share('output', $output);
$container->share('outputAdapter', \Robo\Common\OutputAdapter::class);
// Register logging and related services.
$container->share('logStyler', \Robo\Log\RoboLogStyle::class);
@@ -175,22 +210,30 @@ class Robo
->withArgument('output');
$container->share('resultPrinter', \Robo\Log\ResultPrinter::class);
$container->add('simulator', \Robo\Task\Simulator::class);
$container->share('globalOptionsEventListener', \Robo\GlobalOptionsEventListener::class);
$container->share('globalOptionsEventListener', \Robo\GlobalOptionsEventListener::class)
->withMethodCall('setApplication', ['application']);
$container->share('injectConfigEventListener', \Consolidation\Config\Inject\ConfigForCommand::class)
->withArgument('config')
->withMethodCall('setApplication', ['application']);
$container->share('collectionProcessHook', \Robo\Collection\CollectionProcessHook::class);
$container->share('hookManager', \Consolidation\AnnotatedCommand\Hooks\HookManager::class)
->withMethodCall('addResultProcessor', ['collectionProcessHook', '*']);
$container->share('alterOptionsCommandEvent', \Consolidation\AnnotatedCommand\Options\AlterOptionsCommandEvent::class)
->withArgument('application');
$container->share('hookManager', \Consolidation\AnnotatedCommand\Hooks\HookManager::class)
->withMethodCall('addCommandEvent', ['alterOptionsCommandEvent'])
->withMethodCall('addCommandEvent', ['injectConfigEventListener'])
->withMethodCall('addCommandEvent', ['globalOptionsEventListener'])
->withMethodCall('addResultProcessor', ['collectionProcessHook', '*']);
$container->share('eventDispatcher', \Symfony\Component\EventDispatcher\EventDispatcher::class)
->withMethodCall('addSubscriber', ['globalOptionsEventListener'])
->withMethodCall('addSubscriber', ['alterOptionsCommandEvent'])
->withMethodCall('addSubscriber', ['hookManager']);
$container->share('formatterManager', \Consolidation\OutputFormatters\FormatterManager::class)
->withMethodCall('addDefaultFormatters', [])
->withMethodCall('addDefaultSimplifiers', []);
$container->share('prepareTerminalWidthOption', \Consolidation\AnnotatedCommand\Options\PrepareTerminalWidthOption::class)
->withMethodCall('setApplication', ['application']);
$container->share('commandProcessor', \Consolidation\AnnotatedCommand\CommandProcessor::class)
->withArgument('hookManager')
->withMethodCall('setFormatterManager', ['formatterManager'])
->withMethodCall('addPrepareFormatter', ['prepareTerminalWidthOption'])
->withMethodCall(
'setDisplayErrorFunction',
[
@@ -202,8 +245,13 @@ class Robo
);
$container->share('commandFactory', \Consolidation\AnnotatedCommand\AnnotatedCommandFactory::class)
->withMethodCall('setCommandProcessor', ['commandProcessor']);
// Deprecated: favor using collection builders to direct use of collections.
$container->add('collection', \Robo\Collection\Collection::class);
// Deprecated: use CollectionBuilder::create() instead -- or, better
// yet, BuilderAwareInterface::collectionBuilder() if available.
$container->add('collectionBuilder', \Robo\Collection\CollectionBuilder::class);
static::addInflectors($container);
// Make sure the application is appropriately initialized.
@@ -246,6 +294,10 @@ class Robo
->invokeMethod('setOutput', ['output']);
$container->inflector(\Robo\Contract\ProgressIndicatorAwareInterface::class)
->invokeMethod('setProgressIndicator', ['progressIndicator']);
$container->inflector(\Consolidation\AnnotatedCommand\Events\CustomEventAwareInterface::class)
->invokeMethod('setHookManager', ['hookManager']);
$container->inflector(\Robo\Contract\VerbosityThresholdInterface::class)
->invokeMethod('setOutputAdapter', ['outputAdapter']);
}
/**
@@ -292,7 +344,7 @@ class Robo
}
/**
* @return \Robo\Config
* @return ConfigInterface
*/
public static function config()
{
@@ -334,4 +386,9 @@ class Robo
{
return static::service('input');
}
public static function process(Process $process)
{
return ProcessExecutor::create(static::getContainer(), $process);
}
}
+59 -13
View File
@@ -1,15 +1,12 @@
<?php
namespace Robo;
use League\Container\Container;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\StringInput;
use Consolidation\AnnotatedCommand\PassThroughArgsInput;
use Robo\Contract\BuilderAwareInterface;
use Robo\Collection\CollectionBuilder;
use Robo\Common\IO;
use Robo\Exception\TaskExitException;
use League\Container\ContainerInterface;
use League\Container\ContainerAwareInterface;
use League\Container\ContainerAwareTrait;
@@ -36,6 +33,16 @@ class Runner implements ContainerAwareInterface
*/
protected $dir;
/**
* @var string[]
*/
protected $errorConditions = [];
/**
* @var string GitHub Repo for SelfUpdate
*/
protected $selfUpdateRepository = null;
/**
* Class Constructor
*
@@ -50,6 +57,11 @@ class Runner implements ContainerAwareInterface
$this->dir = getcwd();
}
protected function errorCondtion($msg, $errorType)
{
$this->errorConditions[$msg] = $errorType;
}
/**
* @param \Symfony\Component\Console\Output\OutputInterface $output
*
@@ -70,7 +82,7 @@ class Runner implements ContainerAwareInterface
return true;
}
if (!file_exists($this->dir)) {
$output->writeln("<error>Path `{$this->dir}` is invalid; please provide a valid absolute path to the Robofile to load.</error>");
$this->errorCondtion("Path `{$this->dir}` is invalid; please provide a valid absolute path to the Robofile to load.", 'red');
return false;
}
@@ -79,13 +91,13 @@ class Runner implements ContainerAwareInterface
$roboFilePath = $realDir . DIRECTORY_SEPARATOR . $this->roboFile;
if (!file_exists($roboFilePath)) {
$requestedRoboFilePath = $this->dir . DIRECTORY_SEPARATOR . $this->roboFile;
$output->writeln("<error>Requested RoboFile `$requestedRoboFilePath` is invalid, please provide valid absolute path to load Robofile</error>");
$this->errorCondtion("Requested RoboFile `$requestedRoboFilePath` is invalid, please provide valid absolute path to load Robofile.", 'red');
return false;
}
require_once $roboFilePath;
if (!class_exists($this->roboClass)) {
$output->writeln("<error>Class ".$this->roboClass." was not loaded</error>");
$this->errorCondtion("Class {$this->roboClass} was not loaded.", 'red');
return false;
}
return true;
@@ -136,7 +148,10 @@ class Runner implements ContainerAwareInterface
// If we were not provided a container, then create one
if (!$this->getContainer()) {
$container = Robo::createDefaultContainer($input, $output, $app);
$userConfig = 'robo.yml';
$roboAppConfig = dirname(__DIR__) . '/robo.yml';
$config = Robo::createConfiguration([$userConfig, $roboAppConfig]);
$container = Robo::createDefaultContainer($input, $output, $app, $config);
$this->setContainer($container);
// Automatically register a shutdown function and
// an error handler when we provide the container.
@@ -146,10 +161,13 @@ class Runner implements ContainerAwareInterface
if (!$app) {
$app = Robo::application();
}
if (!isset($commandFiles)) {
$this->yell("Robo is not initialized here. Please run `robo init` to create a new RoboFile", 40, 'yellow');
$app->addInitRoboFileCommand($this->roboFile, $this->roboClass);
$commandFiles = [];
if ($app instanceof \Robo\Application) {
$app->addSelfUpdateCommand($this->getSelfUpdateRepository());
if (!isset($commandFiles)) {
$this->errorCondtion("Robo is not initialized here. Please run `robo init` to create a new RoboFile.", 'yellow');
$app->addInitRoboFileCommand($this->roboFile, $this->roboClass);
$commandFiles = [];
}
}
$this->registerCommandClasses($app, $commandFiles);
@@ -158,6 +176,15 @@ class Runner implements ContainerAwareInterface
} catch (TaskExitException $e) {
$statusCode = $e->getCode() ?: 1;
}
// If there were any error conditions in bootstrapping Robo,
// print them only if the requested command did not complete
// successfully.
if ($statusCode) {
foreach ($this->errorConditions as $msg => $color) {
$this->yell($msg, 40, $color);
}
}
return $statusCode;
}
@@ -222,6 +249,9 @@ class Runner implements ContainerAwareInterface
// If the command class is already an instantiated object, then
// just use it exactly as it was provided to us.
if (is_string($commandClass)) {
if (!class_exists($commandClass)) {
return;
}
$reflectionClass = new \ReflectionClass($commandClass);
if ($reflectionClass->isAbstract()) {
return;
@@ -235,7 +265,7 @@ class Runner implements ContainerAwareInterface
// ensure that it has a builder. Every command class needs
// its own collection builder, as they have references to each other.
if ($commandClass instanceof BuilderAwareInterface) {
$builder = $container->get('collectionBuilder', [$commandClass]);
$builder = CollectionBuilder::create($container, $commandClass);
$commandClass->setBuilder($builder);
}
if ($commandClass instanceof ContainerAwareInterface) {
@@ -416,4 +446,20 @@ class Runner implements ContainerAwareInterface
}
return false;
}
/**
* @return string
*/
public function getSelfUpdateRepository()
{
return $this->selfUpdateRepository;
}
/**
* @param string $selfUpdateRepository
*/
public function setSelfUpdateRepository($selfUpdateRepository)
{
$this->selfUpdateRepository = $selfUpdateRepository;
}
}
@@ -0,0 +1,152 @@
<?php
namespace Robo;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem as sfFilesystem;
/**
* Update the robo.phar from the latest github release
*
* @author Alexander Menk <alex.menk@gmail.com>
*/
class SelfUpdateCommand extends Command
{
const SELF_UPDATE_COMMAND_NAME = 'self:update';
protected $gitHubRepository;
protected $currentVersion;
protected $applicationName;
public function __construct($applicationName = null, $currentVersion = null, $gitHubRepository = null)
{
parent::__construct(self::SELF_UPDATE_COMMAND_NAME);
$this->applicationName = $applicationName;
$this->currentVersion = $currentVersion;
$this->gitHubRepository = $gitHubRepository;
}
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setAliases(array('update'))
->setDescription('Updates the robo.phar to the latest version.')
->setHelp(
<<<EOT
The <info>self-update</info> command checks github for newer
versions of robo and if found, installs the latest.
EOT
);
}
protected function getLatestReleaseFromGithub()
{
$opts = [
'http' => [
'method' => 'GET',
'header' => [
'User-Agent: ' . $this->applicationName . ' (' . $this->gitHubRepository . ')' . ' Self-Update (PHP)'
]
]
];
$context = stream_context_create($opts);
$releases = file_get_contents('https://api.github.com/repos/' . $this->gitHubRepository . '/releases', false, $context);
$releases = json_decode($releases);
if (! isset($releases[0])) {
throw new \Exception('API error - no release found at GitHub repository ' . $this->gitHubRepository);
}
$version = $releases[0]->tag_name;
$url = $releases[0]->assets[0]->browser_download_url;
return [ $version, $url ];
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
if (empty(\Phar::running())) {
throw new \Exception(self::SELF_UPDATE_COMMAND_NAME . ' only works when running the phar version of ' . $this->applicationName . '.');
}
$localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
$programName = basename($localFilename);
$tempFilename = dirname($localFilename) . '/' . basename($localFilename, '.phar') . '-temp.phar';
// check for permissions in local filesystem before start connection process
if (! is_writable($tempDirectory = dirname($tempFilename))) {
throw new \Exception(
$programName . ' update failed: the "' . $tempDirectory .
'" directory used to download the temp file could not be written'
);
}
if (! is_writable($localFilename)) {
throw new \Exception(
$programName . ' update failed: the "' . $localFilename . '" file could not be written (execute with sudo)'
);
}
list( $latest, $downloadUrl ) = $this->getLatestReleaseFromGithub();
if ($this->currentVersion == $latest) {
$output->writeln('No update available');
return;
}
$fs = new sfFilesystem();
$output->writeln('Downloading ' . $this->applicationName . ' (' . $this->gitHubRepository . ') ' . $latest);
$fs->copy($downloadUrl, $tempFilename);
$output->writeln('Download finished');
try {
\error_reporting(E_ALL); // supress notices
@chmod($tempFilename, 0777 & ~umask());
// test the phar validity
$phar = new \Phar($tempFilename);
// free the variable to unlock the file
unset($phar);
@rename($tempFilename, $localFilename);
$output->writeln('<info>Successfully updated ' . $programName . '</info>');
$this->_exit();
} catch (\Exception $e) {
@unlink($tempFilename);
if (! $e instanceof \UnexpectedValueException && ! $e instanceof \PharException) {
throw $e;
}
$output->writeln('<error>The download is corrupted (' . $e->getMessage() . ').</error>');
$output->writeln('<error>Please re-run the self-update command to try again.</error>');
}
}
/**
* Stop execution
*
* This is a workaround to prevent warning of dispatcher after replacing
* the phar file.
*
* @return void
*/
protected function _exit()
{
exit;
}
}
@@ -0,0 +1,12 @@
<?php
namespace Robo\State;
use Robo\State\Data;
interface Consumer
{
/**
* @return Data
*/
public function receiveState(Data $state);
}
@@ -0,0 +1,148 @@
<?php
namespace Robo\State;
/**
* A State\Data object contains a "message" (the primary result) and a
* data array (the persistent state). The message is transient, and does
* not move into the persistent state unless explicitly copied there.
*/
class Data extends \ArrayObject
{
/**
* @var string
*/
protected $message;
/**
* @param string $message
* @param array $data
*/
public function __construct($message = '', $data = [])
{
$this->message = $message;
parent::__construct($data);
}
/**
* @return array
*/
public function getData()
{
return $this->getArrayCopy();
}
/**
* @return string
*/
public function getMessage()
{
return $this->message;
}
/**
* @param string message
*/
public function setMessage($message)
{
$this->message = $message;
}
/**
* Merge another result into this result. Data already
* existing in this result takes precedence over the
* data in the Result being merged.
*
* @param \Robo\ResultData $result
*
* @return $this
*/
public function merge(Data $result)
{
$mergedData = $this->getArrayCopy() + $result->getArrayCopy();
$this->exchangeArray($mergedData);
return $this;
}
/**
* Update the current data with the data provided in the parameter.
* Provided data takes precedence.
*
* @param \ArrayObject $update
*
* @return $this
*/
public function update(\ArrayObject $update)
{
$iterator = $update->getIterator();
while ($iterator->valid()) {
$this[$iterator->key()] = $iterator->current();
$iterator->next();
}
return $this;
}
/**
* Merge another result into this result. Data already
* existing in this result takes precedence over the
* data in the Result being merged.
*
* $data['message'] is handled specially, and is appended
* to $this->message if set.
*
* @param array $data
*
* @return array
*/
public function mergeData(array $data)
{
$mergedData = $this->getArrayCopy() + $data;
$this->exchangeArray($mergedData);
return $mergedData;
}
/**
* @return bool
*/
public function hasExecutionTime()
{
return isset($this['time']);
}
/**
* @return null|float
*/
public function getExecutionTime()
{
if (!$this->hasExecutionTime()) {
return null;
}
return $this['time'];
}
/**
* Accumulate execution time
*/
public function accumulateExecutionTime($duration)
{
// Convert data arrays to scalar
if (is_array($duration)) {
$duration = isset($duration['time']) ? $duration['time'] : 0;
}
$this['time'] = $this->getExecutionTime() + $duration;
return $this->getExecutionTime();
}
/**
* Accumulate the message.
*/
public function accumulateMessage($message)
{
if (!empty($this->message)) {
$this->message .= "\n";
}
$this->message .= $message;
return $this->getMessage();
}
}
@@ -0,0 +1,30 @@
<?php
namespace Robo\State;
use Robo\State\Data;
interface StateAwareInterface
{
/**
* @return Data
*/
public function getState();
/**
* @param Data state
*/
public function setState(Data $state);
/**
* @param $key
* @param value
*/
public function setStateValue($key, $value);
/**
* @param Data update state takes precedence over current state.
*/
public function updateState(Data $update);
public function resetState();
}
@@ -0,0 +1,49 @@
<?php
namespace Robo\State;
use Robo\State\Data;
trait StateAwareTrait
{
protected $state;
/**
* {@inheritdoc}
*/
public function getState()
{
return $this->state;
}
/**
* {@inheritdoc}
*/
public function setState(Data $state)
{
$this->state = $state;
}
/**
* {@inheritdoc}
*/
public function setStateValue($key, $value)
{
$this->state[$key] = $value;
}
/**
* {@inheritdoc}
*/
public function updateState(Data $update)
{
$this->state->update($update);
}
/**
* {@inheritdoc}
*/
public function resetState()
{
$this->state = new Data();
}
}
@@ -12,7 +12,8 @@ use Traversable;
* ``` php
* <?php
* // ApiGen Command
* $this->taskApiGen('./apigen.neon')
* $this->taskApiGen('./vendor/apigen/apigen.phar')
* ->config('./apigen.neon')
* ->templateConfig('vendor/apigen/apigen/templates/bootstrap/config.neon')
* ->wipeout(true)
* ->run();
@@ -30,6 +31,7 @@ class ApiGen extends BaseTask implements CommandInterface
* @var string
*/
protected $command;
protected $operation = 'generate';
/**
* @param null|string $pathToApiGen
@@ -39,6 +41,15 @@ class ApiGen extends BaseTask implements CommandInterface
public function __construct($pathToApiGen = null)
{
$this->command = $pathToApiGen;
$command_parts = [];
preg_match('/((?:.+)?apigen(?:\.phar)?) ?( \w+)? ?(.+)?/', $this->command, $command_parts);
if (count($command_parts) === 3) {
list(, $this->command, $this->operation) = $command_parts;
}
if (count($command_parts) === 4) {
list(, $this->command, $this->operation, $arg) = $command_parts;
$this->arg($arg);
}
if (!$this->command) {
$this->command = $this->findExecutablePhar('apigen');
}
@@ -47,6 +58,31 @@ class ApiGen extends BaseTask implements CommandInterface
}
}
/**
* Pass methods parameters as arguments to executable. Argument values
* are automatically escaped.
*
* @param string|string[] $args
*
* @return $this
*/
public function args($args)
{
if (!is_array($args)) {
$args = func_get_args();
}
$args = array_map(function ($arg) {
if (preg_match('/^\w+$/', trim($arg)) === 1) {
$this->operation = $arg;
return null;
}
return $arg;
}, $args);
$args = array_filter($args);
$this->arguments .= ' ' . implode(' ', array_map('static::escape', $args));
return $this;
}
/**
* @param array|Traversable|string $arg a single object or something traversable
*
@@ -468,7 +504,7 @@ class ApiGen extends BaseTask implements CommandInterface
*/
public function getCommand()
{
return $this->command . $this->arguments;
return "$this->command $this->operation$this->arguments";
}
/**
@@ -31,8 +31,6 @@ use Robo\Common\BuilderAwareTrait;
* ->run();
* ?>
* ```
*
* @method to(string) location to store extracted files
*/
class Extract extends BaseTask implements BuilderAwareInterface
{
+30 -129
View File
@@ -1,6 +1,7 @@
<?php
namespace Robo\Task\Base;
use Robo\Common\ExecTrait;
use Robo\Contract\CommandInterface;
use Robo\Contract\PrintedInterface;
use Robo\Contract\SimulatedInterface;
@@ -41,31 +42,6 @@ class Exec extends BaseTask implements CommandInterface, PrintedInterface, Simul
*/
protected $command;
/**
* @var bool
*/
protected $background = false;
/**
* @var null|int
*/
protected $timeout = null;
/**
* @var null|int
*/
protected $idleTimeout = null;
/**
* @var null|array
*/
protected $env = null;
/**
* @var Process
*/
protected $process;
/**
* @param string|\Robo\Contract\CommandInterface $command
*/
@@ -75,11 +51,11 @@ class Exec extends BaseTask implements CommandInterface, PrintedInterface, Simul
}
/**
* {@inheritdoc}
*
*/
public function getCommand()
public function __destruct()
{
return trim($this->command . $this->arguments);
$this->stop();
}
/**
@@ -87,116 +63,26 @@ class Exec extends BaseTask implements CommandInterface, PrintedInterface, Simul
*
* @return $this
*/
public function background()
public function background($arg = true)
{
self::$instances[] = $this;
$this->background = true;
$this->background = $arg;
return $this;
}
/**
* Stop command if it runs longer then $timeout in seconds
*
* @param int $timeout
*
* @return $this
*/
public function timeout($timeout)
{
$this->timeout = $timeout;
return $this;
}
/**
* Stops command if it does not output something for a while
*
* @param int $timeout
*
* @return $this
*/
public function idleTimeout($timeout)
{
$this->idleTimeout = $timeout;
return $this;
}
/**
* Sets the environment variables for the command
*
* @param array $env
*
* @return $this
*/
public function env(array $env)
{
$this->env = $env;
return $this;
}
public function __destruct()
{
$this->stop();
}
protected function stop()
{
if ($this->background && $this->process->isRunning()) {
$this->process->stop();
$this->printTaskInfo("Stopped {command}", ['command' => $this->getCommand()]);
}
}
/**
* @param array $context
*/
protected function printAction($context = [])
{
$command = $this->getCommand();
$dir = $this->workingDirectory ? " in {dir}" : "";
$this->printTaskInfo("Running {command}$dir", ['command' => $command, 'dir' => $this->workingDirectory] + $context);
}
/**
* {@inheritdoc}
*/
public function run()
protected function getCommandDescription()
{
$this->printAction();
$this->process = new Process($this->getCommand());
$this->process->setTimeout($this->timeout);
$this->process->setIdleTimeout($this->idleTimeout);
$this->process->setWorkingDirectory($this->workingDirectory);
if (isset($this->env)) {
$this->process->setEnv($this->env);
}
if (!$this->background and !$this->isPrinted) {
$this->startTimer();
$this->process->run();
$this->stopTimer();
return new Result($this, $this->process->getExitCode(), $this->process->getOutput(), ['time' => $this->getExecutionTime()]);
}
if (!$this->background and $this->isPrinted) {
$this->startTimer();
$this->process->run(
function ($type, $buffer) {
$progressWasVisible = $this->hideTaskProgress();
print($buffer);
$this->showTaskProgress($progressWasVisible);
}
);
$this->stopTimer();
return new Result($this, $this->process->getExitCode(), $this->process->getOutput(), ['time' => $this->getExecutionTime()]);
}
try {
$this->process->start();
} catch (\Exception $e) {
return Result::fromException($this, $e);
}
return Result::success($this);
return $this->getCommand();
}
/**
* {@inheritdoc}
*/
public function getCommand()
{
return trim($this->command . $this->arguments);
}
/**
@@ -215,6 +101,21 @@ class Exec extends BaseTask implements CommandInterface, PrintedInterface, Simul
}
}
}
/**
* {@inheritdoc}
*/
public function run()
{
// TODO: Symfony 4 requires that we supply the working directory.
$result_data = $this->execute(new Process($this->getCommand(), getcwd()));
return new Result(
$this,
$result_data->getExitCode(),
$result_data->getMessage(),
$result_data->getData()
);
}
}
if (function_exists('pcntl_signal')) {
@@ -2,7 +2,6 @@
namespace Robo\Task\Base;
use Robo\Task\CommandStack;
use Robo\Task\Base;
/**
* Execute commands one by one in stack.
@@ -18,8 +17,6 @@ use Robo\Task\Base;
*
* ?>
* ```
*
* @method $this stopOnFail()
*/
class ExecStack extends CommandStack
{
@@ -1,8 +1,6 @@
<?php
namespace Robo\Task\Base;
use Robo\Contract\ProgressIndicatorAwareInterface;
use Robo\Common\ProgressIndicatorAwareTrait;
use Robo\Contract\CommandInterface;
use Robo\Contract\PrintedInterface;
use Robo\Result;
@@ -22,10 +20,6 @@ use Symfony\Component\Process\Process;
* ->run();
* ?>
* ```
*
*
* @method \Robo\Task\Base\ParallelExec timeout(int $timeout) stops process if it runs longer then `$timeout` (seconds)
* @method \Robo\Task\Base\ParallelExec idleTimeout(int $timeout) stops process if it does not output for time longer then `$timeout` (seconds)
*/
class ParallelExec extends BaseTask implements CommandInterface, PrintedInterface
{
@@ -46,6 +40,11 @@ class ParallelExec extends BaseTask implements CommandInterface, PrintedInterfac
*/
protected $idleTimeout = null;
/**
* @var null|int
*/
protected $waitInterval = 0;
/**
* @var bool
*/
@@ -77,11 +76,14 @@ class ParallelExec extends BaseTask implements CommandInterface, PrintedInterfac
*/
public function process($command)
{
$this->processes[] = new Process($this->receiveCommand($command));
// TODO: Symfony 4 requires that we supply the working directory.
$this->processes[] = new Process($this->receiveCommand($command), getcwd());
return $this;
}
/**
* Stops process if it runs longer then `$timeout` (seconds).
*
* @param int $timeout
*
* @return $this
@@ -93,6 +95,8 @@ class ParallelExec extends BaseTask implements CommandInterface, PrintedInterfac
}
/**
* Stops process if it does not output for time longer then `$timeout` (seconds).
*
* @param int $idleTimeout
*
* @return $this
@@ -103,6 +107,20 @@ class ParallelExec extends BaseTask implements CommandInterface, PrintedInterfac
return $this;
}
/**
* Parallel processing will wait `$waitInterval` seconds after launching each process and before
* the next one.
*
* @param int $waitInterval
*
* @return $this
*/
public function waitInterval($waitInterval)
{
$this->waitInterval = $waitInterval;
return $this;
}
/**
* {@inheritdoc}
*/
@@ -124,16 +142,20 @@ class ParallelExec extends BaseTask implements CommandInterface, PrintedInterfac
*/
public function run()
{
foreach ($this->processes as $process) {
$process->setIdleTimeout($this->idleTimeout);
$process->setTimeout($this->timeout);
$process->start();
$this->printTaskInfo($process->getCommandLine());
}
$this->startProgressIndicator();
$running = $this->processes;
$running = [];
$queue = $this->processes;
$nextTime = time();
while (true) {
if (($nextTime <= time()) && !empty($queue)) {
$process = array_shift($queue);
$process->setIdleTimeout($this->idleTimeout);
$process->setTimeout($this->timeout);
$process->start();
$this->printTaskInfo($process->getCommandLine());
$running[] = $process;
$nextTime = time() + $this->waitInterval;
}
foreach ($running as $k => $process) {
try {
$process->checkTimeout();
@@ -152,7 +174,7 @@ class ParallelExec extends BaseTask implements CommandInterface, PrintedInterfac
unset($running[$k]);
}
}
if (empty($running)) {
if (empty($running) && empty($queue)) {
break;
}
usleep(1000);
@@ -6,7 +6,6 @@ use Robo\Result;
use Robo\Task\BaseTask;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
/**
* Executes Symfony Command
@@ -13,6 +13,9 @@ trait loadTasks
return $this->task(Exec::class, $command);
}
/**
* @return ExecStack
*/
protected function taskExecStack()
{
return $this->task(ExecStack::class);
@@ -7,16 +7,38 @@ use Robo\Contract\InflectionInterface;
use Robo\Common\TaskIO;
use Robo\Contract\TaskInterface;
use Robo\Contract\ProgressIndicatorAwareInterface;
use Robo\Contract\VerbosityThresholdInterface;
use Robo\Common\ProgressIndicatorAwareTrait;
use Robo\Contract\ConfigAwareInterface;
use Psr\Log\LoggerAwareInterface;
use Robo\Contract\OutputAwareInterface;
abstract class BaseTask implements TaskInterface, LoggerAwareInterface, ConfigAwareInterface, ProgressIndicatorAwareInterface, InflectionInterface
abstract class BaseTask implements TaskInterface, LoggerAwareInterface, VerbosityThresholdInterface, ConfigAwareInterface, ProgressIndicatorAwareInterface, InflectionInterface
{
use TaskIO; // uses LoggerAwareTrait and ConfigAwareTrait
use TaskIO; // uses LoggerAwareTrait, VerbosityThresholdTrait and ConfigAwareTrait
use ProgressIndicatorAwareTrait;
use InflectionTrait;
/**
* ConfigAwareInterface uses this to decide where configuration
* items come from. Default is this prefix + class name + key,
* e.g. `task.Remote.Ssh.remoteDir`.
*/
protected static function configPrefix()
{
return 'task.';
}
/**
* ConfigAwareInterface uses this to decide where configuration
* items come from. Default is this prefix + class name + key,
* e.g. `task.Ssh.remoteDir`.
*/
protected static function configPostfix()
{
return '.settings';
}
/**
* {@inheritdoc}
*/
@@ -31,5 +53,8 @@ abstract class BaseTask implements TaskInterface, LoggerAwareInterface, ConfigAw
if ($child instanceof ConfigAwareInterface && $this->getConfig()) {
$child->setConfig($this->getConfig());
}
if ($child instanceof VerbosityThresholdInterface && $this->outputAdapter()) {
$child->setOutputAdapter($this->outputAdapter());
}
}
}
@@ -1,7 +1,6 @@
<?php
namespace Robo\Task\Bower;
use Robo\Task\Bower;
use Robo\Contract\CommandInterface;
/**
@@ -1,8 +1,6 @@
<?php
namespace Robo\Task\Bower;
use Robo\Task\Bower;
/**
* Bower Update
*
@@ -6,7 +6,6 @@ use Robo\Common\ExecCommand;
use Robo\Contract\PrintedInterface;
use Robo\Result;
use Robo\Contract\CommandInterface;
use Robo\Common\DynamicParams;
use Robo\Exception\TaskException;
abstract class CommandStack extends BaseTask implements CommandInterface, PrintedInterface
@@ -60,8 +59,8 @@ abstract class CommandStack extends BaseTask implements CommandInterface, Printe
$command = implode(' ', array_filter($command));
}
$command = $this->executable . ' ' . $this->stripExecutableFromCommand($command);
array_push($this->exec, trim($command));
$command = $this->executable . ' ' . $this->stripExecutableFromCommand($command);
$this->exec[] = trim($command);
return $this;
}
@@ -105,19 +104,31 @@ abstract class CommandStack extends BaseTask implements CommandInterface, Printe
if (empty($this->exec)) {
throw new TaskException($this, 'You must add at least one command');
}
if (!$this->stopOnFail) {
// If 'stopOnFail' is not set, or if there is only one command to run,
// then execute the single command to run.
if (!$this->stopOnFail || (count($this->exec) == 1)) {
$this->printTaskInfo('{command}', ['command' => $this->getCommand()]);
return $this->executeCommand($this->getCommand());
}
// When executing multiple commands in 'stopOnFail' mode, run them
// one at a time so that the result will have the exact command
// that failed available to the caller. This is at the expense of
// losing the output from all successful commands.
$data = [];
$message = '';
$result = null;
foreach ($this->exec as $command) {
$this->printTaskInfo("Executing {command}", ['command' => $command]);
$result = $this->executeCommand($command);
$result->accumulateExecutionTime($data);
$message = $result->accumulateMessage($message);
$data = $result->mergeData($data);
if (!$result->wasSuccessful()) {
return $result;
}
}
return Result::success($this);
return $result;
}
}
@@ -1,11 +1,11 @@
<?php
namespace Robo\Task\Composer;
use Robo\Robo;
use Robo\Task\BaseTask;
use Robo\Contract\CommandInterface;
use Robo\Exception\TaskException;
use Robo\Task\BaseTask;
abstract class Base extends BaseTask
abstract class Base extends BaseTask implements CommandInterface
{
use \Robo\Common\ExecOneCommand;
@@ -14,6 +14,11 @@ abstract class Base extends BaseTask
*/
protected $command = '';
/**
* @var boolena
*/
protected $built = false;
/**
* @var string
*/
@@ -24,11 +29,6 @@ abstract class Base extends BaseTask
*/
protected $dev;
/**
* @var string
*/
protected $optimizeAutoloader;
/**
* @var string
*/
@@ -37,7 +37,7 @@ abstract class Base extends BaseTask
/**
* @var string
*/
protected $dir;
protected $nointeraction;
/**
* Action to use
@@ -46,72 +46,6 @@ abstract class Base extends BaseTask
*/
protected $action = '';
/**
* adds `prefer-dist` option to composer
*
* @return $this
*/
public function preferDist()
{
$this->prefer = '--prefer-dist';
return $this;
}
/**
* adds `prefer-source` option to composer
*
* @return $this
*/
public function preferSource()
{
$this->prefer = '--prefer-source';
return $this;
}
/**
* adds `no-dev` option to composer
*
* @return $this
*/
public function noDev()
{
$this->dev = '--no-dev';
return $this;
}
/**
* adds `no-ansi` option to composer
*
* @return $this
*/
public function noAnsi()
{
$this->ansi = '--no-ansi';
return $this;
}
/**
* adds `ansi` option to composer
*
* @return $this
*/
public function ansi()
{
$this->ansi = '--ansi';
return $this;
}
/**
* adds `optimize-autoloader` option to composer
*
* @return $this
*/
public function optimizeAutoloader()
{
$this->optimizeAutoloader = '--optimize-autoloader';
return $this;
}
/**
* @param null|string $pathToComposer
*
@@ -128,18 +62,187 @@ abstract class Base extends BaseTask
}
}
/**
* adds `prefer-dist` option to composer
*
* @return $this
*/
public function preferDist($preferDist = true)
{
if (!$preferDist) {
return $this->preferSource();
}
$this->prefer = '--prefer-dist';
return $this;
}
/**
* adds `prefer-source` option to composer
*
* @return $this
*/
public function preferSource()
{
$this->prefer = '--prefer-source';
return $this;
}
/**
* adds `dev` option to composer
*
* @return $this
*/
public function dev($dev = true)
{
if (!$dev) {
return $this->noDev();
}
$this->dev = '--dev';
return $this;
}
/**
* adds `no-dev` option to composer
*
* @return $this
*/
public function noDev()
{
$this->dev = '--no-dev';
return $this;
}
/**
* adds `ansi` option to composer
*
* @return $this
*/
public function ansi($ansi = true)
{
if (!$ansi) {
return $this->noAnsi();
}
$this->ansi = '--ansi';
return $this;
}
/**
* adds `no-ansi` option to composer
*
* @return $this
*/
public function noAnsi()
{
$this->ansi = '--no-ansi';
return $this;
}
public function interaction($interaction = true)
{
if (!$interaction) {
return $this->noInteraction();
}
return $this;
}
/**
* adds `no-interaction` option to composer
*
* @return $this
*/
public function noInteraction()
{
$this->nointeraction = '--no-interaction';
return $this;
}
/**
* adds `optimize-autoloader` option to composer
*
* @return $this
*/
public function optimizeAutoloader($optimize = true)
{
if ($optimize) {
$this->option('--optimize-autoloader');
}
return $this;
}
/**
* adds `ignore-platform-reqs` option to composer
*
* @return $this
*/
public function ignorePlatformRequirements($ignore = true)
{
$this->option('--ignore-platform-reqs');
return $this;
}
/**
* disable plugins
*
* @return $this
*/
public function disablePlugins($disable = true)
{
if ($disable) {
$this->option('--no-plugins');
}
return $this;
}
/**
* skip scripts
*
* @return $this
*/
public function noScripts($disable = true)
{
if ($disable) {
$this->option('--no-scripts');
}
return $this;
}
/**
* adds `--working-dir $dir` option to composer
*
* @return $this
*/
public function workingDir($dir)
{
$this->option("--working-dir", $dir);
return $this;
}
/**
* Copy class fields into command options as directed.
*/
public function buildCommand()
{
if (!isset($this->ansi) && $this->getConfig()->get(\Robo\Config\Config::DECORATED)) {
$this->ansi();
}
if (!isset($this->nointeraction) && !$this->getConfig()->get(\Robo\Config\Config::INTERACTIVE)) {
$this->noInteraction();
}
$this->option($this->prefer)
->option($this->dev)
->option($this->nointeraction)
->option($this->ansi);
}
/**
* {@inheritdoc}
*/
public function getCommand()
{
if (!isset($this->ansi) && $this->getConfig()->isDecorated()) {
$this->ansi();
if (!$this->built) {
$this->buildCommand();
$this->built = true;
}
$this->option($this->prefer)
->option($this->dev)
->option($this->optimizeAutoloader)
->option($this->ansi);
return "{$this->command} {$this->action}{$this->arguments}";
}
}
@@ -0,0 +1,93 @@
<?php
namespace Robo\Task\Composer;
/**
* Composer Config
*
* ``` php
* <?php
* // simple execution
* $this->taskComposerConfig()->set('bin-dir', 'bin/')->run();
* ?>
* ```
*/
class Config extends Base
{
/**
* {@inheritdoc}
*/
protected $action = 'config';
/**
* Set a configuration value
* @return $this
*/
public function set($key, $value)
{
$this->arg($key);
$this->arg($value);
return $this;
}
/**
* Operate on the global repository
* @return $this
*/
public function useGlobal($useGlobal = true)
{
if ($useGlobal) {
$this->option('global');
}
return $this;
}
/**
* @return $this
*/
public function repository($id, $uri, $repoType = 'vcs')
{
$this->arg("repositories.$id");
$this->arg($repoType);
$this->arg($uri);
return $this;
}
/**
* @return $this
*/
public function removeRepository($id)
{
$this->option('unset', "repositories.$id");
return $this;
}
/**
* @return $this
*/
public function disableRepository($id)
{
$this->arg("repositories.$id");
$this->arg('false');
return $this;
}
/**
* @return $this
*/
public function enableRepository($id)
{
$this->arg("repositories.$id");
$this->arg('true');
return $this;
}
/**
* {@inheritdoc}
*/
public function run()
{
$command = $this->getCommand();
$this->printTaskInfo('Configuring composer.json: {command}', ['command' => $command]);
return $this->executeCommand($command);
}
}
@@ -0,0 +1,112 @@
<?php
namespace Robo\Task\Composer;
/**
* Composer CreateProject
*
* ``` php
* <?php
* // simple execution
* $this->taskComposerCreateProject()->source('foo/bar')->target('myBar')->run();
* ?>
* ```
*/
class CreateProject extends Base
{
/**
* {@inheritdoc}
*/
protected $action = 'create-project';
protected $source;
protected $target = '';
protected $version = '';
/**
* @return $this
*/
public function source($source)
{
$this->source = $source;
return $this;
}
/**
* @return $this
*/
public function target($target)
{
$this->target = $target;
return $this;
}
/**
* @return $this
*/
public function version($version)
{
$this->version = $version;
return $this;
}
public function keepVcs($keep = true)
{
if ($keep) {
$this->option('--keep-vcs');
}
return $this;
}
public function noInstall($noInstall = true)
{
if ($noInstall) {
$this->option('--no-install');
}
return $this;
}
/**
* @return $this
*/
public function repository($repository)
{
if (!empty($repository)) {
$this->option('repository', $repository);
}
return $this;
}
/**
* @return $this
*/
public function stability($stability)
{
if (!empty($stability)) {
$this->option('stability', $stability);
}
return $this;
}
public function buildCommand()
{
$this->arg($this->source);
if (!empty($this->target)) {
$this->arg($this->target);
}
if (!empty($this->version)) {
$this->arg($this->version);
}
return parent::buildCommand();
}
/**
* {@inheritdoc}
*/
public function run()
{
$command = $this->getCommand();
$this->printTaskInfo('Creating project: {command}', ['command' => $command]);
return $this->executeCommand($command);
}
}
@@ -42,21 +42,14 @@ class DumpAutoload extends Base
/**
* @return $this
*/
public function optimize()
public function optimize($optimize = true)
{
$this->optimize = "--optimize";
if ($optimize) {
$this->option("--optimize");
}
return $this;
}
/**
* {@inheritdoc}
*/
public function getCommand()
{
$this->option($this->optimize);
return parent::getCommand();
}
/**
* {@inheritdoc}
*/
@@ -0,0 +1,115 @@
<?php
namespace Robo\Task\Composer;
/**
* Composer Init
*
* ``` php
* <?php
* // simple execution
* $this->taskComposerInit()->run();
* ?>
* ```
*/
class Init extends Base
{
/**
* {@inheritdoc}
*/
protected $action = 'init';
/**
* @return $this
*/
public function projectName($projectName)
{
$this->option('name', $projectName);
return $this;
}
/**
* @return $this
*/
public function description($description)
{
$this->option('description', $description);
return $this;
}
/**
* @return $this
*/
public function author($author)
{
$this->option('author', $author);
return $this;
}
/**
* @return $this
*/
public function projectType($type)
{
$this->option('type', $type);
return $this;
}
/**
* @return $this
*/
public function homepage($homepage)
{
$this->option('homepage', $homepage);
return $this;
}
/**
* 'require' is a keyword, so it cannot be a method name.
* @return $this
*/
public function dependency($project, $version = null)
{
if (isset($version)) {
$project .= ":$version";
}
$this->option('require', $project);
return $this;
}
/**
* @return $this
*/
public function stability($stability)
{
$this->option('stability', $stability);
return $this;
}
/**
* @return $this
*/
public function license($license)
{
$this->option('license', $license);
return $this;
}
/**
* @return $this
*/
public function repository($repository)
{
$this->option('repository', $repository);
return $this;
}
/**
* {@inheritdoc}
*/
public function run()
{
$command = $this->getCommand();
$this->printTaskInfo('Creating composer.json: {command}', ['command' => $command]);
return $this->executeCommand($command);
}
}
@@ -2,12 +2,12 @@
namespace Robo\Task\Composer;
/**
* Composer Validate
* Composer Remove
*
* ``` php
* <?php
* // simple execution
* $this->taskComposerValidate()->run();
* $this->taskComposerRemove()->run();
* ?>
* ```
*/
@@ -21,45 +21,55 @@ class Remove extends Base
/**
* @return $this
*/
public function dev()
public function dev($dev = true)
{
$this->option('--dev');
if ($dev) {
$this->option('--dev');
}
return $this;
}
/**
* @return $this
*/
public function noProgress()
public function noProgress($noProgress = true)
{
$this->option('--no-progress');
if ($noProgress) {
$this->option('--no-progress');
}
return $this;
}
/**
* @return $this
*/
public function noUpdate()
public function noUpdate($noUpdate = true)
{
$this->option('--no-update');
if ($noUpdate) {
$this->option('--no-update');
}
return $this;
}
/**
* @return $this
*/
public function updateNoDev()
public function updateNoDev($updateNoDev = true)
{
$this->option('--update-no-dev');
if ($updateNoDev) {
$this->option('--update-no-dev');
}
return $this;
}
/**
* @return $this
*/
public function noUpdateWithDependencies()
public function noUpdateWithDependencies($updateWithDependencies = true)
{
$this->option('--no-update-with-dependencies');
if ($updateWithDependencies) {
$this->option('--no-update-with-dependencies');
}
return $this;
}

Some files were not shown because too many files have changed in this diff Show More