| 1 | + | --- |
| 2 | + | title: Automations |
| 3 | + | description: Rules committed to a repository that act when something happens, such as commenting, labelling, putting an agent on an issue or posting to chat. |
| 4 | + | --- |
| 5 | + | |
| 6 | + | An automation is a rule kept in the repository, in |
| 7 | + | `.g1t/automations/`. It says what starts it, what must be true, and the |
| 8 | + | steps to take: |
| 9 | + | |
| 10 | + | ```yaml |
| 11 | + | # .g1t/automations/bugs.yml |
| 12 | + | name: Put an agent on every bug |
| 13 | + | on: issue.opened |
| 14 | + | if: |
| 15 | + | labels: bug |
| 16 | + | do: |
| 17 | + | - comment: "Thanks, {{actor}}. A g1t agent is on it." |
| 18 | + | - assign_agent |
| 19 | + | ``` |
| 20 | + | |
| 21 | + | Commit that file to the default branch and, from the next issue opened |
| 22 | + | with the label `bug`, g1t answers it and starts an agent on it. Change or |
| 23 | + | delete the file and the automation changes with it. |
| 24 | + | |
| 25 | + | Because automations are files, they are reviewed in pull requests like |
| 26 | + | any other change. Each repository can have up to 50. |
| 27 | + | |
| 28 | + | ## See them run |
| 29 | + | |
| 30 | + | Open the repository's **Automations** page, in its sidebar. Each automation |
| 31 | + | is listed in words, such as *On issue.opened, if labelled bug: comment, |
| 32 | + | then put a g1t agent on it*, with its last run. A file g1t cannot read is |
| 33 | + | listed too, with what is wrong with it. |
| 34 | + | |
| 35 | + | **Recent runs** lists the last 50 runs. Open a run to see what started it, |
| 36 | + | and either how each step went or why the run was skipped. |
| 37 | + | |
| 38 | + | Members of the workspace can also: |
| 39 | + | |
| 40 | + | - **Run now**: run it straight away, on an issue or pull request if you |
| 41 | + | give its number. This works for any automation, whatever starts it. |
| 42 | + | - **Turn off** and **Turn on**: stop it without changing its file. It |
| 43 | + | stays off when the file changes. |
| 44 | + | |
| 45 | + | ## What starts it: `on` |
| 46 | + | |
| 47 | + | | `on` | Starts it | |
| 48 | + | | --- | --- | |
| 49 | + | | An event, such as `issue.opened` | Whenever that happens in the repository. | |
| 50 | + | | A list, such as `[pull.merged, pull.closed]` | Whenever any of them happens. | |
| 51 | + | | `schedule: "0 9 * * mon"` | On a cron schedule, in UTC. | |
| 52 | + | | `manual` | Only by **Run now** or the API. | |
| 53 | + | |
| 54 | + | The events are the same as [webhooks'](/guides/webhooks/#events): |
| 55 | + | |
| 56 | + | | Event | When | |
| 57 | + | | --- | --- | |
| 58 | + | | `git.push` | A branch moved. | |
| 59 | + | | `issue.opened`, `issue.updated`, `issue.assigned`, `issue.closed`, `issue.reopened` | An issue changed. | |
| 60 | + | | `comment.created` | A comment or review on an issue or pull request. | |
| 61 | + | | `pull.opened`, `pull.ready`, `pull.updated`, `pull.merge_requested`, `pull.merged`, `pull.closed` | A pull request changed. | |
| 62 | + | | `checks.completed` | An issue's acceptance checks finished on a pull request. | |
| 63 | + | | `review.completed` | A g1t agent reviewed a pull request. | |
| 64 | + | | `queue.changed` | The merge queue changed. | |
| 65 | + | |
| 66 | + | A schedule has five fields: minute, hour, day of the month, month and day |
| 67 | + | of the week. Each field takes `*`, a number, a list (`1,15`), a range |
| 68 | + | (`1-5`) or a step (`*/15`, `0-30/10`). Days of the week also take names, |
| 69 | + | `sun` to `sat`. |
| 70 | + | |
| 71 | + | | Schedule | Runs | |
| 72 | + | | --- | --- | |
| 73 | + | | `"0 9 * * mon"` | Mondays at 09:00 UTC | |
| 74 | + | | `"*/30 * * * *"` | Every half hour | |
| 75 | + | | `"0 0 1 * *"` | At midnight on the first of each month | |
| 76 | + | | `"0 17 * * mon-fri"` | Weekdays at 17:00 UTC | |
| 77 | + | |
| 78 | + | A scheduled run is not about any issue or pull request, so it can only use |
| 79 | + | `open_issue` and `notify`. |
| 80 | + | |
| 81 | + | ## Conditions: `if` |
| 82 | + | |
| 83 | + | `if` maps fields to the values they must have. Give one value or a list: |
| 84 | + | any value in the list matches. Every field must match. Matching ignores |
| 85 | + | case. |
| 86 | + | |
| 87 | + | ```yaml |
| 88 | + | if: |
| 89 | + | labels: [bug, regression] # has either label |
| 90 | + | status: failed # checks.completed's data.status |
| 91 | + | actor: ada # caused by ada |
| 92 | + | ``` |
| 93 | + | |
| 94 | + | | Field | Matches | |
| 95 | + | | --- | --- | |
| 96 | + | | `labels` | The issue has any of these labels. For a pull request, the labels of its issue. | |
| 97 | + | | `actor` | The username of who caused the event. A g1t agent is `g1t-agent`. | |
| 98 | + | | `branch` | The branch pushed to, for `git.push`. | |
| 99 | + | | Any field of the event's `data` | Such as `status` or `verdict`. Write `data.status` to be explicit. | |
| 100 | + | |
| 101 | + | When the conditions do not match, the run is recorded as skipped, with the |
| 102 | + | reason, such as *not labelled bug*. |
| 103 | + | |
| 104 | + | ## Steps: `do` |
| 105 | + | |
| 106 | + | Steps run in order. When a step fails, the steps after it do not run, and |
| 107 | + | the run shows which step failed and why. |
| 108 | + | |
| 109 | + | | Step | Does | |
| 110 | + | | --- | --- | |
| 111 | + | | `comment: text` | Comments on the issue or pull request. | |
| 112 | + | | `label: name` | Adds a label to the issue. | |
| 113 | + | | `unlabel: name` | Removes a label from the issue. | |
| 114 | + | | `assign_agent` | Puts a g1t agent on the issue, which opens a pull request. | |
| 115 | + | | `message_agent: text` | Tells the agent working on the pull request. It reads the message at its next step. | |
| 116 | + | | `close_issue` | Closes the issue as completed. `close_issue: not_planned` closes it as not planned. | |
| 117 | + | | `reopen_issue` | Reopens the issue. | |
| 118 | + | | `open_issue:` | Opens a new issue: `title`, and optionally `body`, `labels` and `assign_agent: true`. | |
| 119 | + | | `notify:` | Posts `text` to an HTTPS `url`, such as a Slack or Discord incoming webhook. | |
| 120 | + | |
| 121 | + | A step without arguments is written as its name alone, such as |
| 122 | + | `- assign_agent`. A step that takes text is written as the name and the |
| 123 | + | text, such as `- label: triaged`. A step that takes several arguments takes |
| 124 | + | a mapping: |
| 125 | + | |
| 126 | + | ```yaml |
| 127 | + | do: |
| 128 | + | - open_issue: |
| 129 | + | title: Weekly tidy-up |
| 130 | + | body: Update dependencies that have new patch releases. |
| 131 | + | labels: [chore] |
| 132 | + | assign_agent: true |
| 133 | + | - notify: |
| 134 | + | url: https://hooks.slack.com/services/T000/B000/XXXX |
| 135 | + | text: "Opened #{{opened}} in {{repo}}" |
| 136 | + | ``` |
| 137 | + | |
| 138 | + | `notify` sends `{"text": …, "content": …}`, the shape that Slack's, |
| 139 | + | Discord's and most chat tools' incoming webhooks take. It posts only to |
| 140 | + | public addresses. |
| 141 | + | |
| 142 | + | ## Filling in text: `{{ }}` |
| 143 | + | |
| 144 | + | Text in steps can use these, written in double braces: |
| 145 | + | |
| 146 | + | | Name | Is | |
| 147 | + | | --- | --- | |
| 148 | + | | `{{repo}}` | The repository, such as `acme/web`. | |
| 149 | + | | `{{event}}` | What started the run, such as `issue.opened`, `schedule` or `manual`. | |
| 150 | + | | `{{automation}}` | The automation's name. | |
| 151 | + | | `{{actor}}` | The username of who caused the event. | |
| 152 | + | | `{{number}}`, `{{title}}`, `{{url}}` | The issue or pull request the event is about. | |
| 153 | + | | `{{branch}}` | The branch pushed to, for `git.push`. | |
| 154 | + | | `{{opened}}` | The number of the issue that `open_issue` just opened. | |
| 155 | + | | `{{data.field}}` | Any field of the event's data, such as `{{data.status}}`. | |
| 156 | + | |
| 157 | + | A name with no value is left empty. |
| 158 | + | |
| 159 | + | ## Who it acts as |
| 160 | + | |
| 161 | + | An automation acts as its workspace. A comment from one shows the |
| 162 | + | workspace as its author and ends with the automation's name. An agent |
| 163 | + | it starts is billed to the workspace like any other run, through the |
| 164 | + | workspace's [model providers](/guides/models/). |
| 165 | + | |
| 166 | + | ## The rules every run keeps |
| 167 | + | |
| 168 | + | - **Once per event.** An event runs each automation at most once, even if |
| 169 | + | it is delivered again. |
| 170 | + | - **No loops.** An automation does not answer an event that its own run |
| 171 | + | caused on the same issue or pull request in the last 10 minutes. An |
| 172 | + | automation that labels issues on `issue.updated` does not keep running |
| 173 | + | because it labelled one. |
| 174 | + | - **A limit per hour.** An automation makes at most 30 runs an hour. |
| 175 | + | Runs past the limit are skipped and recorded. Change the limit, up to |
| 176 | + | 200, with: |
| 177 | + | |
| 178 | + | ```yaml |
| 179 | + | limits: |
| 180 | + | per_hour: 100 |
| 181 | + | ``` |
| 182 | + | |
| 183 | + | ## More examples |
| 184 | + | |
| 185 | + | Tell the agent when checks fail: |
| 186 | + | |
| 187 | + | ```yaml |
| 188 | + | name: Tell the agent when checks fail |
| 189 | + | on: checks.completed |
| 190 | + | if: |
| 191 | + | status: failed |
| 192 | + | do: |
| 193 | + | - message_agent: "The acceptance checks failed. Read their output on #{{number}} and fix the cause." |
| 194 | + | ``` |
| 195 | + | |
| 196 | + | Announce merges in chat: |
| 197 | + | |
| 198 | + | ```yaml |
| 199 | + | name: Announce merges |
| 200 | + | on: pull.merged |
| 201 | + | do: |
| 202 | + | - notify: |
| 203 | + | url: https://hooks.slack.com/services/T000/B000/XXXX |
| 204 | + | text: "Merged into {{repo}}: {{title}} {{url}}" |
| 205 | + | ``` |
| 206 | + | |
| 207 | + | Close questions nobody followed up: |
| 208 | + | |
| 209 | + | ```yaml |
| 210 | + | name: Close answered questions |
| 211 | + | on: manual |
| 212 | + | if: |
| 213 | + | labels: question |
| 214 | + | do: |
| 215 | + | - comment: "Closing this as answered. Reopen it if there is more to ask." |
| 216 | + | - close_issue: not_planned |
| 217 | + | ``` |
| 218 | + | |
| 219 | + | ## From the API |
| 220 | + | |
| 221 | + | | Tool | Route | |
| 222 | + | | --- | --- | |
| 223 | + | | `list_automations` | `GET /repos/{owner}/{name}/automations` | |
| 224 | + | | `list_automation_runs` | `GET /repos/{owner}/{name}/automations/runs`, optionally `?automation={id}` | |
| 225 | + | | `run_automation` | `POST /repos/{owner}/{name}/automations/{id}/runs`, optionally with `number` | |
| 226 | + | | `update_automation` | `PATCH /repos/{owner}/{name}/automations/{id}` with `enabled` | |
| 227 | + | |
| 228 | + | Running an automation or turning one on or off needs a member of the |
| 229 | + | workspace. Agents cannot run or change automations. To add or change one, |
| 230 | + | commit its file. |
| 231 | + | |
| 232 | + | ```sh |
| 233 | + | curl -X POST https://api.g1t.sh/repos/acme/web/automations/aut_01m4…/runs \ |
| 234 | + | -H "Authorization: Bearer $G1T_TOKEN" -H "Content-Type: application/json" \ |
| 235 | + | -d '{"number": 42}' |
| 236 | + | ``` |