diff --git a/glide.lock b/glide.lock index 15af3ea3..1e5dd6a6 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 7ba018ca22cd50a2e909cb7b3e091aa378b2ae048e69d0d999af81cb0583c2a3 -updated: 2018-06-14T13:49:16.756220234-04:00 +hash: e103ef81d6b447b46f5afb30e2d4bcf841a26eb52a11b8743f9dc8f35d16e855 +updated: 2018-07-16T10:45:39.277224709+02:00 imports: - name: github.com/beorn7/perks version: 3a771d992973f24aa725d07868b467d1ddfceafb @@ -58,6 +58,8 @@ imports: version: c12348ce28de40eed0136aa2b644d0ee0650e56c subpackages: - pbutil +- name: github.com/mitchellh/go-wordwrap + version: ad45545899c7b13c020ea92b2072220eefad42b8 - name: github.com/opentracing/opentracing-go version: 1949ddbfd147afd4d964a9f00b24eb291e0e7c38 subpackages: @@ -99,7 +101,7 @@ imports: - name: github.com/uber-go/tally version: 79f2a33b0e55b1255ffbbaf824dcafb09ff34dda - name: github.com/uber/tchannel-go - version: cc0c40929b0dbdb755b727a8c253de2b5d4af46b + version: 7f5c12c66261f3ac3c5dde959d65eed59e3b63af subpackages: - internal/argreader - relay @@ -110,7 +112,7 @@ imports: - name: go.uber.org/atomic version: 1ea20fb1cbb1cc08cbd0d913a96dead89aa18289 - name: go.uber.org/dig - version: 45540ff1846c207f109bbb7de2f2748f7198ca21 + version: d100de9c8cc8358591507951141bc107713ba671 subpackages: - internal/digreflect - name: go.uber.org/fx @@ -128,7 +130,7 @@ imports: - push - tallypush - name: go.uber.org/thriftrw - version: 43dfafd7bb1c99f0460d645a1959b73f6dfa536f + version: fb439a9a7f5a388d8606aae1c1ff6654b8b67fbe subpackages: - version - name: go.uber.org/yarpc @@ -162,7 +164,7 @@ imports: - internal/exit - zapcore - name: golang.org/x/net - version: db08ff08e8622530d9ed3a0e8ac279f6d4c02196 + version: afe8f62b1d6bbd81f31868121a50b06d8188e1f9 subpackages: - bpf - context @@ -193,13 +195,12 @@ imports: - protobuf/ptype - protobuf/source_context - name: google.golang.org/grpc - version: 24f3cca1ffddeab04708c325f8088c76c2d74972 + version: ba63e52faf1676ef8bb26b6e0ba5acf78ff3b8e8 repo: https://github.com/grpc/grpc-go subpackages: - balancer - balancer/base - balancer/roundrobin - - channelz - codes - connectivity - credentials @@ -208,6 +209,7 @@ imports: - grpclog - internal - internal/backoff + - internal/channelz - internal/grpcrand - keepalive - metadata diff --git a/glide.yaml b/glide.yaml index ffc56e5b..3ec0c05f 100644 --- a/glide.yaml +++ b/glide.yaml @@ -6,6 +6,7 @@ import: - package: github.com/gogo/protobuf/protoc-gen-gogoslick - package: github.com/golang/protobuf/protoc-gen-go - package: github.com/jhump/protoreflect/dynamic + - package: github.com/mitchellh/go-wordwrap - package: github.com/spf13/cobra - package: github.com/spf13/pflag - package: go.uber.org/atomic diff --git a/internal/cmd/templates.go b/internal/cmd/templates.go index 50cb3143..99508ca0 100644 --- a/internal/cmd/templates.go +++ b/internal/cmd/templates.go @@ -24,7 +24,9 @@ import ( "fmt" "io" "os" + "strings" + wordwrap "github.com/mitchellh/go-wordwrap" "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/uber/prototool/internal/exec" @@ -32,6 +34,8 @@ import ( "go.uber.org/zap/zapcore" ) +const wordWrapLength uint = 80 + var ( allCmdTemplate = &cmdTemplate{ Use: "all dirOrProtoFiles...", @@ -71,6 +75,7 @@ var ( compileCmdTemplate = &cmdTemplate{ Use: "compile dirOrProtoFiles...", Short: "Compile with protoc to check for failures.", + Long: `Stubs will not be generated. To generate stubs, use the "gen" command. Calling "compile" has the effect of calling protoc with "-o /dev/null".`, Run: func(runner exec.Runner, args []string, flags *flags) error { return runner.Compile(args, flags.dryRun) }, @@ -83,6 +88,62 @@ var ( createCmdTemplate = &cmdTemplate{ Use: "create files...", Short: "Create the given Protobuf files according to a template that passes default prototool lint.", + Long: `Assuming the filename "example_create_file.proto", the file will look like the following: + + syntax = "proto3"; + + package SOME.PKG; + + option go_package = "PKGpb"; + option java_multiple_files = true; + option java_outer_classname = "ExampleCreateFileProto"; + option java_package = "com.SOME.PKG.pb"; + +This matches what the linter expects. "SOME.PKG" will be computed as follows: + +- If "--package" is specified, "SOME.PKG" will be the value passed to + "--package". +- Otherwise, if there is no "prototool.yaml" that would apply to the new file, + use "uber.prototool.generated". +- Otherwise, if there is a "prototool.yaml" file, check if it has a + "dir_to_base_package" setting under the "create" section. If it does, this + package, concatenated with the relative path from the directory with the + "prototool.yaml" will be used. +- Otherwise, if there is no "dir_to_base_package" directive, just use the + relative path from the directory with the "prototool.yaml" file. If the file + is in the same directory as the "prototoo.yaml" file, use + "uber.prototool.generated". + +For example, assume you have the following file at "repo/prototool.yaml": + +create: + dir_to_base_package: + idl: uber + idl/baz: special + +- "prototool create repo/idl/foo/bar/bar.proto" will have the package + "uber.foo.bar". +- "prototool create repo/idl/bar.proto" will have the package "uber". +- "prototool create repo/idl/baz/baz.proto" will have the package "special". +- "prototool create repo/idl/baz/bat/bat.proto" will have the package + "special.bat". +- "prototool create repo/another/dir/bar.proto" will have the package + "another.dir". +- "prototool create repo/bar.proto" will have the package + "uber.prototool.generated". + +This is meant to mimic what you generally want - a base package for your idl directory, followed by packages matching the directory structure. + +Note you can override the directory that the "prototool.yaml" file is in as well. If we update our file at "repo/prototool.yaml" to this: + +create: + dir_to_base_package: + .: foo.bar + +Then "prototool create repo/bar.proto" will have the package "foo.bar", and "prototool create repo/another/dir/bar.proto" will have the package "foo.bar.another.dir". + +If Vim integration is set up, files will be generated when you open a new Protobuf file.`, + Run: func(runner exec.Runner, args []string, flags *flags) error { return runner.Create(args, flags.pkg) }, @@ -135,6 +196,7 @@ var ( formatCmdTemplate = &cmdTemplate{ Use: "format dirOrProtoFiles...", Short: "Format a proto file and compile with protoc to check for failures.", + Long: `By default, the values for "java_multiple_files", "java_outer_classname", and "java_package" are updated to reflect what is expected by the Google Cloud APIs file structure at https://cloud.google.com/apis/design/file_structure, and the value of "go_package" is updated to reflect what we expect for the default Style Guide. By formatting, the linting for these values will pass by default. See the documentation for "create" for an example. This functionality can be suppressed by passing the flag "--no-rewrite".`, Run: func(runner exec.Runner, args []string, flags *flags) error { return runner.Format(args, flags.overwrite, flags.diffMode, flags.lintMode, !flags.noRewrite) }, @@ -161,6 +223,77 @@ var ( grpcCmdTemplate = &cmdTemplate{ Use: "grpc dirOrProtoFiles...", Short: "Call a gRPC endpoint. Be sure to set required flags address, method, and either data or stdin.", + Long: `This command compiles your proto files with "protoc", converts JSON input to binary and converts the result from binary to JSON. All these steps take on the order of milliseconds. For example, the overhead for a file with four dependencies is about 30ms, so there is little overhead for CLI calls to gRPC. + +There is a full example for gRPC in the example directory of Prototool. Run "make init example" to make sure everything is installed and generated. + +Start the example server in a separate terminal by doing "go run example/cmd/excited/main.go". + +prototool grpc dirOrProtoFiles... \ + --address serverAddress \ + --method package.service/Method \ + --data 'requestData' + +Either use "--data 'requestData'" as the the JSON data to input, or "--stdin" which will result in the input being read from stdin as JSON. + +$ make init example # make sure everything is built just in case + +$ cat input.json +{"value":"hello"} + +$ cat input.json | prototool grpc example \ + --address 0.0.0.0:8080 \ + --method foo.ExcitedService/Exclamation \ + --stdin +{ + "value": "hello!" +} + +$ cat input.json | prototool grpc example \ + --address 0.0.0.0:8080 \ + --method foo.ExcitedService/ExclamationServerStream \ + --stdin +{ + "value": "h" +} +{ + "value": "e" +} +{ + "value": "l" +} +{ + "value": "l" +} +{ + "value": "o" +} +{ + "value": "!" +} + +$ cat input.json +{"value":"hello"} +{"value":"salutations"} + +$ cat input.json | prototool grpc example \ + --address 0.0.0.0:8080 \ + --method foo.ExcitedService/ExclamationClientStream \ + --stdin +{ + "value": "hellosalutations!" +} + +$ cat input.json | prototool grpc example \ + --address 0.0.0.0:8080 \ + --method foo.ExcitedService/ExclamationBidiStream \ + --stdin +{ + "value": "hello!" +} +{ + "value": "salutations!" +}`, Run: func(runner exec.Runner, args []string, flags *flags) error { return runner.GRPC(args, flags.headers, flags.address, flags.method, flags.data, flags.callTimeout, flags.connectTimeout, flags.keepaliveTime, flags.stdin) }, @@ -180,6 +313,7 @@ var ( initCmdTemplate = &cmdTemplate{ Use: "init [dirPath]", Short: "Generate an initial config file in the current or given directory.", + Long: `All available options will be generated and commented out except for "protoc_version". Pass the "--uncomment" flag to uncomment all options.`, Args: cobra.MaximumNArgs(1), Run: func(runner exec.Runner, args []string, flags *flags) error { return runner.Init(args, flags.uncomment) @@ -204,6 +338,7 @@ var ( lintCmdTemplate = &cmdTemplate{ Use: "lint dirOrProtoFiles...", Short: "Lint proto files and compile with protoc to check for failures.", + Long: `The default rule set follows the Style Guide at https://github.com/uber/prototool/blob/master/etc/style/uber/uber.proto. You can add or exclude lint rules in your "prototool.yaml" file. The default rule set is very strict and is meant to enforce consistent development patterns.`, Run: func(runner exec.Runner, args []string, flags *flags) error { return runner.Lint(args, flags.listAllLinters, flags.listLinters) }, @@ -294,9 +429,9 @@ type cmdTemplate struct { func (c *cmdTemplate) Build(exitCodeAddr *int, stdin io.Reader, stdout io.Writer, stderr io.Writer, flags *flags) *cobra.Command { command := &cobra.Command{} command.Use = c.Use - command.Short = c.Short + command.Short = strings.TrimSpace(c.Short) if c.Long != "" { - command.Long = fmt.Sprintf("%s\n%s", c.Short, c.Long) + command.Long = wordwrap.WrapString(fmt.Sprintf("%s\n\n%s", strings.TrimSpace(c.Short), strings.TrimSpace(c.Long)), wordWrapLength) } command.Args = c.Args command.Run = func(_ *cobra.Command, args []string) {