diff --git a/Dockerfile-dev b/Dockerfile-dev index e4a22bea7..56e2ec05b 100644 --- a/Dockerfile-dev +++ b/Dockerfile-dev @@ -58,7 +58,7 @@ RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv && \ -e /opt/letsencrypt/src/letsencrypt-nginx \ -e /opt/letsencrypt/src/letshelp-letsencrypt \ -e /opt/letsencrypt/src/letsencrypt-compatibility-test \ - -e /opt/letsencrypt/src[dev,docs,testing] + -e /opt/letsencrypt/src[dev,docs] # install in editable mode (-e) to save space: it's not possible to # "rm -rf /opt/letsencrypt/src" (it's stays in the underlaying image); diff --git a/README.rst b/README.rst index 57908e90f..522400a1f 100644 --- a/README.rst +++ b/README.rst @@ -51,11 +51,11 @@ client will guide you through the process of obtaining and installing certs interactively. You can also tell it exactly what you want it to do from the command line. -For instance, if you want to obtain a cert for ``thing.com``, -``www.thing.com``, and ``otherthing.net``, using the Apache plugin to both +For instance, if you want to obtain a cert for ``example.com``, +``www.example.com``, and ``other.example.net``, using the Apache plugin to both obtain and install the certs, you could do this:: - ./letsencrypt-auto --apache -d thing.com -d www.thing.com -d otherthing.net + ./letsencrypt-auto --apache -d example.com -d www.example.com -d other.example.net (The first time you run the command, it will make an account, and ask for an email and agreement to the Let's Encrypt Subscriber Agreement; you can @@ -64,7 +64,7 @@ automate those with ``--email`` and ``--agree-tos``) If you want to use a webserver that doesn't have full plugin support yet, you can still use "standalone" or "webroot" plugins to obtain a certificate:: - ./letsencrypt-auto certonly --standalone --email admin@thing.com -d thing.com -d www.thing.com -d otherthing.net + ./letsencrypt-auto certonly --standalone --email admin@example.com -d example.com -d www.example.com -d other.example.net Understanding the client in more depth diff --git a/acme/acme/client.py b/acme/acme/client.py index 478536ecc..0c0d75b05 100644 --- a/acme/acme/client.py +++ b/acme/acme/client.py @@ -180,40 +180,41 @@ class Client(object): # pylint: disable=too-many-instance-attributes raise errors.UnexpectedUpdate(authzr) return authzr - def request_challenges(self, identifier, new_authzr_uri): + def request_challenges(self, identifier, new_authzr_uri=None): """Request challenges. - :param identifier: Identifier to be challenged. - :type identifier: `.messages.Identifier` - - :param str new_authzr_uri: new-authorization URI + :param .messages.Identifier identifier: Identifier to be challenged. + :param str new_authzr_uri: ``new-authorization`` URI. If omitted, + will default to value found in ``directory``. :returns: Authorization Resource. :rtype: `.AuthorizationResource` """ new_authz = messages.NewAuthorization(identifier=identifier) - response = self.net.post(new_authzr_uri, new_authz) + response = self.net.post(self.directory.new_authz + if new_authzr_uri is None else new_authzr_uri, + new_authz) # TODO: handle errors assert response.status_code == http_client.CREATED return self._authzr_from_response(response, identifier) - def request_domain_challenges(self, domain, new_authz_uri): + def request_domain_challenges(self, domain, new_authzr_uri=None): """Request challenges for domain names. This is simply a convenience function that wraps around `request_challenges`, but works with domain names instead of - generic identifiers. + generic identifiers. See ``request_challenges`` for more + documentation. :param str domain: Domain name to be challenged. - :param str new_authzr_uri: new-authorization URI :returns: Authorization Resource. :rtype: `.AuthorizationResource` """ return self.request_challenges(messages.Identifier( - typ=messages.IDENTIFIER_FQDN, value=domain), new_authz_uri) + typ=messages.IDENTIFIER_FQDN, value=domain), new_authzr_uri) def answer_challenge(self, challb, response): """Answer challenge. diff --git a/acme/acme/client_test.py b/acme/acme/client_test.py index 9abc69c7c..822a1be5f 100644 --- a/acme/acme/client_test.py +++ b/acme/acme/client_test.py @@ -38,6 +38,8 @@ class ClientTest(unittest.TestCase): 'https://www.letsencrypt-demo.org/acme/new-reg', messages.Revocation: 'https://www.letsencrypt-demo.org/acme/revoke-cert', + messages.NewAuthorization: + 'https://www.letsencrypt-demo.org/acme/new-authz', }) from acme.client import Client @@ -142,7 +144,7 @@ class ClientTest(unittest.TestCase): regr = self.client.update_registration.call_args[0][0] self.assertEqual(self.regr.terms_of_service, regr.body.agreement) - def test_request_challenges(self): + def _prepare_response_for_request_challenges(self): self.response.status_code = http_client.CREATED self.response.headers['Location'] = self.authzr.uri self.response.json.return_value = self.authz.to_json() @@ -150,10 +152,20 @@ class ClientTest(unittest.TestCase): 'next': {'url': self.authzr.new_cert_uri}, } - self.client.request_challenges(self.identifier, self.authzr.uri) - # TODO: test POST call arguments + def test_request_challenges(self): + self._prepare_response_for_request_challenges() + self.client.request_challenges(self.identifier) + self.net.post.assert_called_once_with( + self.directory.new_authz, + messages.NewAuthorization(identifier=self.identifier)) - # TODO: split here and separate test + def test_requets_challenges_custom_uri(self): + self._prepare_response_for_request_challenges() + self.client.request_challenges(self.identifier, 'URI') + self.net.post.assert_called_once_with('URI', mock.ANY) + + def test_request_challenges_unexpected_update(self): + self._prepare_response_for_request_challenges() self.response.json.return_value = self.authz.update( identifier=self.identifier.update(value='foo')).to_json() self.assertRaises( @@ -162,15 +174,20 @@ class ClientTest(unittest.TestCase): def test_request_challenges_missing_next(self): self.response.status_code = http_client.CREATED - self.assertRaises( - errors.ClientError, self.client.request_challenges, - self.identifier, self.regr) + self.assertRaises(errors.ClientError, self.client.request_challenges, + self.identifier) def test_request_domain_challenges(self): self.client.request_challenges = mock.MagicMock() self.assertEqual( self.client.request_challenges(self.identifier), - self.client.request_domain_challenges('example.com', self.regr)) + self.client.request_domain_challenges('example.com')) + + def test_request_domain_challenges_custom_uri(self): + self.client.request_challenges = mock.MagicMock() + self.assertEqual( + self.client.request_challenges(self.identifier, 'URI'), + self.client.request_domain_challenges('example.com', 'URI')) def test_answer_challenge(self): self.response.links['up'] = {'url': self.challr.authzr_uri} diff --git a/acme/setup.py b/acme/setup.py index 8b7b040e5..0e1af6ad3 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -34,17 +34,18 @@ if sys.version_info < (2, 7): else: install_requires.append('mock') +dev_extras = [ + 'nose', + 'pep8', + 'tox', +] + docs_extras = [ 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags 'sphinx_rtd_theme', 'sphinxcontrib-programoutput', ] -testing_extras = [ - 'nose', - 'tox', -] - setup( name='acme', @@ -74,8 +75,8 @@ setup( include_package_data=True, install_requires=install_requires, extras_require={ + 'dev': dev_extras, 'docs': docs_extras, - 'testing': testing_extras, }, entry_points={ 'console_scripts': [ diff --git a/docs/using.rst b/docs/using.rst index 7263452b5..37fca2c57 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -111,11 +111,11 @@ potentially be a separate directory for each domain. When requested a certificate for multiple domains, each domain will use the most recently specified ``--webroot-path``. So, for instance, -``letsencrypt certonly --webroot -w /var/www/example/ -d www.example.com -d example.com -w /var/www/eg -d eg.is -d www.eg.is`` +``letsencrypt certonly --webroot -w /var/www/example/ -d www.example.com -d example.com -w /var/www/other -d other.example.net -d another.other.example.net`` would obtain a single certificate for all of those names, using the ``/var/www/example`` webroot directory for the first two, and -``/var/www/eg`` for the second two. +``/var/www/other`` for the second two. The webroot plugin works by creating a temporary file for each of your requested domains in ``${webroot-path}/.well-known/acme-challenge``. Then the Let's @@ -130,7 +130,7 @@ made to your web server would look like: Note that to use the webroot plugin, your server must be configured to serve files from hidden directories. If ``/.well-known`` is treated specially by your webserver configuration, you might need to modify the configuration -to ensure that files inside ``/.well-known/ache-challenge`` are served by +to ensure that files inside ``/.well-known/acme-challenge`` are served by the webserver. Standalone @@ -439,8 +439,8 @@ want to use the Apache plugin, it has to be installed separately: emerge -av app-crypt/letsencrypt emerge -av app-crypt/letsencrypt-apache -Currently, only the Apache plugin is included in Portage. However, if you -want the nginx plugin, you can use Layman to add the mrueg overlay which +Currently, only the Apache plugin is included in Portage. However, if you +want the nginx plugin, you can use Layman to add the mrueg overlay which does include the nginx plugin package: .. code-block:: shell @@ -450,9 +450,9 @@ does include the nginx plugin package: layman -a mrueg emerge -av app-crypt/letsencrypt-nginx -When using the Apache plugin, you will run into a "cannot find a cert or key +When using the Apache plugin, you will run into a "cannot find a cert or key directive" error if you're sporting the default Gentoo ``httpd.conf``. -You can fix this by commenting out two lines in ``/etc/apache2/httpd.conf`` +You can fix this by commenting out two lines in ``/etc/apache2/httpd.conf`` as follows: Change @@ -471,7 +471,7 @@ to LoadModule ssl_module modules/mod_ssl.so # -For the time being, this is the only way for the Apache plugin to recognise +For the time being, this is the only way for the Apache plugin to recognise the appropriate directives when installing the certificate. Note: this change is not required for the other plugins. diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 30d907690..0630c649a 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -18,25 +18,31 @@ set -e # Work even if somebody does "sh thisscript.sh". XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share} VENV_NAME="letsencrypt" VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} -VENV_BIN=${VENV_PATH}/bin +VENV_BIN="$VENV_PATH/bin" LE_AUTO_VERSION="0.5.0.dev0" # This script takes the same arguments as the main letsencrypt program, but it # additionally responds to --verbose (more output) and --debug (allow support # for experimental platforms) for arg in "$@" ; do - # This first clause is redundant with the third, but hedging on portability - if [ "$arg" = "-v" ] || [ "$arg" = "--verbose" ] || echo "$arg" | grep -E -- "-v+$" ; then - VERBOSE=1 - elif [ "$arg" = "--no-self-upgrade" ] ; then - # Do not upgrade this script (also prevents client upgrades, because each - # copy of the script pins a hash of the python client) - NO_SELF_UPGRADE=1 - elif [ "$arg" = "--os-packages-only" ] ; then - OS_PACKAGES_ONLY=1 - elif [ "$arg" = "--debug" ]; then - DEBUG=1 - fi + case "$arg" in + --debug) + DEBUG=1;; + --os-packages-only) + OS_PACKAGES_ONLY=1;; + --no-self-upgrade) + # Do not upgrade this script (also prevents client upgrades, because each + # copy of the script pins a hash of the python client) + NO_SELF_UPGRADE=1;; + --verbose) + VERBOSE=1;; + [!-]*|-*[!v]*|-) + # Anything that isn't -v, -vv, etc.: that is, anything that does not + # start with a -, contains anything that's not a v, or is just "-" + ;; + *) # -v+ remains. + VERBOSE=1;; + esac done # letsencrypt-auto needs root access to bootstrap OS dependencies, and @@ -91,21 +97,18 @@ ExperimentalBootstrap() { } DeterminePythonVersion() { - if command -v python2.7 > /dev/null ; then - export LE_PYTHON=${LE_PYTHON:-python2.7} - elif command -v python27 > /dev/null ; then - export LE_PYTHON=${LE_PYTHON:-python27} - elif command -v python2 > /dev/null ; then - export LE_PYTHON=${LE_PYTHON:-python2} - elif command -v python > /dev/null ; then - export LE_PYTHON=${LE_PYTHON:-python} - else - echo "Cannot find any Pythons... please install one!" + for LE_PYTHON in "$LE_PYTHON" python2.7 python27 python2 python; do + # Break (while keeping the LE_PYTHON value) if found. + command -v "$LE_PYTHON" > /dev/null && break + done + if [ "$?" != "0" ]; then + echo "Cannot find any Pythons; please install one!" exit 1 fi + export LE_PYTHON - PYVER=`"$LE_PYTHON" --version 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` - if [ $PYVER -lt 26 ]; then + PYVER=`"$LE_PYTHON" -V 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` + if [ "$PYVER" -lt 26 ]; then echo "You have an ancient version of Python entombed in your operating system..." echo "This isn't going to work; you'll need at least version 2.6." exit 1 @@ -324,13 +327,13 @@ BootstrapGentooCommon() { case "$PACKAGE_MANAGER" in (paludis) - "$SUDO" cave resolve --keep-targets if-possible $PACKAGES -x + "$SUDO" cave resolve --preserve-world --keep-targets if-possible $PACKAGES -x ;; (pkgcore) - "$SUDO" pmerge --noreplace $PACKAGES + "$SUDO" pmerge --noreplace --oneshot $PACKAGES ;; (portage|*) - "$SUDO" emerge --noreplace $PACKAGES + "$SUDO" emerge --noreplace --oneshot $PACKAGES ;; esac } @@ -345,20 +348,27 @@ BootstrapFreeBsd() { BootstrapMac() { if ! hash brew 2>/dev/null; then - echo "Homebrew Not Installed\nDownloading..." + echo "Homebrew not installed.\nDownloading..." ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" fi - brew install augeas - brew install dialog + if [ -z "$(brew list --versions augeas)" ]; then + echo "augeas not installed.\nInstalling augeas from Homebrew..." + brew install augeas + fi + + if [ -z "$(brew list --versions dialog)" ]; then + echo "dialog not installed.\nInstalling dialog from Homebrew..." + brew install dialog + fi if ! hash pip 2>/dev/null; then - echo "pip Not Installed\nInstalling python from Homebrew..." + echo "pip not installed.\nInstalling python from Homebrew..." brew install python fi if ! hash virtualenv 2>/dev/null; then - echo "virtualenv Not Installed\nInstalling with pip" + echo "virtualenv not installed.\nInstalling with pip..." pip install virtualenv fi } diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index 1fa12528f..22dfba1b2 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -18,25 +18,31 @@ set -e # Work even if somebody does "sh thisscript.sh". XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share} VENV_NAME="letsencrypt" VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} -VENV_BIN=${VENV_PATH}/bin +VENV_BIN="$VENV_PATH/bin" LE_AUTO_VERSION="{{ LE_AUTO_VERSION }}" # This script takes the same arguments as the main letsencrypt program, but it # additionally responds to --verbose (more output) and --debug (allow support # for experimental platforms) for arg in "$@" ; do - # This first clause is redundant with the third, but hedging on portability - if [ "$arg" = "-v" ] || [ "$arg" = "--verbose" ] || echo "$arg" | grep -E -- "-v+$" ; then - VERBOSE=1 - elif [ "$arg" = "--no-self-upgrade" ] ; then - # Do not upgrade this script (also prevents client upgrades, because each - # copy of the script pins a hash of the python client) - NO_SELF_UPGRADE=1 - elif [ "$arg" = "--os-packages-only" ] ; then - OS_PACKAGES_ONLY=1 - elif [ "$arg" = "--debug" ]; then - DEBUG=1 - fi + case "$arg" in + --debug) + DEBUG=1;; + --os-packages-only) + OS_PACKAGES_ONLY=1;; + --no-self-upgrade) + # Do not upgrade this script (also prevents client upgrades, because each + # copy of the script pins a hash of the python client) + NO_SELF_UPGRADE=1;; + --verbose) + VERBOSE=1;; + [!-]*|-*[!v]*|-) + # Anything that isn't -v, -vv, etc.: that is, anything that does not + # start with a -, contains anything that's not a v, or is just "-" + ;; + *) # -v+ remains. + VERBOSE=1;; + esac done # letsencrypt-auto needs root access to bootstrap OS dependencies, and @@ -91,21 +97,18 @@ ExperimentalBootstrap() { } DeterminePythonVersion() { - if command -v python2.7 > /dev/null ; then - export LE_PYTHON=${LE_PYTHON:-python2.7} - elif command -v python27 > /dev/null ; then - export LE_PYTHON=${LE_PYTHON:-python27} - elif command -v python2 > /dev/null ; then - export LE_PYTHON=${LE_PYTHON:-python2} - elif command -v python > /dev/null ; then - export LE_PYTHON=${LE_PYTHON:-python} - else - echo "Cannot find any Pythons... please install one!" + for LE_PYTHON in "$LE_PYTHON" python2.7 python27 python2 python; do + # Break (while keeping the LE_PYTHON value) if found. + command -v "$LE_PYTHON" > /dev/null && break + done + if [ "$?" != "0" ]; then + echo "Cannot find any Pythons; please install one!" exit 1 fi + export LE_PYTHON - PYVER=`"$LE_PYTHON" --version 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` - if [ $PYVER -lt 26 ]; then + PYVER=`"$LE_PYTHON" -V 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` + if [ "$PYVER" -lt 26 ]; then echo "You have an ancient version of Python entombed in your operating system..." echo "This isn't going to work; you'll need at least version 2.6." exit 1 diff --git a/letsencrypt-auto-source/pieces/bootstrappers/mac.sh b/letsencrypt-auto-source/pieces/bootstrappers/mac.sh index 9318d18c8..23c12eec3 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/mac.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/mac.sh @@ -1,19 +1,26 @@ BootstrapMac() { if ! hash brew 2>/dev/null; then - echo "Homebrew Not Installed\nDownloading..." + echo "Homebrew not installed.\nDownloading..." ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" fi - brew install augeas - brew install dialog + if [ -z "$(brew list --versions augeas)" ]; then + echo "augeas not installed.\nInstalling augeas from Homebrew..." + brew install augeas + fi + + if [ -z "$(brew list --versions dialog)" ]; then + echo "dialog not installed.\nInstalling dialog from Homebrew..." + brew install dialog + fi if ! hash pip 2>/dev/null; then - echo "pip Not Installed\nInstalling python from Homebrew..." + echo "pip not installed.\nInstalling python from Homebrew..." brew install python fi if ! hash virtualenv 2>/dev/null; then - echo "virtualenv Not Installed\nInstalling with pip" + echo "virtualenv not installed.\nInstalling with pip..." pip install virtualenv fi } diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index d5811538e..855c7a467 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -319,12 +319,11 @@ def _handle_identical_cert_request(config, cert): elif config.verb == "certonly": keep_opt = "Keep the existing certificate for now" choices = [keep_opt, - "Renew & replace the cert (limit ~5 per 7 days)", - "Cancel this operation and do nothing"] + "Renew & replace the cert (limit ~5 per 7 days)"] display = zope.component.getUtility(interfaces.IDisplay) response = display.menu(question, choices, "OK", "Cancel", default=0) - if response[0] == "cancel" or response[1] == 2: + if response[0] == display_util.CANCEL: # TODO: Add notification related to command-line options for # skipping the menu for this case. raise errors.Error( diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 6f7f54646..6786ac745 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -112,6 +112,21 @@ def update_configuration(lineagename, target, cli_config): return configobj.ConfigObj(config_filename) +def get_link_target(link): + """Get an absolute path to the target of link. + + :param str link: Path to a symbolic link + + :returns: Absolute path to the target of link + :rtype: str + + """ + target = os.readlink(link) + if not os.path.isabs(target): + target = os.path.join(os.path.dirname(link), target) + return os.path.abspath(target) + + class RenewableCert(object): # pylint: disable=too-many-instance-attributes """Renewable certificate. @@ -194,13 +209,15 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes def _check_symlinks(self): """Raises an exception if a symlink doesn't exist""" - def check(link): - """Checks if symlink points to a file that exists""" - return os.path.exists(os.path.realpath(link)) for kind in ALL_FOUR: - if not check(getattr(self, kind)): + link = getattr(self, kind) + if not os.path.islink(link): raise errors.CertStorageError( - "link: {0} does not exist".format(getattr(self, kind))) + "expected {0} to be a symlink".format(link)) + target = get_link_target(link) + if not os.path.exists(target): + raise errors.CertStorageError("target {0} of symlink {1} does " + "not exist".format(target, link)) def _consistent(self): """Are the files associated with this lineage self-consistent? @@ -225,10 +242,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes return False for kind in ALL_FOUR: link = getattr(self, kind) - where = os.path.dirname(link) - target = os.readlink(link) - if not os.path.isabs(target): - target = os.path.join(where, target) + target = get_link_target(link) # Each element's link must point within the cert lineage's # directory within the official archive directory @@ -343,10 +357,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes logger.debug("Expected symlink %s for %s does not exist.", link, kind) return None - target = os.readlink(link) - if not os.path.isabs(target): - target = os.path.join(os.path.dirname(link), target) - return os.path.abspath(target) + return get_link_target(link) def current_version(self, kind): """Returns numerical version of the specified item. diff --git a/letsencrypt/tests/storage_test.py b/letsencrypt/tests/storage_test.py index 9d402089c..071d6d7bb 100644 --- a/letsencrypt/tests/storage_test.py +++ b/letsencrypt/tests/storage_test.py @@ -687,6 +687,10 @@ class RenewableCertTests(BaseRenewableCertTest): self.assertRaises(errors.CertStorageError, storage.RenewableCert, self.config.filename, self.cli_config) + os.symlink("missing", self.config[ALL_FOUR[0]]) + self.assertRaises(errors.CertStorageError, + storage.RenewableCert, + self.config.filename, self.cli_config) if __name__ == "__main__": diff --git a/setup.cfg b/setup.cfg index ca4c1b1ca..1ea06661e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,9 +1,6 @@ [easy_install] zip_ok = false -[aliases] -dev = develop easy_install letsencrypt[dev,docs,testing] - [nosetests] nocapture=1 cover-package=letsencrypt,acme,letsencrypt_apache,letsencrypt_nginx diff --git a/setup.py b/setup.py index b094e2d04..cbf0ff89d 100644 --- a/setup.py +++ b/setup.py @@ -65,7 +65,12 @@ else: dev_extras = [ # Pin astroid==1.3.5, pylint==1.4.2 as a workaround for #289 'astroid==1.3.5', + 'coverage', + 'nose', + 'nosexcover', + 'pep8', 'pylint==1.4.2', # upstream #248 + 'tox', 'twine', 'wheel', ] @@ -77,14 +82,6 @@ docs_extras = [ 'sphinxcontrib-programoutput', ] -testing_extras = [ - 'coverage', - 'nose', - 'nosexcover', - 'pep8', - 'tox', -] - setup( name='letsencrypt', version=version, @@ -120,7 +117,6 @@ setup( extras_require={ 'dev': dev_extras, 'docs': docs_extras, - 'testing': testing_extras, }, # to test all packages run "python setup.py test -s diff --git a/tools/venv.sh b/tools/venv.sh index 5a09efb0b..73c3bb110 100755 --- a/tools/venv.sh +++ b/tools/venv.sh @@ -4,8 +4,8 @@ export VENV_ARGS="--python python2" ./tools/_venv_common.sh \ - -e acme[testing] \ - -e .[dev,docs,testing] \ + -e acme[dev] \ + -e .[dev,docs] \ -e letsencrypt-apache \ -e letsencrypt-nginx \ -e letshelp-letsencrypt \ diff --git a/tools/venv3.sh b/tools/venv3.sh index 158605f72..645ed0d47 100755 --- a/tools/venv3.sh +++ b/tools/venv3.sh @@ -5,4 +5,4 @@ export VENV_NAME="${VENV_NAME:-venv3}" export VENV_ARGS="--python python3" ./tools/_venv_common.sh \ - -e acme[testing] \ + -e acme[dev] \ diff --git a/tox.ini b/tox.ini index c54c9934c..57359cd86 100644 --- a/tox.ini +++ b/tox.ini @@ -15,9 +15,9 @@ envlist = py{26,27,33,34,35},py{26,27}-oldest,cover,lint # packages installed separately to ensure that downstream deps problems # are detected, c.f. #1002 commands = - pip install -e acme[testing] + pip install -e acme[dev] nosetests -v acme - pip install -e .[testing] + pip install -e .[dev] nosetests -v letsencrypt pip install -e letsencrypt-apache nosetests -v letsencrypt_apache @@ -40,23 +40,23 @@ deps = [testenv:py33] commands = - pip install -e acme[testing] + pip install -e acme[dev] nosetests -v acme [testenv:py34] commands = - pip install -e acme[testing] + pip install -e acme[dev] nosetests -v acme [testenv:py35] commands = - pip install -e acme[testing] + pip install -e acme[dev] nosetests -v acme [testenv:cover] basepython = python2.7 commands = - pip install -e acme -e .[testing] -e letsencrypt-apache -e letsencrypt-nginx -e letshelp-letsencrypt + pip install -e acme[dev] -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letshelp-letsencrypt ./tox.cover.sh [testenv:lint] @@ -66,7 +66,7 @@ basepython = python2.7 # duplicate code checking; if one of the commands fails, others will # continue, but tox return code will reflect previous error commands = - pip install -e acme -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt + pip install -e acme[dev] -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt ./pep8.travis.sh pylint --rcfile=.pylintrc letsencrypt pylint --rcfile=acme/.pylintrc acme/acme