Repository: andreoliwa/nitpick

Issue:
Nitpick should fail when no style is explicitly configured
## Problem

I installed nitpick in my project, but I forget to configure the `style =` key in `[tool.nitpick]`, so when I ran `nitpick fix` it used the default nitpick style which created a `package.json`, `.github/` directory, etc. which I do not need.

## Possible solution

I would have expected the tool to fail and tell me to explicitly configure a style.

I'm creating this issue because I do not really see any use case where using the default nitpick style makes sense. If not, I'm curious about that.


Comments:

Patch:
diff --git a/README.rst b/README.rst
index 6d30ac9..61cbd01 100644
--- a/README.rst
+++ b/README.rst
@@ -350,6 +350,13 @@ Or install it with pip::
 Run
 ~~~
 
+Nitpick_ will fail if no style is explicitly configured.
+Run this command to download and use the opinionated :gitref:`default style file <nitpick-style.toml>`:
+
+    nitpick init
+
+You can use it as a template to :ref:`configure-your-own-style`.
+
 To fix and modify your files directly::
 
     nitpick fix
@@ -358,15 +365,10 @@ To check for errors only::
 
     nitpick check
 
-Nitpick is also a ``flake8`` plugin, so you can run this on a project
-with at least one Python (``.py``) file::
+Nitpick is also a flake8_ plugin, so you can run this on a project with at least one Python (``.py``) file::
 
     flake8 .
 
-Nitpick will download and use the opinionated `default style file <nitpick-style.toml>`_.
-
-You can use it as a template to configure your own style.
-
 Run as a pre-commit hook
 ~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/docs/configuration.rst b/docs/configuration.rst
index 4f39c68..ce2d29f 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -18,7 +18,9 @@ Possible configuration files (in order of precedence):
 
 The first file found will be used; the other files will be ignored.
 
-Run the ``nipick init`` CLI command to create a config file (:ref:`cli_cmd_init`).
+If no style is configured, Nitpick will fail with an error message.
+
+Run ``nipick init`` to create a config file (:ref:`cli_cmd_init`).
 
 To configure your own style, you can either use ``nitpick init``:
 
@@ -217,7 +219,7 @@ Multiple styles
 
 You can also use multiple styles and mix local files and URLs.
 
-Example of usage: the ``[tool.nitpick]`` table on :gitref:`Nitpick's own pyproject.toml <pyproject.toml#L1-L7>`.
+Example of usage: the ``[tool.nitpick]`` table on :gitref:`Nitpick's own pyproject.toml <pyproject.toml>`.
 
 .. code-block:: toml
 
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
index 14907c3..cce7e2c 100644
--- a/docs/quickstart.rst
+++ b/docs/quickstart.rst
@@ -36,6 +36,13 @@ Or install it with pip::
 Run
 ---
 
+Nitpick_ will fail if no style is explicitly configured.
+Run this command to download and use the opinionated :gitref:`default style file <nitpick-style.toml>`:
+
+    nitpick init
+
+You can use it as a template to :ref:`configure-your-own-style`.
+
 To fix and modify your files directly::
 
     nitpick fix
@@ -48,10 +55,6 @@ Nitpick is also a flake8_ plugin, so you can run this on a project with at least
 
     flake8 .
 
-Nitpick_ will download and use the opinionated :gitref:`default style file <nitpick-style.toml>`.
-
-You can use it as a template to :ref:`configure-your-own-style`.
-
 Run as a pre-commit hook
 ------------------------
 
diff --git a/src/nitpick/constants.py b/src/nitpick/constants.py
index 7562b60..4cfd309 100644
--- a/src/nitpick/constants.py
+++ b/src/nitpick/constants.py
@@ -54,6 +54,10 @@ CONFIG_FILES = (DOT_NITPICK_TOML, PYPROJECT_TOML)
 # Config sections and keys
 TOOL_KEY = "tool"
 TOOL_NITPICK_KEY = f"{TOOL_KEY}.{PROJECT_NAME}"
+RUN_NITPICK_INIT_OR_CONFIGURE_STYLE_MANUALLY = (
+    f" Run 'nitpick init' or configure a style manually ({', '.join(CONFIG_FILES)})."
+    f" See {READ_THE_DOCS_URL}configuration.html"
+)
 
 # JMESPath expressions
 TOOL_NITPICK_JMEX = jmespath.compile(TOOL_NITPICK_KEY)
diff --git a/src/nitpick/style/core.py b/src/nitpick/style/core.py
index 98c8157..4c04272 100644
--- a/src/nitpick/style/core.py
+++ b/src/nitpick/style/core.py
@@ -115,8 +115,8 @@ class StyleManager:  # pylint: disable=too-many-instance-attributes
                 chosen_styles = [sorted(paths)[0].expanduser().resolve().as_uri()]
                 log_message = "Using local style found climbing the directory tree"
             else:
