Full Production Pipeline
A complete CI/CD pipeline for a real production environment — from checkout to deployment and notification
A complete pipeline example ready for use in a real production environment. It includes all stages: checkout, lint, test, build, deploy, and notification.
Full Code
name: production-pipeline
# ─────────────────────────────────────────
# Trigger configuration
# ─────────────────────────────────────────
triggers:
# Runs on main/develop push (only when src/ changes)
push:
branches: [main, develop]
paths: [src/**, package.json, Dockerfile]
# Runs on Change Requests targeting main
change_request:
branches: [main]
# Scheduled daily execution at midnight
schedule:
branch: main
cron: ["0 0 * * *"]
# Manual execution available
workflow_dispatch:
# ─────────────────────────────────────────
# Global environment variables
# ─────────────────────────────────────────
env:
APP_NAME: my-app
REGISTRY: "asia-northeast3-docker.pkg.dev"
PROJECT_ID: "${{ vars.GCP_PROJECT_ID }}"
jobs:
# ═══════════════════════════════════════
# Phase 1: Source
# ═══════════════════════════════════════
checkout:
phase: source
steps:
- name: checkout
uses: "collabops/checkout@v2"
with:
repo-url: "https://<collabops-host>/<workspace>/<repository>.git"
fetch-depth: "0" # Full history (for version extraction)
# Version info + metadata collection (placed inside the build Job)
# → Step Outputs can only be referenced within the same Job,
# so the metadata step is placed inside the build Job.
# ═══════════════════════════════════════
# Phase 2: Dependencies
# ═══════════════════════════════════════
install:
phase: deps
needs: [checkout]
steps:
- name: npm-ci
image: node:18
run: |
cd /workspace/source
npm ci # Clean install of dependencies
# ═══════════════════════════════════════
# Phase 3: Test (parallel execution)
# ═══════════════════════════════════════
lint:
phase: test
needs: [install]
steps:
- name: eslint
image: node:18
run: |
cd /workspace/source
npm run lint
- name: typecheck
image: node:18
run: |
cd /workspace/source
npx tsc --noEmit
test:
phase: test
needs: [install] # Runs in parallel with lint
steps:
- name: unit-test
id: test-result
image: node:18
run: |
cd /workspace/source
npm test -- --coverage 2>&1 | tee /tmp/test.txt
# Extract coverage percentage
COVERAGE=$(grep 'Statements' /tmp/test.txt | grep -oP '\d+\.\d+' | head -1 || echo "N/A")
echo "coverage=${COVERAGE}%" >> $COLLABOPS_OUTPUT
env:
CI: "true"
# ═══════════════════════════════════════
# Phase 4: Build
# ═══════════════════════════════════════
# GCP authentication (required for build/deploy)
auth:
phase: build
needs: [checkout]
steps:
- name: gcloud-auth
uses: "collabops/gcloud-auth@v1"
with:
project-id: ${{ vars.GCP_PROJECT_ID }}
credentials: ${{ secrets.GCP_SA_KEY }}
- name: docker-auth
uses: "collabops/gcloud-docker-auth@v1"
with:
registry: ${{ env.REGISTRY }}
build:
phase: build
needs: [lint, test, auth] # After tests pass + auth complete
services:
- docker # Enable Docker service
steps:
# Collect metadata (step output referenced within the same Job)
- name: collect-meta
id: meta
image: node:18
run: |
cd /workspace/source
# package.json version
VERSION=$(node -p "require('./package.json').version")
echo "version=${VERSION}" >> $COLLABOPS_OUTPUT
# Short SHA (for image tag)
SHORT_SHA=$(echo "${{ collabops.sha }}" | cut -c1-7)
echo "short_sha=${SHORT_SHA}" >> $COLLABOPS_OUTPUT
# Build timestamp
echo "build_time=$(date -u +%Y%m%dT%H%M%S)" >> $COLLABOPS_OUTPUT
echo "=== Build Metadata ==="
echo "Version: ${VERSION}"
echo "SHA: ${SHORT_SHA}"
echo "Branch: ${{ collabops.ref_name }}"
echo "Event: ${{ collabops.event_name }}"
- name: build-push
uses: "collabops/docker-build-push@v1"
with:
tags: |
${{ env.REGISTRY }}/${{ env.PROJECT_ID }}/docker/${{ env.APP_NAME }}:latest
${{ env.REGISTRY }}/${{ env.PROJECT_ID }}/docker/${{ env.APP_NAME }}:${{ steps.meta.outputs.version }}
${{ env.REGISTRY }}/${{ env.PROJECT_ID }}/docker/${{ env.APP_NAME }}:${{ steps.meta.outputs.short_sha }}
build-args: |
NODE_ENV=production
APP_VERSION=${{ steps.meta.outputs.version }}
BUILD_TIME=${{ steps.meta.outputs.build_time }}
# ═══════════════════════════════════════
# Phase 5: Deploy (main push only)
# ═══════════════════════════════════════
deploy:
phase: deploy
needs: [build]
# Deploy to production only on main branch push
# For CRs or other branches, only build is executed
if: "collabops.ref == 'refs/heads/main' && collabops.event_name == 'push'"
steps:
# GKE cluster authentication
- name: gke-setup
uses: "collabops/gcloud-setup@v1"
with:
project-id: ${{ vars.GCP_PROJECT_ID }}
cluster-name: ${{ vars.GKE_CLUSTER }}
cluster-location: ${{ vars.GKE_REGION }}
# Production deployment
- name: rollout
image: google/cloud-sdk:527.0.0-slim
run: |
SHORT_SHA=$(echo "${{ collabops.sha }}" | cut -c1-7)
IMAGE="${{ env.REGISTRY }}/${{ env.PROJECT_ID }}/docker/${{ env.APP_NAME }}:${SHORT_SHA}"
echo "Deploying ${IMAGE} to production..."
# Update Deployment image
kubectl set image deployment/${{ env.APP_NAME }} \
app=${IMAGE} \
-n production
# Wait for rollout to complete (up to 5 minutes)
kubectl rollout status deployment/${{ env.APP_NAME }} \
-n production \
--timeout=300s
echo "Deploy complete!"
# ═══════════════════════════════════════
# Phase 6: Notification (always runs)
# ═══════════════════════════════════════
notify:
needs: [deploy]
if: "always()" # Always notify regardless of success/failure
steps:
- name: slack
uses: "collabops/slack-notify@v1"
with:
webhook-url: ${{ secrets.SLACK_WEBHOOK }}
title: "Production Deploy"
message: |
App: ${{ env.APP_NAME }}
Branch: ${{ collabops.ref_name }}
Commit: ${{ collabops.sha }}
Actor: ${{ collabops.actor }}
color: goodExecution Flow
checkout
├── install (dependency installation)
│ ├── lint (ESLint + TypeScript) ← parallel
│ └── test (unit tests + coverage) ← parallel
└── auth (GCP + Docker authentication)
│
└── build (collect metadata → Docker build & push)
│ ※ runs after lint, test, auth all complete
└── deploy (GKE deployment, main push only)
└── notify (Slack notification, always)Behavior by Trigger
| Trigger | Execution Scope |
|---|---|
push (main) | Full execution (test → build → deploy → notify) |
push (develop) | Up to test → build (deploy skipped) |
change_request | Up to test → build (deploy skipped) |
schedule | Up to test → build (deploy skipped) |
workflow_dispatch | Up to test → build (deploy skipped) |
The if condition on the deploy Job checks collabops.event_name == 'push', so any non-push trigger (including schedule and workflow_dispatch) will only execute up to build and skip deployment.