Skip to content

Commit ecef239

Browse files
authored
Add a new workflow to deploy page preview for PRs from forks (#1365)
Closes #1356 The default cloudflare integration with GH doesn't deploy previews for forks as attackers may modify the workflow configs to collect api tokens....etc. So I want to switch from CL's default integration to this new workflow instead: - For PRs opened from `ruby/rdoc` branches, the preview still be built automatically - For PRs opened from forks, their preview needs to be triggered manually by maintainers, **after making sure that the content is not malicious** - To manually build previews for a PR, the PR's number is a required input - On these PRs, another workflow will generate a comment that contains a link to trigger `cloudflare-preview` with the PR number
1 parent 61a2dae commit ecef239

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
name: Cloudflare Pages Preview Deployment
2+
3+
on:
4+
# Runs automatically for PRs from ruby/rdoc
5+
# Fork PRs will be filtered out by the if condition
6+
pull_request:
7+
8+
# Allows manual triggering for fork PRs
9+
workflow_dispatch:
10+
inputs:
11+
pull_request_number:
12+
description: 'Pull Request Number (for fork PRs)'
13+
required: true
14+
type: string
15+
16+
jobs:
17+
deploy-preview:
18+
runs-on: ubuntu-latest
19+
# Skip if PR from fork and NOT manually triggered
20+
if: ${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == 'ruby/rdoc' }}
21+
22+
steps:
23+
- name: Checkout for PR from main repo
24+
if: ${{ github.event_name == 'pull_request' }}
25+
uses: actions/checkout@v4
26+
with:
27+
ref: ${{ github.event.pull_request.head.ref }}
28+
29+
- name: Checkout for manually triggered fork PR
30+
if: ${{ github.event_name == 'workflow_dispatch' }}
31+
uses: actions/checkout@v4
32+
with:
33+
ref: ${{ github.event.pull_request.head.ref }}
34+
repository: ${{ github.event.pull_request.head.repo.full_name }}
35+
36+
- name: Setup Ruby
37+
uses: ruby/setup-ruby@v1
38+
with:
39+
ruby-version: '3.4'
40+
bundler-cache: true
41+
42+
- name: Install dependencies
43+
run: bundle install
44+
45+
- name: Build site
46+
run: bundle exec rake rdoc
47+
48+
- name: Set PR Number
49+
id: pr_number
50+
run: |
51+
if [ "${{ github.event_name }}" == "pull_request" ]; then
52+
echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> $GITHUB_ENV
53+
else
54+
echo "PR_NUMBER=${{ inputs.pull_request_number }}" >> $GITHUB_ENV
55+
fi
56+
57+
# Deploy to Cloudflare Pages using wrangler-action
58+
- name: Deploy to Cloudflare Pages
59+
id: deploy
60+
uses: cloudflare/wrangler-action@v3
61+
with:
62+
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
63+
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
64+
command: pages deploy ./_site --project-name=rdoc --branch="${{ env.PR_NUMBER }}-preview"
65+
66+
# Comment on PR with preview URL - works for both regular PRs and fork PRs
67+
- name: Comment on PR with preview URL
68+
uses: actions/github-script@v7
69+
with:
70+
script: |
71+
const prNumber = ${{ env.PR_NUMBER }};
72+
const url = "${{ steps.deploy.outputs.deployment-url }}";
73+
const commentMarker = "🚀 Preview deployment available at:";
74+
75+
// Get commit SHA based on event type
76+
let commitSha;
77+
if ('${{ github.event_name }}' === 'pull_request') {
78+
commitSha = '${{ github.event.pull_request.head.sha }}';
79+
} else {
80+
// For workflow_dispatch, get the SHA from the current commit
81+
commitSha = '${{ github.sha }}';
82+
}
83+
84+
// Get all comments on the PR
85+
const comments = await github.rest.issues.listComments({
86+
issue_number: prNumber,
87+
owner: context.repo.owner,
88+
repo: context.repo.repo,
89+
per_page: 100
90+
});
91+
92+
// Look for our previous bot comment
93+
const existingComment = comments.data.find(comment =>
94+
comment.body.includes(commentMarker)
95+
);
96+
97+
const commentBody = `${commentMarker} [${url}](${url}) (commit: ${commitSha})`;
98+
99+
if (existingComment) {
100+
// Update existing comment
101+
await github.rest.issues.updateComment({
102+
comment_id: existingComment.id,
103+
owner: context.repo.owner,
104+
repo: context.repo.repo,
105+
body: commentBody
106+
});
107+
console.log("Updated existing preview comment");
108+
} else {
109+
// Create new comment
110+
await github.rest.issues.createComment({
111+
issue_number: prNumber,
112+
owner: context.repo.owner,
113+
repo: context.repo.repo,
114+
body: commentBody
115+
});
116+
console.log("Created new preview comment");
117+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: Comment on Fork PRs
2+
3+
on:
4+
pull_request:
5+
types: [opened, reopened, synchronize]
6+
7+
jobs:
8+
comment-on-fork-pr:
9+
runs-on: ubuntu-latest
10+
# Only run for fork PRs
11+
if: github.event.pull_request.head.repo.fork == true
12+
steps:
13+
- name: Comment on PR with manual deployment instructions
14+
uses: actions/github-script@v7
15+
with:
16+
script: |-
17+
const prNumber = context.payload.pull_request.number;
18+
const workflowUrl = `https://github.com/ruby/rdoc/actions/workflows/cloudflare-preview.yml`;
19+
const branch = context.payload.pull_request.head.ref;
20+
const commentMarker = "## Cloudflare Preview Deployment";
21+
22+
// Create a direct link that pre-fills the PR number input
23+
const dispatchUrl = `${workflowUrl}/dispatch?ref=main&inputs%5Bpull_request_number%5D=${prNumber}`;
24+
25+
// Get all comments on the PR
26+
const comments = await github.rest.issues.listComments({
27+
issue_number: prNumber,
28+
owner: context.repo.owner,
29+
repo: context.repo.repo,
30+
per_page: 100
31+
});
32+
33+
// Look for our previous bot comment
34+
const existingComment = comments.data.find(comment =>
35+
comment.body.includes(commentMarker)
36+
);
37+
38+
const messageLines = [
39+
`${commentMarker}`,
40+
`⚠️ This PR is from a fork, so the preview deployment workflow doesn't run automatically for security reasons.`,
41+
`If you're a maintainer and want to preview this PR:`,
42+
``,
43+
`[➡️ Click here to run the workflow with PR #${prNumber} pre-filled](${dispatchUrl})`,
44+
``,
45+
`This will trigger a Cloudflare Pages preview deployment for this PR.`
46+
];
47+
48+
const commentBody = messageLines.join('\n');
49+
50+
if (existingComment) {
51+
// Update existing comment
52+
await github.rest.issues.updateComment({
53+
comment_id: existingComment.id,
54+
owner: context.repo.owner,
55+
repo: context.repo.repo,
56+
body: commentBody
57+
});
58+
console.log("Updated existing fork PR comment");
59+
} else {
60+
// Create new comment
61+
await github.rest.issues.createComment({
62+
issue_number: prNumber,
63+
owner: context.repo.owner,
64+
repo: context.repo.repo,
65+
body: commentBody
66+
});
67+
console.log("Created new fork PR comment");
68+
}

0 commit comments

Comments
 (0)