CI/CD Pipelines for Vercel Deployment with GitHub Actions: Automating VitePress Builds in datawhalechina/easy-vibe

The datawhalechina/easy-vibe repository implements dual CI/CD pipelines for Vercel deployment with GitHub Actions by running parallel build processes—leveraging Vercel's native Git integration for automatic production hosting while using GitHub Actions to generate identical artifacts for GitHub Pages, ensuring consistent deployments across platforms through shared configuration files.

The datawhalechina/easy-vibe project demonstrates a production-grade approach to CI/CD pipelines for Vercel deployment with GitHub Actions using a VitePress documentation site. This setup maintains two synchronized build pathways that trigger on every push to the main branch, guaranteeing that static assets remain identical regardless of whether they serve from Vercel's edge network or GitHub Pages.

Understanding the Dual Pipeline Architecture

The project employs two complementary deployment mechanisms that execute the same build logic but target different hosting platforms.

Vercel Auto-Deployment handles production traffic by detecting pushes to connected branches, reading build instructions from vercel.json, and executing npm run build. The platform automatically serves static files from docs/.vitepress/dist at the assigned *.vercel.app domain.

GitHub Actions Workflow (defined in .github/workflows/deploy.yml) provides an alternative hosting path for organizations requiring GitHub-only environments. This pipeline builds the site using identical commands, archives the output as a Pages artifact, and deploys to GitHub Pages using the official actions/deploy-pages@v4 action.

Both pipelines share Node.js 20, npm dependency resolution, and the docs/.vitepress/dist output directory, ensuring build parity across platforms.

Configuring Vercel with vercel.json

The root-level vercel.json file instructs the Vercel platform on build orchestration, framework detection, and security policies.

{
  "buildCommand": "npm run build",
  "installCommand": "npm install",
  "framework": "vitepress",
  "outputDirectory": "docs/.vitepress/dist",
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "X-Content-Type-Options", "value": "nosniff" },
        { "key": "X-Frame-Options", "value": "DENY" },
        { "key": "X-XSS-Protection", "value": "1; mode=block" },
        { "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
        { "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" }
      ]
    },
    {
      "source": "/sitemap.xml",
      "headers": [{ "key": "Cache-Control", "value": "public, max-age=86400, s-maxage=86400" }]
    },
    {
      "source": "/robots.txt",
      "headers": [{ "key": "Cache-Control", "value": "public, max-age=86400, s-maxage=86400" }]
    }
  ]
}

This configuration specifies the VitePress framework preset, directs Vercel to run npm install followed by npm run build, and serves content from the VitePress default output directory. The headers array implements security hardening including content-type sniffing protection, clickjacking prevention via X-Frame-Options: DENY, and API permission restrictions.

GitHub Actions Workflow Implementation

The .github/workflows/deploy.yml file defines a two-stage pipeline that mirrors the Vercel build process while targeting GitHub Pages infrastructure.

Build Job Configuration

The build job validates the repository owner before execution to prevent accidental deployments from forked repositories.

build:
  if: github.repository_owner == 'datawhalechina'
  runs-on: ubuntu-latest
  steps:
    - name: Checkout
      uses: actions/checkout@v4
      with:
        fetch-depth: 0
    - name: Setup Node
      uses: actions/setup-node@v4
      with:
        node-version: 20
        cache: npm
    - name: Install dependencies
      run: npm ci
    - name: Build with VitePress
      run: npm run build
    - name: Upload artifact
      uses: actions/upload-pages-artifact@v3
      with:
        path: docs/.vitepress/dist

This job checks out the repository with full Git history, configures Node.js 20 with npm caching, installs dependencies using npm ci for deterministic builds, executes npm run build, and uploads the resulting docs/.vitepress/dist directory as a deployment artifact.

Deploy Job Configuration

The deployment stage requires the build job to complete successfully and utilizes GitHub's OpenID Connect permissions for secure Pages publishing.

deploy:
  environment:
    name: github-pages
    url: ${{ steps.deployment.outputs.page_url }}
  needs: build
  runs-on: ubuntu-latest
  steps:
    - name: Deploy to GitHub Pages
      id: deployment
      uses: actions/deploy-pages@v4

The actions/deploy-pages@v4 action handles the atomic publication to GitHub Pages, making the site available at https://datawhalechina.github.io/easy-vibe/ while preserving the build artifacts for rollback purposes.

Runtime Environment Detection

