1- name : Build, Deploy, and On-Demand Preview
1+ name : Build and Deploy
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
1310
14- env :
15- PREVIEW_ROOT : pr-preview
11+ permissions :
12+ contents : read
13+ pages : write
14+ id-token : write
1615
17- jobs :
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
16+ concurrency :
17+ group : " pages"
18+ cancel-in-progress : false
2819
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'
20+ jobs :
21+ build :
4022 runs-on : ubuntu-latest
41- permissions :
42- contents : write
43- concurrency :
44- group : gh-pages
45- cancel-in-progress : false
4623 steps :
4724 - name : Checkout
4825 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- uses : actions/checkout@v4
63- with :
64- ref : gh-pages
65- path : gh-pages
66-
67- - name : Deploy main site to gh-pages
68- run : |
69- set -euo pipefail
70-
71- find gh-pages -mindepth 1 -maxdepth 1 \
72- ! -name .git \
73- ! -name "${PREVIEW_ROOT}" \
74- -exec rm -rf {} +
75- cp -a site/. gh-pages/
76-
77- cd gh-pages
78- git config user.name "github-actions[bot]"
79- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
80- git add --all
81-
82- if git diff --cached --quiet; then
83- echo "No gh-pages changes to deploy."
84- else
85- git commit -m "Deploy main site"
86- git push origin HEAD:gh-pages
87- fi
88-
89- preview-command :
90- if : >-
91- github.event_name == 'issue_comment' &&
92- github.event.issue.pull_request &&
93- contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) &&
94- (
95- startsWith(github.event.comment.body, '/preview') ||
96- startsWith(github.event.comment.body, '/unpreview')
97- )
98- runs-on : ubuntu-latest
99- permissions :
100- contents : read
101- pull-requests : read
102- outputs :
103- action : ${{ steps.preview.outputs.action }}
104- head_sha : ${{ steps.preview.outputs.head_sha }}
105- pr_number : ${{ steps.preview.outputs.pr_number }}
106- preview_url : ${{ steps.preview.outputs.preview_url }}
107- steps :
108- - name : Read command and PR metadata
109- id : preview
110- env :
111- GH_TOKEN : ${{ github.token }}
112- PR_NUMBER : ${{ github.event.issue.number }}
113- run : |
114- set -euo pipefail
115-
116- body="$(jq -r '.comment.body' "$GITHUB_EVENT_PATH" | tr -d '\r')"
117- body="$(printf '%s' "$body" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
118-
119- case "$body" in
120- /preview)
121- action=deploy
122- ;;
123- /unpreview)
124- action=remove
125- ;;
126- *)
127- action=ignore
128- ;;
129- esac
130-
131- repo_name="${GITHUB_REPOSITORY#*/}"
132- if [ "$repo_name" = "${GITHUB_REPOSITORY_OWNER}.github.io" ]; then
133- preview_url="https://${repo_name}/${PREVIEW_ROOT}/pr-${PR_NUMBER}/"
134- else
135- preview_url="https://${GITHUB_REPOSITORY_OWNER}.github.io/${repo_name}/${PREVIEW_ROOT}/pr-${PR_NUMBER}/"
136- fi
137- {
138- echo "action=${action}"
139- echo "pr_number=${PR_NUMBER}"
140- echo "preview_url=${preview_url}"
141- } >> "$GITHUB_OUTPUT"
142-
143- if [ "$action" != "deploy" ]; then
144- exit 0
145- fi
146-
147- pr_json="$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}")"
148- head_repo="$(printf '%s' "$pr_json" | jq -r '.head.repo.full_name')"
149- base_repo="$(printf '%s' "$pr_json" | jq -r '.base.repo.full_name')"
150- head_sha="$(printf '%s' "$pr_json" | jq -r '.head.sha')"
151-
152- if [ "$head_repo" != "$base_repo" ]; then
153- echo "::error::PR previews are only supported for branches in this repository."
154- exit 1
155- fi
156-
157- if [ -z "$head_sha" ] || [ "$head_sha" = "null" ]; then
158- echo "::error::Could not determine the PR head SHA."
159- exit 1
160- fi
161-
162- echo "head_sha=${head_sha}" >> "$GITHUB_OUTPUT"
163-
164- build-preview :
165- if : needs.preview-command.outputs.action == 'deploy'
166- needs : preview-command
167- runs-on : ubuntu-latest
168- permissions :
169- contents : read
170- steps :
171- - name : Checkout PR head
172- uses : actions/checkout@v4
173- with :
174- ref : ${{ needs.preview-command.outputs.head_sha }}
175- persist-credentials : false
17626
17727 - name : Install uv
17828 uses : astral-sh/setup-uv@v2
@@ -183,137 +33,19 @@ jobs:
18333 - name : Build site
18434 run : uv run mkdocs build --strict
18535
186- - name : Upload preview artifact
187- uses : actions/upload-artifact@v4
36+ - name : Upload artifact
37+ uses : actions/upload-pages- artifact@v3
18838 with :
189- name : pr-preview-${{ needs.preview-command.outputs.pr_number }}
19039 path : site
191- if-no-files-found : error
192-
193- deploy-preview :
194- if : needs.preview-command.outputs.action == 'deploy'
195- needs :
196- - preview-command
197- - build-preview
198- runs-on : ubuntu-latest
199- permissions :
200- contents : write
201- issues : write
202- concurrency :
203- group : gh-pages
204- cancel-in-progress : false
205- steps :
206- - name : Checkout gh-pages
207- uses : actions/checkout@v4
208- with :
209- ref : gh-pages
210- path : gh-pages
211-
212- - name : Download preview artifact
213- uses : actions/download-artifact@v4
214- with :
215- name : pr-preview-${{ needs.preview-command.outputs.pr_number }}
216- path : preview-site
21740
218- - name : Deploy PR preview
219- env :
220- PR_NUMBER : ${{ needs.preview-command.outputs.pr_number }}
221- run : |
222- set -euo pipefail
223-
224- preview_dir="gh-pages/${PREVIEW_ROOT}/pr-${PR_NUMBER}"
225- rm -rf "$preview_dir"
226- mkdir -p "$preview_dir"
227- cp -a preview-site/. "$preview_dir"/
228-
229- cd gh-pages
230- git config user.name "github-actions[bot]"
231- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
232- git add --all "${PREVIEW_ROOT}"
233-
234- if git diff --cached --quiet; then
235- echo "No preview changes to deploy."
236- else
237- git commit -m "Deploy PR ${PR_NUMBER} preview"
238- git push origin HEAD:gh-pages
239- fi
240-
241- - name : Comment with preview URL
242- env :
243- GH_TOKEN : ${{ github.token }}
244- PR_NUMBER : ${{ needs.preview-command.outputs.pr_number }}
245- PREVIEW_URL : ${{ needs.preview-command.outputs.preview_url }}
246- run : |
247- set -euo pipefail
248-
249- marker="<!-- pr-preview:${PR_NUMBER} -->"
250- body="$(printf '%s\n%s\n' "$marker" "Preview published: ${PREVIEW_URL}")"
251-
252- comment_id="$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" --paginate \
253- | jq -r --arg marker "$marker" '.[] | select(.user.login == "github-actions[bot]" and (.body | contains($marker))) | .id' \
254- | tail -n 1)"
255-
256- if [ -n "$comment_id" ]; then
257- gh api --method PATCH "repos/${GITHUB_REPOSITORY}/issues/comments/${comment_id}" -f body="$body"
258- else
259- gh api --method POST "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" -f body="$body"
260- fi
261-
262- remove-preview :
263- if : needs.preview-command.outputs.action == 'remove'
264- needs : preview-command
41+ deploy :
42+ if : github.event_name == 'push' && github.ref == 'refs/heads/main'
43+ needs : build
26544 runs-on : ubuntu-latest
266- permissions :
267- contents : write
268- issues : write
269- concurrency :
270- group : gh-pages
271- cancel-in-progress : false
45+ environment :
46+ name : github-pages
47+ url : ${{ steps.deployment.outputs.page_url }}
27248 steps :
273- - name : Checkout gh-pages
274- uses : actions/checkout@v4
275- with :
276- ref : gh-pages
277- path : gh-pages
278-
279- - name : Remove PR preview
280- env :
281- PR_NUMBER : ${{ needs.preview-command.outputs.pr_number }}
282- run : |
283- set -euo pipefail
284-
285- preview_dir="gh-pages/${PREVIEW_ROOT}/pr-${PR_NUMBER}"
286- rm -rf "$preview_dir"
287- mkdir -p "gh-pages/${PREVIEW_ROOT}"
288-
289- cd gh-pages
290- git config user.name "github-actions[bot]"
291- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
292- git add --all "${PREVIEW_ROOT}"
293-
294- if git diff --cached --quiet; then
295- echo "No preview changes to remove."
296- else
297- git commit -m "Remove PR ${PR_NUMBER} preview"
298- git push origin HEAD:gh-pages
299- fi
300-
301- - name : Comment that preview was removed
302- env :
303- GH_TOKEN : ${{ github.token }}
304- PR_NUMBER : ${{ needs.preview-command.outputs.pr_number }}
305- run : |
306- set -euo pipefail
307-
308- marker="<!-- pr-preview:${PR_NUMBER} -->"
309- body="$(printf '%s\n%s\n' "$marker" "Preview removed for PR #${PR_NUMBER}.")"
310-
311- comment_id="$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" --paginate \
312- | jq -r --arg marker "$marker" '.[] | select(.user.login == "github-actions[bot]" and (.body | contains($marker))) | .id' \
313- | tail -n 1)"
314-
315- if [ -n "$comment_id" ]; then
316- gh api --method PATCH "repos/${GITHUB_REPOSITORY}/issues/comments/${comment_id}" -f body="$body"
317- else
318- gh api --method POST "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" -f body="$body"
319- fi
49+ - name : Deploy to GitHub Pages
50+ id : deployment
51+ uses : actions/deploy-pages@v4
0 commit comments