Skip to content

Commit

Permalink
feat: Use Rust Berry lockfile impl (#4684)
Browse files Browse the repository at this point in the history
### 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 <arlyon@me.com>
  • Loading branch information
chris-olszewski and arlyon committed Apr 27, 2023
1 parent 04a726f commit ad4bc41
Show file tree
Hide file tree
Showing 23 changed files with 1,161 additions and 1,084 deletions.
6 changes: 5 additions & 1 deletion cli/internal/ffi/bindings.h
Expand Up @@ -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);
69 changes: 62 additions & 7 deletions cli/internal/ffi/ffi.go
Expand Up @@ -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))
Expand All @@ -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)
Expand All @@ -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{}
Expand All @@ -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()
}

0 comments on commit ad4bc41

Please sign in to comment.