A reusable workflow for deployments to vercel from Forgejo.
Find a file
keltonhalbert 966d79937a
Add an optional build cache restored before vercel build
A runner job starts from a clean checkout, so build tools that keep a
content-addressed cache on disk redo everything every push; stormscale.io's
image pipeline spent 25 minutes re-encoding unchanged photos. The new
cache_path / cache_key / cache_key_files inputs restore a directory before
the build and save it after, keyed on the files that feed it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vrhb2S7itMry5XseTd4tqk
2026-09-07 15:37:37 -05:00
.forgejo/workflows Add an optional build cache restored before vercel build 2026-09-07 15:37:37 -05:00
README.md Add an optional build cache restored before vercel build 2026-09-07 15:37:37 -05:00

Vercel Deploy Workflow @ v1

A reusable Forgejo workflow for deploying projects to Vercel. Routes pushes to a production branch to the live site and pushes to a preview branch to a preview environment.

Features

  • Separate production and preview deployments based on branch
  • Builds run on your Forgejo runner (build logs stay in Forgejo, not Vercel)
  • Bun and Node.js both available in the build environment
  • Configurable runtime versions, branch names, and runner label

Prerequisites

  • Forgejo v7 or later (reusable workflow support)
  • A registered Forgejo runner with the ubuntu-latest label (or override via the runs_on input)
  • A Vercel project that is not connected to any Git integration
  • Vercel API token, org ID, and project ID

Disconnecting Vercel from Git

If your project was previously deployed via Vercel's GitHub integration, disconnect it first to avoid duplicate deploys:

Vercel dashboard → Project → Settings → Git → Disconnect.

The project and its environment variables stay; only the auto-deploy hook is removed.

Obtaining credentials

Token: Vercel dashboard → Account Settings → Tokens → Create. Scope it to the team that owns the project.

Org ID and Project ID: Run npx vercel link once locally inside the repo. It creates .vercel/project.json containing both IDs. Copy the values, then delete the folder or verify it's in .gitignore.

Vercel's "team ID" and "org ID" refer to the same value — use whichever name your tooling expects.

Configuring a preview domain (optional)

Vercel's per-branch domain assignment requires their Git integration, which only supports GitHub/GitLab/BitBucket — so it doesn't work for Forgejo-driven deploys. Instead, this workflow aliases each preview deployment itself.

Add the subdomain in Vercel project Settings → Domains (without attaching it to a branch), then pass it to the workflow via the preview_domain input. After every preview deploy, the workflow runs vercel alias set to point the domain at the new deployment.

Usage

Add .forgejo/workflows/deploy.yml to your repo:

name: Deploy

on:
  push:
    branches: [main, preview]

jobs:
  deploy:
    uses: you/vercel-deploy-workflow/.forgejo/workflows/deploy.yml@v1
    secrets: inherit

Replace you/vercel-deploy-workflow with the actual path to this repo on your Forgejo instance. Add the three secrets via repo Settings → Actions → Secrets, or at the organization level if multiple repos will use this workflow.

secrets: inherit is required rather than per-secret declarations because Forgejo's runner does not yet implement the workflow_call.secrets key (tracked in forgejo#6069). On the upside, this means adding a fourth secret to the workflow later won't require updating any consumers.

With custom branch names

jobs:
  deploy:
    uses: you/vercel-deploy-workflow/.forgejo/workflows/deploy.yml@v1
    with:
      production_branch: production
      preview_branch: staging
    secrets: inherit

Make sure the on: push: branches: list in the caller matches the branches you've configured.

With a preview domain alias

jobs:
  deploy:
    uses: you/vercel-deploy-workflow/.forgejo/workflows/deploy.yml@v1
    with:
      preview_domain: preview.example.com
    secrets: inherit

With a build cache

jobs:
  deploy:
    uses: you/vercel-deploy-workflow/.forgejo/workflows/deploy.yml@v1
    with:
      cache_path: node_modules/.cache/imagetools
      cache_key: imagetools
      cache_key_files: src/lib/assets/**
    secrets: inherit

The runner starts every job from a clean checkout, so a build tool that keeps a content-addressed cache on disk (vite-imagetools, for one) redoes all of its work on every push. cache_path is restored before vercel build and saved after it. The key changes when the files matched by cache_key_files change, and the newest entry under cache_key is restored otherwise, so a push that adds one image encodes one image. Leave cache_key_files empty to save an entry per commit.

Inputs

Input Type Default Description
production_branch string main Branch that triggers production deploys
preview_branch string preview Branch that triggers preview deploys
preview_domain string "" Custom domain to alias to each preview deploy. Leave empty to skip aliasing.
node_version string "24" Node.js version
bun_version string latest Bun version
runs_on string ubuntu-latest Runner label
cache_path string "" Newline-separated paths to restore before the build and save after it. Empty skips caching.
cache_key string build-cache Cache key prefix; also the restore prefix.
cache_key_files string "" Glob(s) hashed into the key, so a new entry is saved only when they change. Empty keys on the commit SHA.

Secrets

These must be defined at the repo or org level on your Forgejo instance. The reusable workflow doesn't pre-declare them (see the note in Usage), so missing secrets surface as auth errors at the first vercel command rather than as workflow validation errors.

Secret Required Description
VERCEL_TOKEN yes Vercel API token
VERCEL_ORG_ID yes Vercel team/org ID
VERCEL_PROJECT_ID yes Vercel project ID

Notes

GITHUB_API_URL override

The Pull and Build steps explicitly set GITHUB_API_URL and GITHUB_SERVER_URL to GitHub's real URLs. Forgejo runners default these to the Forgejo instance, and Bun reads GITHUB_API_URL to fetch tarballs for any github.com dependency. Without the override, bun install routes through the Forgejo API and 404s on every GitHub-hosted dependency.

Pinned action SHAs

Actions are pinned to commit SHAs from code.forgejo.org, the Forgejo mirror. If your runner is configured to resolve actions from a different source (e.g. github.com directly), you'll need to use SHAs from that source instead. setup-bun is referenced via its full GitHub URL because it isn't mirrored on code.forgejo.org.

To refresh a pin:

git ls-remote https://code.forgejo.org/actions/checkout refs/tags/v4
git ls-remote https://code.forgejo.org/actions/setup-node refs/tags/v4
git ls-remote https://github.com/oven-sh/setup-bun refs/tags/v2
git ls-remote https://code.forgejo.org/actions/cache refs/tags/v4

Versioning this workflow

Consumers pin to a ref via @v1, @v1.2.0, or a full commit SHA. The recommended convention:

  • Cut tags v1.0.0, v1.1.0, etc. for releases
  • Maintain a floating v1 tag pointing at the latest 1.x release for consumers who want minor updates automatically
  • Bump the major version (v2) for any breaking change to inputs or behavior

This mirrors how actions/checkout and similar first-party actions handle versioning.