@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add a local configuration source and a non-environmental ENV config source

Summary:
See discussion in T2221. Before we can move configuration to the database, we have a bootstrapping problem: we need database credentials to live //somewhere// if we can't guess them (and we can only really guess localhost / root / no password).

Some options for this are:

- Have them live in ENV variables.
- These are often somewhat unfamiliar to users.
- Scripts would become a huge pain -- you'd have to dump a bunch of stuff into ENV.
- Some environments have limited ability to set ENV vars.
- SSH is also a pain.
- Have them live in a normal config file.
- This probably isn't really too awful, but:
- Since we deploy/upgrade with git, we can't currently let them edit a file which already exists, or their working copy will become dirty.
- So they have to copy or create a file, then edit it.
- The biggest issue I have with this is that it will be difficult to give specific, easily-followed directions from Setup. The instructions need to be like "Copy template.conf.php to real.conf.php, then edit these keys: x, y, z". This isn't as easy to follow as "run script Y".
- Have them live in an abnormal config file with script access (this diff).
- I think this is a little better than a normal config file, because we can tell users 'run phabricator/bin/config set mysql.user phabricator' and such, which is easier to follow than editing a config file.

I think this is only a marginal improvement over a normal config file and am open to arguments against this approach, but I think it will be a little easier for users to deal with than a normal config file. In most cases they should only need to store three values in this file -- db user/host/pass -- since once we have those we can bootstrap everything else. Normal config files also aren't going away for more advanced users, we're just offering a simple alternative for most users.

This also adds an ENVIRONMENT file as an alternative to PHABRICATOR_ENV. This is just a simple way to specify the environment if you don't have convenient access to env vars.

Test Plan: Ran `config set x y`, verified writes. Wrote to ENVIRONMENT, ran `PHABRICATOR_ENV= ./bin/repository`.

Reviewers: btrahan, vrana, codeblock

Reviewed By: codeblock

CC: aran

Maniphest Tasks: T2221

Differential Revision: https://secure.phabricator.com/D4294

