Repository navigation
2686 lines (2534 loc) · 143 KB
/
Copy pathci.yml
File metadata and controls
2686 lines (2534 loc) · 143 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:
# Only main. A landing on the v5 integration branch runs through the
# pull_request event of its PR into main instead: the full-tier jobs
# below also run for `head_ref == release/v5-prerelease`. A push trigger
# on a release-named branch makes zizmor audit this as a release
# workflow (cache-poisoning).
branches: [main]
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
# The merge queue tests each queued PR merged onto the tip of main (plus
# the PRs ahead of it) before it lands. `ci-ok` below is the required
# check, so this event has to run the same pull_request-tier job set.
merge_group:
types: [checks_requested]
schedule:
# Nightly on main: the `full` tier (e2e-full, cargo-vex-matrix-full,
# yarn-berry-full) and e2e-docker, which pull_request runs skip.
- cron: '41 5 * * *'
workflow_dispatch:
inputs:
hosted_e2e:
# Underscored on purpose: `inputs.hosted-e2e` is not valid expression
# syntax (a hyphenated name needs `inputs['hosted-e2e']`).
description: 'hosted-e2e: auto (obey vars.HOSTED_E2E_DISABLED) | force | skip'
type: choice
default: auto
options: [auto, force, skip]
permissions:
contents: read
# A newer push to the same PR supersedes its older run; nothing else is
# cancelled. Push runs are grouped per commit: a concurrency group holds only
# ONE pending run, so a shared `refs/heads/main` group silently cancelled every
# queued push but the newest during a merge burst, and most main commits never
# got a verdict. Merge-group refs (gh-readonly-queue/...) are unique per queue
# entry already. The nightly keeps its own group so it never queues behind (or
# cancels) a push.
concurrency:
group: ci-${{ github.event.pull_request.number || (github.event_name == 'push' && github.sha) || github.ref }}${{ github.event_name == 'schedule' && '-nightly' || '' }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
# Linux jobs run on Depot's runners (depot-ubuntu-*-4: 4 vCPU, like GitHub's
# public-repo ubuntu-latest), which don't draw on the org's shared GitHub-hosted
# concurrency pool, where merge-queue jobs used to wait behind PR CI. Matrix
# values stay `ubuntu-latest`/`ubuntu-22.04`, so job names, cache keys and
# scripts are unchanged; only `runs-on` maps them. Kill switch: set the
# repository variable DISABLE_DEPOT_RUNNERS=true to fall back to GitHub-hosted
# runners (the same switch depscan uses). The compile-bound critical-path
# jobs (clippy, coverage, e2e-build, test-release) use 16-vCPU Depot runners
# (depot-ubuntu-24.04-16). The compatibility workflows follow
# the same mapping. Release, publish and merge-queue workflows stay
# GitHub-hosted (provenance / write tokens).
# LEAN SCOPE (temporary, while org runner capacity is constrained): unless
# the repository variable CI_SCOPE is `full`, pull_request, merge_group and
# push runs use one targeted e2e row per package manager (the rest are in
# e2e-extended), one row each of the yarn and cargo VEX matrices, and skip
# the Windows/macOS legs (except e2e-build-windows, which still compiles every
# test target on Windows in the merge queue), test, test-release, old cargo toolchains and the
# sbt Docker coverage slice. Everything runs in the nightly schedule and on
# workflow_dispatch. Set CI_SCOPE=full to restore the old gate without a
# commit. ci-ok treats skipped jobs as passing. hosted-e2e is not scoped:
# it always runs, and its step-level HOSTED_E2E_DISABLED switch is the only
# bypass.
# MERGE-QUEUE REUSE: a push to main whose exact SHA already passed this
# workflow in the merge queue (scripts/ci-reuse-merge-group.py, run by
# clippy) only compiles, to refresh the main-only caches, and skips the
# test steps and test-only jobs the queue already ran. Jobs the queue never
# runs (test-release, e2e-full, yarn-berry-full, cargo-vex-matrix-full) and
# hosted-e2e still run. API errors, missing evidence and direct pushes run
# everything.
jobs:
# Required independently of ci-ok so the merge queue sees a compile/lint
# failure immediately. Expensive jobs also depend on this preflight.
clippy:
if: github.event.pull_request.draft != true
runs-on: ${{ vars.DISABLE_DEPOT_RUNNERS == 'true' && 'ubuntu-latest' || 'depot-ubuntu-24.04-16' }}
timeout-minutes: 20
permissions:
contents: read
actions: read
outputs:
reuse: ${{ steps.merge-queue.outputs.reuse }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
# Main remains the cache writer. Skip duplicate test execution only
# when this workflow passed in the merge queue on the identical SHA.
- name: Check merge-queue validation
id: merge-queue
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
env:
GH_TOKEN: ${{ github.token }}
run: python3 scripts/ci-reuse-merge-group.py
- name: Install Rust
# rustup is pre-installed on GitHub-hosted runners. `toolchain
# install` with no name reads rust-toolchain.toml in the repo root,
# then installs the pinned channel + listed components if missing
# (retried past download blips; see the script). No third-party
# action dependency needed for toolchain setup.
run: scripts/rustup-retry.sh toolchain install
- name: Cache cargo
# Swatinem/rust-cache instead of a raw actions/cache of the whole
# target/ dir: it prunes the cache to dependency artifacts (~5-10x
# smaller), which keeps this repo's total cache footprint inside
# GitHub's 10 GiB budget (a raw target/ cache let every PR save evict
# main's caches). save-if restricts writes to main so PR branches
# restore without churning the budget. Compatible build jobs use
# shared-key by profile + runner image (not job or package-manager
# version). rust-cache also hashes the toolchain, Cargo manifests
# and Rust environment. Check/coverage/release keep separate caches.
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Run clippy
run: cargo clippy --locked --workspace --all-features -- -D warnings
- name: Check every test target before starting expensive jobs
# Clippy above checks the shipped targets. This catches errors in
# cfg(test), integration tests and feature-gated targets without
# linking hundreds of executables or waiting for the E2E fan-out.
run: cargo check --locked --workspace --all-targets --all-features
# The napi addon is only ever loaded by Node, so cargo's own tests never
# exercise its JS loader or the engine/provider boundary.
node-addon:
if: github.event.pull_request.draft != true
needs: clippy
runs-on: ${{ vars.DISABLE_DEPOT_RUNNERS == 'true' && 'ubuntu-latest' || 'depot-ubuntu-24.04-4' }}
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Rust
run: scripts/rustup-retry.sh toolchain install
- name: Cache cargo
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
shared-key: dev-ubuntu-latest
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '20.20.2'
- name: Build addon
env:
SOCKET_PATCH_NODE_CARGO_PROFILE: dev
run: node crates/socket-patch-node/npm/scripts/build-addon.mjs
- name: Smoke-test addon
if: needs.clippy.outputs.reuse != 'true'
run: node --test crates/socket-patch-node/npm/test/smoke.mjs
# Check the standalone installer, release scripts, and native installer
# test harnesses.
lint-ecosystems:
if: github.event.pull_request.draft != true
runs-on: ${{ vars.DISABLE_DEPOT_RUNNERS == 'true' && 'ubuntu-latest' || 'depot-ubuntu-24.04-4' }}
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Python — test native installer harnesses
run: python3 -B -m unittest discover -s scripts/tests -v
- name: Shell — shellcheck the curl|sh installer
# shellcheck is pre-installed on ubuntu-latest.
run: shellcheck --shell=sh scripts/install.sh
- name: Shell — run the installer end to end
# shellcheck proves the script parses; this proves it installs. The
# script is what install.socket.dev/patch serves and what the README
# tells people to pipe into a shell, so "it downloads the latest
# release, verifies SHA256SUMS, and produces a binary that runs" is
# worth asserting on every PR rather than discovering from a user.
# Installs the LATEST RELEASE, not this checkout — on a version-bump PR
# that is deliberately the previous version.
run: |
sh scripts/install.sh
command -v socket-patch
socket-patch --version
- name: Shell — run the installer against an alternate origin
# Exercises SOCKET_PATCH_BASE_URL (and SOCKET_PATCH_INSTALL_DIR) with a
# base that is not the default. Uses GitHub's own releases base, which
# is the same URL shape install.socket.dev serves, so the template the
# script builds is covered regardless of whether the Socket relay is
# deployed yet. The dedicated Socket-origin check is the next step.
run: |
SOCKET_PATCH_BASE_URL=https://github.com/SocketDev/socket-patch/releases \
SOCKET_PATCH_INSTALL_DIR="$RUNNER_TEMP/alt-origin" \
sh scripts/install.sh
"$RUNNER_TEMP/alt-origin/socket-patch" --version
- name: Shell — install through install.socket.dev, once it exists
# The whole point of the relay is that a client never has to reach
# github.com. That is only assertable against the deployed host, so this
# step skips itself until the host resolves rather than being red from
# the day it merges (same posture as the installer-drift workflow).
run: |
if ! curl -sfI -m 20 https://install.socket.dev/patch/latest >/dev/null 2>&1; then
echo "::notice::install.socket.dev/patch/latest does not answer yet — skipping the Socket-origin install."
exit 0
fi
latest=$(curl -fsSL -m 20 https://install.socket.dev/patch/latest)
echo "install.socket.dev reports latest=$latest"
SOCKET_PATCH_BASE_URL=https://install.socket.dev/patch/SocketDev/socket-patch/releases \
SOCKET_PATCH_INSTALL_DIR="$RUNNER_TEMP/socket-origin" \
sh scripts/install.sh
installed=$("$RUNNER_TEMP/socket-origin/socket-patch" --version | awk '{print $NF}')
if [ "$installed" != "$latest" ]; then
echo "::error::install.socket.dev says latest is $latest but installed $installed" >&2
exit 1
fi
- name: Shell — the installer URL is consistent across the docs
# The README, the script's own usage comment, and the hosting runbook
# all name the canonical URL. Keeping them in lockstep is the whole
# promise of install.socket.dev/patch being "a copy of this file".
run: |
for f in README.md scripts/install.sh docs/installer-hosting.md; do
if ! grep -qF 'https://install.socket.dev/patch' "$f"; then
echo "Error: $f no longer references https://install.socket.dev/patch" >&2
exit 1
fi
done
- name: Shell — shellcheck the release scripts
run: shellcheck scripts/version-sync.sh scripts/release-lint.sh scripts/dispatch-publish.sh
# Release-readiness gate (scripts/release-lint.sh — the same checks the
# Release workflow's `version` job runs before publishing anything):
# - every PR/push: version coherence — version-sync.sh must be a no-op,
# so a hand-edited version in any single packaging site fails CI here
# instead of surfacing mid-release;
# - the release train's rolling `release-sync` PR (docs/release-train/
# DESIGN.md §3.7), which moves main to the newest cut tag (rc or
# stable): CHANGELOG has a non-empty section for that version and the
# tag already exists. Only a same-repo `release-sync` branch targeting
# main gets this path; the same branch name from a fork (or aimed at
# another base) gets the full gate below;
# - any other PR that bumps the workspace version: the full gate —
# CHANGELOG has a dated, non-empty section for the new version and the
# tag doesn't already exist.
release-readiness:
if: github.event.pull_request.draft != true
runs-on: ${{ vars.DISABLE_DEPOT_RUNNERS == 'true' && 'ubuntu-latest' || 'depot-ubuntu-24.04-4' }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Lint release readiness
env:
EVENT_NAME: ${{ github.event_name }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_REF: ${{ github.head_ref }}
BASE_REF: ${{ github.base_ref }}
HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
REPO: ${{ github.repository }}
run: |
if [ "$EVENT_NAME" = "pull_request" ] && [ "$HEAD_REF" = "release-sync" ] \
&& [ "$HEAD_REPO" = "$REPO" ] && [ "$BASE_REF" = "main" ]; then
bash scripts/release-lint.sh --tag-exists
exit 0
fi
if [ "$EVENT_NAME" = "pull_request" ]; then
# Compare the workspace version against the PR base to detect a
# version bump. The shallow checkout doesn't have the base
# commit; fetch just that object.
git fetch --quiet --depth 1 origin "$BASE_SHA"
BASE_VERSION="$(git show "$BASE_SHA:Cargo.toml" | grep '^version = ' | head -1 | sed 's/version = "\(.*\)"/\1/')"
HEAD_VERSION="$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')"
if [ "$BASE_VERSION" != "$HEAD_VERSION" ]; then
echo "Version bump PR detected ($BASE_VERSION -> $HEAD_VERSION); running the full release gate."
bash scripts/release-lint.sh --tag-check
exit 0
fi
fi
bash scripts/release-lint.sh --sync-only
test:
if: (github.event.pull_request.draft != true) && (vars.CI_SCOPE == 'full' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
needs: clippy
# No ubuntu-latest leg: `coverage` runs this same `cargo test
# --workspace` (debug, default features, plus Go and vexctl) on ubuntu,
# instrumented, and fails on any test failure. A plain ubuntu leg ran
# every Linux test a second time for ~12 job-minutes per run.
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest]
# Two legs per OS (scripts/ci-test-shard.py): one leg linked ~240
# test binaries and ran them serially for ~26 min. Windows balances
# measured runtime plus linking work (scripts/ci-test-durations.json);
# macOS keeps the count split. Shard 1 also owns unit tests and
# doctests.
shard: [1, 2]
exclude:
# macOS legs run on main, the merge queue and nightly, not per PR push.
- os: ${{ github.event_name == 'pull_request' && 'macos-latest' || '' }}
# One compile-only cache writer per OS is enough for an already
# tested SHA.
- shard: ${{ needs.clippy.outputs.reuse == 'true' && 2 || 0 }}
runs-on: ${{ vars.DISABLE_DEPOT_RUNNERS != 'true' && (matrix.os == 'ubuntu-latest' && 'depot-ubuntu-24.04-4' || matrix.os == 'ubuntu-22.04' && 'depot-ubuntu-22.04-4') || matrix.os }}
timeout-minutes: 50
env:
VEXCTL_VERSION: v0.3.0
CI_TEST_TIMINGS: ${{ matrix.os == 'windows-latest' && 'scripts/ci-test-durations.json' || '' }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Rust
# rustup is pre-installed on GitHub-hosted runners. `toolchain
# install` with no name reads rust-toolchain.toml in the repo root,
# then installs the pinned channel + listed components if missing
# (retried past download blips; see the script). No third-party
# action dependency needed for toolchain setup.
shell: bash
run: scripts/rustup-retry.sh toolchain install
- name: Cache cargo
# Swatinem/rust-cache, main-only saves: see the first `Cache cargo`
# step in this file.
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
shared-key: dev-${{ matrix.os }}
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Build
run: cargo build --workspace
- name: Warm the test cache after merge-queue validation
if: needs.clippy.outputs.reuse == 'true'
run: cargo test --locked --workspace --no-run
- name: Install Go (for vexctl)
if: needs.clippy.outputs.reuse != 'true'
id: go
# The `vex` subcommand emits OpenVEX documents; tests/e2e_vex.rs
# validates the output with vexctl when it's on PATH. vexctl is
# a Go binary distributed via `go install`. Setting up Go here
# is the cheapest way to give every test job a usable vexctl.
# Go must be >= 1.24: its linker only began emitting an LC_UUID load
# command then, and the macOS-latest runner's dyld (Sequoia+) refuses
# to load a Mach-O binary without one ("missing LC_UUID load command"),
# so a 1.22-built vexctl crashes on launch and every e2e_vex assertion
# fails. ubuntu/windows are unaffected, but the matrix shares this pin.
# SHA pin resolved from `gh api repos/actions/setup-go/git/refs/tags/v6.4.0`.
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: '1.24'
cache: false
- name: Restore vexctl (macOS)
# The macOS compile below took a median 110s (max 168s) of every
# macOS `test` job and is the step's only network flake source
# (sum.golang.org resets). Its output depends only on the vexctl
# version, the Go toolchain and the runner, so it is cached under
# exactly those. Saved from main only, like the cargo cache.
id: vexctl-cache
if: needs.clippy.outputs.reuse != 'true' && runner.os == 'macOS'
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0
with:
path: ${{ runner.temp }}/vexctl-bin
key: vexctl-${{ env.VEXCTL_VERSION }}-go${{ steps.go.outputs.go-version }}-${{ runner.os }}-${{ runner.arch }}
- name: Install vexctl
if: needs.clippy.outputs.reuse != 'true'
# Linux / Windows: the v0.3.0 release binary, checked against the
# sha256 pinned here (from the release's vexctl_checksums.txt).
# Compiling it took ~80s on ubuntu and ~180s on windows, the
# slowest job in this workflow.
#
# macOS still compiles: the release's darwin binaries are built
# with go1.22.7 and have no LC_UUID, so the runner's dyld refuses
# them (see the Go step above). The compile is retried: it builds
# sigstore/cosign, whose module verification reads dozens of
# sum.golang.org checksum tiles, and transient INTERNAL_ERROR
# stream resets there have failed this step on otherwise-green
# runs. The backoff rides out short resets; a persistent outage
# still fails loudly on the last attempt. A binary restored by the
# step above skips the compile.
#
# --ssl-revoke-best-effort: Windows curl (schannel) otherwise fails
# the TLS handshake with CRYPT_E_REVOCATION_OFFLINE whenever the
# CA's revocation server is unreachable. A revoked certificate still
# fails, and the sha256 check follows. A no-op on other OSes.
#
# Either way the binary's directory goes on PATH so
# `Command::new("vexctl")` in the tests resolves.
shell: bash
env:
VEXCTL_SHA256_LINUX_AMD64: cd7f8b57d20642166ed4eb3dd1fd849bbb30bbd7c5b9f5b0316b6003b57ceaba
VEXCTL_SHA256_WINDOWS_AMD64: 346fb3104b656fe1a407cbae88ea29a636b36f4569ec58a5a8dadca7343993ed
CACHE_HIT: ${{ steps.vexctl-cache.outputs.cache-hit }}
run: |
set -euo pipefail
dir="$RUNNER_TEMP/vexctl-bin"
mkdir -p "$dir"
case "$RUNNER_OS" in
Linux) asset=vexctl-linux-amd64 bin=vexctl sha="$VEXCTL_SHA256_LINUX_AMD64" ;;
Windows) asset=vexctl-windows-amd64.exe bin=vexctl.exe sha="$VEXCTL_SHA256_WINDOWS_AMD64" ;;
*) asset='' ;;
esac
if [ -n "$asset" ]; then
curl -fsSL --retry 5 --retry-all-errors --ssl-revoke-best-effort -o "$dir/$bin" \
"https://github.com/openvex/vexctl/releases/download/$VEXCTL_VERSION/$asset"
echo "$sha $dir/$bin" | sha256sum -c -
chmod +x "$dir/$bin"
"$dir/$bin" version
echo "$dir" >> "$GITHUB_PATH"
exit 0
fi
if [ "$CACHE_HIT" != true ]; then
for attempt in 1 2 3 4 5; do
if GOBIN="$dir" go install "github.com/openvex/vexctl@$VEXCTL_VERSION"; then
break
fi
if [ "$attempt" = 5 ]; then
echo "::error::go install vexctl failed on all 5 attempts"
exit 1
fi
echo "::warning::go install vexctl attempt $attempt failed; retrying"
sleep $((attempt * 20))
done
fi
"$dir/vexctl" version
echo "$dir" >> "$GITHUB_PATH"
- name: Save vexctl (macOS)
if: >-
needs.clippy.outputs.reuse != 'true'
&& runner.os == 'macOS'
&& github.ref == 'refs/heads/main'
&& steps.vexctl-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0
with:
path: ${{ runner.temp }}/vexctl-bin
key: ${{ steps.vexctl-cache.outputs.cache-primary-key }}
- name: Run tests
if: needs.clippy.outputs.reuse != 'true'
# Default features only: `--all-features` would also RUN the
# docker-e2e suites, which soft-skip as "ok" here (no images, and
# macOS/Windows have no Docker) — fake greens hiding a broken
# skip-guard. Their compile rot is caught on every OS by e2e-build,
# which compiles every CLI test target with --all-features; one
# feature set here means one compile.
#
# `--no-fail-fast`: without it cargo stops at the first failing test
# BINARY, so one bad file hides every later binary's result on that
# OS. Run them all and fail at the end instead.
shell: bash
env:
# The real-go hosted/vendored suites (`#![cfg(unix)]`) ride the Go
# installed above for vexctl: fail instead of skip without `go` /
# `zip`, and assert the pinned release.
SOCKET_PATCH_GO_E2E_REQUIRED: '1'
SOCKET_PATCH_GO_E2E_VERSION: '1.24'
TEST_SHARD: ${{ matrix.shard }}
# This leg's share of `cargo test --workspace --no-fail-fast`; the
# shards together run exactly that selection (test_ci_test_shard.py).
run: |
python3 scripts/ci-test-shard.py "$TEST_SHARD" 2
test-release:
# Each PR and main push runs the complete release-mode suite. The merge
# queue already skips this job because each constituent PR ran it.
if: (github.event.pull_request.draft != true && github.event_name != 'merge_group') && (vars.CI_SCOPE == 'full' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
needs: clippy
runs-on: ${{ vars.DISABLE_DEPOT_RUNNERS == 'true' && 'ubuntu-latest' || 'depot-ubuntu-24.04-16' }}
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3]
# Warm-cache runs still spend 20-23 minutes compiling ~240 optimized
# test binaries, followed by 5-6 minutes executing them. Split both
# compilation and execution with the same partitioner as `test`:
# shard 1 owns the unit tests/doctests and fewer integration targets.
# `ci-ok` waits for every shard and fails if any shard fails.
timeout-minutes: 40
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Rust
# rustup is pre-installed on GitHub-hosted runners. `toolchain
# install` with no name reads rust-toolchain.toml in the repo root,
# then installs the pinned channel + listed components if missing
# (retried past download blips; see the script). No third-party
# action dependency needed for toolchain setup.
run: scripts/rustup-retry.sh toolchain install
- name: Cache cargo
# Swatinem/rust-cache, main-only saves: see the first `Cache cargo`
# step in this file.
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Run tests (release)
# `ci-release` = [profile.release] minus the full-LTO link (see the
# profile's comment in Cargo.toml). Same opt-level/debug-assertion
# semantics this job exists to validate; ~23m of LTO relinking gone.
# Default features for the same reason as the `test` job; the
# feature-gated suites' compile rot is e2e-build's.
env:
TEST_SHARD: ${{ matrix.shard }}
TEST_SHARD_COUNT: ${{ strategy.job-total }}
run: python3 scripts/ci-test-shard.py "$TEST_SHARD" "$TEST_SHARD_COUNT" --locked --profile ci-release
coverage:
if: github.event.pull_request.draft != true
needs: clippy
# Code coverage via cargo-llvm-cov (LLVM source-based instrumentation).
# Reports as a markdown table in the job summary and uploads the raw
# lcov.info file as a workflow artifact. No threshold gating — the
# numbers are report-only so contributors get visibility without flaky
# CI when coverage shifts naturally with test edits. A failing TEST
# fails the job: this is the Linux leg of `test`.
runs-on: ${{ vars.DISABLE_DEPOT_RUNNERS == 'true' && 'ubuntu-latest' || 'depot-ubuntu-24.04-16' }}
timeout-minutes: 35
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Rust
# `toolchain install` installs the rust-toolchain.toml channel +
# listed components; `component add` adds the llvm-tools-preview
# bits cargo-llvm-cov needs to merge .profraw files into lcov.
run: |
scripts/rustup-retry.sh toolchain install
scripts/rustup-retry.sh component add llvm-tools-preview
- name: Install cargo-llvm-cov
# taiki-e/install-action ships precompiled binaries — much faster
# than `cargo install` and avoids a per-CI-run compile.
uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2.79.3
with:
tool: cargo-llvm-cov@0.8.7
- name: Cache cargo
# Swatinem/rust-cache, main-only saves: see the first `Cache cargo`
# step in this file.
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Warm the coverage cache after merge-queue validation
if: needs.clippy.outputs.reuse == 'true'
run: |
cargo llvm-cov show-env --sh > "$RUNNER_TEMP/coverage-env.sh"
source "$RUNNER_TEMP/coverage-env.sh"
cargo test --locked --workspace --no-run
- name: Install Go (for vexctl and the real-go suites)
if: needs.clippy.outputs.reuse != 'true'
# This job is the Linux leg of `test` (see there): same Go pin.
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: '1.24'
cache: false
- name: Install vexctl
if: needs.clippy.outputs.reuse != 'true'
# The v0.3.0 Linux release binary, as in `test`'s vexctl step.
env:
VEXCTL_VERSION: v0.3.0
VEXCTL_SHA256_LINUX_AMD64: cd7f8b57d20642166ed4eb3dd1fd849bbb30bbd7c5b9f5b0316b6003b57ceaba
run: |
set -euo pipefail
dir="$RUNNER_TEMP/vexctl-bin"
mkdir -p "$dir"
curl -fsSL --retry 5 --retry-all-errors -o "$dir/vexctl" \
"https://github.com/openvex/vexctl/releases/download/$VEXCTL_VERSION/vexctl-linux-amd64"
echo "$VEXCTL_SHA256_LINUX_AMD64 $dir/vexctl" | sha256sum -c -
chmod +x "$dir/vexctl"
"$dir/vexctl" version
echo "$dir" >> "$GITHUB_PATH"
- name: Run tests with coverage
if: needs.clippy.outputs.reuse != 'true'
# Two-step pattern: `--no-report` runs instrumented tests and
# collects raw profiles, then one `report` exports LCOV. The
# summary is derived from that file (scripts/ci-lcov-summary.py)
# instead of a second `report` that merges the profiles and scans
# every instrumented object again. The output filename matches the `*.lcov`
# gitignore pattern so a stray local run can't accidentally
# commit a 600 KB report.
#
# Default features (instead of --all-features) exclude the
# docker-e2e feature — those tests need Docker images this job
# doesn't build. The coverage-docker matrix covers them
# separately, and coverage-merge stitches everything together.
#
# This is also the gating Linux test run (`test` has no ubuntu
# leg), hence `test`'s Go env and `--no-fail-fast`.
env:
SOCKET_PATCH_GO_E2E_REQUIRED: '1'
SOCKET_PATCH_GO_E2E_VERSION: '1.24'
run: |
cargo llvm-cov --workspace --no-fail-fast \
--no-report
cargo llvm-cov report --lcov --output-path coverage-host.lcov
python3 scripts/ci-lcov-summary.py coverage-host.lcov | tee coverage-summary.txt
- name: Publish coverage summary to job summary
if: needs.clippy.outputs.reuse != 'true'
# Render the per-file LCOV line/function/branch totals as a fenced
# block in the GitHub Actions job summary so reviewers don't
# need to crack open the artifact for a quick look.
run: |
{
echo "## Host coverage summary"
echo ""
echo "(In-process tests only. See coverage-merge for the"
echo "full picture including docker-e2e binary coverage.)"
echo ""
echo '```'
cat coverage-summary.txt
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload host LCOV artifact
if: needs.clippy.outputs.reuse != 'true'
uses: ./.github/actions/upload-artifact
with:
name: coverage-host
path: coverage-host.lcov
if-no-files-found: error
retention-days: 30
# Dockerfile.base compiles the full-LTO release binary inside Docker, with
# no cache. Build it once per run and hand the image to every docker leg.
docker-base:
if: (github.event.pull_request.draft != true && needs.clippy.outputs.reuse != 'true') && (vars.CI_SCOPE == 'full' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.head_ref == 'release/v5-prerelease')
needs: clippy
runs-on: ${{ vars.DISABLE_DEPOT_RUNNERS == 'true' && 'ubuntu-22.04' || 'depot-ubuntu-22.04-4' }}
timeout-minutes: 30
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Docker Buildx
# `driver: docker`: see coverage-docker's matching step.
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
with:
driver: docker
- name: Build base image
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
context: .
file: tests/docker/Dockerfile.base
tags: socket-patch-test-base:latest
load: true
- name: Save base image
run: |
mkdir -p docker-base
docker save --output docker-base/socket-patch-test-base.tar socket-patch-test-base:latest
- uses: ./.github/actions/upload-artifact
with:
name: docker-base-image
path: docker-base/socket-patch-test-base.tar
if-no-files-found: error
retention-days: 3
coverage-docker:
# Per-ecosystem coverage for the Docker-driven e2e suite. Mirrors
# the e2e-docker matrix but builds an instrumented socket-patch
# binary and mounts it into the container along with a host-
# visible profraw directory, so the in-container code paths
# contribute to the lcov merge.
#
# Hooks: docker_e2e_<eco>.rs reads SOCKET_PATCH_COV_BIN +
# SOCKET_PATCH_COV_PROFRAW_DIR. Both unset is the no-op default
# (used by the e2e-docker matrix below). Every per-push docker_e2e
# suite runs here; e2e-docker is the nightly run of the same suites
# against the base image's full-LTO release binary.
#
# Pin to ubuntu-22.04 (glibc 2.35) instead of ubuntu-latest
# (currently 24.04, glibc 2.39). The instrumented binary built
# here gets mounted into the debian:12-slim test container
# (glibc 2.36); a binary linked against a newer glibc than the
# container ships fails to load. ubuntu-22.04's older glibc is
# the highest base that's forward-compatible with debian:12.
if: (vars.CI_SCOPE == 'full' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
needs: docker-base
runs-on: ${{ vars.DISABLE_DEPOT_RUNNERS == 'true' && 'ubuntu-22.04' || 'depot-ubuntu-22.04-4' }}
# sbt's image bakes three JDKs and warm sbt / Mill / scala-cli caches.
timeout-minutes: ${{ matrix.ecosystem == 'sbt' && 45 || 30 }}
permissions:
contents: read
strategy:
fail-fast: false
matrix:
ecosystem: [npm, pypi, gem, cargo, golang, maven, composer, nuget, deno, sbt]
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Docker Buildx
# `driver: docker` makes buildx use the host docker daemon directly
# rather than running BuildKit in its own container. This is what
# lets the per-ecosystem image build see the locally-tagged
# `socket-patch-test-base:latest` from the previous step (with the
# default container driver, BuildKit runs in a sandbox that cannot
# see the host daemon's image store and tries to pull base from
# docker.io, which fails). The trade-off is that `type=gha` cache
# exports aren't supported under the docker driver — we accept
# rebuilding the images per job for correctness.
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
with:
driver: docker
- name: Install Rust
# `toolchain install` consumes rust-toolchain.toml; the explicit
# `component add` covers llvm-tools-preview for cargo-llvm-cov.
run: |
scripts/rustup-retry.sh toolchain install
scripts/rustup-retry.sh component add llvm-tools-preview
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2.79.3
with:
tool: cargo-llvm-cov@0.8.7
# No `actions/cache` here intentionally. This job builds Docker
# images and would be flagged by zizmor's cache-poisoning audit
# (a PR-poisoned cargo cache could compromise the instrumented
# binary we mount into the container).
- name: Download base image
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
pattern: docker-base-image*
merge-multiple: true
path: docker-base
- name: Load base image
run: docker load --input docker-base/socket-patch-test-base.tar
- name: Build ${{ matrix.ecosystem }} image
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
context: .
file: tests/docker/Dockerfile.${{ matrix.ecosystem }}
tags: socket-patch-test-${{ matrix.ecosystem }}:latest
load: true
# Warm only what the blocking agent_sbt_ slice below runs. The
# nightly e2e-docker job and sbt-compatibility.yml keep
# Dockerfile.sbt's full defaults.
build-args: |
${{ matrix.ecosystem == 'sbt' && 'SBT_WARM_VERSIONS=1.2.8 1.13.0' || '' }}
${{ matrix.ecosystem == 'sbt' && 'SBT_WARM_TOOLS=0' || '' }}
- name: Configure docker-e2e coverage hooks
# Mount the instrumented socket-patch that the test step's own
# `cargo llvm-cov` builds for the integration tests (cargo builds
# a package's bins before running its integration tests), and
# write the in-container profraws next to the test processes'
# ones. `cargo llvm-cov report` reads only
# target/llvm-cov-target/*.profraw and objects built there, so a
# separately built binary or profraws elsewhere in target/ never
# reach the lcov.
run: |
echo "SOCKET_PATCH_COV_BIN=$PWD/target/llvm-cov-target/debug/socket-patch" >> "$GITHUB_ENV"
echo "SOCKET_PATCH_COV_PROFRAW_DIR=$PWD/target/llvm-cov-target" >> "$GITHUB_ENV"
- name: Run ${{ matrix.ecosystem }} Docker e2e test with coverage
run: |
# Vendor build-proof capstones ride the same image as their
# ecosystem's main suite (extend the case as new vendor suites land).
EXTRA=""
# libtest arguments after `--`.
FILTER=""
case "${{ matrix.ecosystem }}" in
composer) EXTRA="--test docker_e2e_vendor_composer" ;;
gem) EXTRA="--test docker_e2e_vendor_gem" ;;
maven) EXTRA="--test docker_e2e_vendor_maven" ;;
nuget) EXTRA="--test docker_e2e_vendor_nuget" ;;
pypi) EXTRA="--test docker_e2e_vendor_pypi_pm" ;;
# The blocking sbt slice: the agent cells on 1.2.8 (Ivy) and
# 1.13.0 (Coursier, plus the `useCoursier := false` Ivy cell).
# Mill, scala-cli and the other sbt lines run nightly in
# e2e-docker and in sbt-compatibility.yml.
sbt)
export SOCKET_PATCH_SBT_DOCKER_VERSIONS="1.2.8 1.13.0"
export SOCKET_PATCH_DOCKER_E2E_REQUIRED=1
FILTER="agent_sbt_ --test-threads=1" ;;
esac
# shellcheck disable=SC2086 # EXTRA and FILTER are intentionally word-split
cargo llvm-cov \
--features docker-e2e \
--no-report \
--test docker_e2e_${{ matrix.ecosystem }} $EXTRA \
-- $FILTER
- name: Generate per-ecosystem lcov
run: |
cargo llvm-cov report \
--lcov \
--output-path coverage-docker-${{ matrix.ecosystem }}.lcov
- name: Upload per-ecosystem LCOV artifact
uses: ./.github/actions/upload-artifact
with:
name: coverage-docker-${{ matrix.ecosystem }}
path: coverage-docker-${{ matrix.ecosystem }}.lcov
if-no-files-found: error
retention-days: 30
coverage-merge:
# Merge the host coverage and per-ecosystem docker coverage into a
# single lcov.info. lcov(1) handles the union — same files are
# summed line-by-line so a line covered by ANY test counts.
if: (vars.CI_SCOPE == 'full' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
needs: [coverage, coverage-docker]
runs-on: ${{ vars.DISABLE_DEPOT_RUNNERS == 'true' && 'ubuntu-latest' || 'depot-ubuntu-24.04-4' }}
timeout-minutes: 15
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install lcov
run: sudo apt-get update && sudo apt-get install -y lcov
- name: Download all coverage artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: coverage-artifacts
pattern: coverage-{host*,docker-*}
# Retries have distinct artifact names. Their LCOV filenames stay
# stable, so a lost finalization response cannot double the counts.
merge-multiple: true
- name: Merge LCOV files
# `--add-tracefile` is repeated per input. lcov sums hit counts
# for identical source/line keys, so files covered by both host
# and docker tests report the higher (union) count.
# `find` (not bash globstar) for portability across runners.
run: |
set -e
ARGS=()
while IFS= read -r f; do
ARGS+=(--add-tracefile "$f")
done < <(find coverage-artifacts -name '*.lcov' -type f)
if [ ${#ARGS[@]} -eq 0 ]; then
echo "No lcov files found to merge" >&2
exit 1
fi
lcov "${ARGS[@]}" --output-file coverage.lcov
- name: Render summary
# `lcov --summary` prints a per-file rollup we tee into the job
# summary, same shape as cargo-llvm-cov's own.
run: |
{
echo "## Coverage (host + docker-e2e merged)"
echo ""
echo '```'
lcov --summary coverage.lcov 2>&1 | tail -20
echo '```'
echo ""
echo "Full merged LCOV uploaded as the \`coverage-lcov\` artifact."
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload merged LCOV artifact
uses: ./.github/actions/upload-artifact
with:
name: coverage-lcov
path: coverage.lcov
if-no-files-found: error
retention-days: 30
dispatch-tests:
if: github.event.pull_request.draft != true
runs-on: ${{ vars.DISABLE_DEPOT_RUNNERS == 'true' && 'ubuntu-latest' || 'depot-ubuntu-24.04-4' }}
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '20.20.2'
- name: Run npm dispatch tests
run: node --test npm/socket-patch/bin/socket-patch.test.mjs
- name: Run npm schema tests
# The `./schema` export's zod tests. `npm install --no-save`, not
# `npm ci`, for the reason publish-npm.yml gives (a version-synced
# lock can name platform packages not yet on the registry); the
# platform binaries are optional and not needed here. The pack
# test runs again once `npm test` has built dist/, so it also
# checks that the compiled schema ships.
working-directory: npm/socket-patch
run: |
npm install --no-save --ignore-scripts --no-audit --no-fund --omit=optional
npm test
node --test --test-name-pattern="npm package contents" bin/socket-patch.test.mjs
# Independent OS producers compile the CLI and every CLI test target
# once per OS (--all-features, so this also checks feature-gated suites)
# and upload the binaries the e2e, e2e-full and cargo-vex legs run.
# The legs run the
# test binaries directly from the same checkout path, so `CARGO_BIN_EXE_*`
# and `CARGO_MANIFEST_DIR` resolve as they did under `cargo test`.
e2e-build:
if: github.event.pull_request.draft != true
needs: clippy
outputs:
reuse: ${{ needs.clippy.outputs.reuse }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
runs-on: ${{ vars.DISABLE_DEPOT_RUNNERS != 'true' && (matrix.os == 'ubuntu-latest' && 'depot-ubuntu-24.04-16' || matrix.os == 'ubuntu-22.04' && 'depot-ubuntu-22.04-4') || matrix.os }}
timeout-minutes: 45
env: &e2e-build-env
CARGO_PROFILE_DEV_DEBUG: '0'
CARGO_INCREMENTAL: '0'
steps: &e2e-build-steps
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Rust
shell: bash
run: scripts/rustup-retry.sh toolchain install
- name: Cache cargo
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
shared-key: e2e-${{ matrix.os }}
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Compile the CLI and every CLI test target
shell: bash
run: |
set -euo pipefail
cargo test --locked -p socket-patch-cli --all-features --tests --no-run --message-format=json-render-diagnostics > target-build.json
- name: Bundle the binaries the legs run
if: needs.clippy.outputs.reuse != 'true' || matrix.os == 'ubuntu-latest'
shell: bash
env:
BUNDLE_OS: ${{ matrix.os }}
run: python3 scripts/ci-e2e-bundle.py --os "$BUNDLE_OS" --cargo-json target-build.json --dest target/e2e-bin
- name: Compress the e2e binaries
if: needs.clippy.outputs.reuse != 'true' || matrix.os == 'ubuntu-latest'
# Compress the bundle as one stream so identical Rust code across
# test binaries shares a dictionary. Zstd is on every runner image.