Repository navigation
1463 lines (1430 loc) · 70.9 KB
/
Copy pathci.yml
File metadata and controls
1463 lines (1430 loc) · 70.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
name: CI
on:
push:
branches: [main]
pull_request:
# A merge queue group, once the repository turns the queue on: the tree that would become main, tested before
# it does. It runs what a push to main runs. Nothing starts such a run until then.
merge_group:
# Nightly full macOS sweep. Pushes to main run no macOS jobs, and pull requests run them only when they change
# darwin code (scripts/ci-lanes.mjs).
schedule:
- cron: "17 7 * * *"
# A manual run, for example on a release candidate commit. It runs every lane; macos_suite selects the complete
# suite on macOS, as the nightly sweep runs it, or the darwin suite that pull requests run.
workflow_dispatch:
inputs:
macos_suite:
description: The test suite to run on macOS
type: choice
options: [full, darwin]
default: full
scope:
description: Every lane, or only the shape overlay validation (shape_overlay)
type: choice
options: [all, shape_overlay]
default: all
concurrency:
# A new push to a pull request cancels its previous run. Every push to main, and every merge queue group, runs
# to completion in a group of its own commit, so each merged commit gets a result (releases require a green run
# on their commit). The nightly sweep and manual runs neither cancel nor are cancelled by main pushes.
group: ci-${{ github.event_name == 'schedule' && 'schedule' || github.event_name == 'workflow_dispatch' && format('dispatch-{0}', github.run_id) || github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
# The token every job gets unless it declares its own: it can read the repository and nothing else. No job here
# writes to the repository, comments on a pull request or requests an OIDC token; caches and artifacts do not use
# this token.
permissions:
contents: read
jobs:
# GitHub recommends stack.base.ref == base.ref for the lowest unmerged PR:
# https://docs.github.com/en/pull-requests/how-tos/merge-and-close-pull-requests/optimizing-ci-for-stacked-pull-requests
# Only trusted, same-repository upper layers defer full CI. Forks, ordinary
# PRs, pushes and incomplete metadata always retain full validation.
validation_scope:
name: Select CI scope
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions: {}
outputs:
full: ${{ steps.scope.outputs.full }}
steps:
- id: scope
# Read GitHub's event directly; do not run scripts from a PR checkout.
run: |
node <<'NODE'
const { appendFileSync, readFileSync } = require('node:fs');
const event = JSON.parse(readFileSync(process.env.GITHUB_EVENT_PATH, 'utf8'));
const pr = event.pull_request;
const repository = event.repository?.full_name;
const sameRepository = typeof repository === 'string' && repository.length > 0 &&
pr?.head?.repo?.full_name === repository && pr?.base?.repo?.full_name === repository;
const trustedAuthor = ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(pr?.author_association);
const stackBase = pr?.stack?.base?.ref;
const prBase = pr?.base?.ref;
// Position 1 can already be merged; compare the current base branches.
// A PR into the default branch is the lowest unmerged layer of any
// stack, whatever the stack metadata names as its base.
const defaultBranch = event.repository?.default_branch;
const upperLayer = process.env.GITHUB_EVENT_NAME === 'pull_request' &&
sameRepository && trustedAuthor && typeof stackBase === 'string' && stackBase.length > 0 &&
typeof prBase === 'string' && prBase.length > 0 && stackBase !== prBase &&
prBase !== defaultBranch;
// A manual run of the shape overlay validation alone skips the other lanes.
const shapeOnly = process.env.GITHUB_EVENT_NAME === 'workflow_dispatch' &&
event.inputs?.scope === 'shape_overlay';
const full = !upperLayer && !shapeOnly;
appendFileSync(process.env.GITHUB_OUTPUT, `full=${full}\n`);
console.log(full ? 'Run full CI' : 'Defer full CI until this PR becomes the lowest unmerged layer');
NODE
# Which expensive lanes a pull request needs (scripts/ci-lanes.mjs, run from the base commit against the pull
# request's diff, so a pull request cannot change its own selection). Pushes to main and merge queue groups run
# every lane except macos, whose jobs the nightly sweep runs on main. The nightly sweep and manual runs run every
# lane.
lanes:
needs: validation_scope
if: ${{ needs.validation_scope.outputs.full == 'true' }}
name: Select CI lanes
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
outputs:
# A pull request runs the base revision's lane selector, and one that
# predates a lane emits nothing for it; that lane is off then, so its job
# is skipped and the required check accepts the skip.
linux: ${{ steps.lanes.outputs.linux || 'false' }}
macos: ${{ steps.lanes.outputs.macos || 'false' }}
effects: ${{ steps.lanes.outputs.effects || 'false' }}
bootstrap: ${{ steps.lanes.outputs.bootstrap || 'false' }}
profile: ${{ steps.lanes.outputs.profile || 'false' }}
islands: ${{ steps.lanes.outputs.islands || 'false' }}
# The shards of the effects lane to run: all sixteen, or the one that a pull request changing compiler or
# runtime sources samples (scripts/ci-lanes.mjs).
effect_shards: ${{ steps.lanes.outputs.effect_shards || '[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]' }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
if: github.event_name == 'pull_request'
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- id: lanes
env:
EVENT: ${{ github.event_name }}
BASE: ${{ github.event.pull_request.base.sha }}
HEAD: ${{ github.event.pull_request.head.sha }}
PR: ${{ github.event.pull_request.number }}
run: |
every() { printf 'linux=true\nmacos=true\neffects=true\nbootstrap=true\nprofile=true\nislands=true\n' | tee -a "$GITHUB_OUTPUT"; }
if [ "$EVENT" = push ] || [ "$EVENT" = merge_group ]; then
printf 'linux=true\nmacos=false\neffects=true\nbootstrap=true\nprofile=true\nislands=true\n' | tee -a "$GITHUB_OUTPUT"
elif [ "$EVENT" != pull_request ]; then
every
elif git show "$BASE:scripts/ci-lanes.mjs" > "$RUNNER_TEMP/ci-lanes.mjs" 2> /dev/null; then
node "$RUNNER_TEMP/ci-lanes.mjs" --base "$BASE" --head "$HEAD" --pr "$PR" --github-output
else
echo "The base commit has no scripts/ci-lanes.mjs: every lane"
every
fi
code_quality:
name: lint and format
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm format:check
- name: Code quality and CI policy contracts
run: pnpm test tests/harness/code-quality.test.ts tests/harness/ci-stack-scope.test.ts
env:
SCRIPTC_TEST_WORKERS: "1"
- name: Verify mixed-source generated AST getters
run: pnpm generate:ts7-ast --check
- name: Verify the generated IR field readers
run: pnpm generate:ir-fields --check
- name: Verify the generated IR codec
run: pnpm generate:ir-codec --check
native_macos:
needs: [validation_scope, lanes]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.macos == 'true' }}
name: build (macOS arm64 native packages)
runs-on: macos-15
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- run: pnpm install --frozen-lockfile
- uses: ./.github/actions/native-packages
with:
target: darwin-arm64
- name: Preserve executable permissions in shared packages
run: >-
tar -czf "$RUNNER_TEMP/native-darwin-arm64.tar.gz"
packages/llvm-darwin-arm64/bin
packages/runtime-darwin-arm64/artifacts
packages/runtime-darwin-arm64/runtime-pack.json
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: native-darwin-arm64
path: ${{ runner.temp }}/native-darwin-arm64.tar.gz
retention-days: 1
tests:
needs: [validation_scope, lanes, native_linux]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.linux == 'true' }}
name: test (Linux, ${{ matrix.flavor }}, ${{ matrix.shard }}/20)
# The full suite runs on Linux x64, whose hosted runners start without the
# macOS queue. tests_macos keeps the darwin clang/libc oracles.
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
flavor: [plain, san]
# Twenty shards keep each job near six minutes of tests (the corpus
# programs are balanced by measured cost, tests/harness/shard.ts).
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
env:
# Capped workers: the shared runner's cores oversubscribe under the
# corpus compile fan-out, and vitest's worker RPC times out ("Timeout
# calling onTaskUpdate") with every test green — the same contention
# artifact the harness README documents for local parallel suites.
SCRIPTC_TEST_WORKERS: "4"
# san = ASan + RC audit over the same suite; anything but "1" is plain.
SCRIPTC_SAN: ${{ matrix.flavor == 'san' && '1' || '' }}
# Match macOS, where Apple ASan has no LeakSanitizer: the RC audit owns
# leak detection, including intentional abandonment. Plain shards also
# build some sanitized probes, so this applies to both flavors. The
# san shards' case-sharded corpus turns LeakSanitizer on
# (scripts/ci-test-plan.mjs, tests/harness/lsan.supp).
ASAN_OPTIONS: detect_leaks=0
# Within-file partition for the large case-driven harnesses
# (differential, llvm-differential, npm, server, Test262, coverage, and
# Vercel E2E): partition stable case names into slice i of 20
# (tests/harness/shard.ts). The twenty shards' unions run every case
# exactly once. vitest's own --shard is FILE-granular and cannot split
# these monster files (differential.test.ts alone is ~9 min
# sequential), hence the case-sharded steps below.
SCRIPTC_TEST_SHARD: ${{ matrix.shard }}/20
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- uses: ./.github/actions/llvm-linux
- run: pnpm install --frozen-lockfile
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: native-linux-x64-gnu
path: ${{ runner.temp }}
- run: tar -xzf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
- uses: ./.github/actions/icu-downloads
- run: pnpm build
# Node island tests (`// @requires-libnode` corpus programs and
# node-island.test.ts) load Node from a libnode pack, and fail rather
# than skip in this lane when none is configured.
- uses: ./.github/actions/libnode
# Ubuntu pipes core dumps to apport, and RLIMIT_CORE does not limit a
# piped dump. The kernel then streams every page of the runtime's
# reserved allocator address space through the pipe, so each expected
# abort outlives its test timeout. Write cores to files instead, where
# the limit below applies.
- name: Disable piped core dumps
run: sudo sysctl -w kernel.core_pattern=core
# Start the corpus immediately beside a serial side queue. One shared
# worker budget covers both processes; file and case shard axes stay
# separate so every fixture still runs exactly once in each lane.
# The tests run in a transient scope capped below the runner's 16 GB, so
# the runner itself keeps memory: past 15 GB of RAM the scope swaps, as
# the whole runner did before, and when RAM and swap run out the kernel
# kills the scope's largest process. A sanitized shard peaks near 16 GB,
# and the sanitized self-hosting native-frontend build alone needs 15 GB
# (its clang compile of the frontend takes 10.5 GB).
# OOMPolicy=continue leaves the rest of the scope running, so vitest
# reports the killed worker and the job log names the test. Node sizes
# its default heap from the memory it may use, which the scope lowers
# below 4 GB; the workers keep the 4 GB heap they had on the bare
# runner.
- name: Tests (corpus and integration suites)
run: |
ulimit -c 0
if [ -d /run/systemd/system ]; then
exec sudo --preserve-env systemd-run --scope --quiet -p MemoryMax=15G -p OOMPolicy=continue \
--uid="$(id -u)" --gid="$(id -g)" -- env PATH="$PATH" HOME="$HOME" NODE_OPTIONS="--max-old-space-size=4096" node scripts/ci-test.mjs
fi
node scripts/ci-test.mjs
tests_macos:
needs: [validation_scope, lanes, native_macos]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.macos == 'true' }}
name: test (macOS, ${{ matrix.flavor }}, ${{ (github.event_name != 'schedule' && inputs.macos_suite != 'full') && 'darwin ' || '' }}${{ matrix.shard }}/${{ (github.event_name != 'schedule' && inputs.macos_suite != 'full') && '3' || '10' }})
# arm64 macOS: same clang/libc the differential + number-formatting
# oracles were verified against. Pull requests that change darwin code and
# manual runs with macos_suite set to darwin run the darwin suite
# (scripts/ci-test-plan.mjs) in three slices; the nightly schedule and
# other manual runs run the complete suite in ten. The complete suite on
# every push needed more macOS runners than the repository has.
runs-on: macos-15
strategy:
fail-fast: false
matrix:
flavor: [plain, san]
shard: ${{ fromJSON((github.event_name != 'schedule' && inputs.macos_suite != 'full') && '[1, 2, 3]' || '[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]') }}
env:
# See the Linux tests job for the worker cap and case partitioning.
SCRIPTC_TEST_WORKERS: "4"
SCRIPTC_SAN: ${{ matrix.flavor == 'san' && '1' || '' }}
SCRIPTC_TEST_SHARD: ${{ matrix.shard }}/${{ (github.event_name != 'schedule' && inputs.macos_suite != 'full') && '3' || '10' }}
SCRIPTC_CI_SUITE: ${{ (github.event_name != 'schedule' && inputs.macos_suite != 'full') && 'darwin' || 'full' }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- run: pnpm install --frozen-lockfile
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: native-darwin-arm64
path: ${{ runner.temp }}
- run: tar -xzf "$RUNNER_TEMP/native-darwin-arm64.tar.gz"
- uses: ./.github/actions/icu-downloads
- run: pnpm build
# Node island tests load Node from a libnode pack, and fail rather
# than skip on macOS CI when none is configured.
- uses: ./.github/actions/libnode
- name: Tests (${{ (github.event_name != 'schedule' && inputs.macos_suite != 'full') && 'darwin suite' || 'corpus and integration suites' }})
run: node scripts/ci-test.mjs
native_linux:
needs: [validation_scope, lanes]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.linux == 'true' }}
name: build (Linux x64 native packages)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- uses: ./.github/actions/llvm-linux
- run: pnpm install --frozen-lockfile
- uses: ./.github/actions/zig
- uses: ./.github/actions/native-packages
with:
target: linux-x64-gnu
- name: Preserve executable permissions in shared packages
run: >-
tar -czf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
packages/llvm-linux-x64-gnu/bin
packages/runtime-linux-x64-gnu/artifacts
packages/runtime-linux-x64-gnu/runtime-pack.json
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: native-linux-x64-gnu
path: ${{ runner.temp }}/native-linux-x64-gnu.tar.gz
retention-days: 1
effects:
needs: [validation_scope, lanes, native_linux]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.effects == 'true' }}
name: test (Linux, ${{ matrix.flavor }}, Effect ${{ matrix.shard }}/16)
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
flavor: [plain, san]
# All sixteen, or the one shard a sampled pull request runs.
shard: ${{ fromJSON(needs.lanes.outputs.effect_shards) }}
env:
# One compiler at a time leaves room for its 8 GiB heap, checker and
# LLVM on a 16 GB runner. Parallelism comes from independent shards.
# Both package versions retain every Node/native comparison in both
# flavors; Effect 3 plain also retains its release optimizer coverage.
SCRIPTC_TEST_WORKERS: "1"
SCRIPTC_EFFECT_TEST_CONCURRENCY: "1"
SCRIPTC_TEST_SHARD: ${{ matrix.shard }}/16
SCRIPTC_SAN: ${{ matrix.flavor == 'san' && '1' || '' }}
ASAN_OPTIONS: detect_leaks=0
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- run: pnpm install --frozen-lockfile
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: native-linux-x64-gnu
path: ${{ runner.temp }}
- run: tar -xzf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
- run: pnpm test tests/harness/effect.test.ts tests/harness/effect4.test.ts
# Node island conformance (tests/harness/island-conformance.test.ts): every
# package program under Node, as its default build, and with each package
# forced into a Node island. It builds each program several times, so it
# runs in a pull request only when the change touches island code or the
# programs it runs (scripts/ci-lanes.mjs). It also runs on every push to
# main, in the nightly sweep and in manual runs: its list pins what a
# program prints, and a change outside the island paths (the wording of a
# refusal, the uncaught report) moves entries without running the lane in
# its own pull request. Linux x64 only: known-failures.json pins that
# platform's results.
island_conformance:
needs: [validation_scope, lanes, native_linux]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.islands == 'true' }}
name: test (Linux, Node island conformance ${{ matrix.shard }}/4)
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
env:
SCRIPTC_ISLAND_CONFORMANCE: "1"
SCRIPTC_TEST_WORKERS: "1"
SCRIPTC_TEST_SHARD: ${{ matrix.shard }}/4
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- run: pnpm install --frozen-lockfile
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: native-linux-x64-gnu
path: ${{ runner.temp }}
- run: tar -xzf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
# Every program of the lane loads Node from a libnode pack; the lane
# fails rather than skips when none is configured.
- uses: ./.github/actions/libnode
- run: pnpm test tests/harness/island-conformance.test.ts
# The sanitized three.js nodes build alone: beside the san corpus's other
# workers it took 860 to more than 1200 seconds of its shard
# (SCRIPTC_THREE_SANITIZED_NODES_JOB in scripts/ci-test-plan.mjs). It keeps
# the corpus's LeakSanitizer settings.
three_nodes_san:
needs: [validation_scope, lanes, native_linux]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.linux == 'true' }}
name: test (Linux, san, three.js nodes)
runs-on: ubuntu-24.04
timeout-minutes: 40
env:
SCRIPTC_SAN: "1"
ASAN_OPTIONS: detect_leaks=1
LSAN_OPTIONS: suppressions=${{ github.workspace }}/tests/harness/lsan.supp:print_suppressions=0
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- run: pnpm install --frozen-lockfile
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: native-linux-x64-gnu
path: ${{ runner.temp }}
- run: tar -xzf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
- run: pnpm test tests/harness/three.test.ts -t "'nodes' runs statically through LLVM on 'native'"
# The sanitized self-hosting native-frontend builds alone. Each of these
# three files compiles the whole frontend under ASan in beforeAll: the
# compile in Node holds 6.5 to 7.4 GB while clang builds the executable,
# one clang per core at a time, the largest of them 5.1 GB. Beside a
# shard's corpus workers the kernel killed it intermittently at 670-740 s
# (SIGKILL, empty stderr, every test skipped), so: one file per job, one
# worker, no corpus. On the runner's four cores the build alone has since
# outgrown its 16 GB (the kernel kills the compile at 560 s; a hosted runner
# goes down itself first, and the step ends cancelled about ten minutes in,
# with no test output), so the step runs on two cores: two clangs at a
# time, 14.5 GB at the peak. The plain suite and the macOS shards keep these
# files in the shard side queue (scripts/ci-test-plan.mjs).
native_frontend_san:
needs: [validation_scope, lanes, native_linux]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.linux == 'true' }}
name: test (Linux, san, native frontend ${{ matrix.suite }})
runs-on: ubuntu-24.04
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
include:
- suite: programs
file: tests/harness/self-hosting-native-frontend-programs.test.ts
- suite: checks
file: tests/harness/self-hosting-native-frontend-checks.test.ts
- suite: frontend
file: tests/harness/self-hosting-native-frontend.test.ts
env:
SCRIPTC_SAN: "1"
SCRIPTC_TEST_WORKERS: "1"
ASAN_OPTIONS: detect_leaks=0
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- uses: ./.github/actions/llvm-linux
- run: pnpm install --frozen-lockfile
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: native-linux-x64-gnu
path: ${{ runner.temp }}
- run: tar -xzf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
- name: Tests (${{ matrix.file }})
run: taskset --cpu-list 0,1 pnpm test ${{ matrix.file }}
# Shape inference's overlay (opt-in, SCRIPTC_SHAPE_OVERLAY=1) over the
# suites of real packages: nightly and on manual runs only (scope
# shape_overlay runs it alone), outside the `test` roll-up. Every case
# must build and run as in the default build. It builds its own native
# packages, so it does not depend on the other lanes.
shape_overlay_native:
if: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
name: build (Linux x64 native packages, shape overlay)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- uses: ./.github/actions/llvm-linux
- run: pnpm install --frozen-lockfile
- uses: ./.github/actions/zig
- uses: ./.github/actions/native-packages
with:
target: linux-x64-gnu
- name: Preserve executable permissions in shared packages
run: >-
tar -czf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
packages/llvm-linux-x64-gnu/bin
packages/runtime-linux-x64-gnu/artifacts
packages/runtime-linux-x64-gnu/runtime-pack.json
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: native-linux-x64-gnu-shape-overlay
path: ${{ runner.temp }}/native-linux-x64-gnu.tar.gz
retention-days: 1
shape_overlay:
needs: [shape_overlay_native]
if: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
name: test (Linux, shape overlay, ${{ matrix.suite }} ${{ matrix.shard }}/8)
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
suite: [npm, npm-static, three, effect]
shard: [1, 2, 3, 4, 5, 6, 7, 8]
env:
SCRIPTC_SHAPE_OVERLAY: "1"
SCRIPTC_TEST_WORKERS: "1"
SCRIPTC_EFFECT_TEST_CONCURRENCY: "1"
SCRIPTC_TEST_SHARD: ${{ matrix.shard }}/8
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- run: pnpm install --frozen-lockfile
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: native-linux-x64-gnu-shape-overlay
path: ${{ runner.temp }}
- run: tar -xzf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
# The npm suites' Node island cases load Node from a libnode pack, and
# fail rather than skip in this lane when none is configured.
- uses: ./.github/actions/libnode
if: matrix.suite == 'npm' || matrix.suite == 'npm-static'
- name: Run the suite with the overlay on
run: |
case "${{ matrix.suite }}" in
npm) pnpm test tests/harness/npm-shape-overlay.test.ts ;;
npm-static) pnpm test tests/harness/npm-static.test.ts ;;
three) pnpm test tests/harness/three.test.ts ;;
effect) pnpm test tests/harness/effect.test.ts tests/harness/effect4.test.ts ;;
esac
# Build each instrumented seed once. Downstream contracts retain the same
# compiler and source artifacts without repeating seed construction.
bootstrap_seed:
needs: [validation_scope, lanes]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.bootstrap == 'true' }}
name: build (Linux, ${{ matrix.flavor }}, bootstrap seed)
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
flavor: [plain, san]
env:
SCRIPTC_SAN: ${{ matrix.flavor == 'san' && '1' || '' }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- uses: ./.github/actions/llvm-linux
- run: pnpm install --frozen-lockfile
- uses: ./.github/actions/zig
- uses: ./.github/actions/native-packages
with:
target: linux-x64-gnu
- uses: ./.github/actions/native-packages
with:
target: wasm32-wasi
helper: "false"
- run: pnpm build
# The sanitized seed compiles the compiler's 700 MB module with one clang
# process under AddressSanitizer. It peaks near 14 GB while the Node
# process that emitted the module still holds about 6 GB. Together they
# exceed the runner's 16 GB of memory, so back them with swap.
- name: Enlarge swap for the sanitized seed
if: matrix.flavor == 'san'
run: |
sudo swapoff -a
sudo rm -f /mnt/swapfile
sudo fallocate -l 24G /mnt/swapfile
sudo chmod 600 /mnt/swapfile
sudo mkswap /mnt/swapfile
sudo swapon /mnt/swapfile
free -h
- name: Prepare bootstrap seed
run: node scripts/bootstrap-seed.mjs
env:
SCRIPTC_BOOTSTRAP_SEED_DIRECTORY: ${{ runner.temp }}/native-bootstrap
- name: Archive seed and compiler artifacts
run: tar -C "$RUNNER_TEMP" -cf "$RUNNER_TEMP/bootstrap-seed.tar" native-bootstrap
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: bootstrap-seed-${{ matrix.flavor }}-${{ github.sha }}
path: ${{ runner.temp }}/bootstrap-seed.tar
retention-days: 1
if-no-files-found: error
# Optimized builds of the native compiler use the committed profile and
# layout. The share of the profile's functions a build matches falls as
# compiler code changes, and a stale profile leaves the compiler's cold code
# inline (11 MB more text at 85.4% than with a fresh one). This job reports
# that share on the run page and below 95% posts a warning
# (scripts/build-native-cli-profile.mjs); a stale profile does not fail it,
# because the nightly profile refresh (.github/workflows/profile-refresh.yml)
# moves the profile forward. It fails only when the profile cannot be judged:
# missing, unreadable, or a build laid out differently from the instrumented
# build. The seed above builds without the layout, so this build keeps the
# committed one, as local builds do. It runs on the profile lane, which
# follows compiler code, code generation and the native CLI build, where the
# profile goes stale.
#
# The same build is held to the self-compile budget
# (scripts/self-compile-budget.mjs): the whole-program lowering passes the
# compiler takes over its own source, exactly, and the heap its frontend
# holds at the end of a pass against the budget and the heap limit. One
# construct in the compiler's source can make it lower itself again (#2918
# added two passes and 65% of the build's time), and this is the only build
# of the compiler's own source that every compiler change runs. Compile time
# itself is judged by the zero-cost gate, which has a baseline build to pair
# it with.
native_profile:
needs: [validation_scope, lanes]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.profile == 'true' }}
name: check (native compiler profile)
runs-on: ubuntu-24.04
# The compiler build takes about as long as the seed's on a hosted runner.
timeout-minutes: 90
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- uses: ./.github/actions/llvm-linux
- run: pnpm install --frozen-lockfile
- uses: ./.github/actions/zig
- uses: ./.github/actions/native-packages
with:
target: linux-x64-gnu
- run: pnpm build
- name: Build the native compiler with the committed profile, within the self-compile budget
run: node scripts/self-compile-budget.mjs --native-build
env:
SCRIPTC_NATIVE_PROFILE_CHECK: "1"
bootstrap:
needs: [validation_scope, lanes, bootstrap_seed]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.bootstrap == 'true' }}
name: test (Linux, ${{ matrix.flavor }}, native bootstrap, ${{ matrix.phase }})
# These contracts are portable. Standard Linux runners have enough memory
# for LLVM optimization alongside the compiler that produces its input.
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
# The sanitized self-rebuild is split at the IR boundary: one job
# lowers the compiler and compares its IR with the seed's; the other
# emits, links and probes the next generation from the seed's IR.
include:
- { flavor: plain, phase: commands }
- { flavor: plain, phase: rebuild }
- { flavor: san, phase: commands }
- { flavor: san, phase: rebuild-frontend }
- { flavor: san, phase: rebuild-emit }
env:
SCRIPTC_TEST_WORKERS: "1"
SCRIPTC_SAN: ${{ matrix.flavor == 'san' && '1' || '' }}
SCRIPTC_BOOTSTRAP_PHASE: ${{ matrix.phase }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- uses: ./.github/actions/llvm-linux
- run: pnpm install --frozen-lockfile
- uses: ./.github/actions/zig
# Rebuilds use the relocated seed's packaged helper and runtimes.
- uses: ./.github/actions/native-packages
if: matrix.phase == 'commands'
with:
target: linux-x64-gnu
- uses: ./.github/actions/native-packages
if: matrix.phase == 'commands'
with:
target: wasm32-wasi
helper: "false"
- run: pnpm build
# The command phase builds Node island programs with the native compiler
# and runs them against Node; they load Node from a libnode pack, and
# fail rather than skip in this lane when none is configured.
- uses: ./.github/actions/libnode
if: matrix.phase == 'commands'
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: bootstrap-seed-${{ matrix.flavor }}-${{ github.sha }}
path: ${{ runner.temp }}
- name: Restore bootstrap seed
run: tar -C "$RUNNER_TEMP" -xf "$RUNNER_TEMP/bootstrap-seed.tar"
# The sanitized emitter peaks near 13 GB while it emits the compiler,
# then clang compiles the 348 MB module under AddressSanitizer. Together
# they exceed the runner's 16 GB of memory, so back them with swap.
- name: Enlarge swap for the sanitized emitter
if: matrix.phase == 'rebuild-emit'
run: |
sudo swapoff -a
sudo rm -f /mnt/swapfile
sudo fallocate -l 24G /mnt/swapfile
sudo chmod 600 /mnt/swapfile
sudo mkswap /mnt/swapfile
sudo swapon /mnt/swapfile
free -h
- name: Native bootstrap and package installation checks
# The instrumented emitter's recursion can exceed the default 8 MiB stack.
run: |
if [ "$SCRIPTC_BOOTSTRAP_PHASE" = rebuild-emit ]; then ulimit -s unlimited; fi
node scripts/ci-native-bootstrap.mjs
env:
SCRIPTC_BOOTSTRAP_SEED_DIRECTORY: ${{ runner.temp }}/native-bootstrap
SCRIPTC_BOOTSTRAP_PACKAGE_DIR: ${{ runner.temp }}/native-cli-package
# A real host-clang/glibc build, not the zig cross-target lane: catches
# Linux feature-test macro omissions at compile time and separate-libm
# omissions/order errors at link time.
linux_host_clang:
needs: [validation_scope, lanes]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.linux == 'true' }}
name: test (linux host clang/glibc)
runs-on: ubuntu-24.04
timeout-minutes: 10
env:
# The verdict must come from a live clang invocation, never a cached
# binary copied from a previous build.
SCRIPTC_NO_CACHE: "1"
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm build
- name: Host clang and sanitizer build regressions
run: >-
pnpm test packages/compiler/test/cc-driver.test.ts
packages/compiler/src/native/sanitizer.test.ts
--testNamePattern "host-native clang static build|sanitized vendor archives"
# The TS7 parity sweep exercises the compiler frontend, so it can run
# alongside the Linux LLVM/runtime package build instead of following it.
ts7_parity:
needs: [validation_scope, lanes]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.linux == 'true' }}
name: test (TypeScript 7 parity)
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm build
- name: TypeScript 7 full parity sweep
run: pnpm test:ts7
# tsc-ts, the default checker, and tsgo (SCRIPTC_CHECKER=tsgo) serve the same TypeScript 7 API. Every corpus
# program must compile to the same LLVM IR, with the same diagnostics, with either checker.
checker_identity:
needs: [validation_scope, lanes]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.linux == 'true' }}
name: test (tsgo and tsc-ts give the same IR, ${{ matrix.optimization }})
runs-on: ubuntu-24.04
# The release and the dev corpus compiles do not depend on each other, so
# each has its own job: 15 minutes at the median and 20 on a slow hosted
# runner, where the two back to back took 31 and 41.
timeout-minutes: 40
strategy:
fail-fast: false
matrix:
optimization: [release, dev]
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm build
- name: Corpus LLVM IR with tsgo and with tsc-ts (${{ matrix.optimization }})
run: pnpm test:checker-identity --optimization ${{ matrix.optimization }}
llvm_artifacts_linux_x64:
needs: [validation_scope, lanes, native_linux]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.linux == 'true' }}
name: test (Linux x64 LLVM artifacts, no clang)
runs-on: ubuntu-24.04
# The glibc helper and runtime pack come from native_linux, as the macOS
# artifact jobs take theirs from native_macos. The musl packages and the
# WASI smoke run in llvm_artifacts_linux_x64_musl and
# llvm_artifacts_linux_x64_wasi: together with a rebuild of the glibc
# packages, which a runtime change forces, the one job took 25 to 32
# minutes.
timeout-minutes: 20
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- run: pnpm install --frozen-lockfile
- uses: ./.github/actions/zig
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: native-linux-x64-gnu
path: ${{ runner.temp }}
- run: tar -xzf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
- run: pnpm build
- name: Linux helper object format and no-clang output contract
run: |
PATH="$RUNNER_TEMP/traps:$PATH"
mkdir -p "$RUNNER_TEMP/traps"
for tool in clang cc gcc ar ld; do printf '#!/bin/sh\nexit 97\n' > "$RUNNER_TEMP/traps/$tool"; chmod +x "$RUNNER_TEMP/traps/$tool"; done
node packages/cli/dist/main.js build tests/corpus/001-hello.ts --emit=obj -o "$RUNNER_TEMP/hello.o"
file "$RUNNER_TEMP/hello.o" | grep 'ELF 64-bit LSB relocatable, x86-64'
- name: Linux helper/runtime-pack executable differential smoke
env:
SCRIPTC_NO_CACHE: "1"
SCRIPTC_LEGACY_C_PIPELINE: "0"
run: |
node packages/cli/dist/main.js build tests/corpus/001-hello.ts -o "$RUNNER_TEMP/hello"
test "$("$RUNNER_TEMP/hello")" = 'hello world'
- name: Linux release runtime reachability and debug payload
run: pnpm test tests/harness/runtime-tree-shaking.test.ts
- name: Debian 12 glibc 2.36 runtime-pack smoke
run: |
docker run --rm --volume "$GITHUB_WORKSPACE:/work:ro" --workdir /work node:24-bookworm-slim sh -ec '
apt-get update
apt-get install --yes --no-install-recommends clang
SCRIPTC_NO_CACHE=1 SCRIPTC_LEGACY_C_PIPELINE=0 node packages/cli/dist/main.js build tests/corpus/001-hello.ts -o /tmp/hello
test "$(/tmp/hello)" = "hello world"
'
llvm_artifacts_linux_x64_musl:
needs: [validation_scope, lanes, native_linux]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.linux == 'true' }}
name: test (Linux x64 LLVM artifacts, no clang, musl)
runs-on: ubuntu-24.04
# The static musl helper and runtime pack, restored from the
# native-packages cache when their inputs are unchanged: about 9 minutes
# when both are built. The runtime build emits its units through the
# host's glibc helper (runtime-unit.mjs), which comes from native_linux.
timeout-minutes: 25
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- uses: ./.github/actions/llvm-linux
- run: pnpm install --frozen-lockfile
- uses: ./.github/actions/zig
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: native-linux-x64-gnu
path: ${{ runner.temp }}
- run: tar -xzf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
- uses: ./.github/actions/native-packages
with:
target: linux-x64-musl
- name: Verify the Linux x64 musl release helper
run: |
helper=packages/llvm-linux-x64-musl/bin/scriptc-llvm-codegen
readelf -l "$helper" > "$RUNNER_TEMP/musl-helper-program-headers"
if grep -q INTERP "$RUNNER_TEMP/musl-helper-program-headers"; then
echo "musl helper must be a static executable" >&2
exit 1
fi
docker run --rm --volume "$GITHUB_WORKSPACE:/work:ro" alpine:3.22 \
/work/$helper version --format=json | node -e '
const value = JSON.parse(require("node:fs").readFileSync(0, "utf8"));
if (value.default_target !== "x86_64-unknown-linux-musl" || !value.supported_targets?.includes("wasm32-unknown-wasi")) process.exit(1);
'
- run: pnpm build
- name: Executable hardening and debug payload (glibc and musl)
run: pnpm test tests/harness/executable-hardening.test.ts
llvm_artifacts_linux_x64_wasi:
needs: [validation_scope, lanes, native_linux]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.linux == 'true' }}
name: test (Linux x64 LLVM artifacts, no clang, wasm32)
runs-on: ubuntu-24.04
# The wasm32-wasi runtime pack (restored from the native-packages cache
# when unchanged; its units go through native_linux's glibc helper) and
# the WASI smoke tests, which take 6 to 9 minutes.
timeout-minutes: 25
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
with:
version: 11.1.3
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .node-version
cache: pnpm
- uses: ./.github/actions/llvm-linux
- run: pnpm install --frozen-lockfile
- uses: ./.github/actions/zig
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: native-linux-x64-gnu
path: ${{ runner.temp }}
- run: tar -xzf "$RUNNER_TEMP/native-linux-x64-gnu.tar.gz"
- uses: ./.github/actions/native-packages
with:
target: wasm32-wasi
helper: "false"
- run: pnpm build