From 209f8ebed918719ac60b4c0094b3f9b00c39a7f9 Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Thu, 28 Aug 2025 12:10:22 +0300 Subject: [PATCH 1/3] chore: secure-proof workflows --- .github/workflows/preview-build.yml | 86 +++++++++++++ .github/workflows/preview-comment.yml | 112 +++++++++++++++++ .github/workflows/preview-release.yml | 167 -------------------------- .github/workflows/trigger-tests.yml | 105 ++++++++++++++++ 4 files changed, 303 insertions(+), 167 deletions(-) create mode 100644 .github/workflows/preview-build.yml create mode 100644 .github/workflows/preview-comment.yml delete mode 100644 .github/workflows/preview-release.yml create mode 100644 .github/workflows/trigger-tests.yml diff --git a/.github/workflows/preview-build.yml b/.github/workflows/preview-build.yml new file mode 100644 index 0000000..07e8fea --- /dev/null +++ b/.github/workflows/preview-build.yml @@ -0,0 +1,86 @@ +name: Preview Build + +permissions: + contents: read + pull-requests: read + +on: + pull_request: + types: [opened, synchronize, labeled] + paths: + - 'src/**' + - 'package.json' + - 'package-lock.json' + - 'tsconfig.json' + +jobs: + build-preview: + # Only run if PR has the 'trigger: preview' label + if: | + contains(github.event.pull_request.labels.*.name, 'trigger: preview') + runs-on: ubuntu-latest + outputs: + preview-url: ${{ steps.preview.outputs.url }} + pr-number: ${{ github.event.pull_request.number }} + steps: + # Checkout fork code - safe because no secrets are available + - name: Checkout code + uses: actions/checkout@v4 + + # Log PR author for auditing + - name: Log PR author + run: | + echo "Preview build triggered by: ${{ github.event.pull_request.user.login }}" + echo "PR #${{ github.event.pull_request.number }} from fork: ${{ github.event.pull_request.head.repo.full_name }}" + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Run tests + run: npm test + continue-on-error: true # Don't fail preview on test failures + + - name: Create preview release + id: preview + run: | + set -e + echo "Creating preview release..." + OUTPUT=$(npx pkg-pr-new@latest publish --compact 2>&1) + echo "Full output:" + echo "$OUTPUT" + + # Extract the preview URL + PREVIEW_URL=$(echo "$OUTPUT" | grep -o 'https://pkg\.pr\.new/@supabase/[^[:space:]]*' | head -1) + + if [ -z "$PREVIEW_URL" ]; then + echo "Error: Failed to extract preview URL from pkg-pr-new output" + exit 1 + fi + + echo "Preview Release URL: $PREVIEW_URL" + echo "url=$PREVIEW_URL" >> $GITHUB_OUTPUT + + # Save preview info for the next workflows + - name: Save preview info + run: | + mkdir -p preview-info + echo "${{ steps.preview.outputs.url }}" > preview-info/preview-url.txt + echo "${{ github.event.pull_request.number }}" > preview-info/pr-number.txt + echo "${{ github.event.pull_request.head.sha }}" > preview-info/commit-sha.txt + echo "realtime-js" > preview-info/package-name.txt + + - name: Upload preview info + uses: actions/upload-artifact@v4 + with: + name: preview-info + path: preview-info/ + retention-days: 7 diff --git a/.github/workflows/preview-comment.yml b/.github/workflows/preview-comment.yml new file mode 100644 index 0000000..ebef6cb --- /dev/null +++ b/.github/workflows/preview-comment.yml @@ -0,0 +1,112 @@ +name: Update PR Comment + +permissions: + pull-requests: write + actions: read + +on: + workflow_run: + workflows: ["Preview Build", "Trigger Supabase JS Tests"] + types: [completed] + +jobs: + update-comment: + runs-on: ubuntu-latest + steps: + # Get PR number from the workflow run + - name: Get PR info + id: pr-info + uses: actions/github-script@v7 + with: + script: | + // Get the workflow run details + const workflowRun = context.payload.workflow_run; + + // Find associated PR + const prs = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + head: `${workflowRun.head_repository.owner.login}:${workflowRun.head_branch}` + }); + + if (prs.data.length > 0) { + const pr = prs.data[0]; + core.setOutput('pr-number', pr.number); + core.setOutput('found', 'true'); + console.log(`Found PR #${pr.number}`); + } else { + core.setOutput('found', 'false'); + console.log('No associated PR found'); + } + + # Only continue if we found a PR + - name: Download preview info + if: steps.pr-info.outputs.found == 'true' && github.event.workflow_run.name == 'Preview Build' && github.event.workflow_run.conclusion == 'success' + uses: actions/download-artifact@v4 + with: + name: preview-info + path: preview-info/ + run-id: ${{ github.event.workflow_run.id }} + continue-on-error: true + + - name: Read preview URL + if: steps.pr-info.outputs.found == 'true' && github.event.workflow_run.name == 'Preview Build' && github.event.workflow_run.conclusion == 'success' + id: preview-url + run: | + if [ -f "preview-info/preview-url.txt" ]; then + echo "url=$(cat preview-info/preview-url.txt)" >> $GITHUB_OUTPUT + echo "found=true" >> $GITHUB_OUTPUT + else + echo "found=false" >> $GITHUB_OUTPUT + fi + continue-on-error: true + + # Find existing comment + - name: Find existing comment + if: steps.pr-info.outputs.found == 'true' + uses: peter-evans/find-comment@v3 + id: find-comment + with: + issue-number: ${{ steps.pr-info.outputs.pr-number }} + comment-author: 'github-actions[bot]' + body-includes: '' + + # Create or update comment based on workflow status + - name: Create or update preview comment + if: steps.pr-info.outputs.found == 'true' + uses: peter-evans/create-or-update-comment@v4 + with: + comment-id: ${{ steps.find-comment.outputs.comment-id }} + issue-number: ${{ steps.pr-info.outputs.pr-number }} + body: | + + ## 🚀 Preview Release Status + + ${{ github.event.workflow_run.name == 'Preview Build' && github.event.workflow_run.conclusion == 'success' && steps.preview-url.outputs.found == 'true' && format('✅ **Preview package created successfully!** + + 📦 **Preview URL:** `{0}` + + You can install this preview package in your project by running: + ```bash + npm install {0} + ``` + + 🔄 Supabase-js CI tests have been automatically triggered to verify compatibility. + ', steps.preview-url.outputs.url) || '' }} + + ${{ github.event.workflow_run.name == 'Preview Build' && github.event.workflow_run.conclusion == 'failure' && '❌ **Preview build failed** + + Please check the [workflow logs](' }}${{ github.event.workflow_run.name == 'Preview Build' && github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.html_url || '' }}${{ github.event.workflow_run.name == 'Preview Build' && github.event.workflow_run.conclusion == 'failure' && ') for more details.' || '' }} + + ${{ github.event.workflow_run.name == 'Trigger Supabase JS Tests' && github.event.workflow_run.conclusion == 'success' && '✅ **Supabase-js tests triggered successfully!** + + The integration tests are now running. Results will be posted in the supabase-js repository when complete.' || '' }} + + ${{ github.event.workflow_run.name == 'Trigger Supabase JS Tests' && github.event.workflow_run.conclusion == 'failure' && '⚠️ **Failed to trigger supabase-js tests** + + The preview package was created but the integration tests could not be triggered. You may need to trigger them manually.' || '' }} + + --- + Last updated: ${{ github.event.workflow_run.updated_at }} + edit-mode: replace diff --git a/.github/workflows/preview-release.yml b/.github/workflows/preview-release.yml deleted file mode 100644 index 4f7c686..0000000 --- a/.github/workflows/preview-release.yml +++ /dev/null @@ -1,167 +0,0 @@ -name: Preview release - -permissions: - pull-requests: write - -on: - # Manual trigger with inputs for control - workflow_dispatch: - inputs: - trigger_supabase_js: - description: 'Trigger supabase-js tests' - type: boolean - default: true - target_branch: - description: 'Target branch for supabase-js tests' - type: string - default: 'master' - - # Push to main - only when source code changes - push: - branches: - - main - paths: - - 'src/**' - - 'package.json' - - 'package-lock.json' - - 'tsconfig.json' - - # PR triggers - only when labeled - # Using pull_request_target to access secrets when PRs come from forks - pull_request_target: - types: [labeled, synchronize] - -jobs: - preview: - # Run only for PRs with 'trigger: preview' label or pushes to main - if: > - github.repository == 'supabase/functions-js' && - ( - github.event_name == 'workflow_dispatch' || - github.event_name == 'push' || - (github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'trigger: preview')) - ) - runs-on: ubuntu-latest - outputs: - preview-url: ${{ steps.preview.outputs.url }} - package-name: ${{ steps.preview.outputs.package }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - # For pull_request_target, we need to explicitly checkout the PR's head - ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }} - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Build - run: npm run build - - - name: Publish preview - id: preview - run: | - OUTPUT=$(npx pkg-pr-new@latest publish --compact 2>&1) - PREVIEW_URL=$(echo "$OUTPUT" | grep -o 'https://pkg\.pr\.new/@supabase/[^[:space:]]*' | head -1) - REPO_NAME=$(echo "$GITHUB_REPOSITORY" | cut -d'/' -f2) - - if [ -z "$PREVIEW_URL" ]; then - echo "Error: Failed to extract preview URL from pkg-pr-new output" - echo "Output was: $OUTPUT" - exit 1 - fi - - echo "Preview Release URL: $PREVIEW_URL" - echo "url=$PREVIEW_URL" >> $GITHUB_OUTPUT - echo "package=$REPO_NAME" >> $GITHUB_OUTPUT - - trigger-supabase-js-tests: - needs: preview - # Only run if preview URL exists and either: - # - Not workflow_dispatch, OR - # - workflow_dispatch with trigger_supabase_js = true - if: > - needs.preview.outputs.preview-url != '' && - ( - github.event_name != 'workflow_dispatch' || - github.event.inputs.trigger_supabase_js == 'true' - ) - runs-on: ubuntu-latest - steps: - - name: Generate GitHub App token - id: generate-token - uses: actions/create-github-app-token@v1 - with: - app-id: ${{ vars.CROSS_REPO_APP_ID }} - private-key: ${{ secrets.CROSS_REPO_APP_PRIVATE_KEY }} - owner: supabase - repositories: functions-js,supabase-js - - - name: Trigger supabase-js CI tests - uses: actions/github-script@v7 - with: - github-token: ${{ steps.generate-token.outputs.token }} - script: | - const prNumber = context.issue.number || 'push'; - const triggeringRepo = context.repo.repo; - // Use input target_branch if workflow_dispatch, otherwise default to main - const targetBranch = context.eventName === 'workflow_dispatch' && context.payload.inputs?.target_branch - ? context.payload.inputs.target_branch - : 'main'; - - try { - const response = await github.rest.actions.createWorkflowDispatch({ - owner: 'supabase', - repo: 'supabase-js', - workflow_id: 'external-test.yml', - ref: targetBranch, - inputs: { - triggering_repo: triggeringRepo, - triggering_pr: prNumber.toString(), - preview_url: '${{ needs.preview.outputs.preview-url }}', - package_name: '${{ needs.preview.outputs.package-name }}', - triggering_sha: context.eventName === 'pull_request_target' ? context.payload.pull_request.head.sha : context.sha - } - }); - - console.log('Successfully triggered supabase-js tests'); - console.log('Response:', response.status); - } catch (error) { - console.error('Failed to trigger supabase-js tests:', error); - throw error; - } - - - name: Find existing preview comment - if: github.event_name == 'pull_request_target' - uses: peter-evans/find-comment@v3 - id: find-comment - with: - token: ${{ secrets.GITHUB_TOKEN }} - issue-number: ${{ github.event.pull_request.number }} - comment-author: 'github-actions[bot]' - body-includes: '' - - - name: Create or update preview comment - if: github.event_name == 'pull_request_target' - uses: peter-evans/create-or-update-comment@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - comment-id: ${{ steps.find-comment.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - body: | - - 🚀 **Preview release created!** - - supabase-js CI tests have been automatically triggered on feature branch to verify compatibility. - - **Preview package:** `${{ needs.preview.outputs.preview-url }}` - - Results will be posted here once testing is complete. - - edit-mode: replace diff --git a/.github/workflows/trigger-tests.yml b/.github/workflows/trigger-tests.yml new file mode 100644 index 0000000..9552986 --- /dev/null +++ b/.github/workflows/trigger-tests.yml @@ -0,0 +1,105 @@ +name: Trigger Supabase JS Tests + +permissions: + contents: read + actions: read + +on: + workflow_run: + workflows: ["Preview Build"] + types: [completed] + + workflow_dispatch: + inputs: + preview_url: + description: 'Preview package URL to test' + required: true + type: string + target_branch: + description: 'Target branch for supabase-js tests' + type: string + default: 'master' + +jobs: + trigger-tests: + # Only run if the preview build succeeded + if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' + runs-on: ubuntu-latest + steps: + # For workflow_run trigger, download the preview info + - name: Download preview info + if: github.event_name != 'workflow_dispatch' + uses: actions/download-artifact@v4 + with: + name: preview-info + path: preview-info/ + run-id: ${{ github.event.workflow_run.id }} + + - name: Read preview info + id: info + run: | + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + # Use inputs from workflow_dispatch + echo "url=${{ github.event.inputs.preview_url }}" >> $GITHUB_OUTPUT + echo "pr=manual" >> $GITHUB_OUTPUT + echo "sha=manual" >> $GITHUB_OUTPUT + echo "package=functions-js" >> $GITHUB_OUTPUT + else + # Read from downloaded artifacts + echo "url=$(cat preview-info/preview-url.txt)" >> $GITHUB_OUTPUT + echo "pr=$(cat preview-info/pr-number.txt)" >> $GITHUB_OUTPUT + echo "sha=$(cat preview-info/commit-sha.txt)" >> $GITHUB_OUTPUT + echo "package=$(cat preview-info/package-name.txt)" >> $GITHUB_OUTPUT + fi + + - name: Show preview info + run: | + echo "Preview URL: ${{ steps.info.outputs.url }}" + echo "PR Number: ${{ steps.info.outputs.pr }}" + echo "Commit SHA: ${{ steps.info.outputs.sha }}" + echo "Package: ${{ steps.info.outputs.package }}" + + # Generate GitHub App token for cross-repo access + - name: Generate GitHub App token + id: generate-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.CROSS_REPO_APP_ID }} + private-key: ${{ secrets.CROSS_REPO_APP_PRIVATE_KEY }} + owner: supabase + repositories: functions-js,supabase-js + + - name: Trigger supabase-js CI tests + uses: actions/github-script@v7 + with: + github-token: ${{ steps.generate-token.outputs.token }} + script: | + const targetBranch = context.eventName === 'workflow_dispatch' && context.payload.inputs?.target_branch + ? context.payload.inputs.target_branch + : 'master'; + + console.log('Triggering supabase-js tests...'); + console.log('Target branch:', targetBranch); + console.log('Preview URL:', '${{ steps.info.outputs.url }}'); + + try { + const response = await github.rest.actions.createWorkflowDispatch({ + owner: 'supabase', + repo: 'supabase-js', + workflow_id: 'external-test.yml', + ref: targetBranch, + inputs: { + triggering_repo: '${{ steps.info.outputs.package }}', + triggering_pr: '${{ steps.info.outputs.pr }}', + preview_url: '${{ steps.info.outputs.url }}', + package_name: '${{ steps.info.outputs.package }}', + triggering_sha: '${{ steps.info.outputs.sha }}' + } + }); + + console.log('✅ Successfully triggered supabase-js tests'); + console.log('Response status:', response.status); + } catch (error) { + console.error('❌ Failed to trigger supabase-js tests:', error); + throw error; + } From 3e6fff9b4154d44abc05d7ee5341ba7197c18732 Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Thu, 28 Aug 2025 12:38:41 +0300 Subject: [PATCH 2/3] chore: add more security walls --- .github/workflows/preview-build.yml | 6 ++++-- .github/workflows/preview-comment.yml | 5 ++++- .github/workflows/trigger-tests.yml | 7 +++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/preview-build.yml b/.github/workflows/preview-build.yml index 07e8fea..92aef74 100644 --- a/.github/workflows/preview-build.yml +++ b/.github/workflows/preview-build.yml @@ -15,10 +15,12 @@ on: jobs: build-preview: - # Only run if PR has the 'trigger: preview' label + # Only run if PR has the 'trigger: preview' label and is on the correct repository if: | + github.repository == 'supabase/functions-js' && contains(github.event.pull_request.labels.*.name, 'trigger: preview') runs-on: ubuntu-latest + timeout-minutes: 15 outputs: preview-url: ${{ steps.preview.outputs.url }} pr-number: ${{ github.event.pull_request.number }} @@ -76,7 +78,7 @@ jobs: echo "${{ steps.preview.outputs.url }}" > preview-info/preview-url.txt echo "${{ github.event.pull_request.number }}" > preview-info/pr-number.txt echo "${{ github.event.pull_request.head.sha }}" > preview-info/commit-sha.txt - echo "realtime-js" > preview-info/package-name.txt + echo "functions-js" > preview-info/package-name.txt - name: Upload preview info uses: actions/upload-artifact@v4 diff --git a/.github/workflows/preview-comment.yml b/.github/workflows/preview-comment.yml index ebef6cb..e3d8c39 100644 --- a/.github/workflows/preview-comment.yml +++ b/.github/workflows/preview-comment.yml @@ -11,7 +11,10 @@ on: jobs: update-comment: + # Only run on the correct repository + if: github.repository == 'supabase/functions-js' runs-on: ubuntu-latest + timeout-minutes: 5 steps: # Get PR number from the workflow run - name: Get PR info @@ -101,7 +104,7 @@ jobs: ${{ github.event.workflow_run.name == 'Trigger Supabase JS Tests' && github.event.workflow_run.conclusion == 'success' && '✅ **Supabase-js tests triggered successfully!** - The integration tests are now running. Results will be posted in the supabase-js repository when complete.' || '' }} + The integration tests are now running. Results will be posted here when complete.' || '' }} ${{ github.event.workflow_run.name == 'Trigger Supabase JS Tests' && github.event.workflow_run.conclusion == 'failure' && '⚠️ **Failed to trigger supabase-js tests** diff --git a/.github/workflows/trigger-tests.yml b/.github/workflows/trigger-tests.yml index 9552986..645d1bb 100644 --- a/.github/workflows/trigger-tests.yml +++ b/.github/workflows/trigger-tests.yml @@ -22,9 +22,12 @@ on: jobs: trigger-tests: - # Only run if the preview build succeeded - if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' + # Only run if the preview build succeeded and on the correct repository + if: | + github.repository == 'supabase/functions-js' && + (github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success') runs-on: ubuntu-latest + timeout-minutes: 10 steps: # For workflow_run trigger, download the preview info - name: Download preview info From a7554f1071bb218778de1dec3375c62d18cb3947 Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Thu, 28 Aug 2025 13:47:28 +0300 Subject: [PATCH 3/3] chore: restrict to PRs to master branch --- .github/workflows/preview-build.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/preview-build.yml b/.github/workflows/preview-build.yml index 92aef74..1736f84 100644 --- a/.github/workflows/preview-build.yml +++ b/.github/workflows/preview-build.yml @@ -15,9 +15,10 @@ on: jobs: build-preview: - # Only run if PR has the 'trigger: preview' label and is on the correct repository + # Only run if PR has the 'trigger: preview' label, is on the correct repository, and targets main branch if: | github.repository == 'supabase/functions-js' && + github.event.pull_request.base.ref == 'main' && contains(github.event.pull_request.labels.*.name, 'trigger: preview') runs-on: ubuntu-latest timeout-minutes: 15 @@ -46,17 +47,11 @@ jobs: - name: Build run: npm run build - - - name: Run tests - run: npm test - continue-on-error: true # Don't fail preview on test failures - - name: Create preview release id: preview run: | - set -e echo "Creating preview release..." - OUTPUT=$(npx pkg-pr-new@latest publish --compact 2>&1) + OUTPUT=$(npx pkg-pr-new@latest publish --compact 2>&1 || true) echo "Full output:" echo "$OUTPUT" @@ -65,6 +60,7 @@ jobs: if [ -z "$PREVIEW_URL" ]; then echo "Error: Failed to extract preview URL from pkg-pr-new output" + echo "Output was: $OUTPUT" exit 1 fi