Project113

Example: CI/CD Pipeline

Build a complete CI/CD pipeline with multiple stages and secrets.

Goal

  • Run linting and unit tests on every push.
  • Build a Docker image on pushes to master.
  • Deploy to a staging environment on pushes to master.
  • Store API keys as secrets, not in the config file.

Step 1: Create Secrets

Add the following secrets in your repository settings:

  • DEPLOY_KEY — SSH key for deployment access.
  • API_KEY — Third-party API key used during build.

Step 2: Write the Pipeline Config

steps:
  lint:
    image: node:20
    environment:
      IMAGE_NAME: myapp
    commands:
      - npm ci
      - npm run lint

  test:
    image: node:20
    commands:
      - npm ci
      - npm test

  build:
    image: docker
    environment:
      IMAGE_NAME: myapp
      API_KEY:
        from_secret: API_KEY
    commands:
      - docker build -t $IMAGE_NAME:${CI_COMMIT_SHA:0:10} .
    when:
      branch: [master]

  deploy-staging:
    image: alpine
    environment:
      DEPLOY_KEY:
        from_secret: DEPLOY_KEY
    commands:
      - ssh -i $DEPLOY_KEY deploy@staging.example.com "cd /app && docker compose pull && docker compose up -d"
    when:
      branch: [master]

Step 3: Configure Triggers

Steps run on every push by default. The when: blocks on the build and deploy steps restrict them to the master branch, while lint and test run on all branches:

steps:
  test:
    image: node:20
    commands:
      - npm test
    when:
      event: [push]

Step 4: Set Up Merge Requirements

In repository settings, enable Require CI to pass so merges to master are blocked until the pipeline succeeds:

  • Require CI to pass: Yes

Result

  • Every push triggers linting and testing.
  • Pushes to master build a Docker image.
  • Deployments happen automatically after a successful build.
  • Secrets are never exposed in logs or configuration files.
  • Change requests require passing CI before merge.