-                chosen_styles = [self.get_default_style_url()]
-                log_message = "Using default remote Nitpick style"
+                yield Reporter().make_fuss(StyleViolations.NO_STYLE_CONFIGURED)
+                return
             logger.info(f"{log_message}: {chosen_styles[0]}")
 
         yield from self.include_multiple_styles(
diff --git a/src/nitpick/violations.py b/src/nitpick/violations.py
index db91fea..5242e7b 100644
--- a/src/nitpick/violations.py
+++ b/src/nitpick/violations.py
@@ -10,7 +10,7 @@ from typing import TYPE_CHECKING
 
 import click
 
-from nitpick.constants import CONFIG_FILES, FLAKE8_PREFIX, READ_THE_DOCS_URL
+from nitpick.constants import FLAKE8_PREFIX, RUN_NITPICK_INIT_OR_CONFIGURE_STYLE_MANUALLY
 
 if TYPE_CHECKING:
     from nitpick.plugins.info import FileInfo
@@ -68,6 +68,7 @@ class StyleViolations(ViolationEnum):
     INVALID_DATA_TOOL_NITPICK = (1, " has an incorrect style. Invalid data in [{section}]:")
     INVALID_TOML = (1, " has an incorrect style. Invalid TOML{exception}")
     INVALID_CONFIG = (1, " has an incorrect style. Invalid config:")
+    NO_STYLE_CONFIGURED = (4, f"No style file configured.{RUN_NITPICK_INIT_OR_CONFIGURE_STYLE_MANUALLY}")
 
 
 class ProjectViolations(ViolationEnum):
@@ -75,9 +76,7 @@ class ProjectViolations(ViolationEnum):
 
     NO_ROOT_DIR = (
         101,
-        "No root directory detected."
-        f" Create a configuration file ({', '.join(CONFIG_FILES)}) manually, or run 'nitpick init'."
-        f" See {READ_THE_DOCS_URL}configuration.html",
+        f"No root directory detected.{RUN_NITPICK_INIT_OR_CONFIGURE_STYLE_MANUALLY}",
     )
     NO_PYTHON_FILE = (102, "No Python file was found on the root dir and subdir of {root!r}")
     MISSING_FILE = (103, " should exist{extra}")
@@ -141,7 +140,7 @@ class Reporter:  # pylint: disable=too-few-public-methods
         if cls.fixed:
             parts.append(f"✅ {cls.fixed} fixed")
         if cls.manual:
-            parts.append(f"❌ {cls.manual} to change manually")
+            parts.append(f"❌ {cls.manual} to fix manually")
         if not parts:
             return "No violations found. ✨ 🍰 ✨"
         return f"Violations: {', '.join(parts)}."


Test patch:
diff --git a/tests/helpers.py b/tests/helpers.py
index f2c4542..bfab93e 100644
--- a/tests/helpers.py
+++ b/tests/helpers.py
@@ -386,7 +386,7 @@ class ProjectMock:
             return self
 
         if violations:
-            expected.append(f"Violations: ❌ {violations} to change manually.")
+            expected.append(f"Violations: ❌ {violations} to fix manually.")
         elif expected_str_or_lines:
             # If the number of violations was not passed but a list of errors was,
             # remove the violation count from the actual results.
diff --git a/tests/test_project.py b/tests/test_project.py
index 3136890..28aecae 100644
--- a/tests/test_project.py
+++ b/tests/test_project.py
@@ -13,6 +13,7 @@ from nitpick.constants import (
     PACKAGE_JSON,
     PRE_COMMIT_CONFIG_YAML,
     PYPROJECT_TOML,
+    RUN_NITPICK_INIT_OR_CONFIGURE_STYLE_MANUALLY,
     SETUP_CFG,
     SETUP_PY,
     TOX_INI,
@@ -20,7 +21,6 @@ from nitpick.constants import (
 from nitpick.core import Nitpick
 from nitpick.exceptions import QuitComplainingError
 from nitpick.project import Configuration, confirm_project_root, find_main_python_file
-from nitpick.style import StyleManager
 from nitpick.violations import ProjectViolations
 from tests.helpers import ProjectMock
 
@@ -123,12 +123,12 @@ def test_django_project_structure(tmp_path):
 
 
 def test_when_no_config_file_the_default_style_is_requested(tmp_path, caplog):
-    """There is a root dir (setup.py), but no config file."""
-    project = ProjectMock(tmp_path, pyproject_toml=False, setup_py=True).api_check(offline=True)
-    style_url = StyleManager.get_default_style_url()
+    """There is a root dir (setup.py), but no style file. The user should explicitly set the style, no default will be used."""
+    project = ProjectMock(tmp_path, pyproject_toml=False, setup_py=True)
+    error = f"NIP004 No style file configured.{RUN_NITPICK_INIT_OR_CONFIGURE_STYLE_MANUALLY}"
+    project.flake8().assert_single_error(error).cli_run(error, exit_code=1)
     assert project.nitpick_instance.project.read_configuration() == Configuration(None, [], "")
     assert "Config file: none found {}" in caplog.messages
-    assert f"Using default remote Nitpick style: {style_url} {{}}" in caplog.messages
 
 
 @pytest.mark.parametrize("config_file", [DOT_NITPICK_TOML, PYPROJECT_TOML])
diff --git a/tests/test_violations.py b/tests/test_violations.py
index eac967f..198e665 100644
--- a/tests/test_violations.py
+++ b/tests/test_violations.py
@@ -37,12 +37,12 @@ def test_reporter():
     reporter.increment()
     assert reporter.manual == 1
     assert reporter.fixed == 0
-    assert reporter.get_counts() == "Violations: ❌ 1 to change manually."
+    assert reporter.get_counts() == "Violations: ❌ 1 to fix manually."
 
     reporter.increment(True)
     assert reporter.manual == 1
     assert reporter.fixed == 1
-    assert reporter.get_counts() == "Violations: ✅ 1 fixed, ❌ 1 to change manually."
+    assert reporter.get_counts() == "Violations: ✅ 1 fixed, ❌ 1 to fix manually."
 
     reporter.reset()
     assert reporter.manual == 0