Tasks Overview

Available:

Bamboo 3.1 and later

In this overview of the Task APIs we highlight how Tasks are executed, defined, configured and how to use optional APIs such as Executables, Test Parsing, Logging, Capabilities, Requirements and design considerations for running Tasks on Remote Agents

Introduction

In 3.1 we have introduced Tasks. Each Job can now have multiple Tasks.

  • Tasks have replaced the Builder.
  • Tasks are run in order on a single Agent.
  • If a task fails following tasks will not be executed (Final Tasks are an exception)
  • Final tasks will always be executed regardless of the state of previous tasks (even if you stop the build manually) so can be used to for e.g. clean up tasks
  • Tasks are executed after the Pre Build Plugins and Before the Post Build Plugins.
  • What you choose to do in a task is entirely up to you!

Special Considerations When Developing Tasks

  • People can add multiple of your TaskType plugin to the one Job.
  • People can put your task in the final tasks section so you task needs to be defensive to the situation where previous setup may not exist.
  • It must be a plugins 1 plugin because it needs to be capable of running on remote agents

The TaskType Module Definition

Tasks are designed to be easily extensible. To get a basic Task up and running, you simply need to implement a single Java interface. However, there are optional extension points that allow you to add a configuration screen and other powerful features to your Task.

Minimal TaskType Plugin

<taskType key="task.awesome" name="Awesome Task" class="com.atlassian.bamboo.plugins.MyAwesomeTaskType" />

Advanced TaskType Plugin

<taskType key="task.builder.maven" name="Maven 1.x" class="com.atlassian.bamboo.plugins.maven.task.Maven1BuildTask">
        <description>Execute one or more Maven 1 goals as part of your build</description>
        <category name="builder"/>
        <category name="test"/>
        <executable key="maven" nameKey="builder.maven.executableName" pathHelpKey="builder.maven.helpPath"/>
        <capabilityDefaultsHelper class="com.atlassian.bamboo.plugins.maven.task.Maven1CapabilityDefaultsHelper"/>
        <configuration class="com.atlassian.bamboo.plugins.maven.task.configuration.Maven1BuildTaskConfigurator" />
        <resource type="freemarker" name="edit" location="com/atlassian/bamboo/plugins/maven/task/configuration/maven1BuildTaskEdit.ftl"/>
        <resource type="freemarker" name="view" location="com/atlassian/bamboo/plugins/maven/task/configuration/maven1BuildTaskView.ftl"/>
</taskType>

Selecting the TaskType

When configuring the Job, the user is offered a TaskType picker. Your TaskType module will appear in this list.

The TaskType module name and description will be visible to assist the user in selecting the desired TaskType. The description is optional however we highly recommend including one.

You can also optionally provide categories to group the TaskType in the picker

Available Categories

builder

test

deployment

When a user selects a TaskType, they will have the opportunity to configure it (if required) and then it will be saved as a TaskDefinition. TaskDefinitions are stored against the BuildDefinition of a Job.

The Data Model.

TaskDefinition objects are what we store in the BuildDefinition and will be used whenever you are configuring a Task. When you are executing the TaskType we have provided lots of other useful information, so you will be dealing instead with the TaskContext. Both the TaskDefinition and TaskContext extend the TaskIdentifier interface which may be used when only the core information is required.

Data Model

Executing The Task

The minimal requirement for a TaskType Module is a class implementing the TaskType interface. TaskTypes must be capable of BAMBOO:Running on Remote Agents

The TaskType interface has only 1 method.

/**
     * Execute the task
     *
     * @param taskContext representing any context and configuration of the Task
     * @return a {@link TaskResult} representing the status of the task execution
     * @throws TaskException
     */
    @NotNull
    TaskResult execute(@NotNull final TaskContext taskContext) throws TaskException;

TaskContext

Iin which you can find any build related information and any Task configuration information.

TaskResult

