Skip to content

Commit 9b60d92

Browse files
Merge master into fix/versioned-api-docs
Bring the API documentation routing PR up to date with master and its fix for matching the Semantic Diff workflow to the renderer. Preserve the CI branch filters applied through the GitHub web UI. Signed-off-by: Florian Hammerschmidt <florianh89@gmail.com>
2 parents 80a979e + c9a267c commit 9b60d92

275 files changed

Lines changed: 5089 additions & 1509 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,31 @@ Breaking: <what existing code or tooling stops working, and the migration>
4949
Signed-off-by: Name <email>
5050
```
5151

52-
- The subject and `Signed-off-by` are required. The message ends with its
53-
trailers, git's `Key: value` lines (`Co-Authored-By:`, `Fixes:` or any
54-
other one-word key), `Signed-off-by` last; the page lists the
55-
`Co-Authored-By` names and draws no other trailer. Every section is
56-
optional.
57-
- A section starts at a line `Why:`, `Review:`, `Breaking:`, an object
58-
block's heading, an `Example:` alone on its line, or a `Before (<form>):`
59-
or `After (<form>):` heading, and holds the text after its colon and the
60-
block indented by 4 spaces or more under it; a blank line ends a section
61-
that holds only text. `Review:` repeats, one question each. Every other
52+
A commit without object blocks, such as a script change, is its subject
53+
and a line or two of prose:
54+
55+
```
56+
Draw handles starting with a digit as code in the comment
57+
58+
GitHub usernames may start with a digit, so `@0x` in a commit message
59+
would have mentioned a user; the comment now draws it as code as well.
60+
61+
Signed-off-by: Name <email>
62+
```
63+
64+
- The subject, a description and `Signed-off-by` are required: the
65+
description says why the commit is made, as a line or two of prose or
66+
a `Why:` section, and object blocks then show what it changes. Every
67+
other section is optional. The message ends with its trailers, git's
68+
`Key: value` lines (`Co-Authored-By:`, `Fixes:` or any other one-word
69+
key), `Signed-off-by` last; the page lists the `Co-Authored-By` names
70+
and draws no other trailer.
71+
- A section starts at a line `Why:`, `Review:` or `Breaking:` that no
72+
paragraph of prose runs onto (a blank line or another section above it),
73+
an object block's heading, an `Example:` alone on its line, or a
74+
`Before (<form>):` or `After (<form>):` heading, and holds the text after
75+
its colon and the block indented by 4 spaces or more under it; a blank
76+
line ends a section that holds only text. `Review:` repeats, one question each. Every other
6277
line is prose, and a block indented by 4 spaces outside a section is
6378
ReScript code. Tabs count as 4 columns.
6479
- An **object block** is a heading `<Kind> added|modified|removed (<object>):`,
@@ -159,11 +174,13 @@ The table lists the common kinds.
159174
`-dce` and `-termination` select the other analyses.
160175
- A form of another name is drawn as text.
161176

162-
## When to add a block
177+
## What a commit holds
163178

164-
A commit gets one object block for each object it changes, with the
179+
Every commit says why it is made. A commit that changes what the compiler
180+
or a tool does gets one object block for each object it changes, with the
165181
smallest example that shows the change. A refactor, a test, a script or a
166-
review fix is a subject and a line or two of prose.
182+
review fix needs no block, only its subject and a line or two of prose; a
183+
review fix names the finding it answers.
167184

168185
## Rendering
169186

@@ -175,14 +192,16 @@ node scripts/commitRecords.js --out page.html [--pr <number>] [--title <text>] \
175192

176193
- `--pr` and `--title` head the page with the pull request.
177194
`.github/workflows/semantic_diff.yml` renders each pull request's range
178-
and comments the page's link.
195+
and comments the records as Markdown (`--markdown <file> --url <page>`,
196+
scripts/recordMarkdown.js) with the page's link: after a push that keeps
197+
the previous head, only the commits it added (`--since <previous head>`).
179198
- In prose, `#1234` and full pull request and issue URLs link as `#1234`,
180199
and the 9-character short sha of a commit on the page links to its card.
181200

182201
## What the page shows
183202

184203
The page is the heading, then a card per commit. A card shows the commit's
185-
short sha, author and date, the pull request a `(#1234)` at the end of the
186-
subject names, and the message drawn: the subject, the prose, each object
187-
block with its example and forms, the `Breaking:` sections as banners, the
188-
`Review:` sections as a list, and the co-authors.
204+
short sha, author and co-authors, date, the pull request a `(#1234)` at
205+
the end of the subject names, and the message drawn: the subject, the
206+
prose, each object block with its example and forms, the `Breaking:`
207+
sections as banners, and the `Review:` sections as a list.

‎.github/workflows/ci.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ jobs:
5151
build_playground: true
5252
generate_api_docs: true
5353
benchmarks: true
54+
test_commit_records: true
5455
node-target: linux-arm64
5556
rust-target: aarch64-unknown-linux-musl
5657
dune-profile: static
@@ -194,6 +195,10 @@ jobs:
194195
- name: Test release plan
195196
run: yarn test-release-plan
196197

