Open dcu opened 1 year ago
Mostly, I don't want to put the effort into a stable API. I could provide an unstable one.
Is there a concrete problem you are finding difficult to solve? I'm mostly interested in which parts of the system you are interested in being exposed.
Sometimes using golang.org/x/tools/go/packages
directly might be more easier.
I would use pkgset.Calc to find all dependencies of a package
If you just want all the packages, then this would be sufficient:
package main
import (
"golang.org/x/tools/go/packages"
)
func Dependencies(pkgs ...string) (map[string]*packages.Package, error) {
roots, err := packages.Load(&packages.Config{
Mode: packages.NeedName | packages.NeedImports,
// Tests: true, // if you need tests
}, pkgs...)
if err != nil {
return nil, fmt.Errorf("failed to load packages %v: %w", pkgs, err)
}
r := map[string]*packages.Package{}
include(r, roots)
return r, nil
}
func include(r map[string]*packages.Package, pkgs []*packages.Package) {
for _, pkg := range pkgs {
if _, added := r[pkg.ID]; added {
continue
}
r[pkg.ID] = pkg
include(r, pkg.Imports)
}
}
Is there a concrete problem you are finding difficult to solve?
For me I wanted to experiment with using it in a CI setup, to figure out which packages need to be re-built and tested based on changes in a PR (this is for a monorepo) and stuff like that. I would want to avoid reverse engineering the whole thing, or forking it just to export it :sweat_smile: I could most likely accomplish this with parsing the goda output, but in general I think this sort of thing is nicer to do programmatically.
Mostly, I don't want to put the effort into a stable API. I could provide an unstable one.
I at least think that's reasonable. I would be fine under the assumption that when a new release is out, it likely means I need to change my code
If you can cache the build output, then Go won't test anything that hasn't changed.
Otherwise, the logic for such a things is approximately this:
// !!!
// WARNING: CODE UNTESTED
// !!!
package main
import (
"golang.org/x/tools/go/packages"
)
// TODO: you'll need extra conditions for changing testdata, environment flags, build flags, go.mod, go.work etc.
func NeedsRetest(pkgs, modifiedPackages []string) ([]string, error) {
roots, err := packages.Load(&packages.Config{
Mode: packages.NeedName | packages.NeedImports,
Tests: true,
}, pkgs...)
if err != nil {
return nil, fmt.Errorf("failed to load packages %v: %w", pkgs, err)
}
allPackages := map[string]*packages.Package{}
include(allPackages, roots)
retest := map[string]bool{}
for _, modifiedPackage := range modifiedPackages {
retest[modifiedPackage] = true
}
// check whether parent has any child that needs modification,
// and keep around a cache to avoid double checking packages.
check := func(parent *packages.Package) bool {
if needs, ok := retest[parent.ID]; ok {
return needs
}
for _, pkg := range parent.Imports {
if check(pkg) {
retest[parent.ID] = true
return true
}
}
return false
}
for _, pkg := range allPackages {
check(pkg)
}
result := []string{}
for pkg, ok := range retest {
if ok {
result = append(result, pkg)
}
}
sort.Strings(result)
return result, nil
}
func include(r map[string]*packages.Package, pkgs []*packages.Package) {
for _, pkg := range pkgs {
if _, added := r[pkg.ID]; added {
continue
}
r[pkg.ID] = pkg
include(r, pkg.Imports)
}
}
there are some cases where using as library would be preferable than using as cli
is there a reason to not allow it? thanks