Skip to content

Commit 72206e4

Browse files
Keep the in-memory hosted engine off the disk for Go workspaces (#2031)
* Read the governing go.work in Go discovery Go discovery read only a go.work at the project root and only members inside the root. With GOWORK unset it ignored an ancestor go.work and read the root go.mod in module mode, so in a member of a parent workspace it could attest a go.mod replace that the workspace overrides, doesn't `use`, or makes inert by version selection. Hosted and vendored modes resolve the workspace go itself would use and refuse to write such a replace. On disk, discovery now loads the governing workspace with the same go_workspace::load_with loader hosted mode uses: GOWORK (environment, then the go env -w file), else the nearest go.work at or above the root, with `use` members anywhere. Out-of-root members' requires and replaces now count toward selection and conflicts. In-root members are still re-read through the view, so recognition and parse diagnostics stay the same. A wired Socket replace that this workspace keeps from the build, at exactly the points where hosted mode's host_conflict check refuses, stays a ref (list, rollback and remove still find it) but is marked unattested with the new UnattestedKind::GoWorkspaceOverride. The same applies to a replace in the root go.work while GOWORK makes go build another one. vex_sources reports these under the existing `go_workspace_override` reason, the one vex's own go.mod gate already uses. A replace that workspace selection makes inert is still diagnosed, as in module mode. A GOWORK that go rejects still withholds every ref as patched_ref_invalid. In memory there is no directory to search from and no environment the project builds under. The root go.work and its in-root members are the workspace there, as the in-memory hosted engine reads them, and the same conflict gate applies. GOWORK keeps its old effect in memory and GOFLAGS is not read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep Go replaces the workspace ignores even when they look inert Version-selection inertness in replaced_purl, and the hosted selection checks, ran before Workspace::bypass. A Socket replace that the governing go.work also refuses (not used, overridden by a go.work replace, member conflict) was dropped as inert with a diagnostic instead of being kept as a GoWorkspaceOverride ref, so list, rollback and remove could not find wiring that re-running scan or vendor would never clear. Compute the bypass first and skip the inertness checks for a bypassed replace; it stays a ref and is reported unattested. Found by Cursor Bugbot on #2011. Co-Authored-By: Claude <noreply@anthropic.com> * Address code review findings on Go workspace discovery This fixes the confirmed /code-review high findings on #2011. Each fix has a test that fails on the previous commit. - A Go workspace mark now applies to one file only. The new UnattestedKind::file_scoped is true only for GoWorkspaceOverride. vex_sources drops the bypassed ref from the candidate's basis and vetoes the patch only when no live ref of it remains. wiring_conflicts ignores bypassed refs. So a root go.work replace that go never reads no longer withholds the root go.mod's mirror of it. A leftover member pin that Socket's own go.work replace overrides no longer turns into a wiring conflict that also withholds the go.work patch, which is what vex.rs's WorkHosted exemption intends. - A replace of a module that is itself a `use` member is diagnosed as inert again, both on disk and in memory, hosted and vendored. This check now runs before the workspace bypass. - SumPins now also reads the sum files the loader already read: the governing go.work.sum and each out-of-root member's go.sum. They are used only when they lie inside the git repository. - In memory, workspace conflicts are evaluated in go_workspace::PathSpace::Memory. Paths are compared lexically against the view's files and never against the filesystem or the working directory. conflict/work_conflict delegate to the new *_in variants, so disk behavior is unchanged. - The snapshot probe list is now built by one shared helper (workspace_probes), spelled the way resolve_with spells it. - A root go.work that go doesn't read is no longer judged unparseable, and its wiring stays an unattested ref. - The go_workspace_override omission phrase now covers every case. - read_loaded_workspace uses members_within. The view re-reads only the in-root members' go.mod, which recognition and parse diagnostics need. The duplicate go.sum and go.work.sum reads are gone. - Tests that use a nested project now create a .git, so they keep working once reads are bounded to the repository root (#2025). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the in-memory hosted engine off the disk for Go workspaces The hosted engine hands the golang rewriter the Go workspace it read (HostedGoWorkspace). In memory its paths are root-relative, but the rewriter still judged them with the disk checks: replace_host, rooted_at, selection and host_conflict canonicalized against the process's cwd and probed `<cwd>/go.mod`. So an in-memory project whose root module go.work does not `use` got "nowhere to go" (no go.mod) when the server's cwd had none, and the member answer when it did. HostedGoWorkspace now carries `files`: in memory, the root-relative files that exist (go.work, a readable root go.mod, each in-root member's readable go.mod), filled by the engine as discovery lists them. The rewriter routes every workspace check through `PathSpace::Memory` when it is set, via new `_in` variants of `replace_host`, `selection`, `host_conflict` and `conflict` on GoWorkspace / Loaded. Disk behaviour is unchanged (the old names delegate with `PathSpace::Disk`). `GoWorkspace::selection` also displayed a main-module member through the disk display_path; discovery's build-list graph now carries the workspace's PathSpace so in-memory discovery uses `selection_in` too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep a root go.mod pin of another patch a Go wiring conflict 2984629 dropped every workspace-bypassed ref from wiring_conflicts, so a leftover member pin that Socket's go.work replace overrides would not withhold the go.work patch. That also caught the root go.mod. A GOWORK=off build from the root reads the root go.mod, so a different patch there still disagrees with go.work about what the build links. The behavior is the same reasoning ReplaceHost::mirrors_root_go_mod uses. e2e_vex_lockfile golang_go_mod_and_go_work_disagreeing_attest_neither went red in coverage (2): it got go_workspace_override plus go_version_not_selected skips, not one wiring_conflict skip. Count bypassed refs in the root go.mod as wiring again. Member go.mod pins are still exempt, as a_bypassed_leftover_pin_is_no_wiring_conflict requires. Co-Authored-By: Claude <noreply@anthropic.com> * Address code review findings on the in-memory Go workspace - GoWorkspace carries its own `files` (in memory: the root-relative files that exist; `None` on disk) and every method routes through `GoWorkspace::paths()`. The `_in(PathSpace, ..)` variants and their Disk-default wrappers are gone, so no caller can judge an in-memory workspace against the disk or the cwd. HostedGoWorkspace::files and its paths() go with them; the rewriter's code is back to the base's. - One builder for the in-memory workspace, go_workspace::read_memory, over a MemoryFiles reader: the hosted engine reads through read_advisory, discovery through its guarded reads (diagnosing an unparseable member go.mod). Both list a file in `files` when it exists (a link, an entry without content, a go.mod go cannot parse all count), checked without reading it, and both drop an unparseable member go.mod's text. - members_within no longer counts `../out` or an absolute member as inside the empty in-memory root (strip_prefix("") matched them), so the NoModule hint names in-root members only. - PathSpace::from_files builds the space from an optional file list. - The cwd test moved to hosted::engine's tests; read_go_workspace is private again. The redirect fixtures list the root go.mod whenever the project has one, as read_memory does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep inert Go member replaces as refs; recognize stale sums Second review round on Go workspace discovery. - A Socket replace of a module that is itself a `use` member (every replace of it is inert, and the writers refuse it with `WorkspaceConflict::MainModule`, which a re-run never clears) was dropped with a diagnostic, so rollback, remove and list lost wiring still in the file. It stays a ref, marked `GoWorkspaceOverride` with the same "is inert" detail (owner decision 4). - go.sum / member sums / go.work.sum were read only when a hosted ref existed, so a sum left by a reverted patch was no longer recognized and a dead ledger claim could be re-derived from its raw text (rule 11). They are recognized either way. - The plan both attested a purl (the live go.work patch) and reported it skipped for a lockfile-only candidate whose only ref is a stale, bypassed leftover pin of another patch. That candidate is now dropped with a note when another candidate attests the purl. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep an unparseable member go.mod in the in-memory engine Second review round on the in-memory Go workspace. The in-memory hosted engine dropped the text of a member go.mod go cannot parse, while the disk loader keeps it: a member declaring the patched module (every replace of it inert, `MainModule`) or replacing it was refused on disk but pinned over in memory. `MemoryFiles:: unparseable` now says whether the text is kept: the engine keeps it, discovery still drops it after its diagnostic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep go.mod as host for an unreadable in-memory root module In memory, GoWorkspace::files counts a root go.mod that exists but has no text the rewriter holds (a link, an unreadable file), and conflict() reports such a root as "not a member" when go.work does not use it. The rewriter passes has_go_mod from its candidate texts, so replace_host saw no root module, chose go.work, and ran work_conflict: the replace went into go.work, where it patches the members but never the project. When the root is not a member, treat a root go.mod that the in-memory workspace lists as the root's module, as on disk, so go.mod hosts the replace and conflict() refuses it. Found by Bugbot on df219a6. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent bb17e0d commit 72206e4

4 files changed

Lines changed: 627 additions & 158 deletions

File tree

‎crates/socket-patch-core/src/hosted/engine.rs‎

Lines changed: 180 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -493,64 +493,68 @@ async fn read_advisory(
493493
}
494494
}
495495

