Skip to content

Vendored Gradle: on a Windows (core.autocrlf=true) checkout, vendor --check fails and vendor --revert / remove / rollback leave the settings script behind, because the index and script aren't covered by the -text .gitattributes #429

Description

[agent] Found by the scheduled Gradle bug-hunt routine (ledger #319).

Summary

Gradle vendoring commits two text files that later commands compare byte for byte: .socket/gradle/socket-patch.settings.gradle and .socket/vendor/gradle-index.tsv. The * -text .gitattributes it writes covers only .socket/vendor/gradle/ (GITATTRIBUTES_REL), so neither file is protected. Git for Windows ships core.autocrlf=true in its system gitconfig, as GitHub's windows-latest runners do (file:C:/Program Files/Git/etc/gitconfig true). On such a checkout both files come out CRLF, and:

  1. vendor --check fails on a pristine checkout: exit 1, partialFailure, vendor_check_failed: "vendored wiring or metadata drifted: .socket/gradle/socket-patch.settings.gradle". After fixing that file, it reports …: .socket/vendor/gradle-index.tsv. Any Windows CI that gates on vendor --check (the documented offline integrity audit) is permanently red.
  2. vendor --revert, remove <purl> and rollback exit 0 but leave .socket/gradle/socket-patch.settings.gradle behind, with vendor_lock_entry_drifted (".socket/gradle/socket-patch.settings.gradle was modified; left alone"), so the revert isn't clean. In the buildSrc scenario, buildSrc/settings.gradle (created by vendor) is also left behind as M instead of being removed.

The Gradle build itself is fine: the script is CRLF-tolerant and the jar/pom are protected by -text, so :printCp resolves the patched vendored jar. Only socket-patch's own audit and revert are wrong.

Repro (Linux, simulating the Windows default)

I used the e2e_vendor_jvm_build patch-service fixture and a staged manifest + blob for pkg:maven/org.apache.commons/commons-text@1.10.0, as in gradle_multi_project_vendor_locked_offline_tamper_and_byte_exact_revert. The Gradle-only project is settings.gradle + a Groovy build.gradle with repositories { mavenCentral() } and implementation 'org.apache.commons:commons-text:1.10.0'.

socket-patch vendor --json --offline           # exit 0, applied 1
git init -q && git add -A && git commit -qm vendored
git -c core.autocrlf=true clone -q . ../win && cd ../win
file .socket/vendor/gradle-index.tsv .socket/gradle/socket-patch.settings.gradle   # both "with CRLF line terminators"
socket-patch vendor --check --json             # exit 1: vendor_check_failed "...drifted: .socket/gradle/socket-patch.settings.gradle"
sed -i 's/\r$//' .socket/gradle/socket-patch.settings.gradle
socket-patch vendor --check --json             # exit 1: "...drifted: .socket/vendor/gradle-index.tsv"
sed -i 's/\r$//' .socket/vendor/gradle-index.tsv
socket-patch vendor --check --json             # exit 0: vendor_check_ok
# fresh CRLF clone again:
socket-patch vendor --revert --json --offline  # exit 0, skipped vendor_lock_entry_drifted; the script file remains

Controls: a core.autocrlf=input clone, or an LF clone, gives vendor_check_ok, and revert removes everything. A CRLF settings.gradle on its own is handled correctly (the apply-line fragment matches across endings).

Expected vs actual

  • Expected: docs/design/maven-vendoring.md says vendoring commits "a local repository so another checkout can build without socket-patch", and that vendor --check "checks artifact hashes, recorded tree files, wiring, Gradle's index and script". docs/ecosystems.md sets the precedent: vlt's vendored tree carries "a .gitattributes that turns EOL conversion off so an autocrlf checkout stays byte-exact". A normal Windows checkout of a committed vendored project should pass vendor --check and revert cleanly.
  • Actual: vendor --check exits 1 on a pristine Windows checkout. Revert, remove and rollback exit 0 but leave socket-owned files behind.

OS × Gradle

Probe run (real windows-latest, default Git for Windows config, real Gradle builds): https://github.com/SocketDev/socket-patch/actions/runs/36821988108

OS Gradle vendor --check on fresh clone revert on fresh clone Gradle build
Windows (autocrlf=true default) 6.9.4 (JDK 11) exit 1 script left behind patched ✓
Windows 7.6.6 (JDK 17) exit 1 script left behind patched ✓
Windows 9.8.0 (JDK 21) exit 1 script left behind patched ✓
Linux, git -c core.autocrlf=true clone 8.14.3 exit 1 (2/2) script left behind patched ✓
Linux / macOS, default git 6.9.4 / 7.6.6 / 9.8.0 ok clean patched ✓ (run https://github.com/SocketDev/socket-patch/actions/runs/36821273765)

First bad version: main 2463257 (#277), which introduced Gradle vendoring.

Suspect code

  • crates/socket-patch-core/src/vendor/jvm/gradle.rs:32: GITATTRIBUTES_REL = ".socket/vendor/gradle/.gitattributes" only covers the artifact tree, not INDEX_REL (.socket/vendor/gradle-index.tsv) or SCRIPT_REL (.socket/gradle/socket-patch.settings.gradle).
  • crates/socket-patch-core/src/vendor/jvm/apply.rs:767: vendor --check re-plans and treats any byte difference in a planned write as drift. The revert's owned-file comparison behaves the same way (vendor_lock_entry_drifted, apply.rs:449/:529).

I haven't checked whether the Maven reactor backend's LF files (.mvn/maven.config) have the same exposure. That belongs to the maven routine.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions