cloudflare-workers-ci-cd — quality + safety report

In the Skillier index (secondsky__cloudflare-workers-ci-cd) · scanned 2026-06-03 · engine: builtin+triage

A
Quality
90/100
Safety

2 heuristic flags to review

Heuristic flags from the builtin scanner, which is known to over-flag (it trips on legitimate env-reading integrations, security skills, and library .eval calls). This is NOT an authoritative malicious verdict — re-scan with SkillSpector for the authoritative result. Run the authoritative scan →

Skillproof quality grade A

📇 This skill is in the Skillier index (curated · deduped · quality-filtered). Install Skillier to route & load it into your AI client.

Quality notes

Skill is large (~3744 tokens)
medium · quality · body
→ Tighten to the essential procedure; move long reference material to linked files.
No explicit trigger / 'when to use'
low · quality · body
→ Add a 'When to use' section or 'Use this when …' line listing trigger conditions.

About this skill

Complete CI/CD guide for Cloudflare Workers using GitHub Actions and GitLab CI. Use for automated testing, deployment pipelines, preview environments, secrets management, or encountering deployment failures, workflow errors, environment configuration issues.

📄 Read the SKILL.md
---
name: cloudflare-workers-ci-cd
description: Complete CI/CD guide for Cloudflare Workers using GitHub Actions and GitLab CI. Use for automated testing, deployment pipelines, preview environments, secrets management, or encountering deployment failures, workflow errors, environment configuration issues.
license: MIT
metadata:
  keywords: "cloudflare-workers, workers-ci-cd, github-actions, gitlab-ci, continuous-integration, continuous-deployment, automated-testing, deployment-pipeline, preview-deployments, staging-deployment, production-deployment, secrets-management, wrangler-deploy, environment-variables, github-secrets, deployment-verification, rollback-strategy, blue-green-deployment, canary-deployment, deployment-gates, ci-cd-best-practices, workflow-automation, pull-request-previews, branch-deployments"
  version: "1.0.0"
  last_verified: "2025-01-27"
  production_tested: true
  token_savings: "~75%"
  errors_prevented: 7
  templates_included: 4
  references_included: 4
  scripts_included: 1
  github_actions_version: "v4"
  wrangler_version: "4.50.0"
---

# Cloudflare Workers CI/CD

**Status**: ✅ Production Ready | Last Verified: 2025-01-27
**GitHub Actions**: v4 | **GitLab CI**: Latest | **Wrangler**: 4.50.0

## Table of Contents