496+
/// An in-memory project's Go files as the hosted engine reads them
497+
/// ([`read_advisory`]: a link or an entry without content is unreadable,
498+
/// but still exists).
499+
struct AdvisoryGoFiles<'v, 'p> {
500+
view: &'v ProjectView<'p>,
501+
unreadable: &'v BTreeSet<String>,
502+
}
503+
504+
impl crate::vendor::go_workspace::MemoryFiles for AdvisoryGoFiles<'_, '_> {
505+
async fn exists(&mut self, rel: &str) -> bool {
506+
self.unreadable.contains(rel) || self.view.exists_no_follow(rel).await
507+
}
508+
509+
async fn read(&mut self, rel: &str) -> Option<String> {
510+
read_advisory(self.view, self.unreadable, rel).await
511+
}
512+
513+
async fn read_sum(&mut self, rel: &str) -> Option<String> {
514+
read_advisory(self.view, self.unreadable, rel).await
515+
}
516+
517+
/// Kept, as the disk loader keeps it: a member that declares the
518+
/// patched module, or replaces it, is still weighed (refused), never
519+
/// pinned over.
520+
fn unparseable(&mut self, _rel: &str, _problem: &str) -> bool {
521+
true
522+
}
523+
}
524+
496525
/// The Go workspace in effect for the project, handed to the golang
497526
/// rewriter under [`crate::patch::redirect::GO_WORKSPACE_KEY`]. On disk it is
498527
/// the one go resolves (`GOWORK`, else a go.work at or above the root, with
499528
/// members anywhere, plus a `-modfile` that GOFLAGS sets); in memory a root
500-
/// `go.work` and its in-root members. A root go.work and go.work.sum are candidate files the rewriter writes.
529+
/// `go.work` and its in-root members, read as discovery reads them
530+
/// ([`crate::vendor::go_workspace::read_memory`]), whose paths every check
531+
/// compares lexically, never probing the disk or the process's working
532+
/// directory. A root go.work and go.work.sum are candidate files the
533+
/// rewriter writes.
501534
async fn read_go_workspace(
502535
view: &ProjectView<'_>,
503536
unreadable: &BTreeSet<String>,
504537
out: &mut CandidateFiles,
505538
) {
506-
use crate::vendor::go_workspace::{self, GoWorkspace, Loaded, Member};
539+
use crate::vendor::go_workspace::{self, Loaded};
507540
let (root, loaded, modfile, module_graph) = match view {
508-
ProjectView::Memory(_) => 'memory: {
541+
ProjectView::Memory(_) => {
509542
let Some(work) = read_advisory(view, unreadable, "go.work").await else {
510543
return;
511544
};
512545
let work = crate::vendor::go_mod_edit::normalize_for_read(&work);
513546
// As on disk (`go_workspace::load`): a go.work go cannot parse
514547
// is a workspace go refuses to build, never module mode.
515-
if let Some(problem) = crate::vendor::go_mod_edit::block_structure_error(&work) {
516-
break 'memory (
517-
std::path::PathBuf::new(),
518-
Loaded::Invalid(format!("go.work: {problem}, so go refuses to build")),
519-
None,
520-
None,
521-
);
522-
}
523-
let mut members = Vec::new();
524-
for raw in crate::vendor::go_mod_edit::parse_use_dirs(&work) {
525-
let Some(dir) = go_workspace::in_root_dir(&raw) else {
526-
members.push(Member {
527-
dir: raw.into(),
528-
go_mod: None,
529-
go_sum: None,
530-
});
531-
continue;
532-
};
533-
let at = |file: &str| match dir.as_str() {
534-
"" => file.to_string(),
535-
dir => format!("{dir}/{file}"),
536-
};
537-
members.push(Member {
538-
go_mod: read_advisory(view, unreadable, &at("go.mod"))
539-
.await
540-
.map(|t| crate::vendor::go_mod_edit::normalize_for_read(&t)),
541-
go_sum: read_advisory(view, unreadable, &at("go.sum")).await,
542-
dir: dir.into(),
543-
});
544-
}
545-
let work_sum = read_advisory(view, unreadable, "go.work.sum").await;
546-
let ws = GoWorkspace {
547-
work_file: "go.work".into(),
548-
work,
549-
work_sum,
550-
members,
551-
graph: None,
548+
let loaded = match crate::vendor::go_mod_edit::block_structure_error(&work) {
549+
Some(problem) => {
550+
Loaded::Invalid(format!("go.work: {problem}, so go refuses to build"))
551+
}
552+
None => {
553+
let mut files = AdvisoryGoFiles { view, unreadable };
554+
Loaded::Workspace(go_workspace::read_memory(&work, &mut files).await.0)
555+
}
552556
};
553-
(std::path::PathBuf::new(), Loaded::Workspace(ws), None, None)
557+
(std::path::PathBuf::new(), loaded, None, None)
554558
}
555559
ProjectView::Disk(_) | ProjectView::Snapshot(_) => {
556560
let Some(root) = view.disk_root_reading(std::iter::empty::<&std::path::Path>()) else {
@@ -3677,6 +3681,141 @@ mod tests {
36773681
);
36783682
}
36793683

3684+
/// The in-memory engine's workspace is judged lexically, from the
3685+
/// project's own files: a root module go.work doesn't `use` is "not a
3686+
/// member", whatever the process's working directory holds. The disk
3687+
/// checks probed `<cwd>/go.mod` instead, so with this crate's directory
3688+
/// as the cwd (no go.mod) the rewriter called the root module-less
3689+
/// ("nowhere to go"), and with a go.mod there it gave the member answer.
3690+
/// A root go.mod go cannot parse, or one that cannot be read (a link),
3691+
/// still exists: the same files, the same verdict, as discovery's
3692+
/// `in_memory_workspace_files_are_the_files_that_exist` finds.
3693+
#[tokio::test]
3694+
async fn memory_go_workspace_never_probes_the_cwd() {
3695+
use crate::patch::redirect::{
3696+
rewrite_registry_redirect, HostedGoWorkspace, Integrity, RegistryOverride,
3697+
RegistryOverrideIdentifiers, GO_WORKSPACE_KEY,
3698+
};
3699+
use crate::vendor::go_workspace::{Loaded, WorkspaceConflict};
3700+
const MODULE: &str = "github.com/foo/bar";
3701+
let socket = format!("patch.socket.dev/gopatch/{PATCH_UUID}");
3702+
let hash = format!("h1:{}=", "A".repeat(43));
3703+
let ovr = DepOverride {
3704+
ecosystem: "golang".into(),
3705+
name: MODULE.into(),
3706+
version: "v1.4.2".into(),
3707+
registry_override: Some(RegistryOverride {
3708+
kind: "goproxy".into(),
3709+
index_url: "https://patch.socket.dev/patch-registry/golang".into(),
3710+
identifiers: RegistryOverrideIdentifiers {
3711+
name: MODULE.into(),
3712+
version: "v1.4.2".into(),
3713+
go_module_path: Some(socket.clone()),
3714+
go_module_version: Some("v1.4.2-socketpatch.1".into()),
3715+
..Default::default()
3716+
},
3717+
}),
3718+
integrity: Integrity {
3719+
dirhash_h1: Some(hash.clone()),
3720+
go_mod_h1: Some(hash),
3721+
..Default::default()
3722+
},
3723+
..grant_dep("golang", String::new())
3724+
};
3725+
let go_mod = format!("module example.com/app\n\ngo 1.21\n\nrequire {MODULE} v1.4.2\n");
3726+
let b = format!("module example.com/b\n\ngo 1.21\n\nrequire {MODULE} v1.4.2\n");
3727+
let work = "go 1.21\n\nuse ./b\n";
3728+
let ours = crate::vendor::go_mod_edit::ReplaceEntry {
3729+
module: MODULE.into(),
3730+
version: Some("v1.4.2".into()),
3731+
path: None,
3732+
rhs_module: Some(socket),
3733+
rhs_version: Some("v1.4.2-socketpatch.1".into()),
3734+
owner: Some(crate::vendor::go_mod_edit::ReplaceOwner::Hosted),
3735+
};
3736+
let unparseable = MemoryEntry::Text("module example.com/app\n\nrequire (\n".into());
3737+
for root in [
3738+
MemoryEntry::Text(go_mod.as_str().into()),
3739+
unparseable,
3740+
MemoryEntry::Symlink,
3741+
] {
3742+
let mut p = MemoryProject::new();
3743+
p.insert("go.mod", root.clone());
3744+
p.insert_text("go.work", work);
3745+
p.insert_text("b/go.mod", b.as_str());
3746+
let mut out = CandidateFiles::default();
3747+
read_go_workspace(&ProjectView::Memory(&p), &BTreeSet::new(), &mut out).await;
3748+
let hosted: HostedGoWorkspace =
3749+
serde_json::from_str(&out.files[GO_WORKSPACE_KEY]).unwrap();
3750+
let Loaded::Workspace(ws) = &hosted.loaded else {
3751+
panic!("{root:?}: {:?}", hosted.loaded);
3752+
};
3753+
assert_eq!(
3754+
ws.files,
3755+
Some(
3756+
["go.mod", "go.work", "b/go.mod"]
3757+
.into_iter()
3758+
.map(std::path::PathBuf::from)
3759+
.collect()
3760+
),
3761+
"{root:?}"
3762+
);
3763+
assert!(
3764+
matches!(
3765+
hosted
3766+
.loaded
3767+
.conflict(&hosted.root, MODULE, "v1.4.2", &ours),
3768+
Some(WorkspaceConflict::NotAMember { .. })
3769+
),
3770+
"{root:?}"
3771+
);
3772+
}
3773+
// Review on #2031 (round 2): a member go.mod go cannot parse is
3774+
// still weighed, as the disk loader keeps it: one declaring the
3775+
// patched module makes every replace of it inert (refused).
3776+
let mut p = MemoryProject::new();
3777+
p.insert_text("go.mod", go_mod.as_str());
3778+
p.insert_text("go.work", "go 1.21\n\nuse (\n\t.\n\t./b\n)\n");
3779+
p.insert_text("b/go.mod", format!("module {MODULE}\n\nrequire (\n"));
3780+
let mut out = CandidateFiles::default();
3781+
read_go_workspace(&ProjectView::Memory(&p), &BTreeSet::new(), &mut out).await;
3782+
let hosted: HostedGoWorkspace = serde_json::from_str(&out.files[GO_WORKSPACE_KEY]).unwrap();
3783+
assert!(
3784+
matches!(
3785+
hosted
3786+
.loaded
3787+
.conflict(&hosted.root, MODULE, "v1.4.2", &ours),
3788+
Some(WorkspaceConflict::MainModule { .. })
3789+
),
3790+
"{:?}",
3791+
hosted.loaded
3792+
);
3793+
3794+
// End to end through the rewriter.
3795+
let mut p = MemoryProject::new();
3796+
p.insert_text("go.mod", go_mod.as_str());
3797+
p.insert_text("go.work", work);
3798+
p.insert_text("b/go.mod", b.as_str());
3799+
let mut read = CandidateFiles::default();
3800+
read_go_workspace(&ProjectView::Memory(&p), &BTreeSet::new(), &mut read).await;
3801+
let mut files = read.files;
3802+
for (path, text) in [("go.mod", go_mod.as_str()), ("go.work", work)] {
3803+
files.insert(path.into(), text.into());
3804+
}
3805+
let out = rewrite_registry_redirect(&files, std::slice::from_ref(&ovr));
3806+
assert!(out.files.is_empty(), "{:?}", out.files);
3807+
assert!(out.confirmed_golang_uuids.is_empty());
3808+
assert_eq!(out.warnings.len(), 1, "{:?}", out.warnings);
3809+
assert_eq!(out.warnings[0].code, "redirect_golang_workspace_conflict");
3810+
assert!(
3811+
out.warnings[0]
3812+
.detail
3813+
.contains("go.work does not `use` this module"),
3814+
"{:?}",
3815+
out.warnings
3816+
);
3817+
}
3818+
36803819
/// A disk candidate whose real path leaves the repository root, as
36813820
/// the file itself or through a directory link on its path, is
36823821
/// recorded as a linked read (refused by the guard), never as absent.

‎crates/socket-patch-core/src/patch/redirect/mod.rs‎

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24403,8 +24403,13 @@ packages:
2440324403
}
2440424404

2440524405
/// The engine's [`GO_WORKSPACE_KEY`] for an in-memory go.work whose
24406-
/// members are `(dir, go.mod)` pairs (`.` for the root module).
24407-
fn go_workspace_key(work_extra: &str, members: &[(&str, &str)]) -> (String, String) {
24406+
/// members are `(dir, go.mod)` pairs (`.` for the root module), in a
24407+
/// project with a root go.mod when `root_go_mod`.
24408+
fn go_workspace_key(
24409+
work_extra: &str,
24410+
members: &[(&str, &str)],
24411+
root_go_mod: bool,
24412+
) -> (String, String) {
2440824413
use crate::vendor::go_workspace::{GoWorkspace, Loaded, Member};
2440924414
let uses: String = members.iter().map(|(d, _)| format!("\t{d}\n")).collect();
2441024415
let hosted = HostedGoWorkspace {
@@ -24436,6 +24441,10 @@ packages:
2443624441
})
2443724442
.collect(),
2443824443
),
24444+
files: Some(memory_go_files(
24445+
members.iter().map(|(dir, _)| *dir),
24446+
root_go_mod,
24447+
)),
2443924448
}),
2444024449
modfile: None,
2444124450
module_graph: None,
@@ -24446,14 +24455,37 @@ packages:
2444624455
)
2444724456
}
2444824457

