Chapter 03Lesson 01~105 minutes

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.

Trigger modelEvent identityRefs & SHAsWorkflow selectionNo-run evidence

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 push and pull_request often expose different refs and SHAs for the same repository history.
  • Recognize default-branch-only trigger families such as current schedule, workflow_dispatch, and repository_dispatch behavior.
  • 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

Trigger causality and evidence boundaries
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.
Do not silently substitute SHAs. A tool that scans or signs the PR head is answering a different question from a test that validates the PR merge result. Record which one you intended.

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.

Next lesson

Guided hands-on trigger workflow

Build one probe that exercises push, pull request, manual dispatch, repository dispatch, branch/path filtering, and controlled trigger predictions.

Knowledge check

A workflow file is valid, but a push changes only a path excluded by its trigger. Should you debug the runner first?

Why can github.sha on a pull-request run differ from the PR head commit?

What is the difference between a trigger filter skip and a job if skip?

Why record github.workflow_ref as well as github.sha?

Why not dump the entire github context to logs?

Official references and version notes

Version and compatibility note

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.