Skip to content

Commit dd65e93

Browse files
authored
Playground: Deploy Previews for Pull Requests (#890)
1 parent 47f7acc commit dd65e93

File tree

3 files changed

+193
-37
lines changed

3 files changed

+193
-37
lines changed

.github/workflows/javascript.yml

Lines changed: 141 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ on:
55
branches:
66
- main
77
pull_request:
8-
types: [opened, synchronize, labeled]
9-
pull_request_review:
10-
types: [submitted]
8+
types: [opened, synchronize, labeled, closed]
119
release:
1210
types: [published]
1311

@@ -17,6 +15,7 @@ permissions:
1715

1816
jobs:
1917
main:
18+
if: github.event.action != 'closed'
2019
env:
2120
GH_TOKEN: ${{ github.token }}
2221

@@ -71,6 +70,8 @@ jobs:
7170
- name: Run `build` for all NX packages
7271
env:
7372
RELEASE_BUILD: ${{ github.event_name == 'release' && 'true' || 'false' }}
73+
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }}
74+
GITHUB_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
7475
run: yarn build
7576

7677
- name: Run `test` for all NX packages
@@ -84,6 +85,15 @@ jobs:
8485
if-no-files-found: error
8586
retention-days: 1
8687

88+
- name: Upload docs artifact
89+
if: github.event_name == 'pull_request'
90+
uses: actions/upload-artifact@v4
91+
with:
92+
name: docs-preview
93+
path: docs/.vitepress/dist
94+
if-no-files-found: error
95+
retention-days: 1
96+
8797
# NX is not able to properly detect affected changes if things change in C source or the WASM build
8898
#
8999
# - name: Set NX SHAs
@@ -136,8 +146,6 @@ jobs:
136146
if: |
137147
(github.repository == 'marcoroth/herb' && github.event_name == 'pull_request' && github.event.pull_request.user.login == github.repository_owner) ||
138148
(github.repository == 'marcoroth/herb' && github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'release-preview')) ||
139-
(github.repository == 'marcoroth/herb' && github.event_name == 'pull_request_review' && github.event.review.state == 'approved') ||
140-
(github.repository == 'marcoroth/herb' && github.event_name == 'pull_request_review' && contains(github.event.pull_request.labels.*.name, 'release-preview')) ||
141149
(github.repository == 'marcoroth/herb' && github.event_name == 'push' && github.ref == 'refs/heads/main')
142150
143151
runs-on: ubuntu-latest
@@ -213,3 +221,131 @@ jobs:
213221
run: |
214222
cd javascript/packages/vscode
215223
npx ovsx publish -p $OVSX_PAT
224+
225+
deploy-preview:
226+
name: Deploy docs preview to Cloudflare Pages
227+
needs: [main, preview-check]
228+
if: needs.preview-check.outputs.has-permissions == 'true'
229+
runs-on: ubuntu-latest
230+
231+
permissions:
232+
contents: read
233+
deployments: write
234+
pull-requests: write
235+
236+
steps:
237+
- name: Download docs artifact
238+
uses: actions/download-artifact@v4
239+
with:
240+
name: docs-preview
241+
path: dist
242+
243+
- name: Deploy to Cloudflare Pages
244+
id: deploy
245+
uses: cloudflare/pages-action@v1
246+
with:
247+
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
248+
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
249+
projectName: herb-tools
250+
directory: dist
251+
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
252+
253+
- name: Add comment with preview URL
254+
uses: actions/github-script@v7
255+
with:
256+
script: |
257+
const previewUrl = '${{ steps.deploy.outputs.url }}';
258+
const comment = `### 🌿 Interactive Playground and Documentation Preview
259+
260+
A preview deployment has been built for this pull request. Try out the changes live in the interactive playground:
261+
262+
- **[Playground Preview](${previewUrl}/playground)**
263+
- **[Documentation Preview](${previewUrl})**
264+
- **[Website Preview](${previewUrl})**
265+
266+
---
267+
<sub>🌱 Grown from commit [\`${context.payload.pull_request.head.sha.substring(0, 7)}\`](https://github.com/${context.repo.owner}/${context.repo.repo}/commit/${context.payload.pull_request.head.sha})</sub>`;
268+
269+
const { data: comments } = await github.rest.issues.listComments({
270+
owner: context.repo.owner,
271+
repo: context.repo.repo,
272+
issue_number: context.issue.number,
273+
});
274+
275+
const botComment = comments.find(comment =>
276+
comment.user.type === 'Bot' &&
277+
comment.body.includes('🌿 Interactive Playground and Documentation Preview')
278+
);
279+
280+
if (botComment) {
281+
await github.rest.issues.updateComment({
282+
owner: context.repo.owner,
283+
repo: context.repo.repo,
284+
comment_id: botComment.id,
285+
body: comment
286+
});
287+
} else {
288+
await github.rest.issues.createComment({
289+
owner: context.repo.owner,
290+
repo: context.repo.repo,
291+
issue_number: context.issue.number,
292+
body: comment
293+
});
294+
}
295+
296+
cleanup-preview:
297+
name: Cleanup docs preview deployment
298+
if: github.event_name == 'pull_request' && github.event.action == 'closed'
299+
runs-on: ubuntu-latest
300+
301+
permissions:
302+
pull-requests: write
303+
304+
steps:
305+
- name: Delete Cloudflare Pages deployment
306+
env:
307+
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
308+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
309+
BRANCH_NAME: ${{ github.event.pull_request.head.ref }}
310+
run: |
311+
DEPLOYMENTS=$(curl -s -X GET \
312+
"https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/pages/projects/herb-tools/deployments" \
313+
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
314+
-H "Content-Type: application/json")
315+
316+
DEPLOYMENT_ID=$(echo "$DEPLOYMENTS" | jq -r ".result[] | select(.deployment_trigger.metadata.branch == \"$BRANCH_NAME\") | .id" | head -n 1)
317+
318+
if [ -n "$DEPLOYMENT_ID" ]; then
319+
echo "Deleting deployment $DEPLOYMENT_ID for branch $BRANCH_NAME"
320+
curl -X DELETE \
321+
"https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/pages/projects/herb-tools/deployments/$DEPLOYMENT_ID" \
322+
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
323+
-H "Content-Type: application/json"
324+
else
325+
echo "No deployment found for branch $BRANCH_NAME"
326+
fi
327+
328+
- name: Update PR comment
329+
uses: actions/github-script@v7
330+
with:
331+
script: |
332+
const { data: comments } = await github.rest.issues.listComments({
333+
owner: context.repo.owner,
334+
repo: context.repo.repo,
335+
issue_number: context.issue.number,
336+
});
337+
338+
const botComment = comments.find(comment =>
339+
comment.user.type === 'Bot' &&
340+
comment.body.includes('🌿 Interactive Playground and Documentation Preview')
341+
);
342+
343+
if (botComment) {
344+
const updatedComment = botComment.body + '\n\n---\n\n✅ Preview deployment has been cleaned up.';
345+
await github.rest.issues.updateComment({
346+
owner: context.repo.owner,
347+
repo: context.repo.repo,
348+
comment_id: botComment.id,
349+
body: updatedComment
350+
});
351+
}

