From baa9d96082b738fa8aedd5ddc37b695effa80f8c Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 21 Jan 2013 15:27:42 -0800 Subject: [PATCH] Increase the power of `bin/config` Summary: Fixes T2254. Make the CLI for config more powerful: - Add validation for `set`. - Add `get`. - Add `list`. - Add `delete`. The `get` command produces fairly verbose JSON to support flags like `--all`, or `--source database` later. The other commands are straightforward. Test Plan: Tested `config set`: $ ./bin/config set Usage Exception: Specify a configuration key and a value to set it to. $ ./bin/config set x Usage Exception: Specify a value to set the key 'x' to. $ ./bin/config set phabricator.base-uri Usage Exception: Specify a value to set the key 'phabricator.base-uri' to. $ ./bin/config set phabricator.base-uri x Usage Exception: Config option 'phabricator.base-uri' is invalid. The URI must start with 'http://' or 'https://'. $ ./bin/config set phabricator.base-uri http://x Usage Exception: Config option 'phabricator.base-uri' is invalid. The URI must contain a dot ('.'), like 'http://example.com/', not just a bare name like 'http://example/'. Some web browsers will not set cookies on domains with no TLD. $ ./bin/config set phabricator.base-uri http://x.com Set 'phabricator.base-uri' in local configuration. $ Tested `config get`: $ ./bin/config get pygments.enabled { "config" : [] } $ ./bin/config set pygments.enabled true Set 'pygments.enabled' in local configuration. $ ./bin/config get pygments.enabled { "config" : [ { "key" : "pygments.enabled", "source" : "local", "value" : true } ] } $ Tested `config delete`: $ ./bin/config delete Usage Exception: Specify a configuration key to delete. $ ./bin/config delete x x Usage Exception: Too many arguments: expected one key. $ ./bin/config delete x Usage Exception: No such configuration key 'x'! Use `config list` to list all keys. $ ./bin/config delete pygments.enabled Deleted 'pygments.enabled' from local configuration. $ ./bin/config delete pygments.enabled Usage Exception: Configuration key 'pygments.enabled' is not set in local configuration! $ Tested `config list`: $ ./bin/config list account.editable account.minimum-password-length amazon-ec2.access-key amazon-ec2.secret-key amazon-s3.access-key amazon-s3.endpoint amazon-s3.secret-key amazon-ses.access-key amazon-ses.secret-key aphront.default-application-configuration-class audit.can-author-close-audit auth.email-domains auth.login-message auth.password-auth-enabled auth.require-email-verification auth.sessions.conduit auth.sessions.web auth.sshkeys.enabled cache.enable-deflate celerity.force-disk-reads celerity.minify celerity.resource-hash celerity.resource-path config.hide config.lock config.mask controller.oauth-registration darkconsole.always-on darkconsole.enabled debug.profile-rate debug.stop-on-redirect differential.allow-reopen differential.allow-self-accept differential.always-allow-close differential.anonymous-access differential.custom-remarkup-block-rules differential.custom-remarkup-rules differential.days-fresh differential.days-stale differential.enable-email-accept differential.expose-emails-prudently differential.field-selector differential.generated-paths differential.require-test-plan-field differential.revision-custom-detail-renderer differential.show-host-field differential.show-test-plan-field differential.whitespace-matters disqus.application-id disqus.application-secret disqus.auth-enabled disqus.auth-permanent disqus.registration-enabled disqus.shortname environment.append-paths events.listeners facebook.application-id facebook.application-secret facebook.auth-enabled facebook.auth-permanent facebook.registration-enabled facebook.require-https-auth feed.http-hooks feed.public files.image-mime-types files.viewable-mime-types gcdaemon.ttl.daemon-logs gcdaemon.ttl.differential-parse-cache gcdaemon.ttl.general-cache gcdaemon.ttl.herald-transcripts gcdaemon.ttl.markup-cache gcdaemon.ttl.task-archive github.application-id github.application-secret github.auth-enabled github.auth-permanent github.registration-enabled google.application-id google.application-secret google.auth-enabled google.auth-permanent google.registration-enabled ldap.activedirectory_domain ldap.anonymous-user-name ldap.anonymous-user-password ldap.auth-enabled ldap.base_dn ldap.hostname ldap.port ldap.real_name_attributes ldap.referrals ldap.search-first ldap.search_attribute ldap.start-tls ldap.username-attribute ldap.version load-libraries log.access.format log.access.path maniphest.custom-fields maniphest.custom-task-extensions-class maniphest.default-priority maniphest.enabled metamta.can-send-as-user metamta.default-address metamta.differential.attach-patches metamta.differential.inline-patches metamta.differential.patch-format metamta.differential.reply-handler metamta.differential.reply-handler-domain metamta.differential.subject-prefix metamta.differential.unified-comment-context metamta.diffusion.attach-patches metamta.diffusion.byte-limit metamta.diffusion.inline-patches metamta.diffusion.reply-handler metamta.diffusion.reply-handler-domain metamta.diffusion.subject-prefix metamta.diffusion.time-limit metamta.domain metamta.herald.show-hints metamta.insecure-auth-with-reply-to metamta.macro.reply-handler-domain metamta.macro.subject-prefix metamta.mail-adapter metamta.maniphest.default-public-author metamta.maniphest.public-create-email metamta.maniphest.reply-handler metamta.maniphest.reply-handler-domain metamta.maniphest.subject-prefix metamta.one-mail-per-recipient metamta.package.reply-handler metamta.package.subject-prefix metamta.pholio.reply-handler-domain metamta.pholio.subject-prefix metamta.placeholder-to-recipient metamta.precedence-bulk metamta.public-replies metamta.re-prefix metamta.recipients.show-hints metamta.reply.show-hints metamta.send-immediately metamta.single-reply-handler-prefix metamta.user-address-format metamta.vary-subjects mysql.configuration-provider mysql.host mysql.implementation mysql.pass mysql.user notification.client-uri notification.debug notification.enabled notification.log notification.pidfile notification.server-uri notification.user phabricator.application-id phabricator.application-secret phabricator.auth-enabled phabricator.auth-permanent phabricator.base-uri phabricator.csrf-key phabricator.env phabricator.mail-key phabricator.oauth-uri phabricator.production-uri phabricator.registration-enabled phabricator.serious-business phabricator.setup phabricator.show-beta-applications phabricator.show-error-callout phabricator.show-stack-traces phabricator.timezone phame.skins phd.log-directory phd.pid-directory phd.start-taskmasters phd.trace phd.verbose phid.external-loaders phpmailer.mailer phpmailer.smtp-host phpmailer.smtp-password phpmailer.smtp-port phpmailer.smtp-protocol phpmailer.smtp-user phriction.enabled policy.allow-public pygments.dropdown-choices pygments.enabled recaptcha.enabled recaptcha.private-key recaptcha.public-key remarkup.enable-embedded-youtube repository.default-local-path search.elastic.host search.engine-selector security.alternate-file-domain security.hmac-key security.require-https sendgrid.api-key sendgrid.api-user storage.default-namespace storage.engine-selector storage.local-disk.path storage.mysql-engine.max-size storage.s3.bucket storage.upload-size-limit style.monospace syntax-highlighter.engine syntax.filemap test.value tokenizer.ondemand translation.override translation.provider uri.allowed-protocols $ Reviewers: btrahan, codeblock Reviewed By: btrahan CC: aran Maniphest Tasks: T2254 Differential Revision: https://secure.phabricator.com/D4570 --- scripts/setup/manage_config.php | 3 + src/__phutil_library_map__.php | 10 +- ...bricatorConfigManagementDeleteWorkflow.php | 56 +++++++++++ ...PhabricatorConfigManagementGetWorkflow.php | 62 ++++++++++++ ...habricatorConfigManagementListWorkflow.php | 25 +++++ ...PhabricatorConfigManagementSetWorkflow.php | 99 +++++++++++++++++++ .../PhabricatorConfigManagementWorkflow.php | 0 .../env/PhabricatorConfigProxySource.php | 2 +- ...PhabricatorConfigManagementSetWorkflow.php | 50 ---------- 9 files changed, 254 insertions(+), 53 deletions(-) create mode 100644 src/applications/config/management/PhabricatorConfigManagementDeleteWorkflow.php create mode 100644 src/applications/config/management/PhabricatorConfigManagementGetWorkflow.php create mode 100644 src/applications/config/management/PhabricatorConfigManagementListWorkflow.php create mode 100644 src/applications/config/management/PhabricatorConfigManagementSetWorkflow.php rename src/{infrastructure/env => applications/config}/management/PhabricatorConfigManagementWorkflow.php (100%) delete mode 100644 src/infrastructure/env/management/PhabricatorConfigManagementSetWorkflow.php diff --git a/scripts/setup/manage_config.php b/scripts/setup/manage_config.php index 7e649a7ac1..1610ebd257 100755 --- a/scripts/setup/manage_config.php +++ b/scripts/setup/manage_config.php @@ -15,7 +15,10 @@ EOSYNOPSIS $args->parseStandardArguments(); $workflows = array( + new PhabricatorConfigManagementListWorkflow(), new PhabricatorConfigManagementSetWorkflow(), + new PhabricatorConfigManagementGetWorkflow(), + new PhabricatorConfigManagementDeleteWorkflow(), new PhutilHelpArgumentWorkflow(), ); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index e782b4f2ba..055cc010ed 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -719,8 +719,11 @@ phutil_register_library_map(array( 'PhabricatorConfigJSON' => 'applications/config/json/PhabricatorConfigJSON.php', 'PhabricatorConfigListController' => 'applications/config/controller/PhabricatorConfigListController.php', 'PhabricatorConfigLocalSource' => 'infrastructure/env/PhabricatorConfigLocalSource.php', - 'PhabricatorConfigManagementSetWorkflow' => 'infrastructure/env/management/PhabricatorConfigManagementSetWorkflow.php', - 'PhabricatorConfigManagementWorkflow' => 'infrastructure/env/management/PhabricatorConfigManagementWorkflow.php', + 'PhabricatorConfigManagementDeleteWorkflow' => 'applications/config/management/PhabricatorConfigManagementDeleteWorkflow.php', + 'PhabricatorConfigManagementGetWorkflow' => 'applications/config/management/PhabricatorConfigManagementGetWorkflow.php', + 'PhabricatorConfigManagementListWorkflow' => 'applications/config/management/PhabricatorConfigManagementListWorkflow.php', + 'PhabricatorConfigManagementSetWorkflow' => 'applications/config/management/PhabricatorConfigManagementSetWorkflow.php', + 'PhabricatorConfigManagementWorkflow' => 'applications/config/management/PhabricatorConfigManagementWorkflow.php', 'PhabricatorConfigOption' => 'applications/config/option/PhabricatorConfigOption.php', 'PhabricatorConfigProxySource' => 'infrastructure/env/PhabricatorConfigProxySource.php', 'PhabricatorConfigSource' => 'infrastructure/env/PhabricatorConfigSource.php', @@ -2117,6 +2120,9 @@ phutil_register_library_map(array( 'PhabricatorConfigIssueViewController' => 'PhabricatorConfigController', 'PhabricatorConfigListController' => 'PhabricatorConfigController', 'PhabricatorConfigLocalSource' => 'PhabricatorConfigProxySource', + 'PhabricatorConfigManagementDeleteWorkflow' => 'PhabricatorConfigManagementWorkflow', + 'PhabricatorConfigManagementGetWorkflow' => 'PhabricatorConfigManagementWorkflow', + 'PhabricatorConfigManagementListWorkflow' => 'PhabricatorConfigManagementWorkflow', 'PhabricatorConfigManagementSetWorkflow' => 'PhabricatorConfigManagementWorkflow', 'PhabricatorConfigManagementWorkflow' => 'PhutilArgumentWorkflow', 'PhabricatorConfigOption' => diff --git a/src/applications/config/management/PhabricatorConfigManagementDeleteWorkflow.php b/src/applications/config/management/PhabricatorConfigManagementDeleteWorkflow.php new file mode 100644 index 0000000000..d765b5ffe9 --- /dev/null +++ b/src/applications/config/management/PhabricatorConfigManagementDeleteWorkflow.php @@ -0,0 +1,56 @@ +setName('delete') + ->setExamples('**delete** __key__') + ->setSynopsis('Delete a local configuration value.') + ->setArguments( + array( + array( + 'name' => 'args', + 'wildcard' => true, + ), + )); + } + + public function execute(PhutilArgumentParser $args) { + $console = PhutilConsole::getConsole(); + + $argv = $args->getArg('args'); + if (count($argv) == 0) { + throw new PhutilArgumentUsageException( + "Specify a configuration key to delete."); + } + + $key = $argv[0]; + + if (count($argv) > 1) { + throw new PhutilArgumentUsageException( + "Too many arguments: expected one key."); + } + + $options = PhabricatorApplicationConfigOptions::loadAllOptions(); + if (empty($options[$key])) { + throw new PhutilArgumentUsageException( + "No such configuration key '{$key}'! Use `config list` to list all ". + "keys."); + } + + $config = new PhabricatorConfigLocalSource(); + $values = $config->getKeys(array($key)); + if (!$values) { + throw new PhutilArgumentUsageException( + "Configuration key '{$key}' is not set in local configuration!"); + } + + $config->deleteKeys(array($key)); + + $console->writeOut( + pht("Deleted '%s' from local configuration.", $key)."\n"); + } + +} diff --git a/src/applications/config/management/PhabricatorConfigManagementGetWorkflow.php b/src/applications/config/management/PhabricatorConfigManagementGetWorkflow.php new file mode 100644 index 0000000000..49758e084d --- /dev/null +++ b/src/applications/config/management/PhabricatorConfigManagementGetWorkflow.php @@ -0,0 +1,62 @@ +setName('get') + ->setExamples('**get** __key__') + ->setSynopsis('Get a local configuration value.') + ->setArguments( + array( + array( + 'name' => 'args', + 'wildcard' => true, + ), + )); + } + + public function execute(PhutilArgumentParser $args) { + $console = PhutilConsole::getConsole(); + + $argv = $args->getArg('args'); + if (count($argv) == 0) { + throw new PhutilArgumentUsageException( + "Specify a configuration key to get."); + } + + $key = $argv[0]; + + if (count($argv) > 1) { + throw new PhutilArgumentUsageException( + "Too many arguments: expected one key."); + } + + $options = PhabricatorApplicationConfigOptions::loadAllOptions(); + if (empty($options[$key])) { + throw new PhutilArgumentUsageException( + "No such configuration key '{$key}'! Use `config list` to list all ". + "keys."); + } + + $config = new PhabricatorConfigLocalSource(); + $values = $config->getKeys(array($key)); + + $result = array(); + foreach ($values as $key => $value) { + $result[] = array( + 'key' => $key, + 'source' => 'local', + 'value' => $value, + ); + } + $result = array( + 'config' => $result, + ); + + $json = new PhutilJSON(); + $console->writeOut($json->encodeFormatted($result)); + } + +} diff --git a/src/applications/config/management/PhabricatorConfigManagementListWorkflow.php b/src/applications/config/management/PhabricatorConfigManagementListWorkflow.php new file mode 100644 index 0000000000..10bd6202b2 --- /dev/null +++ b/src/applications/config/management/PhabricatorConfigManagementListWorkflow.php @@ -0,0 +1,25 @@ +setName('list') + ->setExamples('**list**') + ->setSynopsis('List all configuration keys.'); + } + + public function execute(PhutilArgumentParser $args) { + $options = PhabricatorApplicationConfigOptions::loadAllOptions(); + ksort($options); + + $console = PhutilConsole::getConsole(); + foreach ($options as $option) { + $console->writeOut($option->getKey()."\n"); + } + + return 0; + } + +} diff --git a/src/applications/config/management/PhabricatorConfigManagementSetWorkflow.php b/src/applications/config/management/PhabricatorConfigManagementSetWorkflow.php new file mode 100644 index 0000000000..df678b79a1 --- /dev/null +++ b/src/applications/config/management/PhabricatorConfigManagementSetWorkflow.php @@ -0,0 +1,99 @@ +setName('set') + ->setExamples('**set** __key__ __value__') + ->setSynopsis('Set a local configuration value.') + ->setArguments( + array( + array( + 'name' => 'args', + 'wildcard' => true, + ), + )); + } + + public function execute(PhutilArgumentParser $args) { + $console = PhutilConsole::getConsole(); + + $argv = $args->getArg('args'); + if (count($argv) == 0) { + throw new PhutilArgumentUsageException( + "Specify a configuration key and a value to set it to."); + } + + $key = $argv[0]; + + if (count($argv) == 1) { + throw new PhutilArgumentUsageException( + "Specify a value to set the key '{$key}' to."); + } + + $value = $argv[1]; + + if (count($argv) > 2) { + throw new PhutilArgumentUsageException( + "Too many arguments: expected one key and one value."); + } + + $options = PhabricatorApplicationConfigOptions::loadAllOptions(); + if (empty($options[$key])) { + throw new PhutilArgumentUsageException( + "No such configuration key '{$key}'! Use `config list` to list all ". + "keys."); + } + + $option = $options[$key]; + + $type = $option->getType(); + switch ($type) { + case 'string': + case 'class': + $value = (string)$value; + break; + case 'int': + if (!ctype_digit($value)) { + throw new PhutilArgumentUsageException( + "Config key '{$key}' is of type '{$type}'. Specify an integer."); + } + $value = (int)$value; + break; + case 'bool': + if ($value == 'true') { + $value = true; + } else if ($value == 'false') { + $value = false; + } else { + throw new PhutilArgumentUsageException( + "Config key '{$key}' is of type '{$type}'. ". + "Specify 'true' or 'false'."); + } + break; + default: + $value = json_decode($value, true); + if (!is_array($value)) { + throw new PhutilArgumentUsageException( + "Config key '{$key}' is of type '{$type}'. Specify it in JSON."); + } + break; + } + + try { + $option->getGroup()->validateOption($option, $value); + } catch (PhabricatorConfigValidationException $validation) { + // Convert this into a usage exception so we don't dump a stack trace. + throw new PhutilArgumentUsageException($validation->getMessage()); + } + + $config = new PhabricatorConfigLocalSource(); + $config->setKeys(array($key => $value)); + + $console->writeOut( + pht("Set '%s' in local configuration.", $key)."\n"); + } + +} diff --git a/src/infrastructure/env/management/PhabricatorConfigManagementWorkflow.php b/src/applications/config/management/PhabricatorConfigManagementWorkflow.php similarity index 100% rename from src/infrastructure/env/management/PhabricatorConfigManagementWorkflow.php rename to src/applications/config/management/PhabricatorConfigManagementWorkflow.php diff --git a/src/infrastructure/env/PhabricatorConfigProxySource.php b/src/infrastructure/env/PhabricatorConfigProxySource.php index d2804e5ab8..cb0ede7113 100644 --- a/src/infrastructure/env/PhabricatorConfigProxySource.php +++ b/src/infrastructure/env/PhabricatorConfigProxySource.php @@ -38,7 +38,7 @@ abstract class PhabricatorConfigProxySource } public function deleteKeys(array $keys) { - $this->getSource()->deleteKeys(); + $this->getSource()->deleteKeys($keys); return $this; } diff --git a/src/infrastructure/env/management/PhabricatorConfigManagementSetWorkflow.php b/src/infrastructure/env/management/PhabricatorConfigManagementSetWorkflow.php deleted file mode 100644 index c94db76958..0000000000 --- a/src/infrastructure/env/management/PhabricatorConfigManagementSetWorkflow.php +++ /dev/null @@ -1,50 +0,0 @@ -setName('set') - ->setExamples('**set** __key__ __value__') - ->setSynopsis('Set a local configuration value.') - ->setArguments( - array( - array( - 'name' => 'args', - 'wildcard' => true, - ), - )); - } - - public function execute(PhutilArgumentParser $args) { - $console = PhutilConsole::getConsole(); - - $argv = $args->getArg('args'); - if (count($argv) == 0) { - throw new PhutilArgumentUsageException( - "Specify a configuration key and a value to set it to."); - } - - $key = $argv[0]; - - if (count($argv) == 1) { - throw new PhutilArgumentUsageException( - "Specify a value to set the key '{$key}' to."); - } - - $value = $argv[1]; - - if (count($argv) > 2) { - throw new PhutilArgumentUsageException( - "Too many arguments: expected one key and one value."); - } - - $config = new PhabricatorConfigLocalSource(); - $config->setKeys(array($key => $value)); - - $console->writeOut( - pht("Set '%s' to '%s' in local configuration.", $key, $value)."\n"); - } - -}