Skip to content

Commit 6ed4cb0

Browse files
authored
Add on-demand PR preview workflow (#19)
1 parent aca8702 commit 6ed4cb0

3 files changed

Lines changed: 318 additions & 22 deletions

File tree

.github/pull_request_template.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
Optional preview: after opening the PR, comment `/preview` to publish a public preview and `/unpreview` to remove it. The workflow will reply with the preview URL. Please check for secrets before publishing a preview.
2+
13
This template includes 3 checklists, only 1 of which is needed for your PR. Please **delete as appropriate** before submitting the PR.
24

35
For _Papers of the Month_, there are two types of PR:

.github/workflows/deploy.yml

Lines changed: 307 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,189 @@
1-
name: Build and Deploy
1+
name: Build, Deploy, and On-Demand Preview
22

3-
on:
3+
"on":
44
push:
55
branches:
66
- main
77
pull_request:
88
branches:
99
- main
10+
issue_comment:
11+
types:
12+
- created
1013

11-
permissions:
12-
contents: read
13-
pages: write
14-
id-token: write
15-
16-
concurrency:
17-
group: "pages"
18-
cancel-in-progress: false
14+
env:
15+
PREVIEW_ROOT: pr-preview
1916

2017
jobs:
21-
build:
18+
build-pr:
19+
if: github.event_name == 'pull_request'
20+
runs-on: ubuntu-latest
21+
permissions:
22+
contents: read
23+
steps:
24+
- name: Checkout
25+
uses: actions/checkout@v4
26+
with:
27+
persist-credentials: false
28+
29+
- name: Install uv
30+
uses: astral-sh/setup-uv@v2
31+
32+
- name: Install dependencies
33+
run: uv sync
34+
35+
- name: Build site
36+
run: uv run mkdocs build --strict
37+
38+
deploy-main:
39+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
2240
runs-on: ubuntu-latest
41+
permissions:
42+
contents: write
43+
concurrency:
44+
group: gh-pages
45+
cancel-in-progress: false
2346
steps:
2447
- name: Checkout
2548
uses: actions/checkout@v4
49+
with:
50+
persist-credentials: false
51+
52+
- name: Install uv
53+
uses: astral-sh/setup-uv@v2
54+
55+
- name: Install dependencies
56+
run: uv sync
57+
58+
- name: Build site
59+
run: uv run mkdocs build --strict
60+
61+
- name: Checkout gh-pages
62+
env:
63+
GH_TOKEN: ${{ github.token }}
64+
run: |
65+
set -euo pipefail
66+
67+
git init gh-pages
68+
cd gh-pages
69+
git remote add origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
70+
71+
if git ls-remote --exit-code --heads origin gh-pages > /dev/null; then
72+
git fetch --depth=1 origin gh-pages
73+
git checkout -B gh-pages FETCH_HEAD
74+
else
75+
git checkout --orphan gh-pages
76+
fi
77+
78+
- name: Deploy main site to gh-pages
79+
run: |
80+
set -euo pipefail
81+
82+
find gh-pages -mindepth 1 -maxdepth 1 \
83+
! -name .git \
84+
! -name "${PREVIEW_ROOT}" \
85+
-exec rm -rf {} +
86+
cp -a site/. gh-pages/
87+
88+
cd gh-pages
89+
git config user.name "github-actions[bot]"
90+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
91+
git add --all
92+
93+
if git diff --cached --quiet; then
94+
echo "No gh-pages changes to deploy."
95+
else
96+
git commit -m "Deploy main site"
97+
git push origin HEAD:gh-pages
98+
fi
99+
100+
preview-command:
101+
if: >-
102+
github.event_name == 'issue_comment' &&
103+
github.event.issue.pull_request &&
104+
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) &&
105+
(
106+
startsWith(github.event.comment.body, '/preview') ||
107+
startsWith(github.event.comment.body, '/unpreview')
108+
)
109+
runs-on: ubuntu-latest
110+
permissions:
111+
contents: read
112+
pull-requests: read
113+
outputs:
114+
action: ${{ steps.preview.outputs.action }}
115+
head_sha: ${{ steps.preview.outputs.head_sha }}
116+
pr_number: ${{ steps.preview.outputs.pr_number }}
117+
preview_url: ${{ steps.preview.outputs.preview_url }}
118+
steps:
119+
- name: Read command and PR metadata
120+
id: preview
121+
env:
122+
GH_TOKEN: ${{ github.token }}
123+
PR_NUMBER: ${{ github.event.issue.number }}
124+
run: |
125+
set -euo pipefail
126+
127+
body="$(jq -r '.comment.body' "$GITHUB_EVENT_PATH" | tr -d '\r')"
128+
body="$(printf '%s' "$body" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
129+
130+
case "$body" in
131+
/preview)
132+
action=deploy
133+
;;
134+
/unpreview)
135+
action=remove
136+
;;
137+
*)
138+
action=ignore
139+
;;
140+
esac
141+
142+
repo_name="${GITHUB_REPOSITORY#*/}"
143+
if [ "$repo_name" = "${GITHUB_REPOSITORY_OWNER}.github.io" ]; then
144+
preview_url="https://${repo_name}/${PREVIEW_ROOT}/pr-${PR_NUMBER}/"
145+
else
146+
preview_url="https://${GITHUB_REPOSITORY_OWNER}.github.io/${repo_name}/${PREVIEW_ROOT}/pr-${PR_NUMBER}/"
147+
fi
148+
{
149+
echo "action=${action}"
150+
echo "pr_number=${PR_NUMBER}"
151+
echo "preview_url=${preview_url}"
152+
} >> "$GITHUB_OUTPUT"
153+
154+
if [ "$action" != "deploy" ]; then
155+
exit 0
156+
fi
157+
158+
pr_json="$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}")"
159+
head_repo="$(printf '%s' "$pr_json" | jq -r '.head.repo.full_name')"
160+
base_repo="$(printf '%s' "$pr_json" | jq -r '.base.repo.full_name')"
161+
head_sha="$(printf '%s' "$pr_json" | jq -r '.head.sha')"
162+
163+
if [ "$head_repo" != "$base_repo" ]; then
164+
echo "::error::PR previews are only supported for branches in this repository."
165+
exit 1
166+
fi
167+
168+
if [ -z "$head_sha" ] || [ "$head_sha" = "null" ]; then
169+
echo "::error::Could not determine the PR head SHA."
170+
exit 1
171+
fi
172+
173+
echo "head_sha=${head_sha}" >> "$GITHUB_OUTPUT"
174+
175+
build-preview:
176+
if: needs.preview-command.outputs.action == 'deploy'
177+
needs: preview-command
178+
runs-on: ubuntu-latest
179+
permissions:
180+
contents: read
181+
steps:
182+
- name: Checkout PR head
183+
uses: actions/checkout@v4
184+
with:
185+
ref: ${{ needs.preview-command.outputs.head_sha }}
186+
persist-credentials: false
26187

