Merge pull request #216 from dtinit/GCP/fix-static-url-staging #19
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
| # ActivityPub Testbed CI/CD Pipeline | |
| # | |
| # This workflow provides continuous integration testing and automated deployment to Google Cloud Run. | |
| # | |
| # ## Workflow Jobs | |
| # | |
| # 1. test-python: Runs Django tests with PostgreSQL database | |
| # 2. deploy: Deploys to Google Cloud Run (staging or production) | |
| # | |
| # ## Deployment Flow | |
| # | |
| # - Push to main branch: Automatically deploys to Staging environment | |
| # - Manual trigger (workflow_dispatch): Choose Staging or Production environment | |
| # | |
| # ## Required Configuration | |
| # | |
| # ### GitHub Environment Variables (per environment: Staging, Production) | |
| # | |
| # - DJANGO_SETTINGS_MODULE: Python path to Django settings module for the environment | |
| # - GCP_PROJECT_ID: Google Cloud Platform project identifier | |
| # - CLOUD_RUN_SERVICE_NAME: Name of the Cloud Run service to deploy to | |
| # - SERVICE_ACCOUNT_NAME: Email address of the service account used for Cloud Run | |
| # - DATABASE_NAME: PostgreSQL database name in Cloud SQL | |
| # - ARTIFACT_REGISTRY: Name of the container image | |
| # | |
| # ### GitHub Environment Secrets (per environment: Staging, Production) | |
| # | |
| # - GCP_CREDENTIALS: JSON credentials for the GCP service account (for authentication and storage) | |
| # - CLOUD_SQL_ICN: Cloud SQL Instance Connection Name (format: PROJECT:REGION:INSTANCE) | |
| # - POSTGRES_CREDENTIALS: PostgreSQL credentials (format: username:password, may contain special characters) | |
| # - DJANGO_SECRET_KEY: Django secret key for cryptographic signing | |
| # | |
| # ### Shared Secret (available to all environments) | |
| # | |
| # - EMAIL_HOST_PASSWORD: SMTP password for sending emails (e.g., Gmail app password) | |
| # | |
| # ## Deployment Steps | |
| # | |
| # The deploy job performs these operations: | |
| # 1. Authenticates to Google Cloud Platform | |
| # 2. Runs database migrations via Cloud SQL Auth Proxy | |
| # 3. Compiles SCSS to CSS | |
| # 4. Collects static files and uploads to Google Cloud Storage | |
| # 5. Configures CORS for the storage bucket | |
| # 6. Deploys application to Cloud Run using --source flag (builds from Dockerfile) | |
| name: CI-CD | |
| on: | |
| pull_request: | |
| branches: [ main ] | |
| push: | |
| branches: [ main ] | |
| workflow_dispatch: | |
| inputs: | |
| environment: | |
| type: environment | |
| description: Select the target environment | |
| jobs: | |
| # Test Python/Django code with PostgreSQL database | |
| test-python: | |
| runs-on: ubuntu-latest | |
| services: | |
| postgres: | |
| image: postgres:17 | |
| env: | |
| POSTGRES_USER: activitypub_testbed_dbuser | |
| POSTGRES_PASSWORD: activitypub_testbed_testpass | |
| POSTGRES_DB: activitypub_testbed_database | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| ports: | |
| - 5432:5432 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install Dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| - name: Create test environment file | |
| run: | | |
| echo "DEBUG=True" > .env | |
| echo "DJANGO_SECRET_KEY=test-secret-key-for-ci" >> .env | |
| echo "DJ_DATABASE_CONN_STRING=postgres://activitypub_testbed_dbuser:activitypub_testbed_testpass@localhost:5432/activitypub_testbed_database" >> .env | |
| - name: Run Tests | |
| run: pytest --ds=testbed.settings.ci | |
| # Deploy to Google Cloud Run | |
| deploy: | |
| # Only deploy on push to main or manual trigger | |
| if: | | |
| (github.event_name == 'push' && github.ref == 'refs/heads/main') | |
| || github.event_name == 'workflow_dispatch' | |
| # Wait for tests to pass | |
| needs: [test-python] | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: ${{ inputs.environment || 'Staging' }} | |
| env: | |
| DJANGO_SETTINGS_MODULE: ${{ vars.DJANGO_SETTINGS_MODULE }} | |
| # Temporary placeholder - real value passed during Cloud Run deployment | |
| EMAIL_HOST_PASSWORD: 'placeholder' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install Dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| - name: Create service-account-credentials.json | |
| uses: jsdaniell/[email protected] | |
| with: | |
| name: service-account-credentials.json | |
| json: ${{ secrets.GCP_CREDENTIALS }} | |
| - uses: 'google-github-actions/auth@v2' | |
| with: | |
| project_id: ${{ vars.GCP_PROJECT_ID }} | |
| credentials_json: '${{ secrets.GCP_CREDENTIALS }}' | |
| - name: Set up Cloud SDK | |
| uses: 'google-github-actions/setup-gcloud@v2' | |
| with: | |
| project_id: ${{ vars.GCP_PROJECT_ID }} | |
| - name: URL-encode database credentials for special characters | |
| env: | |
| POSTGRES_CREDENTIALS: ${{ secrets.POSTGRES_CREDENTIALS }} | |
| run: | | |
| # URL encode credentials to handle special characters like *, !, (, ) | |
| # Extract and encode password securely without exposing it in logs | |
| ENCODED_CREDENTIALS=$(python3 -c " | |
| import urllib.parse, os, sys | |
| try: | |
| creds = os.environ['POSTGRES_CREDENTIALS'] | |
| if ':' in creds: | |
| user, password = creds.split(':', 1) | |
| encoded_password = urllib.parse.quote(password, safe='') | |
| print(f'{user}:{encoded_password}') | |
| else: | |
| print(creds) | |
| except Exception: | |
| sys.exit(1) | |
| ") | |
| if [ $? -eq 0 ]; then | |
| echo "ENCODED_DB_CREDENTIALS=$ENCODED_CREDENTIALS" >> $GITHUB_ENV | |
| echo "Database credentials encoded successfully" | |
| else | |
| echo "Failed to encode database credentials" | |
| exit 1 | |
| fi | |
| - name: Download and run the Cloud SQL Auth Proxy | |
| run: | | |
| curl -o cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.10.1/cloud-sql-proxy.linux.amd64 | |
| chmod +x cloud-sql-proxy | |
| ./cloud-sql-proxy --address 0.0.0.0 --port 1234 ${{ secrets.CLOUD_SQL_ICN }} & | |
| - name: Run migrations | |
| env: | |
| DJ_DATABASE_CONN_STRING: postgres://${{ env.ENCODED_DB_CREDENTIALS }}@localhost:1234/${{ vars.DATABASE_NAME }} | |
| DJANGO_SECRET_KEY: ${{ secrets.DJANGO_SECRET_KEY }} | |
| run: python3 manage.py migrate | |
| - name: Stop Cloud SQL Auth Proxy | |
| run: pkill cloud-sql-proxy | |
| - name: Compile SCSS | |
| env: | |
| DJANGO_SECRET_KEY: ${{ secrets.DJANGO_SECRET_KEY }} | |
| DJ_DATABASE_CONN_STRING: postgres://${{ env.ENCODED_DB_CREDENTIALS }}@localhost:1234/${{ vars.DATABASE_NAME }} | |
| run: python3 manage.py compilescss | |
| - name: Collect static files | |
| env: | |
| DJANGO_SECRET_KEY: ${{ secrets.DJANGO_SECRET_KEY }} | |
| DJ_DATABASE_CONN_STRING: postgres://${{ env.ENCODED_DB_CREDENTIALS }}@localhost:1234/${{ vars.DATABASE_NAME }} | |
| run: python3 manage.py collectstatic --no-input --ignore=*.scss | |
| - name: Configure CORS for storage bucket | |
| env: | |
| DJANGO_SECRET_KEY: ${{ secrets.DJANGO_SECRET_KEY }} | |
| DJ_DATABASE_CONN_STRING: postgres://${{ env.ENCODED_DB_CREDENTIALS }}@localhost:1234/${{ vars.DATABASE_NAME }} | |
| run: | | |
| python3 manage.py create_gcp_cors_config | |
| export GS_BUCKET_NAME=$(python3 -c "from django.conf import settings; print(settings.GS_BUCKET_NAME)") | |
| gcloud storage buckets update gs://$GS_BUCKET_NAME --cors-file=gcp-cors-config.json | |
| - name: Deploy to Cloud Run | |
| run: > | |
| gcloud run deploy ${{ vars.CLOUD_RUN_SERVICE_NAME }} | |
| --region us-central1 | |
| --source . | |
| --service-account ${{ vars.SERVICE_ACCOUNT_NAME }} | |
| --add-cloudsql-instances=${{ secrets.CLOUD_SQL_ICN }} | |
| --set-env-vars DJANGO_SETTINGS_MODULE="${{ vars.DJANGO_SETTINGS_MODULE }}" | |
| --set-env-vars DJANGO_SECRET_KEY="${{ secrets.DJANGO_SECRET_KEY }}" | |
| --set-env-vars DJ_DATABASE_CONN_STRING="postgres://${{ env.ENCODED_DB_CREDENTIALS }}@//cloudsql/${{ secrets.CLOUD_SQL_ICN }}/${{ vars.DATABASE_NAME }}" | |
| --set-env-vars EMAIL_HOST_PASSWORD="${{ secrets.EMAIL_HOST_PASSWORD }}" | |
| --allow-unauthenticated |