Project: Lagoon (App Definition and Development / Application Definition & Image Build)
File: custom-tasks.md

Lagoon allows for the definition of custom tasks at environment, project, and group levels. This is presently accomplished through the GraphQL API and exposed in the UI. When defining a task you need to determine a number of things. In most cases, the custom task you will be running will be something that will be run in a shell on one of the containers in your application. For instance, in a Node.js application, you may be interested in running a `yarn audit` in your `node` container. The command, in this case, would simply be `yarn audit`. We have to define where this task will be run -- this means two things, first, which project or environment we'll be running the task in, and, second, which service. Let's say that we'd like for our `yarn audit` task to be available to run in any environment in a specific project \(let's say the project's ID is 42 for this example\). We will therefore specify the project's ID when we create our task definition, as we will describe below. The second question regards which environment we want to target with our task. When you set up your project, you specify several services in your . We use this service name to determine where the command is actually executed. There are three levels of permissions to the task system corresponding to project roles. Guest, Developer, and Maintainer -- from most restrictive to least restrictive, with each role being able to invoke the tasks defined for the lower role \(Developer can see Guest tasks, Maintainers can see all tasks\). Tasks are defined by calling the `addAdvancedTaskDefinition` mutation. Importantly, this simply defines the task, it does not invoke it. It simply makes it avaliable to be run in an environment. Schematically, the call looks like this ```graphql title="Define a new task" mutation addAdvancedTask { addAdvancedTaskDefinition(input:{ name: string, confirmationText: string, type: [COMMAND|IMAGE], description: string, service: string, command: string, advancedTaskDefinitionArguments: [ { name: "ENVIROMENTVARIABLENAME", displayName: "Friendly Name For Variable", type: [STRING | ENVIRONMENTSOURCENAME | ENVIRONMENTSOURCENAMEEXCLUDESELF] } ] }) { ... on AdvancedTaskDefinitionImage { id name description service image confirmationText advancedTaskDefinitionArguments { type range name displayName } ... } ... on AdvancedTaskDefinitionCommand { id name description service command advancedTaskDefinitionArguments { type range name displayName } ... } } } ``` Fields `name` and `description` are straightforward. They're simply the name and description of the task - these are used primarily in the UI. The `type` field needs some explanation - for now, only platform admins are able to define `IMAGE` type commands - these allow for the running of specifically created task images as tasks, rather than targeting existing services. Most tasks, though, will be `COMMAND` types. The `[project|environment]` set of fields will attach the task to either the `project` or `environment` \(depending on the key you use\), with the value being the

In the case we're considering for our `yarn audit` we will specify we're targeting a `project` with an ID of `42`. We put the service we'd like to target with our task in the `service` field, and `command` is the actual command that we'd like to run. In order to give more flexibility to the users invoking the tasks via the Lagoon UI, we support defining task arguments. These arguments are displayed as text boxes or drop downs and are required for the task to be invoked. Here is an example of how we might set up two arguments. ```graphql title="Define task arguments" advancedTaskDefinitionArguments: [ { name: "ENVVARNAME_SOURCE", displayName: "Environment source", type: ENVIRONMENTSOURCENAME }, { name: "ENVVARNAME_STRING", displayName: "Echo value", type: STRING } ] }) ``` This fragment shows both types of arguments the system currently supports. The first, `ENVVARNAMESOURCE` is an example of type `ENVIRONMENTSOURCENAME`, which will present the user of the UI a dropdown of the different environments inside of a project. If we don't want to allow the task to be run on the invoking environment (say, if we want to import a database from another environment), we can restrict the environment list by using `ENVIRONMENTSOURCENAMEEXCLUDE_SELF`. The second `ENVVARNAME_STRING` is of type `STRING` and will present the user with a textbox to fill in. The values that the user selects will be available as environment variables in the `COMMAND` type tasks when the task is run. Platform administrators are able to register system wide tasks. These tasks will appear for all environments, subject to the user's permission to invoke them. Creating a system wide task is almost exactly the same as other task types, with two exceptions. First, you set the `systemWide: true` field in your `addAdvancedTaskDefinition` mutation. Second, you make sure you have not specified `groupName`, `project`, or `environment` - which would defeat the purpose, since these fields are used to target specific contexts. When the `confirmationText` field has text, it will be displayed with a confirmation modal in the UI before the user is able to run the task. With the task now defined, the task should now show up in the tasks dropdown in the Lagoon UI. We are also able to invoke it via the GraphQL api by using the `invokeTask` mutation. ```graphql title="Invoke task" mutation invokeTask { invokeRegisteredTask(advancedTaskDefinition: int, environment: int) { status } } ``` Note that `invokeTask` will always invoke a task on a specific environment. Let's now setup our `yarn audit` example. ```graphql title="Define task mutation" mutation runYarnAudit { addAdvancedTaskDefinition(input:{ name:"Run yarn audit", project: 42, type:COMMAND, permission:DEVELOPER, description: "Runs a 'yarn audit'", service:"node", command: "yarn audit"}) { id } } ``` This, then, will define our task for our project \(42\). When we run this, we will get the ID of the task definition back \(for argument's sake, let's say it's `9`\) This task will now be available to run from the UI for anyone with the `DEVELOPER` or `MAINTAINER` role.