Repository: apigee/registry
Pull request #918 (2023-01-10 17:47:55)

Add a test to verify that command metadata conforms to expectations.
[[ Reviewers FYI - I don't think this needs everyone's detailed review (though comments are welcome!), but I want to let you know that this adds automatic checks of certain style characteristics of the registry tool command info. Like golangci-lint, don't worry about knowing the rules, just notice when the test fails. ]]

This directly addresses #909.

The first commit in this PR adds a test that checks metadata on the Cobra commands that are declared in the `registry` tool. Initially all problems are logged, but in subsequent commits, the logs will be changed to errors and associated problems will be fixed. Here's the initial test output:
```
$ go test ./cmd/registry/cmd  -v
=== RUN   TestCommandMetadata
=== RUN   TestCommandMetadata/registry
=== RUN   TestCommandMetadata/registry_annotate
    root_test.go:73: "registry annotate" "filter" flag description must not be empty.
    root_test.go:73: "registry annotate" "jobs" flag description must not be empty.
    root_test.go:73: "registry annotate" "overwrite" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_apply
    root_test.go:73: "registry apply" "parent" flag description must not be empty.
    root_test.go:77: "registry apply" "recursive" flag description must not begin with an upper case letter.
=== RUN   TestCommandMetadata/registry_auth
    root_test.go:39: "registry auth" short description must not end with a period.
    root_test.go:46: "registry auth" short description must refer to `the API Registry` instead of `the registry`.
=== RUN   TestCommandMetadata/registry_auth_print-token
    root_test.go:39: "registry auth print-token" short description must not end with a period.
=== RUN   TestCommandMetadata/registry_check
    root_test.go:46: "registry check" short description must refer to `the API Registry` instead of `the registry`.
=== RUN   TestCommandMetadata/registry_compute
=== RUN   TestCommandMetadata/registry_compute_complexity
=== RUN   TestCommandMetadata/registry_compute_conformance
=== RUN   TestCommandMetadata/registry_compute_lint
    root_test.go:73: "registry compute lint" "linter" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_compute_lintstats
    root_test.go:73: "registry compute lintstats" "linter" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_compute_score
=== RUN   TestCommandMetadata/registry_compute_scorecard
=== RUN   TestCommandMetadata/registry_compute_vocabulary
=== RUN   TestCommandMetadata/registry_config
    root_test.go:39: "registry config" short description must not end with a period.
=== RUN   TestCommandMetadata/registry_config_configurations
=== RUN   TestCommandMetadata/registry_config_configurations_activate
    root_test.go:39: "registry config configurations activate" short description must not end with a period.
=== RUN   TestCommandMetadata/registry_config_configurations_create
    root_test.go:39: "registry config configurations create" short description must not end with a period.
=== RUN   TestCommandMetadata/registry_config_configurations_delete
    root_test.go:39: "registry config configurations delete" short description must not end with a period.
=== RUN   TestCommandMetadata/registry_config_configurations_describe
    root_test.go:39: "registry config configurations describe" short description must not end with a period.
=== RUN   TestCommandMetadata/registry_config_configurations_list
    root_test.go:39: "registry config configurations list" short description must not end with a period.
=== RUN   TestCommandMetadata/registry_config_get
    root_test.go:39: "registry config get" short description must not end with a period.
=== RUN   TestCommandMetadata/registry_config_list
    root_test.go:39: "registry config list" short description must not end with a period.
=== RUN   TestCommandMetadata/registry_config_set
    root_test.go:39: "registry config set" short description must not end with a period.
=== RUN   TestCommandMetadata/registry_config_unset
    root_test.go:39: "registry config unset" short description must not end with a period.
=== RUN   TestCommandMetadata/registry_count
=== RUN   TestCommandMetadata/registry_count_revisions
    root_test.go:73: "registry count revisions" "filter" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_count_versions
    root_test.go:73: "registry count versions" "filter" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_delete
    root_test.go:73: "registry delete" "filter" flag description must not be empty.
    root_test.go:73: "registry delete" "jobs" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_diff
    root_test.go:46: "registry diff" short description must refer to `the API Registry` instead of `the registry`.
=== RUN   TestCommandMetadata/registry_export
=== RUN   TestCommandMetadata/registry_export_csv
    root_test.go:73: "registry export csv" "filter" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_export_yaml
    root_test.go:46: "registry export yaml" short description must refer to `the API Registry` instead of `the registry`.
=== RUN   TestCommandMetadata/registry_get
    root_test.go:73: "registry get" "filter" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_label
    root_test.go:73: "registry label" "filter" flag description must not be empty.
    root_test.go:73: "registry label" "jobs" flag description must not be empty.
    root_test.go:73: "registry label" "overwrite" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_resolve
    root_test.go:43: "registry resolve" short description must not begin with a lower case letter.
    root_test.go:46: "registry resolve" short description must refer to `the API Registry` instead of `the registry`.
    root_test.go:73: "registry resolve" "dry-run" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_rpc
=== RUN   TestCommandMetadata/registry_upload
=== RUN   TestCommandMetadata/registry_upload_bulk
=== RUN   TestCommandMetadata/registry_upload_bulk_discovery
    root_test.go:53: "registry upload bulk discovery" has an empty 'Args' field.
    root_test.go:73: "registry upload bulk discovery" "service" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_upload_bulk_openapi
    root_test.go:73: "registry upload bulk openapi" "base-uri" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_upload_bulk_protos
    root_test.go:73: "registry upload bulk protos" "base-uri" flag description must not be empty.
    root_test.go:73: "registry upload bulk protos" "protoc-root" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_upload_csv
    root_test.go:73: "registry upload csv" "delimiter" flag description must not be empty.
    root_test.go:73: "registry upload csv" "jobs" flag description must not be empty.
    root_test.go:73: "registry upload csv" "project-id" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_upload_spec
    root_test.go:73: "registry upload spec" "style" flag description must not be empty.
    root_test.go:73: "registry upload spec" "version" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_vocabulary
=== RUN   TestCommandMetadata/registry_vocabulary_difference
    root_test.go:73: "registry vocabulary difference" "output" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_vocabulary_intersection
    root_test.go:73: "registry vocabulary intersection" "output" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_vocabulary_union
    root_test.go:73: "registry vocabulary union" "output" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_vocabulary_unique
    root_test.go:73: "registry vocabulary unique" "output-id" flag description must not be empty.
=== RUN   TestCommandMetadata/registry_vocabulary_versions
    root_test.go:73: "registry vocabulary versions" "output" flag description must not be empty.
--- PASS: TestCommandMetadata (0.00s)
    --- PASS: TestCommandMetadata/registry (0.00s)
    --- PASS: TestCommandMetadata/registry_annotate (0.00s)
    --- PASS: TestCommandMetadata/registry_apply (0.00s)
    --- PASS: TestCommandMetadata/registry_auth (0.00s)
    --- PASS: TestCommandMetadata/registry_auth_print-token (0.00s)
    --- PASS: TestCommandMetadata/registry_check (0.00s)
    --- PASS: TestCommandMetadata/registry_compute (0.00s)
    --- PASS: TestCommandMetadata/registry_compute_complexity (0.00s)
    --- PASS: TestCommandMetadata/registry_compute_conformance (0.00s)
    --- PASS: TestCommandMetadata/registry_compute_lint (0.00s)
    --- PASS: TestCommandMetadata/registry_compute_lintstats (0.00s)
    --- PASS: TestCommandMetadata/registry_compute_score (0.00s)
    --- PASS: TestCommandMetadata/registry_compute_scorecard (0.00s)
    --- PASS: TestCommandMetadata/registry_compute_vocabulary (0.00s)
    --- PASS: TestCommandMetadata/registry_config (0.00s)
    --- PASS: TestCommandMetadata/registry_config_configurations (0.00s)
    --- PASS: TestCommandMetadata/registry_config_configurations_activate (0.00s)
    --- PASS: TestCommandMetadata/registry_config_configurations_create (0.00s)
    --- PASS: TestCommandMetadata/registry_config_configurations_delete (0.00s)
    --- PASS: TestCommandMetadata/registry_config_configurations_describe (0.00s)
    --- PASS: TestCommandMetadata/registry_config_configurations_list (0.00s)
    --- PASS: TestCommandMetadata/registry_config_get (0.00s)
    --- PASS: TestCommandMetadata/registry_config_list (0.00s)
    --- PASS: TestCommandMetadata/registry_config_set (0.00s)
    --- PASS: TestCommandMetadata/registry_config_unset (0.00s)
    --- PASS: TestCommandMetadata/registry_count (0.00s)
    --- PASS: TestCommandMetadata/registry_count_revisions (0.00s)
    --- PASS: TestCommandMetadata/registry_count_versions (0.00s)
    --- PASS: TestCommandMetadata/registry_delete (0.00s)
    --- PASS: TestCommandMetadata/registry_diff (0.00s)
    --- PASS: TestCommandMetadata/registry_export (0.00s)
    --- PASS: TestCommandMetadata/registry_export_csv (0.00s)
    --- PASS: TestCommandMetadata/registry_export_yaml (0.00s)
    --- PASS: TestCommandMetadata/registry_get (0.00s)
    --- PASS: TestCommandMetadata/registry_label (0.00s)
    --- PASS: TestCommandMetadata/registry_resolve (0.00s)
    --- PASS: TestCommandMetadata/registry_rpc (0.00s)
    --- PASS: TestCommandMetadata/registry_upload (0.00s)
    --- PASS: TestCommandMetadata/registry_upload_bulk (0.00s)
    --- PASS: TestCommandMetadata/registry_upload_bulk_discovery (0.00s)
    --- PASS: TestCommandMetadata/registry_upload_bulk_openapi (0.00s)
    --- PASS: TestCommandMetadata/registry_upload_bulk_protos (0.00s)
    --- PASS: TestCommandMetadata/registry_upload_csv (0.00s)
    --- PASS: TestCommandMetadata/registry_upload_spec (0.00s)
    --- PASS: TestCommandMetadata/registry_vocabulary (0.00s)
    --- PASS: TestCommandMetadata/registry_vocabulary_difference (0.00s)
    --- PASS: TestCommandMetadata/registry_vocabulary_intersection (0.00s)
    --- PASS: TestCommandMetadata/registry_vocabulary_union (0.00s)
    --- PASS: TestCommandMetadata/registry_vocabulary_unique (0.00s)
    --- PASS: TestCommandMetadata/registry_vocabulary_versions (0.00s)
PASS
ok      github.com/apigee/registry/cmd/registry/cmd 
```

codecov[bot]: # [Codecov](https://codecov.io/gh/apigee/registry/pull/918?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) Report
> Merging [#918](https://codecov.io/gh/apigee/registry/pull/918?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) (e79c077) into [main](https://codecov.io/gh/apigee/registry/commit/0f01d2fc8331fd7521b932ee75bb3e039b8d330c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) (0f01d2f) will **increase** coverage by `0.09%`.
> The diff coverage is `n/a`.

```diff
@@            Coverage Diff             @@
##             main     #918      +/-   ##
==========================================
+ Coverage   62.95%   63.04%   +0.09%     
==========================================
  Files         114      115       +1     
  Lines       10223    10248      +25     
==========================================
+ Hits         6436     6461      +25     
  Misses       3146     3146              
  Partials      641      641              
```


| [Impacted Files](https://codecov.io/gh/apigee/registry/pull/918?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) | Coverage Δ | |
|---|---|---|
| [cmd/registry/cmd/root.go](https://codecov.io/gh/apigee/registry/pull/918?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee#diff-Y21kL3JlZ2lzdHJ5L2NtZC9yb290Lmdv) | `100.00% <0.00%> (ø)` | |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee)


Patch:
diff --git a/cmd/registry/cmd/annotate/annotate.go b/cmd/registry/cmd/annotate/annotate.go
index 752abe4..d3e61e8 100644
--- a/cmd/registry/cmd/annotate/annotate.go
+++ b/cmd/registry/cmd/annotate/annotate.go
@@ -81,9 +81,9 @@ func Command() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().StringVar(&filter, "filter", "", "Filter selected resources")
-	cmd.Flags().BoolVar(&overwrite, "overwrite", false, "Overwrite existing annotations")
-	cmd.Flags().IntVar(&jobs, "jobs", 10, "Number of actions to perform concurrently")
+	cmd.Flags().StringVar(&filter, "filter", "", "filter selected resources")
+	cmd.Flags().BoolVar(&overwrite, "overwrite", false, "overwrite existing annotations")
+	cmd.Flags().IntVarP(&jobs, "jobs", "j", 10, "number of actions to perform concurrently")
 	return cmd
 }
 
diff --git a/cmd/registry/cmd/apply/apply.go b/cmd/registry/cmd/apply/apply.go
index faf3099..2ef8bd5 100644
--- a/cmd/registry/cmd/apply/apply.go
+++ b/cmd/registry/cmd/apply/apply.go
@@ -55,10 +55,10 @@ func Command() *cobra.Command {
 			return patch.Apply(ctx, client, fileName, parent, recursive, jobs)
 		},
 	}
-	cmd.Flags().StringVarP(&fileName, "file", "f", "", "File or directory containing the patch(es) to apply")
-	cmd.Flags().StringVar(&parent, "parent", "", "Parent resource for the patch")
+	cmd.Flags().StringVarP(&fileName, "file", "f", "", "file or directory containing the patch(es) to apply")
+	cmd.Flags().StringVar(&parent, "parent", "", "parent resource for the patch")
 	cmd.Flags().BoolVarP(&recursive, "recursive", "R", false,
-		"Process the directory used in -f, --file recursively. Useful when you want to manage related manifests organized within the same directory")
-	cmd.Flags().IntVarP(&jobs, "jobs", "j", 10, "Number of apply operations to perform simultaneously")
+		"process the directory used in -f, --file recursively")
+	cmd.Flags().IntVarP(&jobs, "jobs", "j", 10, "number of actions to perform concurrently")
 	return cmd
 }
diff --git a/cmd/registry/cmd/auth/auth.go b/cmd/registry/cmd/auth/auth.go
index 11bb81e..18adf4d 100644
--- a/cmd/registry/cmd/auth/auth.go
+++ b/cmd/registry/cmd/auth/auth.go
@@ -24,7 +24,7 @@ import (
 func Command() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "auth",
-		Short: "Manage authentication to the registry.",
+		Short: "Manage authentication to the API Registry",
 		Long: "Authentication to the registry is via tokens. These tokens can be provided " +
 			"directly by using '--registry.token' in any command, or can be generated as needed " +
 			"by setting the token-source property in a configuration. The token-source can be set " +
diff --git a/cmd/registry/cmd/auth/print_token.go b/cmd/registry/cmd/auth/print_token.go
index ed472f4..760fb22 100644
--- a/cmd/registry/cmd/auth/print_token.go
+++ b/cmd/registry/cmd/auth/print_token.go
@@ -24,7 +24,7 @@ import (
 func printTokenCommand() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "print-token",
-		Short: "Invoke token-source to print a token.",
+		Short: "Invoke token-source to print a token",
 		Args:  cobra.ExactArgs(0),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			cmd.SilenceUsage = true
diff --git a/cmd/registry/cmd/check/check.go b/cmd/registry/cmd/check/check.go
index a805ff0..d029b10 100644
--- a/cmd/registry/cmd/check/check.go
+++ b/cmd/registry/cmd/check/check.go
@@ -44,7 +44,7 @@ func defaultConfigs() lint.Configs {
 func Command() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "check [pattern]",
-		Short: "Check entities in the registry",
+		Short: "Check entities in the API Registry",
 		Args:  cobra.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			ctx := cmd.Context()
diff --git a/cmd/registry/cmd/compute/lint.go b/cmd/registry/cmd/compute/lint.go
index 1aca5e5..e234cce 100644
--- a/cmd/registry/cmd/compute/lint.go
+++ b/cmd/registry/cmd/compute/lint.go
@@ -84,7 +84,7 @@ func lintCommand() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().StringVar(&linter, "linter", "", "The linter to use (aip|spectral|gnostic)")
+	cmd.Flags().StringVar(&linter, "linter", "", "the linter to use (aip|spectral|gnostic)")
 	return cmd
 }
 
diff --git a/cmd/registry/cmd/compute/lintstats.go b/cmd/registry/cmd/compute/lintstats.go
index 9eec2ea..248315c 100644
--- a/cmd/registry/cmd/compute/lintstats.go
+++ b/cmd/registry/cmd/compute/lintstats.go
@@ -76,7 +76,7 @@ func lintStatsCommand() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().StringVar(&linter, "linter", "", "The name of the linter whose results will be used to compute stats (aip|spectral|gnostic)")
+	cmd.Flags().StringVar(&linter, "linter", "", "the name of the linter whose results will be used to compute stats (aip|spectral|gnostic)")
 	_ = cmd.MarkFlagRequired("linter")
 	return cmd
 }
diff --git a/cmd/registry/cmd/config/config.go b/cmd/registry/cmd/config/config.go
index 1c9760a..62f1d01 100644
--- a/cmd/registry/cmd/config/config.go
+++ b/cmd/registry/cmd/config/config.go
@@ -24,7 +24,7 @@ import (
 func Command() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "config",
-		Short: "Maintain properties in the active configuration.",
+		Short: "Maintain properties in the active configuration",
 		Long: "Configurations manage sets of properties used when connecting to the registry. " +
 			"These commands manipulate the property values in the active configuration. " +
 			"See `config configurations --help` for information on how to manage configurations. " +
diff --git a/cmd/registry/cmd/config/configurations/activate.go b/cmd/registry/cmd/config/configurations/activate.go
index 8ad48dc..fd50357 100644
--- a/cmd/registry/cmd/config/configurations/activate.go
+++ b/cmd/registry/cmd/config/configurations/activate.go
@@ -24,7 +24,7 @@ import (
 func activateCommand() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "activate CONFIGURATION_NAME",
-		Short: "Activates an existing named configuration.",
+		Short: "Activates an existing named configuration",
 		Args:  cobra.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			cmd.SilenceUsage = true
diff --git a/cmd/registry/cmd/config/configurations/create.go b/cmd/registry/cmd/config/configurations/create.go
index 4e2067a..e107848 100644
--- a/cmd/registry/cmd/config/configurations/create.go
+++ b/cmd/registry/cmd/config/configurations/create.go
@@ -24,7 +24,7 @@ import (
 func createCommand() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "create CONFIGURATION_NAME",
-		Short: "Creates and activates a new named configuration.",
+		Short: "Creates and activates a new named configuration",
 		Long: "Creates and activates a new named configuration. Values will be populated from active " +
 			"configuration (if any) and any passed property flags.",
 		Example: "registry config configurations create localhost --registry.address='locahost:8080'",
diff --git a/cmd/registry/cmd/config/configurations/delete.go b/cmd/registry/cmd/config/configurations/delete.go
index 7355eff..c22ceed 100644
--- a/cmd/registry/cmd/config/configurations/delete.go
+++ b/cmd/registry/cmd/config/configurations/delete.go
@@ -25,7 +25,7 @@ import (
 func deleteCommand() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "delete CONFIGURATION_NAME [CONFIGURATION_NAME ...]",
-		Short: "Deletes a named configuration.",
+		Short: "Deletes a named configuration",
 		Args:  cobra.MinimumNArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			cmd.SilenceUsage = true
diff --git a/cmd/registry/cmd/config/configurations/describe.go b/cmd/registry/cmd/config/configurations/describe.go
index 545fc4e..46fd20f 100644
--- a/cmd/registry/cmd/config/configurations/describe.go
+++ b/cmd/registry/cmd/config/configurations/describe.go
@@ -25,7 +25,7 @@ import (
 func describeCommand() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "describe CONFIGURATION_NAME",
-		Short: "Describes a named configuration by listing its properties.",
+		Short: "Describes a named configuration by listing its properties",
 		Args:  cobra.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			cmd.SilenceUsage = true
diff --git a/cmd/registry/cmd/config/configurations/list.go b/cmd/registry/cmd/config/configurations/list.go
index 091d7a8..b751fa7 100644
--- a/cmd/registry/cmd/config/configurations/list.go
+++ b/cmd/registry/cmd/config/configurations/list.go
@@ -28,7 +28,7 @@ import (
 func listCommand() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "list",
-		Short: "Lists existing named configs.",
+		Short: "Lists existing named configs",
 		Args:  cobra.NoArgs,
 		RunE: func(cmd *cobra.Command, _ []string) error {
 			cmd.SilenceUsage = true
diff --git a/cmd/registry/cmd/config/get.go b/cmd/registry/cmd/config/get.go
index 459a1b4..2099c46 100644
--- a/cmd/registry/cmd/config/get.go
+++ b/cmd/registry/cmd/config/get.go
@@ -24,7 +24,7 @@ import (
 func getCommand() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "get PROPERTY",
-		Short: "Print a property value in the active configuration.",
+		Short: "Print a property value in the active configuration",
 		Args:  cobra.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			cmd.SilenceUsage = true
diff --git a/cmd/registry/cmd/config/list.go b/cmd/registry/cmd/config/list.go
index 234ef20..bfff641 100644
--- a/cmd/registry/cmd/config/list.go
+++ b/cmd/registry/cmd/config/list.go
@@ -24,7 +24,7 @@ import (
 func listCommand() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "list",
-		Short: "List properties in the active configuration.",
+		Short: "List properties in the active configuration",
 		Args:  cobra.NoArgs,
 		RunE: func(cmd *cobra.Command, _ []string) error {
 			cmd.SilenceUsage = true
diff --git a/cmd/registry/cmd/config/set.go b/cmd/registry/cmd/config/set.go
index bd6259c..1be7d79 100644
--- a/cmd/registry/cmd/config/set.go
+++ b/cmd/registry/cmd/config/set.go
@@ -24,7 +24,7 @@ import (
 func setCommand() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "set PROPERTY VALUE",
-		Short: "Set a property value in the active configuration.",
+		Short: "Set a property value in the active configuration",
 		Long: "Set a property value in the active configuration. The following are valid properties:\n" +
 			"  - registry.address\n" +
 			"  - registry.insecure\n" +
diff --git a/cmd/registry/cmd/config/unset.go b/cmd/registry/cmd/config/unset.go
index 69494ec..a6a888f 100644
--- a/cmd/registry/cmd/config/unset.go
+++ b/cmd/registry/cmd/config/unset.go
@@ -24,7 +24,7 @@ import (
 func unsetCommand() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "unset PROPERTY",
-		Short: "Clear the property value from the active configuration.",
+		Short: "Clear the property value from the active configuration",
 		Args:  cobra.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			cmd.SilenceUsage = true
diff --git a/cmd/registry/cmd/count/revisions.go b/cmd/registry/cmd/count/revisions.go
index 3282d3a..e99de7a 100644
--- a/cmd/registry/cmd/count/revisions.go
+++ b/cmd/registry/cmd/count/revisions.go
@@ -83,7 +83,7 @@ func revisionsCommand() *cobra.Command {
 			}
 		},
 	}
-	cmd.Flags().StringVar(&filter, "filter", "", "Filter selected resources")
+	cmd.Flags().StringVar(&filter, "filter", "", "filter selected resources")
 	return cmd
 }
 
diff --git a/cmd/registry/cmd/count/versions.go b/cmd/registry/cmd/count/versions.go
index 905a450..dd13021 100644
--- a/cmd/registry/cmd/count/versions.go
+++ b/cmd/registry/cmd/count/versions.go
@@ -73,7 +73,7 @@ func versionsCommand() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().StringVar(&filter, "filter", "", "Filter selected resources")
+	cmd.Flags().StringVar(&filter, "filter", "", "filter selected resources")
 	return cmd
 }
 
diff --git a/cmd/registry/cmd/delete/delete.go b/cmd/registry/cmd/delete/delete.go
index 00ea5a4..f20bf09 100644
--- a/cmd/registry/cmd/delete/delete.go
+++ b/cmd/registry/cmd/delete/delete.go
@@ -75,9 +75,9 @@ func Command() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().StringVar(&filter, "filter", "", "Filter selected resources")
-	cmd.Flags().IntVar(&jobs, "jobs", 10, "Number of actions to perform concurrently")
-	cmd.Flags().BoolVarP(&force, "force", "f", false, "Force deletion of child resources")
+	cmd.Flags().StringVar(&filter, "filter", "", "filter selected resources")
+	cmd.Flags().IntVarP(&jobs, "jobs", "j", 10, "number of actions to perform concurrently")
+	cmd.Flags().BoolVarP(&force, "force", "f", false, "force deletion of child resources")
 	return cmd
 }
 
diff --git a/cmd/registry/cmd/diff/diff.go b/cmd/registry/cmd/diff/diff.go
index 3643155..3f1b351 100644
--- a/cmd/registry/cmd/diff/diff.go
+++ b/cmd/registry/cmd/diff/diff.go
@@ -35,7 +35,7 @@ import (
 func Command() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "diff",
-		Short: "Compare resources in the registry",
+		Short: "Compare resources in the API Registry",
 		Args:  cobra.ExactArgs(2),
 		Run: func(cmd *cobra.Command, args []string) {
 			ctx := cmd.Context()
diff --git a/cmd/registry/cmd/export/export.go b/cmd/registry/cmd/export/export.go
index 4a4c9f0..14a42d3 100644
--- a/cmd/registry/cmd/export/export.go
+++ b/cmd/registry/cmd/export/export.go
@@ -53,7 +53,7 @@ func Command() *cobra.Command {
 			}
 		},
 	}
-	cmd.Flags().IntVarP(&jobs, "jobs", "j", 10, "Number of file exports to perform simultaneously")
-	cmd.Flags().StringVar(&root, "root", "", "Root directory for export")
+	cmd.Flags().IntVarP(&jobs, "jobs", "j", 10, "number of actions to perform concurrently")
+	cmd.Flags().StringVar(&root, "root", "", "root directory for export")
 	return cmd
 }
diff --git a/cmd/registry/cmd/get/get.go b/cmd/registry/cmd/get/get.go
index ec9df5a..f377c7e 100644
--- a/cmd/registry/cmd/get/get.go
+++ b/cmd/registry/cmd/get/get.go
@@ -71,8 +71,8 @@ func Command() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().StringVar(&filter, "filter", "", "Filter selected resources")
-	cmd.Flags().StringVarP(&output, "output", "o", "name", "Output type (name, yaml, contents)")
+	cmd.Flags().StringVar(&filter, "filter", "", "filter selected resources")
+	cmd.Flags().StringVarP(&output, "output", "o", "name", "output type (name|yaml|contents)")
 	return cmd
 }
 
diff --git a/cmd/registry/cmd/label/label.go b/cmd/registry/cmd/label/label.go
index d1e5d85..f3bc27d 100644
--- a/cmd/registry/cmd/label/label.go
+++ b/cmd/registry/cmd/label/label.go
@@ -81,9 +81,9 @@ func Command() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().StringVar(&filter, "filter", "", "Filter selected resources")
-	cmd.Flags().BoolVar(&overwrite, "overwrite", false, "Overwrite existing labels")
-	cmd.Flags().IntVar(&jobs, "jobs", 10, "Number of actions to perform concurrently")
+	cmd.Flags().StringVar(&filter, "filter", "", "filter selected resources")
+	cmd.Flags().BoolVar(&overwrite, "overwrite", false, "overwrite existing labels")
+	cmd.Flags().IntVarP(&jobs, "jobs", "j", 10, "number of actions to perform concurrently")
 	return cmd
 }
 
diff --git a/cmd/registry/cmd/resolve/resolve.go b/cmd/registry/cmd/resolve/resolve.go
index b069661..3d7a883 100644
--- a/cmd/registry/cmd/resolve/resolve.go
+++ b/cmd/registry/cmd/resolve/resolve.go
@@ -58,7 +58,7 @@ func Command() *cobra.Command {
 	var maxActions int
 	cmd := &cobra.Command{
 		Use:   "resolve MANIFEST_RESOURCE",
-		Short: "resolve the dependencies and update the registry state (experimental)",
+		Short: "Resolve dependencies by performing actions in a specified manifest",
 		Args:  cobra.ExactArgs(1),
 		Run: func(cmd *cobra.Command, args []string) {
 			ctx := cmd.Context()
@@ -120,7 +120,7 @@ func Command() *cobra.Command {
 	}
 
 	cmd.Flags().BoolVar(&dryRun, "dry-run", false, "if set, actions will only be printed and not executed")
-	cmd.Flags().IntVarP(&jobs, "jobs", "j", 10, "Number of actions to execute simultaneously")
-	cmd.Flags().IntVarP(&maxActions, "max-actions", "a", 100, "Maximum number of actions to execute")
+	cmd.Flags().IntVarP(&jobs, "jobs", "j", 10, "number of actions to perform concurrently")
+	cmd.Flags().IntVarP(&maxActions, "max-actions", "a", 100, "maximum number of actions to execute")
 	return cmd
 }
diff --git a/cmd/registry/cmd/root.go b/cmd/registry/cmd/root.go
index bd4f747..2904d1e 100644
--- a/cmd/registry/cmd/root.go
+++ b/cmd/registry/cmd/root.go
@@ -43,7 +43,7 @@ func Command() *cobra.Command {
 	var cmd = &cobra.Command{
 		Use:     "registry",
 		Version: Version,
-		Short:   "A simple and eclectic utility for working with the API Registry",
+		Short:   "A command-line tool for working with the API Registry",
 	}
 	cmd.PersistentFlags().AddFlagSet(pkgconf.Flags)
 
diff --git a/cmd/registry/cmd/upload/bulk/discovery.go b/cmd/registry/cmd/upload/bulk/discovery.go
index 1f1e7ba..6495413 100644
--- a/cmd/registry/cmd/upload/bulk/discovery.go
+++ b/cmd/registry/cmd/upload/bulk/discovery.go
@@ -49,6 +49,7 @@ func discoveryCommand() *cobra.Command {
 	var service string
 	cmd := &cobra.Command{
 		Use:   "discovery",
+		Args:  cobra.NoArgs,
 		Short: "Bulk-upload API Discovery documents from the Google API Discovery service",
 		RunE: func(cmd *cobra.Command, args []string) error {
 			ctx := cmd.Context()
@@ -91,7 +92,7 @@ func discoveryCommand() *cobra.Command {
 		},
 	}
 	cmd.Flags().StringVar(&service, "service", "",
-		fmt.Sprintf("API Discovery Service URL (default %s)", discovery.APIsListServiceURL))
+		fmt.Sprintf("the API Discovery Service URL (default %s)", discovery.APIsListServiceURL))
 	return cmd
 }
 
diff --git a/cmd/registry/cmd/upload/bulk/openapi.go b/cmd/registry/cmd/upload/bulk/openapi.go
index c3b69d9..3c0133e 100644
--- a/cmd/registry/cmd/upload/bulk/openapi.go
+++ b/cmd/registry/cmd/upload/bulk/openapi.go
@@ -74,7 +74,7 @@ func openAPICommand() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().StringVar(&baseURI, "base-uri", "", "Prefix to use for the source_uri field of each spec upload")
+	cmd.Flags().StringVar(&baseURI, "base-uri", "", "prefix to use for the source_uri field of each spec upload")
 	return cmd
 }
 
diff --git a/cmd/registry/cmd/upload/bulk/protos.go b/cmd/registry/cmd/upload/bulk/protos.go
index 9285009..160ff09 100644
--- a/cmd/registry/cmd/upload/bulk/protos.go
+++ b/cmd/registry/cmd/upload/bulk/protos.go
@@ -102,9 +102,8 @@ func protosCommand() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().StringVar(&root, "protoc-root", "", "Root directory to use for proto compilation, defaults to PATH")
-
-	cmd.Flags().StringVar(&baseURI, "base-uri", "", "Prefix to use for the source_uri field of each proto upload")
+	cmd.Flags().StringVar(&root, "protoc-root", "", "root directory to use for proto compilation, defaults to PATH")
+	cmd.Flags().StringVar(&baseURI, "base-uri", "", "prefix to use for the source_uri field of each proto upload")
 	return cmd
 }
 
diff --git a/cmd/registry/cmd/upload/csv.go b/cmd/registry/cmd/upload/csv.go
index 7786d04..ebdaa2c 100644
--- a/cmd/registry/cmd/upload/csv.go
+++ b/cmd/registry/cmd/upload/csv.go
@@ -100,10 +100,10 @@ func csvCommand() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().StringVar(&projectID, "project-id", "", "Project ID to use for each upload")
+	cmd.Flags().StringVar(&projectID, "project-id", "", "project ID to use for each upload")
 	_ = cmd.MarkFlagRequired("project-id")
-	cmd.Flags().StringVar(&delimiter, "delimiter", ",", "Field delimiter for the CSV file")
-	cmd.Flags().IntVar(&jobs, "jobs", 10, "Number of actions to perform concurrently")
+	cmd.Flags().StringVar(&delimiter, "delimiter", ",", "field delimiter for the CSV file")
+	cmd.Flags().IntVarP(&jobs, "jobs", "j", 10, "number of actions to perform concurrently")
 	return cmd
 }
 
diff --git a/cmd/registry/cmd/upload/spec.go b/cmd/registry/cmd/upload/spec.go
index a2a7000..6e301ce 100644
--- a/cmd/registry/cmd/upload/spec.go
+++ b/cmd/registry/cmd/upload/spec.go
@@ -77,9 +77,9 @@ func specCommand() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().StringVar(&version, "version", "", "Version to use as parent for the spec upload")
+	cmd.Flags().StringVar(&version, "version", "", "version to use as parent for the spec upload")
 	_ = cmd.MarkFlagRequired("version")
-	cmd.Flags().StringVar(&style, "style", "", "Style of spec to upload (openapi|discovery|proto)")
+	cmd.Flags().StringVar(&style, "style", "", "style of spec to upload (openapi|discovery|proto)")
 	_ = cmd.MarkFlagRequired("style")
 	return cmd
 }
diff --git a/cmd/registry/cmd/vocabulary/difference.go b/cmd/registry/cmd/vocabulary/difference.go
index 7508b51..89bd228 100644
--- a/cmd/registry/cmd/vocabulary/difference.go
+++ b/cmd/registry/cmd/vocabulary/difference.go
@@ -51,6 +51,6 @@ func differenceCommand() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().String("output", "", "Artifact name to use when saving the vocabulary artifact")
+	cmd.Flags().String("output", "", "artifact name to use when saving the vocabulary artifact")
 	return cmd
 }
diff --git a/cmd/registry/cmd/vocabulary/intersection.go b/cmd/registry/cmd/vocabulary/intersection.go
index 66947b9..e6e5d97 100644
--- a/cmd/registry/cmd/vocabulary/intersection.go
+++ b/cmd/registry/cmd/vocabulary/intersection.go
@@ -51,6 +51,6 @@ func intersectionCommand() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().String("output", "", "Artifact name to use when saving the vocabulary artifact")
+	cmd.Flags().String("output", "", "artifact name to use when saving the vocabulary artifact")
 	return cmd
 }
diff --git a/cmd/registry/cmd/vocabulary/union.go b/cmd/registry/cmd/vocabulary/union.go
index 6841419..63d7bb4 100644
--- a/cmd/registry/cmd/vocabulary/union.go
+++ b/cmd/registry/cmd/vocabulary/union.go
@@ -51,6 +51,6 @@ func unionCommand() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().String("output", "", "Artifact name to use when saving the vocabulary artifact")
+	cmd.Flags().String("output", "", "artifact name to use when saving the vocabulary artifact")
 	return cmd
 }
diff --git a/cmd/registry/cmd/vocabulary/unique.go b/cmd/registry/cmd/vocabulary/unique.go
index 8816728..d51315e 100644
--- a/cmd/registry/cmd/vocabulary/unique.go
+++ b/cmd/registry/cmd/vocabulary/unique.go
@@ -59,6 +59,6 @@ func uniqueCommand() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().StringVar(&outputID, "output-id", "vocabulary-unique", "Artifact ID to use when saving each result vocabulary")
+	cmd.Flags().StringVar(&outputID, "output-id", "vocabulary-unique", "artifact ID to use when saving each result vocabulary")
 	return cmd
 }
diff --git a/cmd/registry/cmd/vocabulary/versions.go b/cmd/registry/cmd/vocabulary/versions.go
index c2667e6..4b6732a 100644
--- a/cmd/registry/cmd/vocabulary/versions.go
+++ b/cmd/registry/cmd/vocabulary/versions.go
@@ -58,6 +58,6 @@ func versionsCommand() *cobra.Command {
 		},
 	}
 
