diff --git a/src/applications/config/management/PhabricatorConfigManagementSetWorkflow.php b/src/applications/config/management/PhabricatorConfigManagementSetWorkflow.php index 22b760872e..9eb83bd61e 100644 --- a/src/applications/config/management/PhabricatorConfigManagementSetWorkflow.php +++ b/src/applications/config/management/PhabricatorConfigManagementSetWorkflow.php @@ -1,150 +1,162 @@ setName('set') ->setExamples( "**set** __key__ __value__\n". "**set** __key__ --stdin < value.json") ->setSynopsis(pht('Set a local configuration value.')) ->setArguments( array( array( 'name' => 'database', 'help' => pht( 'Update configuration in the database instead of '. 'in local configuration.'), ), array( 'name' => 'stdin', 'help' => pht('Read option value from stdin.'), ), array( 'name' => 'args', 'wildcard' => true, ), )); } public function execute(PhutilArgumentParser $args) { - $console = PhutilConsole::getConsole(); $argv = $args->getArg('args'); - if (count($argv) == 0) { + if (!$argv) { throw new PhutilArgumentUsageException( - pht('Specify a configuration key and a value to set it to.')); + pht('Specify the configuration key you want to set.')); } $is_stdin = $args->getArg('stdin'); $key = $argv[0]; if ($is_stdin) { if (count($argv) > 1) { throw new PhutilArgumentUsageException( pht( - 'Too many arguments: expected only a key when using "--stdin".')); + 'Too many arguments: expected only a configuration key when '. + 'using "--stdin".')); } fprintf(STDERR, tsprintf("%s\n", pht('Reading value from stdin...'))); $value = file_get_contents('php://stdin'); } else { if (count($argv) == 1) { throw new PhutilArgumentUsageException( pht( - "Specify a value to set the key '%s' to.", + 'Specify a value to set the configuration key "%s" to, or '. + 'use "--stdin" to read a value from stdin.', $key)); } if (count($argv) > 2) { throw new PhutilArgumentUsageException( pht( 'Too many arguments: expected one key and one value.')); } $value = $argv[1]; } - $options = PhabricatorApplicationConfigOptions::loadAllOptions(); if (empty($options[$key])) { throw new PhutilArgumentUsageException( pht( - "No such configuration key '%s'! Use `%s` to list all keys.", - $key, - 'config list')); + 'Configuration key "%s" is unknown. Use "bin/config list" to list '. + 'all known keys.', + $key)); } $option = $options[$key]; $type = $option->newOptionType(); if ($type) { try { $value = $type->newValueFromCommandLineValue( $option, $value); $type->validateStoredValue($option, $value); } catch (PhabricatorConfigValidationException $ex) { throw new PhutilArgumentUsageException($ex->getMessage()); } } else { // NOTE: For now, this handles both "wild" values and custom types. $type = $option->getType(); switch ($type) { default: $value = json_decode($value, true); if (!is_array($value)) { switch ($type) { default: $message = pht( - 'Config key "%s" is of type "%s". Specify it in JSON.', + 'Configuration key "%s" is of type "%s". Specify it in JSON.', $key, $type); break; } throw new PhutilArgumentUsageException($message); } break; } } $use_database = $args->getArg('database'); if ($option->getLocked() && $use_database) { throw new PhutilArgumentUsageException( pht( 'Config key "%s" is locked and can only be set in local '. 'configuration. To learn more, see "%s" in the documentation.', $key, pht('Configuration Guide: Locked and Hidden Configuration'))); } 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()); } if ($use_database) { - $config_type = 'database'; $config_entry = PhabricatorConfigEntry::loadConfigEntry($key); $config_entry->setValue($value); // If the entry has been deleted, resurrect it. $config_entry->setIsDeleted(0); $config_entry->save(); + + $write_message = pht( + 'Wrote configuration key "%s" to database storage.', + $key); } else { - $config_type = 'local'; - id(new PhabricatorConfigLocalSource()) + $config_source = id(new PhabricatorConfigLocalSource()) ->setKeys(array($key => $value)); + + $local_path = $config_source->getReadablePath(); + + $write_message = pht( + 'Wrote configuration key "%s" to local storage (in file "%s").', + $key, + $local_path); } - $console->writeOut( - "%s\n", - pht("Set '%s' in %s configuration.", $key, $config_type)); + echo tsprintf( + "** %s ** %s\n", + pht('DONE'), + $write_message); + + return 0; } } diff --git a/src/infrastructure/env/PhabricatorConfigLocalSource.php b/src/infrastructure/env/PhabricatorConfigLocalSource.php index 16dc43a9bc..fc1c83f812 100644 --- a/src/infrastructure/env/PhabricatorConfigLocalSource.php +++ b/src/infrastructure/env/PhabricatorConfigLocalSource.php @@ -1,68 +1,73 @@ loadConfig(); $this->setSource(new PhabricatorConfigDictionarySource($config)); } public function setKeys(array $keys) { $result = parent::setKeys($keys); $this->saveConfig(); return $result; } public function deleteKeys(array $keys) { $result = parent::deleteKeys($keys); $this->saveConfig(); return parent::deleteKeys($keys); } private function loadConfig() { $path = $this->getConfigPath(); if (!Filesystem::pathExists($path)) { return array(); } try { $data = Filesystem::readFile($path); } catch (FilesystemException $ex) { throw new PhutilProxyException( pht( 'Configuration file "%s" exists, but could not be read.', $path), $ex); } try { $result = phutil_json_decode($data); } catch (PhutilJSONParserException $ex) { throw new PhutilProxyException( pht( 'Configuration file "%s" exists and is readable, but the content '. 'is not valid JSON. You may have edited this file manually and '. 'introduced a syntax error by mistake. Correct the file syntax '. 'to continue.', $path), $ex); } return $result; } private function saveConfig() { $config = $this->getSource()->getAllKeys(); $json = new PhutilJSON(); $data = $json->encodeFormatted($config); Filesystem::writeFile($this->getConfigPath(), $data); } private function getConfigPath() { $root = dirname(phutil_get_library_root('phabricator')); $path = $root.'/conf/local/local.json'; return $path; } + public function getReadablePath() { + $path = $this->getConfigPath(); + return Filesystem::readablePath($path); + } + }