27188
- name: Install uv
28189
uses: astral-sh/setup-uv@v2
@@ -33,19 +194,143 @@ jobs:
33194
- name: Build site
34195
run: uv run mkdocs build --strict
35196

36-
- name: Upload artifact
37-
uses: actions/upload-pages-artifact@v3
197+
- name: Upload preview artifact
198+
uses: actions/upload-artifact@v4
38199
with:
200+
name: pr-preview-${{ needs.preview-command.outputs.pr_number }}
39201
path: site
202+
if-no-files-found: error
40203

41-
deploy:
42-
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
43-
needs: build
204+
deploy-preview:
205+
if: needs.preview-command.outputs.action == 'deploy'
206+
needs:
207+
- preview-command
208+
- build-preview
209+
runs-on: ubuntu-latest
210+
permissions:
211+
contents: write
212+
issues: write
213+
pull-requests: write
214+
concurrency:
215+
group: gh-pages
216+
cancel-in-progress: false
217+
steps:
218+
- name: Checkout gh-pages
219+
env:
220+
GH_TOKEN: ${{ github.token }}
221+
run: |
222+
set -euo pipefail
223+
224+
git init gh-pages
225+
cd gh-pages
226+
git remote add origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
227+
228+
if git ls-remote --exit-code --heads origin gh-pages > /dev/null; then
229+
git fetch --depth=1 origin gh-pages
230+
git checkout -B gh-pages FETCH_HEAD
231+
else
232+
git checkout --orphan gh-pages
233+
fi
234+
235+
- name: Download preview artifact
236+
uses: actions/download-artifact@v4
237+
with:
238+
name: pr-preview-${{ needs.preview-command.outputs.pr_number }}
239+
path: preview-site
240+
241+
- name: Deploy PR preview
242+
env:
243+
PR_NUMBER: ${{ needs.preview-command.outputs.pr_number }}
244+
run: |
245+
set -euo pipefail
246+
247+
preview_dir="gh-pages/${PREVIEW_ROOT}/pr-${PR_NUMBER}"
248+
rm -rf "$preview_dir"
249+
mkdir -p "$preview_dir"
250+
cp -a preview-site/. "$preview_dir"/
251+
252+
cd gh-pages
253+
git config user.name "github-actions[bot]"
254+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
255+
git add --all "${PREVIEW_ROOT}"
256+
257+
if git diff --cached --quiet; then
258+
echo "No preview changes to deploy."
259+
else
260+
git commit -m "Deploy PR ${PR_NUMBER} preview"
261+
git push origin HEAD:gh-pages
262+
fi
263+
264+
- name: Comment with preview URL
265+
env:
266+
GH_TOKEN: ${{ github.token }}
267+
PR_NUMBER: ${{ needs.preview-command.outputs.pr_number }}
268+
PREVIEW_URL: ${{ needs.preview-command.outputs.preview_url }}
269+
run: |
270+
set -euo pipefail
271+
272+
marker="<!-- pr-preview:${PR_NUMBER} -->"
273+
body="$(printf '%s\n%s\n' "$marker" "Preview published: ${PREVIEW_URL}")"
274+
gh pr comment "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --body "$body"
275+
276+
remove-preview:
277+
if: needs.preview-command.outputs.action == 'remove'
278+
needs: preview-command
44279
runs-on: ubuntu-latest
45-
environment:
46-
name: github-pages
47-
url: ${{ steps.deployment.outputs.page_url }}
280+
permissions:
281+
contents: write
282+
issues: write
283+
pull-requests: write
284+
concurrency:
285+
group: gh-pages
286+
cancel-in-progress: false
48287
steps:
49-
- name: Deploy to GitHub Pages
50-
id: deployment
51-
uses: actions/deploy-pages@v4
288+
- name: Checkout gh-pages
289+
env:
290+
GH_TOKEN: ${{ github.token }}
291+
run: |
292+
set -euo pipefail
293+
294+
git init gh-pages
295+
cd gh-pages
296+
git remote add origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
297+
298+
if git ls-remote --exit-code --heads origin gh-pages > /dev/null; then
299+
git fetch --depth=1 origin gh-pages
300+
git checkout -B gh-pages FETCH_HEAD
301+
else
302+
git checkout --orphan gh-pages
303+
fi
304+
305+
- name: Remove PR preview
306+
env:
307+
PR_NUMBER: ${{ needs.preview-command.outputs.pr_number }}
308+
run: |
309+
set -euo pipefail
310+
311+
preview_dir="gh-pages/${PREVIEW_ROOT}/pr-${PR_NUMBER}"
312+
rm -rf "$preview_dir"
313+
mkdir -p "gh-pages/${PREVIEW_ROOT}"
314+
315+
cd gh-pages
316+
git config user.name "github-actions[bot]"
317+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
318+
git add --all "${PREVIEW_ROOT}"
319+
320+
if git diff --cached --quiet; then
321+
echo "No preview changes to remove."
322+
else
323+
git commit -m "Remove PR ${PR_NUMBER} preview"
324+
git push origin HEAD:gh-pages
325+
fi
326+
327+
- name: Comment that preview was removed
328+
env:
329+
GH_TOKEN: ${{ github.token }}
330+
PR_NUMBER: ${{ needs.preview-command.outputs.pr_number }}
331+
run: |
332+
set -euo pipefail
333+
334+
marker="<!-- pr-preview:${PR_NUMBER} -->"
335+
body="$(printf '%s\n%s\n' "$marker" "Preview removed for PR #${PR_NUMBER}.")"
336+
gh pr comment "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --body "$body"

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,15 @@ The **excerpt separator** `<!-- more -->` must be included, typically after the
8181

8282
To **publish** when you're happy with your post, push it to a branch on [draft-graphcore-research.github.io](https://github.com/graphcore-research/draft-graphcore-research.github.io) for review. When it's merged into `main`, push it to [graphcore-research.github.io](https://github.com/graphcore-research/graphcore-research.github.io), e.g. if the remotes are `public` and `origin`: `git push public origin/main:main`.
8383

84+
### PR previews
85+
86+
The draft repo supports on-demand PR previews via PR comments:
87+
88+
- comment `/preview` on a PR to publish a preview
89+
- comment `/unpreview` to remove it again
90+
91+
The workflow posts the exact preview URL back to the PR. Note that the preview will be visible on the internet, so make sure you are ready to preview, and have checked for any secrets before you do it.
92+
8493
### POTM
8594

8695
Papers-of-the-month are special, consisting of a **root** page at `pages/posts/potm/YYYY/MM/potm.md`, and multiple **child** pages at `pages/posts/potm/YYYY/MM/paper-slug/paper-slug.md`.

0 commit comments

Comments
 (0)