@@ -240,6 +240,8 @@ jobs:
240240 os : [macos-latest, windows-latest]
241241 runs-on : ${{ matrix.os }}
242242 timeout-minutes : 50
243+ env :
244+ VEXCTL_VERSION : v0.3.0
243245 steps :
244246 - name : Checkout
245247 uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -264,6 +266,7 @@ jobs:
264266 run : cargo build --workspace
265267
266268 - name : Install Go (for vexctl)
269+ id : go
267270 # The `vex` subcommand emits OpenVEX documents; tests/e2e_vex.rs
268271 # validates the output with vexctl when it's on PATH. vexctl is
269272 # a Go binary distributed via `go install`. Setting up Go here
@@ -279,6 +282,19 @@ jobs:
279282 go-version : ' 1.24'
280283 cache : false
281284
285+ - name : Restore vexctl (macOS)
286+ # The macOS compile below took a median 110s (max 168s) of every
287+ # macOS `test` job and is the step's only network flake source
288+ # (sum.golang.org resets). Its output depends only on the vexctl
289+ # version, the Go toolchain and the runner, so it is cached under
290+ # exactly those. Saved from main only, like the cargo cache.
291+ id : vexctl-cache
292+ if : runner.os == 'macOS'
293+ uses : actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0
294+ with :
295+ path : ${{ runner.temp }}/vexctl-bin
296+ key : vexctl-${{ env.VEXCTL_VERSION }}-go${{ steps.go.outputs.go-version }}-${{ runner.os }}-${{ runner.arch }}
297+
282298 - name : Install vexctl
283299 # Linux / Windows: the v0.3.0 release binary, checked against the
284300 # sha256 pinned here (from the release's vexctl_checksums.txt).
@@ -292,45 +308,64 @@ jobs:
292308 # sum.golang.org checksum tiles, and transient INTERNAL_ERROR
293309 # stream resets there have failed this step on otherwise-green
294310 # runs. The backoff rides out short resets; a persistent outage
295- # still fails loudly on the last attempt.
311+ # still fails loudly on the last attempt. A binary restored by the
312+ # step above skips the compile.
313+ #
314+ # --ssl-revoke-best-effort: Windows curl (schannel) otherwise fails
315+ # the TLS handshake with CRYPT_E_REVOCATION_OFFLINE whenever the
316+ # CA's revocation server is unreachable. A revoked certificate still
317+ # fails, and the sha256 check follows. A no-op on other OSes.
296318 #
297319 # Either way the binary's directory goes on PATH so
298320 # `Command::new("vexctl")` in the tests resolves.
299321 shell : bash
300322 env :
301- VEXCTL_VERSION : v0.3.0
302323 VEXCTL_SHA256_LINUX_AMD64 : cd7f8b57d20642166ed4eb3dd1fd849bbb30bbd7c5b9f5b0316b6003b57ceaba
303324 VEXCTL_SHA256_WINDOWS_AMD64 : 346fb3104b656fe1a407cbae88ea29a636b36f4569ec58a5a8dadca7343993ed
325+ CACHE_HIT : ${{ steps.vexctl-cache.outputs.cache-hit }}
304326 run : |
305327 set -euo pipefail
328+ dir="$RUNNER_TEMP/vexctl-bin"
329+ mkdir -p "$dir"
306330 case "$RUNNER_OS" in
307331 Linux) asset=vexctl-linux-amd64 bin=vexctl sha="$VEXCTL_SHA256_LINUX_AMD64" ;;
308332 Windows) asset=vexctl-windows-amd64.exe bin=vexctl.exe sha="$VEXCTL_SHA256_WINDOWS_AMD64" ;;
309333 *) asset='' ;;
310334 esac
311335 if [ -n "$asset" ]; then
312- dir="$RUNNER_TEMP/vexctl-bin"
313- mkdir -p "$dir"
314- curl -fsSL --retry 5 --retry-all-errors -o "$dir/$bin" \
336+ curl -fsSL --retry 5 --retry-all-errors --ssl-revoke-best-effort -o "$dir/$bin" \
315337 "https://github.com/openvex/vexctl/releases/download/$VEXCTL_VERSION/$asset"
316338 echo "$sha $dir/$bin" | sha256sum -c -
317339 chmod +x "$dir/$bin"
318340 "$dir/$bin" version
319341 echo "$dir" >> "$GITHUB_PATH"
320342 exit 0
321343 fi
322- for attempt in 1 2 3 4 5; do
323- if go install "github.com/openvex/vexctl@$VEXCTL_VERSION"; then
324- break
325- fi
326- if [ "$attempt" = 5 ]; then
327- echo "::error::go install vexctl failed on all 5 attempts"
328- exit 1
329- fi
330- echo "::warning::go install vexctl attempt $attempt failed; retrying"
331- sleep $((attempt * 20))
332- done
333- echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
344+ if [ "$CACHE_HIT" != true ]; then
345+ for attempt in 1 2 3 4 5; do
346+ if GOBIN="$dir" go install "github.com/openvex/vexctl@$VEXCTL_VERSION"; then
347+ break
348+ fi
349+ if [ "$attempt" = 5 ]; then
350+ echo "::error::go install vexctl failed on all 5 attempts"
351+ exit 1
352+ fi
353+ echo "::warning::go install vexctl attempt $attempt failed; retrying"
354+ sleep $((attempt * 20))
355+ done
356+ fi
357+ "$dir/vexctl" version
358+ echo "$dir" >> "$GITHUB_PATH"
359+
360+ - name : Save vexctl (macOS)
361+ if : >-
362+ runner.os == 'macOS'
363+ && github.ref == 'refs/heads/main'
364+ && steps.vexctl-cache.outputs.cache-hit != 'true'
365+ uses : actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0
366+ with :
367+ path : ${{ runner.temp }}/vexctl-bin
368+ key : ${{ steps.vexctl-cache.outputs.cache-primary-key }}
334369
335370 - name : Run tests
336371 # Default features only: `--all-features` would also RUN the
@@ -557,13 +592,14 @@ jobs:
557592 # the highest base that's forward-compatible with debian:12.
558593 needs : docker-base
559594 runs-on : ubuntu-22.04
560- timeout-minutes : 30
595+ # sbt's image bakes three JDKs and warm sbt / Mill / scala-cli caches.
596+ timeout-minutes : ${{ matrix.ecosystem == 'sbt' && 45 || 30 }}
561597 permissions :
562598 contents : read
563599 strategy :
564600 fail-fast : false
565601 matrix :
566- ecosystem : [npm, pypi, gem, cargo, golang, maven, composer, nuget, deno]
602+ ecosystem : [npm, pypi, gem, cargo, golang, maven, composer, nuget, deno, sbt ]
567603 steps :
568604 - name : Checkout
569605 uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -643,18 +679,29 @@ jobs:
643679 # Vendor build-proof capstones ride the same image as their
644680 # ecosystem's main suite (extend the case as new vendor suites land).
645681 EXTRA=""
682+ # libtest arguments after `--`.
683+ FILTER=""
646684 case "${{ matrix.ecosystem }}" in
647685 composer) EXTRA="--test docker_e2e_vendor_composer" ;;
648686 gem) EXTRA="--test docker_e2e_vendor_gem" ;;
649687 maven) EXTRA="--test docker_e2e_vendor_maven" ;;
650688 nuget) EXTRA="--test docker_e2e_vendor_nuget" ;;
651689 pypi) EXTRA="--test docker_e2e_vendor_pypi_pm" ;;
690+ # The blocking sbt slice: the agent cells on 1.2.8 (Ivy) and
691+ # 1.13.0 (Coursier, plus the `useCoursier := false` Ivy cell).
692+ # Mill, scala-cli and the other sbt lines run nightly in
693+ # e2e-docker and in sbt-compatibility.yml.
694+ sbt)
695+ export SOCKET_PATCH_SBT_DOCKER_VERSIONS="1.2.8 1.13.0"
696+ export SOCKET_PATCH_DOCKER_E2E_REQUIRED=1
697+ FILTER="agent_sbt_ --test-threads=1" ;;
652698 esac
653- # shellcheck disable=SC2086 # EXTRA is intentionally word-split
699+ # shellcheck disable=SC2086 # EXTRA and FILTER are intentionally word-split
654700 cargo llvm-cov \
655701 --features docker-e2e \
656702 --no-report \
657- --test docker_e2e_${{ matrix.ecosystem }} $EXTRA
703+ --test docker_e2e_${{ matrix.ecosystem }} $EXTRA \
704+ -- $FILTER
658705
659706 - name : Generate per-ecosystem lcov
660707 run : |
@@ -1179,6 +1226,12 @@ jobs:
11791226 - {os: ubuntu-latest, suite: 'e2e_gradle_discovery_build e2e_gradle_agent_build e2e_redirect_gradle_build', jvm_tool: gradle, gradle: '9.8.0', java: '21', test_filter: '--ignored gradle_agent_ gradle_hosted_'}
11801227 - {os: ubuntu-latest, suite: 'e2e_vendor_gradle_build e2e_vendor_jvm_build', jvm_tool: gradle, gradle: '9.8.0', java: '21', test_filter: '--ignored gradle_vendor_ gradle_multi_project'}
11811228 - {os: windows-latest, suite: e2e_vendor_jvm_build, jvm_tool: gradle, gradle: '8.14.3', java: '17', test_filter: '--ignored gradle_multi_project'}
1229+ # Real-sbt hosted (socket-patch.sbt) + vendored
1230+ # (socket-patch-vendor.sbt) capstones on the current 1.x line. The
1231+ # other sbt lines, JDK 21, the agent cells beyond coverage-docker's,
1232+ # Mill, scala-cli and macOS / Windows are sbt-compatibility.yml.
1233+ - {os: ubuntu-latest, suite: e2e_sbt_build, jvm_tool: sbt, sbt: '1.13.0', test_filter: '--ignored --test-threads=1'}
1234+ - {os: ubuntu-latest, suite: e2e_sbt_vendor_build, jvm_tool: sbt, sbt: '1.13.0', test_filter: '--ignored --test-threads=1'}
11821235 # Real .NET SDK capstones: hosted + vendored nuget, one leg per SDK
11831236 # major (the suite pins the major through a sandbox global.json):
11841237 # the oldest and newest here, 7-9 on ubuntu in e2e-full.
@@ -1395,18 +1448,38 @@ jobs:
13951448 distribution : temurin
13961449 java-version : ${{ matrix.java || '17' }}
13971450
1451+ - name : Setup sbt (sbt legs)
1452+ if : matrix.sbt != ''
1453+ # The sbt launcher script; it boots whatever `sbt.version` each
1454+ # fixture's project/build.properties pins.
1455+ uses : sbt/setup-sbt@6158cb0903b8ceeae04f830055f3155e1b6a5ad7 # v1.5.11
1456+ with :
1457+ sbt-runner-version : 1.13.0
1458+ disk-cache : false
1459+
1460+ - name : Warm the sbt seed (sbt legs)
1461+ if : matrix.sbt != ''
1462+ # Boots the pinned sbt once into a seed the suites hard-link into
1463+ # each test's private caches (SOCKET_PATCH_SBT_E2E_SEED), and names
1464+ # the launcher (SOCKET_PATCH_SBT_E2E_SBT).
1465+ shell : bash
1466+ env :
1467+ SBT_TEST_VERSION : ${{ matrix.sbt }}
1468+ run : scripts/sbt-warm-seed.sh "$SBT_TEST_VERSION" "$RUNNER_TEMP/sbt-seed" >> "$GITHUB_ENV"
1469+
13981470 - name : Install Maven ${{ matrix.maven || '3.9.16' }}
13991471 if : steps.jvm.outputs.maven == 'true'
14001472 # Straight from the Apache archive (sha512-verified), so a leg gets
14011473 # exactly the release it names rather than the runner's Maven.
1474+ # --ssl-revoke-best-effort: see the `test` job's vexctl step.
14021475 shell : bash
14031476 env :
14041477 MAVEN_VERSION : ${{ matrix.maven || '3.9.16' }}
14051478 run : |
14061479 major="${MAVEN_VERSION%%.*}"
14071480 url="https://archive.apache.org/dist/maven/maven-${major}/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz"
1408- curl -fsSL --retry 5 --retry-all-errors "$url" -o "$RUNNER_TEMP/maven.tgz"
1409- curl -fsSL --retry 5 --retry-all-errors "$url.sha512" -o "$RUNNER_TEMP/maven.sha512"
1481+ curl -fsSL --retry 5 --retry-all-errors --ssl-revoke-best-effort "$url" -o "$RUNNER_TEMP/maven.tgz"
1482+ curl -fsSL --retry 5 --retry-all-errors --ssl-revoke-best-effort "$url.sha512" -o "$RUNNER_TEMP/maven.sha512"
14101483 python -c 'import hashlib, pathlib, os; p=pathlib.Path(os.environ["RUNNER_TEMP"]); assert hashlib.sha512((p/"maven.tgz").read_bytes()).hexdigest() == (p/"maven.sha512").read_text().split()[0]'
14111484 # Python accepts native Windows paths for both archive and destination.
14121485 python -m tarfile -e "$RUNNER_TEMP/maven.tgz" "$RUNNER_TEMP"
@@ -1421,8 +1494,8 @@ jobs:
14211494 GRADLE_VERSION : ${{ matrix.gradle }}
14221495 run : |
14231496 url="https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip"
1424- curl -fsSL --retry 5 --retry-all-errors "$url" -o "$RUNNER_TEMP/gradle.zip"
1425- curl -fsSL --retry 5 --retry-all-errors "$url.sha256" -o "$RUNNER_TEMP/gradle.sha256"
1497+ curl -fsSL --retry 5 --retry-all-errors --ssl-revoke-best-effort "$url" -o "$RUNNER_TEMP/gradle.zip"
1498+ curl -fsSL --retry 5 --retry-all-errors --ssl-revoke-best-effort "$url.sha256" -o "$RUNNER_TEMP/gradle.sha256"
14261499 python -c 'import hashlib, pathlib, os; p=pathlib.Path(os.environ["RUNNER_TEMP"]); assert hashlib.sha256((p/"gradle.zip").read_bytes()).hexdigest() == (p/"gradle.sha256").read_text().strip()'
14271500 unzip -q "$RUNNER_TEMP/gradle.zip" -d "$RUNNER_TEMP"
14281501 launcher="$RUNNER_TEMP/gradle-${GRADLE_VERSION}/bin/gradle"
@@ -1540,6 +1613,8 @@ jobs:
15401613 SOCKET_PATCH_DOTNET_E2E_VERSION : ${{ matrix.dotnet }}
15411614 SOCKET_PATCH_DENO_E2E_REQUIRED : ${{ matrix.deno != '' && '1' || '' }}
15421615 SOCKET_PATCH_DENO_E2E_VERSION : ${{ matrix.deno }}
1616+ SOCKET_PATCH_SBT_E2E_REQUIRED : ${{ matrix.sbt != '' && '1' || '' }}
1617+ SOCKET_PATCH_SBT_E2E_VERSION : ${{ matrix.sbt }}
15431618 E2E_SUITE : ${{ matrix.suite }}
15441619 E2E_TEST_FILTER : ${{ matrix.test_filter || '--ignored' }}
15451620 E2E_JVM_TOOL : ${{ matrix.jvm_tool }}
@@ -1686,13 +1761,13 @@ jobs:
16861761 if : github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.head_ref == 'release/v5-prerelease'
16871762 needs : docker-base
16881763 runs-on : ubuntu-latest
1689- timeout-minutes : 35
1764+ timeout-minutes : ${{ matrix.ecosystem == 'sbt' && 45 || 35 }}
16901765 permissions :
16911766 contents : read
16921767 strategy :
16931768 fail-fast : false
16941769 matrix :
1695- ecosystem : [npm, pypi, gem, cargo, golang, maven, composer, nuget, deno]
1770+ ecosystem : [npm, pypi, gem, cargo, golang, maven, composer, nuget, deno, sbt ]
16961771 steps :
16971772 - name : Checkout
16981773 uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -1741,13 +1816,20 @@ jobs:
17411816 # run only in coverage-docker.
17421817 run : |
17431818 EXTRA=""
1819+ FILTER=""
17441820 case "${{ matrix.ecosystem }}" in
17451821 composer) EXTRA="--test docker_e2e_vendor_composer" ;;
17461822 nuget) EXTRA="--test docker_e2e_vendor_nuget" ;;
17471823 pypi) EXTRA="--test docker_e2e_vendor_pypi_pm" ;;
1824+ # Every sbt / Mill / scala-cli agent cell (the full sbt version
1825+ # x mode matrix is sbt-compatibility.yml).
1826+ sbt)
1827+ export SOCKET_PATCH_SBT_DOCKER_VERSIONS="1.2.8 1.13.0"
1828+ export SOCKET_PATCH_DOCKER_E2E_REQUIRED=1
1829+ FILTER="--test-threads=1" ;;
17481830 esac
1749- # shellcheck disable=SC2086 # EXTRA is intentionally word-split
1750- cargo test -p socket-patch-cli --features docker-e2e --test "docker_e2e_${{ matrix.ecosystem }}" $EXTRA
1831+ # shellcheck disable=SC2086 # EXTRA and FILTER are intentionally word-split
1832+ cargo test -p socket-patch-cli --features docker-e2e --test "docker_e2e_${{ matrix.ecosystem }}" $EXTRA -- $FILTER
17511833
17521834 # ----------------------------------------------------------------------
17531835 # Per-release real-toolchain matrices for the manifest-less VEX work that
0 commit comments