GitHub Actions is a CI/CD platform built directly into GitHub. You define workflows in YAML files, and GitHub runs them on every push, pull request, or schedule you configure.
How GitHub Actions Works
Every GitHub Actions automation is defined by a workflow file stored in .github/workflows/ in your repository. When a triggering event occurs (like a push), GitHub spins up a virtual machine, checks out your code, and executes the steps you defined.
Key Terminology
| Term | Meaning |
|---|---|
| Workflow | A YAML file that defines an automation |
| Event | What triggers the workflow (push, PR, schedule) |
| Job | A set of steps that run on the same machine |
| Step | An individual task within a job |
| Action | A reusable unit of code (from the marketplace or custom) |
| Runner | The virtual machine that executes the job |
Your First Workflow
Create .github/workflows/hello.yml:
name: Hello World
on:
push:
branches: [main]
jobs:
greet:
runs-on: ubuntu-latest
steps:
- name: Say hello
run: echo "Hello from GitHub Actions!"
- name: Show system info
run: |
echo "Runner OS: $RUNNER_OS"
echo "Date: $(date)"
echo "User: $(whoami)"Push this file to your repository, and GitHub will automatically run the workflow.
Workflow Triggers (on)
The on key defines what events trigger the workflow:
# Trigger on push to main
on:
push:
branches: [main]
# Trigger on pull requests to main
on:
pull_request:
branches: [main]
# Trigger on both push and PR
on:
push:
branches: [main]
pull_request:
branches: [main]
# Trigger on a schedule (cron syntax)
on:
schedule:
- cron: "0 6 * * 1" # Every Monday at 6 AM UTC
# Trigger manually from the GitHub UI
on:
workflow_dispatch:
# Trigger on tag creation
on:
push:
tags:
- "v*"You can filter by file paths to only run when relevant files change:
on:
push:
branches: [main]
paths:
- "src/**"
- "package.json"
- ".github/workflows/**"
paths-ignore:
- "docs/**"
- "*.md"Jobs
Jobs define what work to perform. By default, jobs run in parallel. Use needs to make them sequential:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Build the app
run: echo "Building..."
test:
runs-on: ubuntu-latest
needs: build # Waits for build to finish
steps:
- name: Run tests
run: echo "Testing..."
deploy:
runs-on: ubuntu-latest
needs: [build, test] # Waits for both
steps:
- name: Deploy
run: echo "Deploying..."Runners
Runners are the machines that execute your jobs. GitHub provides hosted runners:
| Runner | OS |
|---|---|
ubuntu-latest | Ubuntu Linux (most common) |
ubuntu-22.04 | Ubuntu 22.04 specifically |
windows-latest | Windows Server |
macos-latest | macOS |
jobs:
linux-job:
runs-on: ubuntu-latest
steps:
- run: echo "Running on Linux"
windows-job:
runs-on: windows-latest
steps:
- run: echo "Running on Windows"Steps
Steps are the individual tasks within a job. Each step either runs a shell command or uses a pre-built action:
steps:
# Run a shell command
- name: Print greeting
run: echo "Hello"
# Run multiple commands
- name: Setup environment
run: |
echo "Installing dependencies"
npm install
echo "Done"
# Use a pre-built action
- name: Checkout code
uses: actions/checkout@v4
# Use an action with inputs
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"Essential Actions
These actions are used in almost every workflow:
steps:
# Checkout your repository code
- uses: actions/checkout@v4
# Setup Node.js
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "pnpm"
# Setup Python
- uses: actions/setup-python@v5
with:
python-version: "3.12"
# Cache dependencies
- uses: actions/cache@v4
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
# Upload build artifacts
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/Environment Variables
Set environment variables at different levels:
# Workflow-level (available to all jobs)
env:
NODE_ENV: production
jobs:
build:
runs-on: ubuntu-latest
# Job-level (available to all steps in this job)
env:
CI: true
steps:
# Step-level
- name: Build
env:
API_URL: https://api.example.com
run: |
echo "NODE_ENV: $NODE_ENV"
echo "CI: $CI"
echo "API_URL: $API_URL"Secrets
Never hardcode sensitive values. Use GitHub Secrets instead:
steps:
- name: Deploy
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY_ID }}
run: ./deploy.shAdd secrets in your repository: Settings > Secrets and variables > Actions > New repository secret.
Expressions and Context
GitHub Actions provides context objects and expressions:
steps:
- name: Show context info
run: |
echo "Repository: ${{ github.repository }}"
echo "Branch: ${{ github.ref_name }}"
echo "Commit: ${{ github.sha }}"
echo "Actor: ${{ github.actor }}"
echo "Event: ${{ github.event_name }}"
echo "Runner OS: ${{ runner.os }}"Conditional steps:
steps:
- name: Deploy to production
if: github.ref == 'refs/heads/main'
run: ./deploy-prod.sh
- name: Deploy to staging
if: github.ref == 'refs/heads/develop'
run: ./deploy-staging.sh
- name: Only on pull request
if: github.event_name == 'pull_request'
run: echo "This is a PR"A Complete CI Workflow
Here is a practical workflow that checks out code, installs dependencies, lints, and tests:
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "pnpm"
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Lint
run: pnpm lint
- name: Type check
run: pnpm tsc --noEmit
- name: Run tests
run: pnpm test
- name: Build
run: pnpm buildViewing Workflow Results
After pushing a workflow, go to the Actions tab in your repository to see:
- A list of all workflow runs
- The status of each job (success, failure, in progress)
- Detailed logs for each step
- Duration and resource usage
A green checkmark means the workflow passed. A red X means it failed. Click into a run to see the logs and identify what went wrong.
Summary
GitHub Actions workflows are YAML files that automate tasks on events like pushes and pull requests. You learned about triggers, jobs, steps, runners, environment variables, and secrets. In the next lesson, you will build a real CI pipeline with testing, linting, and build verification.