Skip to content
Open
Changes from 1 commit
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
Original file line number Diff line number Diff line change
@@ -1,14 +1,69 @@
<div>
<p>
Takes a map from branch names to closures and an optional argument <code>failFast</code>
which will terminate all branches upon a failure in any other branch:
</p>
<pre>
parallel firstBranch: {
// do something
}, secondBranch: {
// do something else
},
failFast: true|false
</pre>
Takes a map from branch names to closures and runs the closures in
parallel. The optional <code>failFast</code> option terminates
all jobs as soon as any job fails.
</p>
<p>
Synopsis:
<pre>
parallel firstBranch: {
// do something
}, secondBranch: {
// do something else
},
failFast: true|false
</pre>
</p>
<p>
A branch succeeds if it returns normally. The actual value
returned does not matter. A branch is considered to have failed if its
closure exits by throwing an exception.
</p>
<p>
If any branch fails, the <code>parallel</code> step will throw that
exception once all branches have finished executing or been terminated,
depending on <code>failFast</code> mode. Any non-identical exceptions
thrown by other branches are added to the first exception as having been
"suppressed by" the first exception; see
<code>Throwable.addSuppresssed(...)</code>.
</p>
<p>
Note that a <code>hudson.AbortException</code>, as thrown by failing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AbortException is not necessarily thrown by failing steps.

I think mentioning AbortException is misleading since it doesn't have any special interaction with the parallel step.

steps, will stop and fail the branch but not produce a stack trace.
This produces a:
<pre>
[branchname] Failed in branch branchname
</pre>
message without any details.
</p>
<p>
To report results from branches to the outer script, use the closure's
access to the outer scope's variables. For example, to run a shell script
in parallel on each of a list of arguments and produce a map of argument
to script stdout:
<pre>
def args = ['foo', 'bar', 'baz']
def results = [:]
// Produce a map of args to closures that use each arg
branches = args.collectEntries { arg -&gt; [(arg): {
def out = sh script: "./my-script.sh '${arg}'", returnStdout: true
results &lt;&lt; [(arg): out]
return null
} ] }
branches &lt;&lt; [failFast: true]
parallel branches
</pre>
If all the scripts returned 0, the 'results' array contains a map of arg
to shell stdout string. Otherwise <code>parallel</code> terminates the
other branches and re-throws the <code>hudson.AbortException</code> from
the first failed script.
</p>
<p>
Note: Branch closures should just return <code>null</code>. The return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move this paragraph above the preceding one, and reword it slightly. Since we don't want anyone to rely on the current behavior I think it is best not to mention what that behavior is directly.

      Note: Branch closures should just return <code>null</code>. The return
      value of the <code>parallel</code> step should be ignored; See JENKINS-26033.

value of the <code>parallel</code> step should be ignored.
<code>parallel</code> does capture the return values of closures and
return a map of branch name to return value. But relying on this is not
recommended and the behaviour is subject to change; See JENKINS-26033.
</p>
</div>