+163 -7
+12 -4
.gitignore
··· 1 1 .DS_Store 2 2 ._* 3 - /docs/ 4 - /src/.phutil_module_cache 5 - /conf/custom/* 6 3 /webroot/rsrc/custom 7 - /.divinercache 8 4 .#* 9 5 *# 10 6 *~ ··· 15 11 16 12 # Arcanist scratch directory 17 13 /.arc 14 + 15 + # Diviner 16 + /docs/ 17 + /.divinercache 18 + 19 + # libphutil 20 + /src/.phutil_module_cache 21 + 22 + # Configuration 23 + /conf/custom/* 24 + /conf/local/local.json 25 + /conf/local/ENVIRONMENT
+1
bin/config
··· 1 + ../scripts/setup/manage_config.php
conf/local/.keep

This is a binary file and will not be displayed.

+22
scripts/setup/manage_config.php
··· 1 + #!/usr/bin/env php 2 + <?php 3 + 4 + $root = dirname(dirname(dirname(__FILE__))); 5 + require_once $root.'/scripts/__init_script__.php'; 6 + 7 + $args = new PhutilArgumentParser($argv); 8 + $args->setTagline('manage configuration'); 9 + $args->setSynopsis(<<<EOSYNOPSIS 10 + **config** __command__ [__options__] 11 + Manage Phabricator configuration. 12 + 13 + EOSYNOPSIS 14 + ); 15 + $args->parseStandardArguments(); 16 + 17 + $workflows = array( 18 + new PhabricatorConfigManagementSetWorkflow(), 19 + new PhutilHelpArgumentWorkflow(), 20 + ); 21 + 22 + $args->parseWorkflows($workflows);
+6
src/__phutil_library_map__.php
··· 687 687 'PhabricatorConfigEntryDAO' => 'applications/config/storage/PhabricatorConfigEntryDAO.php', 688 688 'PhabricatorConfigFileSource' => 'infrastructure/env/PhabricatorConfigFileSource.php', 689 689 'PhabricatorConfigListController' => 'applications/config/controller/PhabricatorConfigListController.php', 690 + 'PhabricatorConfigLocalSource' => 'infrastructure/env/PhabricatorConfigLocalSource.php', 691 + 'PhabricatorConfigManagementSetWorkflow' => 'infrastructure/env/management/PhabricatorConfigManagementSetWorkflow.php', 692 + 'PhabricatorConfigManagementWorkflow' => 'infrastructure/env/management/PhabricatorConfigManagementWorkflow.php', 690 693 'PhabricatorConfigProxySource' => 'infrastructure/env/PhabricatorConfigProxySource.php', 691 694 'PhabricatorConfigSource' => 'infrastructure/env/PhabricatorConfigSource.php', 692 695 'PhabricatorConfigStackSource' => 'infrastructure/env/PhabricatorConfigStackSource.php', ··· 2000 2003 'PhabricatorConfigEntryDAO' => 'PhabricatorLiskDAO', 2001 2004 'PhabricatorConfigFileSource' => 'PhabricatorConfigProxySource', 2002 2005 'PhabricatorConfigListController' => 'PhabricatorConfigController', 2006 + 'PhabricatorConfigLocalSource' => 'PhabricatorConfigProxySource', 2007 + 'PhabricatorConfigManagementSetWorkflow' => 'PhabricatorConfigManagementWorkflow', 2008 + 'PhabricatorConfigManagementWorkflow' => 'PhutilArgumentWorkflow', 2003 2009 'PhabricatorConfigProxySource' => 'PhabricatorConfigSource', 2004 2010 'PhabricatorConfigStackSource' => 'PhabricatorConfigSource', 2005 2011 'PhabricatorContentSourceView' => 'AphrontView',
+51
src/infrastructure/env/PhabricatorConfigLocalSource.php
··· 1 + <?php 2 + 3 + final class PhabricatorConfigLocalSource 4 + extends PhabricatorConfigProxySource { 5 + 6 + public function __construct() { 7 + $config = $this->loadConfig(); 8 + $this->setSource(new PhabricatorConfigDictionarySource($config)); 9 + } 10 + 11 + public function setKeys(array $keys) { 12 + $result = parent::setKeys($keys); 13 + $this->saveConfig(); 14 + return $result; 15 + } 16 + 17 + public function deleteKeys(array $keys) { 18 + $result = parent::deleteKeys($keys); 19 + $this->saveConfig(); 20 + return parent::deleteKeys($keys); 21 + } 22 + 23 + private function loadConfig() { 24 + $path = $this->getConfigPath(); 25 + if (@file_exists($path)) { 26 + $data = @file_get_contents($path); 27 + if ($data) { 28 + $data = json_decode($data, true); 29 + if (is_array($data)) { 30 + return $data; 31 + } 32 + } 33 + } 34 + 35 + return array(); 36 + } 37 + 38 + private function saveConfig() { 39 + $config = $this->getSource()->getAllKeys(); 40 + $json = new PhutilJSON(); 41 + $data = $json->encodeFormatted($config); 42 + Filesystem::writeFile($this->getConfigPath(), $data); 43 + } 44 + 45 + private function getConfigPath() { 46 + $root = dirname(phutil_get_library_root('phabricator')); 47 + $path = $root.'/conf/local/local.json'; 48 + return $path; 49 + } 50 + 51 + }
+3 -3
src/infrastructure/env/PhabricatorConfigProxySource.php
··· 29 29 } 30 30 31 31 public function canWrite() { 32 - return $this->getSource->canWrite(); 32 + return $this->getSource()->canWrite(); 33 33 } 34 34 35 35 public function setKeys(array $keys) { 36 - return $this->getSource->setKeys(); 36 + return $this->getSource()->setKeys($keys); 37 37 } 38 38 39 39 public function deleteKeys(array $keys) { 40 - return $this->getSource->deleteKeys(); 40 + return $this->getSource()->deleteKeys(); 41 41 } 42 42 43 43 }
+8
src/infrastructure/env/PhabricatorEnv.php
··· 139 139 $env = idx($_ENV, $env_var); 140 140 } 141 141 142 + if (!$env) { 143 + $root = dirname(phutil_get_library_root('phabricator')); 144 + $path = $root.'/conf/local/ENVIRONMENT'; 145 + if (Filesystem::pathExists($path)) { 146 + $env = trim(Filesystem::readFile($path)); 147 + } 148 + } 149 + 142 150 return $env; 143 151 } 144 152
+50
src/infrastructure/env/management/PhabricatorConfigManagementSetWorkflow.php
··· 1 + <?php 2 + 3 + final class PhabricatorConfigManagementSetWorkflow 4 + extends PhabricatorConfigManagementWorkflow { 5 + 6 + protected function didConstruct() { 7 + $this 8 + ->setName('set') 9 + ->setExamples('**set** __key__ __value__') 10 + ->setSynopsis('Set a local configuration value.') 11 + ->setArguments( 12 + array( 13 + array( 14 + 'name' => 'args', 15 + 'wildcard' => true, 16 + ), 17 + )); 18 + } 19 + 20 + public function execute(PhutilArgumentParser $args) { 21 + $console = PhutilConsole::getConsole(); 22 + 23 + $argv = $args->getArg('args'); 24 + if (count($argv) == 0) { 25 + throw new PhutilArgumentUsageException( 26 + "Specify a configuration key and a value to set it to."); 27 + } 28 + 29 + $key = $argv[0]; 30 + 31 + if (count($argv) == 1) { 32 + throw new PhutilArgumentUsageException( 33 + "Specify a value to set the key '{$key}' to."); 34 + } 35 + 36 + $value = $argv[1]; 37 + 38 + if (count($argv) > 2) { 39 + throw new PhutilArgumentUsageException( 40 + "Too many arguments: expected one key and one value."); 41 + } 42 + 43 + $config = new PhabricatorConfigLocalSource(); 44 + $config->setKeys(array($key => $value)); 45 + 46 + $console->writeOut( 47 + pht("Set '%s' to '%s' in local configuration.", $key, $value)."\n"); 48 + } 49 + 50 + }
+10
src/infrastructure/env/management/PhabricatorConfigManagementWorkflow.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorConfigManagementWorkflow 4 + extends PhutilArgumentWorkflow { 5 + 6 + final public function isExecutable() { 7 + return true; 8 + } 9 + 10 + }