Intro to GitHub Actions (CI/CD)
Reviewed & published by Brayan K
Stop running tests by hand. By the end of this lesson you'll write a workflow YAML file that runs your test suite automatically on every push and pull request — your first taste of continuous integration, built right into GitHub.
Part of the free Git course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
What You'll Learn
- What CI/CD is and how GitHub Actions provides it
- Where workflow YAML files live: .github/workflows/
- The core keys: name, on, jobs, runs-on, steps
- The difference between uses (an action) and run (a command)
- Why actions/checkout is the usual first step
- Run tests across versions with a build matrix, and find actions in the Marketplace
1️⃣ Your First Workflow
A workflow is a YAML file in .github/workflows/. This one runs your tests on every push to main and on every pull request. Commit it, push, and the run shows up in the repo's Actions tab.
# .github/workflows/ci.yml
# A workflow is a YAML file in .github/workflows/. This one runs your tests
# automatically every time someone pushes or opens a pull request.
name: CI # shown in the Actions tab
on: # WHICH events start this workflow
push:
branches: [ main ] # pushes to main
pull_request: # and any pull request
jobs: # one or more jobs (run in parallel by default)
test: # a job id you choose
runs-on: ubuntu-latest # the runner (a fresh virtual machine)
steps: # ordered steps inside the job
- uses: actions/checkout@v4 # 1) check out your repo onto the runner
- uses: actions/setup-node@v4 # 2) install Node from the Marketplace
with:
node-version: '20'
- run: npm ci # 3) install dependencies
- run: npm test # 4) run the test suite2️⃣ The Anatomy of a Workflow
Every workflow shares the same skeleton: name, the triggering events under on, and jobs. Each job sets runs-on (the runner) and a list of steps. A step either uses a prebuilt action or runs shell commands.
# The vocabulary, top to bottom:
#
# name: a label for the workflow (optional)
# on: the EVENTS that trigger it (push, pull_request, schedule, ...)
# jobs: a map of jobs; each runs on its own fresh runner
# <job>:
# runs-on: which runner OS/image (ubuntu-latest, windows-latest, ...)
# steps: an ordered list; each step is EITHER:
# - uses: <action> pull in a reusable action
# - run: <command> run shell commands on the runner
#
# 'uses' pulls prebuilt actions (e.g. actions/checkout@v4) from the
# GitHub Marketplace; 'run' executes plain shell commands like npm test.3️⃣ Testing Across Versions (Matrix)
A matrix runs the same job several times in parallel — once per value you list. It's the standard way to prove your code works on multiple language versions at once, with one small block of YAML.
# A "matrix" runs the SAME job across several versions in parallel — great
# for testing your code on multiple language versions at once.
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 18, 20, 22 ] # three parallel runs
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm testYour turn. Complete a minimal CI workflow. Fill in the three blanks, then commit it as .github/workflows/ci.yml.
# 🎯 YOUR TURN — replace each ___ then commit this as
# .github/workflows/ci.yml and push to trigger it.
name: CI
___: # 👉 the key that lists triggering events
push:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest # the runner
steps:
- ___: actions/checkout@v4 # 👉 the keyword that pulls in an action
- ___: echo "Running tests" # 👉 the keyword that runs a shell command
# ✅ Expected: after pushing, the Actions tab shows a "CI" run that checks
# out your code and prints "Running tests", finishing with a green check.Common Errors (and the fix)
- Workflow never runs: the file must be in .github/workflows/ (exact path) and end in .yml/.yaml. Check the path and that you pushed it.
- "Invalid workflow file": YAML is indentation-sensitive — use spaces, not tabs, and keep keys aligned. The Actions tab shows the exact line.
- Steps fail with "file not found": you forgot actions/checkout. The runner is empty until you check out your code first.
- "command not found" (e.g. npm): the tool isn't installed on the runner. Add a setup action like actions/setup-node before using it.
- Trigger never fires: your on: branches don't match. If you push to dev but only listed main, nothing runs.
Pro Tips
- 💡 Pin action versions: use @v4 tags so a surprise update doesn't break your build.
- 💡 Start from a template: the Actions tab suggests starter workflows for your language.
- 💡 Cache dependencies: setup actions often have a cache option to speed up runs.
- 💡 Add a status badge: drop the workflow's badge in your README so the build status is visible.
📋 Quick Reference
| YAML key | Purpose |
|---|---|
| name: | Label shown in the Actions tab |
| on: | Events that trigger the workflow |
| jobs: | One or more jobs to run |
| runs-on: | The runner OS for a job |
| uses: | Pull in a reusable action |
| run: | Execute shell commands |
Mini-Challenge: Add CI to a Project
No file given this time — just the plan. Wire up real continuous integration and watch it pass, then fail.
# 🎯 MINI-CHALLENGE: add CI to a project.
# No file given — just the plan. Do it in a real repo on GitHub.
#
# 1. Create the folder .github/workflows/ and a file ci.yml inside it
# 2. Trigger on push to main AND on pull_request
# 3. Use a job on ubuntu-latest with these steps:
# - actions/checkout@v4
# - set up your language (e.g. actions/setup-node@v4)
# - install deps, then run your test command
# 4. Commit, push, and open the Actions tab to watch it run
# 5. Break a test on purpose and confirm the run turns red
#
# ✅ Expected: green check when tests pass, red X (with the failing step
# highlighted) when they don't.🎉 Lesson Complete!
- ✅ GitHub Actions is built-in CI/CD driven by YAML workflows
- ✅ Workflows live in .github/workflows/
- ✅ on sets triggers; jobs run on a runs-on runner with steps
- ✅ uses pulls a Marketplace action; run executes commands
- ✅ actions/checkout is the usual first step
Practice quiz
What is GitHub Actions?
- A code editor
- A merge strategy
- GitHub's built-in CI/CD system that runs workflows on events
- A branch type
Answer: GitHub's built-in CI/CD system that runs workflows on events. GitHub Actions automates building, testing, and deploying code in response to repository events.
Where must workflow files live in your repo?
- In .github/workflows/
- In the repo root
- In .git/hooks/
- In /actions/
Answer: In .github/workflows/. Workflow YAML files go in the .github/workflows/ directory of your repository.
What format are workflow files written in?
- JSON
- TOML
- XML
- YAML
Answer: YAML. GitHub Actions workflows are YAML files (.yml or .yaml).
What does the top-level 'on:' key define?
- The operating system
- The events that trigger the workflow (e.g. push, pull_request)
- The output
- The owner
Answer: The events that trigger the workflow (e.g. push, pull_request). 'on' lists the events — like push or pull_request — that start the workflow.
What is a 'job' in a workflow?
- A set of steps that run on one runner machine
- A single command
- A commit
- A secret
Answer: A set of steps that run on one runner machine. A job groups steps that execute together on a runner; jobs can run in parallel by default.
What does 'runs-on: ubuntu-latest' specify?
- The programming language
- The branch
- The runner (virtual machine) the job executes on
- The schedule
Answer: The runner (virtual machine) the job executes on. runs-on chooses the runner OS/image, such as ubuntu-latest, for that job.
What does the 'uses:' keyword in a step do?
- Runs a shell command
- Pulls in a reusable action (e.g. actions/checkout@v4)
- Sets an environment variable
- Declares a job
Answer: Pulls in a reusable action (e.g. actions/checkout@v4). 'uses' references a prebuilt action from the Marketplace or a repo, like actions/checkout.
What does the 'run:' keyword do in a step?
- Imports an action
- Triggers the workflow
- Creates a job
- Executes shell commands on the runner
Answer: Executes shell commands on the runner. 'run' executes shell commands directly, e.g. run: npm test.
Why is actions/checkout usually the first step?
- It deploys the app
- It checks out your repository code onto the runner so later steps can use it
- It installs Node
- It opens a PR
Answer: It checks out your repository code onto the runner so later steps can use it. The runner starts empty, so actions/checkout fetches your code before tests can run.
Where do you find reusable, prebuilt actions?
- The Issues tab
- The Wiki
- The GitHub Marketplace
- The Releases page
Answer: The GitHub Marketplace. The GitHub Marketplace hosts thousands of prebuilt actions you reference with 'uses'.
Continue this course
- Previous: Forking & Contributing to Open Source
- Next: Submodules & Monorepos — Embed one repo inside another with submodules — and when to avoid them
- Quick reference: Git cheat sheet
Frequently asked questions
What is CI/CD, and how does GitHub Actions fit in?
CI (continuous integration) means automatically building and testing every change so problems surface immediately; CD (continuous delivery/deployment) extends that to automatically releasing or deploying. GitHub Actions is GitHub's built-in automation system for both: you describe workflows in YAML, and GitHub runs them on its servers whenever chosen events occur, like a push or a pull request.
Where do workflow files go and what format are they?
They live in the .github/workflows/ directory at the root of your repository, and they're written in YAML (.yml or .yaml). You can have many workflow files; GitHub reads them all. Each defines a name, the events that trigger it (on:), and one or more jobs with steps.
What's the difference between 'uses' and 'run' in a step?
A step does exactly one of the two. 'uses' pulls in a reusable, prebuilt action — for example uses: actions/checkout@v4 to fetch your code, or a Marketplace action to set up a language. 'run' executes shell commands directly on the runner, like run: npm test. You'll typically combine a few 'uses' steps for setup with 'run' steps for your own commands.
Why is actions/checkout almost always the first step?
Each job runs on a fresh, empty runner (a virtual machine) that doesn't have your code yet. actions/checkout clones your repository onto that runner so the following steps — installing dependencies, running tests — actually have something to work with. Forget it, and your build commands fail because the files aren't there.
What is the GitHub Marketplace?
It's a catalog of thousands of reusable actions published by GitHub and the community. Instead of scripting common tasks yourself, you reference one with 'uses', like actions/setup-node@v4 to install Node or actions/upload-artifact to save build output. Pin to a version tag (the @v4 part) so your workflow stays stable when the action updates.