Skip to content

Commit fb22dfa

Browse files
mikolalysenkoclaude
andcommitted
Run Composer compatibility on every related change
The composer compatibility workflow now also runs for vendor, patch, manifest, lockfile and command changes. Docker composer tests fail instead of skipping when SOCKET_PATCH_DOCKER_E2E_REQUIRED=1, and the source-fallback test disables autocrlf so Windows compares LF bytes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 2ce588a commit fb22dfa

4 files changed

Lines changed: 55 additions & 15 deletions

File tree

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,23 @@ on:
2424
paths:
2525
- '.github/workflows/composer-compatibility.yml'
2626
- 'tests/docker/Dockerfile.composer'
27-
- 'crates/socket-patch-core/src/vendor/composer_lock.rs'
28-
- 'crates/socket-patch-core/src/vendor/composer_lock/**'
29-
- 'crates/socket-patch-core/src/vendor/lock_inventory/composer.rs'
30-
- 'crates/socket-patch-core/src/patch/redirect/**'
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/manifest/**'
33+
- 'crates/socket-patch-core/tests/fixtures/redirect/composer/**'
34+
- 'crates/socket-patch-core/tests/fixtures/composer-version-vectors.json'
3135
- 'crates/socket-patch-core/src/crawlers/composer_crawler.rs'
3236
- 'crates/socket-patch-core/src/utils/composer*.rs'
3337
- 'crates/socket-patch-core/src/utils/purl.rs'
3438
- 'crates/socket-patch-core/src/vex/**'
3539
- 'crates/socket-patch-cli/src/commands/vendor*'
40+
- 'crates/socket-patch-cli/src/commands/get*.rs'
41+
- 'crates/socket-patch-cli/src/commands/apply.rs'
42+
- 'crates/socket-patch-cli/src/commands/rollback.rs'
43+
- 'crates/socket-patch-cli/src/commands/remove.rs'
3644
- 'crates/socket-patch-cli/src/commands/composer_hints.rs'
3745
- 'crates/socket-patch-cli/src/commands/scan/**'
3846
- 'crates/socket-patch-cli/src/commands/vex*.rs'
@@ -197,4 +205,6 @@ jobs:
197205
load: true
198206

199207
- name: Run the vendored composer Docker capstone
208+
env:
209+
SOCKET_PATCH_DOCKER_E2E_REQUIRED: '1'
200210
run: cargo test -p socket-patch-cli --features docker-e2e --test docker_e2e_vendor_composer

‎crates/socket-patch-cli/tests/composer_e2e_common/mod.rs‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@ pub fn composer_major(suite: &str) -> Option<u32> {
147147
/// Run `composer <args>` in `cwd` with a PRIVATE home + cache (the host's
148148
/// composer state must neither leak in nor be polluted). The home's
149149
/// `config.json` disables `secure-http`: the hosted capstone's patch server
150-
/// is a plain-http loopback wiremock.
150+
/// is a plain-http loopback wiremock. Git runs with `core.autocrlf=false`:
151+
/// Git for Windows' system default converts a source install's checkout to
152+
/// CRLF, so its bytes would never equal the upstream archive's.
151153
pub fn composer(cwd: &Path, args: &[&str], home: &Path, cache: &Path) -> Output {
152154
std::fs::create_dir_all(home).unwrap();
153155
std::fs::create_dir_all(cache).unwrap();
@@ -163,6 +165,9 @@ pub fn composer(cwd: &Path, args: &[&str], home: &Path, cache: &Path) -> Output
163165
super::cache_env::isolate(&mut cmd);
164166
cmd.env("COMPOSER_HOME", home)
165167
.env("COMPOSER_CACHE_DIR", cache)
168+
.env("GIT_CONFIG_COUNT", "1")
169+
.env("GIT_CONFIG_KEY_0", "core.autocrlf")
170+
.env("GIT_CONFIG_VALUE_0", "false")
166171
.output()
167172
.expect("failed to run composer")
168173
}

‎crates/socket-patch-cli/tests/docker_vendor_common/mod.rs‎

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,20 +58,35 @@ pub fn cov_docker_args() -> Vec<String> {
5858
/// reports `ok`. Build locally with
5959
/// `docker build -f tests/docker/Dockerfile.<eco> -t <image> .`
6060
/// (after `Dockerfile.base` → `socket-patch-test-base:latest`).
61+
///
62+
/// With `SOCKET_PATCH_DOCKER_E2E_REQUIRED=1` (a CI job that built the image
63+
/// itself) a missing docker or image fails the test instead, so a tag
64+
/// mismatch cannot pass the job with zero assertions.
6165
#[must_use]
6266
pub fn skip_if_no_image(image: &str) -> bool {
63-
let Ok(out) = Command::new("docker")
67+
skip_or_require_image(
68+
image,
69+
std::env::var("SOCKET_PATCH_DOCKER_E2E_REQUIRED").is_ok_and(|v| v == "1"),
70+
)
71+
}
72+
73+
/// [`skip_if_no_image`] with the required switch passed in.
74+
#[must_use]
75+
pub fn skip_or_require_image(image: &str, required: bool) -> bool {
76+
let reason = match Command::new("docker")
6477
.args(["image", "inspect", image])
6578
.output()
66-
else {
67-
eprintln!("skipping: `docker` not on PATH");
68-
return true;
79+
{
80+
Err(_) => "`docker` not on PATH".to_string(),
81+
Ok(out) if !out.status.success() => format!("docker image `{image}` not present"),
82+
Ok(_) => return false,
6983
};
70-
if !out.status.success() {
71-
eprintln!("skipping: docker image `{image}` not present");
72-
return true;
73-
}
74-
false
84+
assert!(
85+
!required,
86+
"{reason}; SOCKET_PATCH_DOCKER_E2E_REQUIRED=1 forbids skipping"
87+
);
88+
eprintln!("skipping: {reason}");
89+
true
7590
}
7691

7792
fn docker_run(image: &str, host_dir: &Path, script: &str, extra: &[&str]) -> Output {

‎crates/socket-patch-cli/tests/docker_vendor_common_selftest.rs‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use sha2::{Digest, Sha256};
1818
#[path = "docker_vendor_common/mod.rs"]
1919
mod docker_vendor_common;
2020

21-
use docker_vendor_common::{bash_prelude, stage_patch_fn};
21+
use docker_vendor_common::{bash_prelude, skip_or_require_image, stage_patch_fn};
2222

2323
/// The docker images always have coreutils `sha256sum`; macOS dev hosts may
2424
/// only have perl `shasum`, so shim it for these local runs.
@@ -164,3 +164,13 @@ fn stage_patch_records_git_blob_sha256_and_stages_blob() {
164164
let blob = dir.path().join(".socket/blobs").join(git_sha256(after));
165165
assert_eq!(std::fs::read(&blob).unwrap(), after, "staged blob bytes");
166166
}
167+
168+
/// A required docker suite fails on a missing image instead of skipping;
169+
/// an optional one skips.
170+
#[test]
171+
fn a_required_docker_suite_fails_on_a_missing_image() {
172+
let image = "socket-patch-test-does-not-exist:never";
173+
assert!(skip_or_require_image(image, false));
174+
let required = std::panic::catch_unwind(|| skip_or_require_image(image, true));
175+
assert!(required.is_err(), "a required suite must not skip");
176+
}

0 commit comments

Comments
 (0)