198+
- name: Test commit records
199+
if: matrix.test_commit_records
200+
run: yarn test-commit-records
201+
197202
- name: Run tests
198203
run: node scripts/test.js -all
199204

Lines changed: 42 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
11
name: 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

1221
on:
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

1827
concurrency:
1928
group: semantic-diff-${{ github.event_name }}-${{ github.event.pull_request.number }}
@@ -22,7 +31,6 @@ concurrency:
2231
jobs:
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
@@ -48,9 +59,21 @@ jobs:
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

‎AGENTS.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ on `master`. CI runs on every PR in the stack.
105105
For compiler or standard-library changes, run `make test`. For syntax changes,
106106
also run `make test-syntax`; use `make test-syntax-roundtrip` when parsing or
107107
printing changes. Other focused suites are `make test-gentype`,
108-
`make test-analysis`, `make test-tools`, and `make test-rewatch`.
108+
`make test-analysis`, `make test-tools`, `make test-rewatch` (OCaml build
109+
system), and `make test-rewatch-rust` (Rust build system).
109110

110111
### Testing Requirements
111112

‎Makefile‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ test-gentype: lib
188188
test-rewatch: lib
189189
./rewatch/tests/suite.sh $(RESCRIPT_EXE)
190190

191+
test-rewatch-rust: lib rewatch
192+
./rewatch/tests/suite.sh $(RESCRIPT_RUST_EXE)
193+
191194
test-all: test test-gentype test-analysis test-tools test-rewatch
192195

193196
# Playground
@@ -382,4 +385,4 @@ clean: clean-lib clean-compiler clean-rewatch clean-coverage
382385

383386
.DEFAULT_GOAL := build
384387

385-
.PHONY: yarn-install build rewatch compiler lib artifacts bench test test-analysis test-reanalyze benchmark-reanalyze test-tools test-syntax test-syntax-roundtrip test-gentype test-rewatch test-all playground playground-compiler playground-test playground-cmijs playground-release format checkformat semantic-diff clean-rewatch clean-compiler clean-lib clean-gentype clean-tests clean
388+
.PHONY: yarn-install build rewatch compiler lib artifacts bench test test-analysis test-reanalyze benchmark-reanalyze test-tools test-syntax test-syntax-roundtrip test-gentype test-rewatch test-rewatch-rust test-all playground playground-compiler playground-test playground-cmijs playground-release format checkformat semantic-diff clean-rewatch clean-compiler clean-lib clean-gentype clean-tests clean

‎analysis/src/process_attributes.ml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ open Shared_types
44
let rec find_doc_attribute attributes =
55
match attributes with
66
| [] -> None
7-
| ({Asttypes.txt = "ocaml.doc" | "ns.doc" | "res.doc"}, payload) :: rest -> (
7+
| ({Asttypes.txt = "res.doc"}, payload) :: rest -> (
88
match Ast_payload.semantic_string_of_payload payload with
99
| Some doc -> Some doc
1010
| None -> find_doc_attribute rest)

‎analysis/src/type_utils.ml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,8 @@ let rec extract_type ?(print_opening_debug = true)
476476
{
477477
name = label;
478478
display_name =
479-
Utils.print_maybe_exotic_ident ~allow_uident:true label;
479+
Res_printer.print_poly_var_ident label
480+
|> Res_doc.to_string ~width:80;
480481
args =
481482
(* Multiple arguments are represented as a Ttuple, while a single argument is just the type expression itself. *)
482483
(match field with

‎analysis/src/utils.ml‎

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -242,29 +242,6 @@ let rec flatten_any_namespace_in_path path =
242242
(parts |> List.rev) @ flatten_any_namespace_in_path tail
243243
else head :: flatten_any_namespace_in_path tail
244244

245-
let print_maybe_exotic_ident ?(allow_uident = false) txt =
246-
let len = String.length txt in
247-
248-
let rec loop i =
249-
if i == len then txt
250-
else if i == 0 then
251-
match String.unsafe_get txt i with
252-
| 'A' .. 'Z' when allow_uident -> loop (i + 1)
253-
| 'a' .. 'z' | '$' -> loop (i + 1)
254-
(* Same as Res_printer.classify_ident_content: a lone [_] or one
255-
followed by another character is not scanned as an identifier. *)
256-
| '_' when len > 1 -> (
257-
match String.unsafe_get txt 1 with
258-
| 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' | '_' | '$' -> loop (i + 1)
259-
| _ -> "\"" ^ txt ^ "\"")
260-
| _ -> "\"" ^ txt ^ "\""
261-
else
262-
match String.unsafe_get txt i with
263-
| 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' | '\'' | '_' | '$' -> loop (i + 1)
264-
| _ -> "\"" ^ txt ^ "\""
265-
in
266-
if Res_token.is_keyword_txt txt then "\"" ^ txt ^ "\"" else loop 0
267-
268245
let find_package_json root =
269246
let path = Uri.to_path root in
270247

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Reject `@as` literals of one variant that are the same JavaScript value, such as `@as(1)` and `@as(1.0)`, and accept `@as(1)` beside `@as(1n)`.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- List floats and bigints in the error for an invalid `@as` payload, since `@as` accepts them.

0 commit comments

Comments
 (0)