-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Module aware generator #1436
Module aware generator #1436
Changes from all commits
d225ba8
0243fe0
0463683
25ac916
d7b9dc5
b4ab663
31a1f21
b33fae1
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 |
---|---|---|
@@ -1,4 +1,4 @@ | ||
// Copyright © 2015 Steve Francia <spf@spf13.com>. | ||
// Copyright © 2021 Steve Francia <spf@spf13.com>. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
|
@@ -14,42 +14,41 @@ | |
package cmd | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"path" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/spf13/cobra" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
var ( | ||
pkgName string | ||
|
||
initCmd = &cobra.Command{ | ||
Use: "init [name]", | ||
Use: "init [path]", | ||
Aliases: []string{"initialize", "initialise", "create"}, | ||
Short: "Initialize a Cobra Application", | ||
Long: `Initialize (cobra init) will create a new application, with a license | ||
and the appropriate structure for a Cobra-based CLI application. | ||
|
||
* If a name is provided, a directory with that name will be created in the current directory; | ||
* If no name is provided, the current directory will be assumed; | ||
Cobra init must be run inside of a go module (please run "go mod init <MODNAME>" first) | ||
`, | ||
|
||
Run: func(_ *cobra.Command, args []string) { | ||
|
||
projectPath, err := initializeProject(args) | ||
cobra.CheckErr(err) | ||
cobra.CheckErr(goGet("github.com/spf13/cobra")) | ||
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 not sure how I feel about this utility fetching dependencies for the user. @wfernandes @jpmcb thoughts here? 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 guess the point is having them explicitly added to the 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. Yes, that is the intent. Russ Cox suggested we do this extra step and I agree. It streamlines the experience for the user as well as simplifying the instructions. 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. One of the motivations for this update is that we are writing a tutorial on building CLIs with Go for golang.org / go.dev which uses Cobra. As we wrote the tutorial it became clear that there were lots of places in the onboarding experience that could be streamlined. This set of changes comes from those learnings. 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. 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. See my note on #1240. I think we're likely to resolve this in the next few months by removing the need for cobra generator entirely. 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 inclined to agree on this point but am always wary of tools that try to do too many things. 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. We can move forward with this change though. 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. If this is just in the cobra CLI tool, sounds good to me. I'd be more cautious of anything in the API that would automatically fetch dependencies. But it makes sense to try and streamline the onboarding experience and get people up and going with |
||
if viper.GetBool("useViper") { | ||
cobra.CheckErr(goGet("github.com/spf13/viper")) | ||
} | ||
fmt.Printf("Your Cobra application is ready at\n%s\n", projectPath) | ||
}, | ||
} | ||
) | ||
|
||
func init() { | ||
initCmd.Flags().StringVar(&pkgName, "pkg-name", "", "fully qualified pkg name") | ||
jharshman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
cobra.CheckErr(initCmd.MarkFlagRequired("pkg-name")) | ||
} | ||
|
||
func initializeProject(args []string) (string, error) { | ||
wd, err := os.Getwd() | ||
if err != nil { | ||
|
@@ -62,13 +61,15 @@ func initializeProject(args []string) (string, error) { | |
} | ||
} | ||
|
||
modName := getModImportPath() | ||
|
||
project := &Project{ | ||
AbsolutePath: wd, | ||
PkgName: pkgName, | ||
PkgName: modName, | ||
Legal: getLicense(), | ||
Copyright: copyrightLine(), | ||
Viper: viper.GetBool("useViper"), | ||
AppName: path.Base(pkgName), | ||
AppName: path.Base(modName), | ||
} | ||
|
||
if err := project.Create(); err != nil { | ||
|
@@ -77,3 +78,51 @@ func initializeProject(args []string) (string, error) { | |
|
||
return project.AbsolutePath, nil | ||
} | ||
|
||
func getModImportPath() string { | ||
mod, cd := parseModInfo() | ||
return path.Join(mod.Path, fileToURL(strings.TrimPrefix(cd.Dir, mod.Dir))) | ||
} | ||
|
||
func fileToURL(in string) string { | ||
spf13 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
i := strings.Split(in, string(filepath.Separator)) | ||
return path.Join(i...) | ||
} | ||
|
||
func parseModInfo() (Mod, CurDir) { | ||
var mod Mod | ||
var dir CurDir | ||
|
||
m := modInfoJSON("-m") | ||
cobra.CheckErr(json.Unmarshal(m, &mod)) | ||
|
||
// Unsure why, but if no module is present Path is set to this string. | ||
if mod.Path == "command-line-arguments" { | ||
cobra.CheckErr("Please run `go mod init <MODNAME>` before `cobra init`") | ||
} | ||
|
||
e := modInfoJSON("-e") | ||
cobra.CheckErr(json.Unmarshal(e, &dir)) | ||
|
||
return mod, dir | ||
} | ||
|
||
type Mod struct { | ||
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. nit: can you put each of these variables on their own line? 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 can. What is the motivation for doing so? (mostly just curious). 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. For me it's readability. I find it easier to read the structure fields when they are not condensed on one line. |
||
Path, Dir, GoMod string | ||
} | ||
|
||
type CurDir struct { | ||
Dir string | ||
} | ||
|
||
func goGet(mod string) error { | ||
return exec.Command("go", "get", mod).Run() | ||
} | ||
|
||
func modInfoJSON(args ...string) []byte { | ||
cmdArgs := append([]string{"list", "-json"}, args...) | ||
out, err := exec.Command("go", cmdArgs...).Output() | ||
cobra.CheckErr(err) | ||
|
||
return out | ||
} |
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.
In CI, we are currently using GOBIN (see https://github.com/spf13/cobra/blob/master/.github/workflows/Test.yml). Might be worth adding a reference to https://golang.org/cmd/go/#hdr-Compile_and_install_packages_and_dependencies here?
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.
good idea.