Canary deployment with smoke tests

VerifiedCaution

Deploy a zero-traffic Cloud Run revision, run health checks, and promote to live traffic only if smoke tests pass.

Sby Skills Guide Bot
DevOpsIntermediate
8/4/2026
Claude Code
#canary-deployment#smoke-tests#cloud-run#gcp#deployment

Recommended for

Automated summary

Deploys a zero-traffic Cloud Run revision, runs health checks, and promotes it to production if the tests pass.

Strengths

  • Deploys a zero-traffic revision first, preventing a broken build from reaching users.
  • Resolves the target service automatically from the services: block in .adlc/config.yml, with a documented fallback for single-repo setups.
  • Chains image build, push to Artifact Registry, smoke tests, and promotion on success.
  • Accepts a repo id, a Cloud Run service name, or auto-detects from the current worktree.

Limitations

  • Tightly coupled to Google Cloud Run, gcloud, and Artifact Registry; other platforms require adaptation.
  • Assumes the Cloud Run service already exists and that a Docker image can be built or retrieved.
  • Is no longer auto-invoked from /proceed or /bugfix; operators must run /canary manually.
When to use it

Use when deploying a new revision of an existing Cloud Run service to production and you want to validate it before switching traffic.

When not to use it

Do not use to create a new Cloud Run service, deploy outside GCP, or when the target service is not configured in .adlc/config.yml.

Security analysis

Caution
Quality score87/100

The skill performs legitimate canary deployment operations using Docker, gcloud, and curl. No destructive, exfiltrating, or obfuscating commands are present. However, because it invokes shell and deployment tools, it warrants a caution rating.

Findings
  • •Executes shell commands for context gathering via backtick-bang syntax (e.g., pwd, git, gcloud) and includes external partial scripts, but these are read-only/legitimate deployment operations.
  • •Uses Docker build/push and gcloud run deploy, which are powerful but standard for canary deployment.
How this analysis is made

Examples

Canary deploy a repo service
/canary api
Canary deploy from current worktree
Canary deploy the current service with smoke tests before promoting it to live traffic.
Canary deploy a named Cloud Run service
/canary web

name: canary description: Canary deployment with smoke tests — deploy to a zero-traffic revision, run health checks, and promote on success. Use when the user says "canary deploy", "deploy with canary", "smoke test the deploy", or wants deployment confidence before going live. argument-hint: Optional repo id (from .adlc/config.yml) or service name — auto-detected from current worktree if omitted

/canary — Canary Deployment with Smoke Tests

You are deploying code through a canary process: deploy a zero-traffic revision, verify it works, then promote to live traffic. This prevents broken deploys from reaching users.

Ethos

!sh .adlc/partials/ethos-include.sh 2>/dev/null || sh ~/.claude/skills/partials/ethos-include.sh

Context

  • Current directory: !pwd
  • Current branch: !git branch --show-current 2>/dev/null || echo "Not a git repo"
  • GCP project: !gcloud config get-value project 2>/dev/null || echo "No GCP project configured"
  • Active Cloud Run services: !gcloud run services list --format="table(SERVICE,REGION,URL)" 2>/dev/null || echo "gcloud not configured"

Input

Target: $ARGUMENTS

Prerequisites

  1. gcloud CLI must be authenticated and configured with the correct project
  2. The service must already exist on Cloud Run (this skill deploys revisions, not new services)
  3. A Docker image must be available (either build locally or use the latest from Artifact Registry)

Service Resolution

Service configuration lives in .adlc/config.yml in the primary repo under a services: block, keyed by repo id. This is the single source of truth — no service names, regions, or image paths should be hardcoded in this skill.

Expected config shape (primary repo .adlc/config.yml):

repos:
  api:
    path: ../api
  web:
    path: ../web

services:
  api:
    cloud_run_service: api
    region: us-central1
    image_path: us-central1-docker.pkg.dev/<gcp-project>/api/api
  web:
    cloud_run_service: web
    region: us-central1
    image_path: us-central1-docker.pkg.dev/<gcp-project>/web/web

