cloudflare/kumo

Public

mirrored fromhttps://github.com/cloudflare/kumoAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
886b20f7067e7fc14460caf2887662e26f88fb99

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

.github/workflows/preview-deploy.yml

205lines · modecode

1# Deploys docs preview for fork PRs via workflow_run.
2#
3# Security model: fork code runs in the untrusted pull_request context
4# (preview.yml docs-build job) and produces a static HTML artifact.
5# This workflow runs in the base repo context with secrets, but NEVER
6# checks out or executes fork code — it only deploys the built artifact.
7#
8# For internal PRs, deployment happens directly in preview.yml (docs-deploy job).
9
10name: Preview Deploy (Forks)
11
12on:
13 workflow_run:
14 workflows: ["Preview"]
15 types: [completed]
16
17permissions:
18 pull-requests: write
19
20concurrency:
21 group: preview-deploy-${{ github.event.workflow_run.id }}
22 cancel-in-progress: true
23
24jobs:
25 deploy:
26 name: Docs Deploy (Fork)
27 # Only run for:
28 # 1. Successful workflow runs
29 # 2. Pull request events (not push)
30 # 3. Fork PRs only (internal PRs deploy in preview.yml directly)
31 if: >-
32 github.event.workflow_run.conclusion == 'success' &&
33 github.event.workflow_run.event == 'pull_request' &&
34 github.event.workflow_run.head_repository.full_name != github.repository
35 runs-on: ubuntu-latest
36 timeout-minutes: 10
37 outputs:
38 preview_url: ${{ steps.deploy.outputs.preview_url }}
39 pr_number: ${{ steps.metadata.outputs.pr_number }}
40 steps:
41 - name: Checkout (wrangler config only)
42 uses: actions/checkout@v4
43 with:
44 sparse-checkout: packages/kumo-docs-astro/wrangler.jsonc
45
46 - name: Resolve PR metadata
47 id: metadata
48 uses: actions/github-script@v7
49 with:
50 script: |
51 // workflow_run.pull_requests can be empty for fork PRs.
52 // Fall back to the Pulls API when that happens.
53 const prs = context.payload.workflow_run.pull_requests;
54 if (prs && prs.length > 0) {
55 core.setOutput('pr_number', prs[0].number.toString());
56 core.setOutput('head_sha', prs[0].head.sha);
57 return;
58 }
59
60 // Fallback: find the PR matching the head SHA via API
61 const headSha = context.payload.workflow_run.head_sha;
62 const headBranch = context.payload.workflow_run.head_branch;
63 const { data: pulls } = await github.rest.pulls.list({
64 owner: context.repo.owner,
65 repo: context.repo.repo,
66 state: 'open',
67 head: `${context.payload.workflow_run.head_repository.full_name.split('/')[0]}:${headBranch}`,
68 });
69
70 const match = pulls.find(pr => pr.head.sha === headSha);
71 if (!match) {
72 core.setFailed(`No open PR found for SHA ${headSha} on branch ${headBranch}`);
73 return;
74 }
75
76 core.setOutput('pr_number', match.number.toString());
77 core.setOutput('head_sha', headSha);
78
79 - name: Download docs artifact
80 uses: actions/download-artifact@v4
81 with:
82 name: docs-preview-dist
83 path: packages/kumo-docs-astro/dist/
84 run-id: ${{ github.event.workflow_run.id }}
85 github-token: ${{ secrets.GITHUB_TOKEN }}
86
87 - name: Install wrangler
88 run: npm install -g wrangler
89
90 - name: Deploy docs preview
91 id: deploy
92 working-directory: packages/kumo-docs-astro
93 env:
94 CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
95 CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
96 PR_NUMBER: ${{ steps.metadata.outputs.pr_number }}
97 HEAD_SHA: ${{ steps.metadata.outputs.head_sha }}
98 run: |
99 COMMIT_SHORT="${HEAD_SHA:0:7}"
100
101 VERSION_OUTPUT=$(wrangler versions upload --env="" --message "PR #${PR_NUMBER} (${COMMIT_SHORT})" 2>&1) || true
102 echo "$VERSION_OUTPUT"
103
104 if ! echo "$VERSION_OUTPUT" | grep -q "Worker Version ID:"; then
105 echo "::error::wrangler versions upload failed -- no Worker Version ID in output"
106 exit 1
107 fi
108
109 PREVIEW_URL=$(echo "$VERSION_OUTPUT" | grep -oE 'Version Preview URL: https://[^ ]+' | sed 's/Version Preview URL: //')
110
111 if [ -z "$PREVIEW_URL" ]; then
112 VERSION_ID=$(echo "$VERSION_OUTPUT" | grep -oE 'Worker Version ID: [a-f0-9-]+' | sed 's/Worker Version ID: //')
113 if [ -n "$VERSION_ID" ]; then
114 VERSION_PREFIX=$(echo "$VERSION_ID" | cut -c1-8)
115 PREVIEW_URL="https://${VERSION_PREFIX}-kumo-docs.design-engineering.workers.dev"
116 fi
117 fi
118
119 if [ -z "$PREVIEW_URL" ]; then
120 echo "::error::Failed to determine docs preview URL"
121 exit 1
122 fi
123
124 echo "preview_url=$PREVIEW_URL" >> "$GITHUB_OUTPUT"
125
126 - name: Comment on PR
127 uses: actions/github-script@v7
128 env:
129 PREVIEW_URL: ${{ steps.deploy.outputs.preview_url }}
130 HEAD_SHA: ${{ steps.metadata.outputs.head_sha }}
131 PR_NUMBER: ${{ steps.metadata.outputs.pr_number }}
132 with:
133 script: |
134 const marker = '<!-- kumo-docs-preview -->';
135 const previewUrl = process.env.PREVIEW_URL;
136 const headSha = process.env.HEAD_SHA;
137 const prNumber = parseInt(process.env.PR_NUMBER, 10);
138 const body = [
139 marker,
140 '### Docs Preview',
141 '',
142 `[View docs preview](${previewUrl})`,
143 '',
144 `Commit: \`${headSha.substring(0, 7)}\``,
145 ].join('\n');
146
147 const { data: comments } = await github.rest.issues.listComments({
148 owner: context.repo.owner,
149 repo: context.repo.repo,
150 issue_number: prNumber,
151 });
152
153 const existing = comments.find(c => c.body?.startsWith(marker));
154
155 if (existing) {
156 await github.rest.issues.updateComment({
157 owner: context.repo.owner,
158 repo: context.repo.repo,
159 comment_id: existing.id,
160 body,
161 });
162 } else {
163 await github.rest.issues.createComment({
164 owner: context.repo.owner,
165 repo: context.repo.repo,
166 issue_number: prNumber,
167 body,
168 });
169 }
170
171 visual-regression:
172 name: Visual Regression (Fork)
173 needs: deploy
174 if: ${{ needs.deploy.outputs.preview_url }}
175 runs-on: ubuntu-latest
176 timeout-minutes: 15
177 steps:
178 - name: Checkout
179 uses: actions/checkout@v4
180 with:
181 fetch-depth: 0
182
183 - name: Install Dependencies
184 uses: ./.github/actions/install-dependencies
185 with:
186 filter: "@cloudflare/kumo..."
187
188 - name: Run Visual Regression
189 env:
190 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
191 GITHUB_PR_NUMBER: ${{ needs.deploy.outputs.pr_number }}
192 GITHUB_REPOSITORY: ${{ github.repository }}
193 BEFORE_URL: "https://kumo-ui.com"
194 AFTER_URL: ${{ needs.deploy.outputs.preview_url }}
195 SCREENSHOT_WORKER_URL: ${{ secrets.SCREENSHOT_WORKER_URL }}
196 SCREENSHOT_API_KEY: ${{ secrets.SCREENSHOT_API_KEY }}
197 run: pnpm tsx ci/visual-regression/run-visual-regression.ts
198
199 - name: Upload Screenshots
200 uses: actions/upload-artifact@v4
201 if: always()
202 with:
203 name: visual-regression-screenshots-fork
204 path: ci/visual-regression/screenshots/
205 retention-days: 7
206