On this page
  1. What you’ll build
  2. Step 1 — Create the workflow file
  3. Step 2 — Understand the pieces
  4. Step 3 — Confirm failures block merges
  5. Step 4 — Add a deploy job
  6. Where AI coding assistants get this wrong
  7. Checklist
  8. FAQ
    1. Does GitHub Actions cost money?
    2. What is the difference between a job and a step?
    3. Can I run the workflow locally?
  9. Related topics
  10. Sources
tutorial

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.yml that 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 build

How 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

  • on defines triggers: here, pushes to main and every pull request.
  • jobs.test is the single job; runs-on: ubuntu-latest picks a fresh Linux runner.
  • uses: actions/checkout@v4 downloads your code onto the runner.
  • run: steps execute shell commands. npm ci installs exactly what package-lock.json pins, which is more reproducible than npm 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 on trigger, 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 install where npm ci is 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.

Sources

Share: