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
In 3.1 we have introduced Tasks. Each Job can now have multiple Tasks.
Special Considerations When Developing Tasks
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
1 2<taskType key="task.awesome" name="Awesome Task" class="com.atlassian.bamboo.plugins.MyAwesomeTaskType" />
Advanced TaskType Plugin
1 2<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>
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.
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.
## Executing The TaskThe 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.
1 2/** * 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;
Iin which you can find any build related information and any Task configuration information. | |
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. | |
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. |
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:
1 2ExternalProcess externalProcess = processService.executeProcess( taskContext, new ExternalProcessBuilder() .workingDirectory(config.getWorkingDirectory()) .env(config.getExtraEnvironment()) .command(config.getCommandline()) );
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 .
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
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.
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.
1 2private 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.
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 | Details |
---|---|
key | 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 Required: yes Default: - |
nameKey | i18n key for the display Name of the Executable Type. Will be visible when configuring Capabilities. Required: no Default: Name of the Plugin Module |
pathHelpKey | 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 Required: no Default: - |
primaryCapabilityProvider | This field is only applicable if there is more than one TaskType module defining the same executable type. If so, set to false. Required: no Default: True |
Providing Default Capabilities
If you would also like to detect executables on the Agent or Bamboo application 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
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.
1 2[@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
1 2public RequirementImpl(String key, boolean regexMatch, String match)
Argument | Description |
---|---|
key | The key of the Capability to require. Example: 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. Example: true |
match | The string to match to. Set to ".*" if you only need the capability to exist Example: .* |
For Example:
1 2final 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:
1 2void 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);
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.
If you have migrated a Builder to Tasks, you can use the LegacyBuilderToTaskConverter Plugin Module to update the data.
Rate this page: