Skip to content
This repository has been archived by the owner on Sep 9, 2020. It is now read-only.

Dep status -f doc and string display #1549

Merged
merged 4 commits into from
Mar 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 54 additions & 2 deletions cmd/dep/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,23 @@ import (
"encoding/json"
"flag"
"fmt"
"html/template"
"io"
"io/ioutil"
"log"
"sort"
"strings"
"sync"
"text/tabwriter"
"text/template"

"github.com/golang/dep"
"github.com/golang/dep/gps"
"github.com/golang/dep/gps/paths"
"github.com/pkg/errors"
)

const availableTemplateVariables = "ProjectRoot, Constraint, Version, Revision, Latest, and PackageCount."

const statusShortHelp = `Report the status of the project's dependencies`
const statusLongHelp = `
With no arguments, print the status of each dependency of the project.
Expand All @@ -34,9 +37,43 @@ With no arguments, print the status of each dependency of the project.
LATEST Latest VCS revision available
PKGS USED Number of packages from this project that are actually used

You may use the -f flag to create a custom format for the output of the
dep status command. The available fields you can utilize are as follows:
` + availableTemplateVariables + `

Status returns exit code zero if all dependencies are in a "good state".
`

const statusExamples = `
dep status

Displays a table of the various dependencies in the project along with
their properties such as the constraints they are bound by and the
revision they are at.

dep status -f='{{if eq .Constraint "master"}}{{.ProjectRoot}} {{end}}'

Display the list of package names constrained on the master branch.
The -f flag allows you to use Go templates along with it's various
constructs for formating the output data. Available flags are as follows:
` + availableTemplateVariables + `

dep status -json

Displays the dependency information in JSON format as a list of
project objects. Each project object contains keys which correspond
to the table column names from the standard 'dep status' command.

Linux: dep status -dot | dot -T png | display
MacOS: dep status -dot | dot -T png | open -f -a /Applications/Preview.app
Windows: dep status -dot | dot -T png -o status.png; start status.png

Generate a visual representation of the dependency tree using GraphViz.
(Note: in order for this example to work you must first have graphviz
installed on your system)

`

