mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 05:10:16 +03:00
247 lines
9.9 KiB
YAML
247 lines
9.9 KiB
YAML
name: Auto SaaS Dev Deployment
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- saas-prod
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
FRONTEND_PORT: "901"
|
|
BACKEND_PORT: "902"
|
|
DEPLOY_DIR: /stirling/SAAS-DEV
|
|
|
|
jobs:
|
|
deploy-saas-dev:
|
|
runs-on: ubuntu-latest
|
|
environment: saas-dev
|
|
concurrency:
|
|
group: saas-dev-deploy
|
|
cancel-in-progress: true
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
|
|
steps:
|
|
- name: Harden Runner
|
|
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
|
with:
|
|
egress-policy: audit
|
|
|
|
- name: Check SaaS configuration
|
|
id: config
|
|
env:
|
|
PROJECT_REF: ${{ secrets.SAAS_DB_PROJECT_REF }}
|
|
run: |
|
|
echo "supabase_url=https://${PROJECT_REF}.supabase.co" >> "$GITHUB_OUTPUT"
|
|
echo "meter_endpoint=https://${PROJECT_REF}.supabase.co/functions/v1/meter-payg-units" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Checkout repository
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
|
|
|
- name: Login to GitHub Container Registry
|
|
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ github.token }}
|
|
|
|
- name: Convert repository owner to lowercase
|
|
id: repoowner
|
|
run: echo "lowercase=$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')" >> $GITHUB_OUTPUT
|
|
|
|
- name: Get commit hash
|
|
id: commit-hash
|
|
run: echo "app_short=$(git rev-parse --short=8 HEAD)" >> $GITHUB_OUTPUT
|
|
|
|
- name: Build and push backend image
|
|
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
|
with:
|
|
context: .
|
|
file: ./docker/backend/Dockerfile
|
|
push: true
|
|
cache-from: type=gha,scope=stirling-saas-backend
|
|
cache-to: type=gha,mode=max,scope=stirling-saas-backend
|
|
tags: |
|
|
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf-test:saas-backend-${{ steps.commit-hash.outputs.app_short }}
|
|
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf-test:saas-backend-latest
|
|
build-args: |
|
|
VERSION_TAG=v2-alpha
|
|
STIRLING_FLAVOR=saas
|
|
platforms: linux/amd64
|
|
|
|
- name: Build and push frontend image
|
|
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
|
with:
|
|
context: .
|
|
file: ./docker/frontend/Dockerfile
|
|
push: true
|
|
cache-from: type=gha,scope=stirling-saas-frontend
|
|
cache-to: type=gha,mode=max,scope=stirling-saas-frontend
|
|
tags: |
|
|
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf-test:saas-frontend-${{ steps.commit-hash.outputs.app_short }}
|
|
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf-test:saas-frontend-latest
|
|
build-args: |
|
|
VERSION_TAG=v2-alpha
|
|
STIRLING_FLAVOR=saas
|
|
VITE_BUILD_MODE=development
|
|
VITE_SUPABASE_URL=${{ steps.config.outputs.supabase_url }}
|
|
VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY=${{ secrets.SAAS_SUPABASE_PUBLISHABLE_KEY }}
|
|
platforms: linux/amd64
|
|
|
|
- name: Build and push AI engine image
|
|
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
|
with:
|
|
context: .
|
|
file: ./engine/Dockerfile
|
|
push: true
|
|
cache-from: type=gha,scope=stirling-saas-engine
|
|
cache-to: type=gha,mode=max,scope=stirling-saas-engine
|
|
tags: |
|
|
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf-test:saas-engine-${{ steps.commit-hash.outputs.app_short }}
|
|
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf-test:saas-engine-latest
|
|
platforms: linux/amd64
|
|
|
|
- name: Set up SSH
|
|
env:
|
|
SSH_KEY: ${{ secrets.NEW_VPS_SSH_KEY }}
|
|
run: |
|
|
mkdir -p ~/.ssh/
|
|
echo "$SSH_KEY" > ../private.key
|
|
sudo chmod 600 ../private.key
|
|
|
|
- name: Deploy to VPS
|
|
env:
|
|
IMAGE_BASE: ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf-test
|
|
IMAGE_TAG: ${{ steps.commit-hash.outputs.app_short }}
|
|
GHCR_USER: ${{ github.actor }}
|
|
GHCR_TOKEN: ${{ github.token }}
|
|
VPS_USERNAME: ${{ secrets.NEW_VPS_USERNAME }}
|
|
VPS_HOST: ${{ secrets.NEW_VPS_HOST }}
|
|
SAAS_DB_URL: ${{ secrets.SAAS_DB_URL }}
|
|
SAAS_DB_USERNAME: ${{ secrets.SAAS_DB_USERNAME || 'postgres' }}
|
|
SAAS_DB_PASSWORD: ${{ secrets.SAAS_DB_PASSWORD }}
|
|
SAAS_DB_PROJECT_REF: ${{ secrets.SAAS_DB_PROJECT_REF }}
|
|
SUPABASE_EDGE_FUNCTION_SECRET: ${{ secrets.SUPABASE_EDGE_FUNCTION_SECRET }}
|
|
PAYG_METER_ENDPOINT: ${{ steps.config.outputs.meter_endpoint }}
|
|
STIRLING_KEYGEN_ENABLED: ${{ secrets.KEYGEN_ACCOUNT_ID != '' && secrets.KEYGEN_API_TOKEN != '' && secrets.KEYGEN_POLICY_ID != '' }}
|
|
KEYGEN_ACCOUNT_ID: ${{ secrets.KEYGEN_ACCOUNT_ID }}
|
|
KEYGEN_API_TOKEN: ${{ secrets.KEYGEN_API_TOKEN }}
|
|
KEYGEN_POLICY_ID: ${{ secrets.KEYGEN_POLICY_ID }}
|
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
VOYAGE_API_KEY: ${{ secrets.VOYAGE_API_KEY }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
BASE_URL="http://${VPS_HOST}:${FRONTEND_PORT}"
|
|
|
|
yaml() {
|
|
printf "'%s'" "$(printf '%s' "$1" | sed -e "s/'/''/g" -e 's/\$/$$/g')"
|
|
}
|
|
|
|
ENGINE_SECRET="$(openssl rand -hex 32)"
|
|
AI_BACKEND_VARS="
|
|
SYSTEM_AIENGINE_ENABLED: \"true\"
|
|
SYSTEM_AIENGINE_URL: \"http://saas-engine:5001\"
|
|
APP_AI_SERVICEBASEURL: \"http://saas-engine:5001\"
|
|
STIRLING_ENGINE_SHARED_SECRET: $(yaml "$ENGINE_SECRET")"
|
|
AI_SERVICE="
|
|
|
|
saas-engine:
|
|
container_name: stirling-saas-dev-engine
|
|
image: ${IMAGE_BASE}:saas-engine-${IMAGE_TAG}
|
|
environment:
|
|
ANTHROPIC_API_KEY: $(yaml "$ANTHROPIC_API_KEY")
|
|
VOYAGE_API_KEY: $(yaml "$VOYAGE_API_KEY")
|
|
STIRLING_ENGINE_SHARED_SECRET: $(yaml "$ENGINE_SECRET")
|
|
restart: on-failure:5"
|
|
|
|
cat > docker-compose.yml << EOF
|
|
version: '3.3'
|
|
services:
|
|
saas-backend:
|
|
container_name: stirling-saas-dev-backend
|
|
image: ${IMAGE_BASE}:saas-backend-${IMAGE_TAG}
|
|
ports:
|
|
- "${BACKEND_PORT}:8080"
|
|
volumes:
|
|
- ${DEPLOY_DIR}/config:/configs:rw
|
|
- ${DEPLOY_DIR}/logs:/logs:rw
|
|
- ${DEPLOY_DIR}/storage:/storage:rw
|
|
environment:
|
|
SPRING_PROFILES_ACTIVE: "saas"
|
|
DISABLE_ADDITIONAL_FEATURES: "false"
|
|
SAAS_DB_URL: $(yaml "$SAAS_DB_URL")
|
|
SAAS_DB_USERNAME: $(yaml "$SAAS_DB_USERNAME")
|
|
SAAS_DB_PASSWORD: $(yaml "$SAAS_DB_PASSWORD")
|
|
SAAS_DB_PROJECT_REF: $(yaml "$SAAS_DB_PROJECT_REF")
|
|
SUPABASE_EDGE_FUNCTION_SECRET: $(yaml "$SUPABASE_EDGE_FUNCTION_SECRET")
|
|
PAYG_METER_ENDPOINT: $(yaml "$PAYG_METER_ENDPOINT")
|
|
STIRLING_KEYGEN_ENABLED: $(yaml "$STIRLING_KEYGEN_ENABLED")
|
|
KEYGEN_ACCOUNT_ID: $(yaml "$KEYGEN_ACCOUNT_ID")
|
|
KEYGEN_API_TOKEN: $(yaml "$KEYGEN_API_TOKEN")
|
|
KEYGEN_POLICY_ID: $(yaml "$KEYGEN_POLICY_ID")
|
|
SYSTEM_DEFAULTLOCALE: en-US
|
|
SYSTEM_MAXFILESIZE: "100"
|
|
METRICS_ENABLED: "true"
|
|
SYSTEM_GOOGLEVISIBILITY: "false"
|
|
SWAGGER_SERVER_URL: "${BASE_URL}"
|
|
baseUrl: "${BASE_URL}"${AI_BACKEND_VARS}
|
|
restart: on-failure:5
|
|
|
|
saas-frontend:
|
|
container_name: stirling-saas-dev-frontend
|
|
image: ${IMAGE_BASE}:saas-frontend-${IMAGE_TAG}
|
|
ports:
|
|
- "${FRONTEND_PORT}:80"
|
|
environment:
|
|
VITE_API_BASE_URL: "http://saas-backend:8080"
|
|
depends_on:
|
|
- saas-backend
|
|
restart: on-failure:5${AI_SERVICE}
|
|
EOF
|
|
|
|
SSH_OPTS=(-i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null)
|
|
|
|
scp "${SSH_OPTS[@]}" docker-compose.yml "${VPS_USERNAME}@${VPS_HOST}:/tmp/saas-dev-docker-compose.yml"
|
|
|
|
ssh "${SSH_OPTS[@]}" -T "${VPS_USERNAME}@${VPS_HOST}" << ENDSSH
|
|
set -e
|
|
mkdir -p ${DEPLOY_DIR}/{config,logs,storage}
|
|
mv /tmp/saas-dev-docker-compose.yml ${DEPLOY_DIR}/docker-compose.yml
|
|
chmod 600 ${DEPLOY_DIR}/docker-compose.yml
|
|
cd ${DEPLOY_DIR}
|
|
printf '%s' "${GHCR_TOKEN}" | docker login ghcr.io -u "${GHCR_USER}" --password-stdin
|
|
docker-compose down --remove-orphans 2>/dev/null || true
|
|
docker-compose pull
|
|
docker-compose up -d
|
|
docker logout ghcr.io >/dev/null 2>&1 || true
|
|
docker image prune -af --filter "until=336h" --filter "label!=keep=true" || true
|
|
ENDSSH
|
|
|
|
- name: Wait for the backend to answer
|
|
env:
|
|
VPS_HOST: ${{ secrets.NEW_VPS_HOST }}
|
|
run: |
|
|
URL="http://${VPS_HOST}:${BACKEND_PORT}/api/v1/info/status"
|
|
for i in $(seq 1 60); do
|
|
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 "$URL" || true)
|
|
if [ "$code" = "200" ]; then echo "Healthy after $((i * 10))s"; exit 0; fi
|
|
sleep 10
|
|
done
|
|
echo "::error::SaaS dev backend did not become healthy within 10 minutes"
|
|
exit 1
|
|
|
|
- name: Cleanup temporary files
|
|
if: always()
|
|
run: rm -f ../private.key docker-compose.yml
|
|
continue-on-error: true
|