github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/analyzers/golang/project.go (about) 1 package golang 2 3 import ( 4 "github.com/apex/log" 5 6 "github.com/fossas/fossa-cli/analyzers/golang/resolver" 7 "github.com/fossas/fossa-cli/vcs" 8 ) 9 10 // A Project is a single folder that forms a coherent "project" for a developer 11 // and is versioned as a single unit. It may contain multiple Go packages. 12 type Project struct { 13 Tool resolver.Type // Name of the dependency management tool used by the project, if any. 14 Manifest string // Absolute path to the tool's manifest file for this project, if any. 15 Dir string // Absolute path of the first-party code folder, if any. 16 ImportPath string // Import path prefix of project code. 17 } 18 19 // Project calculates the project containing any Go package. 20 // 21 // This function searches upwards from the Go package's directory, looking for 22 // lockfiles of supported dependency management tools. If none are found, it 23 // fails. 24 // 25 // The rationale for this design is that the packages in a "project" are 26 // versioned together. There are two reasonable ways to capture the notion of 27 // "versioned together": 28 // 29 // 1. The nearest lockfile. The nearest lockfile to the package probably locks 30 // the dependencies of the package. 31 // 2. The nearest VCS repository. The nearest VCS repository probably contains 32 // the current "project" being worked on. The only common exception to this 33 // is monorepos, in which case all the contents of the repository are 34 // probably internal, so allowing packages within the repository to be 35 // unresolved is probably acceptable. 36 // 37 // There are a couple issues with both of these: 38 // 39 // 1. The nearest lockfile is not guaranteed to exist. When it does, it's not 40 // guaranteed to be _the_ semantic lockfile for the package -- this is 41 // merely a very common convention, not a requirement. 42 // 2. The package is not guaranteed to be in a VCS repository. When it is, the 43 // repository might be extremely weird. One example of this is a repository 44 // containing the entire $GOPATH (which is a reasonable convention that 45 // some early adopters of Go used). 46 // 47 // This function tries its best to mitigate both of these issues: 48 // 49 // 1. The nearest lockfile is used for resolving versions. This is a very 50 // strong convention. 51 // 2. The nearest VCS repository is used for determining allowed unresolved 52 // import paths. This is also a very strong convention. 53 // 54 // Both of these assumptions can be overridden by the user. 55 func (a *Analyzer) Project(pkg string) (Project, error) { 56 log.Debugf("%#v", pkg) 57 58 // Check for a cached project. 59 cached, ok := a.projectCache[pkg] 60 if ok { 61 return cached, nil 62 } 63 64 // Get the package directory. 65 dir, err := a.Dir(pkg) 66 if err != nil { 67 return Project{}, err 68 } 69 70 // Find the nearest lockfile. 71 tool, manifestDir, err := NearestLockfile(dir) 72 if err != nil { 73 return Project{}, err 74 } 75 76 // Find the nearest VCS repository. 77 _, repoRoot, err := vcs.Nearest(dir) 78 if err != nil { 79 return Project{}, err 80 } 81 82 // Compute the project import path prefix. 83 importPrefix, err := ImportPath(repoRoot) 84 if err != nil { 85 return Project{}, err 86 } 87 88 // Cache the computed project. 89 project := Project{ 90 Tool: tool, 91 Manifest: manifestDir, 92 Dir: repoRoot, 93 ImportPath: importPrefix, 94 } 95 a.projectCache[pkg] = project 96 return project, nil 97 }