[pull] main from tldraw:main #2278
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 .com | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, labeled, unlabeled] | |
| push: | |
| branches: | |
| - main | |
| - production | |
| permissions: write-all | |
| env: | |
| CI: 1 | |
| PRINT_GITHUB_ANNOTATIONS: 1 | |
| TLDRAW_ENV: ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} | |
| defaults: | |
| run: | |
| shell: bash | |
| jobs: | |
| deploy: | |
| name: Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} | |
| timeout-minutes: 15 | |
| runs-on: ubuntu-latest-16-cores-arm-open | |
| environment: ${{ github.ref == 'refs/heads/production' && 'deploy-production' || 'deploy-staging' }} | |
| concurrency: dotcom-${{ github.ref == 'refs/heads/production' && 'deploy-production' || github.ref }} | |
| if: | | |
| github.event_name == 'push' || | |
| (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'dotcom-preview-please')) | |
| steps: | |
| # - name: Notify initial start | |
| # uses: MineBartekSA/discord-webhook@v2 | |
| # if: github.ref == 'refs/heads/production' | |
| # with: | |
| # webhook: ${{ secrets.DISCORD_DEPLOY_WEBHOOK_URL }} | |
| # content: 'Preparing ${{ env.TLDRAW_ENV }} dotcom deploy: ${{ github.event.head_commit.message }} by ${{ github.event.head_commit.author.name }}' | |
| - name: Check out code | |
| uses: actions/checkout@v6 | |
| with: | |
| submodules: true | |
| fetch-depth: 0 | |
| - uses: ./.github/actions/setup | |
| - name: Determine zero deploy target | |
| id: deploy_target | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| if [[ "${GITHUB_REF}" == "refs/heads/production" ]]; then | |
| echo "DEPLOY_ZERO=false" >> $GITHUB_ENV | |
| elif [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then | |
| echo "DEPLOY_ZERO=flyio-multinode" >> $GITHUB_ENV | |
| elif echo "${{ toJSON(github.event.pull_request.labels.*.name) }}" | grep -q "preview-flyio-zero-deploy-please"; then | |
| echo "DEPLOY_ZERO=flyio" >> $GITHUB_ENV | |
| elif git fetch origin "${GITHUB_BASE_REF}" && git diff --name-only origin/"${GITHUB_BASE_REF}"...HEAD | grep -q '^apps/dotcom/'; then | |
| # gh pr edit ${{ github.event.pull_request.number }} --add-label "preview-flyio-zero-deploy-please" | |
| # echo "DEPLOY_ZERO=flyio" >> $GITHUB_ENV | |
| echo "DEPLOY_ZERO=false" >> $GITHUB_ENV | |
| else | |
| echo "DEPLOY_ZERO=false" >> $GITHUB_ENV | |
| fi | |
| - name: Install Supabase CLI | |
| if: github.event_name == 'pull_request' | |
| uses: supabase/setup-cli@v1 | |
| with: | |
| version: latest | |
| - name: Delete Supabase branch | |
| if: contains(github.event.pull_request.labels.*.name, 'reset-preview-db') | |
| env: | |
| SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} | |
| run: | | |
| supabase branches delete "pr-${{ github.event.number }}" \ | |
| --project-ref "${{ vars.SUPABASE_PREVIEW_PROJECT_ID }}" || echo "::warning::Failed to delete Supabase branch (may not exist yet)" | |
| - name: Create Supabase preview branch | |
| if: github.event_name == 'pull_request' | |
| env: | |
| SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} | |
| run: | | |
| BRANCH_NAME="pr-${{ github.event.number }}" | |
| PROJECT_REF="${{ vars.SUPABASE_PREVIEW_PROJECT_ID }}" | |
| # Create branch if it doesn't already exist. | |
| if supabase branches get "$BRANCH_NAME" \ | |
| --project-ref "$PROJECT_REF" > /dev/null 2>&1; then | |
| echo "Branch $BRANCH_NAME already exists." | |
| else | |
| supabase branches create "$BRANCH_NAME" --project-ref "$PROJECT_REF" > /dev/null | |
| echo "Branch $BRANCH_NAME created." | |
| fi | |
| # Wait for branch to be ready, then extract connection strings. | |
| # Write to a temp file to avoid secrets leaking into CI step output. | |
| BRANCH_JSON=$(mktemp) | |
| for i in $(seq 1 24); do | |
| supabase branches get "$BRANCH_NAME" \ | |
| --project-ref "$PROJECT_REF" \ | |
| -o json > "$BRANCH_JSON" 2>/dev/null | |
| POSTGRES_URL_TRANSACTION=$(jq -r '.POSTGRES_URL // empty' "$BRANCH_JSON") | |
| if [ -n "$POSTGRES_URL_TRANSACTION" ]; then break; fi | |
| echo "Waiting for branch to be ready ($i/24)..." | |
| sleep 5 | |
| done | |
| POSTGRES_URL_NON_POOLING=$(jq -r '.POSTGRES_URL_NON_POOLING' "$BRANCH_JSON") | |
| rm "$BRANCH_JSON" | |
| if [ -z "$POSTGRES_URL_TRANSACTION" ]; then | |
| echo "::error::Branch not ready after 2 minutes — POSTGRES_URL still empty" | |
| exit 1 | |
| fi | |
| if [ -z "$POSTGRES_URL_NON_POOLING" ] || [ "$POSTGRES_URL_NON_POOLING" = "null" ]; then | |
| echo "::error::Failed to get POSTGRES_URL_NON_POOLING from branch" | |
| exit 1 | |
| fi | |
| # Rewrite transaction pooler (port 6543, IPv6-only) to session pooler | |
| # (port 5432, IPv4-compatible) since GitHub Actions runners are IPv4-only. | |
| POSTGRES_URL="${POSTGRES_URL_TRANSACTION/:6543/:5432}" | |
| echo "::add-mask::$POSTGRES_URL" | |
| echo "::add-mask::$POSTGRES_URL_TRANSACTION" | |
| echo "::add-mask::$POSTGRES_URL_NON_POOLING" | |
| echo "POSTGRES_URL=$POSTGRES_URL" >> "$GITHUB_ENV" | |
| echo "POSTGRES_URL_NON_POOLING=$POSTGRES_URL_NON_POOLING" >> "$GITHUB_ENV" | |
| - name: Build types | |
| run: yarn build-types | |
| - name: Setup Fly cli | |
| if: env.DEPLOY_ZERO == 'flyio' || env.DEPLOY_ZERO == 'flyio-multinode' | |
| uses: superfly/flyctl-actions/setup-flyctl@master | |
| - name: Install PostgreSQL client | |
| if: env.DEPLOY_ZERO == 'flyio' || env.DEPLOY_ZERO == 'flyio-multinode' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y postgresql-client | |
| - name: Deploy | |
| run: yarn tsx internal/scripts/deploy-dotcom.ts | |
| env: | |
| RELEASE_COMMIT_HASH: ${{ github.sha }} | |
| GH_TOKEN: ${{ github.token }} | |
| APP_ORIGIN: ${{ vars.APP_ORIGIN }} | |
| ASSET_UPLOAD: ${{ vars.ASSET_UPLOAD }} | |
| IMAGE_WORKER: ${{ vars.IMAGE_WORKER }} | |
| MULTIPLAYER_SERVER: ${{ vars.MULTIPLAYER_SERVER }} | |
| USER_CONTENT_SENTRY_DSN: ${{ secrets.USER_CONTENT_SENTRY_DSN }} | |
| USER_CONTENT_URL: ${{ vars.USER_CONTENT_URL }} | |
| NEXT_PUBLIC_GOOGLE_CLOUD_PROJECT_NUMBER: ${{ vars.NEXT_PUBLIC_GOOGLE_CLOUD_PROJECT_NUMBER }} | |
| SUPABASE_LITE_URL: ${{ vars.SUPABASE_LITE_URL }} | |
| VERCEL_ORG_ID: ${{ vars.VERCEL_ORG_ID }} | |
| VERCEL_PROJECT_ID: ${{ vars.VERCEL_DOTCOM_PROJECT_ID }} | |
| ANALYTICS_API_URL: ${{ secrets.ANALYTICS_API_URL }} | |
| ANALYTICS_API_TOKEN: ${{ secrets.ANALYTICS_API_TOKEN }} | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| ASSET_UPLOAD_SENTRY_DSN: ${{ secrets.ASSET_UPLOAD_SENTRY_DSN }} | |
| BOTCOM_POSTGRES_CONNECTION_STRING: ${{ env.POSTGRES_URL_NON_POOLING || secrets.BOTCOM_POSTGRES_CONNECTION_STRING }} | |
| BOTCOM_POSTGRES_POOLED_CONNECTION_STRING: ${{ env.POSTGRES_URL || secrets.BOTCOM_POSTGRES_POOLED_CONNECTION_STRING }} | |
| CLERK_SECRET_KEY: ${{ secrets.CLERK_SECRET_KEY }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| DISCORD_DEPLOY_WEBHOOK_URL: ${{ secrets.DISCORD_DEPLOY_WEBHOOK_URL }} | |
| DISCORD_FEEDBACK_WEBHOOK_URL: ${{ secrets.DISCORD_FEEDBACK_WEBHOOK_URL }} | |
| DISCORD_HEALTH_WEBHOOK_URL: ${{ secrets.DISCORD_HEALTH_WEBHOOK_URL }} | |
| GC_MAPS_API_KEY: ${{ secrets.GC_MAPS_API_KEY }} | |
| GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} | |
| HEALTH_CHECK_BEARER_TOKEN: ${{ secrets.HEALTH_CHECK_BEARER_TOKEN }} | |
| HEALTH_WORKER_UPDOWN_WEBHOOK_PATH: ${{ secrets.HEALTH_WORKER_UPDOWN_WEBHOOK_PATH }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} | |
| R2_ACCESS_KEY_SECRET: ${{ secrets.R2_ACCESS_KEY_SECRET }} | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| SENTRY_CSP_REPORT_URI: ${{ secrets.SENTRY_CSP_REPORT_URI }} | |
| SENTRY_DSN: ${{ secrets.SENTRY_DSN }} | |
| SUPABASE_LITE_ANON_KEY: ${{ secrets.SUPABASE_LITE_ANON_KEY }} | |
| TLDRAW_LICENSE: ${{ secrets.TLDRAW_LICENSE }} | |
| VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} | |
| VITE_CLERK_PUBLISHABLE_KEY: ${{ secrets.VITE_CLERK_PUBLISHABLE_KEY }} | |
| VITE_POSTHOG_KEY: ${{ secrets.VITE_POSTHOG_KEY }} | |
| VITE_GA4_MEASUREMENT_ID: ${{ secrets.VITE_GA4_MEASUREMENT_ID }} | |
| WORKER_SENTRY_DSN: ${{ secrets.WORKER_SENTRY_DSN }} | |
| FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} | |
| PIERRE_KEY: ${{ secrets.PIERRE_KEY }} | |
| ZERO_ADMIN_PASSWORD: ${{ secrets.ZERO_ADMIN_PASSWORD }} | |
| ZERO_R2_ENDPOINT: ${{ secrets.ZERO_R2_ENDPOINT }} | |
| ZERO_R2_BUCKET_NAME: ${{ secrets.ZERO_R2_BUCKET_NAME }} | |
| ZERO_R2_ACCESS_KEY_ID: ${{ secrets.ZERO_R2_ACCESS_KEY_ID }} | |
| ZERO_R2_SECRET_ACCESS_KEY: ${{ secrets.ZERO_R2_SECRET_ACCESS_KEY }} |