-
Notifications
You must be signed in to change notification settings - Fork 32
Fix circular dependencies #116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Fix circular dependencies #116
Conversation
|
Actually no, but fine this job is going away |
b89d6cf to
2b54fb0
Compare
0f6989d to
8e05911
Compare
| def _rpm_impl(ctx): | ||
| if ctx.attr.urls: | ||
| downloaded_file_path = "downloaded" | ||
| downloaded_file_path = ctx.attr.urls[0].split("/")[-1] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need to change this?
| packageNames := keys(allPackages) | ||
| sortedPackages := make([]*bazeldnf.RPM, 0, len(packageNames)) | ||
| for _, name := range packageNames { | ||
| slices.Sort(targets) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we care about the order of the targets?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To make the lock file generation consistent and stable, we don't want multiple runs of the same required packages against the same db to be changing due to sorting differences
cmd/config_helper.go
Outdated
| pkg.SetDependencies(deps) | ||
| pkg.SetDependencies(deps) | ||
| } else { | ||
| pkg.SetDependencies(nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is presumably to make it so that packages that we didn't directly request reduce the fanout they drag in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like we'll probably fail later rounds of collecting dependencies if we have a scenario like:
A -> B -> D
B -> D
C -> B -> D
In this scenario it (superficially) looks like we're going to to drop B's dependency on D before we process C.
It'd be good if we can separate out the traversal part here via something like https://github.com/dominikbraun/graph which will allow us to do a simple topological sort which we can then transform into the appropriate lockfile outputs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think now this is fixed
8e05911 to
42d52c4
Compare
Make sure only those packages that the lock file required contain the full qualified list of dependencies, this allows to avoid circular dependencies in bazel land
Now the lock file is in the expected state, make the repo config a bit stricter to test even more functionality
Now that we have fixed the bug we can make this job actually fail
In lockfile mode we need to make sure that only those RPMs requested at the time of the lockfile can be depend upon, otherwise we allow users to consume from RPMs that may not have their entire dependency tree populated.
Make sure the depsets are working as expected to avoid getting duplicates in the final list of dependencies going into rpmtree
Make sure circular dependencies are resolved when rendering the lock file, we flatten the dependency list of all requested packages.
When consuming the lock file make sure we only make use of the top level dependencies ignoring transitive ones.
Make sure we have a test with a fake lock file that simulates a totally circular dependency tree to validate that starlark behaves as expected
Make sure we let the user know to not use incompatible bazel versions,
as there are bugs that lead to issues like [this one](
bazel-contrib/rules_jvm_external#1205
)
Make sure that when + is found in a package name and ignore_deps is set to False, then the visible package name also gets + replaced with plus
42d52c4 to
4e0b823
Compare
| lock_file_json = json.decode(content) | ||
|
|
||
| if not config.ignore_deps: | ||
| if versions.is_at_least("7", versions.get()) and not versions.is_at_least("7.4.0", versions.get()): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where are these constraints coming from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't remember exactly why, what I do remember is setting the bazel version to 7.0 and so until 7.4.0 with bazelisk and it was failing with some weird error, same for 8.x
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably worth digging into this a bit more. We're effectively saying that we no longer support pre 7.4 versions of bazel? That's a fairly strong assertion.
| "integrity": attr.string(), | ||
| "dependencies": attr.label_list( | ||
| mandatory = False, | ||
| providers = [RpmInfo], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we want to drop this?
| "repository": []string{}, | ||
| }, | ||
| expectedRPMs: []*bazeldnf.RPM{ | ||
| newSimpleRPM("package1", "package2", "package4"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why wouldn't package2 also have package4 in this case?
| slices.Sort(targets) | ||
| sortedPackages := make([]*bazeldnf.RPM, 0, len(allPackages)) | ||
|
|
||
| requested := make(map[string]bool) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should use empty struct instead of bool here as the former uses zero bytes
| for _, name := range sortedKeys(allPackages) { | ||
| pkg := allPackages[name] | ||
| deps, err := collectDependencies(name, pkg.Dependencies, providers, ignored) | ||
| if _, requested := requested[name]; !requested { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should use ok here instead of requested because that's the normal pattern and this overloads the variable name
| sortedPackages := make([]*bazeldnf.RPM, 0, len(packageNames)) | ||
| for _, name := range packageNames { | ||
| slices.Sort(targets) | ||
| sortedPackages := make([]*bazeldnf.RPM, 0, len(allPackages)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe move this down to where sortedPackages is constructed so it's a bit more clear what's happening
| continue | ||
| } | ||
|
|
||
| pkg.SetDependencies(nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why set dependencies to nil here?
| } | ||
|
|
||
| slices.SortFunc(sortedPackages, func(a, b *bazeldnf.RPM) int { | ||
| return cmp.Compare(a.Name, b.Name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't the sorting already implicit by virtue of having constructed sortedPackages with the sorted keys?
| } | ||
|
|
||
| func collectDependencies(pkg string, requires []string, providers map[string]string, ignored map[string]bool) ([]string, error) { | ||
| func collectDependencies(pkg string, requires []string, providers map[string]string, ignored map[string]bool, allPackages map[string]*bazeldnf.RPM) ([]string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we use something like https://pkg.go.dev/github.com/dominikbraun/graph to do a topological sort of the dependencies here and just get the result of that?
| depSet := make(map[string]bool) | ||
| for _, req := range requires { | ||
| explored := make(map[string]bool) | ||
| for len(requires) > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just use something like https://pkg.go.dev/github.com/dominikbraun/graph and do a topological sort?
Make sure the lock files don't contain circular dependencies, solving the current bugs