Add BookStack analytics dashboard with user and page title tracking #15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy BookStack Agent (Cloud Run) | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - 'bookstack_agent/**' | |
| - 'src/aieng_bot/bookstack/**' | |
| - '.github/workflows/deploy-bookstack-agent.yml' | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: false | |
| env: | |
| PROJECT_ID: coderd | |
| GAR_LOCATION: us-central1 | |
| REPOSITORY: catalog | |
| REGION: us-central1 | |
| jobs: | |
| # ------------------------------------------------------------------------- | |
| # Job 1 — FastAPI backend | |
| # ------------------------------------------------------------------------- | |
| deploy-api: | |
| name: Deploy FastAPI backend | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write | |
| env: | |
| SERVICE_NAME: bookstack-agent-api | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Free disk space | |
| run: | | |
| sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/share/boost "$AGENT_TOOLSDIRECTORY" | |
| docker system prune -af --volumes | |
| - uses: docker/setup-buildx-action@v4 | |
| - name: Authenticate to Google Cloud | |
| uses: google-github-actions/auth@v3 | |
| with: | |
| workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} | |
| service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }} | |
| - uses: google-github-actions/setup-gcloud@v3 | |
| - name: Configure Docker for Artifact Registry | |
| run: gcloud auth configure-docker ${{ env.GAR_LOCATION }}-docker.pkg.dev --quiet | |
| - name: Ensure Artifact Registry repository exists | |
| run: | | |
| gcloud artifacts repositories describe ${{ env.REPOSITORY }} \ | |
| --location=${{ env.GAR_LOCATION }} 2>/dev/null || \ | |
| gcloud artifacts repositories create ${{ env.REPOSITORY }} \ | |
| --repository-format=docker \ | |
| --location=${{ env.GAR_LOCATION }} \ | |
| --description="Docker repository for Catalog services" | |
| - name: Build and push API image | |
| id: build | |
| run: | | |
| IMAGE="${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.SERVICE_NAME }}" | |
| docker build \ | |
| -f bookstack_agent/api/Dockerfile \ | |
| -t "${IMAGE}:${{ github.sha }}" \ | |
| -t "${IMAGE}:latest" \ | |
| --cache-from type=gha,scope=${{ env.SERVICE_NAME }} \ | |
| --cache-to type=gha,mode=max,scope=${{ env.SERVICE_NAME }} \ | |
| . | |
| docker push "${IMAGE}:${{ github.sha }}" | |
| docker push "${IMAGE}:latest" | |
| echo "image=${IMAGE}:${{ github.sha }}" >> $GITHUB_OUTPUT | |
| - name: Deploy API to Cloud Run | |
| id: deploy | |
| run: | | |
| gcloud run deploy ${{ env.SERVICE_NAME }} \ | |
| --image ${{ steps.build.outputs.image }} \ | |
| --region ${{ env.REGION }} \ | |
| --platform managed \ | |
| --allow-unauthenticated \ | |
| --memory=1Gi \ | |
| --cpu=1 \ | |
| --timeout=300s \ | |
| --max-instances=10 \ | |
| --min-instances=1 \ | |
| --concurrency=80 \ | |
| --port=8080 \ | |
| --set-env-vars="ANTHROPIC_API_KEY=${{ secrets.ANTHROPIC_API_KEY }},BOOKSTACK_URL=https://bookstack.vectorinstitute.ai,BOOKSTACK_TOKEN_ID=${{ secrets.BOOKSTACK_TOKEN_ID }},BOOKSTACK_TOKEN_SECRET=${{ secrets.BOOKSTACK_TOKEN_SECRET }}" \ | |
| --update-labels="deployed-by=github-actions,commit=${{ github.sha }},service=${{ env.SERVICE_NAME }}" \ | |
| --quiet | |
| API_URL=$(gcloud run services describe ${{ env.SERVICE_NAME }} \ | |
| --region ${{ env.REGION }} --format 'value(status.url)') | |
| echo "url=$API_URL" >> $GITHUB_OUTPUT | |
| echo "✓ API deployed at $API_URL" | |
| - name: Health-check API | |
| run: | | |
| URL="${{ steps.deploy.outputs.url }}/api/health" | |
| for i in $(seq 1 20); do | |
| if curl -sf --max-time 10 "$URL" > /dev/null; then | |
| echo "✓ API healthy"; exit 0 | |
| fi | |
| echo "Attempt $i/20…"; sleep 5 | |
| done | |
| echo "✗ API health check failed"; exit 1 | |
| outputs: | |
| api_url: ${{ steps.deploy.outputs.url }} | |
| # ------------------------------------------------------------------------- | |
| # Job 2 — Next.js frontend | |
| # ------------------------------------------------------------------------- | |
| deploy-ui: | |
| name: Deploy Next.js frontend | |
| runs-on: ubuntu-latest | |
| needs: deploy-api | |
| permissions: | |
| contents: read | |
| id-token: write | |
| env: | |
| SERVICE_NAME: bookstack-agent-ui | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Free disk space | |
| run: | | |
| sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/share/boost "$AGENT_TOOLSDIRECTORY" | |
| docker system prune -af --volumes | |
| - uses: docker/setup-buildx-action@v4 | |
| - name: Authenticate to Google Cloud | |
| uses: google-github-actions/auth@v3 | |
| with: | |
| workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} | |
| service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }} | |
| - uses: google-github-actions/setup-gcloud@v3 | |
| - name: Configure Docker for Artifact Registry | |
| run: gcloud auth configure-docker ${{ env.GAR_LOCATION }}-docker.pkg.dev --quiet | |
| - name: Build and push UI image | |
| id: build | |
| run: | | |
| IMAGE="${{ env.GAR_LOCATION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.SERVICE_NAME }}" | |
| docker build \ | |
| -f bookstack_agent/ui/Dockerfile \ | |
| -t "${IMAGE}:${{ github.sha }}" \ | |
| -t "${IMAGE}:latest" \ | |
| --build-arg NEXT_PUBLIC_APP_URL="https://bookstack.vectorinstitute.ai" \ | |
| --build-arg NEXT_PUBLIC_GOOGLE_CLIENT_ID="${{ secrets.GOOGLE_CLIENT_ID }}" \ | |
| --cache-from type=gha,scope=${{ env.SERVICE_NAME }} \ | |
| --cache-to type=gha,mode=max,scope=${{ env.SERVICE_NAME }} \ | |
| bookstack_agent/ui/ | |
| docker push "${IMAGE}:${{ github.sha }}" | |
| docker push "${IMAGE}:latest" | |
| echo "image=${IMAGE}:${{ github.sha }}" >> $GITHUB_OUTPUT | |
| - name: Deploy UI to Cloud Run | |
| id: deploy | |
| run: | | |
| gcloud run deploy ${{ env.SERVICE_NAME }} \ | |
| --image ${{ steps.build.outputs.image }} \ | |
| --region ${{ env.REGION }} \ | |
| --platform managed \ | |
| --allow-unauthenticated \ | |
| --memory=512Mi \ | |
| --cpu=1 \ | |
| --timeout=60s \ | |
| --max-instances=10 \ | |
| --min-instances=0 \ | |
| --concurrency=80 \ | |
| --port=8080 \ | |
| --set-env-vars="NODE_ENV=production,NEXT_PUBLIC_APP_URL=https://bookstack.vectorinstitute.ai,BOOKSTACK_API_URL=${{ needs.deploy-api.outputs.api_url }},NEXT_PUBLIC_GOOGLE_CLIENT_ID=${{ secrets.GOOGLE_CLIENT_ID }},GOOGLE_CLIENT_SECRET=${{ secrets.GOOGLE_CLIENT_SECRET }},SESSION_SECRET=${{ secrets.SESSION_SECRET }},REDIRECT_URI=https://bookstack.vectorinstitute.ai/aieng-bot/api/auth/callback,ALLOWED_DOMAINS=vectorinstitute.ai" \ | |
| --update-labels="deployed-by=github-actions,commit=${{ github.sha }},service=${{ env.SERVICE_NAME }}" \ | |
| --quiet | |
| UI_URL=$(gcloud run services describe ${{ env.SERVICE_NAME }} \ | |
| --region ${{ env.REGION }} --format 'value(status.url)') | |
| echo "url=$UI_URL" >> $GITHUB_OUTPUT | |
| echo "✓ UI deployed at $UI_URL" | |
| - name: Verify UI | |
| run: | | |
| URL="${{ steps.deploy.outputs.url }}/aieng-bot" | |
| for i in $(seq 1 20); do | |
| if curl -sf --max-time 10 "$URL" > /dev/null; then | |
| echo "✓ UI responding"; exit 0 | |
| fi | |
| echo "Attempt $i/20…"; sleep 5 | |
| done | |
| echo "✗ UI health check failed"; exit 1 | |
| - name: Deployment summary | |
| run: | | |
| cat >> $GITHUB_STEP_SUMMARY << 'EOF' | |
| ## 🚀 BookStack Agent Deployed | |
| | Service | URL | | |
| |---------|-----| | |
| | FastAPI API | ${{ needs.deploy-api.outputs.api_url }} | | |
| | Next.js UI | ${{ steps.deploy.outputs.url }} | | |
| **Commit:** `${{ github.sha }}` | |
| ### Load Balancer setup for bookstack.vectorinstitute.ai | |
| The load balancer must route: | |
| - `bookstack.vectorinstitute.ai/aieng-bot/*` → `bookstack-agent-ui` Cloud Run service | |
| - `bookstack.vectorinstitute.ai/*` (default) → BookStack VM backend | |
| Run once to wire up the UI service: | |
| ```bash | |
| # Create serverless NEG for the UI Cloud Run service | |
| gcloud compute network-endpoint-groups create bookstack-agent-ui-neg \ | |
| --region=us-central1 \ | |
| --network-endpoint-type=serverless \ | |
| --cloud-run-service=bookstack-agent-ui | |
| # Create backend service | |
| gcloud compute backend-services create bookstack-agent-ui-backend \ | |
| --global \ | |
| --load-balancing-scheme=EXTERNAL_MANAGED | |
| gcloud compute backend-services add-backend bookstack-agent-ui-backend \ | |
| --global \ | |
| --network-endpoint-group=bookstack-agent-ui-neg \ | |
| --network-endpoint-group-region=us-central1 | |
| # Add path rule to the existing bookstack.vectorinstitute.ai URL map | |
| # (replace URLMAP_NAME with the actual name) | |
| gcloud compute url-maps import URLMAP_NAME --global << YAML | |
| pathMatchers: | |
| - name: bookstack-paths | |
| defaultService: BOOKSTACK_VM_BACKEND | |
| pathRules: | |
| - paths: ["/aieng-bot", "/aieng-bot/*"] | |
| service: bookstack-agent-ui-backend | |
| YAML | |
| ``` | |
| EOF |