24458+
/// The files an in-memory workspace of the `use` members `dirs` holds,
24459+
/// as [`crate::vendor::go_workspace::read_memory`] lists them: go.work,
24460+
/// the root go.mod when the project has one (`root_go_mod`, whether or
24461+
/// not `.` is a member) and each in-root member's go.mod.
24462+
fn memory_go_files<'a>(
24463+
dirs: impl IntoIterator<Item = &'a str>,
24464+
root_go_mod: bool,
24465+
) -> std::collections::BTreeSet<std::path::PathBuf> {
24466+
let mut files = std::collections::BTreeSet::from(["go.work".into()]);
24467+
if root_go_mod {
24468+
files.insert("go.mod".into());
24469+
}
24470+
for dir in dirs {
24471+
files.extend(crate::vendor::go_workspace::in_root_dir(dir).map(
24472+
|dir| match dir.as_str() {
24473+
"" => "go.mod".into(),
24474+
dir => format!("{dir}/go.mod").into(),
24475+
},
24476+
));
24477+
}
24478+
files
24479+
}
24480+
2444924481
/// [`go_workspace_key`] plus the root go.work itself as a candidate
2445024482
/// file, as the engine reads it when go.work sits at the root.
2445124483
fn with_root_go_work(
2445224484
files: &mut BTreeMap<String, String>,
2445324485
work_extra: &str,
2445424486
members: &[(&str, &str)],
2445524487
) {
24456-
let (k, v) = go_workspace_key(work_extra, members);
24488+
let (k, v) = go_workspace_key(work_extra, members, files.contains_key("go.mod"));
2445724489
let uses: String = members.iter().map(|(d, _)| format!("\t{d}\n")).collect();
2445824490
files.insert(k, v);
2445924491
files.insert(
@@ -24482,8 +24514,11 @@ packages:
2448224514
("\nreplace github.com/foo/bar => ./fork\n", ""),
2448324515
] {
2448424516
let mut files = files.clone();
24485-
let (k, v) =
24486-
go_workspace_key(work, &[(".", &root), ("b", &go_member("v1.4.2", other))]);
24517+
let (k, v) = go_workspace_key(
24518+
work,
24519+
&[(".", &root), ("b", &go_member("v1.4.2", other))],
24520+
true,
24521+
);
2448724522
files.insert(k, v);
2448824523
let out = rewrite_registry_redirect(&files, std::slice::from_ref(&ovr));
2448924524
assert!(out.files.is_empty(), "{work}{other}: {:?}", out.files);
@@ -24501,6 +24536,7 @@ packages:
2450124536
let (k, v) = go_workspace_key(
2450224537
"\nreplace github.com/foo/bar v1.0.0 => ./fork\n",
2450324538
&[(".", &root), ("b", &go_member("v1.4.2", ""))],
24539+
true,
2450424540
);
2450524541
files.insert(k, v);
2450624542
let out = rewrite_registry_redirect(&files, std::slice::from_ref(&ovr));
@@ -24516,7 +24552,7 @@ packages:
2451624552
let ovr = golang_override();
2451724553
let mut files = golang_files();
2451824554
let root = files["go.mod"].clone();
24519-
let (k, v) = go_workspace_key("", &[(".", &root), ("b", &go_member("v1.5.0", ""))]);
24555+
let (k, v) = go_workspace_key("", &[(".", &root), ("b", &go_member("v1.5.0", ""))], true);
2452024556
files.insert(k, v);
2452124557
let out = rewrite_registry_redirect(&files, std::slice::from_ref(&ovr));
2452224558
assert!(out.files.is_empty());
@@ -24526,7 +24562,7 @@ packages:
2452624562
let bare = "module example.com/app\n\ngo 1.21\n";
2452724563
let mut files = BTreeMap::new();
2452824564
files.insert("go.mod".to_string(), bare.to_string());
24529-
let (k, v) = go_workspace_key("", &[(".", bare), ("b", &go_member("v1.4.2", ""))]);
24565+
let (k, v) = go_workspace_key("", &[(".", bare), ("b", &go_member("v1.4.2", ""))], true);
2453024566
files.insert(k, v);
2453124567
let out = rewrite_registry_redirect(&files, std::slice::from_ref(&ovr));
2453224568
assert!(out.warnings.is_empty(), "{:?}", out.warnings);
@@ -24659,6 +24695,7 @@ packages:
2465924695
member("../out", None),
2466024696
],
2466124697
graph: None,
24698+
files: Some(memory_go_files([".", "../out"], true)),
2466224699
}),
2466324700
modfile: None,
2466424701
module_graph: None,

0 commit comments

Comments
 (0)