- [What Is Workers CI/CD?](#what-is-workers-cicd)
- [New in 2025](#new-in-2025)
- [Quick Start (10 Minutes)](#quick-start-10-minutes)
- [Critical Rules](#critical-rules)
- [Core Concepts](#core-concepts)
- [Top 5 Use Cases](#top-5-use-cases)
- [Best Practices](#best-practices)
- [Top 7 Errors Prevented](#top-7-errors-prevented)
- [When to Load References](#when-to-load-references)

---

## What Is Workers CI/CD?

Automated testing and deployment of Cloudflare Workers using **GitHub Actions** or **GitLab CI**. Enables running tests on every commit, deploying to preview/staging/production environments automatically, managing secrets securely, and implementing deployment gates for safe releases.

**Key capabilities**: Automated testing, multi-environment deployments, preview URLs per PR, secrets management, deployment verification, automatic rollbacks.

---

## New in 2025

**GitHub Actions Updates** (January 2025):
- **NEW**: `cloudflare/wrangler-action@v4` (improved caching, faster deployments)
- **IMPROVED**: Secrets support with `vars` and `secrets` parameters
- **ADDED**: Built-in preview environment cleanup
- **BREAKING**: `apiToken` renamed to `api-token` (kebab-case)

**Migration from v3**:
```yaml
# ❌ OLD (v3)
- uses: cloudflare/wrangler-action@3
  with:
    apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}

# ✅ NEW (v4)
- uses: cloudflare/wrangler-action@v4
  with:
    api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
```

**Wrangler 4.50.0** (January 2025):
- **NEW**: `--dry-run` flag for deployment validation
- **IMPROVED**: Faster deployments with parallel uploads
- **ADDED**: `--keep-vars` to preserve environment variables

---

## Quick Start (10 Minutes)

### GitHub Actions Setup

**1. Create Cloudflare API Token**

Go to: https://dash.cloudflare.com/profile/api-tokens

Create token with permissions:
- **Account.Cloudflare Workers Scripts** - Edit
- **Account.Cloudflare Pages** - Edit (if using Pages)

**2. Add Secret to GitHub**

Repository → Settings → Secrets → Actions → New repository secret:
- Name: `CLOUDFLARE_API_TOKEN`
- Value: [paste token]

**3. Create `.github/workflows/deploy.yml`**

```yaml
name: Deploy

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    name: Deploy to Cloudflare Workers

    steps:
      - uses: actions/checkout@v4

      - uses: oven-sh/setup-bun@v2
        with:
          bun-version: latest

      - run: bun install

      - run: bun test

      - name: Deploy
        uses: cloudflare/wrangler-action@v4
        with:
          api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          command: deploy
```

**4. Push and Verify**

```bash
git add .github/workflows/deploy.yml
git commit -m "Add CI/CD pipeline"
git push
```

Check Actions tab on GitHub to see deployment progress.

---

## Critical Rules

### 1. Never Commit Secrets to Git

**✅ CORRECT**:
```yaml
# Use GitHub Secrets
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
```

**❌ WRONG**:
```yaml
# ❌ NEVER hardcode tokens
api-token: "abc123def456..."
```

**Why**: Exposed tokens allow anyone to deploy to your account.

### 2. Always Run Tests Before Deploy

**✅ CORRECT**:
```yaml
- run: bun test  # ✅ Tests run first

- name: Deploy
  uses: cloudflare/wrangler-action@v4
  with:
    api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
```

**❌ WRONG**:
```yaml
# ❌ Skipping tests
- name: Deploy
  uses: cloudflare/wrangler-action@v4
  # No tests!
```

**Why**: Broken code shouldn't reach production.

### 3. Use Different Environments

**✅ CORRECT**:
```yaml
# Production (main branch)
- name: Deploy to Production
  if: github.ref == 'refs/heads/main'
  run: bunx wrangler deploy --env production

# Staging (other branches)
- name: Deploy to Staging
  if: github.ref != 'refs/heads/main'
  run: bunx wrangler deploy --env staging
```

**❌ WRONG**:
```yaml
# ❌ Always deploying to production
- run: bunx wrangler deploy
```

**Why**: Test changes in staging before production.

### 4. Verify Deployment Success

**✅ CORRECT**:
```yaml
- name: Deploy
  id: deploy
  uses: cloudflare/wrangler-action@v4

- name: Verify Deployment
  run: |
    curl -f https://your-worker.workers.dev/health || exit 1
```

**❌ WRONG**:
```yaml
# ❌ No verification
- name: Deploy
  uses: cloudflare/wrangler-action@v4
  # Assuming it worked...
```

**Why**: Deployments can fail silently (DNS issues, binding errors).

### 5. Use Deployment Gates for Production

**✅ CORRECT**:
```yaml
deploy-production:
  environment:
    name: production
    url: https://your-worker.workers.dev
  # Requires manual approval
```

**❌ WRONG**:
```yaml
# ❌ Auto-deploy to production without review
deploy-production:
  runs-on: ubuntu-latest
```

**Why**: Human review catches issues automation misses.

---

## Core Concepts

### Multi-Environment Strategy

**Recommended setup**:
- **Production**: `main` branch → production environment
- **Staging**: Pull requests → staging environment
- **Preview**: Each PR → unique preview URL

**wrangler.jsonc**:
```jsonc
{
  "name": "my-worker",
  "main": "src/index.ts",

  "env": {
    "production": {
      "name": "my-worker-production",
      "vars": {
        "ENVIRONMENT": "production"
      }
    },
    "staging": {
      "name": "my-worker-staging",
      "vars": {
        "ENVIRONMENT": "staging"
      }
    }
  }
}
```

### Secrets Management

**Types of configuration**:
1. **Public variables** (wrangler.jsonc) - Non-sensitive config
2. **Secrets** (wrangler secret) - API keys, tokens
3. **CI variables** (GitHub Secrets) - Deployment credentials

**Setting secrets**:
```bash
# Local development
wrangler secret put DATABASE_URL

# CI/CD (via GitHub Actions)
bunx wrangler secret put DATABASE_URL --env production <<< "${{ secrets.DATABASE_URL }}"
```

### Preview Deployments

Automatically deploy each PR to a unique URL for testing:

```yaml
- name: Deploy Preview
  uses: cloudflare/wrangler-action@v4
  with:
    api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
    command: deploy --env preview-${{ github.event.number }}
```

Each PR gets URL like: `my-worker-preview-42.workers.dev`

---

## Top 5 Use Cases

### 1. Deploy on Push to Main

```yaml
name: Deploy Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: bun install
      - run: bun test
      - run: bun run build

      - name: Deploy to Production
        uses: cloudflare/wrangler-action@v4
        with:
          api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          command: deploy --env production
```

### 2. Preview Deployments for PRs

```yaml
name: Preview

on:
  pull_request:
    branches: [main]

jobs:
  preview:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: bun install
      - run: bun test

      - name: Deploy Preview
        id: deploy
        uses: cloudflare/wrangler-action@v4
        with:
          api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          command: deploy --env preview-${{ github.event.number }}

      - name: Comment PR
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '✅ Preview deployed to: https://my-worker-preview-${{ github.event.number }}.workers.dev'
            })
```

### 3. Run Tests on Every Commit

```yaml
name: Test

on:
  push:
    branches: ['**']
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: bun install
      - run: bun test --coverage

      - name: Upload Coverage
        uses: codecov/codecov-action@v4
        with:
          files: ./coverage/lcov.info
```

### 4. Deploy with Approval Gate

```yaml
name: Deploy Production (Manual Approval)

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://my-worker.workers.dev
    # Requires manual approval in GitHub Settings

    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: bun install
      - run: bun test

      - name: Deploy
        uses: cloudflare/wrangler-action@v4
        with:
          api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          command: deploy --env production
```

### 5. Staged Rollout (Canary)

```yaml
name: Canary Deployment

on:
  workflow_dispatch:
    inputs:
      percentage:
        description: 'Traffic percentage to new version'
        required: true
        default: '10'

jobs:
  canary:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: bun install

      # Deploy to canary environment
      - name: Deploy Canary
        uses: cloudflare/wrangler-action@v4
        with:
          api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          command: deploy --env canary

      # Configure traffic split via Cloudflare API
      # (See references/deployment-strategies.md for full example)
```

---

## Best Practices

### ✅ DO

1. **Use semantic commit messages**:
   ```
   feat: add user authentication
   fix: resolve rate limiting issue
   chore: update dependencies
   ```

2. **Run linting and type checking**:
   ```yaml
   - run: bun run lint
   - run: bun run type-check
   - run: bun test
   ```

3. **Cache dependencies**:
   ```yaml
   - uses: oven-sh/setup-bun@v2
     with:
       bun-version: latest
   # Bun automatically caches dependencies
   ```

4. **Deploy different branches to different environments**:
   ```yaml
   - name: Deploy
     run: |
       if [ "${{ github.ref }}" == "refs/heads/main" ]; then
         bunx wrangler deploy --env production
       else
         bunx wrangler deploy --env staging
       fi
   ```

5. **Monitor deployments**:
   ```yaml
   - name: Notify Slack
     if: failure()
     uses: slackapi/slack-github-action@v1
     with:
       payload: |
         {"text": "Deployment failed: ${{ github.sha }}"}
   ```

### ❌ DON'T

1. **Don't skip tests**
2. **Don't deploy without verification**
3. **Don't hardcode secrets**
4. **Don't deploy to production from feature branches**
5. **Don't ignore deployment failures**

---

## Top 7 Errors Prevented

### 1. ❌ `Error: A valid Cloudflare API token is required`

**Cause**: Missing or invalid `CLOUDFLARE_API_TOKEN` secret.

**Fix**:
1. Create API token: https://dash.cloudflare.com/profile/api-tokens
2. Add to GitHub Secrets: Settings → Secre

… (truncated)
Scan or optimize your own skill →

Want a live grade + an embeddable README badge? Run your skill through the free scanner.

Graded independently by Skillproof — nothing to sell the author. Quality is mechanical + corpus-grounded; safety flags are heuristic (builtin+triage), not a malicious verdict.