Skip to content

Commit d381e29

Browse files
committed
Merge main into release/v5-prerelease
2 parents 180f10f + f6b7fb9 commit d381e29

140 files changed

Lines changed: 9175 additions & 571 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.

‎.github/workflows/ci.yml‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,7 @@ jobs:
224224
matrix:
225225
os: [ubuntu-latest, macos-latest, windows-latest]
226226
runs-on: ${{ matrix.os }}
227-
# Windows runs the same suite ~1.6x slower than macOS: on the base
228-
# branch it already took 34m40s of a flat 35m budget.
229-
timeout-minutes: ${{ matrix.os == 'windows-latest' && 50 || 35 }}
227+
timeout-minutes: 50
230228
steps:
231229
- name: Checkout
232230
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -767,9 +765,12 @@ jobs:
767765
# coverage.
768766
#
769767
# composer: 1 (packagist stopped serving composer 1 on 2025-09-01,
770-
# so the fixture resolves from an inline repository), 2.2 LTS (the
771-
# other release with the git-source download fallback the hosted
772-
# redirect must drop) and current 2.
768+
# so the fixture resolves from an inline repository), 2.2 LTS and
769+
# current 2. Every release from 1.x through 2.9 falls back to the
770+
# git `source` when the dist download fails (the fallback the
771+
# hosted redirect must drop); 2.10 does not. The exact-release
772+
# matrix, 2.9.8 and macOS/Windows included, is
773+
# composer-compatibility.yml.
773774
- {os: ubuntu-latest, suite: e2e_vendor_composer_build, composer: '2'}
774775
- {os: ubuntu-latest, suite: e2e_vendor_composer_build, composer: '2.2'}
775776
- {os: ubuntu-latest, suite: e2e_vendor_composer_build, composer: '1'}
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
name: Composer patch compatibility
2+
3+
# Exact Composer releases on ubuntu, windows and macOS through the real
4+
# vendored (`e2e_vendor_composer_build`) and hosted
5+
# (`e2e_redirect_composer_build`) capstones, each ending in the
6+
# manifest-less VEX step, plus the hermetic `composer::` VEX cells.
7+
#
8+
# Release boundaries: 1.10.28 = Composer 1 (inline-repository fixture,
9+
# packagist dropped Composer 1 metadata); 2.0.14 / 2.1.14 = the 2.x lines
10+
# whose path mirror still drops `.gitignore`d files; 2.2.30 = LTS;
11+
# 2.5.8 / 2.8.12 = mid 2.x; 2.9.8 = the last release whose dist failure
12+
# falls back to `source`, so the hosted redirect's source drop is load
13+
# bearing there; 2.10.3 = current, no fallback.
14+
#
15+
# PHP: Composer 1 never on 8.5 (broken there). No 7.4 cell: the psr/log
16+
# 3.0.2 capstones need PHP >= 8.0; the depscan harness covers 7.4.
17+
#
18+
# Each leg runs one checksum-pinned composer.phar through `php` (the
19+
# capstones' SOCKET_PATCH_COMPOSER_PHAR mode), which is also what makes
20+
# Windows work: `Command::new("composer")` cannot resolve `composer.bat`.
21+
22+
on:
23+
pull_request:
24+
paths:
25+
- '.github/workflows/composer-compatibility.yml'
26+
- 'tests/docker/Dockerfile.composer'
27+
- 'Cargo.lock'
28+
- 'Cargo.toml'
29+
- 'crates/*/Cargo.toml'
30+
- 'crates/socket-patch-core/src/vendor/**'
31+
- 'crates/socket-patch-core/src/patch/**'
32+
- 'crates/socket-patch-core/src/formats/composer/**'
33+
- 'crates/socket-patch-core/src/formats/registry.rs'
34+
- 'crates/socket-patch-core/src/hosted/**'
35+
- 'crates/socket-patch-core/src/ledgers.rs'
36+
- 'crates/socket-patch-core/src/policy/**'
37+
- 'crates/socket-patch-core/src/rollout.rs'
38+
- 'crates/socket-patch-core/src/rollout/**'
39+
- 'crates/socket-patch-core/src/manifest/**'
40+
- 'crates/socket-patch-core/tests/fixtures/redirect/composer/**'
41+
- 'crates/socket-patch-core/tests/fixtures/composer-version-vectors.json'
42+
- 'crates/socket-patch-core/src/crawlers/composer_crawler.rs'
43+
- 'crates/socket-patch-core/src/crawlers/composer_crawler/**'
44+
- 'crates/socket-patch-core/src/utils/composer*.rs'
45+
- 'crates/socket-patch-core/src/utils/purl.rs'
46+
- 'crates/socket-patch-core/src/utils/group_commit.rs'
47+
- 'crates/socket-patch-core/src/utils/durability.rs'
48+
- 'crates/socket-patch-core/src/vex/**'
49+
- 'crates/socket-patch-cli/src/commands/vendor*'
50+
- 'crates/socket-patch-cli/src/commands/vendored_backend/**'
51+
- 'crates/socket-patch-cli/src/commands/get*.rs'
52+
- 'crates/socket-patch-cli/src/commands/apply.rs'
53+
- 'crates/socket-patch-cli/src/commands/rollback.rs'
54+
- 'crates/socket-patch-cli/src/commands/remove.rs'
55+
- 'crates/socket-patch-cli/src/commands/composer_hints.rs'
56+
- 'crates/socket-patch-cli/src/commands/scan/**'
57+
- 'crates/socket-patch-cli/src/commands/vex*.rs'
58+
- 'crates/socket-patch-cli/tests/e2e_*composer*.rs'
59+
- 'crates/socket-patch-cli/tests/docker_e2e_vendor_composer.rs'
60+
- 'crates/socket-patch-cli/tests/composer_e2e_common/**'
61+
- 'crates/socket-patch-cli/tests/docker_vendor_common/**'
62+
- 'crates/socket-patch-cli/tests/e2e_vex_lockfile/composer.rs'
63+
- 'crates/socket-patch-cli/tests/vex_e2e_common/**'
64+
push:
65+
branches: [main]
66+
workflow_dispatch:
67+
68+
permissions:
69+
contents: read
70+
71+
concurrency:
72+
group: composer-compat-${{ github.event.pull_request.number || github.ref }}
73+
cancel-in-progress: true
74+
75+
env:
76+
SOCKET_NO_CONFIG: '1'
77+
SOCKET_NO_UPDATE_CHECK: '1'
78+
79+
jobs:
80+
native:
81+
name: composer ${{ matrix.composer }} / php ${{ matrix.php }} / ${{ matrix.os }}
82+
strategy:
83+
fail-fast: false
84+
matrix:
85+
include:
86+
- {os: ubuntu-latest, composer: '1.10.28', php: '8.1', sha256: 0915af36eb01e3f0e16cd309adff7051832b9ef014e38371756804b20425cd5a}
87+
- {os: ubuntu-latest, composer: '2.0.14', php: '8.0', sha256: 29454b41558968ca634bf5e2d4d07ff2275d91b637a76d7a05e6747d36dd3473}
88+
- {os: ubuntu-latest, composer: '2.1.14', php: '8.1', sha256: d44a904520f9aaa766e8b4b05d2d9a766ad9a6f03fa1a48518224aad703061a4}
89+
- {os: ubuntu-latest, composer: '2.2.30', php: '8.1', sha256: 8c2b4478b64f8f7cdf1574838fdb0033b29049ca821dad452db7a3dcfcdbffc2}
90+
- {os: ubuntu-latest, composer: '2.2.30', php: '8.3', sha256: 8c2b4478b64f8f7cdf1574838fdb0033b29049ca821dad452db7a3dcfcdbffc2}
91+
- {os: ubuntu-latest, composer: '2.5.8', php: '8.2', sha256: f07934fad44f9048c0dc875a506cca31cc2794d6aebfc1867f3b1fbf48dce2c5}
92+
- {os: ubuntu-latest, composer: '2.8.12', php: '8.4', sha256: f446ea719708bb85fcbf4ef18def5d0515f1f9b4d703f6d820c9c1656e10a2f2}
93+
- {os: ubuntu-latest, composer: '2.9.8', php: '8.4', sha256: 59b2c50e10cafa0d8efc19ede9a326d782f096c674a26baf98cf042ce23de890}
94+
- {os: ubuntu-latest, composer: '2.10.3', php: '8.5', sha256: 7a2d379d5b8ffdaa028580ef26494c36d2feef4b178d3dd1473a4dbc5e17c8d6}
95+
- {os: windows-latest, composer: '1.10.28', php: '8.1', sha256: 0915af36eb01e3f0e16cd309adff7051832b9ef014e38371756804b20425cd5a}
96+
- {os: windows-latest, composer: '2.2.30', php: '8.3', sha256: 8c2b4478b64f8f7cdf1574838fdb0033b29049ca821dad452db7a3dcfcdbffc2}
97+
- {os: windows-latest, composer: '2.9.8', php: '8.4', sha256: 59b2c50e10cafa0d8efc19ede9a326d782f096c674a26baf98cf042ce23de890}
98+
- {os: windows-latest, composer: '2.10.3', php: '8.5', sha256: 7a2d379d5b8ffdaa028580ef26494c36d2feef4b178d3dd1473a4dbc5e17c8d6}
99+
- {os: macos-latest, composer: '1.10.28', php: '8.1', sha256: 0915af36eb01e3f0e16cd309adff7051832b9ef014e38371756804b20425cd5a}
100+
- {os: macos-latest, composer: '2.2.30', php: '8.3', sha256: 8c2b4478b64f8f7cdf1574838fdb0033b29049ca821dad452db7a3dcfcdbffc2}
101+
- {os: macos-latest, composer: '2.10.3', php: '8.5', sha256: 7a2d379d5b8ffdaa028580ef26494c36d2feef4b178d3dd1473a4dbc5e17c8d6}
102+
runs-on: ${{ matrix.os }}
103+
timeout-minutes: 60
104+
steps:
105+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
106+
with:
107+
persist-credentials: false
108+
- run: rustup show
109+
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
110+
with:
111+
key: composer-compat
112+
save-if: ${{ github.ref == 'refs/heads/main' }}
113+
- uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2
114+
with:
115+
php-version: ${{ matrix.php }}
116+
tools: none
117+
extensions: zip, mbstring, curl, openssl
118+
ini-values: memory_limit=-1
119+
coverage: none
120+
- name: Download and verify composer ${{ matrix.composer }}
121+
shell: bash
122+
env:
123+
COMPOSER_RELEASE: ${{ matrix.composer }}
124+
COMPOSER_PHAR_SHA256: ${{ matrix.sha256 }}
125+
# The pinned digest is the contract; the published .sha256sum is a
126+
# second, independent check. php computes the digest so the step
127+
# needs no sha256sum/shasum on any runner image.
128+
run: |
129+
set -euo pipefail
130+
dir="$RUNNER_TEMP"
131+
if command -v cygpath >/dev/null 2>&1; then
132+
dir="$(cygpath -m "$RUNNER_TEMP")"
133+
fi
134+
phar="$dir/composer-$COMPOSER_RELEASE.phar"
135+
base="https://getcomposer.org/download/$COMPOSER_RELEASE/composer.phar"
136+
curl -fsSL --retry 3 -o "$phar" "$base"
137+
published="$(curl -fsSL --retry 3 "$base.sha256sum" | cut -d' ' -f1)"
138+
# shellcheck disable=SC2016 # $argv is PHP, not shell
139+
actual="$(php -r 'echo hash_file("sha256", $argv[1]);' "$phar")"
140+
if [ "$actual" != "$COMPOSER_PHAR_SHA256" ] || [ "$actual" != "$published" ]; then
141+
echo "::error::composer $COMPOSER_RELEASE phar sha256 $actual (pinned $COMPOSER_PHAR_SHA256, published $published)"
142+
exit 1
143+
fi
144+
reported="$(php "$phar" --version --no-ansi)"
145+
echo "$reported"
146+
case "$reported" in
147+
*"Composer version $COMPOSER_RELEASE "*) ;;
148+
*) echo "::error::expected composer $COMPOSER_RELEASE"; exit 1 ;;
149+
esac
150+
echo "SOCKET_PATCH_COMPOSER_PHAR=$phar" >> "$GITHUB_ENV"
151+
- name: Real-composer vendored + hosted flows with manifest-less VEX
152+
shell: bash
153+
env:
154+
SOCKET_PATCH_COMPOSER_E2E_REQUIRED: '1'
155+
SOCKET_PATCH_COMPOSER_E2E_VERSION: ${{ matrix.composer }}
156+
# The build capstones are `#[ignore]`-gated (the unpinned `test` job
157+
# skips them). The hermetic `composer::` VEX cells are a module of
158+
# the shared `e2e_vex_lockfile` binary; their filter goes in a second
159+
# command so it does not also filter the build suites.
160+
run: |
161+
cargo test -p socket-patch-cli --no-fail-fast \
162+
--test e2e_vendor_composer_build --test e2e_redirect_composer_build \
163+
-- --ignored --nocapture
164+
cargo test -p socket-patch-cli --test e2e_vex_lockfile -- composer:: --nocapture
165+
166+
docker:
167+
# The vendored Docker capstone against an exact composer image. Its
168+
# fixture resolves psr/log from packagist, which no longer serves
169+
# Composer 1, so 1.10.28 is covered by the native legs only.
170+
name: docker composer ${{ matrix.composer }}
171+
runs-on: ubuntu-latest
172+
timeout-minutes: 35
173+
strategy:
174+
fail-fast: false
175+
matrix:
176+
include:
177+
- {composer: '2.2.30', sha256: 8c2b4478b64f8f7cdf1574838fdb0033b29049ca821dad452db7a3dcfcdbffc2}
178+
- {composer: '2.10.3', sha256: 7a2d379d5b8ffdaa028580ef26494c36d2feef4b178d3dd1473a4dbc5e17c8d6}
179+
steps:
180+
- name: Checkout
181+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
182+
with:
183+
persist-credentials: false
184+
185+
- name: Set up Docker Buildx
186+
# `driver: docker` so the composer image's
187+
# `FROM socket-patch-test-base:latest` resolves against the host
188+
# daemon (as in ci.yml's e2e-docker job).
189+
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
190+
with:
191+
driver: docker
192+
193+
- name: Install Rust
194+
run: rustup show
195+
196+
# No `actions/cache`: this job builds Docker images (zizmor
197+
# cache-poisoning audit), as in ci.yml's e2e-docker job.
198+
199+
- name: Build base image
200+
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
201+
with:
202+
context: .
203+
file: tests/docker/Dockerfile.base
204+
tags: socket-patch-test-base:latest
205+
load: true
206+
207+
- name: Build composer ${{ matrix.composer }} image
208+
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
209+
with:
210+
context: .
211+
file: tests/docker/Dockerfile.composer
212+
build-args: |
213+
COMPOSER_VERSION=${{ matrix.composer }}
214+
COMPOSER_SHA256=${{ matrix.sha256 }}
215+
tags: socket-patch-test-composer:latest
216+
load: true
217+
218+
- name: Run the vendored composer Docker capstone
219+
env:
220+
SOCKET_PATCH_DOCKER_E2E_REQUIRED: '1'
221+
run: cargo test -p socket-patch-cli --features docker-e2e --test docker_e2e_vendor_composer

