blob: 4644649e1bf9b9dd0e0cdacdca81edf767eeb1ab [file] [log] [blame] [view]
@PLUGIN@
========
The @PLUGIN@ plugin provides a mechanism to manage tasks which need to be
performed on changes. The @PLUGIN@ plugin creates a common place where tasks
can be defined, along with a common way to expose and query this information.
Task definition includes defining which changes each task applies to, and how
to determine the status for each task. Tasks are organized hierarchically.
This hierarchy is considered for task applicability and status.
An important use case of the @PLUGIN@ plugin is to have a common place for CI
systems to define which changes they will operate on, and when they will do
so. This makes it possible for independent and unrelated teams to setup
entirely independent CI systems which operate on different sets of changes,
all while exposing these applicability relations to Gerrit and other teams and
users. This also makes it possible for work for a single change to be split
across multiple cooperating CI systems so that assessments can be staged and
gated upon various other tasks or assessments first completing (or passing).
Exposing task applicability information helps users determine via Gerrit which,
if any, system will "take care" of their changes. Users can thus figure this
out without having the knowledge of "how to", or even "the ability to" query
the configuration of external CI systems. This also makes it possible for
conflicting systems to more easily be detected in cases when more than one
system is mistakenly configured to be responsible for the same changes.
Exposing task hierarchy information via Gerrit helps users understand the
workflow that is expected of their changes. It helps them visualize task
requirements and which tasks are expected to be completed before another task
will even be attempted. It helps them understand how their changes are, or
are not progressing through the outlined stages.
Exposing task status information helps users and CI systems determine via
Gerrit when it is appropriate for them to take action (to perform their task).
It helps them identify blocking tasks. It helps them figure out what they
can do, or perhaps who they need to talk to to ensure that their changes do
make progress through all their hoops.
Task definitions can be split up across multiple files/refs, and even
across multiple projects. This splitting of task definitions allows the
control of task definitions to be delegated to different entities. By
aligning ref boundaries with controlling entities, the standard gerrit ref
ACL mechanisms may be used to control who can define tasks on which changes.
Task Status
-----------
Task status is used to indicate either the readiness of a task for execution
if it has not yet completed execution, or the outcome of the task if it has
completed execution. Tasks generally progress from `WAITING` to `READY` as
their subtasks complete, and then from `READY` to `PASS` once the task itself
completes.
A task with a `WAITING` status is not yet ready to execute. A task in this
state is blocked by its subtasks which are not yet in the `PASS` or `DUPLICATE`
state.
A task with a `READY` status is ready to be executed. All of its subtasks are
in the `PASS` state.
A task can have `DUPLICATE` status in one of the following scenarios:
- It has the same task key as one of its ancestors
- If the task is generated using a task-factory with change type names-factory,
and it has the same task key as another change task descendant of the same
ancestor task-factory
Task keys are generally made up of the canonical task name and the change to
which it applies. To avoid infinite loops, and to potentially reduce needless
duplication, subtasks are ignored on duplicate tasks.
Also see [duplicate-key](#duplicate-key).
A task with a `PASS` status meets all the criteria for `READY`, and has
executed and was successful.
A task with a `FAIL` status has executed and was unsuccessful.
A task with a `INVALID` status has an invalid/missing definition or an
invalid query.
A task with a `SKIPPED` status has been skipped while evaluating the task tree
based on the [evaluation-threshold](#evaluation-threshold) defined for it.
Tasks
-----
Tasks can either be root tasks, or subtasks. Tasks are expected to be
defined in the `All-Projects` project, on the `refs/meta/config` branch
(the root project and branch are [configurable](config.html#section-rootconfig)),
in a file named `task.config`. This file uses the gitconfig format to define
tasks. The special "True" keyword may be used as any query definition
to indicate an always matching query. The following keys may be defined
in any task section:
`applicable`
: This key defines a query that is used to determine whether a task is
applicable to each change. Since tasks are defined hierarchically, the
applicability of subtasks is inherently limited by the applicability of
all tasks above them in the hierarchy.
Example:
```
applicable = status:open
```
`fail`
: This key defines a query that is used to determine whether a task has
already executed and failed for each change.
Example:
```
fail = label:verified-1
```
`in-progress`
: This key defines a query that is used to determine whether a task is
currently in-progress or not. A CI system may use this to ensure that it
only runs one verification instance for a specific change. Either a pass
or fail key is mandatory for leaf tasks. A task with a fail criteria,
but no pass criteria, will pass if it otherwise would be ready. Setting
this to "True" is useful for defining blocking criteria that do not
actually have a task to execute.
Example:
```
in-progress = label:patchset-lock,user=jenkins
```
`pass`
: This key defines a query that is used to determine whether a task has
already executed and passed for each change. Either a pass or fail key is
mandatory for leaf tasks. Tasks with no defined pass criteria and with
defined subtasks are valid, but they are only applicable when at least
one subtask is applicable. Setting this to "True" is useful for defining
informational tasks that are not really expected to execute.
A task with a `fail` key but no pass key has an implied `pass` key which is
the opposite of the `fail` key as if the fail had a `NOT` in front of it.
Such tasks can only pass, fail, or be waiting for their subtasks, they
can never be ready! If they have not failed, and their subtasks have
passed, they have passed also.
Example:
```
pass = label:verified+1
```
`ready-hint`
: This key defines a hint when a task is `READY` describing what
accomplishing the tasks entails. This is meant to be a hint for humans
and may be used as a tool-tip.
Example:
```
ready-hint = Needs to be verified by Jenkins
```
`fail-hint`
: This key defines a hint when a task is in the `FAIL` state describing why
the task is failing. This is meant to be a hint for humans and may be used
as a tool-tip.
Example:
```
fail-hint = Blocked by a negative review score
```
`preload-task`
: This key defines a task whose attributes will be preloaded into the current
task before the current task's attributes are set. Most attributes defined
in the preload-task will be loaded first, and will be overridden by attributes
from the current task if they redefined in the current task. Attributes
which are lists (such as subtasks) or maps (such as properties), will be
preloaded by the preload-task and then extended with the attributes from the
current task. See [Task Expression](task_expression.html) for how to define
optional preload-tasks.
Example:
```
preload-task = Base Jenkins Verification # has a pass criteria and hints
```
`subtask`
: This key lists the name of a subtask of the current task. This key may be
used several times in a task section to define more than one subtask for a
particular task. See [Task Expression](task_expression.html) for how to define
subtasks.
Example:
```
subtask = "Code Review"
subtask = "License Approval"
...
[task "Code Review"]
...
[task "License Approval"]
...
```
`subtasks-factory`
: A subtasks-factory key specifies a task-factory, which generates zero or more
tasks that are subtasks of the current task. This key may be used several times
in a task section to reference tasks-factory sections.
Example:
```
subtasks-factory = "static tasks factory"
...
[tasks-factory "static tasks factory"]
...
```
`subtasks-external`
: This key defines a file containing subtasks of the current task. This
key may be used several times in a task section to define more than one file
containing subtasks for a particular task. The subtasks-external key points
to an external file defined by external section. Note: all of the tasks in
the referenced file will be included as subtasks of the current task!
Example:
```
subtasks-external = my-external
```
`subtasks-file`
: This key defines a file containing subtasks of the current task. This
key may be used several times in a task section to define more than one file
containing subtasks for a particular task. The subtasks-file key points to
a file under the top level task directory in the same project and ref as the
current task file. Note: all of the tasks in the referenced file will be
included as subtasks of the current task!
Example:
```
subtasks-file = common.config # references the file named task/common.config
```
<a id="duplicate-key"></a>
`duplicate-key`
: This key defines an identifier to help identify tasks which should be
considered duplicates even if they are not exact duplicates. When the task
plugin encounters a task with the same duplicate-key as one of its
ancestors, it will be considered a duplicate of that ancestor. Tasks such as
a starting task and a looping tasks-factory that preload the same base task
are not exact duplicates, yet they may logically represent duplicates. In
this case, defining a `duplicate-key` on the base task which is preloaded
from two different places (usually a root and a change tasks-factory), will
ensure that any loops are halted once the original change is reached. Without
a duplicate-key, the walking would generally walk one task further than
desired.
Outlined below is a simple way to walk a change's git dependencies in the
task plugin. While Git does not allow loops in commit histories, sometimes
in Gerrit when changes get rebased, it can cause loops (because Gerrit
sometimes tracks outdated dependencies). The use of the duplicate-key
below results in the loop being detected when you would expect it to be.
Example:
```
[root "git dependencies"]
applicable = status:new
preload-task = git dependencies
[task "git dependencies"]
fail = -status:new
fail-hint = [${_change_status}] dependency needs to be OPEN
subtasks-factory = git dependencies
duplicate-key = git dependencies ${_change_number}
[tasks-factory "git dependencies"]
names-factory = git dependencies
preload-task = git dependencies
[names-factory "git dependencies"]
type = change
changes = -status:merged parentof:${_change_number} project:${_change_project} branch:${_change_branch}
```
<a id="evaluation-threshold"></a>
`evaluation-threshold`
: This key defines a threshold based on which a task can be determined to
be expensive for evaluation and consequently, should be skipped. If the number
of changes in a single `createPluginDefinedInfos()` invocation (such as from a
`gerrit query` command) are more than the `evaluation-threshold` of the task,
it will be skipped i.e. the keys below are not evaluated for the task.
- pass
- fail
- in-progress
- subtask
- subtasks-factory
- subtasks-external
- subtasks-file
- preload-task
- duplicate-key
Example:
```
[root "Root with expensive task"]
subtask = expensive task
[task "expensive task"]
evaluation-threshold = 1
pass = True
```
Root Tasks
----------
Root tasks typically define the "final verification" tasks for changes. Each
root task likely defines a single CI system which is responsible for verifying
and possibly submitting the changes which are managed by that CI system.
`applicable` queries for all root tasks should generally be defined in a non
overlapping fashion.
Root tasks are defined using "root" sections. A sample task.config which
defines 3 non overlapping CI systems might look like this:
```
[root "Jenkins Build"]
applicable = status:open AND (project:a OR project:b) AND -branch:master
...
[root "Jenkins Build and Test"]
applicable = status:open AND (project:a OR project:b) AND branch:master
...
[root "Buildbot"]
applicable = status:open AND project:c
...
```
Subtasks
--------
Subtasks define tasks that must pass before their parent task state is
considered `READY` or `PASS`. Subtasks make it possible to define task
execution dependencies and ordering. Subtasks typically define all the
things that are required for change submission except for the final criteria
that will be assessed by the final verification defined by the change's root
task. This may include tasks that need to be executed by humans, such as
approvals like `code-review`, along with automated tasks such as tests, or
static analysis tool executions.
Subtasks are defined using a "task" section. An example subtask definition:
```
[task "Code Review"]
pass = label:code-review+2
fail = label:code-review-2
```
<a id="tasks_factory"/>
Tasks-Factory
-------------
A tasks-factory section supports all the keys supported by task sections. In
addition, this section must have a names-factory key which refers to a
names-factory section. In conjunction with the names-factory, a tasks-factory
section creates zero or more task definitions that look like regular tasks,
each with a name provided by the names-factory, and all using the task definition
set in the tasks-factory.
A tasks-factory section is referenced by a subtasks-factory key in a "task"
section. A sample task.config which defines a tasks-factory section might look
like this:
```
[task "static task list"]
subtasks-factory = static tasks factory
...
[tasks-factory "static tasks factory"]
names-factory = static names factory list
...
```
Names-Factory
-------------
A names-factory section defines a collection of name keys which are used to
generate the names for task definitions. A names-factory section is referenced
by a names-factory key in a "tasks-factory" section. This section should contain
a `type` key that specifies the type.
`type`
: This key is mandatory and defines the type of the names-factory section. The
type must be one of: `static`, `change`, or `plugin`.
**static type**
: One or more `name` key(s) are required.
`name`
: This key defines the name of the tasks. It can be used several times in order
to define more than one task.
Example:
```
name = my a task
name = 12345
```
Here is an example which defines a names-factory of `static` type.
```
[names-factory "static names factory"]
type = static
name = task A
name = task B
```
**change type**
: The `changes` key is required.
`changes`
: This key defines a query that is used to fetch change numbers which will be
used as the names of the task(s).
Example:
```
changes = change:1 OR change:2
```
Here is an example which defines a names-factory of `change` type.
```
[names-factory "changes names factory"]
type = change
changes = topic:sample AND status:open
```
**plugin type**
: The `plugin` and `provider` keys are required, whereas any `arg` key(s)
are optional. The provider class should implement the `PluginProvidedTaskNamesFactory`
interface. The collection of name strings returned by the `getNames()` method will
be used to generate the names for task definitions.
`arg`
: This key defines an argument that will be passed down to the provider method
`getNames()`. This key may be used several times in order to define a list of
arguments.
Example:
```
arg = foo
```
`plugin`
: This key defines the name of a plugin. The plugin is expected to register
(bind) a class which implements the `PluginProvidedTaskNamesFactory` interface.
Example:
```
plugin = foobar
```
`provider`
: This key defines the exported name used to bind the class which implements the
`PluginProvidedTaskNamesFactory` interface provided by the plugin.
Example:
```
provider = foobar_provider
```
Here is an example which defines a names-factory of `plugin` type.
```
[names-factory "plugin names factory"]
type = plugin
plugin = names_factory_provider
provider = foobar_provider
arg = myarg1
arg = myarg2
```
A plugin `names_factory_provider` created exclusively for use in the test framework
can also be viewed as a reference.
External Entries
----------------
A name for external task files on other projects and branches may be given
by defining an `external` section in a task file. This later allows this
external name to then be referenced by other definitions. The following
keys may be defined in an external section. External references are limited
to files under the top level task directory.
`file`
: This key defines the name of the external task file under the
task directory referenced.
Example:
```
file = common.config # references the file named task/common.config
```
`user`
: This key defines the username of the user's ref in the `All-Users` project
of the external file referenced.
Example:
```
user = first-user # references the sharded user ref refs/users/01/1000001
```
Properties
----------
The task plugin supplies the following properties which may be used anywhere in
a task, tasks-factory, or names-factory definition.
```
${_name} represents the name of the current task
${_change_number} represents the change number of the current change
${_change_id} represents the change id of the current change
${_change_project} represents the project of the current change
${_change_branch} represents the branch of the current change
${_change_status} represents the status of the current change
${_change_topic} represents the topic of the current change
```
Examples:
```
fail-hint = ${_name} needs to be fixed
fail-hint = ${_change_number} with ${_change_status} needs to be fixed
fail-hint = ${_change_id} on ${_change_project} and ${_change_branch} needs to be fixed
changes = parentof:${_change_number} project:${_change_project} branch:${_change_branch}
```
Custom properties may be defined on a task using the following syntax:
```
set-<property-name> = <property-value>
```
Subtasks inherit all custom properties from their parents, however
subtasks may override inherrited properties and give them new values.
Example:
```
[task "foo-project"]
set-project-name = foo
subtask = sub-project
subtask = common-to-many-projects
[task "sub-project"]
set-project-name = sub-foo # Overrides value from 'foo-project'
subtask = common-to-many-projects
[task "common-to-many-projects"]
fail-hint = ${project-name} needs to be fixed
...
```
It is possible to define a custom property value and to export that value
to the json on the current task by using the following syntax:
```
export-<property-name> = <property-value>
```
Example:
```
[task "foo"]
export-ci-system = jenkins
```
```
"subTasks" : [
{
"exported" : {
"ci-system" : "jenkins"
},
...
"name" : "foo",
...
}
]
```
Change Query Output
-------------------
It is possible to add a task section to the query output of changes using
the task plugin switches. The following switches are available:
**\-\-@PLUGIN@\-\-applicable**
This switch is meant to be used to determine the state of applicable
tasks for each change, it outputs applicable tasks for a change.
**\-\-@PLUGIN@\-\-all**
This switch is meant as a debug switch, it outputs all tasks visible to the
calling user, whether they apply to a change or not. When this flag is used,
an additional 'applicable' property is included in each task output to
indicate whether the task actually met its applicability criteria or not.
**\-\-@PLUGIN@\-\-preview**
This switch is meant as a debug switch for previewing changes to task configs.
This switch outputs tasks as if the supplied change were already merged. This
makes it possible to preview the effect of proposed changes before going live
with them. Multiple changes may be previewed together.
**\-\-@PLUGIN@\-\-invalid**
This switch is meant as a debug switch to help find mis-configured tasks,
it causes only invalid tasks and the tasks in the tree hierarchy above them
to be output. To verify task validity, this change runs all queries defined
for a task, no matter what the tasks state is; this makes it possible to
detect some additional configuration problems which may not be detected when
running normally. If all tasks are properly configured, this switch should
not output anything. This switch is particularly useful in combination
with the **\-\-@PLUGIN@\-\-preview** switch.
**\-\-@PLUGIN@\-\-include-paths**
This switch will show the absolute path of each task. This is meant for
debugging when tasks are spread out in different files. A task path includes
task name, type (indicating one of root, task, tasks-factory),
[task factory](#tasks_factory) name (if it is generated by one), file name,
project, and branch the file belongs to. Additionally, if a task is on a user
ref, it also shows the identity of that user. Only users with `viewTaskPaths`
capability on the server can view absolute task paths with this switch.
```
$ ssh -x -p 29418 example.com gerrit query change:123 \-\-@PLUGIN@\-\-include-paths
...
plugins:
name: task
roots:
name: Jenkins Build and Test
inProgress: false
status: READY
path:
name: Jenkins Build and Test
project: All-Projects.git
branch: refs/meta/config
file: task.config
```
**\-\-@PLUGIN@\-\-evaluation-time**
This switch is meant as a debug switch to evaluate task performance. This
switch outputs an elapsed time value on every task indicating how much time
it took to evaluate a task and its subtasks.
**\-\-@PLUGIN@\-\-include-statistics**
A debug switch to include statistics about internal task plugin properties.
This is intended to help task configurators and system administrators profile
task queries to help improve their performance. This option adds some task
plugin statistics to each of the tasks and the overall query statistics to
the last change in the query.
**\-\-@PLUGIN@\-\-only**
This switch can be used to only evaluate tasks under a certain root when tasks
from other roots are unwanted. For example, a CI system may not be interested
in evaluating tasks for another CI system. The switch can be provided multiple
times.
Examples
--------
When tasks are appended to changes, they will have a "task" section under
the plugins section like below:
```
$ ssh -x -p 29418 example.com gerrit query change:123
change I9fdfb1315610a8e3d5c48e4321193b7c265f30ae
...
plugins:
name: task
roots:
name: Jenkins Build and Test
inProgress: false
status: READY
subTasks:
name: code review
status: PASS
```
See [task_states](test/task_states.html) for a comprehensive list of examples
of task configs and their states.
See what the results will look when [previewing](test/preview.html) a change
to those states.