Resolution order:

  1. If $ARGUMENTS is a repo id defined under repos: in the primary's config, look up services[<repo-id>] and use that. If the repo id has no services: entry, stop and ask the user to add one.
  2. If $ARGUMENTS is a Cloud Run service name already (e.g., the user passed api and it matches services.*.cloud_run_service), use that entry.
  3. If no argument: detect which repo the current cwd is inside by walking up to find a git root and matching it against repos[*].path (resolve each to an absolute path first). Then look up its service.
  4. If the project has no .adlc/config.yml (single-repo legacy setup) AND the current repo has a top-level Dockerfile and a single Cloud Run service name that matches the repo basename, fall back to auto-detection: service name = repo basename, region = us-central1, image path = $(gcloud config get-value project)/<service>. Surface this fallback in the logs so the user knows it's being inferred.
  5. If none of the above resolves, stop with a clear error: "Could not determine Cloud Run service. Add a services: block to .adlc/config.yml or pass the service name as an argument."

Operating worktree: if the resolved repo has a feature-branch worktree (e.g., .worktrees/REQ-xxx) that matches the current /proceed pipeline, build from inside that worktree. Otherwise build from the repo's main checkout. The caller can also pass the worktree path explicitly.

Note (post-REQ-380, REQ-381): /canary is no longer auto-invoked from /proceed (REQ-380) or /bugfix (REQ-381). Operators run it manually when a production canary is needed.

Instructions

Step 1: Build and Push Image

  1. Determine the image tag: use the current git SHA (git rev-parse --short HEAD)
  2. Build the Docker image:
    docker build -t <IMAGE_PATH>:canary-<SHA> ./<subdir if needed>
    
  3. Push to Artifact Registry:
    docker push <IMAGE_PATH>:canary-<SHA>
    

If the user says "use latest" or the image was already built by CI, skip the build and use the :latest tag from Artifact Registry.

Step 2: Deploy Canary Revision (Zero Traffic)

Deploy a new revision that receives NO traffic:

gcloud run deploy <SERVICE_NAME> \
  --image=<IMAGE_PATH>:canary-<SHA> \
  --region=us-central1 \
  --no-traffic \
  --tag=canary \
  --format="json"

This creates a tagged revision accessible at https://canary---<SERVICE_NAME>-<hash>.a.run.app without affecting production traffic.

Capture the canary URL from the output.

Step 3: Health Checks

Run basic health checks against the canary URL:

  1. Liveness: curl -s -o /dev/null -w "%{http_code}" <CANARY_URL>/health

    • Expected: 200
    • Retry up to 3 times with 5-second intervals (cold start grace period)
  2. Readiness: curl -s -o /dev/null -w "%{http_code}" <CANARY_URL>/api/health

    • Expected: 200 (or the service's documented readiness endpoint)

If health checks fail after 3 retries, go to Step 6 (Rollback).

Step 4: Smoke Tests

Run smoke tests against the canary URL. Load test definitions from the primary repo's .adlc/context/smoke-tests.md if it exists, otherwise use defaults. In cross-repo mode the primary's smoke-tests.md is the authoritative source for every service — sibling repos do not need their own copy.

Default smoke tests (API services):

GET  /health              -> 200
GET  /api/health          -> 200

Custom smoke tests (from .adlc/context/smoke-tests.md): Each entry should specify: method, path, expected status, optional body pattern.

For each test:

RESPONSE=$(curl -s -w "\n%{http_code}" <CANARY_URL><PATH>)
STATUS=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | head -n -1)

Report results:

## Smoke Test Results

| Test | Method | Path | Expected | Actual | Status |
|------|--------|------|----------|--------|--------|
| Health | GET | /health | 200 | 200 | PASS |
| API Health | GET | /api/health | 200 | 200 | PASS |

Result: 2/2 passed

If any test fails, go to Step 6 (Rollback).

Step 5: Promote to Production

All checks passed — promote the canary revision to 100% traffic:

gcloud run services update-traffic <SERVICE_NAME> \
  --region=us-central1 \
  --to-tags=canary=100

Verify promotion:

gcloud run services describe <SERVICE_NAME> \
  --region=us-central1 \
  --format="table(status.traffic[].percent,status.traffic[].revisionName,status.traffic[].tag)"

