Go apply writes the shared replace into ./go.work when GOWORK names another file in the same directory, reports success, and the workspace builds unpatched

Fermée
#1,810 3 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Les mainteneurs répondent en général sous 1 jour

@mikolalysenko y travaille déjà.

Depuis le 29/10/2026.

  • #1812 par @mikolalysenko — ouverte

Évaluation

Difficulté
4/5
Temps estimé
1-2 jours
Accessibilité débutants
35/100
Type d'issue
Bug
Clarté
Clairement spécifiée
Activité
À l'abandon
Stack technique
go, rust
Domaine
backend, cli

Piste de recherche

Read crates/socket-patch-core/src/vendor/go_mod_edit.rs around lines 209 and 273, then trace how ReplaceHost is chosen in vendor/go_workspace.rs and used by go_consumer_sync.rs and patch/redirect/mod.rs. Reproduce with the GOWORK fixture in the issue and check that apply writes to the workspace file Go uses, or refuses without changing files. Confirm apply --check and the member builds reflect the patch; linked PR #1812 is already open.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

agent:claimed agent:triaged bug bughunt pm:go priority:p2

[agent] Found by the scheduled Go modules bug-hunt routine (ledger #317).

Summary

Since #1764 (eb75d20), apply run at a go.work root puts the shared module's replace into go.work. It decides that from the workspace go actually uses (GOWORK), but it always writes the file <root>/go.work. When GOWORK names another file in the same directory (for example GOWORK=$PWD/ci.work next to a developer go.work), the replace lands in a file go doesn't read. apply still reports 1 of 1 targeted patch applied and exits 0, re-runs do the same, and every member builds the unpatched module.

apply --check (exit 1, "missing go.mod replace") and vex (not_applied, exit 1) both catch it, so nothing gets attested. But apply's own success report is wrong, and it changes a file the user didn't ask it to change.

If <root>/go.work doesn't exist at all (only ci.work), apply fails with a misleading failed to update go.mod: read ./go.work: No such file or directory.

v5.0.1 and the parent of eb75d20 (86457da) fail closed on the same fixture: apply exits 1 and writes nothing.

Impact

CI setups that pick a workspace file with GOWORK=/abs/path/x.work (a documented go feature, and docs/ecosystems.md says socket-patch honours it) get a green apply and an unpatched build. The stray replace also changes the developer go.work.

Repro (Linux, go 1.24.7, hermetic file GOPROXY, hand-staged .socket/manifest.json and blob as in e2e_golang_workspace_build.rs::write_local_patch)

# ws/ has no go.mod; members ./a and ./b both `require example.com/upstream v1.0.0`
# (lib returns "PRISTINE"; the patch blob makes it return "PATCHED")
cd ws
printf 'go 1.18\n\nuse (\n\t./a\n\t./b\n)\n' > ci.work   # the workspace CI uses
printf 'go 1.18\n\nuse ./a\n' > go.work                  # a developer's go.work
export GOWORK=$PWD/ci.work
socket-patch apply --offline --ecosystems golang
#  Summary: 1 of 1 targeted patch applied ...   exit 0
grep -l replace go.work ci.work a/go.mod b/go.mod
#  go.work                                       <- only the file go isn't reading
(cd a && go run .); (cd b && go run .)
#  A: PRISTINE
#  B: PRISTINE
socket-patch apply --check --ecosystems golang   # exit 1: missing go.mod `replace`
socket-patch apply --offline --ecosystems golang # exit 0 again, still unpatched

Controls: with GOWORK unset, the same go.work replace makes a print PATCHED (which shows go really reads ci.work above). With a root go.mod that is a use member, the mirrored root go.mod replace happens to patch the build, but apply still adds the replace to the unused go.work.

Expected vs actual

  • docs/ecosystems.md, "Go workspaces (go.work)": "The go.work in effect is GOWORK when it names an absolute file … Run in the directory holding go.work, apply, vendor and scan --mode hosted write the replace … into go.work." Expected: the replace goes into the file in effect (ci.work), or apply refuses. Actual: it goes into ./go.work, and apply reports the patch as applied.
  • CLI_CONTRACT: apply exits 0 only when the patches it reports as applied take effect.

OS × version

OS go main ecd4924 v5.0.1 / 86457da
Linux 1.21.13 fail (exit 0, PRISTINE) —
Linux 1.24.7 fail (3 fresh fixtures) refuses (exit 1, nothing written)
Linux 1.26.8 fail —
macOS / Windows — untested (no probe branch this run) —

Untested: vendor and scan --mode hosted (the hosted engine also builds go.work paths itself, see hosted/engine.rs:554), and rollback/remove of a replace sitting in a non-go.work workspace file.

First bad commit

eb75d20 "Write shared Go patch replaces into go.work (#1764)". Its parent 86457da refuses.

Suspect code

  • crates/socket-patch-core/src/vendor/go_mod_edit.rs:209: let host_path = project_root.join(host.file_name()); gives <root>/go.work. The host decision comes from the resolved workspace file (GoWorkspace::rooted_at, vendor/go_workspace.rs:642, which compares only its directory).
  • crates/socket-patch-core/src/vendor/go_workspace.rs:238: ReplaceHost::GoWork => "go.work", also used by go_consumer_sync.rs:416 and patch/redirect/mod.rs:9464.
  • crates/socket-patch-core/src/vendor/go_mod_edit.rs:273: drop_replace_entry hardcodes project_root.join("go.work") too.

The host should carry the resolved work_file path, or rooted_at should require the file to be <root>/go.work.

Langage dominant
Rust
Étoiles
8
Forks
2
Merge moyen
19 h 24 min
PR mergées (30 j)
669

Préparer son environnement

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de SocketDev/socket-patch

Toutes les issues de SocketDev/socket-patch

Issues similaires

Plus d'issues Rust

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.