The TaskResult allows you to store any resultData for later use and the TaskState to indicate whether the task Failed or Passed. Bamboo will use this TaskState to determine whether to continue executing other tasks.  The TaskResultBuilder should be used to create these TaskResults.

TaskException

If something goes wrong you can also throw the TaskException.  However consider wisely when you should throw this exception, rather than just Failing the task.

Executing an external process

Seeing as though executing an external process is something we think a lot of you are going to want to do, we have created some utilities to make this easier.

The ProcessService (which can be injected) can be used to run an External Process.  It takes in the ExternalProcessBuilder which you can populate with everything required to run your command.  For example:

ExternalProcess externalProcess = processService.executeProcess(
                    taskContext,
                    new ExternalProcessBuilder()
                            .workingDirectory(config.getWorkingDirectory())
                            .env(config.getExtraEnvironment())
                            .command(config.getCommandline())
            );

Reading Logs

We've added infrastructure for analysing build logs on the fly. For documentation, see LogInterceptor.  You can add these interceptors to the LogInterceptorStack in the BuildLogger. For examples of usage, see com.atlassian.bamboo.build.logger.interceptors.StringMatchingInterceptor and com.atlassian.bamboo.plugins.ant.task.AntBuildTask .

Parsing Tests

If you want your Task to parse test output you can inject the TestCollationService.  It will parse the build files for tests based on the passed in filePattern.  By default it will parse JUnit formatted tests, however the service allows you to provide your own TestReportCollector to support other formats

Configuring The Task

If your TaskType requires configuring you can provide a configuration class. The configuration class must implement TaskConfigurator. You will also need to provide freemarker files for both edit and view of the configuration.

Utilities

AbstractTaskConfigurator
This abstract class provides empty implementations of the methods on the TaskConfigurator interface. You can extend this to simplify tasks with more basic configuration requirements

TaskConfiguratorHelper
The TaskConfiguratorHelper can be injected into your TaskConfigurator class. It contains many utility method to handle basic configuration requirements, especially moving configuration from the TaskDefinition to the FreemarkerContext and from the ActionParameters back to the TaskDefinition. e.g.

private static final List<String> FIELDS_TO_COPY = ImmutableList.of("goal", "environment");

    public Map<String, String> generateTaskConfigMap(@NotNull final ActionParametersMap params, @Nullable final TaskDefinition previousTaskDefinition)
    {
        final HashMap<String,String> config = Maps.newHashMap();
        taskConfiguratorHelper.populateTaskConfigMapWithActionParameters(config, params, FIELDS_TO_COPY);
        return config;
    }

    public void populateContextForEdit(@NotNull Map<String, Object> context, @NotNull TaskDefinition taskDefinition)
    {
        taskConfiguratorHelper.populateContextWithConfiguration(context, taskDefinition, FIELDS_TO_COPY);
    }

The TaskConfiguratorHelper also contains utilities to assist some generic fields such as selecting a JDK, and setting the location of where to find Test Results. However these methods rely on specifically named parameters, so if you want to make use of this functionality you should use TaskConfigConstants for your field naming.

Tasks, Requirements And Capabilities

Executable Capabilities

In your plugin you may wish to mandate that a certain executable is avaible for you to use, for example the Maven task will require the Maven exe to be on the agent to be able to run the Maven build commands.    For your executable to be available in the list of possible Executables that can be added as Capabilities, you need to define the executable with your TaskType.

Attribute

Required

Default

Details

key

(tick)

-

The unique key of this executable.  This is prefixed with "system.builder" and the postfixed with the Executable Label provided by the user to create the key of the Capability 

nameKey

(error)

Name of the Plugin Module

i18n key for the display Name of the Executable Type.  Will be visible when configuring Capabilities.

pathHelpKey

(error)

-

i18n key for the help text to be displayed under the Path field for the capability.  Be clear what you expect the user to enter, e.g. the directory containing the exe, or the path of the actual exe

