-
Notifications
You must be signed in to change notification settings - Fork 171
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
73 changed files
with
396 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,126 @@ | ||
package golang | ||
|
||
// The project of a package is the nearest ancestor with a supported lockfile. | ||
// Two packages in the same project can import each other without needing a | ||
// revision in the lockfile, because these are ostensibly internal. | ||
// Conceptually, anything in the same VCS project (and not in a vendor folder) should be an "internal" import | ||
// because they'll all be versioned together. | ||
// Looking upwards for a lockfile is a subset of this: it's never possible to | ||
// have a lockfile "in between" directories that are not repositories (you'll | ||
// never have github.com/hashicorp/Gopkg.lock), but your internal import might | ||
// be higher up than the closest lockfile. | ||
// alternatively: search upwards for .git or other VCS marker? | ||
// A vendored package always in a different project than the one vendoring it. | ||
func GetProjectFolder(pkg string) (string, error) { | ||
return "", nil | ||
import ( | ||
"github.com/fossas/fossa-cli/files" | ||
) | ||
|
||
// A Project is a single folder that forms a coherent "project" for a developer | ||
// and is versioned as a single unit. It may contain multiple Go packages. | ||
type Project struct { | ||
Tool string // Name of the dependency management tool used by the project, if any. | ||
Manifest string // Absolute path to the tool's manifest file for this project, if any. | ||
Internal string // Absolute path of the first-party code folder, if any. | ||
} | ||
|
||
// i know the location of every package on disk. | ||
// every package that goes up to the same VCS metadata folder _and_ is not in a | ||
// vendor folder is in the same project. | ||
// GetProject gets the project containing any Go package. | ||
// | ||
// This function searches upwards from the Go package's directory, looking for | ||
// lockfiles of supported dependency management tools. If none are found, it | ||
// fails. | ||
// | ||
// The rationale for this design is that the packages in a "project" are | ||
// versioned together. There are two reasonable ways to capture the notion of | ||
// "versioned together": | ||
// | ||
// 1. The nearest lockfile. The nearest lockfile to the package probably locks | ||
// the dependencies of the package. | ||
// 2. The nearest VCS repository. The nearest VCS repository probably contains | ||
// the current "project" being worked on. The only common exception to this | ||
// is monorepos, in which case all the contents of the repository are | ||
// probably internal, so allowing packages within the repository to be | ||
// unresolved is probably acceptable. | ||
// | ||
// There are a couple issues with both of these: | ||
// | ||
// 1. The nearest lockfile is not guaranteed to exist. When it does, it's not | ||
// guaranteed to be _the_ semantic lockfile for the package -- this is | ||
// merely a very common convention, not a requirement. | ||
// 2. The package is not guaranteed to be in a VCS repository. When it is, the | ||
// repository might be extremely weird. One example of this is a repository | ||
// containing the entire $GOPATH (which is a reasonable convention that | ||
// some early adopters of Go used). | ||
// | ||
// fall back to using lockfile location for project discovery _iff_ no VCS is | ||
// found (e.g. new project without git init) | ||
// This function tries its best to mitigate both of these issues: | ||
// | ||
// 1. The nearest lockfile is used for resolving versions. This is a very | ||
// strong convention. | ||
// 2. The nearest VCS repository is used for determining allowed unresolved | ||
// import paths. This is also a very strong convention. | ||
// | ||
// Both of these assumptions can be overridden by the user. | ||
func (a *Analyzer) GetProject(pkg string) (Project, error) { | ||
// Get the directory. | ||
dir, err := a.Dir(pkg) | ||
if err != nil { | ||
return Project{}, err | ||
} | ||
|
||
// Find the nearest lockfile. | ||
var toolName string | ||
manifestDir, err := files.WalkUp(dir, func(d string) (bool, error) { | ||
either := &resultExists{} | ||
either.Find("godep", d, "Godeps", "Godeps.json") | ||
either.Find("govendor", d, "vendor", "vendor.json") | ||
either.Find("dep", d, "Gopkg.toml") | ||
either.Find("vndr", d, "vendor.conf") | ||
either.Find("glide", d, "glide.yaml") | ||
either.Find("gdm", d, "Godeps") | ||
if either.err != nil { | ||
return false, either.err | ||
} | ||
if either.which != "" { | ||
toolName = either.which | ||
} | ||
return either.which != "", nil | ||
}) | ||
|
||
// Find the nearest VCS repository. | ||
repoRoot, err := files.WalkUp(dir, func(d string) (bool, error) { | ||
either := &resultExists{} | ||
either.FindFolder("git", d, ".git") | ||
either.FindFolder("svn", d, ".svn") | ||
either.FindFolder("hg", d, ".hg") | ||
either.FindFolder("bzr", d, ".bzr") | ||
if either.err != nil { | ||
return false, either.err | ||
} | ||
return either.which != "", nil | ||
}) | ||
if err != nil { | ||
return Project{}, err | ||
} | ||
|
||
return Project{ | ||
Tool: toolName, | ||
Manifest: manifestDir, | ||
Internal: repoRoot, | ||
}, nil | ||
} | ||
|
||
// This is a monomorphic Either monad. I miss Haskell. | ||
type resultExists struct { | ||
which string | ||
err error | ||
} | ||
|
||
func (r *resultExists) Bind(which string, find func(pathElems ...string) (bool, error), pathElems ...string) { | ||
if r.err != nil { | ||
return | ||
} | ||
|
||
ok, err := find(pathElems...) | ||
if err != nil { | ||
r.err = err | ||
} | ||
if ok { | ||
r.which = which | ||
} | ||
} | ||
|
||
func (r *resultExists) Find(which string, pathElems ...string) { | ||
r.Bind(which, files.Exists, pathElems...) | ||
} | ||
|
||
func (r *resultExists) FindFolder(which string, pathElems ...string) { | ||
r.Bind(which, files.ExistsFolder, pathElems...) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,36 @@ | ||
package golang | ||
|
||
import ( | ||
"github.com/fossas/fossa-cli/module" | ||
"errors" | ||
|
||
"github.com/fossas/fossa-cli/pkg" | ||
) | ||
|
||
var ( | ||
ErrResolverNotFound = errors.New("unrecognized Go resolver") | ||
) | ||
|
||
func Resolve(a Analyzer, p module.Module) { | ||
// exec.Run(exec.Cmd{ | ||
// Name: a.GoCmd, | ||
// Argv: []string{"list", "-json", p.BuildTarget}, | ||
// }) | ||
// golang.List() | ||
// dep.UsedIn() | ||
// dep.Read() | ||
// A Resolver provides a single method for resolving the revision of a Go | ||
// package. | ||
type Resolver interface { | ||
Resolve(importpath string) pkg.ID | ||
} | ||
|
||
func NewResolver(resolver, dir string) (Resolver, error) { | ||
switch resolver { | ||
case "dep": | ||
return nil, errors.New("not yet implemented") | ||
case "gdm": | ||
return nil, errors.New("not yet implemented") | ||
case "glide": | ||
return nil, errors.New("not yet implemented") | ||
case "godep": | ||
return nil, errors.New("not yet implemented") | ||
case "govendor": | ||
return nil, errors.New("not yet implemented") | ||
case "vndr": | ||
return nil, errors.New("not yet implemented") | ||
default: | ||
return nil, ErrResolverNotFound | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package golang | ||
|
||
// Dir returns the absolute path to a Go package. | ||
func (a *Analyzer) Dir(importpath string) (string, error) { | ||
pkg, err := a.Go.ListOne(importpath) | ||
if err != nil { | ||
return "", err | ||
} | ||
return pkg.Dir, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.