‎CHANGELOG.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,22 @@ into the new version's section — see docs/releasing.md.
13061306
- **Hosted `scan` resolves more than 500 patches.** The package-reference
13071307
request is sent in chunks of 500 uuids, the endpoint's limit; a larger
13081308
scan used to fail with a 400.
1309+
- **Hosted nuget redirects survive a `<clear />` in `nuget.config`.** The
1310+
Socket source (and, in an existing `<packageSourceMapping>`, its
1311+
mapping) was inserted ahead of the section's `<clear />`, which NuGet
1312+
applies to everything read before it: `dotnet restore` then failed
1313+
NU1100 / NU1101 for the patched package. Both now land after the last
1314+
`<clear />`.
1315+
1316+
- **nuget redirects and vendoring edit the config NuGet actually reads.**
1317+
NuGet reads the first of `nuget.config`, `NuGet.config` and
1318+
`NuGet.Config` in a directory. Hosted mode only knew `nuget.config`
1319+
and vendored mode missed `NuGet.config`, so on a case-sensitive
1320+
filesystem they created a fresh `nuget.config` that shadowed the
1321+
project's own file: its sources and mappings vanished and private
1322+
packages failed restore. Both modes now edit the existing spelling in
1323+
place.
1324+
13091325
- **`rollback` fetches a before-blob that only a store peer variant
13101326
needs.** The before-blob gate now probes every pnpm and vlt store variant
13111327
copy the rollback restores, so an online rollback no longer fails

0 commit comments

Comments
 (0)