Repository: certbot/certbot

Issue:
"--webroot --webroot-path $root": confusing error message
The following error output is confusing.  It complains that --webroot-path expected one argument, but that argument was provided.  The same error occurs with `--webroot-path=~/public_html` and `--webroot-path ~/public_html`
This seems like either a bug in flag parsing or a very misleading error message.  Setting the same path in a cli.ini file works fine.

```
Updating letsencrypt and virtual environment dependencies.....Requirement already satisfied (use --upgrade to upgrade): ConfigArgParse from git+https://github.com/kuba/ConfigArgParse.git@python2.6-0.9.3#egg=ConfigArgParse in /home/tstone/.local/share/letsencrypt/lib/python2.7/site-packages (from -r py26reqs.txt (line 1))
..
Running with virtualenv: sudo /home/tstone/.local/share/letsencrypt/bin/letsencrypt --agree-dev-preview --server https://acme-v01.api.letsencrypt.org/directory --webroot --webroot-path /home/tstone/public_html -d trevorstone.org -d www.trevorstone.org certonly
usage: 
  letsencrypt [SUBCOMMAND] [options] [-d domain] [-d domain] ...
  <snip>
letsencrypt: error: argument --webroot-path: expected one argument
```



Discussion:
I don't think `--webroot` is implemented, you should use `-a webroot` instead.

However, this error message is extremely confusing and I think it's valid bug.

Related: #892


Resolving patch:
diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py
--- a/letsencrypt/cli.py
+++ b/letsencrypt/cli.py
@@ -69,6 +69,7 @@
   %s
   --standalone      Run a standalone webserver for authentication
   %s
+  --webroot         Place files in a server's webroot folder for authentication
 
 OR use different servers to obtain (authenticate) the cert and then install it:
 
@@ -80,7 +81,7 @@
                         the available topics are:
 
    all, automation, paths, security, testing, or any of the subcommands or
-   plugins (certonly, install, nginx, apache, standalone, etc)
+   plugins (certonly, install, nginx, apache, standalone, webroot, etc)
 """
 
 
@@ -380,7 +381,7 @@ def diagnose_configurator_problem(cfg_type, requested, plugins):
     raise errors.PluginSelectionError(msg)
 
 
-def choose_configurator_plugins(args, config, plugins, verb):
+def choose_configurator_plugins(args, config, plugins, verb): # pylint: disable=too-many-branches
     """
     Figure out which configurator we're going to use
 
@@ -408,6 +409,10 @@ def choose_configurator_plugins(args, config, plugins, verb):
         req_auth = set_configurator(req_auth, "apache")
     if args.standalone:
         req_auth = set_configurator(req_auth, "standalone")
+    if args.webroot:
+        req_auth = set_configurator(req_auth, "webroot")
+    if args.manual:
+        req_auth = set_configurator(req_auth, "manual")
     logger.debug("Requested authenticator %s and installer %s", req_auth, req_inst)
 
     # Try to meet the user's request and/or ask them to pick plugins
@@ -1026,6 +1031,10 @@ def _plugins_parsing(helpful, plugins):
                 help="Obtain and install certs using Nginx")
     helpful.add("plugins", "--standalone", action="store_true",
                 help='Obtain certs using a "standalone" webserver.')
+    helpful.add("plugins", "--manual", action="store_true",
+                help='Provide laborious manual instructions for obtaining a cert')
+    helpful.add("plugins", "--webroot", action="store_true",
+                help='Obtain certs by placing files in a webroot directory.')
 
     # things should not be reorder past/pre this comment:
     # plugins_group should be displayed in --help before plugin


Test patch:
diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py
--- a/letsencrypt/tests/cli_test.py
+++ b/letsencrypt/tests/cli_test.py
@@ -20,6 +20,7 @@
 from letsencrypt import le_util
 
 from letsencrypt.plugins import disco
+from letsencrypt.plugins import manual
 
 from letsencrypt.tests import renewer_test
 from letsencrypt.tests import test_util
@@ -201,6 +202,16 @@ def test_configurator_selection(self, mock_exe_exists):
             self.assertTrue("Could not find configuration root" in ret)
             self.assertTrue("NoInstallationError" in ret)
 
+        args = ["certonly", "--webroot"]
+        ret, _, _, _ = self._call(args)
+        self.assertTrue("--webroot-path must be set" in ret)
+
+        with mock.patch("letsencrypt.cli._init_le_client") as mock_init:
+            with mock.patch("letsencrypt.cli._auth_from_domains"):
+                self._call(["certonly", "--manual", "-d", "foo.bar"])
+                auth = mock_init.call_args[0][2]
+                self.assertTrue(isinstance(auth, manual.Authenticator))
+
         with MockedVerb("certonly") as mock_certonly:
             self._call(["auth", "--standalone"])
             self.assertEqual(1, mock_certonly.call_count)