View as Markdown

Events

Agent hint

Rules on this page that most often cause invalid routines:

  • Event names are exact and case-sensitive: pr.opened, not PR.Opened or pull_request.opened. List 1 to 8 events, each once.
  • The six reserved names in Reserved events are rejected with “is not supported yet”. Do not use pr.labeled, pr.conflicted, ci.succeeded and the like.
  • Every filter and every template variable must exist for every listed event. A when field needs only one listed event that carries it; on the others it is null (guard with pr.number != null && ...). When the events differ, write one routine per event shape. The filter matrix shows what is allowed.
  • ci.failed has no actor: no sender, and the self and bots filters are refused for it.
  • comment.created carries a pr or an issue (the parent of the comment), never both. ci.failed carries a pr only when the CI run belongs to a pull request. A routine that needs the pull request (its prompt reads pr.number or pr.headBranch) must add filters.when: 'pr.number != null'; otherwise a run without a pull request starts with an empty pr, and limits.maxRunsPerPr does not apply to it.
  • Field names are the same everywhere (pr.baseBranch, comment.mentionsBot), with one exception: the slash command is comment.command.name in templates and comment.commandName in when.

An event is something that happened in a connected repository. Forkline turns the webhook of each forge (GitHub, GitLab, Forgejo) into one forge-neutral event, so the same routine works on all three. A routine lists the events it wants in on.events:

on:
  events: [ci.failed, pr.updated]

This page lists the events, the exact webhook each one comes from, and the typed fields it carries. Filters decide which events start a run, and Prompt templates put the fields into the prompt. Routines started by a schedule or a custom webhook have no events, see Schedule and webhook triggers.

The events

Event Fires when Actor
pr.opened A pull request (merge request on GitLab) was opened, reopened, or marked ready for review the person who acted
pr.updated New commits were pushed to a pull request the person who pushed
pr.merged A pull request was merged the person who merged
pr.closed A pull request was closed without being merged the person who closed it
issue.opened An issue was opened the person who opened it
comment.created A comment was added to an issue or a pull request the commenter
ci.failed A CI run failed or timed out none, Forkline derives this event
push Commits were pushed to a branch the pusher

The actor is the sender of the event: the person or bot whose action caused the webhook. The self and bots filters judge the actor. ci.failed has no actor because the forge reports a failed run, not a person.

Forge mapping

Each cell names the webhook event the forge sends and the action values that map to the event. A webhook event or action that is not in the table is dropped silently: no routine sees it and no run is recorded.

Event GitHub GitLab Forgejo
pr.opened pull_request with action opened, reopened or ready_for_review merge_request with action open or reopen pull_request with action opened, reopened or ready_for_review
pr.updated pull_request with action synchronize merge_request with action update and a changed commit (oldrev present); edits of the title, description or labels are ignored pull_request with action synchronized (or synchronize)
pr.merged pull_request with action closed and merged: true (or a merged_at time) merge_request with action merge, or close with a merged_at time pull_request with action closed and merged: true (or a merged_at time)
pr.closed pull_request with action closed, not merged merge_request with action close, not merged pull_request with action closed, not merged
issue.opened issues with action opened issue with action open issues with action opened
comment.created issue_comment or pull_request_review_comment, action created note on an Issue or a MergeRequest issue_comment or pull_request_comment, action created
ci.failed workflow_run with action completed and conclusion failure or timed_out pipeline with status failed (merge request pipelines included) action_run_failure with run status failure, or workflow_run with action completed and conclusion failure or timed_out
push push to a branch (refs/heads/...) push to a branch push to a branch

Edge cases that change what an event means:

  • Reopen and ready for review count as pr.opened. A routine on pr.opened runs again when a pull request is reopened, and when a draft is marked ready for review (GitHub and Forgejo). A pull request opened as a draft also fires pr.opened once, with pr.draft set to true; the default draft: exclude filter skips it, and the later ready-for-review fires the event again with pr.draft set to false.
  • pr.updated means new commits, not edits. Changing the title, description, labels or assignees does not fire it on any forge.
  • Merge versus close. A merged pull request fires pr.merged only; a pull request closed without merging fires pr.closed only. Listing both gives the routine every end of a pull request.
  • Comments. On GitHub a comment on a pull request conversation arrives as issue_comment and carries the pull request as pr (without its branches and head commit, see the field notes); a comment on an inline review arrives as pull_request_review_comment and carries the full pr. A comment on an issue carries issue. GitLab note events on commits and snippets are dropped. Edited and deleted comments are dropped.
  • Push means a branch push. A tag push, a branch deletion and a push whose new commit is all zeros are dropped. On GitLab, a tag_push is dropped as well.
  • ci.failed fires for failed and timed-out runs only. Cancelled, skipped, neutral and successful runs, GitHub check_run, check_suite and status events, and Forgejo commit statuses are dropped. Forkline starts one routine run per failed run or pipeline; it does not wait to collect several failures of one commit.
  • Labels, assignment and review requests (labeled, assigned, review_requested and the like) are dropped; the events for them are reserved.
  • Draft and fork flags come from the forge payload. Forgejo reports no draft flag on some versions, so Forkline treats a title starting with Draft:, [Draft], (Draft), WIP:, [WIP] or (WIP) as a draft. GitLab merge-request drafts use the draft flag (or the older work_in_progress). Toggling draft status without new commits is not an event on GitLab.