-	cmd.Flags().String("output", "", "Artifact name to use when saving the vocabulary artifact")
+	cmd.Flags().String("output", "", "artifact name to use when saving the vocabulary artifact")
 	return cmd
 }
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 39d3c37..f6b6f98 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -59,11 +59,11 @@ func init() {
 func CreateFlagSet() *pflag.FlagSet {
 	flags := pflag.NewFlagSet("registry", pflag.ExitOnError)
 	flags.StringP("config", "c", "", "name of a configuration profile or path to config file")
-	flags.String("registry.address", "", "the server and port of the registry api (eg. localhost:8080)")
+	flags.String("registry.address", "", "the server and port of the Registry API (eg. localhost:8080)")
 	flags.Bool("registry.insecure", false, "if specified, client connects via http (not https)")
-	flags.String("registry.location", "", "the registry location")
-	flags.String("registry.project", "", "the registry project")
-	flags.String("registry.token", "", "the token to use for authorization to registry")
+	flags.String("registry.location", "", "the API Registry location")
+	flags.String("registry.project", "", "the API Registry project")
+	flags.String("registry.token", "", "the token to use for authorization to the API Registry")
 	return flags
 }
 


Test patch:
diff --git a/cmd/registry/cmd/root_test.go b/cmd/registry/cmd/root_test.go
new file mode 100644
index 0000000..ed853a7
--- /dev/null
+++ b/cmd/registry/cmd/root_test.go
@@ -0,0 +1,130 @@
+// Copyright 2023 Google LLC. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cmd
+
+import (
+	"regexp"
+	"strings"
+	"testing"
+	"unicode"
+
+	"github.com/spf13/cobra"
+	"github.com/spf13/pflag"
+)
+
+func TestCommandMetadata(t *testing.T) {
+	checkCommand(t, Command(), "")
+}
+
+func checkCommand(t *testing.T, cmd *cobra.Command, prefix string) {
+	name := prefix + cmd.Name()
+	t.Run(name, func(t *testing.T) {
+		// Check short descriptions.
+		short := cmd.Short
+		if short == "" {
+			t.Errorf("%q has no short description.", name)
+		} else {
+			if strings.HasSuffix(short, ".") {
+				t.Errorf("%q short description must not end with a period.", name)
+			}
+			first := []rune(short)[0]
+			if unicode.IsLower(first) {
+				t.Errorf("%q short description must not begin with a lower case letter.", name)
+			}
+			if strings.Contains(short, "the registry") {
+				t.Errorf("%q short description must refer to `the API Registry` instead of `the registry`.", name)
+			}
+		}
+		// Perform additional checks on leaf-level commands.
+		if len(cmd.Commands()) == 0 {
+			args := cmd.Args
+			if args == nil {
+				t.Errorf("%q has an empty 'Args' field.", name)
+			}
+			use := cmd.Use
+			if use == "" {
+				t.Errorf("%q has an empty 'Use' field.", name)
+			}
+			if args != nil && use != "" {
+				parts := strings.Split(cmd.Use, " ")
+				err := args(cmd, []string{"one"})
+				if err == nil {
+					if len(parts) == 0 {
+						t.Errorf("%q accepts arguments that are not listed in 'Use' field.", name)
+					}
+				}
+			}
+			// Check field usage messages.
+			flags := cmd.Flags()
+			flags.VisitAll(func(f *pflag.Flag) {
+				if f.Usage == "" {
+					t.Errorf("%q %q flag usage must not be empty.", name, f.Name)
+				} else {
+					first := []rune(f.Usage)[0]
+					if unicode.IsUpper(first) {
+						t.Errorf("%q %q flag usage must not begin with an upper case letter.", name, f.Name)
+					}
+					if strings.HasSuffix(f.Usage, ".") {
+						t.Errorf("%q %q flag usage description must not end with a period.", name, f.Name)
+					}
+					re := regexp.MustCompile(`\(.*,.*\)`)
+					m := re.FindStringSubmatch(f.Usage)
+					if m != nil {
+						t.Errorf("%q %q flag usage seems to contain an enum %q, which should be of the form (a|b|c).", name, f.Name, m[0])
+					}
+					// flag-specific checks
+					commonFlags := []struct {
+						name      string
+						shorthand string
+						usage     string
+					}{
+						{
+							name:      "jobs",
+							shorthand: "j",
+							usage:     "number of actions to perform concurrently",
+						},
+						{
+							name:      "force",
+							shorthand: "f",
+							usage:     "force deletion of child resources",
+						},
+						{
+							name:      "filter",
+							shorthand: "",
+							usage:     "filter selected resources",
+						},
+					}
+					for _, c := range commonFlags {
+						if f.Name == c.name {
+							if f.Shorthand != c.shorthand {
+								t.Errorf("%q %q flag must have %q shorthand.", name, f.Name, c.shorthand)
+							}
+							if f.Usage != c.usage {
+								t.Errorf("%q %q flag must have the standard usage %q.", name, f.Name, c.usage)
+							}
+						}
+					}
+				}
+			})
+		}
+	})
+	// Check subcommands, but skip auto-generated ones. We can't easily fix those.
+	if cmd.Name() == "rpc" {
+		return
+	}
+	for _, c := range cmd.Commands() {
+		checkCommand(t, c, name+" ")
+	}
+}