Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added relentless task. #746

Merged
merged 6 commits into from
Jan 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions classes/phing/tasks/defaults.properties
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ diagnostics=phing.tasks.system.DiagnosticsTask
pathconvert=phing.tasks.system.PathConvert
defaultexcludes=phing.tasks.system.DefaultExcludes
dependset=phing.tasks.system.DependSet
relentless=phing.tasks.system.Relentless

; "Core" contributed tasks
; -- i.e. no taskdef needed.
Expand Down
104 changes: 104 additions & 0 deletions classes/phing/tasks/system/Relentless.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php
/**
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information please see
* <http://phing.info>.
*/

require_once 'phing/BuildException.php';
require_once 'phing/Project.php';
require_once 'phing/Task.php';
require_once 'phing/TaskContainer.php';

/**
* Relentless is an Ant task that will relentlessly execute other tasks,
* ignoring any failures until all tasks have completed. If any of the executed
* tasks fail, then Relentless will fail; otherwise it will succeed.
*
* @author Siad Ardroumli <siad.ardroumli@gmail.com>
* @package phing.tasks.system
*/
class Relentless extends Task implements TaskContainer
{
/**
* We keep the list of tasks we will execute here.
*/
private $taskList = [];

/**
* Flag indicating how much output to generate.
*/
private $terse = false;

/**
* This method will be called when it is time to execute the task.
* @throws \BuildException
*/
public function main()
{
$failCount = 0;
$taskNo = 0;
if (count($this->taskList) === 0) {
throw new BuildException('No tasks specified for <relentless>.');
}
$this->log('Relentlessly executing: ' . $this->getDescription());
foreach ($this->taskList as $t) {
$taskNo++;
$desc = $t->getDescription();
if ($desc === null) {
$desc = 'task ' . $taskNo;
}
if (!$this->terse) {
$this->log('Executing: ' . $desc);
}
try {
$t->perform();
} catch (BuildException $x) {
$this->log('Task ' . $desc . ' failed: ' . $x->getMessage());
$failCount++;
}
}
if ($failCount > 0) {
throw new BuildException(
'Relentless execution: ' . $failCount . ' of ' . count($this->taskList) . ' tasks failed.');
}

$this->log('All tasks completed successfully.');
}

/**
* Ant will call this to inform us of nested tasks.
*/
public function addTask(Task $task)
{
$this->taskList[] = $task;
}

/**
* Set this to true to reduce the amount of output generated.
*/
public function setTerse($terse)
{
$this->terse = $terse;
}

/**
* Retrieve the terse property, indicating how much output we will generate.
*/
public function isTerse()
{
return $this->terse;
}
}
114 changes: 114 additions & 0 deletions docs/docbook5/en/source/appendixes/coretasks.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3571,6 +3571,120 @@ verbose="true" failonerror="false" /></programlisting>
&lt;echo>Resolved [absolute] path: ${absolute_path}&lt;/echo></programlisting>
</sect2>
</sect1>
<sect1 role="taskdef" xml:id="Relentless">
<title>Relentless</title>
<para> The &lt;relentless> task will execute all of the nested tasks, regardless of whether one or more of
the nested tasks fails.</para>
<para>When &lt;relentless> has completed executing the nested tasks, it will either
<itemizedlist>
<listitem>
<para>fail, if any one or more of the nested tasks failed; or</para>
</listitem>
<listitem>
<para>succeed, if all of the nested tasks succeeded.</para>
</listitem>
</itemizedlist>
An appropriate message will be written to the log.</para>
<para>Tasks are executed in the order that they appear within the &lt;relentless> task. It is up to the user
to ensure that relentless execution of the nested tasks is safe.</para>
<table>
<title>Attributes</title>
<tgroup cols="5">
<colspec colname="name" colnum="1" colwidth="1.5*"/>
<colspec colname="type" colnum="2" colwidth="0.8*"/>
<colspec colname="description" colnum="3" colwidth="3.5*"/>
<colspec colname="default" colnum="4" colwidth="0.8*"/>
<colspec colname="required" colnum="5" colwidth="1.2*"/>
<thead>
<row>
<entry>Name</entry>
<entry>Type</entry>
<entry>Description</entry>
<entry>Default</entry>
<entry>Required</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>description</literal></entry>
<entry><literal role="type">String</literal></entry>
<entry>A string that will be included in the log output. This can be useful for helping to
identify sections of large phing builds.
</entry>
<entry>N/A</entry>
<entry>No</entry>
</row>
<row>
<entry><literal>terse</literal></entry>
<entry><literal role="type">Boolean</literal></entry>
<entry>Setting this to true will eliminate some of the progress output generated by &lt;relentless>.
This can reduce clutter in some cases.
</entry>
<entry>false</entry>
<entry>No</entry>
</row>
</tbody>
</tgroup>
</table>
<para> The only nested element supported by &lt;relentless> is a list of tasks to be executed.
At least one task must be specified.</para>
<para>It is important to note that &lt;relentless> only proceeds relentlessly from one task to the next - it does
not apply recursively to any tasks that might be invoked by these nested tasks.
If a nested task invokes some other list of tasks (perhaps by &lt;phingcall> for example), and one of those
other tasks fails, then the nested task will stop at that point.</para>
<sect2>
<title>Example</title>
<para>A relentless task to print out the first five canonical variable names:</para>
<programlisting lang="xml">&lt;relentless description="The first five canonical variable names.">
&lt;echo>foo&lt;/echo>
&lt;echo>bar&lt;/echo>
&lt;echo>baz&lt;/echo>
&lt;echo>bat&lt;/echo>
&lt;echo>blah&lt;/echo>
&lt;/relentless></programlisting>
<para>which should produce output looking more or less like</para>
<programlisting>[relentless] Relentlessly executing: The first five canonical variable names.
[relentless] Executing: task 1
[echo] foo
[relentless] Executing: task 2
[echo] bar
[relentless] Executing: task 3
[echo] baz
[relentless] Executing: task 4
[echo] bat
[relentless] Executing: task 5
[echo] blah
[relentless] All tasks completed successfully.</programlisting>
<para>If you change the first line to set the <literal>terse</literal> parameter,</para>
<programlisting lang="xml">&lt;relentless terse="true" description="The first five canonical variable names."/></programlisting>
<para>the output will look more like this:</para>
<programlisting>[relentless] Relentlessly executing: The first five canonical variable names.
[echo] foo
[echo] bar
[echo] baz
[echo] bat
[echo] blah
[relentless] All tasks completed successfully.</programlisting>
<para>If we change the third task to deliberately fail</para>
<programlisting language="xml">&lt;relentless terse="true" description="The first five canonical variable names.">
&lt;echo>foo&lt;/echo>
&lt;echo>bar&lt;/echo>
&lt;fail>baz&lt;/fail>
&lt;echo>bat&lt;/echo>
&lt;echo>blah&lt;/echo>
&lt;/relentless></programlisting>
<para>then the output should look something like this.</para>
<programlisting>[relentless] Relentlessly executing: The first five canonical variable names.
[echo] foo
[echo] bar
[relentless] Task task 3 failed: baz
[echo] bat
[echo] blah