What each event carries

Every event carries repository. The other roots depend on the event.

Event sender pr issue comment ci push
pr.opened, pr.updated, pr.merged, pr.closed yes yes no no no no
issue.opened yes no yes no no no
comment.created yes when the comment is on a pull request when the comment is on an issue yes no no
ci.failed no when the run belongs to a pull request no no yes no
push yes no no no no yes

“Carries” means filled in. A root that an event does not carry is absent: it renders empty in a template and is null or an empty list in when.

Fields

All field names are camelCase and are the same in templates and in the when condition. Values are bounded when the event is read: short strings (logins, branches, labels, refs, SHAs) at most 1,024 bytes, titles 1,024 bytes, bodies 64 KiB, URLs 4,096 bytes, paths 512 bytes, at most 100 labels, 100 ci.failures and 3,000 push paths. Longer text is cut at a character boundary.

repository (every event)

Field Type May be empty Example
repository.fullName string no acme/api (GitLab: the nested path, group/sub/api)
repository.owner string no acme
repository.name string no api
repository.url string no https://github.com/acme/api
repository.defaultBranch string event runs: no, it comes from the webhook; schedule and webhook runs: yes, until Forkline has synced the repository main
repository.private boolean no false (GitLab: internal repositories count as private)

sender (every event except ci.failed)

Field Type May be empty Example
sender.login string only when the forge sent no sender alice
sender.isBot boolean no true for renovate[bot]

sender.isBot and pr.authorIsBot are true when the forge marks the account as a bot (GitHub and Forgejo user type Bot), or the login ends in [bot], _bot or -bot (any case), or looks like a GitLab project or group token bot (project_12_bot_ab12, group_3_bot_zz). On push the sender is the forge’s sender, or the pusher when there is none.

pr (pr.* events, and sometimes comment.created and ci.failed)

Field Type May be empty Example
pr.number integer no 412
pr.title string see notes Add retry to the uploader
pr.body string yes (no description) Fixes #88
pr.author string see notes alice
pr.authorIsBot boolean no (false on GitLab except when the merge request is first opened) false
pr.baseBranch string see notes main
pr.headBranch string see notes feat/retry
pr.headSha string see notes 9fceb02d0ae598e95dc970b74767f19372d61af8
pr.labels list of strings yes (empty list) [needs-review, backend]
pr.draft boolean no false
pr.fork boolean no true when the head repository differs from the base repository
pr.url string see notes https://github.com/acme/api/pull/412

Notes:

  • Comment on a pull request (comment.created): on GitHub and Forgejo, issue_comment events do not include the branches, so pr.baseBranch, pr.headBranch and pr.headSha are empty and pr.draft and pr.fork are false. Review comments (pull_request_review_comment, pull_request_comment) carry all fields. The agent can read the branch from the forge when it needs it.
  • ci.failed without a pull request (a run on a branch that has no open pull request, or on GitLab any pipeline that is not a merge request pipeline): there is no pr at all. pr.number is null in when and empty in a template, so when: 'pr.number != null' is false and skips the event with outcome filtered at stage when. Put that guard on every ci.failed routine that is about a pull request. Without a pull request there is no pull request run cap (maxRunsPerPr), and the concurrency group key is the repository (a ci.failed run without a pull request has neither a pull request nor an issue number).
  • ci.failed on a pull request: on GitHub and Forgejo, only pr.number, pr.headSha and, on GitHub, pr.baseBranch and pr.headBranch are filled (on Forgejo the branches are usually empty). pr.title, pr.body, pr.author and pr.labels are empty, pr.draft and pr.fork are false, and pr.url is built from the repository URL, so do not rely on it. On GitLab, only a merge request pipeline has a pr. It fills pr.number (the merge request iid), pr.title, pr.url, pr.headSha, pr.baseBranch, pr.headBranch and pr.fork (true when the source and target projects differ). pr.body, pr.author and pr.labels are empty and pr.draft is false. A pipeline that runs in a project other than the merge request’s target project (a fork’s own pipeline) has no pr.
  • GitLab reports the merge-request author only when it is opened (open, not reopen), so pr.author is empty and pr.authorIsBot is false on a reopened merge request, on pr.updated, pr.merged and pr.closed, and for comments on a merge request. !pr.authorIsBot therefore does not skip bot merge requests there; see Authors and labels.
  • pr.fork is true for a pull request whose head repository is a different repository (or was deleted). GitLab compares the source and target project.

