summaryrefslogtreecommitdiffstats
path: root/taskcluster/docs/actions.rst
diff options
context:
space:
mode:
Diffstat (limited to 'taskcluster/docs/actions.rst')
-rw-r--r--taskcluster/docs/actions.rst271
1 files changed, 271 insertions, 0 deletions
diff --git a/taskcluster/docs/actions.rst b/taskcluster/docs/actions.rst
new file mode 100644
index 0000000000..a766c1bb29
--- /dev/null
+++ b/taskcluster/docs/actions.rst
@@ -0,0 +1,271 @@
+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/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/