The VitePress configuration in docs/.vitepress/config.mjs implements runtime logic to detect the hosting platform and adjust the base URL and site metadata accordingly.

// Detect Vercel or EdgeOne environments
const isVercel = process.env.VERCEL === '1' || !!process.env.VERCEL_URL
const isEdgeOne = !!process.env.EDGEONE || process.env.EDGEONE === '1'

// Choose the correct base path
const base = process.env.BASE || (isVercel || isEdgeOne ? '/' : '/easy-vibe/')

When executing on Vercel infrastructure, the presence of process.env.VERCEL or process.env.VERCEL_URL triggers the root base path (/). In GitHub Actions or local development environments, the configuration defaults to /easy-vibe/ to accommodate GitHub Pages repository path requirements.

The configuration also dynamically resolves the public site URL for SEO meta tags:

const getSiteUrl = () => {
  if (isVercel && process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`
  if (isEdgeOne && process.env.EDGEONE_URL) return `https://${process.env.EDGEONE_URL}`
  if (process.env.SITE_URL) return process.env.SITE_URL
  return 'https://datawhalechina.github.io/easy-vibe'
}

This ensures that sitemap generation, canonical URLs, and OpenGraph meta tags reference the correct domain regardless of which CI/CD pipeline executed the build.

Security Headers and Caching Optimization

The vercel.json configuration injects comprehensive security headers that apply to Vercel deployments:

  • X-Content-Type-Options: nosniff prevents MIME-type sniffing attacks
  • X-Frame-Options: DENY blocks clickjacking attempts via iframe embedding
  • X-XSS-Protection: 1; mode=block enables browser XSS filters
  • Referrer-Policy: strict-origin-when-cross-origin controls referrer information leakage
  • Permissions-Policy restricts access to sensitive device APIs including camera, microphone, and geolocation

Static assets like sitemap.xml and robots.txt receive explicit cache-control directives (public, max-age=86400) optimizing CDN distribution and search engine crawling efficiency.

Summary

  • Dual pipeline approach: The repository implements CI/CD pipelines for Vercel deployment with GitHub Actions by running parallel build processes—Vercel handles automatic production hosting while GitHub Actions provides GitHub Pages previews using identical build configurations.
  • Shared build configuration: Both pipelines execute npm run build and output to docs/.vitepress/dist, ensuring deployment consistency across hosting platforms.
  • Environment-aware routing: The docs/.vitepress/config.mjs file dynamically selects base paths (/ vs /easy-vibe/) by inspecting VERCEL, VERCEL_URL, and BASE environment variables at build time.
  • Security hardening: The vercel.json file defines HTTP security headers including X-Frame-Options, X-Content-Type-Options, and Permissions-Policy directives.
  • Access control: The GitHub Actions workflow includes repository owner validation (github.repository_owner == 'datawhalechina') to prevent unauthorized deployments from forked repositories.

Frequently Asked Questions

How does the project detect whether it's running on Vercel versus GitHub Actions?

The docs/.vitepress/config.mjs file checks for the presence of process.env.VERCEL or process.env.VERCEL_URL to identify Vercel's build environment. When these variables exist, the configuration sets the base path to /; otherwise, it defaults to /easy-vibe/ for GitHub Pages compatibility. This detection occurs at build time, ensuring that generated links and asset paths match the intended hosting platform.

Why maintain both Vercel and GitHub Actions pipelines instead of using just one?

The dual approach provides deployment flexibility and redundancy. Vercel offers automatic deployments with preview URLs and edge network distribution, while GitHub Actions enables hosting within the GitHub ecosystem for organizations with Vercel restrictions or for contributors who cannot access the Vercel project. Both pipelines execute the same build steps defined in package.json, guaranteeing identical output regardless of the hosting target.

What Node.js version does the CI/CD pipeline use?

The workflow specifies Node.js 20 in .github/workflows/deploy.yml through the actions/setup-node@v4 action with node-version: 20. This matches the environment constraints documented in the project's package.json and ensures compatibility with modern VitePress features and security updates.

How are security headers applied when deploying through GitHub Actions?

The security headers defined in vercel.json apply specifically to Vercel-hosted deployments. When deploying to GitHub Pages via GitHub Actions, the headers depend on the GitHub Pages server configuration. However, the vercel.json remains the source of truth for security policies, and organizations using GitHub Pages Enterprise can implement equivalent headers through their CDN or repository settings to match the Vercel configuration.

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 →