issue (issue.opened, and comment.created on an issue)

Field Type May be empty Example
issue.number integer no 88
issue.title string no Uploader crashes on empty files
issue.body string yes Steps to reproduce ...
issue.author string yes on GitLab comments bob
issue.labels list of strings yes (empty list) [bug]
issue.url string no https://github.com/acme/api/issues/88

comment (comment.created)

Field Type May be empty Example
comment.id integer no 1840221
comment.body string yes /fix-ci please
comment.author string no alice
comment.url string no https://github.com/acme/api/pull/412#issuecomment-1840221
comment.mentionsBot boolean no true for @forkline please review
comment.command object with name and args name and args are empty strings when there is no command { name: "fix-ci", args: "please" }
  • Slash command. Forkline looks at the first line that starts, after optional spaces, with /, a letter, and then letters, digits, - or _, followed by a space or the end of the line. Lines such as /usr/bin or /1 not are skipped. comment.command.name is the name in lower case without the slash (/Fix-CI gives fix-ci); comment.command.args is the rest of that line, trimmed, at most 1,024 bytes. Later command lines are ignored. A line inside a code block counts like any other line.
  • Mentions. comment.mentionsBot is true when the text contains @forkline or @forkline-bot (any case) as a whole word. It is not triggered by an e-mail address (a@forkline.dev) or a longer name (@forklinex).
  • Flat names in when. In a template, use comment.command.name and comment.command.args. In a when condition the same values are the flat strings comment.commandName and comment.commandArgs; comment.command does not exist there.

ci (ci.failed)

Field Type May be empty Example
ci.name string no CI (GitLab: pipeline unless the pipeline has a name)
ci.runId string no 10847722115
ci.url string no https://github.com/acme/api/actions/runs/10847722115
ci.conclusion string no failure or timed_out (GitHub, Forgejo); failed (GitLab)
ci.headSha string no 9fceb02d0ae598e95dc970b74767f19372d61af8
ci.branch string yes, on Forgejo when the run belongs to a pull request feat/retry
ci.failures list of objects yes on GitLab (no failed job reported); never on GitHub and Forgejo see below

Each entry of ci.failures has exactly four fields: name, runId, url and conclusion. There are no logs: the agent fetches logs itself, for example with the git-api tool in the runner.

  • GitHub and Forgejo: the webhook does not list jobs, so ci.failures has one entry, the failed run itself.
  • GitLab: ci.failures lists the failed jobs of the pipeline (up to 100), without jobs that are allowed to fail. Each job has conclusion failed and a URL of the form {repository}/-/jobs/{id}.
  • ci.branch is the branch the run was on. On GitLab it is the pipeline’s ref, which can be a tag name.

push (push)

Field Type May be empty Example
push.ref string no refs/heads/main
push.branch string no main
push.before string no 0f5e9d7c1b2a (all zeros for a new branch)
push.after string no 9fceb02d0ae598e95dc970b74767f19372d61af8
push.forced boolean no false (always false on GitLab)
push.commitCount integer no 3
push.files.added list of strings yes [docs/guide.md]
push.files.modified list of strings yes [src/main.rs]
push.files.removed list of strings yes [old.txt]
push.filesTruncated boolean no true when the lists are incomplete

The three file lists hold the paths from every commit in the webhook, each path once per list. push.filesTruncated is true when the forge listed fewer commits than were pushed, or when the push has more than 3,000 paths: the lists are then a prefix of the truth. In a when condition each list holds at most 500 paths, and push.filesTruncated is then true; the paths filter sees all of them.

Fields only templates have

Templates also read event.name, event.forge (github, gitlab or forgejo), event.deliveryId (the forge’s delivery ID, at most 200 bytes) and receivedAt; when cannot. See Prompt templates.

Which filters apply to which event

Each filter applies to a fixed set of events, and every filter you set must apply to every event you list. This table is generated from the validator: yes means the filter applies.

Event branches paths labels authors draft fork bots self command when
pr.opened yes no yes yes yes yes yes yes no yes
pr.updated yes no yes yes yes yes yes yes no yes
pr.merged yes no yes yes yes yes yes yes no yes
pr.closed yes no yes yes yes yes yes yes no yes
issue.opened no no yes yes no no yes yes no yes
comment.created no no yes yes no no yes yes yes yes
ci.failed yes no no no no no no no no yes
push yes yes no yes no no yes yes no yes