BUILD FAILED
/path/build.xml:1177: Relentless execution: 1 of 5 tasks failed.</programlisting>
</sect2>
</sect1>
<sect1 role="taskdef" xml:id="RetryTask">
<title>Retry</title>
<para> Retry is a container which executes a single nested task until
Expand Down
16 changes: 16 additions & 0 deletions etc/phing-grammar.rng
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@
<ref name="record"/>
<ref name="reflexive"/>
<ref name="resolvepath"/>
<ref name="relentless"/>
<ref name="retry"/>
<ref name="sleep"/>
<ref name="switch"/>
Expand Down Expand Up @@ -1972,6 +1973,21 @@
</element>
</define>

<define name="relentless">
<element name="relentless">
<interleave>
<optional>
<attribute name="description"/>
</optional>
<optional>
<attribute name="terse" a:defaultValue="false"/>
</optional>
<ref name="coretasks"/>
<ref name="optionaltasks"/>
</interleave>
</element>
</define>

<define name="retry">
<element name="retry">
<interleave>
Expand Down
40 changes: 40 additions & 0 deletions test/classes/phing/tasks/system/RelentlessTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

require_once 'phing/BuildFileTest.php';

/**
* Tests the Relentless Task
*
* @author Siad Ardroumli <siad.ardroumli@gmail.com>
* @package phing.tasks.system
*/
class RelentlessTest extends BuildFileTest
{
public function setUp()
{
$this->configureProject(
PHING_TEST_BASE . '/etc/tasks/system/RelentlessTest.xml'
);
}

public function testRelentless()
{
$this->expectLogContaining(__FUNCTION__, 'Executing: task 3');
}

public function testTerse()
{
$this->executeTarget(__FUNCTION__);
$this->assertNotInLogs('Executing: task 3');
}

/**
* @expectedException BuildException
* @expectedExceptionMessage Relentless execution: 1 of 5 tasks failed.
*/
public function testFailure()
{
$this->executeTarget(__FUNCTION__);
$this->assertInLogs('Task task 3 failed: baz');
}
}
30 changes: 30 additions & 0 deletions test/etc/tasks/system/RelentlessTest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="RetryTest" default="testRelentless">
<target name="testRelentless">
<relentless description="The first five canonical variable names.">
<echo>foo</echo>
<echo>bar</echo>
<echo>baz</echo>
<echo>bat</echo>
<echo>blah</echo>
</relentless>
</target>
<target name="testTerse">
<relentless terse="true" description="The first five canonical variable names.">
<echo>foo</echo>
<echo>bar</echo>
<echo>baz</echo>
<echo>bat</echo>
<echo>blah</echo>
</relentless>
</target>
<target name="testFailure">
<relentless terse="true" description="The first five canonical variable names.">
<echo>foo</echo>
<echo>bar</echo>
<fail>baz</fail>
<echo>bat</echo>
<echo>blah</echo>
</relentless>
</target>
</project>