Skip to content

Add metadata regression test suite with Vitest #13

Add metadata regression test suite with Vitest

Add metadata regression test suite with Vitest #13

Workflow file for this run

name: CI/CD Pipeline

Check failure on line 1 in .github/workflows/ci-cd.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/ci-cd.yml

Invalid workflow file

(Line: 210, Col: 13): Unrecognized named-value: 'secrets'. Located at position 1 within expression: secrets.HYPERLIFT_DEPLOY_HOOK != ''
on:
push:
branches: ["main", "develop"]
pull_request:
branches: ["main", "develop"]
env:
REGISTRY: docker.io
IMAGE_NAME: spectracleanse-api
jobs:
# ─────────────────────────────────────────────────────────────────────────
# LINTING & SECURITY
# ─────────────────────────────────────────────────────────────────────────
security:
name: Security & Audit
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: "npm"
cache-dependency-path: package-lock.json
- name: Install dependencies
run: npm ci
- name: Run npm audit (info level allowed)
run: npm audit --audit-level=moderate || true
- name: Scan for secrets with TruffleHog
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
head: HEAD
extra_args: --debug
# ─────────────────────────────────────────────────────────────────────────
# BACKEND: TYPE-CHECK, LINT, SMOKE TEST
# ─────────────────────────────────────────────────────────────────────────
backend:
name: Backend – Type-check & Smoke Test
runs-on: ubuntu-latest
needs: security
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: "npm"
cache-dependency-path: package-lock.json
- name: Install dependencies
run: npm ci
- name: Type-check TypeScript
run: npx tsc --noEmit
- name: Smoke test – server startup
timeout-minutes: 2
run: |
export JWT_SECRET=ci-test-secret-not-real
export STRIPE_SECRET_KEY=sk_test_ci_placeholder
export STRIPE_WEBHOOK_SECRET=whsec_ci_placeholder
export STRIPE_CREATOR_PRICE_ID=price_ci_creator
export STRIPE_STUDIO_PRICE_ID=price_ci_studio
export GEMINI_API_KEY=ci_gemini_placeholder
export FRONTEND_URL=http://localhost:5173
export DB_PATH=/tmp/spectra-ci.db
export PORT=3001
node server.js > /tmp/server.log 2>&1 &
SERVER_PID=$!
for i in $(seq 1 15); do
sleep 1
if curl -sf http://localhost:3001/api/health; then
echo "Server health check passed"
kill $SERVER_PID 2>/dev/null || true
exit 0
fi
done
echo "=== Server failed to start. Logs: ===" >&2
cat /tmp/server.log >&2
kill $SERVER_PID 2>/dev/null || true
exit 1
# ─────────────────────────────────────────────────────────────────────────
# FRONTEND: TYPE-CHECK & BUILD
# ─────────────────────────────────────────────────────────────────────────
frontend:
name: Frontend – Type-check & Build
runs-on: ubuntu-latest
needs: security
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: "npm"
cache-dependency-path: package-lock.json
- name: Install dependencies
run: npm ci
- name: Type-check
run: npx tsc --noEmit
- name: Run unit tests
run: npm run test:run
- name: Build frontend
env:
VITE_API_URL: https://spectracleanse.com
run: npm run build
- name: Upload frontend artifact
uses: actions/upload-artifact@v4
with:
name: frontend-dist
path: dist/
retention-days: 3
# ─────────────────────────────────────────────────────────────────────────
# DOCKER: BUILD & PUSH
# ─────────────────────────────────────────────────────────────────────────
docker-build:
name: Docker – Build & Push
runs-on: ubuntu-latest
needs: [backend, frontend]
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver-options: image=moby/buildkit:latest
- name: Extract image metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY }}/${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=sha,prefix={{branch}}-
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Log in to Docker Hub (if pushing)
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILDKIT_INLINE_CACHE=1
# ─────────────────────────────────────────────────────────────────────────
# DEPLOY: TRIGGER HYPERLIFT (only on main branch)
# ─────────────────────────────────────────────────────────────────────────
deploy:
name: Deploy to Hyperlift
runs-on: ubuntu-latest
needs: docker-build
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Trigger Hyperlift webhook
if: ${{ secrets.HYPERLIFT_DEPLOY_HOOK != '' }}
run: |
curl -sf -X POST "${{ secrets.HYPERLIFT_DEPLOY_HOOK }}" \
-H "Content-Type: application/json" \
-d '{
"ref":"${{ github.sha }}",
"branch":"${{ github.ref_name }}",
"timestamp":"${{ github.event.head_commit.timestamp }}"
}'
- name: Deployment notification
run: |
echo "Deployment triggered for commit ${{ github.sha }}"
echo "Branch: ${{ github.ref_name }}"
echo "Author: ${{ github.actor }}"