1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-09 00:12:40 +01:00

[Wilds] Prepare for more modular configuration management

Summary:
Ref T13098. This is kind of a catch-all diff with stuff that didn't fit in prior diffs, and which fixes some bugs with that stuff now that I made it at least sort of reachable.

Beyond bugs, the general idea is to replace `ConfigurationManager` (a big class which knew about config-end-to-end) with a more modern/modular `ConfigurationEngine` using the standard Engine + EngineExtension modularity pattern.

Configuration becomes a `ConfigurationSourceList` of `ConfigurationSource` objects, each of which represents one source (a config file, `--config x=y`, etc). The various sources will have the logic to parse values (e.g., decode `x=y` flags or JSON files on disk). A new `--config-file` allows you to replace the system (`/etc/arcconfig`) and user (`~/.arcrc`) files.

This also gets rid of `--library` support entirely for now since it's kind of messy to bridge until Config works. I expect to either restore it or replace it with `arc install` and similar.

Test Plan: Ran `arc liberate`; it actually works now. (The Config stuff does not actually work yet.)

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13098

Differential Revision: https://secure.phabricator.com/D19694
This commit is contained in:
epriestley 2018-09-18 12:49:51 -07:00
parent 11599cedb6
commit 412484022b
23 changed files with 765 additions and 534 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,6 @@
<?php
final class ArcanistArcConfigurationEngineExtension
extends ArcanistConfigurationEngineExtension {
}

View file

@ -0,0 +1,6 @@
<?php
final class ArcanistConduitConfigurationEngineExtension
extends ArcanistConfigurationEngineExtension {
}

View file

@ -0,0 +1,6 @@
<?php
abstract class ArcanistConfigOption
extends Phobject {
}

View file

@ -0,0 +1,98 @@
<?php
final class ArcanistConfigurationEngine
extends Phobject {
private $workingCopy;
private $arguments;
public function setWorkingCopy(ArcanistWorkingCopy $working_copy) {
$this->workingCopy = $working_copy;
return $this;
}
public function getWorkingCopy() {
return $this->workingCopy;
}
public function setArguments(PhutilArgumentParser $arguments) {
$this->arguments = $arguments;
return $this;
}
public function getArguments() {
if (!$this->arguments) {
throw new PhutilInvalidStateException('setArguments');
}
return $this->arguments;
}
public function newConfigurationSourceList() {
$list = new ArcanistConfigurationSourceList();
$list->addSource(new ArcanistDefaultsConfigurationSource());
$arguments = $this->getArguments();
// If the invoker has provided one or more configuration files with
// "--config-file" arguments, read those files instead of the system
// and user configuration files. Otherwise, read the system and user
// configuration files.
$config_files = $arguments->getArg('config-file');
if ($config_files) {
foreach ($config_files as $config_file) {
$list->addSource(new ArcanistFileConfigurationSource($config_file));
}
} else {
$system_path = $this->getSystemConfigurationFilePath();
$list->addSource(new ArcanistSystemConfigurationSource($system_path));
$user_path = $this->getUserConfigurationFilePath();
$list->addSource(new ArcanistUserConfigurationSource($user_path));
}
// If we're running in a working copy, load the ".arcconfig" and any
// local configuration.
$working_copy = $this->getWorkingCopy();
if ($working_copy) {
$project_path = $working_copy->getProjectConfigurationFilePath();
if ($project_path !== null) {
$list->addSource(new ArcanistProjectConfigurationSource($project_path));
}
$local_path = $working_copy->getLocalConfigurationFilePath();
if ($local_path !== null) {
$list->addSource(new ArcanistLocalConfigurationSource($local_path));
}
}
// If the invoker has provided "--config" arguments, parse those now.
$runtime_args = $arguments->getArg('config');
if ($runtime_args) {
$list->addSource(new ArcanistRuntimeConfigurationSource($runtime_args));
}
return $list;
}
private function getSystemConfigurationFilePath() {
if (phutil_is_windows()) {
return Filesystem::resolvePath(
'Phabricator/Arcanist/config',
getenv('ProgramData'));
} else {
return '/etc/arcconfig';
}
}
private function getUserConfigurationFilePath() {
if (phutil_is_windows()) {
return getenv('APPDATA').'/.arcrc';
} else {
return getenv('HOME').'/.arcrc';
}
}
}

View file

@ -0,0 +1,6 @@
<?php
final class ArcanistConfigurationEngineExtension
extends Phobject {
}

View file

@ -0,0 +1,13 @@
<?php
final class ArcanistConfigurationSourceList
extends Phobject {
private $sources = array();
public function addSource(ArcanistConfigurationSource $source) {
$this->sources[] = $source;
return $this;
}
}

View file

@ -0,0 +1,6 @@
<?php
abstract class ArcanistConfigurationSource
extends Phobject {
}

View file

@ -0,0 +1,6 @@
<?php
final class ArcanistDefaultsConfigurationSource
extends ArcanistConfigurationSource {
}

View file

@ -0,0 +1,6 @@
<?php
final class ArcanistFileConfigurationSource
extends ArcanistConfigurationSource {
}

View file

@ -0,0 +1,6 @@
<?php
abstract class ArcanistFilesystemConfigurationSource
extends ArcanistConfigurationSource {
}

View file

@ -0,0 +1,6 @@
<?php
final class ArcanistLocalConfigurationSource
extends ArcanistWorkingCopyConfigurationSource {
}

View file