const (
shortRev uint8 = iota
longRev
Expand All @@ -56,6 +93,7 @@ func (cmd *statusCommand) LongHelp() string { return statusLongHelp }
func (cmd *statusCommand) Hidden() bool { return false }

func (cmd *statusCommand) Register(fs *flag.FlagSet) {
fs.BoolVar(&cmd.examples, "examples", false, "print detailed usage examples")
fs.BoolVar(&cmd.json, "json", false, "output in JSON format")
fs.StringVar(&cmd.template, "f", "", "output in text/template format")
fs.BoolVar(&cmd.dot, "dot", false, "output the dependency graph in GraphViz format")
Expand All @@ -64,6 +102,7 @@ func (cmd *statusCommand) Register(fs *flag.FlagSet) {
}

type statusCommand struct {
examples bool
json bool
template string
output string
Expand Down Expand Up @@ -200,7 +239,15 @@ func (out *templateOutput) BasicHeader() error { return nil }
func (out *templateOutput) BasicFooter() error { return nil }

func (out *templateOutput) BasicLine(bs *BasicStatus) error {
return out.tmpl.Execute(out.w, bs)
data := rawStatus{
ProjectRoot: bs.ProjectRoot,
Constraint: bs.getConsolidatedConstraint(),
Version: bs.getConsolidatedVersion(),
Revision: bs.Revision.String(),
Latest: bs.getConsolidatedLatest(shortRev),
PackageCount: bs.PackageCount,
}
return out.tmpl.Execute(out.w, data)
}

func (out *templateOutput) MissingHeader() error { return nil }
Expand All @@ -211,6 +258,11 @@ func (out *templateOutput) MissingLine(ms *MissingStatus) error {
}

func (cmd *statusCommand) Run(ctx *dep.Ctx, args []string) error {
if cmd.examples {
ctx.Err.Println(strings.TrimSpace(statusExamples))
return nil
}

if err := cmd.validateFlags(); err != nil {
return err
}
Expand Down
83 changes: 63 additions & 20 deletions cmd/dep/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"strings"
"testing"
"text/tabwriter"
"text/template"

"github.com/golang/dep"
"github.com/golang/dep/gps"
Expand Down Expand Up @@ -41,31 +42,40 @@ func TestBasicLine(t *testing.T) {
project := dep.Project{}
aSemverConstraint, _ := gps.NewSemverConstraint("1.2.3")

templateString := "PR:{{.ProjectRoot}}, Const:{{.Constraint}}, Ver:{{.Version}}, Rev:{{.Revision}}, Lat:{{.Latest}}, PkgCt:{{.PackageCount}}"
equalityTestTemplate := `{{if eq .Constraint "1.2.3"}}Constraint is 1.2.3{{end}}|{{if eq .Version "flooboo"}}Version is flooboo{{end}}|{{if eq .Latest "unknown"}}Latest is unknown{{end}}`

tests := []struct {
name string
status BasicStatus
wantDotStatus []string
wantJSONStatus []string
wantTableStatus []string
name string
status BasicStatus
wantDotStatus []string
wantJSONStatus []string
wantTableStatus []string
wantTemplateStatus []string
wantEqTemplateStatus []string
}{
{
name: "BasicStatus with ProjectRoot only",
status: BasicStatus{
ProjectRoot: "github.com/foo/bar",
},
wantDotStatus: []string{`[label="github.com/foo/bar"];`},
wantJSONStatus: []string{`"Version":""`, `"Revision":""`},
wantTableStatus: []string{`github.com/foo/bar 0`},
wantDotStatus: []string{`[label="github.com/foo/bar"];`},
wantJSONStatus: []string{`"Version":""`, `"Revision":""`},
wantTableStatus: []string{`github.com/foo/bar 0`},
wantTemplateStatus: []string{`PR:github.com/foo/bar, Const:, Ver:, Rev:, Lat:, PkgCt:0`},
wantEqTemplateStatus: []string{`||`},
},
{
name: "BasicStatus with Revision",
status: BasicStatus{
ProjectRoot: "github.com/foo/bar",
Revision: gps.Revision("flooboofoobooo"),
},
wantDotStatus: []string{`[label="github.com/foo/bar\nflooboo"];`},
wantJSONStatus: []string{`"Version":""`, `"Revision":"flooboofoobooo"`, `"Constraint":""`},
wantTableStatus: []string{`github.com/foo/bar flooboo 0`},
wantDotStatus: []string{`[label="github.com/foo/bar\nflooboo"];`},
wantJSONStatus: []string{`"Version":""`, `"Revision":"flooboofoobooo"`, `"Constraint":""`},
wantTableStatus: []string{`github.com/foo/bar flooboo 0`},
wantTemplateStatus: []string{`PR:github.com/foo/bar, Const:, Ver:flooboo, Rev:flooboofoobooo, Lat:, PkgCt:0`},
wantEqTemplateStatus: []string{`|Version is flooboo|`},
},
{
name: "BasicStatus with Version and Revision",
Expand All @@ -74,9 +84,11 @@ func TestBasicLine(t *testing.T) {
Version: gps.NewVersion("1.0.0"),
Revision: gps.Revision("flooboofoobooo"),
},
wantDotStatus: []string{`[label="github.com/foo/bar\n1.0.0"];`},
wantJSONStatus: []string{`"Version":"1.0.0"`, `"Revision":"flooboofoobooo"`, `"Constraint":""`},
wantTableStatus: []string{`github.com/foo/bar 1.0.0 flooboo 0`},
wantDotStatus: []string{`[label="github.com/foo/bar\n1.0.0"];`},
wantJSONStatus: []string{`"Version":"1.0.0"`, `"Revision":"flooboofoobooo"`, `"Constraint":""`},
wantTableStatus: []string{`github.com/foo/bar 1.0.0 flooboo 0`},
wantTemplateStatus: []string{`PR:github.com/foo/bar, Const:, Ver:1.0.0, Rev:flooboofoobooo, Lat:, PkgCt:0`},
wantEqTemplateStatus: []string{`||`},
},
{
name: "BasicStatus with Constraint, Version and Revision",
Expand All @@ -86,19 +98,23 @@ func TestBasicLine(t *testing.T) {
Version: gps.NewVersion("1.0.0"),
Revision: gps.Revision("revxyz"),
},
wantDotStatus: []string{`[label="github.com/foo/bar\n1.0.0"];`},
wantJSONStatus: []string{`"Revision":"revxyz"`, `"Constraint":"1.2.3"`, `"Version":"1.0.0"`},
wantTableStatus: []string{`github.com/foo/bar 1.2.3 1.0.0 revxyz 0`},
wantDotStatus: []string{`[label="github.com/foo/bar\n1.0.0"];`},
wantJSONStatus: []string{`"Revision":"revxyz"`, `"Constraint":"1.2.3"`, `"Version":"1.0.0"`},
wantTableStatus: []string{`github.com/foo/bar 1.2.3 1.0.0 revxyz 0`},
wantTemplateStatus: []string{`PR:github.com/foo/bar, Const:1.2.3, Ver:1.0.0, Rev:revxyz, Lat:, PkgCt:0`},
wantEqTemplateStatus: []string{`Constraint is 1.2.3||`},
},
{
name: "BasicStatus with update error",
status: BasicStatus{
ProjectRoot: "github.com/foo/bar",
hasError: true,
},
wantDotStatus: []string{`[label="github.com/foo/bar"];`},
wantJSONStatus: []string{`"Version":""`, `"Revision":""`, `"Latest":"unknown"`},
wantTableStatus: []string{`github.com/foo/bar unknown 0`},
wantDotStatus: []string{`[label="github.com/foo/bar"];`},
wantJSONStatus: []string{`"Version":""`, `"Revision":""`, `"Latest":"unknown"`},
wantTableStatus: []string{`github.com/foo/bar unknown 0`},
wantTemplateStatus: []string{`PR:github.com/foo/bar, Const:, Ver:, Rev:, Lat:unknown, PkgCt:0`},
wantEqTemplateStatus: []string{`||Latest is unknown`},
},
}

Expand Down Expand Up @@ -149,6 +165,33 @@ func TestBasicLine(t *testing.T) {
t.Errorf("Did not find expected Table status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus)
}
}

buf.Reset()
template, _ := template.New("status").Parse(templateString)
templateout := &templateOutput{w: &buf, tmpl: template}
templateout.BasicHeader()
templateout.BasicLine(&test.status)
templateout.BasicFooter()

for _, wantStatus := range test.wantTemplateStatus {
if ok := strings.Contains(buf.String(), wantStatus); !ok {
t.Errorf("Did not find expected template status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is good, but it doesn't actually ensure that these types remain of type string, or more importantly that templates using the eq function continue to work which is sorta what this whole patch is about.

I'd recommend having another test (or another chunk in this test) that attempts equality on every field. This may not even need an expected output, but it should at least ensure that the template can be executed without errors.


// The following test is to ensure that certain fields usable with string operations such as .eq
buf.Reset()
template, _ = template.New("status").Parse(equalityTestTemplate)
templateout = &templateOutput{w: &buf, tmpl: template}
templateout.BasicHeader()
templateout.BasicLine(&test.status)
templateout.BasicFooter()

for _, wantStatus := range test.wantEqTemplateStatus {
if ok := strings.Contains(buf.String(), wantStatus); !ok {
t.Errorf("Did not find expected template status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus)
}
}
})
}
}
Expand Down