diff --git a/certbot/certbot/_internal/cli/__init__.py b/certbot/certbot/_internal/cli/__init__.py index bee3d7745..dcd254e4e 100644 --- a/certbot/certbot/_internal/cli/__init__.py +++ b/certbot/certbot/_internal/cli/__init__.py @@ -162,6 +162,16 @@ def prepare_and_parse_args(plugins: plugins_disco.PluginsRegistry, args: List[st " roll back those changes. It also calls --pre-hook and --post-hook commands" " if they are defined because they may be necessary to accurately simulate" " renewal. --deploy-hook commands are not called.") + helpful.add( + ["testing", "renew", "certonly", "reconfigure"], + "--run-deploy-hook", action="store_true", dest="run_deploy_hook", + default=flag_default("run_deploy_hook"), + help="When performing a test run using `--dry-run` or `reconfigure`, run any applicable" + " deploy hooks. This includes hooks set on the command line, saved in the" + " certificate's renewal configuration file, or present in the renewal-hooks directory." + " To exclude direcory hooks, use --no-directory-hooks. The hook(s) will only" + " be run if the dry run succeeds. This flag is recommended when modifying the deploy" + " hook using `reconfigure`.") helpful.add( ["register", "automation"], "--register-unsafely-without-email", action="store_true", default=flag_default("register_unsafely_without_email"), diff --git a/certbot/certbot/_internal/constants.py b/certbot/certbot/_internal/constants.py index bd999bf6a..d072b0c08 100644 --- a/certbot/certbot/_internal/constants.py +++ b/certbot/certbot/_internal/constants.py @@ -80,6 +80,7 @@ CLI_DEFAULTS: Dict[str, Any] = dict( # noqa eab_hmac_key=None, eab_kid=None, issuance_timeout=90, + run_deploy_hook=False, # Subparsers num=None, diff --git a/certbot/certbot/_internal/hooks.py b/certbot/certbot/_internal/hooks.py index 76cda466a..11ce0902f 100644 --- a/certbot/certbot/_internal/hooks.py +++ b/certbot/certbot/_internal/hooks.py @@ -166,7 +166,7 @@ def deploy_hook(config: configuration.NamespaceConfig, domains: List[str], """ if config.deploy_hook: _run_deploy_hook(config.deploy_hook, domains, - lineage_path, config.dry_run) + lineage_path, config.dry_run, config.run_deploy_hook) def renew_hook(config: configuration.NamespaceConfig, domains: List[str], @@ -190,7 +190,7 @@ def renew_hook(config: configuration.NamespaceConfig, domains: List[str], executed_dir_hooks = set() if config.directory_hooks: for hook in list_hooks(config.renewal_deploy_hooks_dir): - _run_deploy_hook(hook, domains, lineage_path, config.dry_run) + _run_deploy_hook(hook, domains, lineage_path, config.dry_run, config.run_deploy_hook) executed_dir_hooks.add(hook) if config.renew_hook: @@ -199,10 +199,11 @@ def renew_hook(config: configuration.NamespaceConfig, domains: List[str], config.renew_hook) else: _run_deploy_hook(config.renew_hook, domains, - lineage_path, config.dry_run) + lineage_path, config.dry_run, config.run_deploy_hook) -def _run_deploy_hook(command: str, domains: List[str], lineage_path: str, dry_run: bool) -> None: +def _run_deploy_hook(command: str, domains: List[str], lineage_path: str, dry_run: bool, + run_deploy_hook: bool) -> None: """Run the specified deploy-hook (if not doing a dry run). If dry_run is True, command is not run and a message is logged @@ -214,9 +215,10 @@ def _run_deploy_hook(command: str, domains: List[str], lineage_path: str, dry_ru :type domains: `list` of `str` :param str lineage_path: live directory path for the new cert :param bool dry_run: True iff Certbot is doing a dry run + :param bool run_deploy_hook: True if deploy hook should run despite Certbot doing a dry run """ - if dry_run: + if dry_run and not run_deploy_hook: logger.info("Dry run: skipping deploy hook command: %s", command) return diff --git a/certbot/certbot/_internal/main.py b/certbot/certbot/_internal/main.py index 50f0237b2..b71c4fdde 100644 --- a/certbot/certbot/_internal/main.py +++ b/certbot/certbot/_internal/main.py @@ -1737,6 +1737,14 @@ def reconfigure(config: configuration.NamespaceConfig, raise errors.ConfigurationError(f"An existing certificate with name {certname} could not " "be found. Run `certbot certificates` to list available certificates.") + # figure this out before we modify config + if config.deploy_hook and not config.run_deploy_hook: + msg = "You are attempting to set a new deploy hook. Would you like Certbot to run the new " + "hook when it performs a dry run with the new settings? This will run all relevant " + "deploy hooks, including directory hooks, unless --no-directory-hooks is set." + config.run_deploy_hook = display_util.yesno(msg,"Run deploy hook", + "Do not run deploy hook", default=False) + # cache previous version for later comparison try: orig_renewal_conf = configobj.ConfigObj( @@ -1764,10 +1772,6 @@ def reconfigure(config: configuration.NamespaceConfig, _get_and_save_cert(le_client, lineage_config, certname=certname, lineage=renewal_candidate) - domains = renewal_candidate.names() - lineage_config.dry_run = False - hooks.renew_hook(lineage_config, domains, renewal_candidate.live_dir) - # this function will update lineage.configuration with the new values, and save it to disk renewal_candidate.save_new_config_values(lineage_config)