Combining events narrows the choice to the columns that are yes for all of them. For example, [pr.opened, push] allows branches, authors, bots, self and when; [pr.opened, ci.failed] allows branches and when only. See Filters for what each filter does, and the invalid examples there for the error.

Reserved events

These names are planned. They are rejected when you save, so a routine cannot be written against them yet.

Name Planned meaning
pr.conflicted A pull request has a merge conflict with its base branch
ci.succeeded A CI run passed
pr.labeled A label was added to a pull request
issue.labeled A label was added to an issue
issue.assigned An issue was assigned
pr.review_requested A review was requested on a pull request

The error is 422 at /on/events/{index} with the message `pr.labeled` is not supported yet (the name you wrote). Other event-list errors:

Mistake Pointer Message
Misspelled or unknown name /on/events/{index} Unknown event `pr.open`. Did you mean `pr.opened`? Events: `pr.opened`, `pr.updated`, `pr.merged`, `pr.closed`, `issue.opened`, `comment.created`, `ci.failed`, `push`.
Same name twice /on/events/{index} `push` is listed twice
Empty list or more than 8 /on/events list between 1 and 8 events

Until a reserved event exists, approximate it:

# pr.labeled is not available: run on every update and test the label in `when`
filters:
  when: '"needs-review" in pr.labels'
name: label-review
description: A reserved event name is refused
on:
  events: [pr.labeled]
repositories:
  include: all
agent:
  prompt: Review pull request {{ pr.number }}.
name: misspelled-event
description: Event names are exact
on:
  events: [pr.opened, pr.open]
repositories:
  include: all
agent:
  prompt: Review pull request {{ pr.number }}.

Schedule and webhook triggers

A routine can be started by something other than an event: on.schedule (a cron expression or a single time) or on.webhook (a custom webhook URL). These have no forge event, so there is no sender, no pr, no issue, no comment, no ci and no push.

Trigger What the prompt can read
on.schedule repository.*, event.name (schedule), schedule.scheduledAt, receivedAt (the scheduled time)
on.webhook repository.*, event.name (webhook), receivedAt, and the JSON body of the call as payload.* (non-JSON bodies as payload.raw_body)

Run now on an event routine replays a recent stored event instead; see Run now.

The source of a run is event (a forge webhook), schedule, webhook (a call to the custom webhook URL) or manual (Run now); a manual run gives the prompt the same data as a webhook call. Both start one run per selected repository. The only filter they accept is when, and it can read repository (and, on a webhook routine, payload). See Prompt templates for the variables and Repository selection for how a run fans out over repositories.

name: weekly-dependency-update
description: Update dependencies every Monday morning
on:
  schedule:
    cron: "0 6 * * 1"
    timezone: Europe/Berlin
repositories:
  include:
    - name: "acme/*"
filters:
  when: '!repository.private'
agent:
  prompt: |
    Update the dependencies of {{ repository.fullName }}.
    This run was scheduled for {{ schedule.scheduledAt }}.

Recent events and privacy

Forkline records a short summary of every event it receives, so you can see what arrived and what each routine decided. The summary is one line of metadata per event (titles are cut to 200 characters, and a summary keeps at most 20 labels and 10 CI failures): the event name, the forge, the repository, the actor, the numbers, titles, branches and labels of the pull request or issue, the comment’s author and command name, the CI conclusion and failed run names, and the push ref and commit count. It never contains pull request or issue bodies, comment text, command arguments, file lists, logs or the webhook payload.

Event summaries (up to 4 KiB of metadata, no bodies or comment text) are kept for 7 days, for every event of every connected repository, and each owner keeps at most 5,000. Older ones are deleted. Over 4 KiB, labels and failures are dropped from the end of the summary and it is marked truncated. See Testing and activity for how to browse recent events, test a routine against one, and backtest it.

Examples

A routine for failed CI on pull requests, written for the one event whose fields it reads:

name: fix-failed-ci
description: Fix CI when a pull request run fails
on:
  events: [ci.failed]
repositories:
  include: all
filters:
  branches:
    exclude: [main]
  when: 'ci.failures.size() > 0'
agent:
  prompt: |
    The run {{ ci.name }} failed on {{ ci.branch }} ({{ ci.headSha }}).
    {% for f in ci.failures %}Failed: {{ f.name }} {{ f.url }}
    {% endfor %}
    Fetch the logs, fix the failure and push to the branch.

A routine that reacts to a slash command on pull requests and issues alike:

name: fix-command
description: Run on the /fix command
on:
  events: [comment.created]
repositories:
  include: all
filters:
  command:
    names: [fix]
agent:
  prompt: |
    {{ comment.author }} asked for a fix: {{ comment.command.args }}
    Comment: {{ comment.url }}