cloudflare/kumo

Public

mirrored from https://github.com/cloudflare/kumoAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
main

Branches

Tags

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

Clone

HTTPS

Download ZIP

.github/workflows/preview.yml

278lines · modecode

1name: Preview
2
3on:
4 pull_request:
5 # Also trigger on push to PR branches (for bots that push via API)
6 push:
7 branches:
8 - "opencode/**"
9 - "changeset-release/main"
10
11permissions:
12 contents: write
13 pull-requests: write
14
15concurrency:
16 group: preview-${{ github.event.pull_request.number || github.ref }}
17 cancel-in-progress: true
18
19jobs:
20 # Resolve PR number for push events (bots pushing via API)
21 resolve-pr:
22 name: Resolve PR
23 if: ${{ github.event_name == 'push' }}
24 runs-on: ubuntu-latest
25 outputs:
26 pr_number: ${{ steps.find-pr.outputs.pr_number }}
27 is_fork: ${{ steps.find-pr.outputs.is_fork }}
28 steps:
29 - name: Find PR for branch
30 id: find-pr
31 uses: actions/github-script@v7
32 with:
33 script: |
34 const branch = context.ref.replace('refs/heads/', '');
35 const { data: prs } = await github.rest.pulls.list({
36 owner: context.repo.owner,
37 repo: context.repo.repo,
38 head: `${context.repo.owner}:${branch}`,
39 state: 'open',
40 });
41
42 if (prs.length === 0) {
43 core.warning(`No open PR found for branch ${branch}; skipping PR-scoped preview steps.`);
44 core.setOutput('pr_number', '');
45 core.setOutput('is_fork', '');
46 return;
47 }
48
49 const pr = prs[0];
50 core.setOutput('pr_number', pr.number.toString());
51 core.setOutput('is_fork', (pr.head.repo.full_name !== pr.base.repo.full_name).toString());
52
53 pkg-preview:
54 name: Package Preview
55 if: ${{ github.repository_owner == 'cloudflare' }}
56 runs-on: ubuntu-latest
57 timeout-minutes: 15
58 steps:
59 - name: Checkout
60 uses: actions/checkout@v4
61 with:
62 fetch-depth: 1
63
64 - name: Install Dependencies
65 uses: ./.github/actions/install-dependencies
66 with:
67 filter: "@cloudflare/kumo..."
68
69 - name: Build @cloudflare/kumo
70 run: pnpm --filter @cloudflare/kumo build
71
72 - name: Publish preview package
73 run: pnpm dlx pkg-pr-new publish --pnpm --compact --no-template ./packages/kumo
74
75 # Stage 1: Build docs (runs for ALL PRs including forks).
76 # Produces an artifact consumed by either docs-deploy (internal) or
77 # the preview-deploy.yml workflow_run workflow (forks).
78 docs-build:
79 name: Docs Build
80 runs-on: ubuntu-latest
81 timeout-minutes: 15
82 steps:
83 - name: Checkout
84 uses: actions/checkout@v4
85 with:
86 fetch-depth: 1
87
88 - name: Install Dependencies
89 uses: ./.github/actions/install-dependencies
90 with:
91 filter: "@cloudflare/kumo-docs-astro..."
92
93 - name: Build @cloudflare/kumo
94 run: pnpm --filter @cloudflare/kumo build
95
96 - name: Build docs
97 run: pnpm --filter @cloudflare/kumo-docs-astro build
98
99 - name: Test docs
100 run: pnpm --filter @cloudflare/kumo-docs-astro test
101
102 - name: Upload docs artifact
103 uses: actions/upload-artifact@v4
104 with:
105 name: docs-preview-dist
106 path: packages/kumo-docs-astro/dist/
107 include-hidden-files: true
108 retention-days: 1
109
110 # Stage 2 (internal path): Deploy docs preview for non-fork PRs.
111 # Runs in the same workflow with secrets available.
112 # Fork PRs are handled by preview-deploy.yml (workflow_run).
113 docs-deploy:
114 name: Docs Deploy
115 needs: [docs-build, resolve-pr]
116 # Run for pull_request (non-fork) OR push events with resolved PR (non-fork)
117 if: |
118 always() &&
119 needs.docs-build.result == 'success' &&
120 (
121 (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) ||
122 (github.event_name == 'push' && needs.resolve-pr.result == 'success' && needs.resolve-pr.outputs.is_fork == 'false')
123 )
124 runs-on: ubuntu-latest
125 timeout-minutes: 10
126 outputs:
127 preview_url: ${{ steps.deploy.outputs.preview_url }}
128 pr_number: ${{ steps.set-pr.outputs.pr_number }}
129 steps:
130 - name: Set PR number
131 id: set-pr
132 run: |
133 if [ "${{ github.event_name }}" == "pull_request" ]; then
134 echo "pr_number=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT"
135 else
136 echo "pr_number=${{ needs.resolve-pr.outputs.pr_number }}" >> "$GITHUB_OUTPUT"
137 fi
138
139 - name: Checkout
140 uses: actions/checkout@v4
141 with:
142 sparse-checkout: packages/kumo-docs-astro/wrangler.jsonc
143
144 - name: Download docs artifact
145 uses: actions/download-artifact@v4
146 with:
147 name: docs-preview-dist
148 path: packages/kumo-docs-astro/dist/
149
150 - name: Deploy docs preview
151 id: deploy
152 working-directory: packages/kumo-docs-astro
153 env:
154 CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
155 CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
156 PR_NUMBER: ${{ steps.set-pr.outputs.pr_number }}
157 run: |
158 COMMIT_SHORT="${GITHUB_SHA:0:7}"
159
160 # "wrangler versions upload" creates a preview version without
161 # routing production traffic. Safe to run on every PR.
162 VERSION_OUTPUT=$(npx wrangler versions upload --env="" --message "PR #${PR_NUMBER} (${COMMIT_SHORT})" 2>&1) || true
163 echo "$VERSION_OUTPUT"
164
165 # Verify upload succeeded
166 if ! echo "$VERSION_OUTPUT" | grep -q "Worker Version ID:"; then
167 echo "::error::wrangler versions upload failed -- no Worker Version ID in output"
168 exit 1
169 fi
170
171 # Extract preview URL from wrangler output
172 PREVIEW_URL=$(echo "$VERSION_OUTPUT" | grep -oE 'Version Preview URL: https://[^ ]+' | sed 's/Version Preview URL: //')
173
174 # Fallback: construct URL from version ID
175 if [ -z "$PREVIEW_URL" ]; then
176 VERSION_ID=$(echo "$VERSION_OUTPUT" | grep -oE 'Worker Version ID: [a-f0-9-]+' | sed 's/Worker Version ID: //')
177 if [ -n "$VERSION_ID" ]; then
178 VERSION_PREFIX=$(echo "$VERSION_ID" | cut -c1-8)
179 PREVIEW_URL="https://${VERSION_PREFIX}-kumo-docs.design-engineering.workers.dev"
180 fi
181 fi
182
183 if [ -z "$PREVIEW_URL" ]; then
184 echo "::error::Failed to determine docs preview URL"
185 exit 1
186 fi
187
188 echo "preview_url=$PREVIEW_URL" >> "$GITHUB_OUTPUT"
189
190 - name: Comment on PR
191 uses: actions/github-script@v7
192 env:
193 PREVIEW_URL: ${{ steps.deploy.outputs.preview_url }}
194 PR_NUMBER: ${{ steps.set-pr.outputs.pr_number }}
195 with:
196 script: |
197 const marker = '<!-- kumo-docs-preview -->';
198 const previewUrl = process.env.PREVIEW_URL;
199 const prNumber = parseInt(process.env.PR_NUMBER, 10);
200 const commitSha = context.sha.substring(0, 7);
201 const body = [
202 marker,
203 '### Docs Preview',
204 '',
205 `[View docs preview](${previewUrl})`,
206 '',
207 `Commit: \`${commitSha}\``,
208 ].join('\n');
209
210 const { data: comments } = await github.rest.issues.listComments({
211 owner: context.repo.owner,
212 repo: context.repo.repo,
213 issue_number: prNumber,
214 });
215
216 const existing = comments.find(c => c.body?.startsWith(marker));
217
218 if (existing) {
219 await github.rest.issues.updateComment({
220 owner: context.repo.owner,
221 repo: context.repo.repo,
222 comment_id: existing.id,
223 body,
224 });
225 } else {
226 await github.rest.issues.createComment({
227 owner: context.repo.owner,
228 repo: context.repo.repo,
229 issue_number: prNumber,
230 body,
231 });
232 }
233
234 visual-regression:
235 name: Visual Regression
236 needs: docs-deploy
237 if: always() && needs.docs-deploy.result == 'success' && needs.docs-deploy.outputs.preview_url != ''
238 runs-on: ubuntu-latest
239 timeout-minutes: 15
240 steps:
241 # Pin to main so that a PR modifying run-visual-regression.ts cannot
242 # execute its own version of the script with secrets in env.
243 - name: Checkout VR script from main
244 uses: actions/checkout@v4
245 with:
246 ref: main
247 sparse-checkout: |
248 ci/visual-regression
249 .github/actions/install-dependencies
250
251 # Fetch the PR's HEAD so git diff can detect changed files.
252 # (HEAD points to main after checkout, so we need the explicit SHA)
253 - name: Fetch PR head for git diff
254 run: git fetch --depth=1 origin ${{ github.event.pull_request.head.sha }}
255
256 - name: Install Dependencies
257 uses: ./.github/actions/install-dependencies
258 with:
259 filter: "@cloudflare/kumo..."
260
261 - name: Run Visual Regression
262 env:
263 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
264 GITHUB_PR_NUMBER: ${{ needs.docs-deploy.outputs.pr_number }}
265 GITHUB_REPOSITORY: ${{ github.repository }}
266 PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
267 BEFORE_URL: "https://kumo-ui.com"
268 AFTER_URL: ${{ needs.docs-deploy.outputs.preview_url }}
269 SCREENSHOT_API_KEY: ${{ secrets.SCREENSHOT_API_KEY }}
270 run: pnpm tsx ci/visual-regression/run-visual-regression.ts
271
272 - name: Upload Screenshots
273 uses: actions/upload-artifact@v4
274 if: always()
275 with:
276 name: visual-regression-screenshots
277 path: ci/visual-regression/screenshots/
278 retention-days: 7
279