diff --git a/.github/workflows/cloudflare-preview.yml b/.github/workflows/cloudflare-preview.yml index bccc125764..3ab103f6f4 100644 --- a/.github/workflows/cloudflare-preview.yml +++ b/.github/workflows/cloudflare-preview.yml @@ -1,62 +1,41 @@ -name: Cloudflare Pages Preview Deployment +name: Build and Deploy Cloudflare Preview on: - # Runs automatically for PRs from ruby/rdoc - # Fork PRs will be filtered out by the if condition - pull_request: - - # Allows manual triggering for fork PRs - workflow_dispatch: + workflow_call: inputs: - pull_request_number: - description: 'Pull Request Number (for fork PRs)' + pr_number: + description: 'The pull request number' + required: true + type: string + pr_head_sha: + description: 'The SHA of the PR head commit' required: true type: string + pr_checkout_repository: + description: 'The repository to checkout (owner/repo)' + required: true + type: string + secrets: + cloudflare_api_token: + description: 'Cloudflare API Token' + required: true + cloudflare_account_id: + description: 'Cloudflare Account ID' + required: true + +permissions: + pull-requests: write # To allow commenting on the PR jobs: - deploy-preview: + build-deploy-and-comment: + name: Build, Deploy, and Comment runs-on: ubuntu-latest - # Skip if PR from fork and NOT manually triggered - if: ${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == 'ruby/rdoc' }} - steps: - - name: Checkout for PR from main repo - if: ${{ github.event_name == 'pull_request' }} + - name: Checkout PR Code uses: actions/checkout@v4 with: - ref: ${{ github.event.pull_request.head.ref }} - - # For fork PRs that are manually triggered, we need to get the PR details first - - name: Get PR details for fork - if: ${{ github.event_name == 'workflow_dispatch' }} - id: pr_details - uses: actions/github-script@v7 - with: - script: | - const prNumber = ${{ inputs.pull_request_number }}; - - // Get PR details to find the head SHA - const { data: pr } = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: prNumber - }); - - console.log(`Fork PR head SHA: ${pr.head.sha}`); - console.log(`Fork PR head ref: ${pr.head.ref}`); - console.log(`Fork PR repo: ${pr.head.repo.full_name}`); - - // Set outputs for checkout step - core.setOutput('head_sha', pr.head.sha); - core.setOutput('head_ref', pr.head.ref); - core.setOutput('repo_full_name', pr.head.repo.full_name); - - - name: Checkout for manually triggered fork PR - if: ${{ github.event_name == 'workflow_dispatch' }} - uses: actions/checkout@v4 - with: - ref: ${{ steps.pr_details.outputs.head_sha }} - repository: ${{ steps.pr_details.outputs.repo_full_name }} + repository: ${{ inputs.pr_checkout_repository }} + ref: ${{ inputs.pr_head_sha }} - name: Setup Ruby uses: ruby/setup-ruby@v1 @@ -64,49 +43,26 @@ jobs: ruby-version: '3.4' bundler-cache: true - - name: Install dependencies - run: bundle install - - name: Build site run: bundle exec rake rdoc - - name: Set PR Number - id: pr_number - run: | - if [ "${{ github.event_name }}" == "pull_request" ]; then - echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> $GITHUB_ENV - else - echo "PR_NUMBER=${{ inputs.pull_request_number }}" >> $GITHUB_ENV - fi - - # Deploy to Cloudflare Pages using wrangler-action - name: Deploy to Cloudflare Pages id: deploy uses: cloudflare/wrangler-action@v3 with: - apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - command: pages deploy ./_site --project-name=rdoc --branch="${{ env.PR_NUMBER }}-preview" + apiToken: ${{ secrets.cloudflare_api_token }} + accountId: ${{ secrets.cloudflare_account_id }} + command: pages deploy ./_site --project-name=rdoc --branch="${{ inputs.pr_number }}-preview" - # Comment on PR with preview URL - works for both regular PRs and fork PRs - name: Comment on PR with preview URL uses: actions/github-script@v7 with: script: | - const prNumber = ${{ env.PR_NUMBER }}; + const prNumber = ${{ inputs.pr_number }}; const url = "${{ steps.deploy.outputs.deployment-url }}"; const commentMarker = "🚀 Preview deployment available at:"; + const commitSha = '${{ inputs.pr_head_sha }}'; - // Get commit SHA based on event type - let commitSha; - if ('${{ github.event_name }}' === 'pull_request') { - commitSha = '${{ github.event.pull_request.head.sha }}'; - } else { - // For workflow_dispatch, get the SHA from the PR details - commitSha = '${{ steps.pr_details.outputs.head_sha }}'; - } - - // Get all comments on the PR const comments = await github.rest.issues.listComments({ issue_number: prNumber, owner: context.repo.owner, @@ -114,7 +70,6 @@ jobs: per_page: 100 }); - // Look for our previous bot comment const existingComment = comments.data.find(comment => comment.body.includes(commentMarker) ); @@ -122,7 +77,6 @@ jobs: const commentBody = `${commentMarker} [${url}](${url}) (commit: ${commitSha})`; if (existingComment) { - // Update existing comment await github.rest.issues.updateComment({ comment_id: existingComment.id, owner: context.repo.owner, @@ -131,7 +85,6 @@ jobs: }); console.log("Updated existing preview comment"); } else { - // Create new comment await github.rest.issues.createComment({ issue_number: prNumber, owner: context.repo.owner, diff --git a/.github/workflows/pr-preview-check.yml b/.github/workflows/pr-preview-check.yml new file mode 100644 index 0000000000..db531f96d0 --- /dev/null +++ b/.github/workflows/pr-preview-check.yml @@ -0,0 +1,42 @@ +name: PR Preview Check and Trigger + +on: + pull_request: + +jobs: + # For PRs from the main repo - direct call to the shared workflow + trigger-main-repo-preview: + name: Trigger Preview (Main Repo) + uses: ./.github/workflows/cloudflare-preview.yml + if: github.event.pull_request.head.repo.fork == false + with: + pr_number: ${{ github.event.pull_request.number }} + pr_head_sha: ${{ github.event.pull_request.head.sha }} + pr_checkout_repository: ${{ github.repository }} + secrets: + cloudflare_api_token: ${{ secrets.CLOUDFLARE_API_TOKEN }} + cloudflare_account_id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + + # For fork PRs - this job requires approval + wait-for-approval: + name: Wait for Approval (Fork PR) + runs-on: ubuntu-latest + if: github.event.pull_request.head.repo.fork == true + environment: fork-preview-protection + # This job only serves as an approval gate - it doesn't do anything else + steps: + - run: echo "Approval granted. Proceeding with preview deployment for commit ${{ github.event.pull_request.head.sha }}." + + # Once approval is granted, call the shared workflow + trigger-fork-preview: + name: Trigger Preview (Fork - After Approval) + needs: wait-for-approval + uses: ./.github/workflows/cloudflare-preview.yml + if: github.event.pull_request.head.repo.fork == true + with: + pr_number: ${{ github.event.pull_request.number }} + pr_head_sha: ${{ github.event.pull_request.head.sha }} + pr_checkout_repository: ${{ github.event.pull_request.head.repo.full_name }} + secrets: + cloudflare_api_token: ${{ secrets.CLOUDFLARE_API_TOKEN_FOR_FORKS }} + cloudflare_account_id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID_FOR_FORKS }} diff --git a/.github/workflows/pr-preview-comment.yml b/.github/workflows/pr-preview-comment.yml deleted file mode 100644 index 018b57596f..0000000000 --- a/.github/workflows/pr-preview-comment.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Comment on Fork PRs - -on: - pull_request_target: - types: [opened, reopened, synchronize] - -# Required permissions for commenting on PRs -permissions: - contents: read - pull-requests: write - -jobs: - comment-on-fork-pr: - runs-on: ubuntu-latest - # Only run for fork PRs - if: github.event.pull_request.head.repo.fork == true - steps: - - name: Comment on PR with manual deployment instructions - uses: actions/github-script@v7 - with: - script: |- - const prNumber = context.payload.pull_request.number; - const workflowUrl = `https://github.com/ruby/rdoc/actions/workflows/cloudflare-preview.yml`; - const commentMarker = "## Cloudflare Preview Deployment"; - - // Create a direct link that pre-fills the PR number input - const dispatchUrl = `${workflowUrl}/dispatch?ref=main&inputs%5Bpull_request_number%5D=${prNumber}`; - - // Get all comments on the PR - const comments = await github.rest.issues.listComments({ - issue_number: prNumber, - owner: context.repo.owner, - repo: context.repo.repo, - per_page: 100 - }); - - // Look for our previous bot comment - const existingComment = comments.data.find(comment => - comment.body.includes(commentMarker) - ); - - const messageLines = [ - `${commentMarker}`, - `⚠️ This PR is from a fork, so the preview deployment workflow doesn't run automatically for security reasons.`, - ``, - `### For Maintainers:`, - `[🚀 Click here to run the preview deployment workflow](${dispatchUrl})`, - ``, - `This will trigger a Cloudflare Pages preview deployment for this PR.` - ]; - - const commentBody = messageLines.join('\n'); - - if (existingComment) { - // Update existing comment - await github.rest.issues.updateComment({ - comment_id: existingComment.id, - owner: context.repo.owner, - repo: context.repo.repo, - body: commentBody - }); - console.log("Updated existing fork PR comment"); - } else { - // Create new comment - await github.rest.issues.createComment({ - issue_number: prNumber, - owner: context.repo.owner, - repo: context.repo.repo, - body: commentBody - }); - console.log("Created new fork PR comment"); - }