What CI/CD Pipelines Are Defined in .github/workflows for open-seo?
The open-seo repository defines four GitHub Actions workflows in .github/workflows: ci.yml for core checks, docker-image.yml for multi-arch container builds, sourcemaps.yml for PostHog error tracking, and pr-preview.yml for Cloudflare Workers preview deployments.
The open-seo project by every-app automates its entire software delivery lifecycle through GitHub Actions. All CI/CD configurations live in the .github/workflows directory, providing continuous integration, reproducible builds, error monitoring, and on-demand preview environments. This guide breaks down each pipeline, its triggers, and how they work together as derived directly from the source code.
CI Workflow: ci.yml
The ci.yml workflow serves as the primary quality gate for every code change.
Triggers and Purpose
- Triggers:
pushtomainand any pull request - Key steps: dependency installation, linting, type-checking, unit tests, Vite worker bundle build, and website compilation
The workflow uses actions/setup-node@v4 with cache: pnpm for fast, reproducible dependency restoration. Concurrency is controlled via group: ci-${{ github.ref }}, automatically cancelling stale runs when new commits arrive.
Core Pipeline Steps
# From .github/workflows/ci.yml
# Simplified representation of the job structure
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm run ci:check # Lint + type-check
- run: pnpm run test:ci # Unit test suite
- run: pnpm vite build # Worker bundle
- run: pnpm --dir web install && pnpm --dir web run build
Run this locally to match CI behavior:
pnpm install --frozen-lockfile
pnpm run ci:check
pnpm run test:ci
pnpm vite build
pnpm --dir web install && pnpm --dir web run build
Docker Image Build Workflow: docker-image.yml
The docker-image.yml workflow produces multi-architecture container images for self-hosting.
Triggers and Publishing
- Triggers:
pushtomain,v*tags, and manual dispatch viaworkflow_dispatch - Output: GitHub Container Registry (GHCR)
This workflow builds for both linux/amd64 and linux/arm64 using QEMU and Buildx. The docker/metadata-action@v5 auto-generates tags and labels, while type=gha cache acceleration speeds up subsequent builds.
Multi-Arch Build Configuration
# From .github/workflows/docker-image.yml
# Key Docker buildx configuration
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile.selfhost
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max
Trigger manually with the GitHub CLI:
gh workflow run docker-image.yml --ref main
Sourcemaps Upload Workflow: sourcemaps.yml
The sourcemaps.yml workflow ensures production errors remain debuggable.
Purpose and Trigger
- Trigger:
pushtomainonly - Function: Compile and upload sourcemaps to PostHog for error de-obfuscation
The workflow requires POSTHOG_CLI_TOKEN via ${{ secrets.POSTHOG_CLI_TOKEN }}. This enables the team to trace minified production errors back to original TypeScript source locations.
PR Preview Workflow: pr-preview.yml
The pr-preview.yml workflow creates ephemeral preview environments for every pull request on Cloudflare Workers.
Full Lifecycle Automation
| PR Event | Action |
|---|---|
opened |
Deploy new preview stage |
synchronize |
Update existing preview with new commits |
reopened |
Redeploy previously closed preview |
closed |
Automatically destroy preview and clean up resources |
Preview URL Generation and Verification
The workflow reads WORKERS_SUBDOMAIN from the ENV_PREVIEW secret (a .env.preview file) to construct URLs like https://open-seo-pr-12.example.workers.dev. After deployment, it validates that Cloudflare Access protection is active, then posts the preview URL as a PR comment.
Access the preview from the posted comment:
# Example URL from PR comment
curl -I https://open-seo-pr-12.example.workers.dev
Shared Architecture Patterns
All four workflows in .github/workflows follow consistent design principles:
- Concurrency control: Each uses
concurrencyblocks withcancel-in-progress: trueto prevent resource waste - Secret injection: Sensitive credentials use
${{ secrets... }}and${{ vars... }}contexts - Caching strategy: pnpm dependencies and Docker layers are aggressively cached
- Fail-fast behavior: Quality checks run early to surface issues before expensive builds
Summary
ci.ymlruns lint, type-check, tests, and builds on every push and PRdocker-image.ymlpublishes multi-arch images to GHCR on main/tags or manual triggersourcemaps.ymluploads production sourcemaps to PostHog after successful main branch buildspr-preview.ymldeploys and tears down Cloudflare Workers previews tied to PR lifecycle- All workflows use concurrency controls, strategic caching, and secret-based configuration for secure, efficient automation
Frequently Asked Questions
How do I trigger a Docker build without pushing code?
Use the GitHub CLI or GitHub web UI to run the docker-image.yml workflow with workflow_dispatch. Run: gh workflow run docker-image.yml --ref main.
What architectures does the Docker image support?
The docker-image.yml workflow builds for linux/amd64 and linux/arm64 using QEMU emulation and Docker Buildx, ensuring compatibility with most server environments.
Where do PR preview URLs come from?
The pr-preview.yml workflow constructs URLs using the WORKERS_SUBDOMAIN value from the ENV_PREVIEW secret, producing addresses like https://open-seo-pr-N.subdomain.workers.dev and posting them as PR comments.
Why are sourcemaps uploaded separately from the main CI?
Sourcemap generation requires production build artifacts and the PostHog CLI token. The sourcemaps.yml workflow isolates this concern, running only after successful main branch builds to ensure de-obfuscation data matches deployed code.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →