1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-21 22:32:41 +01:00

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
This commit is contained in:
epriestley 2013-01-21 15:27:42 -08:00
parent 08e61c6ff1
commit baa9d96082
9 changed files with 254 additions and 53 deletions

View file

@ -15,7 +15,10 @@ EOSYNOPSIS
$args->parseStandardArguments();
$workflows = array(
new PhabricatorConfigManagementListWorkflow(),
new PhabricatorConfigManagementSetWorkflow(),
new PhabricatorConfigManagementGetWorkflow(),
new PhabricatorConfigManagementDeleteWorkflow(),
new PhutilHelpArgumentWorkflow(),
);

View file

@ -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' =>

View file

@ -0,0 +1,56 @@
<?php
final class PhabricatorConfigManagementDeleteWorkflow
extends PhabricatorConfigManagementWorkflow {
protected function didConstruct() {
$this
->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");
}
}

View file

@ -0,0 +1,62 @@
<?php
final class PhabricatorConfigManagementGetWorkflow
extends PhabricatorConfigManagementWorkflow {
protected function didConstruct() {
$this
->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));
}
}

View file

@ -0,0 +1,25 @@
<?php
final class PhabricatorConfigManagementListWorkflow
extends PhabricatorConfigManagementWorkflow {
protected function didConstruct() {
$this
->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;
}
}

View file

@ -0,0 +1,99 @@
<?php
final class PhabricatorConfigManagementSetWorkflow
extends PhabricatorConfigManagementWorkflow {
protected function didConstruct() {
$this
->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");
}
}

View file

@ -38,7 +38,7 @@ abstract class PhabricatorConfigProxySource
}
public function deleteKeys(array $keys) {
$this->getSource()->deleteKeys();
$this->getSource()->deleteKeys($keys);
return $this;
}

View file

@ -1,50 +0,0 @@
<?php
final class PhabricatorConfigManagementSetWorkflow
extends PhabricatorConfigManagementWorkflow {
protected function didConstruct() {
$this
->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");
}
}