playground/src/controllers/playground_controller.js

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -778,16 +778,26 @@ export default class extends Controller {
778778
if (this.hasCommitHashTarget) {
779779
if (typeof __COMMIT_INFO__ !== 'undefined') {
780780
const commitInfo = __COMMIT_INFO__
781-
const githubUrl = `https://github.com/marcoroth/herb/commit/${commitInfo.hash}`
782781

783-
if (commitInfo.ahead > 0) {
784-
this.commitHashTarget.textContent = `${commitInfo.tag} (+${commitInfo.ahead} commits) ${commitInfo.hash}`
782+
if (commitInfo.prNumber) {
783+
const prUrl = `https://github.com/marcoroth/herb/pull/${commitInfo.prNumber}`
784+
const commitUrl = `https://github.com/marcoroth/herb/commit/${commitInfo.hash}`
785+
786+
this.commitHashTarget.textContent = `PR #${commitInfo.prNumber} @ ${commitInfo.hash}`
787+
this.commitHashTarget.href = prUrl
788+
this.commitHashTarget.title = `View PR #${commitInfo.prNumber} on GitHub (commit ${commitInfo.hash})`
785789
} else {
786-
this.commitHashTarget.textContent = `${commitInfo.tag} ${commitInfo.hash}`
787-
}
790+
const githubUrl = `https://github.com/marcoroth/herb/commit/${commitInfo.hash}`
791+
792+
if (commitInfo.ahead > 0) {
793+
this.commitHashTarget.textContent = `${commitInfo.tag} (+${commitInfo.ahead} commits) ${commitInfo.hash}`
794+
} else {
795+
this.commitHashTarget.textContent = `${commitInfo.tag} ${commitInfo.hash}`
796+
}
788797

789-
this.commitHashTarget.href = githubUrl
790-
this.commitHashTarget.title = `View commit ${commitInfo.hash} on GitHub`
798+
this.commitHashTarget.href = githubUrl
799+
this.commitHashTarget.title = `View commit ${commitInfo.hash} on GitHub`
800+
}
791801
} else {
792802
this.commitHashTarget.textContent = 'unknown'
793803
this.commitHashTarget.removeAttribute('href')

playground/vite.config.ts

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,37 @@ function getCommitInfo() {
55
let hash = "unknown"
66
let tag = "unknown"
77
let ahead = 0
8+
let prNumber = null
89

9-
try {
10-
try {
11-
hash = execSync("git rev-parse --short HEAD", { encoding: "utf8" }).trim()
12-
} catch (hashError) {
13-
if (process.env.GITHUB_SHA) {
14-
hash = process.env.GITHUB_SHA.substring(0, 7)
15-
}
10+
const isGitHubActions = process.env.GITHUB_ACTIONS === 'true'
11+
12+
if (isGitHubActions) {
13+
if (process.env.GITHUB_EVENT_NAME === 'pull_request' && process.env.GITHUB_PR_HEAD_SHA) {
14+
hash = process.env.GITHUB_PR_HEAD_SHA.substring(0, 8)
15+
prNumber = process.env.GITHUB_PR_NUMBER
16+
tag = `PR #${prNumber}`
17+
ahead = 0
18+
console.log(`GitHub Actions PR build: hash=${hash}, PR=${prNumber}`)
19+
} else {
20+
hash = process.env.GITHUB_SHA?.substring(0, 8) || "unknown"
1621
}
1722

23+
if (process.env.GITHUB_REF?.startsWith('refs/tags/')) {
24+
tag = process.env.GITHUB_REF.replace('refs/tags/', '')
25+
ahead = 0
26+
console.log(`GitHub Actions tag build: hash=${hash}, tag=${tag}`)
27+
} else {
28+
tag = process.env.GITHUB_REF_NAME || "unknown"
29+
ahead = 0
30+
console.log(`GitHub Actions branch build: hash=${hash}, ref=${tag}`)
31+
}
32+
33+
return { hash, tag, ahead, prNumber }
34+
}
35+
36+
try {
37+
hash = execSync("git rev-parse --short=8 HEAD", { encoding: "utf8" }).trim()
38+
1839
try {
1940
execSync("git fetch --tags --force", { stdio: 'ignore' })
2041
tag = execSync("git describe --tags --abbrev=0", { encoding: "utf8" }).trim()
@@ -27,36 +48,25 @@ function getCommitInfo() {
2748
}
2849
} catch (tagError) {
2950
console.warn("Could not get git tag info:", tagError.message)
30-
31-
if (process.env.GITHUB_REF) {
32-
if (process.env.GITHUB_REF.startsWith('refs/tags/')) {
33-
tag = process.env.GITHUB_REF.replace('refs/tags/', '')
34-
ahead = 0
35-
} else if (process.env.GITHUB_REF_NAME) {
36-
tag = process.env.GITHUB_REF_NAME
37-
}
38-
}
51+
tag = "dev"
3952
}
4053

41-
console.log(`Git info: hash=${hash}, tag=${tag}, ahead=${ahead}`)
42-
return { hash, tag, ahead }
54+
console.log(`Local build: hash=${hash}, tag=${tag}, ahead=${ahead}`)
4355
} catch (error) {
4456
console.warn("Could not get git commit info:", error.message)
45-
return {
46-
hash: process.env.GITHUB_SHA?.substring(0, 7) || "unknown",
47-
tag: process.env.GITHUB_REF_NAME || "unknown",
48-
ahead: 0
49-
}
57+
hash = "unknown"
58+
tag = "unknown"
5059
}
60+
61+
return { hash, tag, ahead, prNumber }
5162
}
5263

5364
export default defineConfig({
5465
define: {
5566
__COMMIT_INFO__: JSON.stringify(getCommitInfo()),
5667
},
5768
server: {
58-
port: process.env.PORT ? parseInt(process.env.PORT) : 5173,
59-
allowedHosts: ["playground.herb-tools.dev"],
69+
port: process.env.PORT ? parseInt(process.env.PORT) : 5173
6070
},
6171
plugins: [],
6272
})

0 commit comments

Comments
 (0)