Events, Triggers, Filters, Schedules, Manual Runs, and Repository Dispatch: Core Concepts and Mental Model
Chapters 01 and 02 established the event-to-run lifecycle and the
workflow document itself. Chapter 03 now focuses on the selection
boundary: which real-world or API event exists, whether the
workflow's on configuration matches it, which ref and
SHA GitHub associates with that event, which workflow revision is
selected, and whether a workflow run is created at all.
Learning objectives
- Model trigger selection as event production → filter matching → ref/SHA resolution → workflow selection → run creation.
- Distinguish event name and activity type from branch/path filters, job-level conditions, and later runner execution.
-
Explain why
pushandpull_requestoften expose different refs and SHAs for the same repository history. -
Recognize default-branch-only trigger families such as current
schedule,workflow_dispatch, andrepository_dispatchbehavior. - Perform read-only inspection of event, ref, SHA, actor, workflow revision, and run identity without dumping sensitive contexts.
1. A workflow trigger is a selection problem, not a timer attached to YAML
A workflow file can be perfectly valid and still produce no run. GitHub first receives or generates an event. That event has an identity, payload, repository, and—depending on the event—a ref and commit SHA. GitHub then evaluates workflow trigger configuration against that event. Only after those conditions match does a run exist. Job conditions, runner queueing, and step execution happen later.
This ordering gives you a powerful debugging rule: if no run exists, do not start by debugging the runner. Ask whether the event occurred, whether the relevant workflow revision was eligible, and whether trigger filters matched.
2. Mental model: event → filters → revision → run
flowchart TD
A[GitHub event / schedule / API request] --> B[Event name + activity + payload]
B --> C[on configuration]
C --> D{branch / tag / path / type filters match?}
D -- no --> E[No workflow run created]
D -- yes --> F[Resolve event ref + SHA]
F --> G[Select eligible workflow revision]
G --> H[Create run ID / attempt]
H --> I[Evaluate jobs and if conditions]
I --> J[Queue jobs for runners]
Notice where “no run” appears. A trigger filter is upstream of run
creation. By contrast, jobs.<id>.if is
downstream: the run can exist while one or more jobs are skipped.
Those states look different in history and should not be described
with the same word.
3. Event identity has several fields that answer different questions
| Evidence | Question it answers | Example |
|---|---|---|
github.event_name |
Which event family created this run? |
push, pull_request,
workflow_dispatch
|
github.event.action |
Which activity subtype occurred, when the event has one? | opened, synchronize |
github.actor |
Which GitHub actor initiated the event? | user or automation identity |
github.ref |
Which fully qualified ref did the event expose? | refs/heads/main or PR merge ref |
github.sha |
Which commit SHA is GitHub using for the event context? | push commit, PR merge commit, default-branch tip |
github.base_ref/github.head_ref
|
For PR-style events, what are the target/source branch names? | main / feature/docs |
github.workflow_ref |
Which workflow path/ref identity is executing? | repository + path + ref |
github.run_id/github.run_attempt
|
Which persistent run and attempt are you examining? | IDs for evidence correlation |
4. Push and pull-request refs answer different CI questions
A push run naturally describes the branch or tag that
was pushed. For an open pull_request run, GitHub
normally sets GITHUB_REF to the PR merge branch and
GITHUB_SHA to the merge commit for that merge branch.
That lets CI test the result of merging the PR head into the base
branch rather than only the head commit in isolation.
| Trigger | Typical ref/SHA meaning | Useful interpretation |
|---|---|---|
push to main |
refs/heads/main; pushed commit SHA |
Validate the exact state that landed on the branch. |
pull_request |
refs/pull/N/merge; PR merge commit SHA |
Validate merge-result compatibility with the target branch. |
| PR head SHA | github.event.pull_request.head.sha |
Use only when the task explicitly needs the contributor branch's head commit. |
5. The workflow file itself has a revision
GitHub searches .github/workflows at the commit/ref
associated with the event, subject to event-specific rules. A run
therefore has both a source/event revision and a workflow definition
that GitHub selected. For several trigger families, the workflow
must exist on the default branch before GitHub will accept the event
as a workflow trigger. This matters when someone tests a new
workflow_dispatch or scheduled workflow only on an
unmerged feature branch and wonders why no button or run appears.
6. Read-only event probe: inspect selected fields, not the whole context
The following workflow intentionally does not check out code or call the GitHub API. It records only the fields needed to understand trigger identity. Passing values through environment variables also avoids directly interpolating event-derived text into a shell command.
name: Chapter 03 event probe
on:
workflow_dispatch:
permissions: {}
defaults:
run:
shell: bash
jobs:
inspect:
runs-on: ubuntu-24.04
env:
EVENT_NAME: ${{ github.event_name }}
EVENT_ACTION: ${{ github.event.action }}
ACTOR: ${{ github.actor }}
REF: ${{ github.ref }}
SHA: ${{ github.sha }}
BASE_REF: ${{ github.base_ref }}
HEAD_REF: ${{ github.head_ref }}
WORKFLOW_REF: ${{ github.workflow_ref }}
steps:
- name: Print bounded event identity
run: |
printf 'event=%s action=%s actor=%s\n' "$EVENT_NAME" "$EVENT_ACTION" "$ACTOR"
printf 'ref=%s sha=%s\n' "$REF" "$SHA"
printf 'base=%s head=%s\n' "$BASE_REF" "$HEAD_REF"
printf 'workflow_ref=%s\n' "$WORKFLOW_REF"
printf 'run=%s attempt=%s\n' "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT"
Do not print the entire github context as a beginner
shortcut. It is much easier to reason about a small allow-list of
fields, and later security chapters will explain why broad context
dumping is unsafe.
7. Trigger skip versus job skip
| Decision layer | Example | Expected history |
|---|---|---|
| Trigger filter | push.paths does not match |
No workflow run is created for that workflow. |
| Job condition |
if: github.ref == 'refs/heads/main' is false
|
Run exists; the job is recorded as skipped. |
| Step condition | Step-level if false |
Run/job exist; individual step is skipped. |
One subtle governance consequence: when a workflow is skipped by branch/path filtering, checks associated with that workflow can remain pending if branch protection requires them. Trigger design and required-check design must therefore be reviewed together.
8. DevOps operating principle: prove why the run exists
For every production run, you should be able to reconstruct: event name/action, actor, ref/SHA, workflow path/revision, trigger filters, run ID/attempt, and the resulting job graph. That record lets incident responders answer whether the wrong event fired, whether the right event selected the wrong revision, or whether the trigger layer worked and the failure belongs later.
Knowledge check
A workflow file is valid, but a push changes only a path excluded by its trigger. Should you debug the runner first?
No. Trigger filtering happens before run creation, so first verify the event and branch/path match; there may be no runner evidence at all.
Why can github.sha on a pull-request run differ
from the PR head commit?
The normal pull_request context uses the PR merge
branch and its merge commit so CI can validate the merge result.
What is the difference between a trigger filter skip and a job
if skip?
A trigger filter can prevent the workflow run from being created; a job condition is evaluated inside an existing run and records the job as skipped.
Why record github.workflow_ref as well as
github.sha?
The source/event revision and the workflow definition identity are separate evidence; reproducibility requires knowing both.
Why not dump the entire github context to
logs?
It creates unnecessary exposure and noise. Inspect only fields needed for the diagnostic question, and treat event-derived data as untrusted input.
Official references and version notes
- Events that trigger workflows — authoritative event-specific ref/SHA, default-branch, activity-type, schedule, and dispatch semantics.
- Triggering a workflow — branch/path filters, manual inputs, event configuration, and trigger examples.
-
Workflow syntax for GitHub Actions
— current
onsyntax, filter combination rules, schedule syntax, and manual input types. -
Contexts reference
— current
githubcontext fields and safe access patterns. -
Variables reference
— current meanings of
GITHUB_REF,GITHUB_SHA, event path, and run identity variables. - Troubleshooting workflows — current guidance for trigger conditions, default-branch requirements, merge-conflict behavior, and schedule delays.
Version-sensitive trigger behavior was rechecked against current
primary GitHub documentation on 2026-09-09.
Executable labs use a disposable repository,
ubuntu-24.04, built-in shell steps only, and explicit
permissions: {} because no repository API mutation is
required from inside the workflow. At verification time,
workflow_dispatch, repository_dispatch,
and schedule require the workflow file to exist on
the default branch; scheduled workflows run from the latest
default-branch commit, support POSIX cron plus an optional IANA
timezone, and have a minimum five-minute interval but may be
delayed under load. GitHub is continuously delivered, so event
payload fields, limits, schedule behavior, and default-branch
rules must be rechecked before long-lived production use. The
lesson intentionally avoids external actions and repository
mutations so the trigger-selection layer is observable without
additional dependency or permission noise.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.