11name : Preview
22
3- # Builds every site under demo/sites/ for a pull request and deploys each to
4- # its own Cloudflare Pages project (uq-rcc-<site>). Each preview is served at
5- # the root of its own hostname, so root-relative links need no base path.
6- # Production stays on GitHub Pages; this workflow never deploys main.
3+ # Deploys each site under demo/sites/ to Cloudflare Pages project uq-rcc-<site>.
74
85on :
96 pull_request :
3229 list=$(find demo/sites -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort | jq -Rsc 'split("\n") | map(select(length > 0))')
3330 echo "list=$list" >> "$GITHUB_OUTPUT"
3431
32+ pending :
33+ needs : sites
34+ runs-on : ubuntu-24.04
35+ permissions :
36+ contents : read
37+ pull-requests : write
38+ steps :
39+ - uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
40+ with :
41+ sparse-checkout : .github/scripts
42+
43+ - name : Mark preview as pending
44+ uses : actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
45+ with :
46+ script : |
47+ const panel = require('./.github/scripts/preview-panel.cjs');
48+ await panel.update({ github, context }, panel.pending());
49+
3550 deploy :
3651 needs : sites
3752 runs-on : ubuntu-24.04
@@ -58,52 +73,71 @@ jobs:
5873 run : npx astro build
5974
6075 - name : Deploy to Cloudflare Pages
76+ id : deploy
6177 uses : cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4
6278 with :
6379 apiToken : ${{ secrets.CLOUDFLARE_API_TOKEN }}
6480 accountId : ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
6581 command : >-
6682 pages deploy demo/sites/${{ matrix.site }}/dist
6783 --project-name=uq-rcc-${{ matrix.site }}
68- --branch=pr- ${{ github.event.pull_request.number }}
84+ --branch=${{ github.head_ref }}
6985
70- comment :
71- needs : [sites, deploy]
86+ # Matrix jobs cannot merge outputs, so pass results on as artifacts.
87+ - name : Record preview result
88+ if : always()
89+ env :
90+ SITE : ${{ matrix.site }}
91+ STATUS : ${{ steps.deploy.outcome }}
92+ COMMIT_URL : ${{ steps.deploy.outputs.deployment-url }}
93+ BRANCH_URL : ${{ steps.deploy.outputs.pages-deployment-alias-url }}
94+ run : |
95+ jq -n '{site: env.SITE, status: env.STATUS, commit: env.COMMIT_URL, branch: env.BRANCH_URL}' > preview.json
96+
97+ - uses : actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
98+ if : always()
99+ with :
100+ name : preview-${{ matrix.site }}
101+ path : preview.json
102+ retention-days : 1
103+
104+ panel :
105+ needs : [sites, pending, deploy]
106+ if : always() && needs.sites.result == 'success'
72107 runs-on : ubuntu-24.04
73108 permissions :
109+ contents : read
74110 pull-requests : write
75111 steps :
76- - name : Post preview links
112+ - uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
113+ with :
114+ sparse-checkout : .github/scripts
115+
116+ - uses : actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
117+ with :
118+ pattern : preview-*
119+ path : previews
120+
121+ - name : Update preview panel
77122 uses : actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
78123 env :
79124 SITES : ${{ needs.sites.outputs.list }}
80125 with :
81126 script : |
82- const pr = context.payload.pull_request;
83- const marker = '<!-- preview-links -->';
84- const sha = pr.head.sha.substring(0, 7);
85- // --branch=pr-N gives each project a stable alias for this PR.
86- const rows = JSON.parse(process.env.SITES).map(
87- (site) => `| ${site} | https://pr-${pr.number}.uq-rcc-${site}.pages.dev |`,
88- );
89- const body = [
90- marker,
91- `**Previews** for \`${sha}\``,
92- '',
93- '| Site | URL |',
94- '| --- | --- |',
95- ...rows,
96- ].join('\n');
97-
98- const { owner, repo } = context.repo;
99- const comments = await github.paginate(github.rest.issues.listComments, {
100- owner, repo, issue_number: pr.number,
127+ const fs = require('node:fs');
128+ const path = require('node:path');
129+ const panel = require('./.github/scripts/preview-panel.cjs');
130+
131+ const previews = JSON.parse(process.env.SITES).map((site) => {
132+ const file = path.join('previews', `preview-${site}`, 'preview.json');
133+ return fs.existsSync(file)
134+ ? JSON.parse(fs.readFileSync(file, 'utf8'))
135+ : { site, status: 'failure' };
101136 });
102- const existing = comments.find(
103- (c) => c.user?.type === 'Bot' && c.body?.startsWith(marker),
104- );
105- if (existing) {
106- await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
107- } else {
108- await github.rest.issues.createComment({ owner, repo, issue_number: pr.number, body });
109- }
137+
138+ const logs = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
139+ const sha = context.payload.pull_request.head.sha;
140+ await panel.update({ github, context }, panel.result(previews, { sha, logs }));
141+
142+ const failed = previews.filter((p) => p.status !== 'success');
143+ if (failed.length) core.setFailed(`Preview failed for ${failed.map((p) => p.site).join(', ')}`);
0 commit comments