Back
Jul 30, 2026 · 8 min read

From manual deploy script to a CI/CD pipeline with GitHub Actions

Continuing the Vercel CLI deploy guide: why the manual script doesn't scale past one contributor, and how to move it into a GitHub Action.

GitHub ActionsCI/CDVercelDevOps

This continues the Vercel CLI deploy guide. That script solves the problem for one person; once the repo has more than one contributor, it falls short.

01 · The problem

Vercel's Hobby plan only auto-deploys commits whose Git author matches the account that owns the project. In a private repo with more than one contributor, any commit from someone else gets blocked.

02 · Why the manual script doesn't scale

The first fix was a script that rewrites the last commit's author on a temporary local branch (never pushed) and deploys from there:

#!/usr/bin/env bash
set -e

git checkout main
git fetch origin && git pull origin main
git branch -D deploy-temp 2>/dev/null || true
git checkout -b deploy-temp origin/main
git commit --amend --author="Name <email>" --no-edit
vercel --prod
git checkout main
It works, but only whoever has the Vercel account authenticated locally can run it — a bottleneck tied to a person, not a limitation of the tool.

03 · Moving it into a GitHub Action

The idea: move the author rewrite and the deploy into a process that runs in CI, not on someone's machine. That way anyone with push access can trigger a deploy without needing their own Vercel account.

name: Deploy to production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
      VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - run: |
          git config user.email "you@email.com"
          git config user.name "Your Name"
          git commit --amend --reset-author --no-edit

      - run: npm install --global pnpm@latest
      - run: npm install --global vercel@latest
      - run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
      - run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
      - run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}
Needs 3 repo secrets: VERCEL_TOKEN, VERCEL_ORG_ID and VERCEL_PROJECT_ID (the last two come from .vercel/project.json after running vercel link).

04 · Issues hit during implementation

spawn pnpm ENOENT

The GitHub runner doesn't ship pnpm preinstalled. Fixed by adding npm install --global pnpm@latest before using the Vercel CLI.

ERR_PNPM_IGNORED_BUILDS

pnpm 10+ blocks install scripts from dependencies with native binaries unless explicitly approved. The approval syntax (onlyBuiltDependencies) moves location between versions (package.json → pnpm-workspace.yaml). More robust to downgrade the block to a warning:

onlyBuiltDependencies: [core-js, sharp]
strictDepBuilds: false

System environment variables not available

A build run outside Vercel's native infrastructure doesn't get the variables Vercel auto-injects on a normal deploy (e.g. the deploy URL). Any library that implicitly depends on that — next-auth needing its own base URL, for instance — fails unless that variable is set explicitly in the dashboard.

Variables marked "Sensitive"

Vercel lets you mark a variable unreadable after it's saved — not from the dashboard, the CLI, or any process. If an external process needs the real value (vercel pull), the variable has to be normal, not sensitive. The symptom is silent: the variable "exists" and the process doesn't throw a config error, but returns a placeholder value instead of the real one.

Conclusion

An automation tied to a personal identity is a structural bottleneck, not a limitation of the specific tool. Moving it to a process with its own credentials, run in a shared environment, is what removes the dependency on one person — the rest (tooling versions, environment variables) are point-in-time implementation details.

Back