primaryCapabilityProvider

(error)

True

This field is only applicable if there is more than one TaskType module defining the same executable type.  If so, set to false.

Providing Default Capabilities

If you would also like to detect executables on the Agent or Server to be added as Capabilities automatically you can use a CapabilityDefaultsHelper. 

The class provided must implement the CapabilityDefaultsHelper interface.  There are also some Abstract classes which you might wish to make use of including AbstractFileCapabilityDefaultsHelper and AbstractHomeDirectoryCapabilityDefaultsHelper. Remember that CapabilityDefaultHelpers must be able to BAMBOO:run on Remote agents

Task Requirements

Requirements are now tied much more closely to Tasks. It is the Task's responsibility to provide the requirements which it needs to run. If you want your Tasks to have requirements on Capabilities for either your own plugin-defined Executables or any other Capabilities (such as JDK's), your TaskConfigurator will also need to implement TaskRequirementSupport.  This class simply provides a method to return a list of Requirements of the Task which will then get managed internally by Bamboo.

Executables in our UI
Often you want your users to pick which executable or JDK they want to use for this Task.
You can use our UIConfigBean to populate the list of available Executables of Jdk's. You can then use the value submitted by the user to create the correct Capability key to use when creating your requirement.

[@ww.select cssClass="builderSelectWidget" labelKey='executable.type' name='selectedExecutable'
            list=uiConfigBean.getExecutableLabels('myExecutable') /]

[@ww.select cssClass="jdkSelectWidget"
            labelKey='builder.common.jdk' name='buildJdk'
            list=uiConfigBean.jdkLabels required='true' /]

Creating the Requirements
So how do create the correct Requirements? Create a new RequirementImpl

public RequirementImpl(String key, boolean regexMatch, String match)

Argument

Description

Example

key

The key of the Capability to require.

system.builder.ant.Ant+1.8

regexMatch

True if it requires a regex match, false if it requires an exact match. Set to true, if you only need the capability to exist.

true

match

The string to match to. Set to ".*" if you only need the capability to exist

.*

For Example:

final String executableLabel = taskDefinition.getConfiguration().get("selectedExecutable");
   Requirement req = new RequirementImpl("system.builder.myExecutable" + "." + executableLabel, true, ".*")

   final String jdkLabel = taskDefinition.getConfiguration().get("buildJdk");
   Requirement req = new RequirementImpl("system.builder.jdk" + "." + jdkLabel, true, ".*")

Utilities
The TaskConfiguratorHelper provides some methods to assist in creating requirements including:

void addJdkRequirement(@NotNull Set<Requirement> requirements, @NotNull TaskDefinition taskDefinition, @NotNull String cfgJdkLabel);

    void addSystemRequirementFromConfiguration(@NotNull Set<Requirement> requirements, @NotNull TaskDefinition taskDefinition,
                                               @NotNull String cfgKey, @NotNull String requirementPrefix);

Running On Remote Agents

Throughout this overview there have been several mentions of things that must be able to run on Remote Agents, so, what does this mean?
On a Remote Agent your code does not have access to the database or the server's file system. So this means things like extended configuration information or previous results are not available to you. It also means that a lot of Managers which you can usually use are off-limits. Apart from things passed into your classes, the following is the list of Services you are allowed to use on remote agents at the time of writing this. These can be injected into your plugin classes.

  • TestCollationService
  • ProcessService
  • RemotableEventManager
  • ArtifactManager
  • CustomVariableContext
  • ErrorUpdateHandler
  • AgentContext
  • CapabilityConfigurationManager
  • CapabilityContext
  • CapabilityDefaultsHelper
  • SystemInfo

Updating your Builder configuration

If you have migrated a Builder to Tasks, you can use the LegacyBuilderToTaskConverter Plugin Module to update the data.

Was this page helpful?

Have a question about this article?

See questions about this article

Powered by Confluence and Scroll Viewport