Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F969810
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/configuration/ArcanistConfigurationManager.php b/src/configuration/ArcanistConfigurationManager.php
index 79c5a756..bc22240c 100644
--- a/src/configuration/ArcanistConfigurationManager.php
+++ b/src/configuration/ArcanistConfigurationManager.php
@@ -1,261 +1,261 @@
<?php
/**
* This class holds everything related to configuration and configuration files.
*
* @group config
*/
final class ArcanistConfigurationManager {
private $runtimeConfig = array();
private $workingCopy = null;
private $customArcrcFilename = null;
public function setWorkingCopyIdentity(
ArcanistWorkingCopyIdentity $working_copy) {
$this->workingCopy = $working_copy;
}
/* -( Get config )--------------------------------------------------------- */
const CONFIG_SOURCE_RUNTIME = 'runtime';
const CONFIG_SOURCE_LOCAL = 'local';
const CONFIG_SOURCE_PROJECT = 'project';
const CONFIG_SOURCE_USER = 'user';
const CONFIG_SOURCE_SYSTEM = 'system';
public function getProjectConfig($key) {
if ($this->workingCopy) {
return $this->workingCopy->getProjectConfig($key);
}
return null;
}
public function getLocalConfig($key) {
if ($this->workingCopy) {
return $this->workingCopy->getLocalConfig($key);
}
return null;
}
public function getWorkingCopyIdentity() {
return $this->workingCopy;
}
/**
* Read a configuration directive from any available configuration source.
* This includes the directive in local, user and system configuration in
* addition to project configuration, and configuration provided as command
* arguments ("runtime").
* The precedence is runtime > local > project > user > system
*
* @param key Key to read.
* @param wild Default value if key is not found.
* @return wild Value, or default value if not found.
*
* @task config
*/
public function getConfigFromAnySource($key, $default = null) {
$all = $this->getConfigFromAllSources($key);
return empty($all) ? $default : head($all);
}
/**
* For the advanced case where you want customized configuratin handling.
*
* Reads the configuration from all available sources, returning a map (array)
* of results, with the source as key. Missing values will not be in the map,
* so an empty array will be returned if no results are found.
*
* The map is ordered by the cannonical sources precedence, which is:
* runtime > local > project > user > system
*
* @param key Key to read
* @return array Mapping of source => value read. Sources with no value are
* not in the array.
*
* @task config
*/
public function getConfigFromAllSources($key) {
$results = array();
$settings = new ArcanistSettings();
$pval = idx($this->runtimeConfig, $key);
if ($pval !== null) {
$results[self::CONFIG_SOURCE_RUNTIME] =
$settings->willReadValue($key, $pval);
}
$pval = $this->getLocalConfig($key);
if ($pval !== null) {
$results[self::CONFIG_SOURCE_LOCAL] =
$settings->willReadValue($key, $pval);
}
$pval = $this->getProjectConfig($key);
if ($pval !== null) {
$results[self::CONFIG_SOURCE_PROJECT] =
$settings->willReadValue($key, $pval);
}
$user_config = $this->readUserArcConfig();
$pval = idx($user_config, $key);
if ($pval !== null) {
$results[self::CONFIG_SOURCE_USER] =
$settings->willReadValue($key, $pval);
}
$system_config = $this->readSystemArcConfig();
$pval = idx($system_config, $key);
if ($pval !== null) {
$results[self::CONFIG_SOURCE_SYSTEM] =
$settings->willReadValue($key, $pval);
}
return $results;
}
/**
* Sets a runtime config value that takes precedence over any static
* config values.
*
* @param key Key to set.
* @param value The value of the key.
*
* @task config
*/
public function setRuntimeConfig($key, $value) {
$this->runtimeConfig[$key] = $value;
return $this;
}
/* -( Read/write config )--------------------------------------------------- */
public function readLocalArcConfig() {
if ($this->workingCopy) {
return $this->workingCopy->readLocalArcConfig();
}
return array();
}
public function writeLocalArcConfig(array $config) {
if ($this->workingCopy) {
return $this->workingCopy->writeLocalArcConfig($config);
}
- return false;
+ throw new Exception(pht("No working copy to write config to!"));
}
/**
* This is probably not the method you're looking for; try
* @{method:readUserArcConfig}.
*/
public function readUserConfigurationFile() {
$user_config = array();
$user_config_path = self::getUserConfigurationFileLocation();
if (Filesystem::pathExists($user_config_path)) {
if (!phutil_is_windows()) {
$mode = fileperms($user_config_path);
if (!$mode) {
throw new Exception("Unable to get perms of '{$user_config_path}'!");
}
if ($mode & 0177) {
// Mode should allow only owner access.
$prompt = "File permissions on your ~/.arcrc are too open. ".
"Fix them by chmod'ing to 600?";
if (!phutil_console_confirm($prompt, $default_no = false)) {
throw new ArcanistUsageException("Set ~/.arcrc to file mode 600.");
}
execx('chmod 600 %s', $user_config_path);
// Drop the stat cache so we don't read the old permissions if
// we end up here again. If we don't do this, we may prompt the user
// to fix permissions multiple times.
clearstatcache();
}
}
$user_config_data = Filesystem::readFile($user_config_path);
$user_config = json_decode($user_config_data, true);
if (!is_array($user_config)) {
throw new ArcanistUsageException(
"Your '~/.arcrc' file is not a valid JSON file.");
}
}
return $user_config;
}
/**
* This is probably not the method you're looking for; try
* @{method:writeUserArcConfig}.
*/
public function writeUserConfigurationFile($config) {
$json_encoder = new PhutilJSON();
$json = $json_encoder->encodeFormatted($config);
$path = self::getUserConfigurationFileLocation();
Filesystem::writeFile($path, $json);
if (!phutil_is_windows()) {
execx('chmod 600 %s', $path);
}
}
public function setUserConfigurationFileLocation($custom_arcrc) {
if (!Filesystem::pathExists($custom_arcrc)) {
throw new Exception(
"Custom arcrc file was specified, but it was not found!");
}
$this->customArcrcFilename = $custom_arcrc;
}
public function getUserConfigurationFileLocation() {
if (strlen($this->customArcrcFilename)) {
return $this->customArcrcFilename;
}
if (phutil_is_windows()) {
return getenv('APPDATA').'/.arcrc';
} else {
return getenv('HOME').'/.arcrc';
}
}
public function readUserArcConfig() {
return idx(self::readUserConfigurationFile(), 'config', array());
}
public function writeUserArcConfig(array $options) {
$config = self::readUserConfigurationFile();
$config['config'] = $options;
self::writeUserConfigurationFile($config);
}
public function getSystemArcConfigLocation() {
if (phutil_is_windows()) {
return Filesystem::resolvePath(
'Phabricator/Arcanist/config',
getenv('ProgramData'));
} else {
return '/etc/arcconfig';
}
}
public function readSystemArcConfig() {
$system_config = array();
$system_config_path = self::getSystemArcConfigLocation();
if (Filesystem::pathExists($system_config_path)) {
$file = Filesystem::readFile($system_config_path);
if ($file) {
$system_config = json_decode($file, true);
}
}
return $system_config;
}
}
diff --git a/src/workingcopyidentity/ArcanistWorkingCopyIdentity.php b/src/workingcopyidentity/ArcanistWorkingCopyIdentity.php
index 7c617153..e94dec93 100644
--- a/src/workingcopyidentity/ArcanistWorkingCopyIdentity.php
+++ b/src/workingcopyidentity/ArcanistWorkingCopyIdentity.php
@@ -1,341 +1,332 @@
<?php
/**
* Interfaces with basic information about the working copy.
*
*
* @task config
*
* @group workingcopy
*/
final class ArcanistWorkingCopyIdentity {
private $projectConfig;
private $projectRoot;
private $localConfig = array();
private $localMetaDir;
private $vcsType;
private $vcsRoot;
public static function newDummyWorkingCopy() {
return self::newFromPathWithConfig('/', array());
}
public static function newFromPath($path) {
return self::newFromPathWithConfig($path, null);
}
/**
* Locate all the information we need about a directory which we presume
* to be a working copy. Particularly, we want to discover:
*
* - Is the directory inside a working copy (hg, git, svn)?
* - If so, what is the root of the working copy?
* - Is there a `.arcconfig` file?
*
* This is complicated, mostly because Subversion has special rules. In
* particular:
*
* - Until 1.7, Subversion put a `.svn/` directory inside //every//
* directory in a working copy. After 1.7, it //only// puts one at the
* root.
* - We allow `.arcconfig` to appear anywhere in a Subversion working copy,
* and use the one closest to the directory.
* - Although we may use a `.arcconfig` from a subdirectory, we store
* metadata in the root's `.svn/`, because it's the only one guaranteed
* to exist.
*
* Users also do these kinds of things in the wild:
*
* - Put working copies inside other working copies.
* - Put working copies inside `.git/` directories.
* - Create `.arcconfig` files at `/.arcconfig`, `/home/.arcconfig`, etc.
*
* This method attempts to be robust against all sorts of possible
* misconfiguration.
*
* @param string Path to load information for, usually the current working
* directory (unless running unit tests).
* @param map|null Pass `null` to locate and load a `.arcconfig` file if one
* exists. Pass a map to use it to set configuration.
* @return ArcanistWorkingCopyIdentity Constructed working copy identity.
*/
private static function newFromPathWithConfig($path, $config) {
$project_root = null;
$vcs_root = null;
$vcs_type = null;
// First, find the outermost directory which is a Git, Mercurial or
// Subversion repository, if one exists. We go from the top because this
// makes it easier to identify the root of old SVN working copies (which
// have a ".svn/" directory inside every directory in the working copy) and
// gives us the right result if you have a Git repository inside a
// Subversion repository or something equally ridiculous.
$paths = Filesystem::walkToRoot($path);
$config_paths = array();
$paths = array_reverse($paths);
foreach ($paths as $path_key => $parent_path) {
$try = array(
'git' => $parent_path.'/.git',
'hg' => $parent_path.'/.hg',
'svn' => $parent_path.'/.svn',
);
foreach ($try as $vcs => $try_dir) {
if (!Filesystem::pathExists($try_dir)) {
continue;
}
// NOTE: We're distinguishing between the `$project_root` and the
// `$vcs_root` because they may not be the same in Subversion. Normally,
// they are identical. However, in Subversion, the `$vcs_root` is the
// base directory of the working copy (the directory which has the
// `.svn/` directory, after SVN 1.7), while the `$project_root` might
// be any subdirectory of the `$vcs_root`: it's the the directory
// closest to the current directory which contains a `.arcconfig`.
$project_root = $parent_path;
$vcs_root = $parent_path;
$vcs_type = $vcs;
if ($vcs == 'svn') {
// For Subversion, we'll look for a ".arcconfig" file here or in
// any subdirectory, starting at the deepest subdirectory.
$config_paths = array_slice($paths, $path_key);
$config_paths = array_reverse($config_paths);
} else {
// For Git and Mercurial, we'll only look for ".arcconfig" right here.
$config_paths = array($parent_path);
}
break;
}
}
$console = PhutilConsole::getConsole();
foreach ($config_paths as $config_path) {
$config_file = $config_path.'/.arcconfig';
if (Filesystem::pathExists($config_file)) {
// We always need to examine the filesystem to look for `.arcconfig`
// so we can set the project root correctly. We might or might not
// actually read the file: if the caller passed in configuration data,
// we'll ignore the actual file contents.
$project_root = $config_path;
if ($config === null) {
$console->writeLog(
"%s\n",
pht('Working Copy: Reading .arcconfig from "%s".', $config_file));
$config_data = Filesystem::readFile($config_file);
$config = self::parseRawConfigFile($config_data, $config_file);
}
break;
}
}
if ($config === null) {
// We didn't find a ".arcconfig" anywhere, so just use an empty array.
$config = array();
}
if ($project_root === null) {
// We aren't in a working directory at all. This is fine if we're
// running a command like "arc help". If we're running something that
// requires a working directory, an exception will be raised a little
// later on.
$console->writeLog(
"%s\n",
pht('Working Copy: Path "%s" is not in any working copy.', $path));
return new ArcanistWorkingCopyIdentity($path, $config);
}
$console->writeLog(
"%s\n",
pht(
'Working Copy: Path "%s" is part of `%s` working copy "%s".',
$path,
$vcs_type,
$vcs_root));
$console->writeLog(
"%s\n",
pht(
'Working Copy: Project root is at "%s".',
$project_root));
$identity = new ArcanistWorkingCopyIdentity($project_root, $config);
$identity->localMetaDir = $vcs_root.'/.'.$vcs_type;
$identity->localConfig = $identity->readLocalArcConfig();
$identity->vcsType = $vcs_type;
$identity->vcsRoot = $vcs_root;
return $identity;
}
public static function newFromRootAndConfigFile(
$root,
$config_raw,
$from_where) {
if ($config_raw === null) {
$config = array();
} else {
$config = self::parseRawConfigFile($config_raw, $from_where);
}
return self::newFromPathWithConfig($root, $config);
}
private static function parseRawConfigFile($raw_config, $from_where) {
$proj = json_decode($raw_config, true);
if (!is_array($proj)) {
throw new Exception(
"Unable to parse '.arcconfig' file '{$from_where}'. The file contents ".
"should be valid JSON.\n\n".
"FILE CONTENTS\n".
substr($raw_config, 0, 2048));
}
$required_keys = array(
'project_id',
);
foreach ($required_keys as $key) {
if (!array_key_exists($key, $proj)) {
throw new Exception(
"Required key '{$key}' is missing from '.arcconfig' file ".
"'{$from_where}'.");
}
}
return $proj;
}
private function __construct($root, array $config) {
$this->projectRoot = $root;
$this->projectConfig = $config;
}
public function getProjectID() {
return $this->getProjectConfig('project_id');
}
public function getProjectRoot() {
return $this->projectRoot;
}
public function getProjectPath($to_file) {
return $this->projectRoot.'/'.$to_file;
}
public function getVCSType() {
return $this->vcsType;
}
public function getVCSRoot() {
return $this->vcsRoot;
}
/* -( Config )------------------------------------------------------------- */
public function readProjectConfig() {
return $this->projectConfig;
}
/**
* Deprecated; use @{method:getProjectConfig}.
*/
public function getConfig($key, $default = null) {
return $this->getProjectConfig($key, $default);
}
/**
* Read a configuration directive from project configuration. This reads ONLY
* permanent project configuration (i.e., ".arcconfig"), not other
* configuration sources. See @{method:getConfigFromAnySource} to read from
* user configuration.
*
* @param key Key to read.
* @param wild Default value if key is not found.
* @return wild Value, or default value if not found.
*
* @task config
*/
public function getProjectConfig($key, $default = null) {
$settings = new ArcanistSettings();
$pval = idx($this->projectConfig, $key);
// Test for older names in the per-project config only, since
// they've only been used there.
if ($pval === null) {
$legacy = $settings->getLegacyName($key);
if ($legacy) {
$pval = $this->getProjectConfig($legacy);
}
}
if ($pval === null) {
$pval = $default;
} else {
$pval = $settings->willReadValue($key, $pval);
}
return $pval;
}
/**
* Read a configuration directive from local configuration. This
* reads ONLY the per-working copy configuration,
* i.e. .(git|hg|svn)/arc/config, and not other configuration
* sources. See @{method:getConfigFromAnySource} to read from any
* config source or @{method:getProjectConfig} to read permanent
* project-level config.
*
* @task config
*/
public function getLocalConfig($key, $default=null) {
return idx($this->localConfig, $key, $default);
}
public function readLocalArcConfig() {
if (strlen($this->localMetaDir)) {
$local_path = Filesystem::resolvePath(
'arc/config',
$this->localMetaDir);
if (Filesystem::pathExists($local_path)) {
$file = Filesystem::readFile($local_path);
if ($file) {
return json_decode($file, true);
}
}
}
return array();
}
public function writeLocalArcConfig(array $config) {
+ $json_encoder = new PhutilJSON();
+ $json = $json_encoder->encodeFormatted($config);
+
$dir = $this->localMetaDir;
if (!strlen($dir)) {
- return false;
+ throw new Exception(pht('No working copy to write config into!'));
}
- if (!Filesystem::pathExists($dir)) {
- try {
- Filesystem::createDirectory($dir);
- } catch (Exception $ex) {
- return false;
- }
- }
-
- $json_encoder = new PhutilJSON();
- $json = $json_encoder->encodeFormatted($config);
-
- $config_file = Filesystem::resolvePath('arc/config', $dir);
- try {
- Filesystem::writeFile($config_file, $json);
- } catch (FilesystemException $ex) {
- return false;
+ $local_dir = $dir.DIRECTORY_SEPARATOR.'arc';
+ if (!Filesystem::pathExists($local_dir)) {
+ Filesystem::createDirectory($local_dir, 0755);
}
- return true;
+ $config_file = $local_dir.DIRECTORY_SEPARATOR.'config';
+ Filesystem::writeFile($config_file, $json);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 23, 23:27 (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
548037
Default Alt Text
(19 KB)
Attached To
Mode
R118 Arcanist - fork
Attached
Detach File
Event Timeline
Log In to Comment