Skip to content

Commit e1d1cb1

Browse files
cristianocclaude
andcommitted
Link a semantic diff page on each pull request
When a pull request is opened, reopened or pushed, the Semantic Diff workflow renders its commits with scripts/commitRecords.js, uploads the page to the CDN at semantic-diff/pr-<N>/index.html, and posts its link as a comment that replaces the previous one, so the newest link is the last comment on the pull request. The link carries the rendered head commit as a query string. Closing the pull request deletes the page. A pull request from a branch of this repository runs on pull_request, with its own workflow and renderer, from its first push and on any base branch. A pull request from a fork runs on pull_request_target. Security: pull_request_target takes the workflow from the default branch and runs it with the repository's secrets, so for forks the job runs only the default branch's code. It checks out the base commit without persisted credentials, fetches refs/pull/<N>/head as git objects, and renders them with the base branch's renderer, which reads git data and escapes all text it writes into the page. Each pull request runs on one of the two events: the jobs compare the event with the head repository, and the concurrency group names the event. Review: Why the CDN and not GitHub Pages? Pages here is one deployment from master (the dev playground) that each deploy replaces whole; the CDN already holds the playground's per-pull-request bundles under dev-playground-bundles/pr-<N>/. Area: ci docs Changelog: none Output: unchanged Tests: none, because the workflow runs only on GitHub; its render step is the renderer that scripts/commitRecords.test.js covers PR: #8870 Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent d219c92 commit e1d1cb1

2 files changed

Lines changed: 110 additions & 0 deletions

File tree

‎.claude/skills/records/SKILL.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,6 @@ node scripts/commitRecords.js --fragment --out part.html <rev>...
127127
the description of the work. A pull request description is a rendering of
128128
its range.
129129
- Before review or merge: render `master..<branch>` and hand over the page.
130+
- On GitHub, `.github/workflows/semantic_diff.yml` renders each pull
131+
request's range when it is opened or pushed, and comments the page's link
132+
(`Semantic diff: https://cdn.rescript-lang.org/semantic-diff/pr-<N>/…`).
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
name: Semantic Diff
2+
3+
# Renders the commit records of each pull request (scripts/commitRecords.js)
4+
# as a review page on the CDN and links it from the pull request.
5+
#
6+
# A pull request from a branch of this repository runs on pull_request, with
7+
# its own workflow and renderer. A pull request from a fork runs on
8+
# pull_request_target, which GitHub takes from the default branch and runs
9+
# with the repository's secrets: it executes only the default branch's code,
10+
# and the fork's commits are fetched as git data and never checked out.
11+
12+
on:
13+
pull_request:
14+
types: [opened, synchronize, reopened, closed]
15+
pull_request_target:
16+
types: [opened, synchronize, reopened, closed]
17+
18+
concurrency:
19+
group: semantic-diff-${{ github.event_name }}-${{ github.event.pull_request.number }}
20+
cancel-in-progress: true
21+
22+
jobs:
23+
render:
24+
if: >-
25+
github.event.action != 'closed' &&
26+
github.actor != 'dependabot[bot]' &&
27+
(github.event_name == 'pull_request') ==
28+
(github.event.pull_request.head.repo.full_name == github.repository)
29+
runs-on: ubuntu-24.04
30+
permissions:
31+
contents: read
32+
pull-requests: write
33+
env:
34+
PR_NUMBER: ${{ github.event.pull_request.number }}
35+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
36+
steps:
37+
- name: Checkout renderer
38+
uses: actions/checkout@v7
39+
with:
40+
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.event.pull_request.base.sha }}
41+
fetch-depth: 0
42+
filter: blob:none
43+
persist-credentials: false
44+
45+
- name: Render semantic diff
46+
id: render
47+
run: |
48+
git fetch --no-tags --filter=blob:none origin "refs/pull/${PR_NUMBER}/head:refs/heads/pr-${PR_NUMBER}"
49+
base=$(git merge-base "$BASE_SHA" "pr-${PR_NUMBER}")
50+
mkdir -p semantic-diff
51+
node scripts/commitRecords.js --out semantic-diff/index.html "${base}..pr-${PR_NUMBER}"
52+
echo "head=$(git rev-parse --short=9 "pr-${PR_NUMBER}")" >> "$GITHUB_OUTPUT"
53+
54+
- name: Setup Rclone
55+
uses: cometkim/rclone-actions/setup-rclone@main
56+
57+
- name: Configure Rclone remote
58+
uses: cometkim/rclone-actions/configure-remote/s3-provider@main
59+
with:
60+
name: rescript
61+
provider: Cloudflare
62+
endpoint: https://${{ vars.CLOUDFLARE_ACCOUNT_ID }}.r2.cloudflarestorage.com
63+
access-key-id: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }}
64+
secret-access-key: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }}
65+
acl: private
66+
67+
- name: Upload semantic diff
68+
run: rclone copyto --s3-no-check-bucket semantic-diff/index.html "rescript:cdn-assets/semantic-diff/pr-${PR_NUMBER}/index.html"
69+
70+
# The query string names the rendered commit, so each push gets a URL
71+
# that no cache has seen.
72+
- name: Comment semantic diff URL
73+
uses: thollander/actions-comment-pull-request@v3
74+
with:
75+
pr-number: ${{ github.event.pull_request.number }}
76+
comment-tag: semantic-diff
77+
# Each push replaces the comment, so the newest link is the last
78+
# comment on the pull request.
79+
mode: recreate
80+
message: |
81+
Semantic diff: https://cdn.rescript-lang.org/semantic-diff/pr-${{ github.event.pull_request.number }}/index.html?v=${{ steps.render.outputs.head }}
82+
83+
cleanup:
84+
if: >-
85+
github.event.action == 'closed' &&
86+
github.actor != 'dependabot[bot]' &&
87+
(github.event_name == 'pull_request') ==
88+
(github.event.pull_request.head.repo.full_name == github.repository)
89+
runs-on: ubuntu-24.04
90+
permissions: {}
91+
steps:
92+
- name: Setup Rclone
93+
uses: cometkim/rclone-actions/setup-rclone@main
94+
95+
- name: Configure Rclone remote
96+
uses: cometkim/rclone-actions/configure-remote/s3-provider@main
97+
with:
98+
name: rescript
99+
provider: Cloudflare
100+
endpoint: https://${{ vars.CLOUDFLARE_ACCOUNT_ID }}.r2.cloudflarestorage.com
101+
access-key-id: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }}
102+
secret-access-key: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }}
103+
acl: private
104+
105+
- name: Delete semantic diff
106+
continue-on-error: true
107+
run: rclone purge --s3-no-check-bucket "rescript:cdn-assets/semantic-diff/pr-${{ github.event.pull_request.number }}"

0 commit comments

Comments
 (0)