11name : Semantic Diff
22
33# 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.
4+ # as a review page on the CDN, and comments them on the pull request as
5+ # Markdown (scripts/recordMarkdown.js) with the page's link. The page holds
6+ # every commit; the comment holds the commits a push added, or every commit
7+ # when the pull request is opened or reopened or its branch is force-pushed.
58#
69# 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.
10+ # the workflow and renderer of its merge into the base branch: GitHub takes
11+ # the workflow from that merge, so the renderer comes from it too, and a
12+ # branch made before the workflow changed still gets matching scripts. A
13+ # pull request from a fork runs on pull_request_target, which GitHub takes
14+ # from the default branch and runs with the repository's secrets: it
15+ # executes only the default branch's code, and the fork's commits are
16+ # fetched as git data and never checked out.
17+ #
18+ # Pages are never deleted, so the links in the comments stay valid after a
19+ # pull request is merged or closed.
1120
1221on :
1322 pull_request :
14- types : [opened, synchronize, reopened, closed ]
23+ types : [opened, synchronize, reopened]
1524 pull_request_target :
16- types : [opened, synchronize, reopened, closed ]
25+ types : [opened, synchronize, reopened]
1726
1827concurrency :
1928 group : semantic-diff-${{ github.event_name }}-${{ github.event.pull_request.number }}
@@ -22,7 +31,6 @@ concurrency:
2231jobs :
2332 render :
2433 if : >-
25- github.event.action != 'closed' &&
2634 github.actor != 'dependabot[bot]' &&
2735 (github.event_name == 'pull_request') ==
2836 (github.event.pull_request.head.repo.full_name == github.repository)
@@ -34,11 +42,14 @@ jobs:
3442 PR_NUMBER : ${{ github.event.pull_request.number }}
3543 PR_TITLE : ${{ github.event.pull_request.title }}
3644 BASE_SHA : ${{ github.event.pull_request.base.sha }}
45+ # The head before a push; empty when the pull request is opened or
46+ # reopened.
47+ BEFORE_SHA : ${{ github.event.action == 'synchronize' && github.event.before || '' }}
3748 steps :
3849 - name : Checkout renderer
3950 uses : actions/checkout@v7
4051 with :
41- ref : ${{ github.event_name == 'pull_request' && github.event.pull_request.head. sha || github.event.pull_request.base.sha }}
52+ ref : ${{ github.event_name == 'pull_request' && github.sha || github.event.pull_request.base.sha }}
4253 fetch-depth : 0
4354 filter : blob:none
4455 persist-credentials : false
4859 run : |
4960 git fetch --no-tags --filter=blob:none origin "refs/pull/${PR_NUMBER}/head:refs/heads/pr-${PR_NUMBER}"
5061 base=$(git merge-base "$BASE_SHA" "pr-${PR_NUMBER}")
62+ head=$(git rev-parse --short=9 "pr-${PR_NUMBER}")
63+ # The query string names the rendered commit, so each push gets a
64+ # URL that no cache has seen.
65+ url="https://cdn.rescript-lang.org/semantic-diff/pr-${PR_NUMBER}/index.html?v=${head}"
66+ # A push that keeps the previous head adds commits on top of it:
67+ # the comment holds those. After a force push the previous head is
68+ # no ancestor (nor, from a fork, fetched), and the comment holds
69+ # every commit.
70+ since=()
71+ if [ -n "$BEFORE_SHA" ] && git merge-base --is-ancestor "$BEFORE_SHA" "pr-${PR_NUMBER}" 2>/dev/null; then
72+ since=(--since "$BEFORE_SHA")
73+ fi
5174 mkdir -p semantic-diff
52- node scripts/commitRecords.js --out semantic-diff/index.html --pr "$PR_NUMBER" --title "$PR_TITLE" "${base}..pr-${PR_NUMBER}"
53- echo "head=$(git rev-parse --short=9 "pr-${PR_NUMBER}") " >> "$GITHUB_OUTPUT"
75+ node scripts/commitRecords.js --out semantic-diff/index.html --markdown semantic-diff/comment.md "${since[@]}" --url "$url" -- pr "$PR_NUMBER" --title "$PR_TITLE" "${base}..pr-${PR_NUMBER}"
76+ echo "head=${head} " >> "$GITHUB_OUTPUT"
5477
5578 - name : Setup Rclone
5679 uses : cometkim/rclone-actions/setup-rclone@8d9c6b95341f7698609deee65c3d62af42893288 # main, 2025-06-18
@@ -68,41 +91,14 @@ jobs:
6891 - name : Upload semantic diff
6992 run : rclone copyto --s3-no-check-bucket semantic-diff/index.html "rescript:cdn-assets/semantic-diff/pr-${PR_NUMBER}/index.html"
7093
71- # The query string names the rendered commit, so each push gets a URL
72- # that no cache has seen.
73- - name : Comment semantic diff URL
94+ # A push that adds only merge commits writes no comment.
95+ - name : Comment semantic diff
96+ if : hashFiles(' semantic- diff/comment.md') != ''
7497 uses : thollander/actions-comment-pull-request@v3
7598 with :
7699 pr-number : ${{ github.event.pull_request.number }}
77- comment-tag : semantic-diff
78- # Each push replaces the comment, so the newest link is the last
79- # comment on the pull request.
80- mode : recreate
81- message : |
82- Semantic diff: https://cdn.rescript-lang.org/semantic-diff/pr-${{ github.event.pull_request.number }}/index.html?v=${{ steps.render.outputs.head }}
83-
84- cleanup :
85- if : >-
86- github.event.action == 'closed' &&
87- github.actor != 'dependabot[bot]' &&
88- (github.event_name == 'pull_request') ==
89- (github.event.pull_request.head.repo.full_name == github.repository)
90- runs-on : ubuntu-26.04
91- permissions : {}
92- steps :
93- - name : Setup Rclone
94- uses : cometkim/rclone-actions/setup-rclone@8d9c6b95341f7698609deee65c3d62af42893288 # main, 2025-06-18
95-
96- - name : Configure Rclone remote
97- uses : cometkim/rclone-actions/configure-remote/s3-provider@8d9c6b95341f7698609deee65c3d62af42893288 # main, 2025-06-18
98- with :
99- name : rescript
100- provider : Cloudflare
101- endpoint : https://${{ vars.CLOUDFLARE_ACCOUNT_ID }}.r2.cloudflarestorage.com
102- access-key-id : ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }}
103- secret-access-key : ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }}
104- acl : private
105-
106- - name : Delete semantic diff
107- continue-on-error : true
108- run : rclone purge --s3-no-check-bucket "rescript:cdn-assets/semantic-diff/pr-${{ github.event.pull_request.number }}"
100+ # Each push gets its own comment, so the comments of earlier pushes
101+ # stay; a re-run of the same head updates its comment.
102+ comment-tag : semantic-diff-${{ steps.render.outputs.head }}
103+ mode : upsert
104+ file-path : semantic-diff/comment.md
0 commit comments