Repository: dbt-labs/dbt-core

Issue:
[CT-1766] [DX] Review command + param help text in CLI / top-level API
Let's give the "help" text defined below another review from a DX (Developer Experience) lens:

- https://github.com/dbt-labs/dbt-core/blob/main/core/dbt/cli/main.py
- https://github.com/dbt-labs/dbt-core/blob/main/core/dbt/cli/params.py

Anything marked `TODO` is especially in need, but I think all help text deserves another look-over.

This is also our opportunity to remove params / help text that are completely unused or irrelevant. For example, [`dbt compile --parse-only`](https://github.com/dbt-labs/dbt-core/blob/94d6d19fb43f1ed466c9049dea40e203b23f89da/core/dbt/cli/params.py#L165-L170) doesn't do anything, and I don't believe it ever has.

### Context

This is what users will see when running `dbt --help` (invoked as module for now):
```bash
$ python3 -m core.dbt.cli.main --help
Usage: python -m core.dbt.cli.main [OPTIONS] COMMAND [ARGS]...

  An ELT tool for managing your SQL transformations and data models. For more
  documentation on these commands, visit: docs.getdbt.com

Options:
  --anonymous-usage-stats / --no-anonymous-usage-stats
                                  Send anonymous usage stats to dbt Labs.
  --cache-selected-only / --no-cache-selected-only
                                  Pre cache database objects relevant to
                                  selected resource only.
  -d, --debug / --no-debug        Display debug logging during dbt execution.
                                  Useful for debugging and making bug reports.
```

And it's also what they'll see in our auto-generated "API docs."

<img width="666" alt="Screenshot 2023-01-08 at 18 19 28" src="https://user-images.githubusercontent.com/13897643/211209857-9def46e5-e934-477f-8480-4f57a9c474ea.png">


Discussion:
@dbeatty10 Let's figure out a sprint where you have capacity to pick this up & drive this forward, between now and mid-April (RC release)
Might be nice: using the `click-option-group` extension to group together related commands in the `--help` output (and in the codebase).
- https://click-option-group.readthedocs.io/en/stable/index.html

Not necessary for the scope for this issue, only if inspiration strikes while looking at existing params.

Resolving patch:
diff --git a/core/dbt/cli/main.py b/core/dbt/cli/main.py
--- a/core/dbt/cli/main.py
+++ b/core/dbt/cli/main.py
@@ -202,7 +202,7 @@ def cli(ctx, **kwargs):
 @requires.runtime_config
 @requires.manifest
 def build(ctx, **kwargs):
-    """Run all Seeds, Models, Snapshots, and tests in DAG order"""
+    """Run all seeds, models, snapshots, and tests in DAG order"""
     task = BuildTask(
         ctx.obj["flags"],
         ctx.obj["runtime_config"],
@@ -323,7 +323,6 @@ def docs_serve(ctx, **kwargs):
 @p.show_output_format
 @p.indirect_selection
 @p.introspect
-@p.parse_only
 @p.profile
 @p.profiles_dir
 @p.project_dir
@@ -370,7 +369,6 @@ def compile(ctx, **kwargs):
 @p.show_limit
 @p.indirect_selection
 @p.introspect
-@p.parse_only
 @p.profile
 @p.profiles_dir
 @p.project_dir
@@ -417,7 +415,8 @@ def show(ctx, **kwargs):
 @requires.postflight
 @requires.preflight
 def debug(ctx, **kwargs):
-    """Show some helpful information about dbt for debugging. Not to be confused with the --debug option which increases verbosity."""
+    """Test the database connection and show information for debugging purposes. Not to be confused with the --debug option which increases verbosity."""
+
     task = DebugTask(
         ctx.obj["flags"],
         None,
@@ -516,7 +515,6 @@ def list(ctx, **kwargs):
 # dbt parse
 @cli.command("parse")
 @click.pass_context
-@p.compile_parse
 @p.profile
 @p.profiles_dir
 @p.project_dir
@@ -525,7 +523,6 @@ def list(ctx, **kwargs):
 @p.threads
 @p.vars
 @p.version_check
-@p.write_manifest
 @requires.postflight
 @requires.preflight
 @requires.profile
diff --git a/core/dbt/cli/params.py b/core/dbt/cli/params.py
--- a/core/dbt/cli/params.py
+++ b/core/dbt/cli/params.py
@@ -40,18 +40,11 @@
     default=True,
 )
 
-compile_parse = click.option(
-    "--compile/--no-compile",
-    envvar=None,
-    help="TODO: No help text currently available",
-    default=True,
-)
-
 config_dir = click.option(
     "--config-dir",
     envvar=None,
-    help="If specified, DBT will show path information for this project",
-    type=click.STRING,
+    help="Show the configured location for the profiles.yml file and exit",
+    is_flag=True,
 )
 
 debug = click.option(
@@ -65,7 +58,7 @@
 defer = click.option(
     "--defer/--no-defer",
     envvar="DBT_DEFER",
-    help="If set, defer to the state variable for resolving unselected nodes.",
+    help="If set, resolve unselected nodes by deferring to the manifest within the --state directory.",
 )
 
 deprecated_defer = click.option(
@@ -121,7 +114,7 @@
 indirect_selection = click.option(
     "--indirect-selection",
     envvar="DBT_INDIRECT_SELECTION",
-    help="Select all tests that are adjacent to selected resources, even if they those resources have been explicitly selected.",
+    help="Choose which tests to select that are adjacent to selected resources. Eager is most inclusive, cautious is most exclusive, and buildable is in between. Empty includes no tests at all.",
     type=click.Choice(["eager", "cautious", "buildable", "empty"], case_sensitive=False),
     default="eager",
 )
@@ -135,7 +128,7 @@
 log_format = click.option(
     "--log-format",
     envvar="DBT_LOG_FORMAT",
-    help="Specify the log format, overriding the command's default.",
+    help="Specify the format of logging to the console and the log file. Use --log-format-file to configure the format for the log file differently than the console.",
     type=click.Choice(["text", "debug", "json", "default"], case_sensitive=False),
     default="default",
 )
@@ -143,7 +136,7 @@
 log_format_file = click.option(
     "--log-format-file",
     envvar="DBT_LOG_FORMAT_FILE",
-    help="Specify the file log format, overriding the command's default and the value of --log-format.",
+    help="Specify the format of logging to the log file by overriding the default value and the general --log-format setting.",
     type=click.Choice(["text", "debug", "json", "default"], case_sensitive=False),
     default="debug",
 )
@@ -151,7 +144,7 @@
 log_level = click.option(
     "--log-level",
     envvar="DBT_LOG_LEVEL",
-    help="Specify the minimum severity of events that are logged.",
+    help="Specify the minimum severity of events that are logged to the console and the log file. Use --log-level-file to configure the severity for the log file differently than the console.",
     type=click.Choice(["debug", "info", "warn", "error", "none"], case_sensitive=False),
     default="info",
 )
@@ -159,11 +152,25 @@
 log_level_file = click.option(
     "--log-level-file",
     envvar="DBT_LOG_LEVEL_FILE",
-    help="Specify the minimum severity of events that are logged to file, overriding the value of --log-level-file.",
+    help="Specify the minimum severity of events that are logged to the log file by overriding the default value and the general --log-level setting.",
     type=click.Choice(["debug", "info", "warn", "error", "none"], case_sensitive=False),
     default="debug",
 )
 
+use_colors = click.option(
+    "--use-colors/--no-use-colors",
+    envvar="DBT_USE_COLORS",
+    help="Specify whether log output is colorized in the console and the log file. Use --use-colors-file/--no-use-colors-file to colorize the log file differently than the console.",
+    default=True,
+)
+
+use_colors_file = click.option(
+    "--use-colors-file/--no-use-colors-file",
+    envvar="DBT_USE_COLORS_FILE",
+    help="Specify whether log file output is colorized by overriding the default value and the general --use-colors/--no-use-colors setting.",
+    default=True,
+)
+
 log_path = click.option(
     "--log-path",
     envvar="DBT_LOG_PATH",
@@ -178,10 +185,11 @@
     hidden=True,
 )
 
+# This less standard usage of --output where output_path below is more standard
 output = click.option(
     "--output",
     envvar=None,
-    help="TODO: No current help text",
+    help="Specify the output format: either JSON or a newline-delimited list of selectors, paths, or names",
     type=click.Choice(["json", "name", "path", "selector"], case_sensitive=False),
     default="selector",
 )
@@ -219,18 +227,11 @@
     "--output",
     "-o",
     envvar=None,
-    help="Specify the output path for the json report. By default, outputs to 'target/sources.json'",
+    help="Specify the output path for the JSON report. By default, outputs to 'target/sources.json'",
     type=click.Path(file_okay=True, dir_okay=False, writable=True),
     default=PurePath.joinpath(Path.cwd(), "target/sources.json"),
 )
 
-parse_only = click.option(
-    "--parse-only",
-    envvar=None,
-    help="TODO:  No help text currently available",
-    is_flag=True,
-)
-
 partial_parse = click.option(
     "--partial-parse/--no-partial-parse",
     envvar="DBT_PARTIAL_PARSE",
@@ -310,6 +311,7 @@
 
 quiet = click.option(
     "--quiet/--no-quiet",
+    "-q",
     envvar="DBT_QUIET",
     help="Suppress all non-error logging to stdout. Does not affect {{ print() }} macro calls.",
 )
@@ -326,7 +328,7 @@
     "--resource-types",
     "--resource-type",
     envvar=None,
-    help="TODO: No current help text",
+    help="Restricts the types of resources that dbt will include",
     type=ChoiceTuple(
         [
             "metric",
@@ -357,7 +359,11 @@
     "type": tuple,
 }
 
-inline = click.option("--inline", envvar=None, help="Pass SQL inline to dbt compile and show")
+inline = click.option(
+    "--inline",
+    envvar=None,
+    help="Pass SQL inline to dbt compile and show",
+)
 
 # `--select` and `--models` are analogous for most commands except `dbt list` for legacy reasons.
 # Most CLI arguments should use the combined `select` option that aliases `--models` to `--select`.
@@ -368,7 +374,9 @@
 select = click.option(*select_decls, *model_decls, **select_attrs)
 
 selector = click.option(
-    "--selector", envvar=None, help="The selector name to use, as defined in selectors.yml"
+    "--selector",
+    envvar=None,
+    help="The selector name to use, as defined in selectors.yml",
 )
 
 send_anonymous_usage_stats = click.option(
@@ -379,7 +387,10 @@
 )
 
 show = click.option(
-    "--show", envvar=None, help="Show a sample of the loaded data in the terminal", is_flag=True
+    "--show",
+    envvar=None,
+    help="Show a sample of the loaded data in the terminal",
+    is_flag=True,
 )
 
 # TODO:  The env var is a correction!
@@ -396,13 +407,17 @@
 )
 
 skip_profile_setup = click.option(
-    "--skip-profile-setup", "-s", envvar=None, help="Skip interactive profile setup.", is_flag=True
+    "--skip-profile-setup",
+    "-s",
+    envvar=None,
+    help="Skip interactive profile setup.",
+    is_flag=True,
 )
 
 state = click.option(
     "--state",
     envvar="DBT_STATE",
-    help="If set, use the given directory as the source for json files to compare with this project.",
+    help="If set, use the given directory as the source for JSON files to compare with this project.",
     type=click.Path(
         dir_okay=True,
         file_okay=False,
@@ -441,7 +456,10 @@
 )
 
 target = click.option(
-    "--target", "-t", envvar=None, help="Which target to load for the given profile"
+    "--target",
+    "-t",
+    envvar=None,
+    help="Which target to load for the given profile",
 )
 
 target_path = click.option(
@@ -459,20 +477,6 @@
     type=click.INT,
 )
 
-use_colors = click.option(
-    "--use-colors/--no-use-colors",
-    envvar="DBT_USE_COLORS",
-    help="Specify whether log output is colorized.",
-    default=True,
-)
-
-use_colors_file = click.option(
-    "--use-colors-file/--no-use-colors-file",
-    envvar="DBT_USE_COLORS_FILE",
-    help="Specify whether log file output is colorized overriding --use-colors/--no-use-colors.",
-    default=True,
-)
-
 use_experimental_parser = click.option(
     "--use-experimental-parser/--no-use-experimental-parser",
     envvar="DBT_USE_EXPERIMENTAL_PARSER",
@@ -499,10 +503,12 @@ def _version_callback(ctx, _param, value):
 
 version = click.option(
     "--version",
+    "-V",
+    "-v",
     callback=_version_callback,
     envvar=None,
     expose_value=False,
-    help="Show version information",
+    help="Show version information and exit",
     is_eager=True,
     is_flag=True,
 )
@@ -510,7 +516,7 @@ def _version_callback(ctx, _param, value):
 version_check = click.option(
     "--version-check/--no-version-check",
     envvar="DBT_VERSION_CHECK",
-    help="Ensure dbt's version matches the one specified in the dbt_project.yml file ('require-dbt-version')",
+    help="If set, ensure the installed dbt version matches the require-dbt-version specified in the dbt_project.yml file (if any). Otherwise, allow them to differ.",
     default=True,
 )
 
@@ -534,13 +540,6 @@ def _version_callback(ctx, _param, value):
 write_json = click.option(
     "--write-json/--no-write-json",
     envvar="DBT_WRITE_JSON",
-    help="Writing the manifest and run_results.json files to disk",
-    default=True,
-)
-
-write_manifest = click.option(
-    "--write-manifest/--no-write-manifest",
-    envvar=None,
-    help="TODO: No help text currently available",
+    help="Whether or not to write the manifest.json and run_results.json files to the target directory",
     default=True,
 )
diff --git a/core/dbt/cli/requires.py b/core/dbt/cli/requires.py
--- a/core/dbt/cli/requires.py
+++ b/core/dbt/cli/requires.py
@@ -224,8 +224,8 @@ def wrapper(*args, **kwargs):
 
 def manifest(*args0, write=True, write_perf_info=False):
     """A decorator used by click command functions for generating a manifest
-    given a profile, project, and runtime config. This also registers the adaper
-    from the runtime config and conditionally writes the manifest to disc.
+    given a profile, project, and runtime config. This also registers the adapter
+    from the runtime config and conditionally writes the manifest to disk.
     """
 
     def outer_wrapper(func):


Test patch:
diff --git a/tests/functional/partial_parsing/test_pp_docs.py b/tests/functional/partial_parsing/test_pp_docs.py
--- a/tests/functional/partial_parsing/test_pp_docs.py
+++ b/tests/functional/partial_parsing/test_pp_docs.py
@@ -223,7 +223,7 @@ def models(self):
         }
 
     def test_remove_replace(self, project):
-        run_dbt(["parse", "--write-manifest"])
+        run_dbt(["parse"])
         manifest = get_manifest(project.project_root)
         doc_id = "doc.test.whatever"
         assert doc_id in manifest.docs
@@ -243,7 +243,7 @@ def test_remove_replace(self, project):
         rm_file(project.project_root, "models", "my_model.md")
         # remove description from schema file
         write_file(my_model_no_description_yml, project.project_root, "models", "my_model.yml")
-        run_dbt(["parse", "--write-manifest"])
+        run_dbt(["parse"])
         manifest = get_manifest(project.project_root)
         assert doc_id not in manifest.docs
         # The bug was that the file still existed in manifest.files