github-actions
Deploy to Hugging Face
c794b6b
Raw
History Blame Contribute Delete
7.67 kB
# Deploy backend (Cloud Run) and frontend (Firebase Hosting) on push.
#
# Auth: WIF (provider + service account email) or legacy JSON key — see docs/CI_GITHUB.md
# Note: `secrets` cannot be used in job-level `if:`; we gate in the first step instead.
name: Deploy
on:
push:
branches: [main, master]
workflow_dispatch:
concurrency:
group: deploy-${{ github.ref_name }}
cancel-in-progress: true
env:
GCP_PROJECT: rapidmk
GCP_REGION: us-central1
AR_REPO: us-central1-docker.pkg.dev/rapidmk/cepheus/api
CLOUD_RUN_SERVICE: cepheus-api
FIREBASE_PROJECT: rapid-eec43
permissions:
contents: read
id-token: write
jobs:
quality-gate:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip
cache-dependency-path: backend/requirements-ci.txt
- name: Install backend dependencies
run: |
python -m pip install --upgrade pip
pip install -r backend/requirements-ci.txt
- name: Backend tests
env:
CEPHEUS_CLOUD: "1"
CEPHEUS_API_KEY: test-key
CEPHEUS_AUTH_DEV_MODE: "1"
CEPHEUS_CI_STUB_VISION: "1"
run: python -m pytest backend/tests -q
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
cache-dependency-path: cepheus/package-lock.json
- name: Frontend quality checks
run: |
cd cepheus
npm ci
npm run lint
npm run test
npm run build
- name: Start API for launch gate
env:
CEPHEUS_CLOUD: "1"
CEPHEUS_API_KEY: test-key
CEPHEUS_AUTH_DEV_MODE: "1"
CEPHEUS_CI_STUB_VISION: "1"
run: |
cd backend && uvicorn main:app --host 127.0.0.1 --port 8765 &
sleep 5
curl -sf http://127.0.0.1:8765/health/live
- name: Launch gate (API smoke)
env:
CEPHEUS_API_URL: http://127.0.0.1:8765
CEPHEUS_API_KEY: test-key
run: node cepheus/scripts/launch-gate.mjs
deploy-backend:
needs: quality-gate
runs-on: ubuntu-latest
steps:
- name: Check backend credentials
id: creds
env:
WIFP: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
WIFSA: ${{ secrets.GCP_WIF_SERVICE_ACCOUNT }}
KEY: ${{ secrets.GCP_SA_KEY }}
run: |
if [ -n "$WIFP" ] && [ -n "$WIFSA" ]; then
echo "run=true" >> "$GITHUB_OUTPUT"
echo "auth=wif" >> "$GITHUB_OUTPUT"
elif [ -n "$KEY" ]; then
echo "run=true" >> "$GITHUB_OUTPUT"
echo "auth=key" >> "$GITHUB_OUTPUT"
else
echo "run=false" >> "$GITHUB_OUTPUT"
echo "skip backend deploy: set GCP WIF or GCP_SA_KEY (see docs/CI_GITHUB.md)" >> "$GITHUB_STEP_SUMMARY"
fi
- uses: actions/checkout@v4
if: steps.creds.outputs.run == 'true'
- name: Authenticate to Google Cloud (Workload Identity Federation)
if: steps.creds.outputs.run == 'true' && steps.creds.outputs.auth == 'wif'
uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.GCP_WIF_SERVICE_ACCOUNT }}
- name: Authenticate to Google Cloud (JSON key, legacy)
if: steps.creds.outputs.run == 'true' && steps.creds.outputs.auth == 'key'
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_SA_KEY }}
- uses: google-github-actions/setup-gcloud@v2
if: steps.creds.outputs.run == 'true'
with:
project_id: ${{ env.GCP_PROJECT }}
- name: Build and push image
if: steps.creds.outputs.run == 'true'
run: |
set -eux
gcloud config set project "$GCP_PROJECT"
gcloud auth configure-docker "${GCP_REGION}-docker.pkg.dev" --quiet
TAG="${GITHUB_SHA:0:12}"
echo "IMAGE_TAG=$TAG" >> "$GITHUB_ENV"
docker build -f backend/Dockerfile.hf -t "${AR_REPO}:${TAG}" -t "${AR_REPO}:latest" ./backend
docker push "${AR_REPO}:${TAG}"
docker push "${AR_REPO}:latest"
- name: Deploy Cloud Run
if: steps.creds.outputs.run == 'true'
env:
CEPHEUS_API_KEY: ${{ secrets.CEPHEUS_API_KEY }}
CEPHEUS_JWT_SECRET: ${{ secrets.CEPHEUS_JWT_SECRET }}
CEPHEUS_AUTH_USERS: ${{ secrets.CEPHEUS_AUTH_USERS }}
CORS_ORIGINS: ${{ secrets.CORS_ORIGINS }}
run: |
if [ -z "$CEPHEUS_API_KEY" ] || [ -z "$CEPHEUS_JWT_SECRET" ]; then
echo "skip Cloud Run deploy: set CEPHEUS_API_KEY and CEPHEUS_JWT_SECRET in GitHub secrets" >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
gcloud run deploy "$CLOUD_RUN_SERVICE" \
--image "${AR_REPO}:${IMAGE_TAG}" \
--region "$GCP_REGION" \
--project "$GCP_PROJECT" \
--allow-unauthenticated \
--set-env-vars "^@^CEPHEUS_CLOUD=1@CEPHEUS_PRODUCTION=1@CEPHEUS_AUTH_DEV_MODE=0@CORS_ORIGINS=${CORS_ORIGINS}@CEPHEUS_API_KEY=${CEPHEUS_API_KEY}@CEPHEUS_JWT_SECRET=${CEPHEUS_JWT_SECRET}@CEPHEUS_AUTH_USERS=${CEPHEUS_AUTH_USERS}" \
--memory 2Gi \
--cpu 2 \
--timeout 3600 \
--max-instances 5
deploy-frontend:
needs: quality-gate
runs-on: ubuntu-latest
steps:
- name: Check frontend credentials
id: creds
env:
WIFP: ${{ secrets.FIREBASE_WORKLOAD_IDENTITY_PROVIDER }}
WIFSA: ${{ secrets.FIREBASE_WIF_SERVICE_ACCOUNT }}
KEY: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
run: |
if [ -n "$WIFP" ] && [ -n "$WIFSA" ]; then
echo "run=true" >> "$GITHUB_OUTPUT"
echo "auth=wif" >> "$GITHUB_OUTPUT"
elif [ -n "$KEY" ]; then
echo "run=true" >> "$GITHUB_OUTPUT"
echo "auth=key" >> "$GITHUB_OUTPUT"
else
echo "run=false" >> "$GITHUB_OUTPUT"
echo "skip hosting deploy: set Firebase WIF or FIREBASE_SERVICE_ACCOUNT (see docs/CI_GITHUB.md)" >> "$GITHUB_STEP_SUMMARY"
fi
- uses: actions/checkout@v4
if: steps.creds.outputs.run == 'true'
- name: Authenticate for Firebase (Workload Identity Federation)
if: steps.creds.outputs.run == 'true' && steps.creds.outputs.auth == 'wif'
uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ secrets.FIREBASE_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.FIREBASE_WIF_SERVICE_ACCOUNT }}
- name: Authenticate for Firebase (JSON key, legacy)
if: steps.creds.outputs.run == 'true' && steps.creds.outputs.auth == 'key'
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
- uses: actions/setup-node@v4
if: steps.creds.outputs.run == 'true'
with:
node-version: "20"
cache: npm
cache-dependency-path: cepheus/package-lock.json
- name: Install and build
if: steps.creds.outputs.run == 'true'
run: |
set -eux
cd cepheus
npm ci
npm run build
- name: Deploy to Firebase Hosting
if: steps.creds.outputs.run == 'true'
run: npx -y firebase-tools@latest deploy --only hosting --project "$FIREBASE_PROJECT" --non-interactive