umanggarg's picture
Fix deploy: run vercel from repo root to avoid ui/ui double path
240beb5
# deploy.yml β€” Continuous Deployment to HuggingFace Spaces + Vercel
#
# ── What this file does ──────────────────────────────────────────────────────
#
# On every push to main (after CI passes), two jobs run in parallel:
#
# deploy-backend β†’ pushes the project directory to a HuggingFace Space,
# which rebuilds the Docker container automatically
#
# deploy-frontend β†’ runs `vercel --prod` to deploy the React app to
# Vercel's global CDN
#
# ── Why separate jobs? ───────────────────────────────────────────────────────
#
# Jobs run in parallel by default in GitHub Actions. Backend and frontend
# are independent β€” no reason to wait for one before starting the other.
# Total deploy time = max(backend_time, frontend_time), not the sum.
#
# ── Secrets vs Variables ─────────────────────────────────────────────────────
#
# GitHub Actions has two kinds of stored values:
# secrets β†’ encrypted, never shown in logs (tokens, API keys)
# vars β†’ plain text, visible in logs (usernames, IDs, config)
#
# Set these in: GitHub repo β†’ Settings β†’ Secrets and variables β†’ Actions
#
# Secrets required:
# HF_TOKEN HuggingFace token with "write" permission to your Space
# VERCEL_TOKEN Vercel personal access token
# VERCEL_ORG_ID From `vercel whoami` or project settings
# VERCEL_PROJECT_ID From `vercel project ls` or .vercel/project.json
#
# Variables required:
# HF_USERNAME Your HuggingFace username (e.g. "umang")
# HF_SPACE_NAME Your HF Space name (e.g. "github-rag-copilot")
#
# ── One-time setup (before this workflow will work) ──────────────────────────
#
# 1. Create HF Space at https://huggingface.co/new-space
# - SDK: Docker
# - Visibility: Public (free hardware requires public spaces)
# - Hardware: CPU basic (free)
# - Set env vars in Space Settings β†’ Variables (never commit secrets):
# QDRANT_URL, QDRANT_API_KEY, QDRANT_COLLECTION,
# GROQ_API_KEY, GEMINI_API_KEY, NOMIC_API_KEY,
# FRONTEND_URL (your Vercel URL)
#
# 2. Create Vercel project at https://vercel.com/new
# - Root directory: cartographer/ui
# - Build command: npm run build
# - Output dir: dist
# - Set env var: VITE_API_URL = https://<hf-username>-<hf-space-name>.hf.space
# - Get IDs: `npx vercel whoami` and `npx vercel project ls`
#
# 3. Add the secrets/variables above to this GitHub repo
name: Deploy
on:
push:
branches: [main]
# Only run deploy after CI passes β€” ensures broken code is never deployed
# This references the workflow named "CI" in ci.yml
# Remove this if you want deploys to run independently of CI
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: true # if a new push arrives, cancel the in-flight deploy
jobs:
# ── Backend β†’ HuggingFace Spaces ─────────────────────────────────────────
#
# HuggingFace Spaces is a git-based hosting platform. Every Space has a git
# repository at https://huggingface.co/spaces/{user}/{space}. When you push
# new code there, HF automatically:
# 1. Detects the Dockerfile in the repo root
# 2. Builds a Docker image from it
# 3. Runs the container (your FastAPI app) on port 7860
#
# The challenge: our Dockerfile lives inside a subdirectory (cartographer/)
# of a larger monorepo. HF Spaces expects the Dockerfile at the repo root.
#
# Solution: `git subtree split`
# This Git command creates a new synthetic branch where the specified
# prefix directory becomes the root. The full history is preserved but
# rewritten so every commit only contains files from that directory.
# We push this synthetic branch as `main` to HF Spaces.
#
deploy-backend:
name: Backend β†’ HuggingFace Spaces
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
# fetch-depth: 0 means fetch the FULL git history, not just the latest commit.
# git subtree split needs to walk history to build the synthetic branch β€”
# with a shallow clone (fetch-depth: 1, the default) it would fail.
fetch-depth: 0
- name: Configure git identity
# Git requires a name + email to create commits. GitHub Actions runner
# has no global git config, so we set it here. These values are
# arbitrary β€” they appear in the HF Space's git log.
run: |
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
- name: Push to HuggingFace Space
# The GitHub repo root IS the project root (Dockerfile is at root),
# so we push main directly to HF Spaces β€” no subtree split needed.
# --force is safe here: the HF Space is a deployment target only,
# nobody commits to it directly.
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
HF_USERNAME: ${{ vars.HF_USERNAME }}
HF_SPACE_NAME: ${{ vars.HF_SPACE_NAME }}
run: |
git push \
"https://$HF_USERNAME:$HF_TOKEN@huggingface.co/spaces/$HF_USERNAME/$HF_SPACE_NAME" \
main:main \
--force
# ── Frontend β†’ Vercel ──────────────────────────────────────────────────────
#
# Vercel hosts static sites. The React app is built with `npm run build`
# which produces a `dist/` directory. Vercel uploads this to their global
# CDN (100+ edge nodes) so users get the assets from the nearest location.
#
# `vercel --prod` is the CLI command that:
# 1. Runs the build command configured in the Vercel project
# 2. Uploads the output to Vercel's CDN
# 3. Promotes the deployment to the production URL
#
# Authentication: Vercel uses a token + org ID + project ID triple.
# These come from your Vercel dashboard (see setup instructions above).
#
deploy-frontend:
name: Frontend β†’ Vercel
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
cache-dependency-path: ui/package-lock.json
- name: Install dependencies
working-directory: ui
run: npm ci
# `npm ci` (clean install) is preferred over `npm install` in CI:
# - Installs exactly the versions in package-lock.json (reproducible)
# - Fails if package-lock.json is out of sync with package.json
# - Deletes node_modules before installing (clean state)
- name: Deploy to Vercel
# We pass VITE_API_URL so the React app knows where the backend lives.
# This env var is baked into the JS bundle at build time by Vite β€”
# it becomes a literal string in the compiled output (not a runtime env var).
# Run from repo root β€” Vercel project already has root dir set to "ui",
# so running from ui/ would make Vercel look for "ui/ui" (double-nested).
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
# Set this after you know your HF Space URL.
# Format: https://<hf-username>-<hf-space-name>.hf.space
# (HF converts "my-name/my-space" β†’ "my-name-my-space.hf.space")
VITE_API_URL: ${{ vars.HF_SPACE_URL }}
run: |
npx vercel --prod \
--token="$VERCEL_TOKEN" \
--yes
# --yes skips interactive confirmation prompts