On this page
How to Set Up a CI/CD Pipeline With GitHub Actions
A step-by-step GitHub Actions tutorial: build and test a Node.js app on every push and deploy on release. Includes a working workflow file.
Quick answer
- GitHub Actions runs a workflow file in
.github/workflows/whenever you push or open a pull request.- A workflow is a list of jobs and steps; each step is a command or a reusable action.
- Add a
ci.ymlthat installs dependencies, runs tests, and builds, and every change is verified automatically.
What you’ll build
A pipeline for a Node.js app that, on every push and pull request, checks out the code, installs dependencies, runs tests, and produces a production build. By the end, a failing test will block a pull request automatically.
Step 1 — Create the workflow file
Create .github/workflows/ci.yml in your repository:
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
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 test
- run: npm run buildHow to verify it worked: push the file to GitHub, open the Actions tab, and watch the test job complete with a green check. A red ✕ means a step failed and you can click into it for the log.
Step 2 — Understand the pieces
ondefines triggers: here, pushes tomainand every pull request.jobs.testis the single job;runs-on: ubuntu-latestpicks a fresh Linux runner.uses: actions/checkout@v4downloads your code onto the runner.run:steps execute shell commands.npm ciinstalls exactly whatpackage-lock.jsonpins, which is more reproducible thannpm install.
Step 3 — Confirm failures block merges
Change a test so it fails, then open a pull request. The pipeline should run and report a failure on the pull request. Revert the change, and the pipeline goes green again. This proves the pipeline is a real gate, not just decoration.
Step 4 — Add a deploy job
Add a second job that runs only on release tags:
deploy:
if: startsWith(github.ref, 'refs/tags/v')
needs: test
runs-on: ubuntu-latest
steps:
- run: echo "Deploying $GITHUB_REF_NAME"needs: test makes deploy wait for tests to pass, and the if guard limits deploys to version tags.
Where this bites vibecoders
AI assistants happily generate workflow YAML, but it often fails on the first run: a wrong action version, a missing
ontrigger, or a secret pasted in plaintext. Treat the first green pipeline as the milestone, not the generation of the file. Also pin secrets in the repository’s Settings → Secrets and variables → Actions, not in the YAML.
Where AI coding assistants get this wrong
- Hardcoding API keys in the workflow instead of using
${{ secrets.NAME }}. - Using
npm installwherenpm ciis correct, allowing drift from the lockfile. - Referencing action versions (e.g.
@main) that shift under you instead of pinned tags like@v4. - Writing a deploy job with no
needs, so a broken test run can still deploy.
Checklist
- Store the workflow in version control and trigger it on push and pull request.
- Pin dependencies with a lockfile and use
npm ci(or the equivalent). - Put every secret in GitHub’s secret store and reference it, never inline it.
- Make the pipeline fail on a real failing test before you trust it.
- Gate deploys on the test job with
needs, and scope them to tags or protected branches.
FAQ
Does GitHub Actions cost money?
Public repositories get GitHub Actions minutes free. Private repositories get a monthly free allowance, then pay per minute. A small test pipeline on a personal project usually stays within the free tier. Check GitHub’s billing page for current limits.
What is the difference between a job and a step?
A job is a set of steps that run on one runner and share a filesystem. A step is a single unit of work — a shell command or an action. Jobs can run in parallel; steps within a job run in order.
Can I run the workflow locally?
GitHub Actions runs on GitHub’s runners, but you can debug the individual commands locally and use act to approximate a run on your machine. The most reliable feedback is the Actions tab itself.
Related topics
- What Is CI/CD?
- How to Add Security Scanning to Your CI/CD Pipeline
- What Is DevSecOps?
- How to Add SAST Scanning to a GitHub Repo