From ad4bc412ac5629f5786f3bd3682e7af2e8f6c51a Mon Sep 17 00:00:00 2001 From: Chris Olszewski Date: Thu, 27 Apr 2023 09:06:37 -0700 Subject: [PATCH] feat: Use Rust Berry lockfile impl (#4684) ### Description Built on top of #4629 Overview of changes: - ~~Change `subgraph` so we calculate workspace's closure in parallel in a similar manner to Go~~ This had to get reverted, it appears that rayon was causing [compilation issues on linux](https://github.com/vercel/turbo/actions/runs/4791974785/jobs/8522988391#step:4:648) - `npm_subgraph` -> `subgraph` makes this FFI call generic - Addition of an optional `resolutions` map to the `subgraph` and `transitive_closure`, this is only used by Berry. In the ideal world this would be all of the root `package.json`, but serializing all of that via protobuf is overkill since berry is the only lockfile that needs that info and we only use that field. - Addition of `patches` FFI function, this wasn't needed before as NPM doesn't have patch files encoded in the lockfile - Addition of `global_changes` FFI function, previously for NPM we were just doing this in Go since there were just two fields we needed to check and parsing JSON is cheap. - Switches lockfile usage of berry from the Go implementation to the Rust one introduced in #4589 Reviewer notes: - The passing of the optional resolutions map is very icky and one off, but this is the only lockfile the requires additional information to properly parse. Once the package graph is squarely in Rust land this should go back to being more generic. This will finally close out #2791 ### Testing Instructions Added a new integration test that uses resolutions. This integration test invokes the new `patches` FFI call. For a full test of the new resolution feature, clone and follow the instructions in the [repro](https://github.com/erj826/turbo-prune-bug-repro-11-21) The `lockfile_aware_caching` integration tests all hit the new `global_changes` FFI callsite and provide test coverage. --------- Co-authored-by: Alexander Lyon --- cli/internal/ffi/bindings.h | 6 +- cli/internal/ffi/ffi.go | 69 +- cli/internal/ffi/proto/messages.pb.go | 668 ++++++++++++++--- cli/internal/lockfile/berry_lockfile.go | 681 +----------------- cli/internal/lockfile/berry_lockfile_test.go | 284 +------- cli/internal/lockfile/lockfile.go | 11 +- cli/internal/lockfile/npm_lockfile.go | 20 +- cli/internal/packagemanager/berry.go | 16 +- crates/turborepo-ffi/messages.proto | 35 +- crates/turborepo-ffi/src/lib.rs | 2 +- crates/turborepo-ffi/src/lockfile.rs | 142 +++- .../src/berry/identifiers.rs | 59 +- crates/turborepo-lockfiles/src/berry/mod.rs | 59 +- .../src/berry/resolution.rs | 48 +- crates/turborepo-lockfiles/src/lib.rs | 18 +- crates/turborepo-lockfiles/src/npm.rs | 9 + .../_fixtures/berry_resolutions/.yarnrc.yml | 1 + .../_fixtures/berry_resolutions/package.json | 11 + .../berry_resolutions/packages/a/package.json | 6 + .../berry_resolutions/packages/b/package.json | 6 + .../_fixtures/berry_resolutions/yarn.lock | 62 ++ .../tests/_helpers/copy_fixture.sh | 7 + .../integration/tests/prune/resolutions.t | 25 + 23 files changed, 1161 insertions(+), 1084 deletions(-) create mode 100644 turborepo-tests/integration/tests/_fixtures/berry_resolutions/.yarnrc.yml create mode 100644 turborepo-tests/integration/tests/_fixtures/berry_resolutions/package.json create mode 100644 turborepo-tests/integration/tests/_fixtures/berry_resolutions/packages/a/package.json create mode 100644 turborepo-tests/integration/tests/_fixtures/berry_resolutions/packages/b/package.json create mode 100644 turborepo-tests/integration/tests/_fixtures/berry_resolutions/yarn.lock create mode 100755 turborepo-tests/integration/tests/_helpers/copy_fixture.sh create mode 100644 turborepo-tests/integration/tests/prune/resolutions.t diff --git a/cli/internal/ffi/bindings.h b/cli/internal/ffi/bindings.h index 53f9bd0d86f2f..8c6d63d7d889e 100644 --- a/cli/internal/ffi/bindings.h +++ b/cli/internal/ffi/bindings.h @@ -18,4 +18,8 @@ struct Buffer previous_content(struct Buffer buffer); struct Buffer transitive_closure(struct Buffer buf); -struct Buffer npm_subgraph(struct Buffer buf); +struct Buffer subgraph(struct Buffer buf); + +struct Buffer patches(struct Buffer buf); + +struct Buffer global_change(struct Buffer buf); diff --git a/cli/internal/ffi/ffi.go b/cli/internal/ffi/ffi.go index 82646b19a0836..7bb9518fdb50f 100644 --- a/cli/internal/ffi/ffi.go +++ b/cli/internal/ffi/ffi.go @@ -169,7 +169,11 @@ func PreviousContent(gitRoot, fromCommit, filePath string) ([]byte, error) { } // TransitiveDeps returns the transitive external deps for all provided workspaces -func TransitiveDeps(content []byte, packageManager string, workspaces map[string]map[string]string) (map[string]*ffi_proto.LockfilePackageList, error) { +func TransitiveDeps(content []byte, packageManager string, workspaces map[string]map[string]string, resolutions map[string]string) (map[string]*ffi_proto.LockfilePackageList, error) { + var additionalData *ffi_proto.AdditionalBerryData + if resolutions != nil { + additionalData = &ffi_proto.AdditionalBerryData{Resolutions: resolutions} + } flatWorkspaces := make(map[string]*ffi_proto.PackageDependencyList) for workspace, deps := range workspaces { packageDependencyList := make([]*ffi_proto.PackageDependency, len(deps)) @@ -187,6 +191,7 @@ func TransitiveDeps(content []byte, packageManager string, workspaces map[string Contents: content, PackageManager: toPackageManager(packageManager), Workspaces: flatWorkspaces, + Resolutions: additionalData, } reqBuf := Marshal(&req) resBuf := C.transitive_closure(reqBuf) @@ -209,20 +214,28 @@ func toPackageManager(packageManager string) ffi_proto.PackageManager { switch packageManager { case "npm": return ffi_proto.PackageManager_NPM + case "berry": + return ffi_proto.PackageManager_BERRY default: panic(fmt.Sprintf("Invalid package manager string: %s", packageManager)) } } -// NpmSubgraph returns the contents of a npm lockfile subgraph -func NpmSubgraph(content []byte, workspaces []string, packages []string) ([]byte, error) { +// Subgraph returns the contents of a lockfile subgraph +func Subgraph(packageManager string, content []byte, workspaces []string, packages []string, resolutions map[string]string) ([]byte, error) { + var additionalData *ffi_proto.AdditionalBerryData + if resolutions != nil { + additionalData = &ffi_proto.AdditionalBerryData{Resolutions: resolutions} + } req := ffi_proto.SubgraphRequest{ - Contents: content, - Workspaces: workspaces, - Packages: packages, + Contents: content, + Workspaces: workspaces, + Packages: packages, + PackageManager: toPackageManager(packageManager), + Resolutions: additionalData, } reqBuf := Marshal(&req) - resBuf := C.npm_subgraph(reqBuf) + resBuf := C.subgraph(reqBuf) reqBuf.Free() resp := ffi_proto.SubgraphResponse{} @@ -236,3 +249,45 @@ func NpmSubgraph(content []byte, workspaces []string, packages []string) ([]byte return resp.GetContents(), nil } + +// Patches returns all patch files referenced in the lockfile +func Patches(content []byte, packageManager string) []string { + req := ffi_proto.PatchesRequest{ + Contents: content, + PackageManager: toPackageManager(packageManager), + } + reqBuf := Marshal(&req) + resBuf := C.patches(reqBuf) + reqBuf.Free() + + resp := ffi_proto.PatchesResponse{} + if err := Unmarshal(resBuf, resp.ProtoReflect().Interface()); err != nil { + panic(err) + } + + if err := resp.GetError(); err != "" { + panic(err) + } + + return resp.GetPatches().GetPatches() +} + +// GlobalChange checks if there are any differences between lockfiles that would completely invalidate +// the cache. +func GlobalChange(packageManager string, prevContents []byte, currContents []byte) bool { + req := ffi_proto.GlobalChangeRequest{ + PackageManager: toPackageManager(packageManager), + PrevContents: prevContents, + CurrContents: currContents, + } + reqBuf := Marshal(&req) + resBuf := C.patches(reqBuf) + reqBuf.Free() + + resp := ffi_proto.GlobalChangeResponse{} + if err := Unmarshal(resBuf, resp.ProtoReflect().Interface()); err != nil { + panic(err) + } + + return resp.GetGlobalChange() +} diff --git a/cli/internal/ffi/proto/messages.pb.go b/cli/internal/ffi/proto/messages.pb.go index 68a155a44eca1..6b051e4013473 100644 --- a/cli/internal/ffi/proto/messages.pb.go +++ b/cli/internal/ffi/proto/messages.pb.go @@ -23,16 +23,19 @@ const ( type PackageManager int32 const ( - PackageManager_NPM PackageManager = 0 + PackageManager_NPM PackageManager = 0 + PackageManager_BERRY PackageManager = 1 ) // Enum value maps for PackageManager. var ( PackageManager_name = map[int32]string{ 0: "NPM", + 1: "BERRY", } PackageManager_value = map[string]int32{ - "NPM": 0, + "NPM": 0, + "BERRY": 1, } ) @@ -806,6 +809,7 @@ type TransitiveDepsRequest struct { Contents []byte `protobuf:"bytes,1,opt,name=contents,proto3" json:"contents,omitempty"` PackageManager PackageManager `protobuf:"varint,2,opt,name=package_manager,json=packageManager,proto3,enum=PackageManager" json:"package_manager,omitempty"` Workspaces map[string]*PackageDependencyList `protobuf:"bytes,3,rep,name=workspaces,proto3" json:"workspaces,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Resolutions *AdditionalBerryData `protobuf:"bytes,4,opt,name=resolutions,proto3,oneof" json:"resolutions,omitempty"` } func (x *TransitiveDepsRequest) Reset() { @@ -861,6 +865,13 @@ func (x *TransitiveDepsRequest) GetWorkspaces() map[string]*PackageDependencyLis return nil } +func (x *TransitiveDepsRequest) GetResolutions() *AdditionalBerryData { + if x != nil { + return x.Resolutions + } + return nil +} + type TransitiveDepsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -941,6 +952,53 @@ func (*TransitiveDepsResponse_Dependencies) isTransitiveDepsResponse_Response() func (*TransitiveDepsResponse_Error) isTransitiveDepsResponse_Response() {} +type AdditionalBerryData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Resolutions map[string]string `protobuf:"bytes,1,rep,name=resolutions,proto3" json:"resolutions,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *AdditionalBerryData) Reset() { + *x = AdditionalBerryData{} + if protoimpl.UnsafeEnabled { + mi := &file_turborepo_ffi_messages_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AdditionalBerryData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdditionalBerryData) ProtoMessage() {} + +func (x *AdditionalBerryData) ProtoReflect() protoreflect.Message { + mi := &file_turborepo_ffi_messages_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AdditionalBerryData.ProtoReflect.Descriptor instead. +func (*AdditionalBerryData) Descriptor() ([]byte, []int) { + return file_turborepo_ffi_messages_proto_rawDescGZIP(), []int{14} +} + +func (x *AdditionalBerryData) GetResolutions() map[string]string { + if x != nil { + return x.Resolutions + } + return nil +} + type LockfilePackage struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -954,7 +1012,7 @@ type LockfilePackage struct { func (x *LockfilePackage) Reset() { *x = LockfilePackage{} if protoimpl.UnsafeEnabled { - mi := &file_turborepo_ffi_messages_proto_msgTypes[14] + mi := &file_turborepo_ffi_messages_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -967,7 +1025,7 @@ func (x *LockfilePackage) String() string { func (*LockfilePackage) ProtoMessage() {} func (x *LockfilePackage) ProtoReflect() protoreflect.Message { - mi := &file_turborepo_ffi_messages_proto_msgTypes[14] + mi := &file_turborepo_ffi_messages_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -980,7 +1038,7 @@ func (x *LockfilePackage) ProtoReflect() protoreflect.Message { // Deprecated: Use LockfilePackage.ProtoReflect.Descriptor instead. func (*LockfilePackage) Descriptor() ([]byte, []int) { - return file_turborepo_ffi_messages_proto_rawDescGZIP(), []int{14} + return file_turborepo_ffi_messages_proto_rawDescGZIP(), []int{15} } func (x *LockfilePackage) GetKey() string { @@ -1015,7 +1073,7 @@ type LockfilePackageList struct { func (x *LockfilePackageList) Reset() { *x = LockfilePackageList{} if protoimpl.UnsafeEnabled { - mi := &file_turborepo_ffi_messages_proto_msgTypes[15] + mi := &file_turborepo_ffi_messages_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1028,7 +1086,7 @@ func (x *LockfilePackageList) String() string { func (*LockfilePackageList) ProtoMessage() {} func (x *LockfilePackageList) ProtoReflect() protoreflect.Message { - mi := &file_turborepo_ffi_messages_proto_msgTypes[15] + mi := &file_turborepo_ffi_messages_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1041,7 +1099,7 @@ func (x *LockfilePackageList) ProtoReflect() protoreflect.Message { // Deprecated: Use LockfilePackageList.ProtoReflect.Descriptor instead. func (*LockfilePackageList) Descriptor() ([]byte, []int) { - return file_turborepo_ffi_messages_proto_rawDescGZIP(), []int{15} + return file_turborepo_ffi_messages_proto_rawDescGZIP(), []int{16} } func (x *LockfilePackageList) GetList() []*LockfilePackage { @@ -1056,16 +1114,17 @@ type SubgraphRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Contents []byte `protobuf:"bytes,1,opt,name=contents,proto3" json:"contents,omitempty"` - PackageManager string `protobuf:"bytes,2,opt,name=package_manager,json=packageManager,proto3" json:"package_manager,omitempty"` - Workspaces []string `protobuf:"bytes,3,rep,name=workspaces,proto3" json:"workspaces,omitempty"` - Packages []string `protobuf:"bytes,4,rep,name=packages,proto3" json:"packages,omitempty"` + Contents []byte `protobuf:"bytes,1,opt,name=contents,proto3" json:"contents,omitempty"` + PackageManager PackageManager `protobuf:"varint,2,opt,name=package_manager,json=packageManager,proto3,enum=PackageManager" json:"package_manager,omitempty"` + Workspaces []string `protobuf:"bytes,3,rep,name=workspaces,proto3" json:"workspaces,omitempty"` + Packages []string `protobuf:"bytes,4,rep,name=packages,proto3" json:"packages,omitempty"` + Resolutions *AdditionalBerryData `protobuf:"bytes,5,opt,name=resolutions,proto3,oneof" json:"resolutions,omitempty"` } func (x *SubgraphRequest) Reset() { *x = SubgraphRequest{} if protoimpl.UnsafeEnabled { - mi := &file_turborepo_ffi_messages_proto_msgTypes[16] + mi := &file_turborepo_ffi_messages_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1078,7 +1137,7 @@ func (x *SubgraphRequest) String() string { func (*SubgraphRequest) ProtoMessage() {} func (x *SubgraphRequest) ProtoReflect() protoreflect.Message { - mi := &file_turborepo_ffi_messages_proto_msgTypes[16] + mi := &file_turborepo_ffi_messages_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1091,7 +1150,7 @@ func (x *SubgraphRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SubgraphRequest.ProtoReflect.Descriptor instead. func (*SubgraphRequest) Descriptor() ([]byte, []int) { - return file_turborepo_ffi_messages_proto_rawDescGZIP(), []int{16} + return file_turborepo_ffi_messages_proto_rawDescGZIP(), []int{17} } func (x *SubgraphRequest) GetContents() []byte { @@ -1101,11 +1160,11 @@ func (x *SubgraphRequest) GetContents() []byte { return nil } -func (x *SubgraphRequest) GetPackageManager() string { +func (x *SubgraphRequest) GetPackageManager() PackageManager { if x != nil { return x.PackageManager } - return "" + return PackageManager_NPM } func (x *SubgraphRequest) GetWorkspaces() []string { @@ -1122,6 +1181,13 @@ func (x *SubgraphRequest) GetPackages() []string { return nil } +func (x *SubgraphRequest) GetResolutions() *AdditionalBerryData { + if x != nil { + return x.Resolutions + } + return nil +} + type SubgraphResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1136,7 +1202,7 @@ type SubgraphResponse struct { func (x *SubgraphResponse) Reset() { *x = SubgraphResponse{} if protoimpl.UnsafeEnabled { - mi := &file_turborepo_ffi_messages_proto_msgTypes[17] + mi := &file_turborepo_ffi_messages_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1149,7 +1215,7 @@ func (x *SubgraphResponse) String() string { func (*SubgraphResponse) ProtoMessage() {} func (x *SubgraphResponse) ProtoReflect() protoreflect.Message { - mi := &file_turborepo_ffi_messages_proto_msgTypes[17] + mi := &file_turborepo_ffi_messages_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1162,7 +1228,7 @@ func (x *SubgraphResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SubgraphResponse.ProtoReflect.Descriptor instead. func (*SubgraphResponse) Descriptor() ([]byte, []int) { - return file_turborepo_ffi_messages_proto_rawDescGZIP(), []int{17} + return file_turborepo_ffi_messages_proto_rawDescGZIP(), []int{18} } func (m *SubgraphResponse) GetResponse() isSubgraphResponse_Response { @@ -1202,6 +1268,298 @@ func (*SubgraphResponse_Contents) isSubgraphResponse_Response() {} func (*SubgraphResponse_Error) isSubgraphResponse_Response() {} +type PatchesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Contents []byte `protobuf:"bytes,1,opt,name=contents,proto3" json:"contents,omitempty"` + PackageManager PackageManager `protobuf:"varint,2,opt,name=package_manager,json=packageManager,proto3,enum=PackageManager" json:"package_manager,omitempty"` +} + +func (x *PatchesRequest) Reset() { + *x = PatchesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_turborepo_ffi_messages_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PatchesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PatchesRequest) ProtoMessage() {} + +func (x *PatchesRequest) ProtoReflect() protoreflect.Message { + mi := &file_turborepo_ffi_messages_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PatchesRequest.ProtoReflect.Descriptor instead. +func (*PatchesRequest) Descriptor() ([]byte, []int) { + return file_turborepo_ffi_messages_proto_rawDescGZIP(), []int{19} +} + +func (x *PatchesRequest) GetContents() []byte { + if x != nil { + return x.Contents + } + return nil +} + +func (x *PatchesRequest) GetPackageManager() PackageManager { + if x != nil { + return x.PackageManager + } + return PackageManager_NPM +} + +type PatchesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Response: + // *PatchesResponse_Patches + // *PatchesResponse_Error + Response isPatchesResponse_Response `protobuf_oneof:"response"` +} + +func (x *PatchesResponse) Reset() { + *x = PatchesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_turborepo_ffi_messages_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PatchesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PatchesResponse) ProtoMessage() {} + +func (x *PatchesResponse) ProtoReflect() protoreflect.Message { + mi := &file_turborepo_ffi_messages_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PatchesResponse.ProtoReflect.Descriptor instead. +func (*PatchesResponse) Descriptor() ([]byte, []int) { + return file_turborepo_ffi_messages_proto_rawDescGZIP(), []int{20} +} + +func (m *PatchesResponse) GetResponse() isPatchesResponse_Response { + if m != nil { + return m.Response + } + return nil +} + +func (x *PatchesResponse) GetPatches() *Patches { + if x, ok := x.GetResponse().(*PatchesResponse_Patches); ok { + return x.Patches + } + return nil +} + +func (x *PatchesResponse) GetError() string { + if x, ok := x.GetResponse().(*PatchesResponse_Error); ok { + return x.Error + } + return "" +} + +type isPatchesResponse_Response interface { + isPatchesResponse_Response() +} + +type PatchesResponse_Patches struct { + Patches *Patches `protobuf:"bytes,1,opt,name=patches,proto3,oneof"` +} + +type PatchesResponse_Error struct { + Error string `protobuf:"bytes,2,opt,name=error,proto3,oneof"` +} + +func (*PatchesResponse_Patches) isPatchesResponse_Response() {} + +func (*PatchesResponse_Error) isPatchesResponse_Response() {} + +type Patches struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Patches []string `protobuf:"bytes,1,rep,name=patches,proto3" json:"patches,omitempty"` +} + +func (x *Patches) Reset() { + *x = Patches{} + if protoimpl.UnsafeEnabled { + mi := &file_turborepo_ffi_messages_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Patches) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Patches) ProtoMessage() {} + +func (x *Patches) ProtoReflect() protoreflect.Message { + mi := &file_turborepo_ffi_messages_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Patches.ProtoReflect.Descriptor instead. +func (*Patches) Descriptor() ([]byte, []int) { + return file_turborepo_ffi_messages_proto_rawDescGZIP(), []int{21} +} + +func (x *Patches) GetPatches() []string { + if x != nil { + return x.Patches + } + return nil +} + +type GlobalChangeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PackageManager PackageManager `protobuf:"varint,1,opt,name=package_manager,json=packageManager,proto3,enum=PackageManager" json:"package_manager,omitempty"` + PrevContents []byte `protobuf:"bytes,2,opt,name=prev_contents,json=prevContents,proto3" json:"prev_contents,omitempty"` + CurrContents []byte `protobuf:"bytes,3,opt,name=curr_contents,json=currContents,proto3" json:"curr_contents,omitempty"` +} + +func (x *GlobalChangeRequest) Reset() { + *x = GlobalChangeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_turborepo_ffi_messages_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GlobalChangeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GlobalChangeRequest) ProtoMessage() {} + +func (x *GlobalChangeRequest) ProtoReflect() protoreflect.Message { + mi := &file_turborepo_ffi_messages_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GlobalChangeRequest.ProtoReflect.Descriptor instead. +func (*GlobalChangeRequest) Descriptor() ([]byte, []int) { + return file_turborepo_ffi_messages_proto_rawDescGZIP(), []int{22} +} + +func (x *GlobalChangeRequest) GetPackageManager() PackageManager { + if x != nil { + return x.PackageManager + } + return PackageManager_NPM +} + +func (x *GlobalChangeRequest) GetPrevContents() []byte { + if x != nil { + return x.PrevContents + } + return nil +} + +func (x *GlobalChangeRequest) GetCurrContents() []byte { + if x != nil { + return x.CurrContents + } + return nil +} + +type GlobalChangeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GlobalChange bool `protobuf:"varint,1,opt,name=global_change,json=globalChange,proto3" json:"global_change,omitempty"` +} + +func (x *GlobalChangeResponse) Reset() { + *x = GlobalChangeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_turborepo_ffi_messages_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GlobalChangeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GlobalChangeResponse) ProtoMessage() {} + +func (x *GlobalChangeResponse) ProtoReflect() protoreflect.Message { + mi := &file_turborepo_ffi_messages_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GlobalChangeResponse.ProtoReflect.Descriptor instead. +func (*GlobalChangeResponse) Descriptor() ([]byte, []int) { + return file_turborepo_ffi_messages_proto_rawDescGZIP(), []int{23} +} + +func (x *GlobalChangeResponse) GetGlobalChange() bool { + if x != nil { + return x.GlobalChange + } + return false +} + var File_turborepo_ffi_messages_proto protoreflect.FileDescriptor var file_turborepo_ffi_messages_proto_rawDesc = []byte{ @@ -1278,7 +1636,7 @@ var file_turborepo_ffi_messages_proto_rawDesc = []byte{ 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x4c, 0x6f, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x8c, 0x02, 0x0a, 0x15, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, + 0x38, 0x01, 0x22, 0xd9, 0x02, 0x0a, 0x15, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x38, 0x0a, 0x0f, 0x70, 0x61, 0x63, 0x6b, @@ -1289,47 +1647,97 @@ var file_turborepo_ffi_messages_proto_rawDesc = []byte{ 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, - 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x1a, 0x55, 0x0a, 0x0f, 0x57, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, - 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0x7a, 0x0a, 0x16, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x44, - 0x65, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0c, 0x64, - 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x16, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x65, 0x70, - 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x64, 0x65, 0x70, - 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x53, 0x0a, - 0x0f, 0x4c, 0x6f, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, - 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, - 0x6e, 0x64, 0x22, 0x3b, 0x0a, 0x13, 0x4c, 0x6f, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, - 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x04, 0x6c, 0x69, 0x73, - 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x4c, 0x6f, 0x63, 0x6b, 0x66, 0x69, - 0x6c, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x22, - 0x92, 0x01, 0x0a, 0x0f, 0x53, 0x75, 0x62, 0x67, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, - 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, - 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x77, 0x6f, 0x72, 0x6b, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x63, 0x6b, - 0x61, 0x67, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x63, 0x6b, - 0x61, 0x67, 0x65, 0x73, 0x22, 0x54, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x67, 0x72, 0x61, 0x70, 0x68, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, + 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x0b, 0x72, 0x65, + 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x42, 0x65, 0x72, 0x72, + 0x79, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x88, 0x01, 0x01, 0x1a, 0x55, 0x0a, 0x0f, 0x57, 0x6f, 0x72, 0x6b, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x50, 0x61, + 0x63, 0x6b, 0x61, 0x67, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0e, + 0x0a, 0x0c, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x7a, + 0x0a, 0x16, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x70, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0c, 0x64, 0x65, 0x70, 0x65, + 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, + 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, + 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x0a, - 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x19, 0x0a, 0x0e, 0x50, 0x61, - 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x07, 0x0a, 0x03, - 0x4e, 0x50, 0x4d, 0x10, 0x00, 0x42, 0x0b, 0x5a, 0x09, 0x66, 0x66, 0x69, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9e, 0x01, 0x0a, 0x13, 0x41, + 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x42, 0x65, 0x72, 0x72, 0x79, 0x44, 0x61, + 0x74, 0x61, 0x12, 0x47, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x61, 0x6c, 0x42, 0x65, 0x72, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, + 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, + 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x52, + 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x53, 0x0a, 0x0f, 0x4c, + 0x6f, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, + 0x75, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, + 0x22, 0x3b, 0x0a, 0x13, 0x4c, 0x6f, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x63, 0x6b, + 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x4c, 0x6f, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x65, + 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x22, 0xf0, 0x01, + 0x0a, 0x0f, 0x53, 0x75, 0x62, 0x67, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x38, 0x0a, + 0x0f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, + 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x52, 0x0e, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, + 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, + 0x67, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, + 0x67, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x41, 0x64, 0x64, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x42, 0x65, 0x72, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, + 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x88, 0x01, 0x01, + 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0x54, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x67, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x66, 0x0a, 0x0e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x73, 0x12, 0x38, 0x0a, 0x0f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, + 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x52, 0x0e, + 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x22, 0x5b, + 0x0a, 0x0f, 0x50, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x24, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x48, 0x00, 0x52, 0x07, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x42, + 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x0a, 0x07, 0x50, + 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, + 0x22, 0x99, 0x01, 0x0a, 0x13, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0f, 0x70, 0x61, 0x63, 0x6b, + 0x61, 0x67, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x0f, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x72, 0x52, 0x0e, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x65, 0x76, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x76, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, + 0x63, 0x75, 0x72, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3b, 0x0a, 0x14, + 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x67, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2a, 0x24, 0x0a, 0x0e, 0x50, 0x61, 0x63, + 0x6b, 0x61, 0x67, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x07, 0x0a, 0x03, 0x4e, + 0x50, 0x4d, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x42, 0x45, 0x52, 0x52, 0x59, 0x10, 0x01, 0x42, + 0x0b, 0x5a, 0x09, 0x66, 0x66, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1345,7 +1753,7 @@ func file_turborepo_ffi_messages_proto_rawDescGZIP() []byte { } var file_turborepo_ffi_messages_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_turborepo_ffi_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 20) +var file_turborepo_ffi_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 27) var file_turborepo_ffi_messages_proto_goTypes = []interface{}{ (PackageManager)(0), // 0: PackageManager (*TurboDataDirResp)(nil), // 1: TurboDataDirResp @@ -1362,29 +1770,43 @@ var file_turborepo_ffi_messages_proto_goTypes = []interface{}{ (*WorkspaceDependencies)(nil), // 12: WorkspaceDependencies (*TransitiveDepsRequest)(nil), // 13: TransitiveDepsRequest (*TransitiveDepsResponse)(nil), // 14: TransitiveDepsResponse - (*LockfilePackage)(nil), // 15: LockfilePackage - (*LockfilePackageList)(nil), // 16: LockfilePackageList - (*SubgraphRequest)(nil), // 17: SubgraphRequest - (*SubgraphResponse)(nil), // 18: SubgraphResponse - nil, // 19: WorkspaceDependencies.DependenciesEntry - nil, // 20: TransitiveDepsRequest.WorkspacesEntry + (*AdditionalBerryData)(nil), // 15: AdditionalBerryData + (*LockfilePackage)(nil), // 16: LockfilePackage + (*LockfilePackageList)(nil), // 17: LockfilePackageList + (*SubgraphRequest)(nil), // 18: SubgraphRequest + (*SubgraphResponse)(nil), // 19: SubgraphResponse + (*PatchesRequest)(nil), // 20: PatchesRequest + (*PatchesResponse)(nil), // 21: PatchesResponse + (*Patches)(nil), // 22: Patches + (*GlobalChangeRequest)(nil), // 23: GlobalChangeRequest + (*GlobalChangeResponse)(nil), // 24: GlobalChangeResponse + nil, // 25: WorkspaceDependencies.DependenciesEntry + nil, // 26: TransitiveDepsRequest.WorkspacesEntry + nil, // 27: AdditionalBerryData.ResolutionsEntry } var file_turborepo_ffi_messages_proto_depIdxs = []int32{ 4, // 0: GlobResp.files:type_name -> GlobRespList 7, // 1: ChangedFilesResp.files:type_name -> ChangedFilesList 10, // 2: PackageDependencyList.list:type_name -> PackageDependency - 19, // 3: WorkspaceDependencies.dependencies:type_name -> WorkspaceDependencies.DependenciesEntry + 25, // 3: WorkspaceDependencies.dependencies:type_name -> WorkspaceDependencies.DependenciesEntry 0, // 4: TransitiveDepsRequest.package_manager:type_name -> PackageManager - 20, // 5: TransitiveDepsRequest.workspaces:type_name -> TransitiveDepsRequest.WorkspacesEntry - 12, // 6: TransitiveDepsResponse.dependencies:type_name -> WorkspaceDependencies - 15, // 7: LockfilePackageList.list:type_name -> LockfilePackage - 16, // 8: WorkspaceDependencies.DependenciesEntry.value:type_name -> LockfilePackageList - 11, // 9: TransitiveDepsRequest.WorkspacesEntry.value:type_name -> PackageDependencyList - 10, // [10:10] is the sub-list for method output_type - 10, // [10:10] is the sub-list for method input_type - 10, // [10:10] is the sub-list for extension type_name - 10, // [10:10] is the sub-list for extension extendee - 0, // [0:10] is the sub-list for field type_name + 26, // 5: TransitiveDepsRequest.workspaces:type_name -> TransitiveDepsRequest.WorkspacesEntry + 15, // 6: TransitiveDepsRequest.resolutions:type_name -> AdditionalBerryData + 12, // 7: TransitiveDepsResponse.dependencies:type_name -> WorkspaceDependencies + 27, // 8: AdditionalBerryData.resolutions:type_name -> AdditionalBerryData.ResolutionsEntry + 16, // 9: LockfilePackageList.list:type_name -> LockfilePackage + 0, // 10: SubgraphRequest.package_manager:type_name -> PackageManager + 15, // 11: SubgraphRequest.resolutions:type_name -> AdditionalBerryData + 0, // 12: PatchesRequest.package_manager:type_name -> PackageManager + 22, // 13: PatchesResponse.patches:type_name -> Patches + 0, // 14: GlobalChangeRequest.package_manager:type_name -> PackageManager + 17, // 15: WorkspaceDependencies.DependenciesEntry.value:type_name -> LockfilePackageList + 11, // 16: TransitiveDepsRequest.WorkspacesEntry.value:type_name -> PackageDependencyList + 17, // [17:17] is the sub-list for method output_type + 17, // [17:17] is the sub-list for method input_type + 17, // [17:17] is the sub-list for extension type_name + 17, // [17:17] is the sub-list for extension extendee + 0, // [0:17] is the sub-list for field type_name } func init() { file_turborepo_ffi_messages_proto_init() } @@ -1562,7 +1984,7 @@ func file_turborepo_ffi_messages_proto_init() { } } file_turborepo_ffi_messages_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LockfilePackage); i { + switch v := v.(*AdditionalBerryData); i { case 0: return &v.state case 1: @@ -1574,7 +1996,7 @@ func file_turborepo_ffi_messages_proto_init() { } } file_turborepo_ffi_messages_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LockfilePackageList); i { + switch v := v.(*LockfilePackage); i { case 0: return &v.state case 1: @@ -1586,7 +2008,7 @@ func file_turborepo_ffi_messages_proto_init() { } } file_turborepo_ffi_messages_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SubgraphRequest); i { + switch v := v.(*LockfilePackageList); i { case 0: return &v.state case 1: @@ -1598,6 +2020,18 @@ func file_turborepo_ffi_messages_proto_init() { } } file_turborepo_ffi_messages_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubgraphRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_turborepo_ffi_messages_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SubgraphResponse); i { case 0: return &v.state @@ -1609,6 +2043,66 @@ func file_turborepo_ffi_messages_proto_init() { return nil } } + file_turborepo_ffi_messages_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PatchesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_turborepo_ffi_messages_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PatchesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_turborepo_ffi_messages_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Patches); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_turborepo_ffi_messages_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GlobalChangeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_turborepo_ffi_messages_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GlobalChangeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_turborepo_ffi_messages_proto_msgTypes[2].OneofWrappers = []interface{}{ (*GlobResp_Files)(nil), @@ -1623,21 +2117,27 @@ func file_turborepo_ffi_messages_proto_init() { (*PreviousContentResp_Content)(nil), (*PreviousContentResp_Error)(nil), } + file_turborepo_ffi_messages_proto_msgTypes[12].OneofWrappers = []interface{}{} file_turborepo_ffi_messages_proto_msgTypes[13].OneofWrappers = []interface{}{ (*TransitiveDepsResponse_Dependencies)(nil), (*TransitiveDepsResponse_Error)(nil), } - file_turborepo_ffi_messages_proto_msgTypes[17].OneofWrappers = []interface{}{ + file_turborepo_ffi_messages_proto_msgTypes[17].OneofWrappers = []interface{}{} + file_turborepo_ffi_messages_proto_msgTypes[18].OneofWrappers = []interface{}{ (*SubgraphResponse_Contents)(nil), (*SubgraphResponse_Error)(nil), } + file_turborepo_ffi_messages_proto_msgTypes[20].OneofWrappers = []interface{}{ + (*PatchesResponse_Patches)(nil), + (*PatchesResponse_Error)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_turborepo_ffi_messages_proto_rawDesc, NumEnums: 1, - NumMessages: 20, + NumMessages: 27, NumExtensions: 0, NumServices: 0, }, diff --git a/cli/internal/lockfile/berry_lockfile.go b/cli/internal/lockfile/berry_lockfile.go index e76f230d7209e..ce6bbaae4faef 100644 --- a/cli/internal/lockfile/berry_lockfile.go +++ b/cli/internal/lockfile/berry_lockfile.go @@ -1,82 +1,16 @@ package lockfile import ( - "bytes" - "encoding/json" - "fmt" "io" - "reflect" - "regexp" - "sort" - "strconv" - "strings" - "github.com/Masterminds/semver" - "github.com/andybalholm/crlf" - "github.com/pkg/errors" + "github.com/vercel/turbo/cli/internal/ffi" "github.com/vercel/turbo/cli/internal/turbopath" - "github.com/vercel/turbo/cli/internal/yaml" ) -var _multipleKeyRegex = regexp.MustCompile(" *, *") - -// A tag cannot start with a "v" -var _tagRegex = regexp.MustCompile("^[a-zA-Z0-9.-_-[v]][a-zA-Z0-9._-]*$") - -var _metadataKey = "__metadata" - -type _void struct{} - -// BerryLockfileEntry package information from yarn lockfile -// Full Definition at https://github.com/yarnpkg/berry/blob/master/packages/yarnpkg-core/sources/Manifest.ts -// Only a subset of full definition are written to the lockfile -type BerryLockfileEntry struct { - Version string `yaml:"version"` - LanguageName string `yaml:"languageName,omitempty"` - - Dependencies map[string]string `yaml:"dependencies,omitempty"` - PeerDependencies map[string]string `yaml:"peerDependencies,omitempty"` - - DependenciesMeta map[string]BerryDependencyMetaEntry `yaml:"dependenciesMeta,omitempty"` - PeerDependenciesMeta map[string]BerryDependencyMetaEntry `yaml:"peerDependenciesMeta,omitempty"` - - Bin map[string]string `yaml:"bin,omitempty"` - - LinkType string `yaml:"linkType,omitempty"` - Resolution string `yaml:"resolution,omitempty"` - Checksum string `yaml:"checksum,omitempty"` - Conditions string `yaml:"conditions,omitempty"` - - // Only used for metadata entry - CacheKey string `yaml:"cacheKey,omitempty"` -} - -// Return a list of descriptors that this entry possibly uses -func (b *BerryLockfileEntry) possibleDescriptors() []_Descriptor { - descriptors := []_Descriptor{} - addDescriptor := func(name, version string) { - descriptors = append(descriptors, berryPossibleKeys(name, version)...) - } - - for dep, version := range b.Dependencies { - addDescriptor(dep, version) - } - - return descriptors -} - // BerryLockfile representation of berry lockfile type BerryLockfile struct { - packages map[_Locator]*BerryLockfileEntry - version int - cacheKey string - // Mapping descriptors (lodash@npm:^4.17.21) to their resolutions (lodash@npm:4.17.21) - descriptors map[_Descriptor]_Locator - // Mapping regular package locators to patched package locators - patches map[_Locator]_Locator - // Descriptors that are only used by package extensions - packageExtensions map[_Descriptor]_void - hasCRLF bool + contents []byte + resolutions map[string]string } // BerryDependencyMetaEntry Structure for holding if a package is optional or not @@ -89,621 +23,58 @@ var _ Lockfile = (*BerryLockfile)(nil) // ResolvePackage Given a package and version returns the key, resolved version, and if it was found func (l *BerryLockfile) ResolvePackage(_workspace turbopath.AnchoredUnixPath, name string, version string) (Package, error) { - for _, key := range berryPossibleKeys(name, version) { - if locator, ok := l.descriptors[key]; ok { - entry := l.packages[locator] - return Package{ - Found: true, - Key: locator.String(), - Version: entry.Version, - }, nil - } - } - - return Package{}, nil + panic("Should use Rust implementation") } // AllDependencies Given a lockfile key return all (dev/optional/peer) dependencies of that package func (l *BerryLockfile) AllDependencies(key string) (map[string]string, bool) { - deps := map[string]string{} - var locator _Locator - if err := locator.parseLocator(key); err != nil { - // We should never hit this as we have already vetted all entries in the lockfile - // during the creation of the lockfile struct - panic(fmt.Sprintf("invalid locator string: %s", key)) - } - entry, ok := l.packages[locator] - if !ok { - return deps, false - } - - for name, version := range entry.Dependencies { - deps[name] = version - } - - return deps, true + panic("Should use Rust implementation") } // Subgraph Given a list of lockfile keys returns a Lockfile based off the original one that only contains the packages given func (l *BerryLockfile) Subgraph(workspacePackages []turbopath.AnchoredSystemPath, packages []string) (Lockfile, error) { - prunedPackages := make(map[_Locator]*BerryLockfileEntry, len(packages)) - prunedDescriptors := make(map[_Descriptor]_Locator, len(prunedPackages)) - patches := make(map[_Locator]_Locator, len(l.patches)) - reverseLookup := l.locatorToDescriptors() - - // add workspace package entries - for locator, pkg := range l.packages { - if locator.reference == "workspace:." { - prunedPackages[locator] = pkg - descriptor := _Descriptor{locator._Ident, locator.reference} - prunedDescriptors[descriptor] = locator - for desc := range reverseLookup[locator] { - prunedDescriptors[desc] = locator - } - } - } - for _, workspacePackage := range workspacePackages { - expectedReference := fmt.Sprintf("workspace:%s", workspacePackage.ToUnixPath().ToString()) - for locator, pkg := range l.packages { - if locator.reference == expectedReference { - prunedPackages[locator] = pkg - descriptor := _Descriptor{locator._Ident, locator.reference} - prunedDescriptors[descriptor] = locator - } - } - } - - for _, key := range packages { - var locator _Locator - if err := locator.parseLocator(key); err != nil { - // We should never hit this as we have already vetted all entries in the lockfile - // during the creation of the lockfile struct - panic(fmt.Sprintf("invalid locator string: %s", key)) - } - entry, ok := l.packages[locator] - if ok { - prunedPackages[locator] = entry - } - // If a package has a patch it should be included in the subgraph - patchLocator, ok := l.patches[locator] - if ok { - patches[locator] = patchLocator - prunedPackages[patchLocator] = l.packages[patchLocator] - } - } - - for _, entry := range prunedPackages { - for _, desc := range entry.possibleDescriptors() { - locator, ok := l.descriptors[desc] - if ok { - prunedDescriptors[desc] = locator - } - } - } - - // For each patch we find all descriptors for the primary package and patched package - for primaryLocator, patchLocator := range patches { - primaryDescriptors := reverseLookup[primaryLocator] - patchDescriptors := reverseLookup[patchLocator] - - // For each patch descriptor we extract the primary descriptor that each patch descriptor targets - // and check if that descriptor is present in the pruned map and add it if it is present - for patch := range patchDescriptors { - primaryVersion, _ := patch.primaryVersion() - primaryDescriptor := _Descriptor{patch._Ident, primaryVersion} - _, isPresent := primaryDescriptors[primaryDescriptor] - if !isPresent { - panic(fmt.Sprintf("Unable to find primary descriptor %s", &primaryDescriptor)) - } - - _, ok := prunedDescriptors[primaryDescriptor] - if ok { - if !ok { - panic(fmt.Sprintf("Unable to find patch for %s", &patchLocator)) - } - prunedDescriptors[patch] = patchLocator - } - } - } - - // Add any descriptors used by package extensions - for descriptor := range l.packageExtensions { - locator := l.descriptors[descriptor] - _, ok := prunedPackages[locator] - if ok { - prunedDescriptors[descriptor] = locator - } + workspaces := make([]string, len(workspacePackages)) + for i, workspace := range workspacePackages { + workspaces[i] = workspace.ToUnixPath().ToString() } - - // berry only includes a cache key in the lockfile if there are entries with a checksum - cacheKey := "" - for _, entry := range prunedPackages { - if entry.Checksum != "" { - cacheKey = l.cacheKey - break - } + contents, err := ffi.Subgraph("berry", l.contents, workspaces, packages, l.resolutions) + if err != nil { + return nil, err } - - return &BerryLockfile{ - packages: prunedPackages, - version: l.version, - cacheKey: cacheKey, - descriptors: prunedDescriptors, - patches: patches, - packageExtensions: l.packageExtensions, - hasCRLF: l.hasCRLF, - }, nil + return &BerryLockfile{contents: contents, resolutions: l.resolutions}, nil } // Encode encode the lockfile representation and write it to the given writer func (l *BerryLockfile) Encode(w io.Writer) error { - // Map all resolved packages to the descriptors that match them - reverseLookup := l.locatorToDescriptors() - - lockfile := make(map[string]*BerryLockfileEntry, len(l.packages)) - - lockfile[_metadataKey] = &BerryLockfileEntry{ - Version: fmt.Sprintf("%d", l.version), - CacheKey: l.cacheKey, - } - - for locator, descriptors := range reverseLookup { - sortedDescriptors := make([]string, len(descriptors)) - i := 0 - for descriptor := range descriptors { - sortedDescriptors[i] = descriptor.String() - i++ - } - sort.Strings(sortedDescriptors) - - key := strings.Join(sortedDescriptors, ", ") - - entry, ok := l.packages[locator] - if !ok { - return fmt.Errorf("Unable to find entry for %s", &locator) - } - - lockfile[key] = entry - } - - if l.hasCRLF { - w = crlf.NewWriter(w) - } - - _, err := io.WriteString(w, `# This file is generated by running "yarn install" inside your project. -# Manual changes might be lost - proceed with caution! -`) - if err != nil { - return errors.Wrap(err, "unable to write header to lockfile") - } - - return _writeBerryLockfile(w, lockfile) -} - -// Invert the descriptor to locator map -func (l *BerryLockfile) locatorToDescriptors() map[_Locator]map[_Descriptor]_void { - reverseLookup := make(map[_Locator]map[_Descriptor]_void, len(l.packages)) - for descriptor, locator := range l.descriptors { - descriptors, ok := reverseLookup[locator] - if !ok { - reverseLookup[locator] = map[_Descriptor]_void{descriptor: {}} - } else { - descriptors[descriptor] = _void{} - } - } - - return reverseLookup + _, err := w.Write(l.contents) + return err } // Patches return a list of patches used in the lockfile func (l *BerryLockfile) Patches() []turbopath.AnchoredUnixPath { - patches := []turbopath.AnchoredUnixPath{} - - for _, patchLocator := range l.patches { - patchPath, isPatch := patchLocator.patchPath() - - if isPatch && !strings.HasPrefix(patchPath, "~") && !_builtinRegexp.MatchString(patchPath) { - patches = append(patches, turbopath.AnchoredUnixPath(patchPath)) - } - } - - if len(patches) == 0 { + rawPatches := ffi.Patches(l.contents, "berry") + if len(rawPatches) == 0 { return nil } - + patches := make([]turbopath.AnchoredUnixPath, len(rawPatches)) + for i, patch := range rawPatches { + patches[i] = turbopath.AnchoredUnixPath(patch) + } return patches } // DecodeBerryLockfile Takes the contents of a berry lockfile and returns a struct representation -func DecodeBerryLockfile(contents []byte) (*BerryLockfile, error) { - var packages map[string]*BerryLockfileEntry - - hasCRLF := bytes.HasSuffix(contents, _crlfLiteral) - err := yaml.Unmarshal(contents, &packages) - if err != nil { - return &BerryLockfile{}, fmt.Errorf("could not unmarshal lockfile: %w", err) - } - - metadata, ok := packages[_metadataKey] - if !ok { - return nil, errors.New("No __metadata entry found when decoding yarn.lock") - } - version, err := strconv.Atoi(metadata.Version) - if err != nil { - return nil, errors.Wrap(err, "yarn lockfile version isn't valid integer") - } - delete(packages, _metadataKey) - - locatorToPackage := map[_Locator]*BerryLockfileEntry{} - descriptorToLocator := map[_Descriptor]_Locator{} - // A map from packages to their patch entries - patches := map[_Locator]_Locator{} - - for key, data := range packages { - var locator _Locator - if err := locator.parseLocator(data.Resolution); err != nil { - return nil, errors.Wrap(err, "unable to parse entry") - } - - if _, isPatch := locator.patchPath(); isPatch { - // A patch will have the same identifier and version allowing us to construct the non-patch entry - originalLocator := _Locator{locator._Ident, fmt.Sprintf("npm:%s", data.Version)} - patches[originalLocator] = locator - } - - // Before storing cacheKey set it to "" so we know it's invalid - data.CacheKey = "" - - locatorToPackage[locator] = data - - // All descriptors that resolve to a single locator are grouped into a single key - for _, entry := range _multipleKeyRegex.Split(key, -1) { - descriptor := _Descriptor{} - if err := descriptor.parseDescriptor(entry); err != nil { - return nil, errors.Wrap(err, "Bad entry key found") - } - - // Before lockfile version 6 descriptors could be missing the npm protocol - if version <= 6 && descriptor.versionRange != "*" { - _, err := semver.NewConstraint(descriptor.versionRange) - if err == nil || _tagRegex.MatchString(descriptor.versionRange) { - descriptor.versionRange = fmt.Sprintf("npm:%s", descriptor.versionRange) - } - } - - descriptorToLocator[descriptor] = locator - } - } - - // Build up list of all descriptors in the file - packageExtensions := make(map[_Descriptor]_void, len(descriptorToLocator)) - for descriptor := range descriptorToLocator { - if descriptor.protocol() == "npm" { - packageExtensions[descriptor] = _void{} - } - } - // Remove any that are found in the lockfile entries - for _, entry := range packages { - for _, descriptor := range entry.possibleDescriptors() { - delete(packageExtensions, descriptor) - } - } - - lockfile := BerryLockfile{ - packages: locatorToPackage, - version: version, - cacheKey: metadata.CacheKey, - descriptors: descriptorToLocator, - patches: patches, - packageExtensions: packageExtensions, - hasCRLF: hasCRLF, - } - return &lockfile, nil +func DecodeBerryLockfile(contents []byte, resolutions map[string]string) (*BerryLockfile, error) { + return &BerryLockfile{contents: contents, resolutions: resolutions}, nil } // GlobalChange checks if there are any differences between lockfiles that would completely invalidate // the cache. func (l *BerryLockfile) GlobalChange(other Lockfile) bool { - otherBerry, ok := other.(*BerryLockfile) - return !ok || - l.cacheKey != otherBerry.cacheKey || - l.version != otherBerry.version || - // This is probably overly cautious, but getting it correct will be hard - !reflect.DeepEqual(l.patches, otherBerry.patches) -} - -// Fields shared between _Locator and _Descriptor -type _Ident struct { - // Scope of package without leading @ - scope string - // Name of package - name string -} - -type _Locator struct { - _Ident - // Resolved version e.g. 1.2.3 - reference string -} - -type _Descriptor struct { - _Ident - // Version range e.g. ^1.0.0 - // Can be prefixed with the protocol e.g. npm, workspace, patch, - versionRange string -} - -func (i _Ident) String() string { - if i.scope == "" { - return i.name - } - return fmt.Sprintf("@%s/%s", i.scope, i.name) -} - -var _locatorRegexp = regexp.MustCompile("^(?:@([^/]+?)/)?([^/]+?)(?:@(.+))$") - -func (l *_Locator) parseLocator(data string) error { - matches := _locatorRegexp.FindStringSubmatch(data) - if len(matches) != 4 { - return fmt.Errorf("%s is not a valid locator string", data) - } - l.scope = matches[1] - l.name = matches[2] - l.reference = matches[3] - - return nil -} - -func (l *_Locator) String() string { - if l.scope == "" { - return fmt.Sprintf("%s@%s", l.name, l.reference) - } - return fmt.Sprintf("@%s/%s@%s", l.scope, l.name, l.reference) -} - -var _builtinRegexp = regexp.MustCompile("^builtin<([^>]+)>$") - -func (l *_Locator) patchPath() (string, bool) { - if strings.HasPrefix(l.reference, "patch:") { - patchFileIndex := strings.Index(l.reference, "#") - paramIndex := strings.LastIndex(l.reference, "::") - if patchFileIndex == -1 || paramIndex == -1 { - // Better error handling - panic("Unable to extract patch file path from lockfile entry") - } - patchPath := strings.TrimPrefix(l.reference[patchFileIndex+1:paramIndex], "./") - - return patchPath, true - } - - return "", false -} - -var _descriptorRegexp = regexp.MustCompile("^(?:@([^/]+?)/)?([^/]+?)(?:@(.+))?$") - -func (d *_Descriptor) parseDescriptor(data string) error { - matches := _descriptorRegexp.FindStringSubmatch(data) - if len(matches) != 4 { - return fmt.Errorf("%s is not a valid descriptor string", data) - } - - d.scope = matches[1] - d.name = matches[2] - d.versionRange = matches[3] - - return nil -} - -// If the descriptor is for a patch it will return the primary descriptor that it patches -func (d *_Descriptor) primaryVersion() (string, bool) { - if !strings.HasPrefix(d.versionRange, "patch:") { - return "", false - } - patchFileIndex := strings.Index(d.versionRange, "#") - versionRangeIndex := strings.Index(d.versionRange, "@") - if patchFileIndex < 0 || versionRangeIndex < 0 { - panic("Patch reference is missing required markers") - } - // The ':' following npm protocol gets encoded as '%3A' in the patch string - version := strings.Replace(d.versionRange[versionRangeIndex+1:patchFileIndex], "%3A", ":", 1) - if !strings.HasPrefix(version, "npm:") { - version = fmt.Sprintf("npm:%s", version) - } - - return version, true -} - -// Returns the protocol of the descriptor -func (d *_Descriptor) protocol() string { - if index := strings.Index(d.versionRange, ":"); index > 0 { - return d.versionRange[:index] - } - return "" -} - -func (d *_Descriptor) String() string { - if d.scope == "" { - return fmt.Sprintf("%s@%s", d.name, d.versionRange) - } - return fmt.Sprintf("@%s/%s@%s", d.scope, d.name, d.versionRange) -} - -func berryPossibleKeys(name string, version string) []_Descriptor { - makeDescriptor := func(protocol string) _Descriptor { - descriptorString := fmt.Sprintf("%s@%s%s", name, protocol, version) - var descriptor _Descriptor - if err := descriptor.parseDescriptor(descriptorString); err != nil { - panic("Generated invalid descriptor") - } - return descriptor - } - return []_Descriptor{ - makeDescriptor(""), - makeDescriptor("npm:"), - makeDescriptor("file:"), - makeDescriptor("workspace:"), - makeDescriptor("yarn:"), - } -} - -func _writeBerryLockfile(w io.Writer, lockfile map[string]*BerryLockfileEntry) error { - keys := make([]string, len(lockfile)) - i := 0 - for key := range lockfile { - keys[i] = key - i++ - } - - // The __metadata key gets hoisted to the top - sort.Slice(keys, func(i, j int) bool { - if keys[i] == _metadataKey { - return true - } else if keys[j] == _metadataKey { - return false - } - return keys[i] < keys[j] - }) - - for _, key := range keys { - value, ok := lockfile[key] - if !ok { - panic(fmt.Sprintf("Unable to find entry for %s", key)) - } - - wrappedKey := _wrapString(key) - wrappedValue := _stringifyEntry(*value, 1) - - var keyPart string - if len(wrappedKey) > 1024 { - keyPart = fmt.Sprintf("? %s\n:", keyPart) - } else { - keyPart = fmt.Sprintf("%s:", wrappedKey) - } - - _, err := io.WriteString(w, fmt.Sprintf("\n%s\n%s\n", keyPart, wrappedValue)) - if err != nil { - return errors.Wrap(err, "unable to write to lockfile") - } - } - - return nil -} - -var _simpleStringPattern = regexp.MustCompile("^[^-?:,\\][{}#&*!|>'\"%@` \t\r\n]([ \t]*[^,\\][{}:# \t\r\n])*$") - -func _wrapString(str string) string { - if !_simpleStringPattern.MatchString(str) { - var b bytes.Buffer - encoder := json.NewEncoder(&b) - encoder.SetEscapeHTML(false) - err := encoder.Encode(str) - if err != nil { - panic("Unexpected error wrapping key") - } - - return strings.TrimRight(b.String(), "\n") - } - return str -} - -func _stringifyEntry(entry BerryLockfileEntry, indentLevel int) string { - lines := []string{} - addLine := func(field, value string, inline bool) { - var line string - if inline { - line = fmt.Sprintf(" %s: %s", field, value) - } else { - line = fmt.Sprintf(" %s:\n%s", field, value) - } - lines = append(lines, line) - } - - if entry.Version != "" { - addLine("version", _wrapString(entry.Version), true) - } - if entry.Resolution != "" { - addLine("resolution", _wrapString(entry.Resolution), true) - } - if len(entry.Dependencies) > 0 { - addLine("dependencies", _stringifyDeps(entry.Dependencies), false) - } - if len(entry.PeerDependencies) > 0 { - addLine("peerDependencies", _stringifyDeps(entry.PeerDependencies), false) - } - if len(entry.DependenciesMeta) > 0 { - addLine("dependenciesMeta", _stringifyDepsMeta(entry.DependenciesMeta), false) - } - if len(entry.PeerDependenciesMeta) > 0 { - addLine("peerDependenciesMeta", _stringifyDepsMeta(entry.PeerDependenciesMeta), false) - } - - if len(entry.Bin) > 0 { - addLine("bin", _stringifyDeps(entry.Bin), false) - } - if entry.Checksum != "" { - addLine("checksum", _wrapString(entry.Checksum), true) - } - if entry.Conditions != "" { - addLine("conditions", _wrapString(entry.Conditions), true) - } - if entry.LanguageName != "" { - addLine("languageName", _wrapString(entry.LanguageName), true) - } - if entry.LinkType != "" { - addLine("linkType", _wrapString(entry.LinkType), true) - } - if entry.CacheKey != "" { - addLine("cacheKey", _wrapString(entry.CacheKey), true) - } - - return strings.Join(lines, "\n") -} - -func _stringifyDeps(deps map[string]string) string { - keys := make([]string, len(deps)) - i := 0 - for key := range deps { - keys[i] = key - i++ - } - sort.Strings(keys) - - lines := make([]string, 0, len(deps)) - addLine := func(name, version string) { - lines = append(lines, fmt.Sprintf(" %s: %s", _wrapString(name), _wrapString(version))) - } - - for _, name := range keys { - version := deps[name] - addLine(name, version) - } - - return strings.Join(lines, "\n") -} - -func _stringifyDepsMeta(meta map[string]BerryDependencyMetaEntry) string { - keys := make([]string, len(meta)) - i := 0 - for key := range meta { - keys[i] = key - i++ - } - sort.Strings(keys) - - lines := make([]string, 0, len(meta)) - addLine := func(name string, key string) { - lines = append(lines, fmt.Sprintf(" %s:\n %s: true", _wrapString(name), key)) - } - - for _, name := range keys { - optional := meta[name] - if optional.Optional { - addLine(name, "optional") - } - if optional.Unplugged { - addLine(name, "unplugged") - } + o, ok := other.(*BerryLockfile) + if !ok { + return true } - return strings.Join(lines, "\n") + return ffi.GlobalChange("berry", o.contents, l.contents) } diff --git a/cli/internal/lockfile/berry_lockfile_test.go b/cli/internal/lockfile/berry_lockfile_test.go index afcbe46b20623..66e6894f04f43 100644 --- a/cli/internal/lockfile/berry_lockfile_test.go +++ b/cli/internal/lockfile/berry_lockfile_test.go @@ -1,273 +1,47 @@ package lockfile import ( - "bytes" "testing" "github.com/vercel/turbo/cli/internal/turbopath" "gotest.tools/v3/assert" ) -func getBerryLockfile(t *testing.T, filename string) *BerryLockfile { - content, err := getFixture(t, filename) - if err != nil { - t.Error(err) - } - lockfile, err := DecodeBerryLockfile(content) - if err != nil { - t.Error(err) - } - return lockfile -} - -func Test_DecodingBerryLockfile(t *testing.T) { - lockfile := getBerryLockfile(t, "berry.lock") - assert.Equal(t, lockfile.version, 6) - assert.Equal(t, lockfile.cacheKey, "8c0") -} - -func Test_ResolvePackage(t *testing.T) { - lockfile := getBerryLockfile(t, "berry.lock") - - type Case struct { - name string - semver string - key string - version string - found bool - } - - cases := map[string]Case{ - "can resolve '||' semver syntax": { - name: "js-tokens", - semver: "^3.0.0 || ^4.0.0", - key: "js-tokens@npm:4.0.0", - version: "4.0.0", - found: true, - }, - "handles packages with multiple descriptors": { - name: "js-tokens", - semver: "^4.0.0", - key: "js-tokens@npm:4.0.0", - version: "4.0.0", - found: true, - }, - "doesn't find nonexistent descriptors": { - name: "@babel/code-frame", - semver: "^7.12.11", - found: false, - }, - "handles workspace packages": { - name: "eslint-config-custom", - semver: "*", - key: "eslint-config-custom@workspace:packages/eslint-config-custom", - version: "0.0.0-use.local", - found: true, - }, - } - - for testName, testCase := range cases { - pkg, err := lockfile.ResolvePackage("some-pkg", testCase.name, testCase.semver) - assert.NilError(t, err) - if testCase.found { - assert.Equal(t, pkg.Key, testCase.key, testName) - assert.Equal(t, pkg.Version, testCase.version, testName) - } - assert.Equal(t, pkg.Found, testCase.found, testName) - } -} - -func Test_AllDependencies(t *testing.T) { - lockfile := getBerryLockfile(t, "berry.lock") - - pkg, err := lockfile.ResolvePackage("some-pkg", "react-dom", "18.2.0") +func Test_BerryPatches(t *testing.T) { + contents := getRustFixture(t, "berry.lock") + lf, err := DecodeBerryLockfile(contents, nil) assert.NilError(t, err) - assert.Assert(t, pkg.Found, "expected to find react-dom") - deps, found := lockfile.AllDependencies(pkg.Key) - assert.Assert(t, found, "expected lockfile key for react-dom to be present") - assert.Equal(t, len(deps), 2, "expected to find all react-dom direct dependencies") - for pkgName, version := range deps { - pkg, err := lockfile.ResolvePackage("some-pkg", pkgName, version) - assert.NilError(t, err, "error finding %s@%s", pkgName, version) - assert.Assert(t, pkg.Found, "expected to find lockfile entry for %s@%s", pkgName, version) - } -} - -func Test_BerryPatchList(t *testing.T) { - lockfile := getBerryLockfile(t, "berry.lock") - - var locator _Locator - if err := locator.parseLocator("resolve@npm:2.0.0-next.4"); err != nil { - t.Error(err) - } - - patchLocator, ok := lockfile.patches[locator] - assert.Assert(t, ok, "Expected to find patch locator") - patch, ok := lockfile.packages[patchLocator] - assert.Assert(t, ok, "Expected to find patch") - assert.Equal(t, patch.Version, "2.0.0-next.4") -} - -func Test_PackageExtensions(t *testing.T) { - lockfile := getBerryLockfile(t, "berry.lock") - - expectedExtensions := map[_Descriptor]_void{} - for _, extension := range []string{"@babel/types@npm:^7.8.3", "lodash@npm:4.17.21"} { - var extensionDescriptor _Descriptor - if err := extensionDescriptor.parseDescriptor(extension); err != nil { - t.Error(err) - } - expectedExtensions[extensionDescriptor] = _void{} - } - - assert.DeepEqual(t, lockfile.packageExtensions, expectedExtensions) -} - -func Test_StringifyMetadata(t *testing.T) { - metadata := BerryLockfileEntry{ - Version: "6", - CacheKey: "8c0", - } - lockfile := map[string]*BerryLockfileEntry{"__metadata": &metadata} - - var b bytes.Buffer - err := _writeBerryLockfile(&b, lockfile) - assert.Assert(t, err == nil) - assert.Equal(t, b.String(), ` -__metadata: - version: 6 - cacheKey: 8c0 -`) -} - -func Test_BerryRoundtrip(t *testing.T) { - content, err := getFixture(t, "berry.lock") - if err != nil { - t.Error(err) - } - lockfile, err := DecodeBerryLockfile(content) - if err != nil { - t.Error(err) - } - - var b bytes.Buffer - if err := lockfile.Encode(&b); err != nil { - t.Error(err) - } - - assert.Equal(t, b.String(), string(content)) -} - -func Test_PatchPathExtraction(t *testing.T) { - type Case struct { - locator string - patchPath string - isPatch bool - } - cases := []Case{ - { - locator: "lodash@patch:lodash@npm%3A4.17.21#./.yarn/patches/lodash-npm-4.17.21-6382451519.patch::version=4.17.21&hash=2c6e9e&locator=berry-patch%40workspace%3A.", - patchPath: ".yarn/patches/lodash-npm-4.17.21-6382451519.patch", - isPatch: true, - }, - { - locator: "lodash@npm:4.17.21", - isPatch: false, - }, - { - locator: "resolve@patch:resolve@npm%3A2.0.0-next.4#~builtin::version=2.0.0-next.4&hash=07638b", - patchPath: "~builtin", - isPatch: true, - }, - } - - for _, testCase := range cases { - var locator _Locator - err := locator.parseLocator(testCase.locator) - if err != nil { - t.Error(err) - } - patchPath, isPatch := locator.patchPath() - assert.Equal(t, isPatch, testCase.isPatch, locator) - assert.Equal(t, patchPath, testCase.patchPath, locator) - } + patches := lf.Patches() + assert.DeepEqual(t, patches, []turbopath.AnchoredUnixPath{".yarn/patches/lodash-npm-4.17.21-6382451519.patch"}) } -func Test_PatchPrimaryVersion(t *testing.T) { - // todo write tests to make sure extraction actually works - type TestCase struct { - descriptor string - version string - isPatch bool - } - testCases := []TestCase{ - { - descriptor: "lodash@patch:lodash@npm%3A4.17.21#./.yarn/patches/lodash-npm-4.17.21-6382451519.patch::locator=berry-patch%40workspace%3A.", - version: "npm:4.17.21", - isPatch: true, - }, - { - descriptor: "typescript@patch:typescript@^4.5.2#~builtin", - version: "npm:^4.5.2", - isPatch: true, - }, - { - descriptor: "react@npm:18.2.0", - isPatch: false, - }, - } - - for _, testCase := range testCases { - var d _Descriptor - err := d.parseDescriptor(testCase.descriptor) - assert.NilError(t, err, testCase.descriptor) - actual, isPatch := d.primaryVersion() - assert.Equal(t, isPatch, testCase.isPatch, testCase) - if testCase.isPatch { - assert.Equal(t, actual, testCase.version, testCase.descriptor) - } - } +func Test_EmptyBerryPatches(t *testing.T) { + contents := getRustFixture(t, "minimal-berry.lock") + lf, err := DecodeBerryLockfile(contents, nil) + assert.NilError(t, err) + patches := lf.Patches() + assert.Assert(t, patches == nil) } -func Test_BerryPruneDescriptors(t *testing.T) { - lockfile := getBerryLockfile(t, "minimal-berry.lock") - prunedLockfile, err := lockfile.Subgraph( - []turbopath.AnchoredSystemPath{ - turbopath.AnchoredUnixPath("packages/a").ToSystemPath(), - turbopath.AnchoredUnixPath("packages/c").ToSystemPath(), +func Test_BerryTransitiveClosure(t *testing.T) { + contents := getRustFixture(t, "berry.lock") + lf, err := DecodeBerryLockfile(contents, map[string]string{"lodash@^4.17.21": "patch:lodash@npm%3A4.17.21#./.yarn/patches/lodash-npm-4.17.21-6382451519.patch"}) + assert.NilError(t, err) + closures, err := AllTransitiveClosures(map[turbopath.AnchoredUnixPath]map[string]string{ + turbopath.AnchoredUnixPath(""): {}, + turbopath.AnchoredUnixPath("apps/web"): {}, + turbopath.AnchoredUnixPath("apps/docs"): { + "lodash": "^4.17.21", }, - []string{"lodash@npm:4.17.21"}, - ) - if err != nil { - t.Error(err) - } - lockfileA := prunedLockfile.(*BerryLockfile) + }, lf) + assert.NilError(t, err) + assert.Equal(t, len(closures), 3) - prunedLockfile, err = lockfile.Subgraph( - []turbopath.AnchoredSystemPath{ - turbopath.AnchoredUnixPath("packages/b").ToSystemPath(), - turbopath.AnchoredUnixPath("packages/c").ToSystemPath(), - }, - []string{"lodash@npm:4.17.21"}, - ) - if err != nil { - t.Error(err) + lodash := Package{ + Key: "lodash@npm:4.17.21", + Version: "4.17.21", + Found: true, } - lockfileB := prunedLockfile.(*BerryLockfile) - - lodashIdent := _Ident{name: "lodash"} - lodashA := _Descriptor{lodashIdent, "npm:^4.17.0"} - lodashB := _Descriptor{lodashIdent, "npm:^3.0.0 || ^4.0.0"} - - lodashEntryA, hasLodashA := lockfileA.descriptors[lodashA] - lodashEntryB, hasLodashB := lockfileB.descriptors[lodashB] - - assert.Assert(t, hasLodashA, "Expected lockfile a to have descriptor used by a") - assert.Assert(t, hasLodashB, "Expected lockfile b to have descriptor used by b") - assert.DeepEqual(t, lodashEntryA.reference, lodashEntryB.reference) - - _, lockfileAHasB := lockfileA.descriptors[lodashB] - _, lockfileBHasA := lockfileB.descriptors[lodashA] - assert.Assert(t, !lockfileAHasB, "Expected lockfile a not to have descriptor used by b") - assert.Assert(t, !lockfileBHasA, "Expected lockfile b not to have descriptor used by a") + assert.Assert(t, !closures[turbopath.AnchoredUnixPath("apps/web")].Contains(lodash)) + assert.Assert(t, closures[turbopath.AnchoredUnixPath("apps/docs")].Contains(lodash)) } diff --git a/cli/internal/lockfile/lockfile.go b/cli/internal/lockfile/lockfile.go index b24deee88f818..81667fde7abdc 100644 --- a/cli/internal/lockfile/lockfile.go +++ b/cli/internal/lockfile/lockfile.go @@ -72,9 +72,12 @@ func AllTransitiveClosures( workspaces map[turbopath.AnchoredUnixPath]map[string]string, lockFile Lockfile, ) (map[turbopath.AnchoredUnixPath]mapset.Set, error) { + // We special case as Rust implementations have their own dep crawl if lf, ok := lockFile.(*NpmLockfile); ok { - // We special case as Rust implementations have their own dep crawl - return rustTransitiveDeps(lf.contents, "npm", workspaces) + return rustTransitiveDeps(lf.contents, "npm", workspaces, nil) + } + if lf, ok := lockFile.(*BerryLockfile); ok { + return rustTransitiveDeps(lf.contents, "berry", workspaces, lf.resolutions) } g := new(errgroup.Group) @@ -163,12 +166,12 @@ func transitiveClosureHelper( } } -func rustTransitiveDeps(content []byte, packageManager string, workspaces map[turbopath.AnchoredUnixPath]map[string]string) (map[turbopath.AnchoredUnixPath]mapset.Set, error) { +func rustTransitiveDeps(content []byte, packageManager string, workspaces map[turbopath.AnchoredUnixPath]map[string]string, resolutions map[string]string) (map[turbopath.AnchoredUnixPath]mapset.Set, error) { processedWorkspaces := make(map[string]map[string]string, len(workspaces)) for workspacePath, workspace := range workspaces { processedWorkspaces[workspacePath.ToString()] = workspace } - workspaceDeps, err := ffi.TransitiveDeps(content, packageManager, processedWorkspaces) + workspaceDeps, err := ffi.TransitiveDeps(content, packageManager, processedWorkspaces, resolutions) if err != nil { return nil, err } diff --git a/cli/internal/lockfile/npm_lockfile.go b/cli/internal/lockfile/npm_lockfile.go index dceb560f4c782..f2282e979c81e 100644 --- a/cli/internal/lockfile/npm_lockfile.go +++ b/cli/internal/lockfile/npm_lockfile.go @@ -1,7 +1,6 @@ package lockfile import ( - "encoding/json" "io" "github.com/vercel/turbo/cli/internal/ffi" @@ -34,7 +33,7 @@ func (l *NpmLockfile) Subgraph(workspacePackages []turbopath.AnchoredSystemPath, for i, workspace := range workspacePackages { workspaces[i] = workspace.ToUnixPath().ToString() } - contents, err := ffi.NpmSubgraph(l.contents, workspaces, packages) + contents, err := ffi.Subgraph("npm", l.contents, workspaces, packages, nil) if err != nil { return nil, err } @@ -60,22 +59,7 @@ func (l *NpmLockfile) GlobalChange(other Lockfile) bool { return true } - // We just grab the few global fields and check if they've changed - type minimalJSON struct { - LockfileVersion string `json:"version"` - Requires bool `json:"requires"` - } - var self minimalJSON - var otherJSON minimalJSON - if err := json.Unmarshal(o.contents, &otherJSON); err != nil { - return true - } - if err := json.Unmarshal(l.contents, &self); err != nil { - return true - } - - return self.LockfileVersion != otherJSON.LockfileVersion || - self.Requires != otherJSON.Requires + return ffi.GlobalChange("npm", o.contents, l.contents) } var _ (Lockfile) = (*NpmLockfile)(nil) diff --git a/cli/internal/packagemanager/berry.go b/cli/internal/packagemanager/berry.go index d6264b1c6c2e0..080f9a328fd77 100644 --- a/cli/internal/packagemanager/berry.go +++ b/cli/internal/packagemanager/berry.go @@ -113,8 +113,20 @@ var nodejsBerry = PackageManager{ return true, nil }, - UnmarshalLockfile: func(_rootPackageJSON *fs.PackageJSON, contents []byte) (lockfile.Lockfile, error) { - return lockfile.DecodeBerryLockfile(contents) + UnmarshalLockfile: func(rootPackageJSON *fs.PackageJSON, contents []byte) (lockfile.Lockfile, error) { + var resolutions map[string]string + if untypedResolutions, ok := rootPackageJSON.RawJSON["resolutions"]; ok { + if untypedResolutions, ok := untypedResolutions.(map[string]interface{}); ok { + resolutions = make(map[string]string, len(untypedResolutions)) + for resolution, reference := range untypedResolutions { + if reference, ok := reference.(string); ok { + resolutions[resolution] = reference + } + } + } + } + + return lockfile.DecodeBerryLockfile(contents, resolutions) }, prunePatches: func(pkgJSON *fs.PackageJSON, patches []turbopath.AnchoredUnixPath) error { diff --git a/crates/turborepo-ffi/messages.proto b/crates/turborepo-ffi/messages.proto index 31bd10d514d1b..b609cab5ac1c1 100644 --- a/crates/turborepo-ffi/messages.proto +++ b/crates/turborepo-ffi/messages.proto @@ -56,6 +56,7 @@ message PreviousContentResp { enum PackageManager { NPM = 0; + BERRY = 1; } message PackageDependency { @@ -75,6 +76,7 @@ message TransitiveDepsRequest { bytes contents = 1; PackageManager package_manager = 2; map workspaces = 3; + optional AdditionalBerryData resolutions = 4; } message TransitiveDepsResponse { @@ -84,6 +86,10 @@ message TransitiveDepsResponse { } } +message AdditionalBerryData { + map resolutions = 1; +} + message LockfilePackage { string key = 1; string version = 2; @@ -96,9 +102,10 @@ message LockfilePackageList { message SubgraphRequest { bytes contents = 1; - string package_manager = 2; + PackageManager package_manager = 2; repeated string workspaces = 3; repeated string packages = 4; + optional AdditionalBerryData resolutions = 5; } message SubgraphResponse { @@ -107,3 +114,29 @@ message SubgraphResponse { string error = 2; } } + +message PatchesRequest { + bytes contents = 1; + PackageManager package_manager = 2; +} + +message PatchesResponse { + oneof response { + Patches patches = 1; + string error = 2; + } +} + +message Patches { + repeated string patches = 1; +} + +message GlobalChangeRequest { + PackageManager package_manager = 1; + bytes prev_contents = 2; + bytes curr_contents = 3; +} + +message GlobalChangeResponse { + bool global_change = 1; +} diff --git a/crates/turborepo-ffi/src/lib.rs b/crates/turborepo-ffi/src/lib.rs index 720853594463b..52f97c64c0522 100644 --- a/crates/turborepo-ffi/src/lib.rs +++ b/crates/turborepo-ffi/src/lib.rs @@ -6,7 +6,7 @@ mod lockfile; use std::{mem::ManuallyDrop, path::PathBuf}; -pub use lockfile::{npm_subgraph, transitive_closure}; +pub use lockfile::{patches, subgraph, transitive_closure}; mod proto { include!(concat!(env!("OUT_DIR"), "/_.rs")); diff --git a/crates/turborepo-ffi/src/lockfile.rs b/crates/turborepo-ffi/src/lockfile.rs index 326b6cd11d399..7864610749e08 100644 --- a/crates/turborepo-ffi/src/lockfile.rs +++ b/crates/turborepo-ffi/src/lockfile.rs @@ -4,7 +4,7 @@ use std::{ }; use thiserror::Error; -use turborepo_lockfiles::{self, npm_subgraph as real_npm_subgraph, NpmLockfile, Package}; +use turborepo_lockfiles::{self, BerryLockfile, LockfileData, NpmLockfile, Package}; use super::{proto, Buffer}; @@ -21,10 +21,14 @@ impl From for proto::LockfilePackage { #[derive(Debug, Error)] enum Error { - #[error("error performing lockfile operation")] + #[error("error performing lockfile operation: {0}")] Lockfile(#[from] turborepo_lockfiles::Error), #[error("error decoding protobuf")] Protobuf(#[from] prost::DecodeError), + #[error(transparent)] + BerryParse(#[from] turborepo_lockfiles::BerryError), + #[error("unsupported package manager {0}")] + UnsupportedPackageManager(proto::PackageManager), } #[no_mangle] @@ -42,8 +46,10 @@ pub extern "C" fn transitive_closure(buf: Buffer) -> Buffer { fn transitive_closure_inner(buf: Buffer) -> Result { let request: proto::TransitiveDepsRequest = buf.into_proto()?; + match request.package_manager() { proto::PackageManager::Npm => npm_transitive_closure_inner(request), + proto::PackageManager::Berry => berry_transitive_closure_inner(request), } } @@ -56,25 +62,38 @@ fn npm_transitive_closure_inner( .. } = request; let lockfile = NpmLockfile::load(contents.as_slice())?; - let dependencies = workspaces - .into_iter() - .map(|(workspace_dir, dependencies)| { - let closure = turborepo_lockfiles::transitive_closure( - &lockfile, - &workspace_dir, - dependencies.into(), - )?; - Ok((workspace_dir, proto::LockfilePackageList::from(closure))) - }) - .collect::, Error>>()?; - Ok(proto::WorkspaceDependencies { dependencies }) + let dependencies = turborepo_lockfiles::all_transitive_closures( + &lockfile, + workspaces.into_iter().map(|(k, v)| (k, v.into())).collect(), + )?; + Ok(dependencies.into()) +} + +fn berry_transitive_closure_inner( + request: proto::TransitiveDepsRequest, +) -> Result { + let proto::TransitiveDepsRequest { + contents, + workspaces, + resolutions, + .. + } = request; + let resolutions = + resolutions.map(|r| turborepo_lockfiles::BerryManifest::with_resolutions(r.resolutions)); + let data = LockfileData::from_bytes(contents.as_slice())?; + let lockfile = BerryLockfile::new(&data, resolutions.as_ref())?; + let dependencies = turborepo_lockfiles::all_transitive_closures( + &lockfile, + workspaces.into_iter().map(|(k, v)| (k, v.into())).collect(), + )?; + Ok(dependencies.into()) } #[no_mangle] -pub extern "C" fn npm_subgraph(buf: Buffer) -> Buffer { +pub extern "C" fn subgraph(buf: Buffer) -> Buffer { use proto::subgraph_response::Response; proto::SubgraphResponse { - response: Some(match npm_subgraph_inner(buf) { + response: Some(match subgraph_inner(buf) { Ok(contents) => Response::Contents(contents), Err(err) => Response::Error(err.to_string()), }), @@ -82,12 +101,85 @@ pub extern "C" fn npm_subgraph(buf: Buffer) -> Buffer { .into() } -fn npm_subgraph_inner(buf: Buffer) -> Result, Error> { +fn subgraph_inner(buf: Buffer) -> Result, Error> { let request: proto::SubgraphRequest = buf.into_proto()?; - let contents = real_npm_subgraph(&request.contents, &request.workspaces, &request.packages)?; + let package_manager = request.package_manager(); + let proto::SubgraphRequest { + contents, + workspaces, + packages, + resolutions, + .. + } = request; + let contents = match package_manager { + proto::PackageManager::Npm => { + turborepo_lockfiles::npm_subgraph(&contents, &workspaces, &packages)? + } + proto::PackageManager::Berry => turborepo_lockfiles::berry_subgraph( + &contents, + &workspaces, + &packages, + resolutions.map(|res| res.resolutions), + )?, + }; Ok(contents) } +#[no_mangle] +pub extern "C" fn patches(buf: Buffer) -> Buffer { + use proto::patches_response::Response; + proto::PatchesResponse { + response: Some(match patches_internal(buf) { + Ok(patches) => Response::Patches(patches), + Err(err) => Response::Error(err.to_string()), + }), + } + .into() +} + +fn patches_internal(buf: Buffer) -> Result { + let request: proto::PatchesRequest = buf.into_proto()?; + let patches = match request.package_manager() { + proto::PackageManager::Berry => { + let data = LockfileData::from_bytes(&request.contents)?; + let lockfile = BerryLockfile::new(&data, None)?; + Ok(lockfile + .patches() + .into_iter() + .map(|p| { + p.to_str() + .expect("patch coming from yarn.lock isn't valid utf8") + .to_string() + }) + .collect::>()) + } + pm => Err(Error::UnsupportedPackageManager(pm)), + }?; + Ok(proto::Patches { patches }) +} + +#[no_mangle] +pub extern "C" fn global_change(buf: Buffer) -> Buffer { + // If there's any issue checking if there's been a global lockfile change + // we assume one has changed. + let global_change = global_change_inner(buf).unwrap_or(true); + proto::GlobalChangeResponse { global_change }.into() +} + +fn global_change_inner(buf: Buffer) -> Result { + let request: proto::GlobalChangeRequest = buf.into_proto()?; + match request.package_manager() { + proto::PackageManager::Npm => Ok(turborepo_lockfiles::npm_global_change( + &request.prev_contents, + &request.curr_contents, + )?), + proto::PackageManager::Berry => Ok(turborepo_lockfiles::berry_global_change( + &request.prev_contents, + &request.curr_contents, + )?), + } +} + impl From for HashMap { fn from(other: proto::PackageDependencyList) -> Self { other @@ -109,10 +201,24 @@ impl From> for proto::LockfilePackageList { } } +impl From>> for proto::WorkspaceDependencies { + fn from(value: HashMap>) -> Self { + proto::WorkspaceDependencies { + dependencies: value + .into_iter() + .map(|(workspace, dependencies)| { + (workspace, proto::LockfilePackageList::from(dependencies)) + }) + .collect(), + } + } +} + impl fmt::Display for proto::PackageManager { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(match self { proto::PackageManager::Npm => "npm", + proto::PackageManager::Berry => "berry", }) } } diff --git a/crates/turborepo-lockfiles/src/berry/identifiers.rs b/crates/turborepo-lockfiles/src/berry/identifiers.rs index bb0e147268848..ea3b07ed9ae05 100644 --- a/crates/turborepo-lockfiles/src/berry/identifiers.rs +++ b/crates/turborepo-lockfiles/src/berry/identifiers.rs @@ -200,7 +200,7 @@ impl<'a> Locator<'a> { }) } - fn from_patch_reference(patch_reference: &'a str) -> Option { + pub fn from_patch_reference(patch_reference: &'a str) -> Option { let caps = patch_ref().captures(patch_reference)?; let capture_group = caps.get(1)?; let Locator { ident, reference } = Locator::try_from(capture_group.as_str()).ok()?; @@ -238,14 +238,28 @@ impl<'a> Locator<'a> { patch_ref() .captures(&self.reference) .and_then(|caps| caps.get(2)) - .map(|m| m.as_str()) + .map(|m| { + let s = m.as_str(); + s.strip_prefix("./").unwrap_or(s) + }) } pub fn patched_locator(&self) -> Option { + // THis has an issue of cutting off the last char Locator::from_patch_reference(&self.reference) } } +impl<'a> From> for Descriptor<'a> { + fn from(value: Locator<'a>) -> Self { + let Locator { ident, reference } = value; + Descriptor { + ident, + range: reference, + } + } +} + #[cfg(test)] mod test { use pretty_assertions::assert_eq; @@ -295,6 +309,26 @@ mod test { ) } + #[test] + fn test_locator_buildin_patch() { + assert_eq!( + Locator::try_from( + "resolve@patch:resolve@npm%3A1.22.1#~builtin::version=1.22.1&\ + hash=07638b" + ) + .unwrap(), + Locator { + ident: Ident { + scope: None, + name: "resolve".into() + }, + reference: "patch:resolve@npm%3A1.22.1#~builtin::version=1.22.1&\ + hash=07638b" + .into() + }, + ); + } + #[test] fn test_descriptor_roundtrip() { for descriptor in [ @@ -353,32 +387,33 @@ mod test { fn test_patch_primary_version() { struct TestCase { locator: &'static str, - version: Option<&'static str>, + original: Option<&'static str>, } let test_cases = [ TestCase { locator: "lodash@patch:lodash@npm%3A4.17.21#./.yarn/patches/lodash-npm-4.17.\ 21-6382451519.patch::locator=berry-patch%40workspace%3A.", - version: Some("npm:4.17.21"), + original: Some("lodash@npm:4.17.21"), }, TestCase { locator: "typescript@patch:typescript@^4.5.2#~builtin", - version: Some("npm:^4.5.2"), + original: Some("typescript@npm:^4.5.2"), }, TestCase { locator: "react@npm:18.2.0", - version: None, + original: None, + }, + TestCase { + locator: "resolve@patch:resolve@npm%3A1.22.1#~builtin::version=1.\ + 22.1&hash=07638b", + original: Some("resolve@npm:1.22.1"), }, ]; for tc in test_cases { let locator = Locator::try_from(tc.locator).unwrap(); + let expected = tc.original.map(Locator::try_from).transpose().unwrap(); let patch_locator = locator.patched_locator(); - assert_eq!( - tc.version, - patch_locator.as_ref().map(|l| l.reference.as_ref()), - "{}", - tc.locator - ); + assert_eq!(patch_locator, expected, "{}", tc.locator); } } } diff --git a/crates/turborepo-lockfiles/src/berry/mod.rs b/crates/turborepo-lockfiles/src/berry/mod.rs index c778f0a5d02a3..7e1172383a1dc 100644 --- a/crates/turborepo-lockfiles/src/berry/mod.rs +++ b/crates/turborepo-lockfiles/src/berry/mod.rs @@ -364,6 +364,7 @@ impl<'a> BerryLockfile<'a> { resolution.reduce_dependency(reference, &dependency, locator) { dependency = override_dependency; + break; } } @@ -391,29 +392,13 @@ impl<'a> Lockfile for BerryLockfile<'a> { }) .ok_or_else(|| crate::Error::MissingWorkspace(workspace_path.to_string()))?; - let mut dependency = Descriptor::new(name, version) + let dependency = self + .resolve_dependency(workspace_locator, name, version) .unwrap_or_else(|_| panic!("{name} is an invalid lockfile identifier")); - for (resolution, reference) in &self.overrides { - if let Some(override_dependency) = - resolution.reduce_dependency(reference, &dependency, workspace_locator) - { - dependency = override_dependency; - } - } - - if dependency.protocol().is_none() { - if let Some(range) = self.resolver.get(&dependency) { - dependency.range = range.into(); - } - } - let locator = self.resolutions.get(&dependency); - - if locator.is_none() { + let Some(locator) = self.resolutions.get(&dependency) else { return Ok(None); - } - - let locator = locator.unwrap(); + }; let package = self .locator_package @@ -432,9 +417,8 @@ impl<'a> Lockfile for BerryLockfile<'a> { ) -> Result>, crate::Error> { let locator = Locator::try_from(key).unwrap_or_else(|_| panic!("Was passed invalid locator: {key}")); - let package = self.locator_package.get(&locator); - let Some(package) = package else { + let Some(package) = self.locator_package.get(&locator) else { return Ok(None); }; @@ -620,6 +604,16 @@ mod test { assert_eq!(package.version.as_ref(), "2.0.0-next.4"); } + #[test] + fn test_empty_patch_list() { + let data = + LockfileData::from_bytes(include_bytes!("../../fixtures/minimal-berry.lock")).unwrap(); + let lockfile = BerryLockfile::new(&data, None).unwrap(); + + let empty_vec: Vec<&Path> = Vec::new(); + assert_eq!(lockfile.patches(), empty_vec); + } + #[test] fn test_basic_descriptor_prune() { let data: LockfileData = @@ -653,6 +647,27 @@ mod test { assert_eq!(lodash_desc.unwrap().reference, "npm:4.17.21"); } + #[test] + fn test_closure_with_patch() { + let data = LockfileData::from_bytes(include_bytes!("../../fixtures/berry.lock")).unwrap(); + let resolutions = BerryManifest::with_resolutions(vec![( + "lodash@^4.17.21".into(), + "patch:lodash@npm%3A4.17.21#./.yarn/patches/lodash-npm-4.17.21-6382451519.patch".into(), + )]); + let lockfile = BerryLockfile::new(&data, Some(&resolutions)).unwrap(); + let closure = crate::transitive_closure( + &lockfile, + "apps/docs", + HashMap::from_iter(vec![("lodash".into(), "^4.17.21".into())]), + ) + .unwrap(); + + assert!(closure.contains(&Package { + key: "lodash@npm:4.17.21".into(), + version: "4.17.21".into() + })); + } + #[test] fn test_basic_resolutions_dependencies() { let data: LockfileData = serde_yaml::from_str(include_str!( diff --git a/crates/turborepo-lockfiles/src/berry/resolution.rs b/crates/turborepo-lockfiles/src/berry/resolution.rs index d88ffce63e712..8f6bc6638cc27 100644 --- a/crates/turborepo-lockfiles/src/berry/resolution.rs +++ b/crates/turborepo-lockfiles/src/berry/resolution.rs @@ -103,6 +103,8 @@ impl<'a> Resolution<'a> { dependency: &Descriptor<'b>, locator: &Locator, ) -> Option> { + // if the ref is patch locator then it will have a suffix of + // ::locator={ROOT_WORKSPACE}@workspace:. ( with @ and : escaped) if let Some(from) = &self.from { let from_ident = from.ident(); // If the from doesn't match the locator we skip @@ -159,6 +161,20 @@ impl<'a> Resolution<'a> { dependency_override.range.to_mut().insert_str(0, "npm:") } + // Patch references aren't complete in the resolutions field so we + // instead resolve to the package getting patched. + // The patch still gets picked up as we include patches for any + // packages in the pruned lockfile if the package is a member. + if matches!(dependency_override.protocol(), Some("patch")) { + return Some( + Descriptor::from( + Locator::from_patch_reference(reference) + .expect("expected patch reference to contain locator"), + ) + .into_owned(), + ); + } + Some(dependency_override) } } @@ -175,7 +191,7 @@ impl fmt::Display for Resolution<'_> { f.write_fmt(format_args!("{from}/"))?; } f.write_fmt(format_args!("{}", self.descriptor))?; - todo!() + Ok(()) } } @@ -267,7 +283,21 @@ mod test { description: None } } - ) + ); + + assert_eq!( + parse_resolution("is-even/is-odd").unwrap(), + Resolution { + from: Some(Specifier { + full_name: "is-even", + description: None + }), + descriptor: Specifier { + full_name: "is-odd", + description: None + } + } + ); } #[test] @@ -286,4 +316,18 @@ mod test { } ) } + + #[test] + fn test_patch_resolution() { + let resolution = parse_resolution("lodash@^4.17.21").unwrap(); + let dependency = resolution.reduce_dependency( + "patch:lodash@npm%3A4.17.21#./.yarn/patches/lodash-npm-4.17.21-6382451519.patch", + &Descriptor::try_from("lodash@^4.17.21").unwrap(), + &Locator::try_from("test@workspace:.").unwrap(), + ); + assert_eq!( + dependency, + Some(Descriptor::try_from("lodash@npm:4.17.21").unwrap()) + ); + } } diff --git a/crates/turborepo-lockfiles/src/lib.rs b/crates/turborepo-lockfiles/src/lib.rs index 74ccc86f5f939..c18d272ec1252 100644 --- a/crates/turborepo-lockfiles/src/lib.rs +++ b/crates/turborepo-lockfiles/src/lib.rs @@ -6,11 +6,12 @@ mod npm; use std::collections::{HashMap, HashSet}; -pub use berry::*; +pub use berry::{Error as BerryError, *}; pub use error::Error; pub use npm::*; +use serde::Serialize; -#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] +#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash, Serialize)] pub struct Package { pub key: String, pub version: String, @@ -33,6 +34,19 @@ pub trait Lockfile { fn all_dependencies(&self, key: &str) -> Result>, Error>; } +pub fn all_transitive_closures( + lockfile: &L, + workspaces: HashMap>, +) -> Result>, Error> { + workspaces + .into_iter() + .map(|(workspace, unresolved_deps)| { + let closure = transitive_closure(lockfile, &workspace, unresolved_deps)?; + Ok((workspace, closure)) + }) + .collect() +} + // this should get replaced by petgraph in the future :) pub fn transitive_closure( lockfile: &L, diff --git a/crates/turborepo-lockfiles/src/npm.rs b/crates/turborepo-lockfiles/src/npm.rs index a551652df93f5..37c4ec5398a2c 100644 --- a/crates/turborepo-lockfiles/src/npm.rs +++ b/crates/turborepo-lockfiles/src/npm.rs @@ -205,6 +205,15 @@ pub fn npm_subgraph( Ok(new_contents) } +pub fn npm_global_change(prev_contents: &[u8], curr_contents: &[u8]) -> Result { + let prev_lockfile = NpmLockfile::load(prev_contents)?; + let curr_lockfile = NpmLockfile::load(curr_contents)?; + + Ok( + prev_lockfile.lockfile_version != curr_lockfile.lockfile_version + || prev_lockfile.other.get("requires") != curr_lockfile.other.get("requires"), + ) +} #[cfg(test)] mod test { use super::*; diff --git a/turborepo-tests/integration/tests/_fixtures/berry_resolutions/.yarnrc.yml b/turborepo-tests/integration/tests/_fixtures/berry_resolutions/.yarnrc.yml new file mode 100644 index 0000000000000..7f3d03fd8b0a9 --- /dev/null +++ b/turborepo-tests/integration/tests/_fixtures/berry_resolutions/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: "node-modules" diff --git a/turborepo-tests/integration/tests/_fixtures/berry_resolutions/package.json b/turborepo-tests/integration/tests/_fixtures/berry_resolutions/package.json new file mode 100644 index 0000000000000..75aa970324989 --- /dev/null +++ b/turborepo-tests/integration/tests/_fixtures/berry_resolutions/package.json @@ -0,0 +1,11 @@ +{ + "name": "prune-resolutions", + "packageManager": "yarn@3.3.0", + "workspaces": [ + "packages/**" + ], + "resolutions": { + "is-even/is-odd": "3.0.0", + "is-number": "6.0.0" + } +} diff --git a/turborepo-tests/integration/tests/_fixtures/berry_resolutions/packages/a/package.json b/turborepo-tests/integration/tests/_fixtures/berry_resolutions/packages/a/package.json new file mode 100644 index 0000000000000..2b2cc18470e38 --- /dev/null +++ b/turborepo-tests/integration/tests/_fixtures/berry_resolutions/packages/a/package.json @@ -0,0 +1,6 @@ +{ + "name": "a", + "dependencies": { + "is-even": "^1.0.0" + } +} diff --git a/turborepo-tests/integration/tests/_fixtures/berry_resolutions/packages/b/package.json b/turborepo-tests/integration/tests/_fixtures/berry_resolutions/packages/b/package.json new file mode 100644 index 0000000000000..4b60f34fa3bcc --- /dev/null +++ b/turborepo-tests/integration/tests/_fixtures/berry_resolutions/packages/b/package.json @@ -0,0 +1,6 @@ +{ + "name": "b", + "dependencies": { + "is-odd": "^3.0.1" + } +} diff --git a/turborepo-tests/integration/tests/_fixtures/berry_resolutions/yarn.lock b/turborepo-tests/integration/tests/_fixtures/berry_resolutions/yarn.lock new file mode 100644 index 0000000000000..a3ee46f126fb4 --- /dev/null +++ b/turborepo-tests/integration/tests/_fixtures/berry_resolutions/yarn.lock @@ -0,0 +1,62 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 6 + cacheKey: 8 + +"a@workspace:packages/a": + version: 0.0.0-use.local + resolution: "a@workspace:packages/a" + dependencies: + is-even: ^1.0.0 + languageName: unknown + linkType: soft + +"b@workspace:packages/b": + version: 0.0.0-use.local + resolution: "b@workspace:packages/b" + dependencies: + is-odd: ^3.0.1 + languageName: unknown + linkType: soft + +"is-even@npm:^1.0.0": + version: 1.0.0 + resolution: "is-even@npm:1.0.0" + dependencies: + is-odd: ^0.1.2 + checksum: 0267545d7cb6724aee249e88942cf22f6263aa006cd9bf83c2ddbb2a1d47280e8c4d72b2d50e38bd3575df717c993904b44153cc1772a55dabca250ca40cc4f7 + languageName: node + linkType: hard + +"is-number@npm:6.0.0": + version: 6.0.0 + resolution: "is-number@npm:6.0.0" + checksum: f73bfced022128b5684bf77e0266a74e5222522bbc40f81cc1e949170c774a3c14b59a208be025d2d97a9c6b79c7c45fe351ab1c2c780872464fdedde0ae067a + languageName: node + linkType: hard + +"is-odd@npm:3.0.0": + version: 3.0.0 + resolution: "is-odd@npm:3.0.0" + dependencies: + is-number: ^6.0.0 + checksum: 1b0614ce1d6af841a5c04980cb8f9a44594444e448b35b5afd05c0aed5ce297e131f898412c42cdbe2e9b24551488e3f5d12a5917395ca42e2a0bc5518893642 + languageName: node + linkType: hard + +"is-odd@npm:^3.0.1": + version: 3.0.1 + resolution: "is-odd@npm:3.0.1" + dependencies: + is-number: ^6.0.0 + checksum: 4e2b20764dd2296bafe44823d127f281c7039b37d2feaf5caffc1bf162502ef2920bcd4ad171490f371d3f15f52232c763a8ffc0b3633d4c83385fe20f3493af + languageName: node + linkType: hard + +"prune-resolutions@workspace:.": + version: 0.0.0-use.local + resolution: "prune-resolutions@workspace:." + languageName: unknown + linkType: soft diff --git a/turborepo-tests/integration/tests/_helpers/copy_fixture.sh b/turborepo-tests/integration/tests/_helpers/copy_fixture.sh new file mode 100755 index 0000000000000..cf0f1dde3b4c6 --- /dev/null +++ b/turborepo-tests/integration/tests/_helpers/copy_fixture.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -e + +SCRIPT_DIR=$(dirname ${BASH_SOURCE[0]}) +TARGET_DIR=$1 +FIXTURE="_fixtures/${2-basic_monorepo}" +cp -a ${SCRIPT_DIR}/../$FIXTURE/. ${TARGET_DIR}/ diff --git a/turborepo-tests/integration/tests/prune/resolutions.t b/turborepo-tests/integration/tests/prune/resolutions.t new file mode 100644 index 0000000000000..d138bf97f29c8 --- /dev/null +++ b/turborepo-tests/integration/tests/prune/resolutions.t @@ -0,0 +1,25 @@ +Setup + $ . ${TESTDIR}/../../../helpers/setup.sh + $ . ${TESTDIR}/../_helpers/copy_fixture.sh $(pwd) berry_resolutions + +Prune a +We expect to no longer have the non-resolved is-odd descriptor and +only have the override that has been set + $ ${TURBO} prune --scope=a + Generating pruned monorepo for a in .*out (re) + - Added a + $ grep -F '"is-odd@npm:' out/yarn.lock + "is-odd@npm:3.0.0": + resolution: "is-odd@npm:3.0.0" + + +Prune b +We should no longer have the override for is-odd + $ ${TURBO} prune --scope=b + Generating pruned monorepo for b in .*out (re) + - Added b + + + $ grep -F '"is-odd@npm:' out/yarn.lock + "is-odd@npm:^3.0.1": + resolution: "is-odd@npm:3.0.1"