From 94cbdd7b2eb1d7cbd9e82414aec65cb725f6babd Mon Sep 17 00:00:00 2001 From: jumski <9126+jumski@users.noreply.github.com> Date: Wed, 3 Dec 2025 11:31:23 +0000 Subject: [PATCH] ci: create reusable deploy-website action and separate deployment workflow (#489) # Refactor website deployment workflow with dedicated GitHub Action This PR improves the website deployment process by: 1. Creating a reusable `deploy-website` GitHub Action that: - Validates Supabase environment variables - Handles both preview and production deployments - Posts deployment comments 2. Separating deployment workflows: - Modified `ci.yml` to only handle PR preview deployments - Added new `deploy.yml` workflow for production deployments on main branch This approach provides better separation of concerns, reduces duplication, and makes the deployment process more maintainable. The reusable action ensures consistent deployment behavior across both preview and production environments. --- .github/actions/deploy-website/action.yml | 82 +++++++++++++++++++++++ .github/workflows/ci.yml | 68 ++++--------------- .github/workflows/deploy.yml | 41 ++++++++++++ 3 files changed, 135 insertions(+), 56 deletions(-) create mode 100644 .github/actions/deploy-website/action.yml create mode 100644 .github/workflows/deploy.yml diff --git a/.github/actions/deploy-website/action.yml b/.github/actions/deploy-website/action.yml new file mode 100644 index 000000000..0256cf9f4 --- /dev/null +++ b/.github/actions/deploy-website/action.yml @@ -0,0 +1,82 @@ +name: 'Deploy Website' +description: 'Deploy website to Cloudflare Pages (preview or production)' + +inputs: + environment: + description: 'Deployment environment (preview or production)' + required: true + supabase-url: + description: 'Supabase URL for the website' + required: true + supabase-anon-key: + description: 'Supabase anonymous key' + required: true + cloudflare-api-token: + description: 'Cloudflare API token' + required: true + cloudflare-account-id: + description: 'Cloudflare account ID' + required: true + plausible-proxy-url: + description: 'Plausible proxy URL (optional, typically production only)' + required: false + default: '' + preview-name: + description: 'Preview name for PR deployments (e.g., pr-123)' + required: false + default: '' + preview-url: + description: 'Preview URL for deployment comments' + required: false + default: '' + production-url: + description: 'Production URL for deployment comments' + required: true + default: 'https://pgflow.dev' + +runs: + using: 'composite' + steps: + - name: Validate Supabase environment variables + shell: bash + env: + VITE_SUPABASE_URL: ${{ inputs.supabase-url }} + VITE_SUPABASE_ANON_KEY: ${{ inputs.supabase-anon-key }} + run: | + if [ -z "$VITE_SUPABASE_URL" ]; then + echo "::error::VITE_SUPABASE_URL is not set" + exit 1 + fi + if [ -z "$VITE_SUPABASE_ANON_KEY" ]; then + echo "::error::VITE_SUPABASE_ANON_KEY is not set" + exit 1 + fi + if [[ ! "$VITE_SUPABASE_URL" =~ ^https:// ]]; then + echo "::error::VITE_SUPABASE_URL must use https:// (not http://)" + exit 1 + fi + echo "Supabase environment variables validated" + + - name: Deploy website + shell: bash + env: + CLOUDFLARE_API_TOKEN: ${{ inputs.cloudflare-api-token }} + CLOUDFLARE_ACCOUNT_ID: ${{ inputs.cloudflare-account-id }} + VITE_SUPABASE_URL: ${{ inputs.supabase-url }} + VITE_SUPABASE_ANON_KEY: ${{ inputs.supabase-anon-key }} + PLAUSIBLE_PROXY_URL: ${{ inputs.plausible-proxy-url }} + CLOUDFLARE_BRANCH: ${{ inputs.preview-name }} + run: | + if [ "${{ inputs.environment }}" = "preview" ]; then + pnpm nx run website:deploy:preview --skip-nx-cache + else + pnpm nx run website:deploy --skip-nx-cache + fi + + - name: Post deployment comment + if: always() + uses: ./.github/actions/deployment-comment + with: + project-name: Website + preview-url: ${{ inputs.preview-url }} + production-url: ${{ inputs.production-url }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7f8cdf6a..3dbd2a3ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,6 @@ name: CI on: workflow_dispatch: pull_request: - push: - branches: [main] - # TODO: Optimize - separate deployment workflow to avoid re-running tests on main concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -146,19 +143,14 @@ jobs: if: steps.check.outputs.affected == 'true' run: pnpm nx run cli:e2e - # ────────────────────────────────── 3. DEPLOY WEBSITE ─────────────────────────── + # ────────────────────────────────── 3. DEPLOY WEBSITE (PREVIEW) ─────────────────────────── deploy-website: + if: github.event_name == 'pull_request' needs: [build-and-test, edge-worker-e2e, cli-e2e] runs-on: ubuntu-latest - environment: ${{ github.event_name == 'pull_request' && 'preview' || 'production' }} + environment: preview env: NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - DEPLOYMENT_ENV: ${{ github.event_name == 'pull_request' && 'preview' || 'production' }} - VITE_SUPABASE_URL: ${{ github.event_name == 'pull_request' && secrets.WEBSITE_PREVIEW_SUPABASE_URL || secrets.WEBSITE_PRODUCTION_SUPABASE_URL }} - VITE_SUPABASE_ANON_KEY: ${{ github.event_name == 'pull_request' && secrets.WEBSITE_PREVIEW_SUPABASE_ANON_KEY || secrets.WEBSITE_PRODUCTION_SUPABASE_ANON_KEY }} - PLAUSIBLE_PROXY_URL: ${{ secrets.WEBSITE_PLAUSIBLE_PROXY_URL }} steps: - uses: actions/checkout@v4 with: @@ -169,19 +161,10 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} atlas-cloud-token: ${{ secrets.ATLAS_CLOUD_TOKEN }} - - name: Set Nx base for affected commands - run: | - echo "NX_BASE=origin/main" >> $GITHUB_ENV - echo "NX_HEAD=HEAD" >> $GITHUB_ENV - - name: Check if website is affected id: check-affected run: | - if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/main" ]]; then - # Always deploy website on main branch - echo "affected=true" >> $GITHUB_OUTPUT - echo "Main branch push - deploying website to production" - elif pnpm nx show projects --affected -t build --base="$NX_BASE" --head="$NX_HEAD" | grep -q "^website$"; then + if pnpm nx show projects --affected -t build --base=origin/main --head=HEAD | grep -q "^website$"; then echo "affected=true" >> $GITHUB_OUTPUT echo "Website is affected by changes" else @@ -189,43 +172,16 @@ jobs: echo "Website is not affected by changes - skipping deployment" fi - - name: Validate Supabase environment variables + - name: Deploy website preview if: steps.check-affected.outputs.affected == 'true' - run: | - if [ -z "$VITE_SUPABASE_URL" ]; then - echo "❌ Error: VITE_SUPABASE_URL is not set" - echo "Required GitHub secret missing: WEBSITE_${{ github.event_name == 'pull_request' && 'PREVIEW' || 'PRODUCTION' }}_SUPABASE_URL" - exit 1 - fi - if [ -z "$VITE_SUPABASE_ANON_KEY" ]; then - echo "❌ Error: VITE_SUPABASE_ANON_KEY is not set" - echo "Required GitHub secret missing: WEBSITE_${{ github.event_name == 'pull_request' && 'PREVIEW' || 'PRODUCTION' }}_SUPABASE_ANON_KEY" - exit 1 - fi - if [[ ! "$VITE_SUPABASE_URL" =~ ^https:// ]]; then - echo "❌ Error: VITE_SUPABASE_URL must use https:// (not http://)" - echo "Current value: $VITE_SUPABASE_URL" - exit 1 - fi - echo "✅ Supabase environment variables are valid" - - - name: Deploy website - id: deploy-website - if: steps.check-affected.outputs.affected == 'true' - env: - CLOUDFLARE_BRANCH: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'main' }} - run: | - if [[ "${{ github.event_name }}" == "pull_request" ]]; then - pnpm nx run website:deploy:preview --skip-nx-cache - else - pnpm nx run website:deploy --skip-nx-cache - fi - - - name: Post deployment comment - if: always() - uses: ./.github/actions/deployment-comment + uses: ./.github/actions/deploy-website with: - project-name: Website + environment: preview + supabase-url: ${{ secrets.WEBSITE_PREVIEW_SUPABASE_URL }} + supabase-anon-key: ${{ secrets.WEBSITE_PREVIEW_SUPABASE_ANON_KEY }} + cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} + cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + preview-name: pr-${{ github.event.pull_request.number }} preview-url: https://pr-${{ github.event.pull_request.number }}.pgflow.pages.dev production-url: https://pgflow.dev diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..a6fb3ecc3 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,41 @@ +name: Deploy + +on: + push: + branches: [main] + workflow_dispatch: + +concurrency: + group: deploy-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + pull-requests: write # for deployment comments + +jobs: + deploy-website: + runs-on: ubuntu-latest + environment: production + env: + NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: ./.github/actions/setup + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + atlas-cloud-token: ${{ secrets.ATLAS_CLOUD_TOKEN }} + + - name: Deploy website to production + uses: ./.github/actions/deploy-website + with: + environment: production + supabase-url: ${{ secrets.WEBSITE_PRODUCTION_SUPABASE_URL }} + supabase-anon-key: ${{ secrets.WEBSITE_PRODUCTION_SUPABASE_ANON_KEY }} + cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} + cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + plausible-proxy-url: ${{ secrets.WEBSITE_PLAUSIBLE_PROXY_URL }} + production-url: https://pgflow.dev