mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-21 04:50:55 +01:00
Allow bin/config to affect database configuration and migrate between local and database configuration
Summary: Fixes T4018. Basically hits the bullet points in that task description except the "ideally" one. Test Plan: ran bin/config migrate and saw sensible output. ``` ~> ./bin/config migrate Migrating file-based config to more modern config... Skipping config of source type PhabricatorConfigDatabaseSource... Skipping config of source type PhabricatorConfigLocalSource... Skipping config of source type PhabricatorConfigDefaultSource... Done. Migrated 0 keys. ``` Reviewers: epriestley Reviewed By: epriestley Subscribers: hach-que, epriestley, Korvin Maniphest Tasks: T4018 Differential Revision: https://secure.phabricator.com/D10490
This commit is contained in:
parent
f4604ae147
commit
c0848bca6d
9 changed files with 251 additions and 42 deletions
|
@ -1380,6 +1380,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorConfigManagementDeleteWorkflow' => 'applications/config/management/PhabricatorConfigManagementDeleteWorkflow.php',
|
||||
'PhabricatorConfigManagementGetWorkflow' => 'applications/config/management/PhabricatorConfigManagementGetWorkflow.php',
|
||||
'PhabricatorConfigManagementListWorkflow' => 'applications/config/management/PhabricatorConfigManagementListWorkflow.php',
|
||||
'PhabricatorConfigManagementMigrateWorkflow' => 'applications/config/management/PhabricatorConfigManagementMigrateWorkflow.php',
|
||||
'PhabricatorConfigManagementSetWorkflow' => 'applications/config/management/PhabricatorConfigManagementSetWorkflow.php',
|
||||
'PhabricatorConfigManagementWorkflow' => 'applications/config/management/PhabricatorConfigManagementWorkflow.php',
|
||||
'PhabricatorConfigOption' => 'applications/config/option/PhabricatorConfigOption.php',
|
||||
|
@ -4342,6 +4343,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorConfigManagementDeleteWorkflow' => 'PhabricatorConfigManagementWorkflow',
|
||||
'PhabricatorConfigManagementGetWorkflow' => 'PhabricatorConfigManagementWorkflow',
|
||||
'PhabricatorConfigManagementListWorkflow' => 'PhabricatorConfigManagementWorkflow',
|
||||
'PhabricatorConfigManagementMigrateWorkflow' => 'PhabricatorConfigManagementWorkflow',
|
||||
'PhabricatorConfigManagementSetWorkflow' => 'PhabricatorConfigManagementWorkflow',
|
||||
'PhabricatorConfigManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorConfigOption' => array(
|
||||
|
|
|
@ -59,7 +59,10 @@ final class PhabricatorConfigIgnoreController
|
|||
}
|
||||
|
||||
PhabricatorConfigEditor::storeNewValue(
|
||||
$config_entry, $list, $this->getRequest());
|
||||
$this->getRequest()->getUser(),
|
||||
$config_entry,
|
||||
$list,
|
||||
PhabricatorContentSource::newFromRequest($this->getRequest()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -113,9 +113,10 @@ final class PhabricatorConfigEditor
|
|||
}
|
||||
|
||||
public static function storeNewValue(
|
||||
PhabricatorUser $user,
|
||||
PhabricatorConfigEntry $config_entry,
|
||||
$value,
|
||||
AphrontRequest $request) {
|
||||
PhabricatorContentSource $source) {
|
||||
|
||||
$xaction = id(new PhabricatorConfigTransaction())
|
||||
->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT)
|
||||
|
@ -126,9 +127,30 @@ final class PhabricatorConfigEditor
|
|||
));
|
||||
|
||||
$editor = id(new PhabricatorConfigEditor())
|
||||
->setActor($request->getUser())
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContentSourceFromRequest($request);
|
||||
->setActor($user)
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContentSource($source);
|
||||
|
||||
$editor->applyTransactions($config_entry, array($xaction));
|
||||
}
|
||||
|
||||
public static function deleteConfig(
|
||||
PhabricatorUser $user,
|
||||
PhabricatorConfigEntry $config_entry,
|
||||
PhabricatorContentSource $source) {
|
||||
|
||||
$xaction = id(new PhabricatorConfigTransaction())
|
||||
->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT)
|
||||
->setNewValue(
|
||||
array(
|
||||
'deleted' => true,
|
||||
'value' => null,
|
||||
));
|
||||
|
||||
$editor = id(new PhabricatorConfigEditor())
|
||||
->setActor($user)
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContentSource($source);
|
||||
|
||||
$editor->applyTransactions($config_entry, array($xaction));
|
||||
}
|
||||
|
|
|
@ -7,9 +7,14 @@ final class PhabricatorConfigManagementDeleteWorkflow
|
|||
$this
|
||||
->setName('delete')
|
||||
->setExamples('**delete** __key__')
|
||||
->setSynopsis('Delete a local configuration value.')
|
||||
->setSynopsis(pht('Delete a local configuration value.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'database',
|
||||
'help' => pht('Delete configuration in the database instead of '.
|
||||
'in local configuration.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'args',
|
||||
'wildcard' => true,
|
||||
|
@ -22,28 +27,50 @@ final class PhabricatorConfigManagementDeleteWorkflow
|
|||
|
||||
$argv = $args->getArg('args');
|
||||
if (count($argv) == 0) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
'Specify a configuration key to delete.');
|
||||
throw new PhutilArgumentUsageException(pht(
|
||||
'Specify a configuration key to delete.'));
|
||||
}
|
||||
|
||||
$key = $argv[0];
|
||||
|
||||
if (count($argv) > 1) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
'Too many arguments: expected one key.');
|
||||
throw new PhutilArgumentUsageException(pht(
|
||||
'Too many arguments: expected one key.'));
|
||||
}
|
||||
|
||||
$config = new PhabricatorConfigLocalSource();
|
||||
|
||||
$use_database = $args->getArg('database');
|
||||
if ($use_database) {
|
||||
$config = new PhabricatorConfigDatabaseSource('default');
|
||||
$config_type = 'database';
|
||||
} else {
|
||||
$config = new PhabricatorConfigLocalSource();
|
||||
$config_type = 'local';
|
||||
}
|
||||
$values = $config->getKeys(array($key));
|
||||
if (!$values) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
"Configuration key '{$key}' is not set in local configuration!");
|
||||
throw new PhutilArgumentUsageException(pht(
|
||||
"Configuration key '%s' is not set in %s configuration!",
|
||||
$key,
|
||||
$config_type));
|
||||
}
|
||||
|
||||
$config->deleteKeys(array($key));
|
||||
if ($use_database) {
|
||||
$config_entry = id(new PhabricatorConfigOption())
|
||||
->loadOneWhere(
|
||||
'namespace = %s and key = %s',
|
||||
'default',
|
||||
$key);
|
||||
PhabricatorConfigEditor::deleteConfig(
|
||||
$this->getViewer(),
|
||||
$config_entry,
|
||||
PhabricatorContentSource::newConsoleSource());
|
||||
} else {
|
||||
$config->deleteKeys(array($key));
|
||||
}
|
||||
|
||||
$console->writeOut(
|
||||
pht("Deleted '%s' from local configuration.", $key)."\n");
|
||||
pht("Deleted '%s' from %s configuration.", $key, $config_type)."\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,15 +40,60 @@ final class PhabricatorConfigManagementGetWorkflow
|
|||
"keys.");
|
||||
}
|
||||
|
||||
$values = array();
|
||||
$config = new PhabricatorConfigLocalSource();
|
||||
$values = $config->getKeys(array($key));
|
||||
$local_value = $config->getKeys(array($key));
|
||||
if (empty($local_value)) {
|
||||
$values['local'] = array(
|
||||
'key' => $key,
|
||||
'value' => null,
|
||||
'status' => 'unset',
|
||||
'errorInfo' => null,
|
||||
);
|
||||
} else {
|
||||
$values['local'] = array(
|
||||
'key' => $key,
|
||||
'value' => reset($local_value),
|
||||
'status' => 'set',
|
||||
'errorInfo' => null,
|
||||
);
|
||||
}
|
||||
|
||||
$database_config = new PhabricatorConfigDatabaseSource('default');
|
||||
try {
|
||||
$database_value = $database_config->getKeys(array($key));
|
||||
if (empty($database_value)) {
|
||||
$values['database'] = array(
|
||||
'key' => $key,
|
||||
'value' => null,
|
||||
'status' => 'unset',
|
||||
'errorInfo' => null,
|
||||
);
|
||||
} else {
|
||||
$values['database'] = array(
|
||||
'key' => $key,
|
||||
'value' => reset($database_value),
|
||||
'status' => 'set',
|
||||
'errorInfo' => null,
|
||||
);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$values['database'] = array(
|
||||
'key' => $key,
|
||||
'value' => null,
|
||||
'status' => 'error',
|
||||
'errorInfo' => pht('Database source is not configured properly'),
|
||||
);
|
||||
}
|
||||
|
||||
$result = array();
|
||||
foreach ($values as $key => $value) {
|
||||
foreach ($values as $source => $value) {
|
||||
$result[] = array(
|
||||
'key' => $key,
|
||||
'source' => 'local',
|
||||
'value' => $value,
|
||||
'key' => $value['key'],
|
||||
'source' => $source,
|
||||
'value' => $value['value'],
|
||||
'status' => $value['status'],
|
||||
'errorInfo' => $value['errorInfo'],
|
||||
);
|
||||
}
|
||||
$result = array(
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorConfigManagementMigrateWorkflow
|
||||
extends PhabricatorConfigManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('migrate')
|
||||
->setExamples('**migrate**')
|
||||
->setSynopsis(pht(
|
||||
'Migrate file-based configuration to more modern storage.'));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
$key_count = 0;
|
||||
|
||||
$options = PhabricatorApplicationConfigOptions::loadAllOptions();
|
||||
$local_config = new PhabricatorConfigLocalSource();
|
||||
$database_config = new PhabricatorConfigDatabaseSource('default');
|
||||
$config_sources = PhabricatorEnv::getConfigSourceStack()->getStack();
|
||||
$console->writeOut(
|
||||
pht('Migrating file-based config to more modern config...')."\n");
|
||||
foreach ($config_sources as $config_source) {
|
||||
if (!($config_source instanceof PhabricatorConfigFileSource)) {
|
||||
$console->writeOut(
|
||||
pht('Skipping config of source type %s...',
|
||||
get_class($config_source))."\n");
|
||||
continue;
|
||||
}
|
||||
$console->writeOut(pht('Migrating file source...')."\n");
|
||||
$all_keys = $config_source->getAllKeys();
|
||||
foreach ($all_keys as $key => $value) {
|
||||
$option = idx($options, $key);
|
||||
if (!$option) {
|
||||
$console->writeOut(pht('Skipping obsolete option: %s', $key)."\n");
|
||||
continue;
|
||||
}
|
||||
$in_local = $local_config->getKeys(array($option->getKey()));
|
||||
if ($in_local) {
|
||||
$console->writeOut(pht(
|
||||
'Skipping option "%s"; already in local config.', $key)."\n");
|
||||
continue;
|
||||
}
|
||||
$is_locked = $option->getLocked();
|
||||
if ($is_locked) {
|
||||
$local_config->setKeys(array($option->getKey() => $value));
|
||||
$key_count++;
|
||||
$console->writeOut(pht(
|
||||
'Migrated option "%s" from file to local config.', $key)."\n");
|
||||
} else {
|
||||
$in_database = $database_config->getKeys(array($option->getKey()));
|
||||
if ($in_database) {
|
||||
$console->writeOut(pht(
|
||||
'Skipping option "%s"; already in database config.', $key)."\n");
|
||||
continue;
|
||||
} else {
|
||||
PhabricatorConfigEditor::deleteConfig(
|
||||
$this->getViewer(),
|
||||
$option,
|
||||
PhabricatorContentSource::newFromConsole());
|
||||
$key_count++;
|
||||
$console->writeOut(pht(
|
||||
'Migrated option "%s" from file to local config.', $key)."\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$console->writeOut(pht(
|
||||
'Done. Migrated %d keys.', $key_count)."\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,9 +7,14 @@ final class PhabricatorConfigManagementSetWorkflow
|
|||
$this
|
||||
->setName('set')
|
||||
->setExamples('**set** __key__ __value__')
|
||||
->setSynopsis('Set a local configuration value.')
|
||||
->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' => 'args',
|
||||
'wildcard' => true,
|
||||
|
@ -21,29 +26,31 @@ final class PhabricatorConfigManagementSetWorkflow
|
|||
$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.');
|
||||
throw new PhutilArgumentUsageException(pht(
|
||||
'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.");
|
||||
throw new PhutilArgumentUsageException(pht(
|
||||
"Specify a value to set the key '%s' to.",
|
||||
$key));
|
||||
}
|
||||
|
||||
$value = $argv[1];
|
||||
|
||||
if (count($argv) > 2) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
'Too many arguments: expected one key and one value.');
|
||||
throw new PhutilArgumentUsageException(pht(
|
||||
'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.");
|
||||
throw new PhutilArgumentUsageException(pht(
|
||||
"No such configuration key '%s'! Use `config list` to list all ".
|
||||
"keys.",
|
||||
$key));
|
||||
}
|
||||
|
||||
$option = $options[$key];
|
||||
|
@ -57,8 +64,10 @@ final class PhabricatorConfigManagementSetWorkflow
|
|||
break;
|
||||
case 'int':
|
||||
if (!ctype_digit($value)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
"Config key '{$key}' is of type '{$type}'. Specify an integer.");
|
||||
throw new PhutilArgumentUsageException(pht(
|
||||
"Config key '%s' is of type '%s'. Specify an integer.",
|
||||
$key,
|
||||
$type));
|
||||
}
|
||||
$value = (int)$value;
|
||||
break;
|
||||
|
@ -68,19 +77,30 @@ final class PhabricatorConfigManagementSetWorkflow
|
|||
} else if ($value == 'false') {
|
||||
$value = false;
|
||||
} else {
|
||||
throw new PhutilArgumentUsageException(
|
||||
"Config key '{$key}' is of type '{$type}'. ".
|
||||
"Specify 'true' or 'false'.");
|
||||
throw new PhutilArgumentUsageException(pht(
|
||||
"Config key '%s' is of type '%s'. ".
|
||||
"Specify 'true' or 'false'.",
|
||||
$key,
|
||||
$type));
|
||||
}
|
||||
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.");
|
||||
throw new PhutilArgumentUsageException(pht(
|
||||
"Config key '%s' is of type '%s'. Specify it in JSON.",
|
||||
$key,
|
||||
$type));
|
||||
}
|
||||
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.',
|
||||
$key));
|
||||
}
|
||||
|
||||
try {
|
||||
$option->getGroup()->validateOption($option, $value);
|
||||
|
@ -89,11 +109,22 @@ final class PhabricatorConfigManagementSetWorkflow
|
|||
throw new PhutilArgumentUsageException($validation->getMessage());
|
||||
}
|
||||
|
||||
$config = new PhabricatorConfigLocalSource();
|
||||
$config->setKeys(array($key => $value));
|
||||
if ($use_database) {
|
||||
$config_type = 'database';
|
||||
PhabricatorConfigEditor::storeNewValue(
|
||||
$this->getViewer(),
|
||||
id(new PhabricatorConfigEntry())
|
||||
->loadOneWhere('namespace = %s AND key = %s', 'default', $key),
|
||||
$value,
|
||||
PhabricatorContentSource::newConsoleSource());
|
||||
} else {
|
||||
$config_type = 'local';
|
||||
id(new PhabricatorConfigLocalSource())
|
||||
->setKeys(array($key => $value));
|
||||
}
|
||||
|
||||
$console->writeOut(
|
||||
pht("Set '%s' in local configuration.", $key)."\n");
|
||||
pht("Set '%s' in %s configuration.", $key, $config_type)."\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -104,9 +104,10 @@ final class PhabricatorApplicationEditController
|
|||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
PhabricatorConfigEditor::storeNewValue(
|
||||
$user,
|
||||
$config_entry,
|
||||
$value,
|
||||
$this->getRequest());
|
||||
PhabricatorContentSource::newFromRequest($this->getRequest()));
|
||||
}
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($view_uri);
|
||||
|
|
|
@ -90,7 +90,10 @@ final class PhabricatorApplicationUninstallController
|
|||
}
|
||||
|
||||
PhabricatorConfigEditor::storeNewValue(
|
||||
$config_entry, $list, $this->getRequest());
|
||||
$this->getRequest()->getUser(),
|
||||
$config_entry,
|
||||
$list,
|
||||
PhabricatorContentSource::newFromRequest($this->getRequest()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue