-
Notifications
You must be signed in to change notification settings - Fork 11
Added init command #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,245 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "io/ioutil" | ||
| "os" | ||
| "path" | ||
| "strings" | ||
|
|
||
| "github.com/jingweno/nut/vendor/_nuts/github.com/codegangsta/cli" | ||
| ) | ||
|
|
||
| // dependencyFinder is an interface for finding project dependencies. | ||
| // This can later be used to migrate from godep, etc | ||
| type dependencyFinder interface { | ||
| FindDependencies(vcsRefs bool) (map[string]string, error) | ||
| } | ||
|
|
||
| var initCmd = cli.Command{ | ||
| Name: "init", | ||
| Usage: "init nut on an existing go project, writing all dependencies to Nut.toml", | ||
| Action: runInit, | ||
| Flags: []cli.Flag{ | ||
| cli.StringSliceFlag{ | ||
| Name: "ignore, i", | ||
| Usage: "import path prefixes to ignore", | ||
| Value: &cli.StringSlice{}, | ||
| }, | ||
| cli.BoolFlag{ | ||
| Name: "verbose, v", | ||
| Usage: "verbose output", | ||
| }, | ||
| cli.BoolFlag{ | ||
| Name: "refs, r", | ||
| Usage: "extract VCS refs for packages", | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| // depFinder finds the dependencies of an existing project by traversing go deps | ||
| type depFinder struct { | ||
| // the seen dependencies during traversal | ||
| deps map[string]*pkg | ||
|
|
||
| // the root package. we ignore all its sub packages | ||
| root string | ||
|
|
||
| // a list of ignore import prefixes (e.g. gitlab.mydomain.com) | ||
| ignored []string | ||
|
|
||
| // the current working directory (this changes durgin traversal) | ||
| wd string | ||
|
|
||
| // output verbose messages during traversal | ||
| verbose bool | ||
|
|
||
| lister pkgLister | ||
| } | ||
|
|
||
| // isIgnored checks if an import path start with a prefix that has been set to be ignored | ||
| func (c *depFinder) isIgnored(path string) bool { | ||
|
|
||
| for _, prefix := range c.ignored { | ||
| if strings.HasPrefix(path, prefix) { | ||
| return true | ||
| } | ||
| } | ||
|
|
||
| return false | ||
| } | ||
|
|
||
| // findDeps recursively finds all the dependencies of a package that are not part of the same | ||
| // project or the standard library | ||
| func (c *depFinder) findDeps(p *pkg) error { | ||
|
|
||
| if c.root == "" { | ||
| c.root = p.ImportPath | ||
| } | ||
|
|
||
| deps, err := c.lister.List(p.Deps...) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| for _, dep := range deps { | ||
| if dep.Standard { | ||
| continue | ||
| } | ||
|
|
||
| // do not scan deps twice | ||
| if _, found := c.deps[dep.ImportPath]; found { | ||
| continue | ||
| } | ||
|
|
||
| if !strings.HasPrefix(dep.ImportPath, c.root) && !c.isIgnored(dep.ImportPath) { | ||
| if c.verbose { | ||
| fmt.Println(dep.ImportPath) | ||
| } | ||
| c.deps[dep.ImportPath] = dep | ||
|
|
||
| } else { | ||
| // we put ignored packages in the dep map as nils just so we'll scan them once for their deps | ||
| c.deps[dep.ImportPath] = nil | ||
| } | ||
|
|
||
| // we find deps even on "ignored" packages - we want their deps | ||
| c.findDeps(dep) | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // findDepsInDir finds all the deps for the packages under the dir in path. | ||
| // this is used to recursively traverse all packages in a project | ||
| func (c *depFinder) findDepsInDir(pth string) error { | ||
|
|
||
| c.wd = pth | ||
| os.Chdir(c.wd) | ||
|
|
||
| pkgs, err := c.lister.List("") | ||
| if err != nil { | ||
| return err | ||
| } | ||
| for _, p := range pkgs { | ||
| c.findDeps(p) | ||
| } | ||
|
|
||
| files, err := ioutil.ReadDir(pth) | ||
|
|
||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| for _, file := range files { | ||
| if !file.IsDir() { | ||
| continue | ||
| } | ||
|
|
||
| if strings.HasPrefix(file.Name(), ".") || strings.HasPrefix(file.Name(), "_") { | ||
| continue | ||
| } | ||
|
|
||
| err := c.findDepsInDir(path.Join(pth, file.Name())) | ||
| if err != nil { | ||
| fmt.Println("error traversing", file.Name(), ": ", err) | ||
| return err | ||
| } | ||
|
|
||
| } | ||
|
|
||
| return nil | ||
|
|
||
| } | ||
|
|
||
| // FindDependencies traverses the current WD and retrieves a dependency map (package => revision) | ||
| // for all 3rd party libraries. | ||
| // VCS refs are retrieved only if the vcsRefs flag is set to true | ||
| func (c *depFinder) FindDependencies(vcsRefs bool) (map[string]string, error) { | ||
| err := c.findDepsInDir(c.wd) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| ret := make(map[string]string) | ||
| if c.verbose && vcsRefs { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a bit confused on
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, where else would we get them from? This is a "virgin" repo that we assume had no vendoring solution before, so we can just "freeze" the current state of GOPATH's revisions. |
||
| fmt.Println("Finding VCS refs for packages") | ||
| } | ||
|
|
||
| for dep, pkg := range c.deps { | ||
|
|
||
| // nil pkg's are placeholders for not entering the same dir twice, we ignore them | ||
| if pkg == nil { | ||
| continue | ||
| } | ||
| tag := "" | ||
| if vcsRefs { | ||
| _, vc, err := VCSForImportPath(dep) | ||
|
|
||
| if err == nil { | ||
| tag, _ = vc.Identify(pkg.Dir) | ||
| } else { | ||
| fmt.Printf("Error extracting tag for %s: %s\n", dep, err) | ||
| } | ||
|
|
||
| } | ||
| ret[dep] = tag | ||
|
|
||
| } | ||
|
|
||
| return ret, nil | ||
| } | ||
|
|
||
| func runInit(c *cli.Context) { | ||
|
|
||
| //the first arg can be the path of a project | ||
| if len(c.Args()) > 0 { | ||
| pth := c.Args().First() | ||
| if pth != "" && pth != "." { | ||
| err := os.Chdir(pth) | ||
| if err != nil { | ||
| fmt.Printf("Invalid path %s (%s)\n", pth, err) | ||
| os.Exit(1) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| cwd, err := os.Getwd() | ||
| if err != nil { | ||
| fmt.Println(err) | ||
| os.Exit(1) | ||
| } | ||
|
|
||
| ctx := &depFinder{ | ||
| deps: make(map[string]*pkg), | ||
| ignored: c.StringSlice("ignored"), | ||
| wd: cwd, | ||
| verbose: c.Bool("verbose"), | ||
| lister: pkgLister{ | ||
| Env: os.Environ(), | ||
| }, | ||
| } | ||
|
|
||
| deps, err := ctx.FindDependencies(c.Bool("refs")) | ||
|
|
||
| // create a manifest from the gathered data | ||
| manifest := Manifest{ | ||
| App: ManifestApp{ | ||
| Name: path.Base(cwd), | ||
| Authors: []string{"Your Name <you@example.com>"}, | ||
| Version: "0.0.1", | ||
| }, | ||
| Deps: ManifestDeps(deps), | ||
| } | ||
|
|
||
| os.Chdir(cwd) | ||
|
|
||
| err = manifest.write() | ||
| if err != nil { | ||
| fmt.Println(err) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor, it would be nice to print error to STDERR.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. right. I'll change that. it's weird BTW that the cli package doesn't allow you to return an error from running the app. any idea why that is? |
||
| os.Exit(1) | ||
| } | ||
|
|
||
| fmt.Println("New manifest file written to", setting.ConfigFile) | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ func main() { | |
| app.Commands = []cli.Command{ | ||
| installCmd, | ||
| newCmd, | ||
| initCmd, | ||
| } | ||
|
|
||
| app.Run(os.Args) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could call
go list -e -json ./...to return all deps in current path instead of traversing folders. It requires some code changes in https://github.com/jingweno/nut/blob/master/pkg.go#L246 though.