Skip to content

Commit 71dfe2d

Browse files
committed
Merge origin/main into arch-refactor/872-registry-timeouts
Take main's jvm_jar.rs and utils/digest.rs: #878 landed the digest helper port this branch had carried, so the branch keeps only its registry_fetch/maven_repo timeout change. Co-Authored-By: Claude <noreply@anthropic.com>
2 parents 28d4d52 + 1c6c509 commit 71dfe2d

949 files changed

Lines changed: 57992 additions & 1070 deletions

File tree

Some content is hidden

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

‎.gitattributes‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,12 @@ crates/socket-patch-cli/tests/fixtures/legacy-ledgers/** -text
3838
# include_str! and written into user repos byte for byte; a CRLF checkout would change it.
3939
crates/socket-patch-core/src/vendor/jvm/socket-patch.settings.gradle -text
4040
crates/socket-patch-core/src/patch/redirect/socket-patch.hosted.settings.gradle -text
41+
42+
# sbt resolution evidence is real sbt output (JSON, Ivy XML and the 0.13
43+
# binary update cache); the evidence parser's tests read it byte for byte.
44+
crates/socket-patch-core/tests/fixtures/sbt/** -text
45+
46+
# The sbt harness scripts run under bash on the Windows legs too
47+
# (sbt-compatibility.yml); a CRLF checkout breaks them ($'\r').
48+
scripts/sbt-warm-seed.sh text eol=lf
49+
scripts/sbt-compat-matrix.sh text eol=lf

‎.github/workflows/ci.yml‎

Lines changed: 111 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -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

‎.github/workflows/composer-compatibility.yml‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,12 @@ jobs:
133133
fi
134134
phar="$dir/composer-$COMPOSER_RELEASE.phar"
135135
base="https://getcomposer.org/download/$COMPOSER_RELEASE/composer.phar"
136-
curl -fsSL --retry 5 --retry-all-errors -o "$phar" "$base"
137-
published="$(curl -fsSL --retry 5 --retry-all-errors "$base.sha256sum" | cut -d' ' -f1)"
136+
# --ssl-revoke-best-effort: Windows curl (schannel) otherwise fails the
137+
# TLS handshake with CRYPT_E_REVOCATION_OFFLINE whenever the CA's
138+
# revocation server is unreachable. A revoked certificate still fails;
139+
# the body is checked against a digest right after. A no-op elsewhere.
140+
curl -fsSL --retry 5 --retry-all-errors --ssl-revoke-best-effort -o "$phar" "$base"
141+
published="$(curl -fsSL --retry 5 --retry-all-errors --ssl-revoke-best-effort "$base.sha256sum" | cut -d' ' -f1)"
138142
# shellcheck disable=SC2016 # $argv is PHP, not shell
139143
actual="$(php -r 'echo hash_file("sha256", $argv[1]);' "$phar")"
140144
if [ "$actual" != "$COMPOSER_PHAR_SHA256" ] || [ "$actual" != "$published" ]; then

‎.github/workflows/gradle-compatibility.yml‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,15 @@ jobs:
241241
env:
242242
MAVEN_VERSION: '3.9.16'
243243
run: |
244+
# --retry-all-errors: plain --retry skips refused connections and TLS
245+
# handshake errors (curl exit 7/35), as in ci.yml's copy of this step.
246+
# --ssl-revoke-best-effort: Windows curl (schannel) otherwise fails the
247+
# TLS handshake with CRYPT_E_REVOCATION_OFFLINE whenever the CA's
248+
# revocation server is unreachable. A revoked certificate still fails;
249+
# the body is checked against a digest right after. A no-op elsewhere.
244250
url="https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz"
245-
curl -fsSL --retry 3 "$url" -o "$RUNNER_TEMP/maven.tgz"
246-
curl -fsSL --retry 3 "$url.sha512" -o "$RUNNER_TEMP/maven.sha512"
251+
curl -fsSL --retry 5 --retry-all-errors --ssl-revoke-best-effort "$url" -o "$RUNNER_TEMP/maven.tgz"
252+
curl -fsSL --retry 5 --retry-all-errors --ssl-revoke-best-effort "$url.sha512" -o "$RUNNER_TEMP/maven.sha512"
247253
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]'
248254
python -m tarfile -e "$RUNNER_TEMP/maven.tgz" "$RUNNER_TEMP"
249255
launcher="$RUNNER_TEMP/apache-maven-${MAVEN_VERSION}/bin/mvn"
@@ -259,9 +265,10 @@ jobs:
259265
env:
260266
GRADLE_VERSION: ${{ matrix.gradle }}
261267
run: |
268+
# Download flags: see the Maven step above.
262269
url="https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip"
263-
curl -fsSL --retry 3 "$url" -o "$RUNNER_TEMP/gradle.zip"
264-
curl -fsSL --retry 3 "$url.sha256" -o "$RUNNER_TEMP/gradle.sha256"
270+
curl -fsSL --retry 5 --retry-all-errors --ssl-revoke-best-effort "$url" -o "$RUNNER_TEMP/gradle.zip"
271+
curl -fsSL --retry 5 --retry-all-errors --ssl-revoke-best-effort "$url.sha256" -o "$RUNNER_TEMP/gradle.sha256"
265272
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()'
266273
unzip -q "$RUNNER_TEMP/gradle.zip" -d "$RUNNER_TEMP"
267274
launcher="$RUNNER_TEMP/gradle-${GRADLE_VERSION}/bin/gradle"

‎.github/workflows/npm-compatibility.yml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ on:
3636
permissions:
3737
contents: read
3838

39+
# A newer push to the same PR supersedes the older run. Every other event
40+
# gets its own group (run_id), so no main push is cancelled, even while
41+
# pending behind another.
42+
concurrency:
43+
group: npm-compat-${{ github.event.pull_request.number || github.run_id }}
44+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
45+
3946
jobs:
4047
build:
4148
runs-on: ubuntu-latest

‎.github/workflows/pipenv-compatibility.yml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ on:
3434
permissions:
3535
contents: read
3636

37+
# A newer push to the same PR supersedes the older run. Every other event
38+
# gets its own group (run_id), so no main push is cancelled, even while
39+
# pending behind another.
40+
concurrency:
41+
group: pipenv-compat-${{ github.event.pull_request.number || github.run_id }}
42+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
43+
3744
jobs:
3845
matrix:
3946
strategy:

0 commit comments

Comments
 (0)