diff --git a/conf/default.conf.php b/conf/default.conf.php index d0b97256f5..229c9e4335 100644 --- a/conf/default.conf.php +++ b/conf/default.conf.php @@ -27,6 +27,10 @@ return array( // contain the right links. 'phabricator.production-uri' => null, + // Setting this to 'true' will invoke a special setup mode which helps guide + // you through setting up Phabricator. + 'phabricator.setup' => false, + // The default PHID for users who haven't uploaded a profile image. It should // be 50x50px. 'user.default-profile-image-phid' => 'PHID-FILE-4d61229816cfe6f2b2a3', @@ -78,7 +82,7 @@ return array( // some data like queries and stack traces, so you should be careful about // turning it on in production (although users can not normally see it, even // if the deployment configuration enables it). - 'darkconsole.enabled' => true, + 'darkconsole.enabled' => false, // Always enable DarkConsole, even for logged out users. This potentially // exposes sensitive information to users, so make sure untrusted users can diff --git a/conf/development.conf.php b/conf/development.conf.php index 85673eab81..51a1c1a6f0 100644 --- a/conf/development.conf.php +++ b/conf/development.conf.php @@ -18,5 +18,6 @@ return array( + 'darkconsole.enabled' => true, ) + phabricator_read_config_file('default'); diff --git a/conf/production.conf.php b/conf/production.conf.php index f4dd87dcb5..7228521c76 100644 --- a/conf/production.conf.php +++ b/conf/production.conf.php @@ -18,7 +18,5 @@ return array( - 'darkconsole.enabled' => false, - ) + phabricator_read_config_file('default'); diff --git a/conf/setup.conf.php b/conf/setup.conf.php deleted file mode 100644 index 85673eab81..0000000000 --- a/conf/setup.conf.php +++ /dev/null @@ -1,22 +0,0 @@ -withSuffix('sql'); - $results = $finder->find(); - - $patches = array(); - foreach ($results as $r) { - $matches = array(); - if (preg_match('/(\d+)\..*\.sql$/', $r, $matches)) { - $patches[] = array('version' => (int)$matches[1], - 'file' => $r); - } else { - print - "*** WARNING : File {$r} does not follow the normal naming ". - "convention. ***\n"; - } - } - - // Files are in some 'random' order returned by the operating system - // We need to apply them in proper order - $patches = isort($patches, 'version'); + $patches = PhabricatorSQLPatchList::getPatchList(); $patch_applied = false; foreach ($patches as $patch) { @@ -124,16 +104,15 @@ END; continue; } - print "Applying patch {$patch['file']}\n"; - - $path = Filesystem::resolvePath($patches_dir.$patch['file']); + $short_name = basename($patch['path']); + print "Applying patch {$short_name}...\n"; list($stdout, $stderr) = execx( "mysql --user=%s --password=%s --host=%s < %s", $conn_user, $conn_pass, $conn_host, - $path); + $patch['path']); if ($stderr) { print $stderr; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 571c13726f..d5ff859d10 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -425,6 +425,7 @@ phutil_register_library_map(array( 'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'applications/repository/daemon/commitdiscovery/svn', 'PhabricatorRepositorySvnCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/svn', 'PhabricatorRepositoryType' => 'applications/repository/constants/repositorytype', + 'PhabricatorSQLPatchList' => 'infrastructure/setup/sql', 'PhabricatorSearchAbstractDocument' => 'applications/search/index/abstractdocument', 'PhabricatorSearchBaseController' => 'applications/search/controller/base', 'PhabricatorSearchController' => 'applications/search/controller/search', @@ -440,6 +441,7 @@ phutil_register_library_map(array( 'PhabricatorSearchMySQLExecutor' => 'applications/search/execute/mysql', 'PhabricatorSearchQuery' => 'applications/search/storage/query', 'PhabricatorSearchRelationship' => 'applications/search/constants/relationship', + 'PhabricatorSetup' => 'infrastructure/setup', 'PhabricatorStandardPageView' => 'view/page/standard', 'PhabricatorStatusController' => 'applications/status/base', 'PhabricatorTaskmasterDaemon' => 'infrastructure/daemon/workers/taskmaster', diff --git a/src/docs/configuration_guide.diviner b/src/docs/configuration_guide.diviner index fd247d159a..e072bbed5b 100644 --- a/src/docs/configuration_guide.diviner +++ b/src/docs/configuration_guide.diviner @@ -16,9 +16,32 @@ schemata into it. First, load the initial database schema. mysql -uroot < path/to/phabricator/resources/sql/init/initialize.sql -After this you need to upgrade the schema see @{article:Upgrading Schema}, +After this you need to upgrade the schema (see @{article:Upgrading Schema}), but you need to finish the rest of the configuration first. += Configuring Phabricator = + +Create a new file here: + + path/to/phabricator/conf/custom/myconfig.conf.php + +...where ##myconfig## is some name which identifies your installation. Put this +in the file: + + true, + + ) + phabricator_read_config_file('production'); + +For the last line, you can also use ##'development'## instead of +##'production'## if you are planning to develop Phabricator itself. This will +turn on some debugging features. + = Configuring Apache = Get Apache running and verify it's serving a test page. Consult the Apache @@ -49,27 +72,38 @@ this: RewriteRule ^/favicon.ico - [L,QSA] RewriteRule ^(.*)$ /index.php?__path__=$1 [L,QSA] - # This will use "setup" defaults for configuration options, which will - # expose error messages. Before you make the install public, you should - # change this to "production" and/or customize your configuration. See - # the next section for details. - SetEnv PHABRICATOR_ENV setup + # This will use the config file you set up in the previous step. If you + # called it something other than 'myconfig', put that here. + SetEnv PHABRICATOR_ENV custom/myconfig Now, restart apache and navigate to whichever subdomain you set up. You should -either see the Phabricator login screen, which means you're all set, or some -useful error message telling you what else you need to fix (for instance, you -may need to set up MySQL credentials). If you see something else, you did -something very wrong and/or this document lied to you. +either see the Phabricator setup screen, which is a simple text page that looks +something like this: + + PHABRICATOR SETUP + + This setup mode will guide you through setting up your Phabricator + configuration. + + >>> REQUIRED PHP EXTENSIONS ------------------------------------------------ + ... + +If you see this, you're in good shape. Follow the instructions and correct any +problems setup detects. If you don't see it but you do see a useful error +message, try to fix that. If neither of these cover you, something is wrong. +If you can't figure it out, come get help in IRC or on the mailing list (see +http://phabricator.org/ for links). = Configuring Phabricator = -Now that basic setup is complete, you should configure Phabricator. Phabricator -configuration options which control how the applications behave are stored here: +Now that basic setup is complete, you should configure Phabricator for your +installation. Phabricator configuration options which control how the +applications behave are documented here: - /path/to/phabricator/conf/ + /path/to/phabricator/conf/default.conf.php -There are several configuration templates: +There are several builtin configurations: - ##default.conf.php##: root configuration, lists every configuration option and sets some default for it. Look in this file to figure out what you can @@ -80,44 +114,14 @@ There are several configuration templates: changes to Phabricator itself. - ##production.conf.php##: pulls in ##default.conf.php##, but overrides some configuration options to provide better values for a production install. - Once you've completed setup, you should switch to this configuration or - one based upon it. - - ##setup.conf.php##: pulls in ##default.conf.php##, but sets some flags that - make it easier to set up a Phabricator install. Switch away from this before - deploying a production install. - -While you can use these templates as-is, you'll probably want to set up custom -configuration. To do this, create a new file: - - /path/to/phabricator/conf/custom/myconfig.conf.php - -Put this in the file: - - 'some_value', - - ) + phabricator_read_config_file('production'); - -This will create a new config called "custom/myconfig" which uses the -"production" config as the default but allows you to override options. You can -select it by editing the VirtualHost or Directory entry you set up when -configuring Apache: - - - # ... - SetEnv PHABRICATOR_ENV custom/myconfig - # ... - -Now, look through ##default.conf.php## and override any options you want to -change by providing overrides in ##myconfig.conf.php##. +To actually configure your install, edit your ##custom/myconfig.conf.php## file +and override values from either the ##'production'## or ##'development'## +configurations. You should not edit the builtin configurations directly because +that will make upgrading Phabricator more difficult in the future. = Upgrading Schema = After you have configured Phabricator, you need to upgrade the database -schema, see @{article:Upgrading Schema}. You'll also need to do this after you +schema -- see @{article:Upgrading Schema}. You'll also need to do this after you update the code in the future. diff --git a/src/infrastructure/setup/PhabricatorSetup.php b/src/infrastructure/setup/PhabricatorSetup.php new file mode 100644 index 0000000000..842c84eee3 --- /dev/null +++ b/src/infrastructure/setup/PhabricatorSetup.php @@ -0,0 +1,298 @@ + 5) { + self::writeNote( + "Your MySQL connect timeout is very high ({$timeout} seconds). ". + "Consider reducing it by setting 'mysql.connect_timeout' in your ". + "php.ini."); + } + + self::write(" okay Trying to connect to MySQL database ". + "{$conn_user}@{$conn_host}...\n"); + + ini_set('mysql.connect_timeout', 2); + + $conn_raw = new AphrontMySQLDatabaseConnection( + array( + 'user' => $conn_user, + 'pass' => $conn_pass, + 'host' => $conn_host, + 'database' => null, + )); + + try { + queryfx($conn_raw, 'SELECT 1'); + self::write(" okay Connection successful!\n"); + } catch (AphrontQueryConnectionException $ex) { + self::writeFailure(); + self::write( + "Setup failure! Unable to connect to MySQL database ". + "'{$conn_host}' with user '{$conn_user}'. Edit Phabricator ". + "configuration keys 'mysql.user', 'mysql.host' and 'mysql.pass' to ". + "enable Phabricator to connect."); + return; + } + + $databases = queryfx_all($conn_raw, 'SHOW DATABASES'); + $databases = ipull($databases, 'Database'); + $databases = array_fill_keys($databases, true); + if (empty($databases['phabricator_meta_data'])) { + self::writeFailure(); + self::write( + "Setup failure! You haven't loaded the 'initialize.sql' file into ". + "MySQL. This file initializes necessary databases. See this guide for ". + "instructions:\n"); + self::writeDoc('article/Configuration_Guide.html'); + return; + } else { + self::write(" okay Databases have been initialized.\n"); + } + + $schema_version = queryfx_one( + $conn_raw, + 'SELECT version FROM phabricator_meta_data.schema_version'); + $schema_version = idx($schema_version, 'version', 'null'); + + $expect = PhabricatorSQLPatchList::getExpectedSchemaVersion(); + if ($schema_version != $expect) { + self::writeFailure(); + self::write( + "Setup failure! You haven't upgraded your database schema to the ". + "latest version. Expected version is '{$expect}', but your local ". + "version is '{$schema_version}'. See this guide for instructions:\n"); + self::writeDoc('article/Upgrading_Schema.html'); + return; + } else { + self::write(" okay Database schema are up to date (v{$expect}).\n"); + } + + self::write("[OKAY] Database configuration OKAY\n"); + + + self::writeHeader('SUCCESS!'); + self::write( + "Congratulations! Your setup seems mostly correct, or at least fairly ". + "reasonable.\n\n". + "*** NEXT STEP ***\n". + "Edit your configuration file (conf/{$env}.conf.php) and remove the ". + "'phabricator.setup' line to finish installation."); + + } + + public static function requireExtension($extension) { + if (extension_loaded($extension)) { + self::write(" okay Extension '{$extension}' installed.\n"); + return true; + } else { + self::write("[FAIL] Extension '{$extension}' is NOT INSTALLED!\n"); + return false; + } + } + + private static function writeFailure() { + self::write("\n\n<<< *** FAILURE! *** >>>\n"); + } + + private static function write($str) { + echo $str; + ob_flush(); + flush(); + + // This, uh, makes it look cool. -_- + usleep(40000); + } + + private static function writeNote($note) { + self::write( + 'Note: '.wordwrap($note, 75, "\n ", true)."\n\n"); + } + + public static function writeHeader($header) { + $template = '>>>'.str_repeat('-', 77); + $template = substr_replace( + $template, + ' '.$header.' ', + 3, + strlen($header) + 4); + self::write("\n\n{$template}\n\n"); + } + + public static function writeDoc($doc) { + self::write( + "\n". + ' http://phabricator.com/docs/phabricator/'.$doc. + "\n\n"); + } + +} diff --git a/src/infrastructure/setup/__init__.php b/src/infrastructure/setup/__init__.php new file mode 100644 index 0000000000..e11efddfe6 --- /dev/null +++ b/src/infrastructure/setup/__init__.php @@ -0,0 +1,20 @@ +withSuffix('sql'); + $results = $finder->find(); + + $patches = array(); + foreach ($results as $path) { + $matches = array(); + if (preg_match('/(\d+)\..*\.sql$/', $path, $matches)) { + $patches[] = array( + 'version' => (int)$matches[1], + 'path' => $patches_dir.$path, + ); + } else { + throw new Exception("Patch file '{$path}' is not properly named."); + } + } + + // Files are in some 'random' order returned by the operating system + // We need to apply them in proper order + $patches = isort($patches, 'version'); + + return $patches; + } + + public static function getExpectedSchemaVersion() { + $patches = self::getPatchList(); + $versions = ipull($patches, 'version'); + $max_version = max($versions); + return $max_version; + } + +} diff --git a/src/infrastructure/setup/sql/__init__.php b/src/infrastructure/setup/sql/__init__.php new file mode 100644 index 0000000000..248a60848c --- /dev/null +++ b/src/infrastructure/setup/sql/__init__.php @@ -0,0 +1,14 @@ +