Confirm canary revision is now serving 100% traffic.

Remove the canary tag (clean up):

gcloud run services update-traffic <SERVICE_NAME> \
  --region=us-central1 \
  --remove-tags=canary

Report:

Canary promoted to production.
Service: <SERVICE_NAME>
Revision: <REVISION_NAME>
URL: <PRODUCTION_URL>
All smoke tests passed.

Step 6: Rollback (Failure Path)

If health checks or smoke tests fail:

  1. Delete the canary revision tag (so it's not addressable):

    gcloud run services update-traffic <SERVICE_NAME> \
      --region=us-central1 \
      --remove-tags=canary
    
  2. Report the failure:

    CANARY FAILED — rolled back.
    Service: <SERVICE_NAME>
    Failed revision: canary-<SHA>
    
    Failures:
    - [list failed health checks or smoke tests]
    
    Production traffic is unchanged — still serving the previous revision.
    
  3. Suggest next steps:

    • Check Cloud Run logs: gcloud run services logs read <SERVICE_NAME> --region=us-central1 --limit=50
    • Investigate the failure locally
    • Fix and re-run /canary

Step 7: Update Pipeline State (if in /proceed context)

If pipeline-state.json exists for the current REQ:

  1. Add a canary entry to phaseHistory with the result (passed/failed)
  2. Include: service name, revision, smoke test results, canary URL

Smoke Test Configuration

To customize smoke tests, create .adlc/context/smoke-tests.md with this format:

# Smoke Tests

## api
| Method | Path | Expected Status | Body Pattern |
|--------|------|-----------------|--------------|
| GET | /health | 200 | |
| GET | /api/health | 200 | |
| GET | /api/v1/config | 200 | "version" |

## web
| Method | Path | Expected Status | Body Pattern |
|--------|------|-----------------|--------------|
| GET | / | 200 | |
| GET | /api/health | 200 | |

The keys above (api, web) are repo ids — the same ids you used under repos: in .adlc/config.yml. /canary looks up the smoke tests for the service it's deploying by matching repo id.

What This Skill Does NOT Do

  • It does not create new Cloud Run services — the service must already exist
  • It does not handle iOS deployments — TestFlight is already a canary-like process
  • It does not modify CI/CD workflows — it's a manual deployment confidence tool
  • It does not handle database migrations — run those separately before deploying

From the file to a first result

The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.

View the source and supporting files

This download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.

1. Download the file

Download SKILL.md

From the root of a test project, save the file as .skill-downloads/canary-deployment-with-smoke-tests-mse7inry/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.

Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.

mkdir -p '.skill-downloads/canary-deployment-with-smoke-tests-mse7inry' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/canary-deployment-with-smoke-tests-mse7inry/download' --output '.skill-downloads/canary-deployment-with-smoke-tests-mse7inry/SKILL.md.part' && mv '.skill-downloads/canary-deployment-with-smoke-tests-mse7inry/SKILL.md.part' '.skill-downloads/canary-deployment-with-smoke-tests-mse7inry/SKILL.md'

2. Check prerequisites

Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.

Original excerpt: Prerequisites
  1. gcloud CLI must be authenticated and configured with the correct project
  2. The service must already exist on Cloud Run (this skill deploys revisions, not new services)
  3. A Docker image must be available (either build locally or use the latest from Artifact Registry)

The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.

3. Load the skill

After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.

Claude Code
test -f '.skill-downloads/canary-deployment-with-smoke-tests-mse7inry/SKILL.md' && test -s '.skill-downloads/canary-deployment-with-smoke-tests-mse7inry/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/canary' && cp -R '.skill-downloads/canary-deployment-with-smoke-tests-mse7inry' '.claude/skills/canary'

OpenSkills

OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.

Official OpenSkills installation
openskills install './.skill-downloads/canary-deployment-with-smoke-tests-mse7inry'

openskills read 'canary-deployment-with-smoke-tests-mse7inry'

Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.

For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.

openskills sync

Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.

4. Try a first task

Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:

Read .skill-downloads/canary-deployment-with-smoke-tests-mse7inry/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.

Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.

Related skills