From bbdfc498b92861c4f6c6f9aeee0ea1312ba44109 Mon Sep 17 00:00:00 2001 From: Sunny Date: Fri, 10 Nov 2017 23:15:58 +0530 Subject: [PATCH 01/14] status: add project status --- cmd/dep/status.go | 79 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/cmd/dep/status.go b/cmd/dep/status.go index e995277bf6..e3694c86a6 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -237,6 +237,10 @@ func (cmd *statusCommand) Run(ctx *dep.Ctx, args []string) error { return err } + if len(args) > 0 { + return runProjectStatus(ctx, args, p, sm) + } + var buf bytes.Buffer var out outputter switch { @@ -831,3 +835,78 @@ type byProject []projectConstraint func (p byProject) Len() int { return len(p) } func (p byProject) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func (p byProject) Less(i, j int) bool { return p[i].Project < p[j].Project } + +type projectStatus struct { + Project string + Version string + Constraints []string + Source string + AltSource string + PubVersions []string + Revision string + LatestAllowed string + SourceType string + Packages string + ProjectImporters map[string]string + PackageImporters map[string]string + UpstreamExists bool + UpstreamVersionExists bool +} + +func (ps projectStatus) String() string { + return fmt.Sprintf(` +PROJECT: %s +VERSION: %s +CONSTRAINTS: %s +SOURCE: %s +ALT SOURCE: %s +PUB VERSION: %s +REVISION: %s +LATEST ALLOWED: %s +SOURCE TYPE: %s +PACKAGES: %s +PROJECT IMPORTERS: %s +PACKAGE IMPORTERS: %s +UPSTREAM EXISTS: %t +UPSTREAM VERSION EXISTS: %t +`, + ps.Project, ps.Version, ps.Constraints, ps.Source, ps.AltSource, + ps.PubVersions, ps.Revision, ps.LatestAllowed, ps.SourceType, ps.Packages, + ps.ProjectImporters, ps.PackageImporters, ps.UpstreamExists, + ps.UpstreamVersionExists) +} + +func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.SourceManager) error { + // Collect all the project roots from the arguments. + var prs []gps.ProjectRoot + + for _, arg := range args { + pr, err := sm.DeduceProjectRoot(arg) + if err != nil { + return err + } + prs = append(prs, pr) + } + + for _, pr := range prs { + // Create projectStatus and add Project name. + projStatus := projectStatus{ + Project: string(pr), + } + + // Get the currently selected version from from lock. + for _, pl := range p.Lock.Projects() { + if pr == pl.Ident().ProjectRoot { + projStatus.Version = pl.Version().String() + projStatus.Source = projStatus.Project + projStatus.AltSource = pl.Ident().Source + rev, _, _ := gps.VersionComponentStrings(pl.Version()) + projStatus.Revision = rev + } + } + + fmt.Println(projStatus) + } + + return nil +} From 10a6372485aa02f83715c857c18af618f67543a0 Mon Sep 17 00:00:00 2001 From: Sunny Date: Sat, 11 Nov 2017 18:01:32 +0530 Subject: [PATCH 02/14] gps: Add getSourceType in SourceManager --- cmd/dep/status.go | 5 +++++ gps/solve_basic_test.go | 4 ++++ gps/source.go | 14 ++++++++----- gps/source_manager.go | 44 +++++++++++++++++++++++++++++++++++++++++ gps/vcs_source.go | 16 +++++++++++++-- 5 files changed, 76 insertions(+), 7 deletions(-) diff --git a/cmd/dep/status.go b/cmd/dep/status.go index e3694c86a6..0790aff042 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -902,6 +902,11 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source projStatus.AltSource = pl.Ident().Source rev, _, _ := gps.VersionComponentStrings(pl.Version()) projStatus.Revision = rev + vcsType, err := sm.GetVcsType(pl.Ident()) + if err != nil { + return err + } + projStatus.SourceType = vcsType } } diff --git a/gps/solve_basic_test.go b/gps/solve_basic_test.go index 910b9d622b..f937dda837 100644 --- a/gps/solve_basic_test.go +++ b/gps/solve_basic_test.go @@ -1487,6 +1487,10 @@ func (sm *depspecSourceManager) SourceURLsForPath(ip string) ([]*url.URL, error) return nil, fmt.Errorf("dummy sm doesn't implement SourceURLsForPath") } +func (sm *depspecSourceManager) GetSourceType(id ProjectIdentifier) (SourceType, error) { + return InvalidSource, fmt.Errorf("dummy sm doesn't implement GetSourceType") +} + func (sm *depspecSourceManager) rootSpec() depspec { return sm.specs[0] } diff --git a/gps/source.go b/gps/source.go index 54723754d0..7aef47b1f8 100644 --- a/gps/source.go +++ b/gps/source.go @@ -388,6 +388,10 @@ func (sg *sourceGateway) getManifestAndLock(ctx context.Context, pr ProjectRoot, return m, l, nil } +func (sg *sourceGateway) getSourceType() SourceType { + return sg.src.sourceType() +} + // FIXME ProjectRoot input either needs to parameterize the cache, or be // incorporated on the fly on egress...? func (sg *sourceGateway) listPackages(ctx context.Context, pr ProjectRoot, v Version) (pkgtree.PackageTree, error) { @@ -570,7 +574,7 @@ func (sg *sourceGateway) require(ctx context.Context, wanted sourceState) (errSt case sourceIsSetUp: sg.src, addlState, err = sg.maybe.try(ctx, sg.cachedir, sg.cache, sg.suprvsr) case sourceExistsUpstream: - err = sg.suprvsr.do(ctx, sg.src.sourceType(), ctSourcePing, func(ctx context.Context) error { + err = sg.suprvsr.do(ctx, sg.src.sourceType().String(), ctSourcePing, func(ctx context.Context) error { if !sg.src.existsUpstream(ctx) { return fmt.Errorf("%s does not exist upstream", sg.src.upstreamURL()) } @@ -578,7 +582,7 @@ func (sg *sourceGateway) require(ctx context.Context, wanted sourceState) (errSt }) case sourceExistsLocally: if !sg.src.existsLocally(ctx) { - err = sg.suprvsr.do(ctx, sg.src.sourceType(), ctSourceInit, func(ctx context.Context) error { + err = sg.suprvsr.do(ctx, sg.src.sourceType().String(), ctSourceInit, func(ctx context.Context) error { return sg.src.initLocal(ctx) }) @@ -590,7 +594,7 @@ func (sg *sourceGateway) require(ctx context.Context, wanted sourceState) (errSt } case sourceHasLatestVersionList: var pvl []PairedVersion - err = sg.suprvsr.do(ctx, sg.src.sourceType(), ctListVersions, func(ctx context.Context) error { + err = sg.suprvsr.do(ctx, sg.src.sourceType().String(), ctListVersions, func(ctx context.Context) error { pvl, err = sg.src.listVersions(ctx) return err }) @@ -599,7 +603,7 @@ func (sg *sourceGateway) require(ctx context.Context, wanted sourceState) (errSt sg.cache.setVersionMap(pvl) } case sourceHasLatestLocally: - err = sg.suprvsr.do(ctx, sg.src.sourceType(), ctSourceFetch, func(ctx context.Context) error { + err = sg.suprvsr.do(ctx, sg.src.sourceType().String(), ctSourceFetch, func(ctx context.Context) error { return sg.src.updateLocal(ctx) }) } @@ -634,5 +638,5 @@ type source interface { revisionPresentIn(Revision) (bool, error) disambiguateRevision(context.Context, Revision) (Revision, error) exportRevisionTo(context.Context, Revision, string) error - sourceType() string + sourceType() SourceType } diff --git a/gps/source_manager.go b/gps/source_manager.go index 489084d7ef..9fc37965bb 100644 --- a/gps/source_manager.go +++ b/gps/source_manager.go @@ -26,6 +26,33 @@ import ( "github.com/sdboyer/constext" ) +// SourceType is the type of project source (VCS, packaged file, etc). +type SourceType uint8 + +// SourceType constants. +const ( + InvalidSource = iota + VcsGit + VcsHg + VcsBzr + VcsSvn +) + +func (st SourceType) String() string { + switch st { + case VcsGit: + return "git" + case VcsHg: + return "hg" + case VcsBzr: + return "bzr" + case VcsSvn: + return "svn" + default: + return fmt.Sprintf("%#v does not represent a known source type, this probably indicates a bug", st) + } +} + // Used to compute a friendly filepath from a URL-shaped input. var sanitizer = strings.NewReplacer("-", "--", ":", "-", "/", "-", "+", "-") @@ -100,6 +127,9 @@ type SourceManager interface { // repository root. GetManifestAndLock(ProjectIdentifier, Version, ProjectAnalyzer) (Manifest, Lock, error) + // GetSourceType returns SourceType for the provided ProjectIdentifier. + GetSourceType(ProjectIdentifier) (SourceType, error) + // ExportProject writes out the tree of the provided import path, at the // provided version, to the provided directory. ExportProject(context.Context, ProjectIdentifier, Version, string) error @@ -422,6 +452,20 @@ func (sm *SourceMgr) GetManifestAndLock(id ProjectIdentifier, v Version, an Proj return srcg.getManifestAndLock(context.TODO(), id.ProjectRoot, v, an) } +// GetSourceType returns SourceType for the provided ProjectIdentifier. +func (sm *SourceMgr) GetSourceType(id ProjectIdentifier) (SourceType, error) { + if atomic.LoadInt32(&sm.releasing) == 1 { + return InvalidSource, ErrSourceManagerIsReleased + } + + srcg, err := sm.srcCoord.getSourceGatewayFor(context.TODO(), id) + if err != nil { + return InvalidSource, err + } + + return srcg.getSourceType(), nil +} + // ListPackages parses the tree of the Go packages at and below the ProjectRoot // of the given ProjectIdentifier, at the given version. func (sm *SourceMgr) ListPackages(id ProjectIdentifier, v Version) (pkgtree.PackageTree, error) { diff --git a/gps/vcs_source.go b/gps/vcs_source.go index f6b7aef136..3e1e5324a6 100644 --- a/gps/vcs_source.go +++ b/gps/vcs_source.go @@ -14,6 +14,7 @@ import ( "strings" "github.com/Masterminds/semver" + "github.com/Masterminds/vcs" "github.com/golang/dep/gps/pkgtree" "github.com/golang/dep/internal/fs" "github.com/pkg/errors" @@ -23,8 +24,19 @@ type baseVCSSource struct { repo ctxRepo } -func (bs *baseVCSSource) sourceType() string { - return string(bs.repo.Vcs()) +func (bs *baseVCSSource) sourceType() SourceType { + switch bs.repo.Vcs() { + case vcs.Git: + return VcsGit + case vcs.Hg: + return VcsHg + case vcs.Bzr: + return VcsBzr + case vcs.Svn: + return VcsSvn + default: + return InvalidSource + } } func (bs *baseVCSSource) existsLocally(ctx context.Context) bool { From 19a451c7067a48a27949ac5c7b954612d942afdc Mon Sep 17 00:00:00 2001 From: Sunny Date: Sat, 11 Nov 2017 18:31:36 +0530 Subject: [PATCH 03/14] gps: Add ExistsUpstream in SourceManager --- cmd/dep/status.go | 8 ++++++++ gps/solve_basic_test.go | 4 ++++ gps/source_manager.go | 17 +++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/cmd/dep/status.go b/cmd/dep/status.go index 0790aff042..c73d02efc0 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -900,13 +900,21 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source projStatus.Version = pl.Version().String() projStatus.Source = projStatus.Project projStatus.AltSource = pl.Ident().Source + rev, _, _ := gps.VersionComponentStrings(pl.Version()) projStatus.Revision = rev + vcsType, err := sm.GetVcsType(pl.Ident()) if err != nil { return err } projStatus.SourceType = vcsType + + existsUpstream, err := sm.ExistsUpstream(pl.Ident()) + if err != nil { + return err + } + projStatus.UpstreamExists = existsUpstream } } diff --git a/gps/solve_basic_test.go b/gps/solve_basic_test.go index f937dda837..434256119d 100644 --- a/gps/solve_basic_test.go +++ b/gps/solve_basic_test.go @@ -1491,6 +1491,10 @@ func (sm *depspecSourceManager) GetSourceType(id ProjectIdentifier) (SourceType, return InvalidSource, fmt.Errorf("dummy sm doesn't implement GetSourceType") } +func (sm *depspecSourceManager) ExistsUpstream(id ProjectIdentifier) (bool, error) { + return false, fmt.Errorf("dummy sm doesn't implement ExistsUpstream") +} + func (sm *depspecSourceManager) rootSpec() depspec { return sm.specs[0] } diff --git a/gps/source_manager.go b/gps/source_manager.go index 9fc37965bb..f7598ab1c3 100644 --- a/gps/source_manager.go +++ b/gps/source_manager.go @@ -130,6 +130,9 @@ type SourceManager interface { // GetSourceType returns SourceType for the provided ProjectIdentifier. GetSourceType(ProjectIdentifier) (SourceType, error) + // ExistsUpstream indicates whether the provided Project exists upstream. + ExistsUpstream(ProjectIdentifier) (bool, error) + // ExportProject writes out the tree of the provided import path, at the // provided version, to the provided directory. ExportProject(context.Context, ProjectIdentifier, Version, string) error @@ -466,6 +469,20 @@ func (sm *SourceMgr) GetSourceType(id ProjectIdentifier) (SourceType, error) { return srcg.getSourceType(), nil } +// ExistsUpstream indicates whether the provided Project exists upstream. +func (sm *SourceMgr) ExistsUpstream(id ProjectIdentifier) (bool, error) { + if atomic.LoadInt32(&sm.releasing) == 1 { + return false, ErrSourceManagerIsReleased + } + + srcg, err := sm.srcCoord.getSourceGatewayFor(context.TODO(), id) + if err != nil { + return false, err + } + + return srcg.existsUpstream(context.TODO()), nil +} + // ListPackages parses the tree of the Go packages at and below the ProjectRoot // of the given ProjectIdentifier, at the given version. func (sm *SourceMgr) ListPackages(id ProjectIdentifier, v Version) (pkgtree.PackageTree, error) { From 7ca608f84cae3647ac328def132d66305efcc29c Mon Sep 17 00:00:00 2001 From: Sunny Date: Sat, 11 Nov 2017 19:41:21 +0530 Subject: [PATCH 04/14] status: Add more attributes in project status UPSTREAM VERSION EXISTS, PACKAGES, PUB VERSIONS, CONSTRAINTS --- cmd/dep/status.go | 102 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 83 insertions(+), 19 deletions(-) diff --git a/cmd/dep/status.go b/cmd/dep/status.go index c73d02efc0..dd8ab502fe 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -14,6 +14,7 @@ import ( "io/ioutil" "log" "sort" + "strings" "sync" "text/tabwriter" @@ -842,11 +843,11 @@ type projectStatus struct { Constraints []string Source string AltSource string - PubVersions []string + PubVersions map[string][]string Revision string LatestAllowed string SourceType string - Packages string + Packages []string ProjectImporters map[string]string PackageImporters map[string]string UpstreamExists bool @@ -871,7 +872,8 @@ UPSTREAM EXISTS: %t UPSTREAM VERSION EXISTS: %t `, ps.Project, ps.Version, ps.Constraints, ps.Source, ps.AltSource, - ps.PubVersions, ps.Revision, ps.LatestAllowed, ps.SourceType, ps.Packages, + ps.PubVersions, ps.Revision, ps.LatestAllowed, ps.SourceType, + strings.Join(ps.Packages, ", "), ps.ProjectImporters, ps.PackageImporters, ps.UpstreamExists, ps.UpstreamVersionExists) } @@ -888,37 +890,99 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source prs = append(prs, pr) } + // This ptree would not be required with the new collectConstraints() implementation. + ptree, err := p.ParseRootPackageTree() + if err != nil { + return err + } + // Collect all the constraints. + cc := collectConstraints(ptree, p, sm) + for _, pr := range prs { // Create projectStatus and add Project name. projStatus := projectStatus{ Project: string(pr), } - // Get the currently selected version from from lock. + // Get the currently selected version from lock. for _, pl := range p.Lock.Projects() { - if pr == pl.Ident().ProjectRoot { - projStatus.Version = pl.Version().String() - projStatus.Source = projStatus.Project - projStatus.AltSource = pl.Ident().Source + if pr != pl.Ident().ProjectRoot { + continue + } - rev, _, _ := gps.VersionComponentStrings(pl.Version()) - projStatus.Revision = rev + // VERSION + projStatus.Version = pl.Version().String() + // SOURCE + projStatus.Source = projStatus.Project + // ALT SOURCE + projStatus.AltSource = pl.Ident().Source - vcsType, err := sm.GetVcsType(pl.Ident()) - if err != nil { - return err + // REVISION + projStatus.Revision, _, _ = gps.VersionComponentStrings(pl.Version()) + + srcType, err := sm.GetSourceType(pl.Ident()) + if err != nil { + return err + } + // SOURCE TYPE + projStatus.SourceType = srcType.String() + + existsUpstream, err := sm.ExistsUpstream(pl.Ident()) + if err != nil { + return err + } + // UPSTREAM EXISTS + projStatus.UpstreamExists = existsUpstream + + // Fetch all the versions. + pvs, err := sm.ListVersions(pl.Ident()) + if err != nil { + return err + } + + // UPSTREAM VERSION EXISTS + for _, pv := range pvs { + if pv.Unpair().String() == pl.Version().String() { + projStatus.UpstreamVersionExists = true + break } - projStatus.SourceType = vcsType + } - existsUpstream, err := sm.ExistsUpstream(pl.Ident()) - if err != nil { - return err + pkgtree, err := sm.ListPackages(pl.Ident(), pl.Version()) + if err != nil { + return err + } + + // PACKAGES + for pkgPath := range pkgtree.Packages { + projStatus.Packages = append(projStatus.Packages, pkgPath) + } + + // PUB VERSION + var semvers, branches, nonsemvers []string + for _, pv := range pvs { + switch pv.Type() { + case gps.IsSemver: + semvers = append(semvers, pv.Unpair().String()) + case gps.IsBranch: + branches = append(branches, pv.Unpair().String()) + default: + nonsemvers = append(nonsemvers, pv.Unpair().String()) } - projStatus.UpstreamExists = existsUpstream + } + projStatus.PubVersions = make(map[string][]string) + projStatus.PubVersions["semver"] = semvers + projStatus.PubVersions["branches"] = branches + projStatus.PubVersions["nonsemvers"] = nonsemvers + + // CONSTRAINTS + constraints := cc[string(pr)] + for _, c := range constraints { + projStatus.Constraints = append(projStatus.Constraints, c.String()) } } - fmt.Println(projStatus) + ctx.Out.Println(projStatus) } return nil From 644f0c273cf6d56c4ddde349aaa227a30a354e96 Mon Sep 17 00:00:00 2001 From: Sunny Date: Fri, 17 Nov 2017 00:03:59 +0530 Subject: [PATCH 05/14] runProjectStatus: Add PACKAGE IMPORTS & PROJECT IMPORTS --- cmd/dep/status.go | 51 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/cmd/dep/status.go b/cmd/dep/status.go index dd8ab502fe..b51e88cfee 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -848,8 +848,8 @@ type projectStatus struct { LatestAllowed string SourceType string Packages []string - ProjectImporters map[string]string - PackageImporters map[string]string + ProjectImporters map[string]bool + PackageImporters map[string][]string UpstreamExists bool UpstreamVersionExists bool } @@ -866,7 +866,7 @@ REVISION: %s LATEST ALLOWED: %s SOURCE TYPE: %s PACKAGES: %s -PROJECT IMPORTERS: %s +PROJECT IMPORTERS: %v PACKAGE IMPORTERS: %s UPSTREAM EXISTS: %t UPSTREAM VERSION EXISTS: %t @@ -902,18 +902,57 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source // Create projectStatus and add Project name. projStatus := projectStatus{ Project: string(pr), + Source: string(pr), } - // Get the currently selected version from lock. for _, pl := range p.Lock.Projects() { + // Get the corresponding locked project for the target project. if pr != pl.Ident().ProjectRoot { + // PROJECT IMPORTERS & PACKAGE IMPORTERS + // Since this project is not the target project, we should + // check if this project imports the target project. + // Get the Package List and check for existence of target + // project's import paths in the list. + // Add the Project to PROJECT IMPORTERS and package to PACKAGE + // IMPORTERS if they import the target project. + pkgtree, err := sm.ListPackages(pl.Ident(), pl.Version()) + if err != nil { + return err + } + + proot := string(pl.Ident().ProjectRoot) + + // Get the reachmap. Reachmap contains package name and imports + // of the package. + prm, _ := pkgtree.ToReachMap(true, true, false, p.Manifest.IgnoredPackages()) + for pkg, ie := range prm { + // Iterate through the external imports and check if they + // import any package from the target project. + for _, p := range ie.External { + if strings.HasPrefix(p, string(pr)) { + // Initialize ProjectImporters map if it's the first entry. + if len(projStatus.ProjectImporters) == 0 { + projStatus.ProjectImporters = make(map[string]bool) + } + // Add to ProjectImporters if it doesn't exists. + if _, ok := projStatus.ProjectImporters[proot]; !ok { + projStatus.ProjectImporters[proot] = true + } + // Initialize PackageImporters map if it's the first entry. + if len(projStatus.PackageImporters[proot]) == 0 { + projStatus.PackageImporters = make(map[string][]string) + } + // List Packages that import packages from target project. + projStatus.PackageImporters[p] = append(projStatus.PackageImporters[p], pkg) + } + } + } + continue } // VERSION projStatus.Version = pl.Version().String() - // SOURCE - projStatus.Source = projStatus.Project // ALT SOURCE projStatus.AltSource = pl.Ident().Source From 1b03d9f5fccaafda16b7d239ddd2dc5824ee9105 Mon Sep 17 00:00:00 2001 From: Sunny Date: Fri, 17 Nov 2017 18:15:03 +0530 Subject: [PATCH 06/14] runProjectStatus: Add LATEST ALLOWED --- cmd/dep/status.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/cmd/dep/status.go b/cmd/dep/status.go index b51e88cfee..b5ebbf8653 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -6,6 +6,7 @@ package main import ( "bytes" + "context" "encoding/json" "flag" "fmt" @@ -1019,6 +1020,37 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source for _, c := range constraints { projStatus.Constraints = append(projStatus.Constraints, c.String()) } + + // LATEST ALLOWED + // Perform a solve to get the latest allowed revision. + params := p.MakeParams() + if ctx.Verbose { + params.TraceLogger = ctx.Err + } + + params.RootPackageTree, err = p.ParseRootPackageTree() + if err != nil { + return err + } + + params.ToChange = append(params.ToChange, pr) + + solver, err := gps.Prepare(params, sm) + if err != nil { + return err + } + solution, err := solver.Solve(context.TODO()) + if err != nil { + return err + } + + prjcts := solution.Projects() + for _, prj := range prjcts { + if pr == prj.Ident().ProjectRoot { + r, _, _ := gps.VersionComponentStrings(prj.Version()) + projStatus.LatestAllowed = r + } + } } ctx.Out.Println(projStatus) From d12d486155a24a2c5ab175f1346c74986aabdc1a Mon Sep 17 00:00:00 2001 From: Sunny Date: Fri, 17 Nov 2017 18:51:54 +0530 Subject: [PATCH 07/14] runProjectStatus: perform single gps solve Perform solve at the end for all the target projects together and get the latest allowed revision for them. --- cmd/dep/status.go | 70 +++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/cmd/dep/status.go b/cmd/dep/status.go index b5ebbf8653..7169fb3be8 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -883,6 +883,10 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source // Collect all the project roots from the arguments. var prs []gps.ProjectRoot + // Collect pointers to resultant projectStatus. + var resultStatus []*projectStatus + + // Get the proper project root of the projects. for _, arg := range args { pr, err := sm.DeduceProjectRoot(arg) if err != nil { @@ -900,11 +904,12 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source cc := collectConstraints(ptree, p, sm) for _, pr := range prs { - // Create projectStatus and add Project name. + // Create projectStatus and add project name and source. projStatus := projectStatus{ Project: string(pr), Source: string(pr), } + resultStatus = append(resultStatus, &projStatus) for _, pl := range p.Lock.Projects() { // Get the corresponding locked project for the target project. @@ -923,8 +928,8 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source proot := string(pl.Ident().ProjectRoot) - // Get the reachmap. Reachmap contains package name and imports - // of the package. + // Get the PackageTree reachmap. Reachmap contains package name + // and imports of the package. prm, _ := pkgtree.ToReachMap(true, true, false, p.Manifest.IgnoredPackages()) for pkg, ie := range prm { // Iterate through the external imports and check if they @@ -1020,40 +1025,45 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source for _, c := range constraints { projStatus.Constraints = append(projStatus.Constraints, c.String()) } + } + } - // LATEST ALLOWED - // Perform a solve to get the latest allowed revision. - params := p.MakeParams() - if ctx.Verbose { - params.TraceLogger = ctx.Err - } + // LATEST ALLOWED + // Perform a solve to get the latest allowed revisions. + params := p.MakeParams() + if ctx.Verbose { + params.TraceLogger = ctx.Err + } - params.RootPackageTree, err = p.ParseRootPackageTree() - if err != nil { - return err - } + params.RootPackageTree, err = p.ParseRootPackageTree() + if err != nil { + return err + } - params.ToChange = append(params.ToChange, pr) + // Add all the projects in params.ToChange. + for _, rs := range resultStatus { + params.ToChange = append(params.ToChange, gps.ProjectRoot(rs.Project)) + } - solver, err := gps.Prepare(params, sm) - if err != nil { - return err - } - solution, err := solver.Solve(context.TODO()) - if err != nil { - return err - } + solver, err := gps.Prepare(params, sm) + if err != nil { + return err + } + solution, err := solver.Solve(context.TODO()) + if err != nil { + return err + } - prjcts := solution.Projects() - for _, prj := range prjcts { - if pr == prj.Ident().ProjectRoot { - r, _, _ := gps.VersionComponentStrings(prj.Version()) - projStatus.LatestAllowed = r - } + // Iterate through the solution and get the revisions for the target projects. + projects := solution.Projects() + for _, rs := range resultStatus { + for _, project := range projects { + if gps.ProjectRoot(rs.Project) == project.Ident().ProjectRoot { + r, _, _ := gps.VersionComponentStrings(project.Version()) + rs.LatestAllowed = r } } - - ctx.Out.Println(projStatus) + ctx.Out.Println(rs) } return nil From b73e137a155290b3f9f2af554a3377c5a30b5216 Mon Sep 17 00:00:00 2001 From: Sunny Date: Fri, 17 Nov 2017 20:15:04 +0530 Subject: [PATCH 08/14] runProjectStatus: Optimizations to avoid redundant computation --- cmd/dep/status.go | 113 +++++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 51 deletions(-) diff --git a/cmd/dep/status.go b/cmd/dep/status.go index 7169fb3be8..2660fc20dc 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -903,6 +903,34 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source // Collect all the constraints. cc := collectConstraints(ptree, p, sm) + // Collect list of packages in target projects. + pkgs := make(map[gps.ProjectRoot][]string) + + // Collect reachmap of all the locked projects. + reachmaps := make(map[string]pkgtree.ReachMap) + + // Collect and store all the necessary data from pkgtree(s). + // TODO: Make this concurrent. + for _, pl := range p.Lock.Projects() { + pkgtree, err := sm.ListPackages(pl.Ident(), pl.Version()) + if err != nil { + return err + } + + // Collect reachmaps. + prm, _ := pkgtree.ToReachMap(true, true, false, p.Manifest.IgnoredPackages()) + reachmaps[string(pl.Ident().ProjectRoot)] = prm + + // Collect list of packages if it's one of the target projects. + for _, pr := range prs { + if pr == pl.Ident().ProjectRoot { + for pkg := range pkgtree.Packages { + pkgs[pr] = append(pkgs[pr], pkg) + } + } + } + } + for _, pr := range prs { // Create projectStatus and add project name and source. projStatus := projectStatus{ @@ -911,49 +939,41 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source } resultStatus = append(resultStatus, &projStatus) - for _, pl := range p.Lock.Projects() { - // Get the corresponding locked project for the target project. - if pr != pl.Ident().ProjectRoot { - // PROJECT IMPORTERS & PACKAGE IMPORTERS - // Since this project is not the target project, we should - // check if this project imports the target project. - // Get the Package List and check for existence of target - // project's import paths in the list. - // Add the Project to PROJECT IMPORTERS and package to PACKAGE - // IMPORTERS if they import the target project. - pkgtree, err := sm.ListPackages(pl.Ident(), pl.Version()) - if err != nil { - return err - } + // Gather PROJECT IMPORTERS & PACKAGE IMPORTERS data. + for projectroot, rmap := range reachmaps { + // If it's not the target project, check if it imports the target + // project. + if string(pr) == projectroot { + continue + } - proot := string(pl.Ident().ProjectRoot) - - // Get the PackageTree reachmap. Reachmap contains package name - // and imports of the package. - prm, _ := pkgtree.ToReachMap(true, true, false, p.Manifest.IgnoredPackages()) - for pkg, ie := range prm { - // Iterate through the external imports and check if they - // import any package from the target project. - for _, p := range ie.External { - if strings.HasPrefix(p, string(pr)) { - // Initialize ProjectImporters map if it's the first entry. - if len(projStatus.ProjectImporters) == 0 { - projStatus.ProjectImporters = make(map[string]bool) - } - // Add to ProjectImporters if it doesn't exists. - if _, ok := projStatus.ProjectImporters[proot]; !ok { - projStatus.ProjectImporters[proot] = true - } - // Initialize PackageImporters map if it's the first entry. - if len(projStatus.PackageImporters[proot]) == 0 { - projStatus.PackageImporters = make(map[string][]string) - } - // List Packages that import packages from target project. - projStatus.PackageImporters[p] = append(projStatus.PackageImporters[p], pkg) + for pkg, ie := range rmap { + // Iterate through the external imports and check if they + // import any package from the target project. + for _, p := range ie.External { + if strings.HasPrefix(p, string(pr)) { + // Initialize ProjectImporters map if it's the first entry. + if len(projStatus.ProjectImporters) == 0 { + projStatus.ProjectImporters = make(map[string]bool) + } + // Add to ProjectImporters if it doesn't exists. + if _, ok := projStatus.ProjectImporters[projectroot]; !ok { + projStatus.ProjectImporters[projectroot] = true } + // Initialize PackageImporters map if it's the first entry. + if len(projStatus.PackageImporters[p]) == 0 { + projStatus.PackageImporters = make(map[string][]string) + } + // List Packages that import packages from target project. + projStatus.PackageImporters[p] = append(projStatus.PackageImporters[p], pkg) } } + } + } + // Gather data from the locked project. + for _, pl := range p.Lock.Projects() { + if pr != pl.Ident().ProjectRoot { continue } @@ -961,7 +981,6 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source projStatus.Version = pl.Version().String() // ALT SOURCE projStatus.AltSource = pl.Ident().Source - // REVISION projStatus.Revision, _, _ = gps.VersionComponentStrings(pl.Version()) @@ -993,15 +1012,8 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source } } - pkgtree, err := sm.ListPackages(pl.Ident(), pl.Version()) - if err != nil { - return err - } - // PACKAGES - for pkgPath := range pkgtree.Packages { - projStatus.Packages = append(projStatus.Packages, pkgPath) - } + projStatus.Packages = pkgs[pr] // PUB VERSION var semvers, branches, nonsemvers []string @@ -1040,15 +1052,14 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source return err } - // Add all the projects in params.ToChange. - for _, rs := range resultStatus { - params.ToChange = append(params.ToChange, gps.ProjectRoot(rs.Project)) - } + // Add all the target projects in params.ToChange. + params.ToChange = append(params.ToChange, prs...) solver, err := gps.Prepare(params, sm) if err != nil { return err } + solution, err := solver.Solve(context.TODO()) if err != nil { return err From a9ca3226de10f171a1ae78a96d950724f2a47b55 Mon Sep 17 00:00:00 2001 From: Sunny Date: Sat, 18 Nov 2017 19:35:57 +0530 Subject: [PATCH 09/14] projectStatus: Prettify output --- cmd/dep/status.go | 166 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 142 insertions(+), 24 deletions(-) diff --git a/cmd/dep/status.go b/cmd/dep/status.go index 2660fc20dc..e81d0c203a 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -5,6 +5,7 @@ package main import ( + "bufio" "bytes" "context" "encoding/json" @@ -838,45 +839,162 @@ func (p byProject) Len() int { return len(p) } func (p byProject) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func (p byProject) Less(i, j int) bool { return p[i].Project < p[j].Project } +// pubVersion type to store Public Version data of a project. +type pubVersions map[string][]string + +// TabString returns a tabwriter compatible string of pubVersion. +func (pv pubVersions) TabString() string { + var buf bytes.Buffer + w := bufio.NewWriter(&buf) + + // Create a list of version categories and sort it for consistent results. + var catgs []string + + for catg := range pv { + catgs = append(catgs, catg) + } + + // Sort the list of categories. + sort.Strings(catgs) + + // Count the number of different version categories. Use this count to add + // a newline("\n") and tab("\t") in all the version string list except the + // first one. This is required to maintain the indentation of the strings + // when used with tabwriter. + // semver:...\n \tbranches:...\n \tnonsemvers:... + count := 0 + for _, catg := range catgs { + count++ + if count > 1 { + fmt.Fprintf(w, "\n \t") + } + + vers := pv[catg] + + // Sort the versions list for consistent result. + sort.Strings(vers) + + fmt.Fprintf(w, "%s: %s", catg, strings.Join(vers, ", ")) + } + w.Flush() + + return buf.String() +} + +type projectImporters map[string]bool + +func (pi projectImporters) String() string { + var projects []string + + for p := range pi { + projects = append(projects, p) + } + + // Sort the projects for a consistent result. + sort.Strings(projects) + + return strings.Join(projects, ", ") +} + +type packageImporters map[string][]string + +func (pi packageImporters) TabString() string { + var buf bytes.Buffer + w := bufio.NewWriter(&buf) + + // Create a list of packages in the map and sort it for consistent results. + var pkgs []string + + for pkg := range pi { + pkgs = append(pkgs, pkg) + } + + // Sort the list of packages. + sort.Strings(pkgs) + + // Count the number of different packages. Use this count to add + // a newline("\n") and tab("\t") in all the package string header except the + // first one. This is required to maintain the indentation of the strings + // when used with tabwriter. + // github.com/x/y\n \t github.com/a/b/foo\n \t github.com/a/b/bar + count := 0 + for _, pkg := range pkgs { + count++ + if count > 1 { + fmt.Fprintf(w, "\n \t") + } + + fmt.Fprintf(w, "%s", pkg) + + importers := pi[pkg] + + // Sort the importers list for consistent result. + sort.Strings(importers) + + for _, p := range importers { + fmt.Fprintf(w, "\n \t %s", p) + } + } + w.Flush() + + return buf.String() +} + type projectStatus struct { Project string Version string Constraints []string Source string AltSource string - PubVersions map[string][]string + PubVersions pubVersions Revision string LatestAllowed string SourceType string Packages []string - ProjectImporters map[string]bool - PackageImporters map[string][]string + ProjectImporters projectImporters + PackageImporters packageImporters UpstreamExists bool UpstreamVersionExists bool } func (ps projectStatus) String() string { - return fmt.Sprintf(` -PROJECT: %s -VERSION: %s -CONSTRAINTS: %s -SOURCE: %s -ALT SOURCE: %s -PUB VERSION: %s -REVISION: %s -LATEST ALLOWED: %s -SOURCE TYPE: %s -PACKAGES: %s -PROJECT IMPORTERS: %v -PACKAGE IMPORTERS: %s -UPSTREAM EXISTS: %t -UPSTREAM VERSION EXISTS: %t -`, + var buf bytes.Buffer + + w := tabwriter.NewWriter(&buf, 0, 4, 2, ' ', 0) + + upstreamExists := "no" + if ps.UpstreamExists { + upstreamExists = "yes" + } + + upstreamVersionExists := "no" + if ps.UpstreamVersionExists { + upstreamVersionExists = "yes" + } + + fmt.Fprintf(w, "\n"+ + "PROJECT:\t%s\n"+ + "VERSION:\t%s\n"+ + "CONSTRAINTS:\t%s\n"+ + "SOURCE:\t%s\n"+ + "ALT SOURCE:\t%s\n"+ + "PUB VERSION:\t%s\n"+ + "REVISION:\t%s\n"+ + "LATEST ALLOWED:\t%s\n"+ + "SOURCE TYPE:\t%s\n"+ + "PACKAGES:\t%s\n"+ + "PROJECT IMPORTERS:\t%s\n"+ + "PACKAGE IMPORTERS:\t%s\n"+ + "UPSTREAM EXISTS:\t%s\n"+ + "UPSTREAM VERSION EXISTS:\t%s", ps.Project, ps.Version, ps.Constraints, ps.Source, ps.AltSource, - ps.PubVersions, ps.Revision, ps.LatestAllowed, ps.SourceType, - strings.Join(ps.Packages, ", "), - ps.ProjectImporters, ps.PackageImporters, ps.UpstreamExists, - ps.UpstreamVersionExists) + ps.PubVersions.TabString(), ps.Revision, ps.LatestAllowed, ps.SourceType, + strings.Join(ps.Packages, ", "), ps.ProjectImporters, + ps.PackageImporters.TabString(), upstreamExists, upstreamVersionExists, + ) + w.Flush() + + return buf.String() } func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.SourceManager) error { @@ -1028,7 +1146,7 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source } } projStatus.PubVersions = make(map[string][]string) - projStatus.PubVersions["semver"] = semvers + projStatus.PubVersions["semvers"] = semvers projStatus.PubVersions["branches"] = branches projStatus.PubVersions["nonsemvers"] = nonsemvers From db77a192248355f2266a3a80705956db8d61503d Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 19 Nov 2017 01:25:03 +0530 Subject: [PATCH 10/14] test: Integration test for status project tableoutput --- .../status/project_status/final/Gopkg.lock | 27 +++++++++++++++++++ .../status/project_status/final/Gopkg.toml | 11 ++++++++ .../status/project_status/final/main.go | 13 +++++++++ .../status/project_status/initial/Gopkg.lock | 27 +++++++++++++++++++ .../status/project_status/initial/Gopkg.toml | 11 ++++++++ .../status/project_status/initial/main.go | 13 +++++++++ .../status/project_status/stdout.txt | 20 ++++++++++++++ .../status/project_status/testcase.json | 12 +++++++++ 8 files changed, 134 insertions(+) create mode 100644 cmd/dep/testdata/harness_tests/status/project_status/final/Gopkg.lock create mode 100644 cmd/dep/testdata/harness_tests/status/project_status/final/Gopkg.toml create mode 100644 cmd/dep/testdata/harness_tests/status/project_status/final/main.go create mode 100644 cmd/dep/testdata/harness_tests/status/project_status/initial/Gopkg.lock create mode 100644 cmd/dep/testdata/harness_tests/status/project_status/initial/Gopkg.toml create mode 100644 cmd/dep/testdata/harness_tests/status/project_status/initial/main.go create mode 100644 cmd/dep/testdata/harness_tests/status/project_status/stdout.txt create mode 100644 cmd/dep/testdata/harness_tests/status/project_status/testcase.json diff --git a/cmd/dep/testdata/harness_tests/status/project_status/final/Gopkg.lock b/cmd/dep/testdata/harness_tests/status/project_status/final/Gopkg.lock new file mode 100644 index 0000000000..326bc04f3c --- /dev/null +++ b/cmd/dep/testdata/harness_tests/status/project_status/final/Gopkg.lock @@ -0,0 +1,27 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "github.com/darkowlzz/deptest-project-status" + packages = ["pkgfoo"] + revision = "bddc1e2c8e4f0f264223389adf345d3858823677" + +[[projects]] + name = "github.com/sdboyer/deptest" + packages = ["."] + revision = "ff2948a2ac8f538c4ecd55962e919d1e13e74baf" + version = "v1.0.0" + +[[projects]] + name = "github.com/sdboyer/deptestdos" + packages = ["."] + revision = "5c607206be5decd28e6263ffffdcee067266015e" + version = "v2.0.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "36a300774881748908b8209ab36dbfda73e8c51145d6bdbc9d87afd2655186c9" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/cmd/dep/testdata/harness_tests/status/project_status/final/Gopkg.toml b/cmd/dep/testdata/harness_tests/status/project_status/final/Gopkg.toml new file mode 100644 index 0000000000..6dbb73c194 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/status/project_status/final/Gopkg.toml @@ -0,0 +1,11 @@ +[[constraint]] + branch = "master" + name = "github.com/darkowlzz/deptest-project-status" + +[[constraint]] + name = "github.com/sdboyer/deptest" + version = "1.0.0" + +[[constraint]] + name = "github.com/sdboyer/deptestdos" + version = "2.0.0" diff --git a/cmd/dep/testdata/harness_tests/status/project_status/final/main.go b/cmd/dep/testdata/harness_tests/status/project_status/final/main.go new file mode 100644 index 0000000000..579d6d2a29 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/status/project_status/final/main.go @@ -0,0 +1,13 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + _ "github.com/darkowlzz/deptest-project-status/pkgfoo" + _ "github.com/sdboyer/deptest" + _ "github.com/sdboyer/deptestdos" +) + +func main() {} diff --git a/cmd/dep/testdata/harness_tests/status/project_status/initial/Gopkg.lock b/cmd/dep/testdata/harness_tests/status/project_status/initial/Gopkg.lock new file mode 100644 index 0000000000..326bc04f3c --- /dev/null +++ b/cmd/dep/testdata/harness_tests/status/project_status/initial/Gopkg.lock @@ -0,0 +1,27 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "github.com/darkowlzz/deptest-project-status" + packages = ["pkgfoo"] + revision = "bddc1e2c8e4f0f264223389adf345d3858823677" + +[[projects]] + name = "github.com/sdboyer/deptest" + packages = ["."] + revision = "ff2948a2ac8f538c4ecd55962e919d1e13e74baf" + version = "v1.0.0" + +[[projects]] + name = "github.com/sdboyer/deptestdos" + packages = ["."] + revision = "5c607206be5decd28e6263ffffdcee067266015e" + version = "v2.0.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "36a300774881748908b8209ab36dbfda73e8c51145d6bdbc9d87afd2655186c9" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/cmd/dep/testdata/harness_tests/status/project_status/initial/Gopkg.toml b/cmd/dep/testdata/harness_tests/status/project_status/initial/Gopkg.toml new file mode 100644 index 0000000000..6dbb73c194 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/status/project_status/initial/Gopkg.toml @@ -0,0 +1,11 @@ +[[constraint]] + branch = "master" + name = "github.com/darkowlzz/deptest-project-status" + +[[constraint]] + name = "github.com/sdboyer/deptest" + version = "1.0.0" + +[[constraint]] + name = "github.com/sdboyer/deptestdos" + version = "2.0.0" diff --git a/cmd/dep/testdata/harness_tests/status/project_status/initial/main.go b/cmd/dep/testdata/harness_tests/status/project_status/initial/main.go new file mode 100644 index 0000000000..579d6d2a29 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/status/project_status/initial/main.go @@ -0,0 +1,13 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + _ "github.com/darkowlzz/deptest-project-status/pkgfoo" + _ "github.com/sdboyer/deptest" + _ "github.com/sdboyer/deptestdos" +) + +func main() {} diff --git a/cmd/dep/testdata/harness_tests/status/project_status/stdout.txt b/cmd/dep/testdata/harness_tests/status/project_status/stdout.txt new file mode 100644 index 0000000000..99c6db1cd5 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/status/project_status/stdout.txt @@ -0,0 +1,20 @@ + +PROJECT: github.com/sdboyer/deptest +VERSION: v1.0.0 +CONSTRAINTS: [] +SOURCE: github.com/sdboyer/deptest +ALT SOURCE: +PUB VERSION: branches: master + nonsemvers: + semvers: v0.8.0, v0.8.1, v1.0.0 +REVISION: ff2948a2ac8f538c4ecd55962e919d1e13e74baf +LATEST ALLOWED: ff2948a2ac8f538c4ecd55962e919d1e13e74baf +SOURCE TYPE: git +PACKAGES: github.com/sdboyer/deptest +PROJECT IMPORTERS: github.com/darkowlzz/deptest-project-status, github.com/sdboyer/deptestdos +PACKAGE IMPORTERS: github.com/sdboyer/deptest + github.com/darkowlzz/deptest-project-status/pkgbar + github.com/darkowlzz/deptest-project-status/pkgfoo + github.com/sdboyer/deptestdos +UPSTREAM EXISTS: yes +UPSTREAM VERSION EXISTS: yes diff --git a/cmd/dep/testdata/harness_tests/status/project_status/testcase.json b/cmd/dep/testdata/harness_tests/status/project_status/testcase.json new file mode 100644 index 0000000000..0509f7d837 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/status/project_status/testcase.json @@ -0,0 +1,12 @@ +{ + "commands": [ + ["ensure"], + ["status", "github.com/sdboyer/deptest"] + ], + "error-expected": "", + "vendor-final": [ + "github.com/darkowlzz/deptest-project-status", + "github.com/sdboyer/deptest", + "github.com/sdboyer/deptestdos" + ] +} From 436cab07770f48a668c92f0c2d403bd5b5605fce Mon Sep 17 00:00:00 2001 From: Sunny Date: Mon, 20 Nov 2017 19:00:29 +0530 Subject: [PATCH 11/14] test: Add TestProjectStatusString --- cmd/dep/status_test.go | 90 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/cmd/dep/status_test.go b/cmd/dep/status_test.go index c65ed0c67e..1a9d611e90 100644 --- a/cmd/dep/status_test.go +++ b/cmd/dep/status_test.go @@ -480,3 +480,93 @@ func TestValidateFlags(t *testing.T) { }) } } + +func TestProjectStatusString(t *testing.T) { + testCases := []struct { + name string + ps projectStatus + wantString string + }{ + { + name: "basic projectStatus", + ps: projectStatus{ + Project: "github.com/x/y", + Version: "v1.0", + Constraints: nil, + Source: "github.com/x/y", + AltSource: "https://github.com/z/y", + PubVersions: pubVersions{ + "semvers": []string{"v0.5", "v0.7", "v1.0", "v1.5"}, + "branches": []string{"master", "dev"}, + "nonsemvers": []string{"v1.0-rc1", "v1.5-rc"}, + }, + Revision: "some-rev", + LatestAllowed: "some-other-rev", + SourceType: "git", + Packages: []string{"github.com/x/y/pkgA", "github.com/x/y/pkgB", "github.com/x/y/pkgB/foo"}, + ProjectImporters: projectImporters{ + "github.com/a/b": true, + "github.com/foo/bar": true, + }, + PackageImporters: packageImporters{ + "github.com/x/y/pkgA": []string{"github.com/a/b/z", "github.com/foo/bar/k"}, + "github.com/x/y/pkgB/foo": []string{"github.com/a/b/j"}, + }, + UpstreamExists: true, + UpstreamVersionExists: true, + }, + wantString: ` +PROJECT: github.com/x/y +VERSION: v1.0 +CONSTRAINTS: [] +SOURCE: github.com/x/y +ALT SOURCE: https://github.com/z/y +PUB VERSION: branches: dev, master + nonsemvers: v1.0-rc1, v1.5-rc + semvers: v0.5, v0.7, v1.0, v1.5 +REVISION: some-rev +LATEST ALLOWED: some-other-rev +SOURCE TYPE: git +PACKAGES: github.com/x/y/pkgA, github.com/x/y/pkgB, github.com/x/y/pkgB/foo +PROJECT IMPORTERS: github.com/a/b, github.com/foo/bar +PACKAGE IMPORTERS: github.com/x/y/pkgA + github.com/a/b/z + github.com/foo/bar/k + github.com/x/y/pkgB/foo + github.com/a/b/j +UPSTREAM EXISTS: yes +UPSTREAM VERSION EXISTS: yes`, + }, + { + name: "projectStatus with no upstream exists", + ps: projectStatus{ + UpstreamExists: false, + UpstreamVersionExists: false, + }, + wantString: ` +PROJECT: +VERSION: +CONSTRAINTS: [] +SOURCE: +ALT SOURCE: +PUB VERSION: +REVISION: +LATEST ALLOWED: +SOURCE TYPE: +PACKAGES: +PROJECT IMPORTERS: +PACKAGE IMPORTERS: +UPSTREAM EXISTS: no +UPSTREAM VERSION EXISTS: no`, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + gotString := tc.ps.String() + if gotString != tc.wantString { + t.Errorf("unexpected projectStatus string: \n\t(GOT): %v\n\t(WNT): %v", gotString, tc.wantString) + } + }) + } +} From ec23702470ade3210cf6cf98f6396172e9d109a8 Mon Sep 17 00:00:00 2001 From: Sunny Date: Mon, 20 Nov 2017 21:52:56 +0530 Subject: [PATCH 12/14] status: Exclude pkgs not in depgraph from project status --- cmd/dep/status.go | 14 ++++++++++++++ .../harness_tests/status/project_status/stdout.txt | 1 - 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/cmd/dep/status.go b/cmd/dep/status.go index e81d0c203a..b6c16cc7d9 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -1027,6 +1027,15 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source // Collect reachmap of all the locked projects. reachmaps := make(map[string]pkgtree.ReachMap) + // Create a list of depgraph packages to be used to exclude packages that + // the root project does not use. + rootPkgTree, err := p.ParseRootPackageTree() + if err != nil { + return err + } + rootrm, _ := rootPkgTree.ToReachMap(true, true, false, p.Manifest.IgnoredPackages()) + depgraphPkgs := rootrm.FlattenFn(paths.IsStandardImportPath) + // Collect and store all the necessary data from pkgtree(s). // TODO: Make this concurrent. for _, pl := range p.Lock.Projects() { @@ -1066,6 +1075,11 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source } for pkg, ie := range rmap { + // Exclude packages not used by root project. + if !contains(depgraphPkgs, pkg) { + continue + } + // Iterate through the external imports and check if they // import any package from the target project. for _, p := range ie.External { diff --git a/cmd/dep/testdata/harness_tests/status/project_status/stdout.txt b/cmd/dep/testdata/harness_tests/status/project_status/stdout.txt index 99c6db1cd5..7dfe5a3ed8 100644 --- a/cmd/dep/testdata/harness_tests/status/project_status/stdout.txt +++ b/cmd/dep/testdata/harness_tests/status/project_status/stdout.txt @@ -13,7 +13,6 @@ SOURCE TYPE: git PACKAGES: github.com/sdboyer/deptest PROJECT IMPORTERS: github.com/darkowlzz/deptest-project-status, github.com/sdboyer/deptestdos PACKAGE IMPORTERS: github.com/sdboyer/deptest - github.com/darkowlzz/deptest-project-status/pkgbar github.com/darkowlzz/deptest-project-status/pkgfoo github.com/sdboyer/deptestdos UPSTREAM EXISTS: yes From 05516b8d307c4f40b50982e35b27fded09df8945 Mon Sep 17 00:00:00 2001 From: Sunny Date: Mon, 20 Nov 2017 22:26:03 +0530 Subject: [PATCH 13/14] status: project status error out when project is not in lock --- cmd/dep/status.go | 6 +++++ .../final/Gopkg.lock | 27 +++++++++++++++++++ .../final/Gopkg.toml | 11 ++++++++ .../project_status_not_in_lock/final/main.go | 13 +++++++++ .../initial/Gopkg.lock | 27 +++++++++++++++++++ .../initial/Gopkg.toml | 11 ++++++++ .../initial/main.go | 13 +++++++++ .../project_status_not_in_lock/testcase.json | 12 +++++++++ 8 files changed, 120 insertions(+) create mode 100644 cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/final/Gopkg.lock create mode 100644 cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/final/Gopkg.toml create mode 100644 cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/final/main.go create mode 100644 cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/initial/Gopkg.lock create mode 100644 cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/initial/Gopkg.toml create mode 100644 cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/initial/main.go create mode 100644 cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/testcase.json diff --git a/cmd/dep/status.go b/cmd/dep/status.go index b6c16cc7d9..9a2281d4a1 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -1010,6 +1010,12 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source if err != nil { return err } + + // Check if the target project is in the lock. + if !p.Lock.HasProjectWithRoot(pr) { + return errors.Errorf("%s is not in the lock file. Ensure that the project is being used and run `dep ensure` to generate a new lock file.", pr) + } + prs = append(prs, pr) } diff --git a/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/final/Gopkg.lock b/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/final/Gopkg.lock new file mode 100644 index 0000000000..326bc04f3c --- /dev/null +++ b/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/final/Gopkg.lock @@ -0,0 +1,27 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "github.com/darkowlzz/deptest-project-status" + packages = ["pkgfoo"] + revision = "bddc1e2c8e4f0f264223389adf345d3858823677" + +[[projects]] + name = "github.com/sdboyer/deptest" + packages = ["."] + revision = "ff2948a2ac8f538c4ecd55962e919d1e13e74baf" + version = "v1.0.0" + +[[projects]] + name = "github.com/sdboyer/deptestdos" + packages = ["."] + revision = "5c607206be5decd28e6263ffffdcee067266015e" + version = "v2.0.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "36a300774881748908b8209ab36dbfda73e8c51145d6bdbc9d87afd2655186c9" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/final/Gopkg.toml b/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/final/Gopkg.toml new file mode 100644 index 0000000000..6dbb73c194 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/final/Gopkg.toml @@ -0,0 +1,11 @@ +[[constraint]] + branch = "master" + name = "github.com/darkowlzz/deptest-project-status" + +[[constraint]] + name = "github.com/sdboyer/deptest" + version = "1.0.0" + +[[constraint]] + name = "github.com/sdboyer/deptestdos" + version = "2.0.0" diff --git a/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/final/main.go b/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/final/main.go new file mode 100644 index 0000000000..579d6d2a29 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/final/main.go @@ -0,0 +1,13 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + _ "github.com/darkowlzz/deptest-project-status/pkgfoo" + _ "github.com/sdboyer/deptest" + _ "github.com/sdboyer/deptestdos" +) + +func main() {} diff --git a/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/initial/Gopkg.lock b/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/initial/Gopkg.lock new file mode 100644 index 0000000000..326bc04f3c --- /dev/null +++ b/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/initial/Gopkg.lock @@ -0,0 +1,27 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "github.com/darkowlzz/deptest-project-status" + packages = ["pkgfoo"] + revision = "bddc1e2c8e4f0f264223389adf345d3858823677" + +[[projects]] + name = "github.com/sdboyer/deptest" + packages = ["."] + revision = "ff2948a2ac8f538c4ecd55962e919d1e13e74baf" + version = "v1.0.0" + +[[projects]] + name = "github.com/sdboyer/deptestdos" + packages = ["."] + revision = "5c607206be5decd28e6263ffffdcee067266015e" + version = "v2.0.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "36a300774881748908b8209ab36dbfda73e8c51145d6bdbc9d87afd2655186c9" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/initial/Gopkg.toml b/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/initial/Gopkg.toml new file mode 100644 index 0000000000..6dbb73c194 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/initial/Gopkg.toml @@ -0,0 +1,11 @@ +[[constraint]] + branch = "master" + name = "github.com/darkowlzz/deptest-project-status" + +[[constraint]] + name = "github.com/sdboyer/deptest" + version = "1.0.0" + +[[constraint]] + name = "github.com/sdboyer/deptestdos" + version = "2.0.0" diff --git a/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/initial/main.go b/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/initial/main.go new file mode 100644 index 0000000000..579d6d2a29 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/initial/main.go @@ -0,0 +1,13 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + _ "github.com/darkowlzz/deptest-project-status/pkgfoo" + _ "github.com/sdboyer/deptest" + _ "github.com/sdboyer/deptestdos" +) + +func main() {} diff --git a/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/testcase.json b/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/testcase.json new file mode 100644 index 0000000000..2bbbc78cdb --- /dev/null +++ b/cmd/dep/testdata/harness_tests/status/project_status_not_in_lock/testcase.json @@ -0,0 +1,12 @@ +{ + "commands": [ + ["ensure"], + ["status", "github.com/golang/dep"] + ], + "error-expected": "github.com/golang/dep is not in the lock file. Ensure that the project is being used and run `dep ensure` to generate a new lock file.", + "vendor-final": [ + "github.com/darkowlzz/deptest-project-status", + "github.com/sdboyer/deptest", + "github.com/sdboyer/deptestdos" + ] +} From 4b8290ece9999776e9de73eba8e6edcbc6dc5ce7 Mon Sep 17 00:00:00 2001 From: Sunny Date: Wed, 29 Nov 2017 16:23:12 +0530 Subject: [PATCH 14/14] status: make project status compatible with collectConstraints --- cmd/dep/status.go | 58 ++++++++++++++++--- cmd/dep/status_test.go | 21 ++++--- .../status/project_status/stdout.txt | 2 +- 3 files changed, 64 insertions(+), 17 deletions(-) diff --git a/cmd/dep/status.go b/cmd/dep/status.go index 9a2281d4a1..8904cf0128 100644 --- a/cmd/dep/status.go +++ b/cmd/dep/status.go @@ -23,6 +23,7 @@ import ( "github.com/golang/dep" "github.com/golang/dep/gps" "github.com/golang/dep/gps/paths" + "github.com/golang/dep/gps/pkgtree" "github.com/pkg/errors" ) @@ -747,6 +748,10 @@ type projectConstraint struct { Constraint gps.Constraint } +func (pc projectConstraint) String() string { + return fmt.Sprintf("%s(%s)", pc.Constraint.String(), string(pc.Project)) +} + // constraintsCollection is a map of ProjectRoot(dependency) and a collection of // projectConstraint for the dependencies. This can be used to find constraints // on a dependency and the projects that apply those constraints. @@ -881,6 +886,7 @@ func (pv pubVersions) TabString() string { return buf.String() } +// projectImporters stores a map of project names that import a specific project. type projectImporters map[string]bool func (pi projectImporters) String() string { @@ -896,6 +902,7 @@ func (pi projectImporters) String() string { return strings.Join(projects, ", ") } +// packageImporters stores a map of package and projects that import them. type packageImporters map[string][]string func (pi packageImporters) TabString() string { @@ -940,10 +947,37 @@ func (pi packageImporters) TabString() string { return buf.String() } +// projectConstraints is a slice of projectConstraint +type projectConstraints []projectConstraint + +func (pcs projectConstraints) TabString() string { + var buf bytes.Buffer + w := bufio.NewWriter(&buf) + + // Sort for consistent result. + sort.Sort(byProject(pcs)) + + // Count lines and add newlines("\n") and tabs("\t"), compatible with + // tabwriter. + // ^0.5.0(btb.com/x/y)\n \t^1.0.0(gh.com/f/b)\t \t^1.5.0(gh.com/a/c) + count := 0 + for _, c := range pcs { + count++ + if count > 1 { + fmt.Fprintf(w, "\n \t") + } + + fmt.Fprintf(w, "%s", c) + } + w.Flush() + + return buf.String() +} + type projectStatus struct { Project string Version string - Constraints []string + Constraints projectConstraints Source string AltSource string PubVersions pubVersions @@ -987,7 +1021,7 @@ func (ps projectStatus) String() string { "PACKAGE IMPORTERS:\t%s\n"+ "UPSTREAM EXISTS:\t%s\n"+ "UPSTREAM VERSION EXISTS:\t%s", - ps.Project, ps.Version, ps.Constraints, ps.Source, ps.AltSource, + ps.Project, ps.Version, ps.Constraints.TabString(), ps.Source, ps.AltSource, ps.PubVersions.TabString(), ps.Revision, ps.LatestAllowed, ps.SourceType, strings.Join(ps.Packages, ", "), ps.ProjectImporters, ps.PackageImporters.TabString(), upstreamExists, upstreamVersionExists, @@ -1019,13 +1053,19 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source prs = append(prs, pr) } - // This ptree would not be required with the new collectConstraints() implementation. - ptree, err := p.ParseRootPackageTree() - if err != nil { - return err - } // Collect all the constraints. - cc := collectConstraints(ptree, p, sm) + cc, ccerrs := collectConstraints(ctx, p, sm) + // If found any errors, print to stderr. + if len(ccerrs) > 0 { + if ctx.Verbose { + for _, e := range ccerrs { + ctx.Err.Println(e) + } + } else { + ctx.Out.Println("Got errors while collecting constraints. Rerun with `-v` flag to see details.") + } + return errors.New("errors while collecting constraints") + } // Collect list of packages in target projects. pkgs := make(map[gps.ProjectRoot][]string) @@ -1173,7 +1213,7 @@ func runProjectStatus(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.Source // CONSTRAINTS constraints := cc[string(pr)] for _, c := range constraints { - projStatus.Constraints = append(projStatus.Constraints, c.String()) + projStatus.Constraints = append(projStatus.Constraints, c) } } } diff --git a/cmd/dep/status_test.go b/cmd/dep/status_test.go index 1a9d611e90..91a35a21f0 100644 --- a/cmd/dep/status_test.go +++ b/cmd/dep/status_test.go @@ -482,6 +482,9 @@ func TestValidateFlags(t *testing.T) { } func TestProjectStatusString(t *testing.T) { + ver1, _ := gps.NewSemverConstraintIC("v1.0.0") + ver05, _ := gps.NewSemverConstraintIC("v0.5.0") + testCases := []struct { name string ps projectStatus @@ -490,11 +493,14 @@ func TestProjectStatusString(t *testing.T) { { name: "basic projectStatus", ps: projectStatus{ - Project: "github.com/x/y", - Version: "v1.0", - Constraints: nil, - Source: "github.com/x/y", - AltSource: "https://github.com/z/y", + Project: "github.com/x/y", + Version: "v1.0", + Constraints: projectConstraints{ + {"gh.com/f/b", ver1}, + {"btb.com/x/y", ver05}, + }, + Source: "github.com/x/y", + AltSource: "https://github.com/z/y", PubVersions: pubVersions{ "semvers": []string{"v0.5", "v0.7", "v1.0", "v1.5"}, "branches": []string{"master", "dev"}, @@ -518,7 +524,8 @@ func TestProjectStatusString(t *testing.T) { wantString: ` PROJECT: github.com/x/y VERSION: v1.0 -CONSTRAINTS: [] +CONSTRAINTS: ^0.5.0(btb.com/x/y) + ^1.0.0(gh.com/f/b) SOURCE: github.com/x/y ALT SOURCE: https://github.com/z/y PUB VERSION: branches: dev, master @@ -546,7 +553,7 @@ UPSTREAM VERSION EXISTS: yes`, wantString: ` PROJECT: VERSION: -CONSTRAINTS: [] +CONSTRAINTS: SOURCE: ALT SOURCE: PUB VERSION: diff --git a/cmd/dep/testdata/harness_tests/status/project_status/stdout.txt b/cmd/dep/testdata/harness_tests/status/project_status/stdout.txt index 7dfe5a3ed8..34942c7449 100644 --- a/cmd/dep/testdata/harness_tests/status/project_status/stdout.txt +++ b/cmd/dep/testdata/harness_tests/status/project_status/stdout.txt @@ -1,7 +1,7 @@ PROJECT: github.com/sdboyer/deptest VERSION: v1.0.0 -CONSTRAINTS: [] +CONSTRAINTS: SOURCE: github.com/sdboyer/deptest ALT SOURCE: PUB VERSION: branches: master