How to Set Up a CI/CD Pipeline for OpenSEO: A Complete GitHub Actions and Cloudflare Guide

You can set up a CI/CD pipeline for OpenSEO by configuring GitHub Actions workflows for linting, testing, and building, then automating deployments to Cloudflare Workers using Alchemy, with secure secret management and isolated PR preview stages.

OpenSEO (every-app/open-seo) uses a modern CI/CD architecture built around pnpm, GitHub Actions, and Cloudflare Workers. The pipeline ensures code quality through automated checks while enabling safe, previewable deployments for every pull request. This guide walks through the exact configuration files and commands used in the repository to replicate this setup.

Continuous Integration Pipeline

The foundation of the OpenSEO CI/CD pipeline is the GitHub Actions workflow defined in .github/workflows/ci.yml. This workflow runs on every push and pull request to validate code integrity before it reaches production.

The CI Workflow Structure

The ci job executes on ubuntu-latest and follows a deterministic sequence of steps:

  1. Environment setup – Checks out the exact commit using actions/checkout@v4, then installs pnpm via pnpm/action-setup@v4 and Node.js 22 using actions/setup-node@v4
  2. Dependency installation – Runs pnpm install --frozen-lockfile to ensure reproducible builds using the locked dependency tree
  3. Static analysis – Executes pnpm run ci:check which runs linting (oxlint) and dead code detection (knip)
  4. Test execution – Runs pnpm run test:ci to execute the full test suite
  5. Bundle verification – Builds the worker with pnpm vite build, which includes a guard that fails if deny-listed packages (such as dataforseo-client) enter the eager startup graph
  6. Web UI validation – Installs web dependencies with pnpm --dir web install, runs TypeScript checks via pnpm --dir web run types:check, and builds the static site using pnpm --dir web run build

All steps are configured to fail fast, ensuring that lint errors, type mismatches, or test failures block the pipeline immediately.

Local CI Verification

Developers can replicate the CI environment locally before pushing changes:


# Enable corepack for the exact pnpm version specified in package.json

corepack enable

# Install dependencies with frozen lockfile

pnpm install --frozen-lockfile

# Run the full CI check suite

pnpm run ci:check

# Execute tests

pnpm run test:ci

# Verify worker bundle (includes dependency guard)

pnpm vite build

Continuous Delivery and Deployment

OpenSEO implements a multi-stage delivery process that includes Docker image builds for self-hosting and Cloudflare Worker deployments for the hosted version.

Docker Image Build

The .github/workflows/docker-image.yml workflow produces a self-hostable container using Dockerfile.selfhost:

  • Uses docker/build-push-action@v6 with GitHub Actions cache (type=gha) to speed up subsequent builds
  • Targets linux/amd64 architecture for broad compatibility
  • Loads the image locally for verification rather than pushing to a registry automatically

To build the image manually:

docker build -f Dockerfile.selfhost -t openseo:selfhost .

PR Preview Deployments

The PR preview workflow in .github/workflows/pr-preview.yml creates isolated Cloudflare Worker stages for every pull request, enabling safe testing of changes without affecting production.

The workflow executes these critical steps:

  1. Environment preparation – Writes the secret ENV_PREVIEW to .env.preview using printf '%s' "$ENV_PREVIEW" > .env.preview
  2. URL derivation – Extracts WORKERS_SUBDOMAIN from the environment file to construct the preview URL: https://open-seo-<stage>.<subdomain>
  3. Deployment – Runs pnpm deploy:preview --stage "$STAGE" --yes, which triggers a Vite build and Alchemy deployment
  4. Security verification – Uses curl to verify the preview URL redirects to Cloudflare Access authentication (https://*.cloudflareaccess.com/...), failing the job if the stage is publicly accessible
  5. PR integration – Posts the preview URL as a comment using the gh CLI
  6. Cleanup – When the PR closes, automatically runs pnpm destroy:preview --stage "$STAGE" --yes to remove the temporary infrastructure

Required repository secrets for this workflow:

  • CLOUDFLARE_API_TOKEN
  • CLOUDFLARE_ACCOUNT_ID
  • ENV_PREVIEW (contains the full .env.preview file contents)

Production Deployment

Production deployments use the same Alchemy tooling but target a fixed stage name hosted-prod. Unlike the automated PR previews, production pushes are typically performed manually to prevent accidental secret exposure:


# Run database migrations and deploy to production

pnpm deploy:postgres

This command reads from .env.production and executes the full deployment stack.

Required Secrets and Configuration

To set up the complete CI/CD pipeline, configure these GitHub Actions secrets in your repository settings:

Secret Purpose
CLOUDFLARE_API_TOKEN Authenticates with Cloudflare API for Worker deployments
CLOUDFLARE_ACCOUNT_ID Identifies the target Cloudflare account
ENV_PREVIEW Base64-encoded or raw contents of .env.preview containing WORKERS_SUBDOMAIN and other preview-stage variables

The pipeline strictly validates secret presence early in each workflow run. If ENV_PREVIEW is missing or malformed, the PR preview job aborts immediately to prevent deployment failures.

For local preview deployments (mirroring CI behavior), authenticate once with Alchemy:

pnpm alchemy login

Then create your local environment file:

cp .env.preview.example .env.preview

# Edit .env.preview with your values

pnpm preview:access  # Sets up the shared Cloudflare Access gate (idempotent)

pnpm deploy:preview --stage my-feature --yes

Summary

  • OpenSEO's CI pipeline uses .github/workflows/ci.yml to run linting, type-checking, tests, and bundle verification on every push
  • The delivery process combines Docker image builds (docker-image.yml) with Cloudflare Worker deployments via Alchemy
  • PR previews create isolated, Access-protected stages for each pull request using .github/workflows/pr-preview.yml
  • Security is enforced through deny-list bundle guards, mandatory Access authentication on previews, and strict secret management via GitHub Actions
  • Local development can replicate all CI steps using pnpm scripts defined in package.json

Frequently Asked Questions

What Node.js version does OpenSEO require?

OpenSEO requires Node.js 22, as specified in the GitHub Actions workflows using actions/setup-node@v4. The CI pipeline uses pnpm 9.x (managed via corepack) for deterministic dependency resolution across all environments.

How does OpenSEO prevent heavy dependencies from bloating the worker bundle?

The build process includes a bundle guard in the Vite build step (pnpm vite build) that explicitly checks for deny-listed packages like dataforseo-client. If any forbidden package enters the worker's eager startup graph, the build fails immediately, preventing accidental inclusion of heavy dependencies that would degrade cold-start performance.

Can I deploy OpenSEO without using Cloudflare?

Yes. While the hosted version uses Cloudflare Workers, the repository includes a Dockerfile.selfhost and a docker-image.yml workflow for building self-hostable containers. You can run the Docker image in any container runtime (Kubernetes, AWS ECS, etc.) by providing the necessary environment variables manually rather than using Alchemy.

Why are production deployments manual rather than automated?

Production deployments use pnpm deploy:postgres with a fixed hosted-prod stage but are intentionally excluded from automatic GitHub Actions triggers to prevent accidental exposure of production secrets or erroneous pushes. This manual gate ensures that production credentials remain isolated from CI environments and that deploys require explicit human verification.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →