Build Job on Push Events
Run a build job on push events — CI/CD foundations tutorial, lesson 5. Learn hands-on steps, troubleshooting, and what to study next.
Focus: run a build job on push events
You just pushed a commit, and nothing happened. No tests ran, no build kicked off, no feedback loop — just a silent remote and a lingering 'did I break anything?' feeling in your gut. That silence is the number one sign you haven't wired up a CI/CD trigger yet. This lesson shows you how to close that gap by configuring a workflow that runs a build job on push events — the backbone of every modern continuous integration pipeline.
The problem this lesson solves
Without an automatic trigger, your build only runs when you remember to run it — and in a team, that means it runs whenever someone happens to think of it. The result? Broken main branches, merge conflicts that surface days late, and a deployment process that depends on tribal knowledge instead of tooling.
The core problem is reliability through automation. A push event is the earliest, most natural signal that new code exists. By tying your build to that event, you turn every commit into a potential quality checkpoint. No more "works on my machine" — the pipeline becomes the source of truth.
This lesson teaches you to:
- Explain the core idea behind running a build job on push events
- Complete a practical exercise that wires a real GitHub Actions workflow
- Connect this trigger to the broader CI/CD foundations track, including pipeline anatomy, artifacts, and promotions
Core concept / mental model
Think of a push event as a knock on the door. Your CI system is the butler who hears that knock and immediately starts executing a predefined checklist. The checklist is your workflow file, and each step in the checklist is a job.
In GitHub Actions, three components work together:
- Event — the trigger (here,
push) - Workflow — a YAML file that defines jobs and steps
- Runner — the machine that executes the steps (can be GitHub-hosted or self-hosted)
Here's the mental model in words: the push event fires after you run git push. GitHub sees it, matches it against workflow files in the repository, and for each matching workflow it spawns a virtual machine (the runner). That runner clones your code, runs the commands you specify, and reports the results back to the GitHub UI.
Pro tip: You can think of the workflow file as a recipe. The event is the occasion, the jobs are the dishes, and the steps are the instructions. You can have multiple recipes (workflows) for the same occasion, but each one runs independently.
The key insight: the push event is the when; the workflow is the what. You can attach any number of workflows to the same event, and you can filter events by branch, tags, or paths.
Here's a minimal YAML mental model to keep in your head:
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
# do something
That's it — three key parts. The rest is details (and you'll add many of them soon).
How it works step by step
Step 1: Create the workflow file
GitHub Actions looks for YAML files in the .github/workflows/ directory. The filename doesn't matter, but it's conventional to name it after the purpose, e.g., build.yml.
Step 2: Define the trigger
The on: key is the event definition. The simplest form is on: push, which triggers the workflow on every push to any branch. But you can refine it:
on:
push:
branches:
- main
This limits the trigger to pushes on main only — a common pattern for production builds.
Step 3: Define jobs
A workflow can contain multiple jobs. Each job runs on its own runner and can depend on other jobs (we'll cover dependencies in a later lesson). For now, keep it simple with a single build job.
Step 4: Add steps
Inside a job, you define an ordered list of steps. Each step either runs a shell command or uses an action (a reusable piece of code). The first step is almost always actions/checkout, which clones your repository onto the runner.
Step 5: Commit and push
When you push the workflow file to your repository, GitHub picks it up and executes it. Any subsequent push to the target branch re-triggers the workflow.
The cause-and-effect chain is: git push → GitHub receives it → event fires → workflow matches → runner starts → jobs execute → status reported.
Pro tip: You can also trigger workflows manually with
workflow_dispatch, but that defeats the purpose of automation. Usepushas your default unless you have a specific reason not to.
Hands-on walkthrough
Prerequisites
- A GitHub repository (public or private)
- Git installed locally
- Basic command-line comfort
We'll use a small Python project as our example, but the pattern works for any language.
Step 1: Create a minimal workflow
Create a directory .github/workflows/ in your repo and add a file named build.yml with the following content:
name: Build
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
- name: Run tests
run: pytest
This workflow does four things:
- Checks out your code
- Sets up Python 3.11
- Installs
pytest - Runs the tests
If your project doesn't have tests yet, you can create a simple test file to see the workflow in action.
Step 2: Add a sample test
Create test_sample.py in the root of your repo:
def test_always_passes():
assert True
def test_addition():
assert 1 + 1 == 2
Now your repo should look like:
.
├── .github
│ └── workflows
│ └── build.yml
└── test_sample.py
Step 3: Push and watch it run
git add .
git commit -m "Add CI workflow"
git push origin main
Navigate to the Actions tab in your GitHub repository. You should see a workflow run with the name "Build." Click on it to watch the live logs.
Expected output:
- The build job shows a green checkmark
- The log for each step shows success
- The Run tests step shows output like 2 passed in 0.02s
If you see a red ✗, don't panic — head to the Troubleshooting section below.
Step 4: Trigger it again
Make a small change (e.g., edit test_sample.py), commit, and push. Notice that the workflow runs automatically — no manual clicks. That's the magic of push events.
Compare options / when to choose what
Not all CI triggers are created equal. Here's how push stacks up against other common triggers:
| Trigger | Best for | Drawbacks |
|---|---|---|
push |
Getting fast feedback on any code change | Can run on every commit, which may be wasteful |
pull_request |
Checking code before merge | Doesn't run until a PR is opened |
workflow_dispatch |
Manual retries / ad-hoc runs | Requires human action |
schedule (cron) |
Nightly builds, dependency checks | Not immediate |
push + branch filter |
Protecting main from bad code |
Misses features branches |
In practice, you'll often combine triggers to get the best coverage.
Pro tip: Use
pushfor your main branch andpull_requestfor feature branches. That way you get fast feedback on PRs without cluttering the main branch's build history.
Another dimension to compare: where do you run the job? GitHub-hosted runners are simple and free for public repos, but they have limits. Self-hosted runners give you more control (custom hardware, caching) but require maintenance.
Variations of the push event
You can filter by branches, tags, and paths to fine-tune when your build runs. For example:
on:
push:
paths:
- 'src/**'
- '!docs/**'
This runs the workflow only when files under src/ change, and ignores documentation-only pushes.
Troubleshooting & edge cases
Error: Workflow not appearing / not running
Symptom: You push the workflow file, but the Actions tab shows nothing.
Cause: The file might be in the wrong location or have an invalid YAML syntax.
Fix:
- Ensure the file is exactly at
.github/workflows/build.yml(not.github/build.yml) - Check for trailing spaces or tabs in YAML
- Use a YAML linter locally
Error: No such file or directory when running pytest
Symptom: The step fails with pytest: command not found.
Cause: The Python environment isn't set up correctly, or the runner uses a different Python version.
Fix: Make sure you added the actions/setup-python step before the pip install step, and double-check the Python version.
Workflow runs but tests fail unexpectedly
Symptom: The job fails with a test assertion error, but passes locally.
Cause: Different platform or dependency versions.
Fix: Pin your dependency versions in requirements.txt and use the same Python version locally as in CI.
Common mistakes
- Wrong trigger syntax —
on: [push]is valid, buton: { push }is not - Forgetting
actions/checkout— without it, your CI runner has no code to build - Indentation errors in YAML (e.g., mixing tabs and spaces)
- Running
pushon every branch when you only meantmain— use branch filters - Not committing the workflow file — ensure the
.githubdirectory isn't in.gitignore
What you learned & what's next
You now understand the foundational trigger of CI: how to run a build job on push events. You learned:
- The three pillars: event → workflow → runner
- How to structure a YAML workflow file
- How to filter pushes by branch, tag, or path
- How to debug common workflow issues
This is the heartbeat of CI. Without it, there's no automation — and no foundation for the advanced topics ahead.
The next lesson in this track dives into pipeline anatomy, where you'll learn to break your build into multiple jobs and stages — how to run tests, package artifacts, and pass them between steps. That's where these push-triggered builds start to earn their keep.
Pro tip: Bookmark this lesson. You'll come back to the trigger syntax more times than you think — it's the entry point for every workflow you'll ever write.
Practice recap
Create a new GitHub repository, add a simple Python file with a passing test, and write a workflow that runs on push to main. Then push a small change and watch the workflow execute automatically. Commit the workflow file first, then push a code change to see the trigger in action.
Common mistakes
- Using
on: [push]but then wondering why the workflow doesn't run on tag pushes —pushincludes all pushes, so you might be triggering more often than expected. - Forgetting to add
actions/checkoutas the first step — your runner will have no code to build, and every subsequent step fails with 'No such file or directory'. - Indenting YAML with tabs instead of spaces — GitHub Actions will reject the file with a parsing error.
- Pushing the workflow file and then immediately editing it in the GitHub UI — the push event fires before your edit, so the old version runs.
- Assuming
pushfires on pull request merges — it does, but only on the merge commit to the target branch, not on every PR update.
Variations
- Use
pull_requesttriggers alongsidepushto get earlier feedback on feature branches before they hitmain. - Try
schedulewith cron syntax for nightly builds that run even when no one pushes. - Experiment with
workflow_dispatchto manually trigger a build on demand — perfect for debugging or ad-hoc retries.
Real-world use cases
- A developer pushes a commit to a feature branch, and CI automatically runs unit tests and linting, flagging errors within minutes.
- A team merges a PR into
main, triggering a production build that compiles the app and uploads it as an artifact for deployment later. - An open-source project uses a push-triggered workflow to build docs site on every commit, ensuring the latest content is always live.
Key takeaways
- The
on: pushtrigger is the simplest way to run CI automatically on every code push. - A workflow file defines the event, the jobs, and the steps that execute on a runner.
- You control triggers with filters for branches, tags, and paths — use them to avoid wasteful or overly broad builds.
- Always start your job with
actions/checkoutto clone your repository onto the runner. - Watch the Actions tab in GitHub to see live logs and debug failures step by step.
- Mastering push triggers lays the foundation for understanding more complex CI components like artifacts and promotions.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.