Skip to main content

GitHub Actions

What is GitHub Actions?

GitHub Actions is a CI/CD (Continuous Integration / Continuous Deployment) platform built directly into GitHub. It lets you automate tasks in response to events in your repository — like running tests whenever code is pushed, or deploying your app when a PR is merged.

You define workflows in YAML files stored in .github/workflows/. GitHub runs them on virtual machines called runners.

Core Concepts

TermDescription
WorkflowAn automated process defined in a YAML file
EventWhat triggers the workflow (push, pull_request, schedule, etc.)
JobA set of steps that run on the same runner
StepAn individual task — run a command or use an action
ActionA reusable unit of work (from GitHub Marketplace or your own)
RunnerThe virtual machine that executes the job

Your First Workflow

Create .github/workflows/ci.yml in your project:

.github/workflows/ci.yml
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"

- name: Install dependencies
run: npm ci

- name: Run tests
run: npm test

This workflow runs on every push to main and every PR targeting main. It checks out your code, sets up Node.js, installs dependencies, and runs your test suite.

Common Events

# Any push to any branch
on: push

# Push to specific branches only
on:
push:
branches: [main, develop]

# Multiple events with filters
on:
push:
branches: [main]
pull_request:
branches: [main] # PRs targeting main
schedule:
- cron: "0 9 * * 1" # every Monday at 9am UTC
workflow_dispatch: # manual trigger from GitHub UI

Practical Workflow: Lint + Test + Build

A realistic Node.js project workflow:

.github/workflows/ci.yml
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
lint-and-test:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [18, 20] # test on multiple Node versions

steps:
- uses: actions/checkout@v4

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "npm"

- run: npm ci

- name: Lint
run: npm run lint

- name: Test
run: npm test -- --coverage

- name: Build
run: npm run build

The matrix strategy runs the same job multiple times with different configurations — here, it tests on both Node 18 and Node 20 simultaneously.

Using Secrets

Never hardcode API keys or passwords in workflows. Store them in GitHub Secrets (Settings → Secrets and variables → Actions) and reference them as environment variables:

steps:
- name: Deploy
env:
API_KEY: ${{ secrets.MY_API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: npm run deploy

Secrets are masked in logs — they'll never appear as plain text.

Caching Dependencies

Speed up workflows by caching node_modules between runs:

- name: Cache node_modules
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-

actions/setup-node with cache: 'npm' does this automatically — you usually don't need to add this separately.

Deploy on Merge

A common pattern: run tests on PRs, deploy when merged to main:

.github/workflows/deploy.yml
name: Deploy

on:
push:
branches: [main]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"

- run: npm ci
- run: npm run build

- name: Deploy to production
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: npx vercel --prod --token $VERCEL_TOKEN

Workflow Status Badge

Add a status badge to your README so everyone can see the CI status at a glance:

![CI](https://github.com/YOUR_USERNAME/YOUR_REPO/actions/workflows/ci.yml/badge.svg)

Useful Marketplace Actions

GitHub Marketplace has thousands of pre-built actions. Some popular ones:

ActionPurpose
actions/checkout@v4Check out repository code
actions/setup-node@v4Set up Node.js
actions/cache@v4Cache files between runs
actions/upload-artifact@v4Save files from a job
actions/download-artifact@v4Download files from another job

Viewing Workflow Runs

Go to your repository on GitHub → Actions tab. You can see:

  • All past and running workflows
  • Logs for each step
  • How long each job took
  • Which jobs failed and why

Click on a failed job to see the exact error output.

Quick Troubleshooting

ProblemFix
Workflow doesn't triggerCheck the on: event and branch name matches exactly
npm ci failsEnsure package-lock.json is committed
Secret not foundVerify the secret name matches exactly (case-sensitive)
Job takes too longAdd dependency caching
Tests pass locally but fail in CICheck environment differences (Node version, OS, env vars)
tip

Start simple — a workflow that just runs npm test on every PR is already a massive improvement over manual testing. Add complexity (deploy, notifications, etc.) only once the basics are solid.