Skip to content

Vendored yarn classic exits 0 when .gitignore covers the vendored tarball (*.tgz, vendor/, .socket/), so the commit drops it and every fresh checkout's install fails #831

Description

[agent] Found by the scheduled Yarn classic (1.x) bug-hunt routine (ledger #304).

Summary

scan --mode vendored on a yarn classic project writes .socket/vendor/npm/<uuid>/<name>-<ver>.tgz and wires yarn.lock to it. It doesn't check whether git ignores that path. If the project's .gitignore covers it, the scan still exits 0 with status: success and no warning, then prints "Commit .socket/vendor/ and the updated lockfiles". The rewired yarn.lock gets committed but the tarball doesn't, so every fresh checkout fails yarn install --frozen-lockfile.

Three ordinary ignore rules trigger it:

  • *.tgz: GitHub's stock Node.gitignore ships this ("Output of 'npm pack'"), so most JS repos have it.
  • vendor/: common in polyglot repos (Go, PHP, Ruby). It matches .socket/vendor/ at any depth.
  • .socket/: here the vlt backend already refuses with vendor_artifact_gitignored.

The vlt backend handles exactly this. It probes with git check-ignore and refuses (vendor_artifact_gitignored, CLI_CONTRACT.md error-code table), and it writes a <uuid>/.gitignore with !* to re-include the payload. The yarn classic tarball backend does neither.

vendor --check (the CI gate) doesn't catch the broken checkout either:

  • Under *.tgz, the ledger is committed, so --check exits 1 (Missing).
  • Under vendor/ or .socket/, state.json is ignored as well. vendor --check then exits 0 with discovered: 0 while yarn.lock still resolves file:./.socket/vendor/npm/…. CLI_CONTRACT.md says "Missing ledger entries fail with vendor_ledger_missing", but --check only compares the manifest with the ledger and never reads the lock's references. (repair does notice them.)

Impact

On a typical JS repo, the documented vendored workflow (scan, commit, push) breaks CI and every teammate's install. The scan reports success, and with vendor/ or .socket/ ignored, vendor --check reports success too. Nothing points at the cause.

Repro (Linux, yarn 1.22.22; same on 1.7.0 / 1.10.1)

I used a local mock patch API: the self-contained mock.py from the ledger's run-18 probe workflow, which serves left-pad@1.3.0 with a marker prepended to index.js.

API="--api-url http://127.0.0.1:8787 --org o --api-token x"
mkdir p && cd p && git init -q
echo '{"name":"p","version":"1.0.0","private":true,"dependencies":{"left-pad":"1.3.0"}}' > package.json
printf 'node_modules\n*.tgz\n' > .gitignore        # or 'vendor/' or '.socket/'
yarn install && git add -A && git commit -qm init
socket-patch scan --mode vendored --vendor-source service --json --yes $API   # exit 0, status success, warnings []
git add -A && git commit -qm vendored
git ls-files .socket          # socket-patch.vendor.json + state.json only; no .tgz
git clone -q . ../fresh && cd ../fresh
yarn install --frozen-lockfile   # exit 1
# error "./.socket/vendor/npm/1111…/left-pad-1.3.0.tgz": Tarball is not in network and can not be located in cache
socket-patch vendor --check      # *.tgz: exit 1 (Missing); vendor/ or .socket/: exit 0, nothing discovered
socket-patch vex --output v.json $API   # exit 1, vendor_artifact_missing / record_unavailable

Expected vs actual

  • Expected: the same treatment vlt gets (CLI_CONTRACT.md vendor_artifact_gitignored: "inside a git work tree, git check-ignore --no-index reports the new artifact's uuid directory as ignored… Refused before any write"). That means re-including the artifact through a <uuid>/.gitignore where a nested rule can override the ignore (*.tgz), and refusing before any write where it can't (vendor/, .socket/). vendor is documented as ejecting into a "committable .socket/vendor/" (CLI_CONTRACT.md command table). vendor --check should fail when a lockfile references .socket/vendor/<eco>/<uuid>/ that has no ledger entry (vendor_ledger_missing).
  • Actual: exit 0 with no warning, the tarball is never committed, and fresh frozen installs fail. With vendor/ or .socket/ ignored, vendor --check also exits 0.

Matrix (Linux, Node 22; scan exit / fresh-clone yarn install --frozen-lockfile / vendor --check in the clone)

yarn *.tgz vendor/ .socket/
1.7.0 0 / fail / 1 0 / fail / 0 0 / fail / 0
1.10.1 0 / fail / 1 0 / fail / 0 0 / fail / 0
1.22.22 0 / fail / 1 0 / fail / 0 0 / fail / 0

Each cell was reproduced twice on main 045d7ec. The yarn version doesn't matter; this is purely a socket-patch gap. I haven't run macOS or Windows, because git's ignore semantics are the same there.

Suspect code

  • crates/socket-patch-core/src/vendor/yarn_classic_lock.rs:60 (vendor_yarn_classic) stages and wires the tarball with no ignore probe. Compare crates/socket-patch-core/src/vendor/vlt_lock.rs:762-770, which calls npm_dir::gitignored and refuses with GITIGNORED, and npm_dir.rs:46 (UUID_GITIGNORE).
  • crates/socket-patch-cli/src/commands/vendor.rs:973-985: --check reports vendor_ledger_missing only for manifest keys, not for lockfile references (repair::scan_vendor_references already finds them).
  • Related: Vendored Gradle exits 0 when the project's .gitignore excludes *.jar, so the commit silently drops the patched jar and every fresh checkout fails to build #620 (the same shape for Gradle *.jar). The other npm-family tarball backends (npm, pnpm, bun) probably share this; I haven't tested them, and I've handed it to those routines.

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