зеркало из https://github.com/mozilla/gecko-dev.git
272 строки
11 KiB
ReStructuredText
272 строки
11 KiB
ReStructuredText
Actions
|
|
=======
|
|
|
|
This document shows how to define an action in-tree such that it shows up in
|
|
supported user interfaces like Treeherder. For details on interface between
|
|
in-tree logic and external user interfaces, see `the actions.json spec`_.
|
|
|
|
At a very high level, the process looks like this:
|
|
|
|
* The decision task produces an artifact, ``public/actions.json``, indicating
|
|
what actions are available.
|
|
|
|
* A user interface (for example, Treeherder or the Taskcluster tools) consults
|
|
``actions.json`` and presents appropriate choices to the user, if necessary
|
|
gathering additional data from the user, such as the number of times to
|
|
re-trigger a test case.
|
|
|
|
* The user interface follows the action description to carry out the action.
|
|
In most cases (``action.kind == 'task'``), that entails creating an "action
|
|
task", including the provided information. That action task is responsible
|
|
for carrying out the named action, and may create new sub-tasks if necessary
|
|
(for example, to re-trigger a task).
|
|
|
|
Defining Action Tasks
|
|
---------------------
|
|
|
|
There is one options for defining actions: creating a callback action.
|
|
A callback action automatically defines an action task that will invoke a
|
|
Python function of your devising.
|
|
|
|
Creating a Callback Action
|
|
--------------------------
|
|
|
|
.. note::
|
|
|
|
You can generate ``actions.json`` on the command line with ``./mach taskgraph actions``.
|
|
|
|
A *callback action* is an action that calls back into in-tree logic. That is,
|
|
you register the action with name, title, description, context, input schema and a
|
|
python callback. When the action is triggered in a user interface,
|
|
input matching the schema is collected, passed to a new task which then calls
|
|
your python callback, enabling it to do pretty much anything it wants to.
|
|
|
|
To create a new callback action you must create a file
|
|
``taskcluster/gecko_taskgraph/actions/my-action.py``, that at minimum contains::
|
|
|
|
from __future__ import absolute_import, print_function, unicode_literals
|
|
|
|
from .registry import register_callback_action
|
|
|
|
@register_callback_action(
|
|
name='hello',
|
|
title='Say Hello',
|
|
symbol='hw', # Show the callback task in treeherder as 'hw'
|
|
description="Simple **proof-of-concept** callback action",
|
|
order=10000, # Order in which it should appear relative to other actions
|
|
)
|
|
def hello_world_action(parameters, graph_config, input, task_group_id, task_id, task):
|
|
print("Hello was triggered from taskGroupId: {}".format(task_group_id))
|
|
|
|
The arguments are:
|
|
|
|
``parameters``
|
|
an instance of ``taskgraph.parameters.Parameters``, carrying decision task parameters from the original decision task.
|
|
|
|
``graph_config``
|
|
an instance of ``taskgraph.config.GraphConfig``, carrying configuration for this tree
|
|
|
|
``input``
|
|
the input from the user triggering the action (if any)
|
|
|
|
``task_group_id``
|
|
the target task group on which this action should operate
|
|
|
|
``task_id``
|
|
the target task on which this action should operate (or None if it is operating on the whole group)
|
|
|
|
``task``
|
|
the definition of the target task (or None, as for ``task_id``)
|
|
|
|
The example above defines an action that is available in the context-menu for
|
|
the entire task-group (result-set or push in Treeherder terminology). To create
|
|
an action that shows up in the context menu for a task we would specify the
|
|
``context`` parameter.
|
|
|
|
The ``order`` value is the sort key defining the order of actions in the
|
|
resulting ``actions.json`` file. If multiple actions have the same name and
|
|
match the same task, the action with the smallest ``order`` will be used.
|
|
|
|
Setting the Action Context
|
|
..........................
|
|
The context parameter should be a list of tag-sets, such as
|
|
``context=[{"platform": "linux"}]``, which will make the task show up in the
|
|
context-menu for any task with ``task.tags.platform = 'linux'``. Below is
|
|
some examples of context parameters and the resulting conditions on
|
|
``task.tags`` (tags used below are just illustrative).
|
|
|
|
``context=[{"platform": "linux"}]``:
|
|
Requires ``task.tags.platform = 'linux'``.
|
|
``context=[{"kind": "test", "platform": "linux"}]``:
|
|
Requires ``task.tags.platform = 'linux'`` **and** ``task.tags.kind = 'test'``.
|
|
``context=[{"kind": "test"}, {"platform": "linux"}]``:
|
|
Requires ``task.tags.platform = 'linux'`` **or** ``task.tags.kind = 'test'``.
|
|
``context=[{}]``:
|
|
Requires nothing and the action will show up in the context menu for all tasks.
|
|
``context=[]``:
|
|
Is the same as not setting the context parameter, which will make the action
|
|
show up in the context menu for the task-group.
|
|
(i.e., the action is not specific to some task)
|
|
|
|
The example action below will be shown in the context-menu for tasks with
|
|
``task.tags.platform = 'linux'``::
|
|
|
|
from registry import register_callback_action
|
|
|
|
@register_callback_action(
|
|
name='retrigger',
|
|
title='Retrigger',
|
|
symbol='re-c', # Show the callback task in treeherder as 're-c'
|
|
description="Create a clone of the task",
|
|
order=1,
|
|
context=[{'platform': 'linux'}]
|
|
)
|
|
def retrigger_action(parameters, graph_config, input, task_group_id, task_id, task):
|
|
# input will be None
|
|
print "Retriggering: {}".format(task_id)
|
|
print "task definition: {}".format(task)
|
|
|
|
When the ``context`` parameter is set, the ``task_id`` and ``task`` parameters
|
|
will provided to the callback. In this case the ``task_id`` and ``task``
|
|
parameters will be the ``taskId`` and *task definition* of the task from whose
|
|
context-menu the action was triggered.
|
|
|
|
Typically, the ``context`` parameter is used for actions that operate on
|
|
tasks, such as retriggering, running a specific test case, creating a loaner,
|
|
bisection, etc. You can think of the context as a place the action should
|
|
appear, but it's also very much a form of input the action can use.
|
|
|
|
|
|
Specifying an Input Schema
|
|
..........................
|
|
In call examples so far the ``input`` parameter for the callbacks has been
|
|
``None``. To make an action that takes input you must specify an input schema.
|
|
This is done by passing a JSON schema as the ``schema`` parameter.
|
|
|
|
When designing a schema for the input it is important to exploit as many of the
|
|
JSON schema validation features as reasonably possible. Furthermore, it is
|
|
*strongly* encouraged that the ``title`` and ``description`` properties in
|
|
JSON schemas is used to provide a detailed explanation of what the input
|
|
value will do. Authors can reasonably expect JSON schema ``description``
|
|
properties to be rendered as markdown before being presented.
|
|
|
|
The example below illustrates how to specify an input schema. Notice that while
|
|
this example doesn't specify a ``context`` it is perfectly legal to specify
|
|
both ``input`` and ``context``::
|
|
|
|
from registry import register_callback_action
|
|
|
|
@register_callback_action(
|
|
name='run-all',
|
|
title='Run All Tasks',
|
|
symbol='ra-c', # Show the callback task in treeherder as 'ra-c'
|
|
description="**Run all tasks** that have been _optimized_ away.",
|
|
order=1,
|
|
input={
|
|
'title': 'Action Options',
|
|
'description': 'Options for how you wish to run all tasks',
|
|
'properties': {
|
|
'priority': {
|
|
'title': 'priority'
|
|
'description': 'Priority that should be given to the tasks',
|
|
'type': 'string',
|
|
'enum': ['low', 'normal', 'high'],
|
|
'default': 'low',
|
|
},
|
|
'runTalos': {
|
|
'title': 'Run Talos'
|
|
'description': 'Do you wish to also include talos tasks?',
|
|
'type': 'boolean',
|
|
'default': 'false',
|
|
}
|
|
},
|
|
'required': ['priority', 'runTalos'],
|
|
'additionalProperties': False,
|
|
},
|
|
)
|
|
def retrigger_action(parameters, graph_config, input, task_group_id, task_id, task):
|
|
print "Create all pruned tasks with priority: {}".format(input['priority'])
|
|
if input['runTalos']:
|
|
print "Also running talos jobs..."
|
|
|
|
When the ``schema`` parameter is given the callback will always be called with
|
|
an ``input`` parameter that satisfies the previously given JSON schema.
|
|
It is encouraged to set ``additionalProperties: false``, as well as specifying
|
|
all properties as ``required`` in the JSON schema. Furthermore, it's good
|
|
practice to provide ``default`` values for properties, as user interface generators
|
|
will often take advantage of such properties.
|
|
|
|
It is possible to specify the ``schema`` parameter as a callable that returns
|
|
the JSON schema. It will be called with a keyword parameter ``graph_config``
|
|
with the `graph configuration <taskgraph-graph-config>` of the current
|
|
taskgraph.
|
|
|
|
Once you have specified input and context as applicable for your action you can
|
|
do pretty much anything you want from within your callback. Whether you want
|
|
to create one or more tasks or run a specific piece of code like a test.
|
|
|
|
Conditional Availability
|
|
........................
|
|
|
|
The decision parameters ``taskgraph.parameters.Parameters`` passed to
|
|
the callback are also available when the decision task generates the list of
|
|
actions to be displayed in the user interface. When registering an action
|
|
callback the ``availability`` option can be used to specify a callable
|
|
which, given the decision parameters, determines if the action should be available.
|
|
The feature is illustrated below::
|
|
|
|
from registry import register_callback_action
|
|
|
|
@register_callback_action(
|
|
name='hello',
|
|
title='Say Hello',
|
|
symbol='hw', # Show the callback task in treeherder as 'hw'
|
|
description="Simple **proof-of-concept** callback action",
|
|
order=2,
|
|
# Define an action that is only included if this is a push to try
|
|
available=lambda parameters: parameters.get('project', None) == 'try',
|
|
)
|
|
def try_only_action(parameters, graph_config, input, task_group_id, task_id, task):
|
|
print "My try-only action"
|
|
|
|
Properties of ``parameters`` are documented in the
|
|
:doc:`parameters section <parameters>`. You can also examine the
|
|
``parameters.yml`` artifact created by decisions tasks.
|
|
|
|
Context can be similarly conditionalized by passing a function which returns
|
|
the appropriate context::
|
|
|
|
context=lambda params:
|
|
[{}] if int(params['level']) < 3 else [{'worker-implementation': 'docker-worker'}],
|
|
|
|
Creating Tasks
|
|
--------------
|
|
|
|
The ``create_tasks`` utility function provides a full-featured way to create
|
|
new tasks. Its features include creating prerequisite tasks, operating in a
|
|
"testing" mode with ``./mach taskgraph test-action-callback``, and generating
|
|
artifacts that can be used by later action tasks to figure out what happened.
|
|
See the source for more detailed docmentation.
|
|
|
|
The artifacts are:
|
|
|
|
``task-graph.json`` (or ``task-graph-<suffix>.json``:
|
|
The graph of all tasks created by the action task. Includes tasks
|
|
created to satisfy requirements.
|
|
``to-run.json`` (or ``to-run-<suffix>.json``:
|
|
The set of tasks that the action task requested to build. This does not
|
|
include the requirements.
|
|
``label-to-taskid.json`` (or ``label-to-taskid-<suffix>.json``:
|
|
This is the mapping from label to ``taskid`` for all tasks involved in
|
|
the task-graph. This includes dependencies.
|
|
|
|
More Information
|
|
----------------
|
|
|
|
For further details on actions in general, see `the actions.json spec`_.
|
|
The hooks used for in-tree actions are set up by `ci-admin`_ based on configuration in `ci-configuration`_.
|
|
|
|
.. _the actions.json spec: https://firefox-ci-tc.services.mozilla.com/docs/manual/tasks/actions/spec
|
|
.. _ci-admin: http://hg.mozilla.org/ci/ci-admin/
|
|
.. _ci-configuration: http://hg.mozilla.org/ci/ci-configuration/
|