// forEachRepository calls found for each repository it finds in all GOPATH workspaces. func forEachRepository(found func(Repo)) { for _, workspace := range filepath.SplitList(build.Default.GOPATH) { srcRoot := filepath.Join(workspace, "src") if _, err := os.Stat(srcRoot); os.IsNotExist(err) { continue } // TODO: Confirm that ignoring filepath.Walk error is correct/desired behavior. _ = filepath.Walk(srcRoot, func(path string, fi os.FileInfo, err error) error { if err != nil { log.Printf("can't stat file %s: %v\n", path, err) return nil } if !fi.IsDir() { return nil } if strings.HasPrefix(fi.Name(), ".") || strings.HasPrefix(fi.Name(), "_") || fi.Name() == "testdata" { return filepath.SkipDir } // Determine repo root. This is potentially somewhat slow. vcsCmd, root, err := vcs.FromDir(path, srcRoot) if err != nil { // Directory not under VCS. return nil } found(Repo{Path: path, Root: root, VCS: vcsCmd}) return filepath.SkipDir // No need to descend inside repositories. }) } }
// uniqueWorker finds unique repos out of all input Go packages. func (w *workspace) uniqueWorker(wg *sync.WaitGroup) { defer wg.Done() for importPath := range w.ImportPaths { // Determine repo root. // This is potentially somewhat slow. bpkg, err := build.Import(importPath, wd, build.FindOnly) if err != nil { w.Errors <- err continue } if bpkg.Goroot { // gostatus has no support for printing status of packages in GOROOT, so skip those. continue } vcsCmd, root, err := vcs.FromDir(bpkg.Dir, bpkg.SrcRoot) if err != nil { // Go package not under VCS. var pkg *Repo w.reposMu.Lock() if _, ok := w.repos[bpkg.ImportPath]; !ok { pkg = &Repo{ Path: bpkg.Dir, Root: bpkg.ImportPath, vcs: nil, } w.repos[bpkg.ImportPath] = pkg } w.reposMu.Unlock() // If new package, send off to next stage. if pkg != nil { w.unique <- pkg } continue } vcs, err := vcsstate.NewVCS(vcsCmd) if err != nil { w.Errors <- fmt.Errorf("repo %v not supported by vcsstate: %v", root, err) continue } var repo *Repo w.reposMu.Lock() if _, ok := w.repos[root]; !ok { repo = &Repo{ Path: bpkg.Dir, Root: root, vcs: vcs, } w.repos[root] = repo } w.reposMu.Unlock() // If new repo, send off to next stage. if repo != nil { w.unique <- repo } } }
func FromDir(dir, srcRoot string) (*VCS, string, error) { vcscmd, reporoot, err := vcs.FromDir(dir, srcRoot) if err != nil { return nil, "", fmt.Errorf("error while inspecting %q: %v", dir, err) } vcsext := cmd[vcscmd] if vcsext == nil { return nil, "", fmt.Errorf("%s is unsupported: %s", vcscmd.Name, dir) } return vcsext, reporoot, nil }
// ResolveRepo on a local resolver may return an error if: // * The local package is not present (no directory) in LocalPath // * The local "package" is a file in localpath // * There was an error stating the directory for the localPkg func (lr *LocalRepoResolver) ResolveRepo(pkg string, dep *CanticleDependency) (VCS, error) { LogVerbose("Finding local vcs for package: %s\n", pkg) fullPath := PackageSource(lr.LocalPath, getResolvePath(pkg)) s, err := os.Stat(fullPath) switch { case err != nil: LogVerbose("Error stating local copy of package: %s %s\n", fullPath, err.Error()) return nil, err case s != nil && s.IsDir(): cmd, root, err := vcs.FromDir(fullPath, lr.LocalPath) if err != nil { LogVerbose("Error with local vcs: %s", err.Error()) return nil, err } root, _ = PackageName(lr.LocalPath, path.Join(lr.LocalPath, root)) v := NewLocalVCS(root, root, lr.LocalPath, cmd) LogVerbose("Created vcs for local pkg: %+v", v) return v, nil default: LogVerbose("Could not resolve local vcs for package: %s", fullPath) return nil, NewResolutionFailureError(pkg, "local") } }
// RepoInfo attempts to find the repo information for the caller, and returns // the path to the top of the repo, the commit ID, and the tag. func RepoInfo() (repoRoot string, repoRevision string, repoTag string) { var vcsCmd *vcs.Cmd // Walk up the call stack until we find main.main for i := 1; ; i++ { caller, file, _, ok := runtime.Caller(i) if !ok { break } f := runtime.FuncForPC(caller) if f == nil { continue } if path.Base(f.Name()) == "main.main" { repoRoot = path.Dir(file) break } // see if we're in testing if path.Base(f.Name()) == "testing.tRunner" { // go back up one level _, file, _, _ := runtime.Caller(i - 1) repoRoot = path.Dir(file) break } } // Try and find the root of the VCS repo using repoRoot as a starting point. if repoRoot != "" { // walk up the dir until we get to the first directory in root. // This is an unfortunately necessity to use vcs.FromDir() topDir := repoRoot for dir := path.Dir(topDir); dir != "." && dir[len(dir)-1] != '/' && dir != topDir; dir = path.Dir(topDir) { topDir = dir } var vcsRoot string vcsCmd, vcsRoot, _ = vcs.FromDir(path.Dir(repoRoot), topDir) if len(vcsRoot) > 0 { if vcsRoot[0] != '/' { vcsRoot = topDir + "/" + vcsRoot } repoRoot = vcsRoot } } if vcsCmd != nil && vcsCmd.Name == "Git" { execCmd := exec.Command("git", "describe", "--dirty", "--match", "", "--always") execCmd.Dir = repoRoot output, err := execCmd.Output() if err == nil { repoRevision = string(bytes.TrimRight(output, "\n")) } execCmd = exec.Command("git", "describe", "--dirty", "--tags", "--always") execCmd.Dir = repoRoot output, err = execCmd.Output() if err == nil { repoTag = string(bytes.TrimRight(output, "\n")) } } return }
// LoadDependencies is not done func LoadDependencies(pkgs []string, ignores []string) ([]Dependency, error) { stdlib, err := golist.Std() if err != nil { return nil, err } pkgs, err = golist.Deps(pkgs...) if err != nil { return nil, err } // faster to remove stdlib pkgs = removeIfEquals(pkgs, stdlib) pkgs = removeIfSubstring(pkgs, ignores) deps, err := golist.Packages(pkgs...) if err != nil { return nil, err } visited := make(map[string]string, len(deps)) out := make([]Dependency, 0, len(deps)) for _, v := range deps { src := filepath.Join(v.Root, "src") path := filepath.Join(src, filepath.FromSlash(v.ImportPath)) cmd, _, err := vcs.FromDir(path, src) if err != nil { log.Printf("error computing vcs %s: %s", path, err) continue } rr, err := vcs.RepoRootForImportPath(v.ImportPath, false) if err != nil { log.Printf("error computing repo for %s: %s", v.ImportPath, err) continue } e := Dependency{ Package: v, } e.Project.Repo = rr.Repo e.Project.VcsName = cmd.Name e.Project.VcsCmd = cmd.Cmd e.License = GetLicense(path) visited[v.ImportPath] = e.License.Type // if child have no license, parent has license, continue // if child has no license, parent has no license, carry on if e.License.Type == "" { lic, ok := visited[rr.Root] if ok && lic != "" { // case is child has no license, but parent does // => just ignore this package continue } // if ok && lic = "" => dont look up parent if !ok { // first time checking parent parentpkg, err := golist.GetPackage(rr.Root) if err == nil { parent := Dependency{ Package: parentpkg, Project: e.Project, } path = filepath.Join(src, filepath.FromSlash(rr.Root)) parent.License = GetLicense(path) visited[rr.Root] = parent.License.Type if parent.License.Type != "" { // case child has no license, parent does // ignore child and use parent e = parent } } } } commit, err := GetLastCommit(path) if err == nil { e.Commit = commit } e.Project.LicenseLink = LinkToFile(e.ImportPath, e.License.File, e.Commit.Rev) out = append(out, e) } return out, err }