Git, CI/CD & GitOpsXXXVII · CI FundamentalsCI Fundamentals
The trigger to result pipeline — what fires a CI run
What you'll learn
- List the four canonical trigger categories for a CI run
- Trace a trigger from Git event to runner checkout to commit status
- Choose the correct trigger for each of build, test, deploy, and nightly jobs
- Recognise the difference between push events and pull_request events in branch-protection context
Prerequisites
None — start here.
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
A CI run has a beginning and an end, and the shape between them is the same on every forge. The beginning is a trigger - a piece of metadata the runner receives from the forge. The end is a result - a binary status published against the commit that fired the trigger. Everything between is the pipeline file: a declarative description of jobs and steps the runner will execute. This lesson is about the two ends: what fires a run, and what comes back.
The four trigger categories
Every CI trigger belongs to one of four categories:
flowchart TB
subgraph TRIGGERS["Trigger sources"]
T1["git push\n(push event)"]
T2["pull request\n(opened, synchronised, closed)"]
T3["Schedule\n(cron)"]
T4["Manual\n(gh workflow run,\nAPI call, UI button)"]
end
TRIGGERS --> R["Runner\n(checkout + steps)"]
R --> S["Commit status\n(success / failure / cancelled)"]
-
Push events. A commit lands on a branch or a tag. The forge fires the push event; the runner receives the commit SHA, the ref, and the repository URL. The trigger is the canonical “code just arrived” signal.
git commit -m "feat(terraform): add logs bucket" git push origin main -
Pull request events. Opening, synchronising (new commits pushed to the branch), reopening, or closing a pull request fires the event. The forge sends the PR number, the head SHA, and the base ref. Crucially, the runner checks out the merge ref - a synthetic SHA produced by merging the head into the base - so the pipeline runs against the change as it would appear if merged.
-
Scheduled events. A cron expression in the pipeline file fires the trigger at a fixed time. Nightly builds, dependency-update jobs, drift-detection scans all use this trigger. There is no commit; the trigger is purely temporal.
-
Manual events.
gh workflow run, the GitHub API, the GitLab UI button, or apipeline triggertoken. The operator chooses when to fire; the runner receives the inputs the operator passed.
gh workflow run build.yml --ref feature/add-logs-bucket
gh run list --workflow=build.yml --limit 5
The four categories share the same downstream shape: a
GITHUB_SHA / CI_COMMIT_SHA, a ref, a checkout, and a set of
environment variables. The pipeline file does not care which
category fired; it runs the same steps.
From trigger to runner checkout
When the trigger fires, the runner receives a payload that contains at minimum:
- Repository URL and credentials. The runner can clone.
- Commit SHA. The exact bytes the pipeline runs against.
Not
main, notHEAD- the SHA. - Ref. The branch or tag the commit was pushed to.
- Trigger metadata. Which event, who pushed, which PR number.
# What the runner sees in its environment
echo "$GITHUB_SHA" # commit SHA, not a branch name
echo "$GITHUB_REF" # refs/heads/main
echo "$GITHUB_EVENT_NAME" # push | pull_request | schedule | workflow_dispatch
The runner clones the repository at the SHA, cd’s into the working tree, and begins executing steps. Every step sees the same checkout - the only thing that changes between triggers is the SHA and the event metadata.
From runner to result
The runner finishes. The exit code is zero, or it is not. The forge records:
- Commit status. A pass/fail entry against the SHA. The branch protection rule looks at this. The pull request decoration looks at this.
- Artefacts. The container image, the SBOM, the Terraform plan file - each is a blob whose identity is the SHA, stored against the run.
- Logs. Step-by-step output, retrievable by run ID.
gh run watch 1234567890 --exit-status
gh run watch blocks until the run completes and prints the
final state. It is the simplest way for an engineer to follow
a manual trigger end-to-end.
Trigger design rules
flowchart LR
A["What fires this job?"] --> B{"Type of work"}
B -->|build + test| C["pull_request + push\n(both)"]
B -->|deploy| D["push to release branch\nor manual"]
B -->|nightly scan| E["schedule (cron)"]
B -->|incident response| F["manual (workflow_dispatch)"]
- Build and test on every PR and every push to main. Both events, both required to fire, both attaching a status to the relevant SHA.
- Deploy on push to a release branch or on a manual trigger. A deploy that fires on every PR is a deploy that targets the merge ref and is the wrong default.
- Nightly jobs on a schedule. Drift detection, dependency updates, security scans that are too expensive to run on every push.
- Incident-response jobs on a manual trigger. The operator chooses when the job runs; the pipeline receives inputs the operator types.
Production discipline
- Pin to SHA in every step.
GITHUB_SHA/CI_COMMIT_SHAis the reference; branch names are display. - Fire both push and pull_request for required checks. Branch protection looks at the SHA; the pipeline must publish a status against the SHA on both events.
- Manual triggers for deploy, scheduled for batch, push for build. Each trigger category has a job type it fits. Mixing them is how nightly scans end up running on every PR.
Cross-course references
- Linux for Production Sysadmins - Part XXXIV (ConfigMgmt) covers the same trigger taxonomy for apt builds: push to a packaging branch fires the build; manual gate releases.
- Ansible for Production Sysadmins - Part XXXVII (RepoArch) covers the analogous trigger layout for playbook CI: PR fires ansible-lint, merge fires the package build, schedule fires the molecule test matrix.
- Terraform for Production Sysadmins - Parts IX-XII (State) cover the trigger design for plan-on-PR and apply-on-merge.
Quiz
Knowledge check · 4 questions
Q1. An engineer expects a pipeline to attach a required status check to commits on main. The pipeline fires only on pull_request events. What is the visible symptom?
Q2. A pipeline that checks out origin/main inside the job always runs against the same commit that fired the trigger.
Q3. Name the four trigger categories for a CI run, and give one example job type that fits each category.
Q4. Diagnose why a status check that is required by branch protection is reported as missing on a merge.
Branch protection on main requires the check 'build / terraform-plan'. Engineer E opens a PR. The check runs, succeeds, and is visible on the PR. Engineer E pushes a fixup commit directly to the PR branch. The PR's 'Checks' tab still shows the green build. Engineer E clicks Merge; the merge UI warns 'no required checks'.
Passing score: 75%. Answers are checked in this browser.