@ -0,0 +1,6 @@
<?php
final class ArcanistProjectConfigurationSource
extends ArcanistWorkingCopyConfigurationSource {
}

View file

@ -0,0 +1,6 @@
<?php
final class ArcanistRuntimeConfigurationSource
extends ArcanistConfigurationSource {
}

View file

@ -0,0 +1,6 @@
<?php
final class ArcanistSystemConfigurationSource
extends ArcanistFilesystemConfigurationSource {
}

View file

@ -0,0 +1,6 @@
<?php
final class ArcanistUserConfigurationSource
extends ArcanistFilesystemConfigurationSource {
}

View file

@ -0,0 +1,6 @@
<?php
abstract class ArcanistWorkingCopyConfigurationSource
extends ArcanistFilesystemConfigurationSource {
}

View file

@ -1054,7 +1054,8 @@ final class Filesystem extends Phobject {
// separator, so we don't end up with "path//to///thing.c". // separator, so we don't end up with "path//to///thing.c".
$components = preg_replace( $components = preg_replace(
'('.preg_quote(DIRECTORY_SEPARATOR).'{2,})', '('.preg_quote(DIRECTORY_SEPARATOR).'{2,})',
DIRECTORY_SEPARATOR); DIRECTORY_SEPARATOR,
$components);
return $components; return $components;
} }

View file

@ -11,7 +11,7 @@ final class ArcanistGitWorkingCopy
$working_directory, $working_directory,
$ancestor_directory) { $ancestor_directory) {
if (!Filesystem::pathExits($ancestor_directory.'/.git')) { if (!Filesystem::pathExists($ancestor_directory.'/.git')) {
return null; return null;
} }

View file

@ -11,7 +11,7 @@ final class ArcanistMercurialWorkingCopy
$working_directory, $working_directory,
$ancestor_directory) { $ancestor_directory) {
if (!Filesystem::pathExits($ancestor_directory.'/.hg')) { if (!Filesystem::pathExists($ancestor_directory.'/.hg')) {
return null; return null;
} }

View file

@ -39,11 +39,11 @@ final class ArcanistSubversionWorkingCopy
$working_directory, $working_directory,
$ancestor_directory) { $ancestor_directory) {
if (!Filesystem::pathExits($ancestor_directory.'/.svn')) { if (!Filesystem::pathExists($ancestor_directory.'/.svn')) {
return null; return null;
} }
return id(new self()); return new self();
} }

View file

@ -8,7 +8,7 @@ abstract class ArcanistWorkingCopy
public static function newFromWorkingDirectory($path) { public static function newFromWorkingDirectory($path) {
$working_types = id(new PhutilClassMapQuery()) $working_types = id(new PhutilClassMapQuery())
->setParentClass(__CLASS__) ->setAncestorClass(__CLASS__)
->execute(); ->execute();
// Find the outermost directory which is under version control. We go from // Find the outermost directory which is under version control. We go from

View file

@ -45,6 +45,14 @@ final class ArcanistRuntime {
'repeat' => true, 'repeat' => true,
'help' => pht('Specify a runtime configuration value.'), 'help' => pht('Specify a runtime configuration value.'),
), ),
array(
'name' => 'config-file',
'param' => 'path',
'repeat' => true,
'help' => pht(
'Load one or more configuration files. If this flag is provided, '.
'the system and user configuration files are ignored.'),
),
); );
$args = id(new PhutilArgumentParser($argv)) $args = id(new PhutilArgumentParser($argv))
@ -202,9 +210,10 @@ final class ArcanistRuntime {
} }
private function loadConfiguration(PhutilArgumentParser $args) { private function loadConfiguration(PhutilArgumentParser $args) {
$engine = new ArcanistConfigurationEngine(); $engine = id(new ArcanistConfigurationEngine())
->setArguments($args);
$working_copy = ArcanistWorkingCopyIdentity::newFromPath(getcwd()); $working_copy = ArcanistWorkingCopy::newFromWorkingDirectory(getcwd());
if ($working_copy) { if ($working_copy) {
$engine->setWorkingCopy($working_copy); $engine->setWorkingCopy($working_copy);
} }
@ -214,28 +223,16 @@ final class ArcanistRuntime {
private function loadLibraries( private function loadLibraries(
PhutilArgumentParser $args, PhutilArgumentParser $args,
ArcanistConfigurationManager $config) { ArcanistConfigurationSourceList $config) {
// TOOLSETS: Make this work again -- or replace it entirely with package
// management?
return;
$is_trace = $args->getArg('trace'); $is_trace = $args->getArg('trace');
if ($is_trace) {
$libraries = array(
'phutil',
'arcanist',
);
foreach ($libraries as $library_name) {
$this->logTrace(
pht('LOAD'),
pht(
'Loaded "%s" from "%s".',
$library_name,
phutil_get_library_root($library_name)));
}
}
$load = array(); $load = array();
$working_copy = $config->getWorkingCopyIdentity(); $working_copy = $this->getWorkingCopy();
$cli_libraries = $args->getArg('library'); $cli_libraries = $args->getArg('library');
if ($cli_libraries) { if ($cli_libraries) {
@ -463,7 +460,7 @@ final class ArcanistRuntime {
private function resolveAliases( private function resolveAliases(
array $workflows, array $workflows,
array $argv, array $argv,
ArcanistConfigurationManager $config) { ArcanistConfigurationSourceList $config) {
$command = head($argv); $command = head($argv);