Skip to content

Commit 24c8155

Browse files
authored
Migrate PR preview deploys from Netlify to GitHub Pages (#109)
Netlify's GitHub integration stopped posting preview deploys. Replace it with a GitHub Pages based PR preview, mirroring the secure split used in glsp-client and theia-website: - pr-preview-build: builds the Hugo site for a PR in an untrusted context (read-only token, no secrets) and uploads it as an artifact - pr-preview-deploy: deploys the artifact to the gh-pages branch from a trusted workflow_run context and comments the preview URL on the PR - pr-preview-remove: removes the preview when the PR is closed - gh-pages-cleanup: squashes gh-pages history monthly Remove the now-unused netlify.toml.
1 parent a77315e commit 24c8155

5 files changed

Lines changed: 193 additions & 35 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: GH Pages Cleanup
2+
3+
on:
4+
schedule:
5+
- cron: '0 3 1 * *' # monthly, 1st day 3am UTC
6+
workflow_dispatch:
7+
8+
jobs:
9+
cleanup:
10+
name: Squash History
11+
runs-on: ubuntu-22.04
12+
permissions:
13+
contents: write
14+
steps:
15+
- name: Squash gh-pages history
16+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
17+
with:
18+
script: |
19+
const owner = context.repo.owner;
20+
const repo = context.repo.repo;
21+
22+
const { data: ref } = await github.rest.git.getRef({ owner, repo, ref: 'heads/gh-pages' });
23+
const { data: commit } = await github.rest.git.getCommit({ owner, repo, commit_sha: ref.object.sha });
24+
25+
// Create an orphan commit (no parents) reusing the current tree
26+
const { data: newCommit } = await github.rest.git.createCommit({
27+
owner, repo,
28+
message: 'Squash gh-pages history',
29+
tree: commit.tree.sha,
30+
parents: []
31+
});
32+
await github.rest.git.updateRef({ owner, repo, ref: 'heads/gh-pages', sha: newCommit.sha, force: true });
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: PR Preview Build
2+
3+
on:
4+
pull_request:
5+
branches: [master]
6+
7+
permissions:
8+
contents: read
9+
10+
concurrency:
11+
group: pr-preview-${{ github.event.pull_request.number }}
12+
cancel-in-progress: true
13+
14+
jobs:
15+
build:
16+
name: Build Preview
17+
runs-on: ubuntu-22.04
18+
steps:
19+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
20+
with:
21+
submodules: recursive
22+
- name: Setup Hugo
23+
uses: peaceiris/actions-hugo@2752ce1d29631191ea3f27c23495fa06139a5b78 # v3.2.1
24+
with:
25+
hugo-version: '0.78.1'
26+
extended: true
27+
- name: Compute preview base URL
28+
id: base
29+
run: |
30+
OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
31+
REPO="${{ github.event.repository.name }}"
32+
echo "url=https://${OWNER}.github.io/${REPO}/pr-previews/pr-${{ github.event.number }}/" >> "$GITHUB_OUTPUT"
33+
- name: Build website
34+
run: hugo --gc --minify --buildFuture -b "${{ steps.base.outputs.url }}"
35+
- name: Save PR number
36+
run: |
37+
mkdir -p ./pr-metadata
38+
echo "${{ github.event.number }}" > ./pr-metadata/pr-number
39+
- name: Upload preview artifact
40+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
41+
with:
42+
name: preview-site
43+
path: ./public
44+
- name: Upload PR metadata
45+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
46+
with:
47+
name: pr-metadata
48+
path: ./pr-metadata
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: PR Preview Deploy
2+
3+
# NOTE: Do NOT add any steps that run scripts from the repository (e.g., npm install,
4+
# npm run, or any other commands that execute repository code). Doing so would allow
5+
# malicious PR authors to execute arbitrary code with access to repository secrets.
6+
7+
on:
8+
workflow_run:
9+
workflows: ['PR Preview Build']
10+
types: [completed]
11+
12+
permissions:
13+
actions: read
14+
contents: write
15+
pull-requests: write
16+
17+
concurrency:
18+
group: pr-preview-deploy
19+
cancel-in-progress: false
20+
21+
jobs:
22+
deploy:
23+
name: Deploy Preview
24+
if: >
25+
github.event.workflow_run.conclusion == 'success' &&
26+
github.event.workflow_run.event == 'pull_request'
27+
runs-on: ubuntu-22.04
28+
steps:
29+
- name: Download PR metadata
30+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
31+
with:
32+
name: pr-metadata
33+
path: ./pr-metadata
34+
github-token: ${{ secrets.GITHUB_TOKEN }}
35+
run-id: ${{ github.event.workflow_run.id }}
36+
# The pr-number artifact comes from the untrusted (fork-controlled) build
37+
# job. Reading a number is low-risk, but we still sanity-check it's digits
38+
# only so it can't inject output or traverse paths when used below.
39+
- name: Read PR number
40+
id: pr
41+
run: |
42+
number="$(cat ./pr-metadata/pr-number)"
43+
[[ "$number" =~ ^[0-9]+$ ]] || { echo "::error::Refusing non-numeric PR number from build artifact"; exit 1; }
44+
echo "number=$number" >> "$GITHUB_OUTPUT"
45+
- name: Checkout
46+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
47+
- name: Download preview site
48+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
49+
with:
50+
name: preview-site
51+
path: ./preview-site
52+
github-token: ${{ secrets.GITHUB_TOKEN }}
53+
run-id: ${{ github.event.workflow_run.id }}
54+
- name: Deploy preview
55+
uses: rossjrw/pr-preview-action@ffa7509e91a3ec8dfc2e5536c4d5c1acdf7a6de9 # v1.8.1
56+
id: preview
57+
with:
58+
source-dir: ./preview-site
59+
preview-branch: gh-pages
60+
umbrella-dir: pr-previews
61+
pr-number: ${{ steps.pr.outputs.number }}
62+
action: deploy
63+
comment: false
64+
- name: Comment on PR
65+
uses: marocchino/sticky-pull-request-comment@0ea0beb66eb9baf113663a64ec522f60e49231c0 # v3.0.4
66+
with:
67+
header: pr-preview
68+
number: ${{ steps.pr.outputs.number }}
69+
message: |
70+
🚀 **Website preview deployed to** ${{ steps.preview.outputs.deployment-url }}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: PR Preview Remove
2+
3+
# SECURITY: This workflow uses pull_request_target which has access to secrets.
4+
# It is safe because:
5+
# 1. It only triggers on PR close (not open/sync)
6+
# 2. It only checks out the base branch (not PR code)
7+
# 3. It never executes any PR-controlled scripts
8+
#
9+
# NOTE: Do NOT add any steps that run scripts from the repository (e.g., npm install,
10+
# npm run, or any other commands that execute repository code). Doing so would allow
11+
# malicious PR authors to execute arbitrary code with access to repository secrets.
12+
13+
on:
14+
pull_request_target:
15+
types: [closed]
16+
17+
permissions:
18+
contents: write
19+
pull-requests: write
20+
21+
concurrency:
22+
group: pr-preview-deploy
23+
cancel-in-progress: false
24+
25+
jobs:
26+
remove:
27+
name: Remove Preview
28+
runs-on: ubuntu-22.04
29+
steps:
30+
- name: Checkout
31+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
32+
- name: Remove preview
33+
uses: rossjrw/pr-preview-action@ffa7509e91a3ec8dfc2e5536c4d5c1acdf7a6de9 # v1.8.1
34+
with:
35+
preview-branch: gh-pages
36+
umbrella-dir: pr-previews
37+
action: remove
38+
- name: Update PR comment
39+
uses: marocchino/sticky-pull-request-comment@0ea0beb66eb9baf113663a64ec522f60e49231c0 # v3.0.4
40+
with:
41+
header: pr-preview
42+
number: ${{ github.event.pull_request.number }}
43+
message: '**🌐 Website preview:** Removed (PR closed).'

netlify.toml

Lines changed: 0 additions & 35 deletions
This file was deleted.

0 commit comments

Comments
 (0)