| |
| |
| |
| |
| 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 |
|
|