@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.

Make SQL patch management DAG-based and provide namespace support

Summary:
This addresses three issues with the current patch management system:

# Two people developing at the same time often pick the same SQL patch number, and then have to go rename it. The system catches this, but it's silly.
# Second/third-party developers can't use the same system to manage auxiliary storage they may want to add.
# There's no way to build mock databases for unit tests that need to do reads.

To resolve these things, you can now name your patches whatever you want and conflicts are just merge conflicts, which are less of a pain to fix than filename conflicts.

Dependencies are now a DAG, with implicit dependencies created on the prior patch if no dependencies are specified. Developers can add new concrete subclasses of `PhabricatorSQLPatchList` to add storage management, and define the dependency branchpoint of their patches so they apply in the correct order (although, generally, they should not depend on the mainline patches, presumably).

The commands `storage upgrade --namespace test1234` and `storage destroy --namespace test1234` will allow unit tests to build and destroy MySQL storage.

A "quickstart" mode allows an upgrade from scratch in ~1200ms. Destruction takes about 200ms. These seem like fairily reasonable costs to actually use in tests. Building from scratch patch-by-patch takes about 6000ms.

Test Plan:
- Created new databases from scratch with and without quickstart in a separate test namespace. Pointed the webapp at the test namespaces, browsed around, everything looked good.
- Compared quickstart and no-quickstart dump states, they're identical except for mysqldump timestamps and a few similar things.
- Upgraded a legacy database to the new storage format.
- Destroyed / dumped storage.

Reviewers: edward, vrana, btrahan, jungejason

Reviewed By: btrahan

CC: aran, nh

Maniphest Tasks: T140, T345

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

+2168 -354
+1
bin/storage
··· 1 + ../scripts/sql/manage_storage.php
+11 -1
scripts/__init_script__.php
··· 42 42 ? $_SERVER['PHABRICATOR_ENV'] 43 43 : getenv('PHABRICATOR_ENV'); 44 44 if (!$env) { 45 - echo "Define PHABRICATOR_ENV before running this script.\n"; 45 + phutil_require_module('phutil', 'console'); 46 + echo phutil_console_wrap( 47 + phutil_console_format( 48 + "**ERROR**: PHABRICATOR_ENV Not Set\n\n". 49 + "Define the __PHABRICATOR_ENV__ environment variable before running ". 50 + "this script. You can do it on the command line like this:\n\n". 51 + " $ PHABRICATOR_ENV=__custom/myconfig__ %s ...\n\n". 52 + "Replace __custom/myconfig__ with the path to your configuration file. ". 53 + "For more information, see the 'Configuration Guide' in the ". 54 + "Phabricator documentation.\n\n", 55 + $argv[0])); 46 56 exit(1); 47 57 } 48 58
+1 -1
scripts/install/update_phabricator.sh
··· 49 49 sudo /etc/init.d/httpd stop 50 50 51 51 # Upgrade the database schema. 52 - $ROOT/phabricator/scripts/sql/upgrade_schema.php -f 52 + $ROOT/phabricator/bin/storage upgrade --force 53 53 54 54 # Restart apache. 55 55 sudo /etc/init.d/httpd start
+123
scripts/sql/manage_storage.php
··· 1 + #!/usr/bin/env php 2 + <?php 3 + 4 + /* 5 + * Copyright 2012 Facebook, Inc. 6 + * 7 + * Licensed under the Apache License, Version 2.0 (the "License"); 8 + * you may not use this file except in compliance with the License. 9 + * You may obtain a copy of the License at 10 + * 11 + * http://www.apache.org/licenses/LICENSE-2.0 12 + * 13 + * Unless required by applicable law or agreed to in writing, software 14 + * distributed under the License is distributed on an "AS IS" BASIS, 15 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 + * See the License for the specific language governing permissions and 17 + * limitations under the License. 18 + */ 19 + 20 + $root = dirname(dirname(dirname(__FILE__))); 21 + require_once $root.'/scripts/__init_script__.php'; 22 + 23 + $args = new PhutilArgumentParser($argv); 24 + $args->setTagline('manage Phabricator storage and schemata'); 25 + $args->setSynopsis(<<<EOHELP 26 + **storage** __workflow__ [__options__] 27 + Manage Phabricator database storage and schema versioning. 28 + 29 + **storage** upgrade 30 + Initialize or upgrade Phabricator storage. 31 + 32 + **storage** upgrade --user __root__ --password __hunter2__ 33 + Use administrative credentials for schema changes. 34 + EOHELP 35 + ); 36 + $args->parseStandardArguments(); 37 + 38 + $conf = PhabricatorEnv::newObjectFromConfig('mysql.configuration-provider'); 39 + 40 + $default_user = $conf->getUser(); 41 + $default_password = $conf->getPassword(); 42 + $default_host = $conf->getHost(); 43 + $default_namespace = 'phabricator'; 44 + 45 + try { 46 + $args->parsePartial( 47 + array( 48 + array( 49 + 'name' => 'force', 50 + 'short' => 'f', 51 + 'help' => 'Do not prompt before performing dangerous operations.', 52 + ), 53 + array( 54 + 'name' => 'user', 55 + 'short' => 'u', 56 + 'param' => 'username', 57 + 'default' => $default_user, 58 + 'help' => "Connect with __username__ instead of the configured ". 59 + "default ('{$default_user}').", 60 + ), 61 + array( 62 + 'name' => 'password', 63 + 'short' => 'p', 64 + 'param' => 'password', 65 + 'default' => $default_password, 66 + 'help' => 'Use __password__ instead of the configured default.', 67 + ), 68 + array( 69 + 'name' => 'namespace', 70 + 'param' => 'name', 71 + 'default' => $default_namespace, 72 + 'help' => "Use namespace __namespace__ instead of the configured ". 73 + "default ('{$default_namespace}'). This is an advanced ". 74 + "feature used by unit tests; you should not normally ". 75 + "use this flag.", 76 + ), 77 + array( 78 + 'name' => 'dryrun', 79 + 'help' => 'Do not actually change anything, just show what would be '. 80 + 'changed.', 81 + ), 82 + )); 83 + } catch (PhutilArgumentUsageException $ex) { 84 + $args->printUsageException($ex); 85 + exit(77); 86 + } 87 + 88 + $api = new PhabricatorStorageManagementAPI(); 89 + $api->setUser($args->getArg('user')); 90 + $api->setHost($default_host); 91 + $api->setPassword($args->getArg('password')); 92 + $api->setNamespace($args->getArg('namespace')); 93 + 94 + try { 95 + queryfx( 96 + $api->getConn('meta_data', $select_database = false), 97 + 'SELECT 1'); 98 + } catch (AphrontQueryException $ex) { 99 + echo phutil_console_format( 100 + "**%s**: %s\n", 101 + 'Unable To Connect', 102 + $ex->getMessage()); 103 + exit(1); 104 + } 105 + 106 + $workflows = array( 107 + new PhabricatorStorageManagementDatabasesWorkflow(), 108 + new PhabricatorStorageManagementDestroyWorkflow(), 109 + new PhabricatorStorageManagementDumpWorkflow(), 110 + new PhabricatorStorageManagementStatusWorkflow(), 111 + new PhabricatorStorageManagementUpgradeWorkflow(), 112 + ); 113 + 114 + $patches = PhabricatorSQLPatchList::buildAllPatches(); 115 + 116 + foreach ($workflows as $workflow) { 117 + $workflow->setAPI($api); 118 + $workflow->setPatches($patches); 119 + } 120 + 121 + $workflows[] = new PhutilHelpArgumentWorkflow(); 122 + 123 + $args->parseWorkflows($workflows);
+7 -201
scripts/sql/upgrade_schema.php
··· 17 17 * limitations under the License. 18 18 */ 19 19 20 - $root = dirname(dirname(dirname(__FILE__))); 21 - require_once $root.'/scripts/__init_script__.php'; 22 - 23 - phutil_require_module('phutil', 'console'); 24 - phutil_require_module('phabricator', 'infrastructure/setup/sql'); 25 - 26 - define('SCHEMA_VERSION_TABLE_NAME', 'schema_version'); 27 - 28 - // TODO: getopt() is super terrible, move to something less terrible. 29 - $options = getopt('fhdv:u:p:m:') + array( 30 - 'v' => null, // Upgrade from specific version 31 - 'u' => null, // Override MySQL User 32 - 'p' => null, // Override MySQL Pass 33 - 'm' => null, // Specify max version to upgrade to 34 - ); 35 - 36 - foreach (array('h', 'f', 'd') as $key) { 37 - // By default, these keys are set to 'false' to indicate that the flag was 38 - // passed. 39 - if (array_key_exists($key, $options)) { 40 - $options[$key] = true; 41 - } 42 - } 43 - 44 - if (!empty($options['h']) || ($options['v'] && !is_numeric($options['v'])) 45 - || ($options['m'] && !is_numeric($options['m']))) { 46 - usage(); 47 - } 48 - 49 - if (empty($options['f']) && empty($options['d'])) { 50 - echo phutil_console_wrap( 51 - "Before running this script, you should take down the Phabricator web ". 52 - "interface and stop any running Phabricator daemons."); 53 - 54 - if (!phutil_console_confirm('Are you ready to continue?')) { 55 - echo "Cancelled.\n"; 56 - exit(1); 57 - } 58 - } 59 - 60 - // Use always the version from the commandline if it is defined 61 - $next_version = isset($options['v']) ? (int)$options['v'] : null; 62 - $max_version = isset($options['m']) ? (int)$options['m'] : null; 63 - 64 - $conf = PhabricatorEnv::newObjectFromConfig('mysql.configuration-provider'); 65 - 66 - if ($options['u']) { 67 - $conn_user = $options['u']; 68 - $conn_pass = $options['p']; 69 - } else { 70 - $conn_user = $conf->getUser(); 71 - $conn_pass = $conf->getPassword(); 72 - } 73 - $conn_host = $conf->getHost(); 74 - 75 - // Split out port information, since the command-line client requires a 76 - // separate flag for the port. 77 - $uri = new PhutilURI('mysql://'.$conn_host); 78 - if ($uri->getPort()) { 79 - $conn_port = $uri->getPort(); 80 - $conn_bare_hostname = $uri->getDomain(); 81 - } else { 82 - $conn_port = null; 83 - $conn_bare_hostname = $conn_host; 84 - } 85 - 86 - $conn = PhabricatorEnv::newObjectFromConfig( 87 - 'mysql.implementation', 88 - array( 89 - array( 90 - 'user' => $conn_user, 91 - 'pass' => $conn_pass, 92 - 'host' => $conn_host, 93 - 'database' => null, 94 - ), 95 - )); 96 - 97 - try { 98 - 99 - $create_sql = <<<END 100 - CREATE DATABASE IF NOT EXISTS `phabricator_meta_data`; 101 - END; 102 - queryfx($conn, $create_sql); 103 - 104 - $create_sql = <<<END 105 - CREATE TABLE IF NOT EXISTS phabricator_meta_data.`schema_version` ( 106 - `version` INTEGER not null 107 - ); 108 - END; 109 - queryfx($conn, $create_sql); 110 - 111 - // Get the version only if commandline argument wasn't given 112 - if ($next_version === null) { 113 - $version = queryfx_one( 114 - $conn, 115 - 'SELECT * FROM phabricator_meta_data.%T', 116 - SCHEMA_VERSION_TABLE_NAME); 117 - 118 - if (!$version) { 119 - print "*** No version information in the database ***\n"; 120 - print "*** Give the first patch version which to ***\n"; 121 - print "*** apply as the command line argument ***\n"; 122 - exit(-1); 123 - } 124 - 125 - $next_version = $version['version'] + 1; 126 - } 127 - 128 - $patches = PhabricatorSQLPatchList::getPatchList(); 129 - 130 - $patch_applied = false; 131 - foreach ($patches as $patch) { 132 - if ($patch['version'] < $next_version) { 133 - continue; 134 - } 135 - 136 - if ($max_version && $patch['version'] > $max_version) { 137 - continue; 138 - } 139 - 140 - $short_name = basename($patch['path']); 141 - print "Applying patch {$short_name}...\n"; 142 - 143 - if (!empty($options['d'])) { 144 - $patch_applied = true; 145 - continue; 146 - } 147 - 148 - if ($conn_port) { 149 - $port = '--port='.(int)$conn_port; 150 - } else { 151 - $port = null; 152 - } 153 - 154 - if (preg_match('/\.php$/', $patch['path'])) { 155 - $schema_conn = $conn; 156 - require_once $patch['path']; 157 - } else { 158 - list($stdout, $stderr) = execx( 159 - "mysql --user=%s --password=%s --host=%s {$port} ". 160 - "--default-character-set=utf8 < %s", 161 - $conn_user, 162 - $conn_pass, 163 - $conn_bare_hostname, 164 - $patch['path']); 165 - 166 - if ($stderr) { 167 - print $stderr; 168 - exit(-1); 169 - } 170 - } 171 - 172 - // Patch was successful, update the db with the latest applied patch version 173 - // 'DELETE' and 'INSERT' instead of update, because the table might be empty 174 - queryfx( 175 - $conn, 176 - 'DELETE FROM phabricator_meta_data.%T', 177 - SCHEMA_VERSION_TABLE_NAME); 178 - queryfx( 179 - $conn, 180 - 'INSERT INTO phabricator_meta_data.%T VALUES (%d)', 181 - SCHEMA_VERSION_TABLE_NAME, 182 - $patch['version']); 183 - 184 - $patch_applied = true; 185 - } 186 - 187 - if (!$patch_applied) { 188 - print "Your database is already up-to-date.\n"; 189 - } 190 - 191 - } catch (AphrontQueryAccessDeniedException $ex) { 192 - echo 193 - "ACCESS DENIED\n". 194 - "The user '{$conn_user}' does not have sufficient MySQL privileges to\n". 195 - "execute the schema upgrade. Use the -u and -p flags to run as a user\n". 196 - "with more privileges (e.g., root).". 197 - "\n\n". 198 - "EXCEPTION:\n". 199 - $ex->getMessage(). 200 - "\n\n"; 201 - exit(1); 202 - } 203 - 204 - function usage() { 205 - echo 206 - "usage: upgrade_schema.php [-v version] [-u user -p pass] [-f] [-h]". 207 - "\n\n". 208 - "Run 'upgrade_schema.php -u root -p hunter2' to override the configured ". 209 - "default user.\n". 210 - "Run 'upgrade_schema.php -v 12' to apply all patches starting from ". 211 - "version 12. It is very unlikely you need to do this.\n". 212 - "Run 'upgrade_schema.php -m 110' to apply all patches up to and ". 213 - "including version 110 (but nothing past).\n". 214 - "Use the -f flag to upgrade noninteractively, without prompting.\n". 215 - "Use the -d flag to do a dry run - patches that would be applied ". 216 - "will be listed, but not applied.\n". 217 - "Use the -h flag to show this help.\n"; 218 - exit(1); 219 - } 220 - 20 + echo "This script has been replaced by 'phabricator/bin/storage'.\n\n". 21 + "Run:\n\n". 22 + " phabricator/bin $ ./storage help\n\n". 23 + "...for help, or:\n\n". 24 + " phabricator/bin $ ./storage upgrade\n\n". 25 + "...to upgrade storage.\n\n"; 26 + exit(1);
+17 -1
src/__phutil_library_map__.php
··· 520 520 'PhabricatorAuditReplyHandler' => 'applications/audit/replyhandler', 521 521 'PhabricatorAuditStatusConstants' => 'applications/audit/constants/status', 522 522 'PhabricatorAuthController' => 'applications/auth/controller/base', 523 + 'PhabricatorBuiltinPatchList' => 'infrastructure/setup/sql/phabricator', 523 524 'PhabricatorCalendarBrowseController' => 'applications/calendar/controller/browse', 524 525 'PhabricatorCalendarController' => 'applications/calendar/controller/base', 525 526 'PhabricatorChangesetResponse' => 'infrastructure/diff/response', ··· 851 852 'PhabricatorRepositoryTestCase' => 'applications/repository/storage/repository/__tests__', 852 853 'PhabricatorRepositoryType' => 'applications/repository/constants/repositorytype', 853 854 'PhabricatorS3FileStorageEngine' => 'applications/files/engine/s3', 854 - 'PhabricatorSQLPatchList' => 'infrastructure/setup/sql', 855 + 'PhabricatorSQLPatchList' => 'infrastructure/setup/sql/base', 855 856 'PhabricatorScopedEnv' => 'infrastructure/env', 856 857 'PhabricatorSearchAbstractDocument' => 'applications/search/index/abstractdocument', 857 858 'PhabricatorSearchAttachController' => 'applications/search/controller/attach', ··· 893 894 'PhabricatorSortTableExample' => 'applications/uiexample/examples/sorttable', 894 895 'PhabricatorStandardPageView' => 'view/page/standard', 895 896 'PhabricatorStatusController' => 'applications/status/base', 897 + 'PhabricatorStorageManagementAPI' => 'infrastructure/setup/storage/management', 898 + 'PhabricatorStorageManagementDatabasesWorkflow' => 'infrastructure/setup/storage/workflow/databases', 899 + 'PhabricatorStorageManagementDestroyWorkflow' => 'infrastructure/setup/storage/workflow/destroy', 900 + 'PhabricatorStorageManagementDumpWorkflow' => 'infrastructure/setup/storage/workflow/dump', 901 + 'PhabricatorStorageManagementStatusWorkflow' => 'infrastructure/setup/storage/workflow/status', 902 + 'PhabricatorStorageManagementUpgradeWorkflow' => 'infrastructure/setup/storage/workflow/upgrade', 903 + 'PhabricatorStorageManagementWorkflow' => 'infrastructure/setup/storage/workflow/base', 904 + 'PhabricatorStoragePatch' => 'infrastructure/setup/storage/patch', 896 905 'PhabricatorSymbolNameLinter' => 'infrastructure/lint/hook/xhpastsymbolname', 897 906 'PhabricatorSyntaxHighlighter' => 'applications/markup/syntax', 898 907 'PhabricatorTaskmasterDaemon' => 'infrastructure/daemon/workers/taskmaster', ··· 1435 1444 'PhabricatorAuditPreviewController' => 'PhabricatorAuditController', 1436 1445 'PhabricatorAuditReplyHandler' => 'PhabricatorMailReplyHandler', 1437 1446 'PhabricatorAuthController' => 'PhabricatorController', 1447 + 'PhabricatorBuiltinPatchList' => 'PhabricatorSQLPatchList', 1438 1448 'PhabricatorCalendarBrowseController' => 'PhabricatorCalendarController', 1439 1449 'PhabricatorCalendarController' => 'PhabricatorController', 1440 1450 'PhabricatorChangesetResponse' => 'AphrontProxyResponse', ··· 1742 1752 'PhabricatorSortTableExample' => 'PhabricatorUIExample', 1743 1753 'PhabricatorStandardPageView' => 'AphrontPageView', 1744 1754 'PhabricatorStatusController' => 'PhabricatorController', 1755 + 'PhabricatorStorageManagementDatabasesWorkflow' => 'PhabricatorStorageManagementWorkflow', 1756 + 'PhabricatorStorageManagementDestroyWorkflow' => 'PhabricatorStorageManagementWorkflow', 1757 + 'PhabricatorStorageManagementDumpWorkflow' => 'PhabricatorStorageManagementWorkflow', 1758 + 'PhabricatorStorageManagementStatusWorkflow' => 'PhabricatorStorageManagementWorkflow', 1759 + 'PhabricatorStorageManagementUpgradeWorkflow' => 'PhabricatorStorageManagementWorkflow', 1760 + 'PhabricatorStorageManagementWorkflow' => 'PhutilArgumentWorkflow', 1745 1761 'PhabricatorSymbolNameLinter' => 'ArcanistXHPASTLintNamingHook', 1746 1762 'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon', 1747 1763 'PhabricatorTestCase' => 'ArcanistPhutilTestCase',
+5 -1
src/applications/base/storage/configuration/base/DatabaseConfigurationProvider.php
··· 21 21 */ 22 22 interface DatabaseConfigurationProvider { 23 23 24 - public function __construct(LiskDAO $dao = null, $mode = 'r'); 24 + public function __construct( 25 + LiskDAO $dao = null, 26 + $mode = 'r', 27 + $namespace = 'phabricator'); 28 + 25 29 public function getUser(); 26 30 public function getPassword(); 27 31 public function getHost();
+8 -2
src/applications/base/storage/configuration/default/DefaultDatabaseConfigurationProvider.php
··· 21 21 22 22 private $dao; 23 23 private $mode; 24 + private $namespace; 24 25 25 - public function __construct(LiskDAO $dao = null, $mode = 'r') { 26 + public function __construct( 27 + LiskDAO $dao = null, 28 + $mode = 'r', 29 + $namespace = 'phabricator') { 30 + 26 31 $this->dao = $dao; 27 32 $this->mode = $mode; 33 + $this->namespace = $namespace; 28 34 } 29 35 30 36 public function getUser() { ··· 43 49 if (!$this->getDao()) { 44 50 return null; 45 51 } 46 - return 'phabricator_'.$this->getDao()->getApplicationName(); 52 + return $this->namespace.'_'.$this->getDao()->getApplicationName(); 47 53 } 48 54 49 55 final protected function getDao() {
+9 -1
src/applications/base/storage/lisk/PhabricatorLiskDAO.php
··· 23 23 abstract class PhabricatorLiskDAO extends LiskDAO { 24 24 25 25 private $edges = array(); 26 + private static $namespace = 'phabricator'; 26 27 27 28 28 29 /* -( Managing Edges )----------------------------------------------------- */ ··· 61 62 62 63 /* -( Configuring Storage )------------------------------------------------ */ 63 64 65 + /** 66 + * @task config 67 + */ 68 + public static function setApplicationNamespace($namespace) { 69 + self::$namespace = $namespace; 70 + } 71 + 64 72 65 73 /** 66 74 * @task config ··· 68 76 public function establishLiveConnection($mode) { 69 77 $conf = PhabricatorEnv::newObjectFromConfig( 70 78 'mysql.configuration-provider', 71 - array($this, $mode)); 79 + array($this, $mode, self::$namespace)); 72 80 73 81 return PhabricatorEnv::newObjectFromConfig( 74 82 'mysql.implementation',
+37 -18
src/docs/configuration/configuration_guide.diviner
··· 8 8 This document assumes you've already installed all the components you need. 9 9 If you haven't, see @{article:Installation Guide}. 10 10 11 - = Configuring MySQL = 12 - 13 - Get MySQL running and verify you can connect to it. Consult the MySQL 14 - documentation for help. When MySQL works, you need to load the Phabricator 15 - schemata into it. First, load the initial database schema. 16 - 17 - mysql -uroot < path/to/phabricator/resources/sql/init/initialize.sql 18 - 19 - After this you need to upgrade the schema (see @{article:Upgrading Schema}), 20 - but you need to finish the rest of the configuration first. 21 - 22 11 = Configuring Phabricator = 23 12 24 13 Create a new file here: ··· 59 48 ##'production'## if you are planning to develop Phabricator itself. This will 60 49 turn on some debugging features. 61 50 51 + = PHABRICATOR_ENV Environment Variable = 52 + 53 + When running Phabricator scripts, they will ask you to set the `PHABRICATOR_ENV` 54 + environment variable to point at your config. If you put your script in 55 + `custom/myconfig.conf.php`, you can identify the config with 56 + `custom/myconfig`, like this: 57 + 58 + $ PHABRICATOR_ENV=custom/myconfig ./some_phabricator_command 59 + 60 + NOTE: Make sure you put 'PHABRICATOR_ENV=...' at the beginning of the line, not 61 + in the middle. The shell won't parse environmental variables declared after the 62 + command. You can also ##export PHABRICATOR_ENV=...## in your ~/.bashrc or 63 + ~/.profile or similar, depending on which shell you use and how your system is 64 + configured. 65 + 66 + = Storage: Configuring MySQL = 67 + 68 + Get MySQL running and verify you can connect to it. Consult the MySQL 69 + documentation for help. When MySQL works, you need to load the Phabricator 70 + schemata into it. To do this, run: 71 + 72 + phabricator/ $ ./bin/storage upgrade 73 + 74 + If your configuration uses an unprivileged user to connect to the database, you 75 + may have to override the default user so the schema changes can be applied with 76 + root or some other admin user: 77 + 78 + phabricator/ $ ./bin/storage upgrade --user <user> --password <password> 79 + 80 + You can avoid the prompt the script issues by passing the ##--force## flag (for 81 + example, if you are scripting the upgrade process). 82 + 83 + phabricator/ $ ./bin/storage upgrade --force 84 + 85 + NOTE: When you update Phabricator, run `storage upgrade` again to apply any 86 + new updates. 87 + 62 88 = Webserver: Configuring Apache = 63 89 64 90 Get Apache running and verify it's serving a test page. Consult the Apache ··· 145 171 } 146 172 } 147 173 148 - = Upgrading Schema = 149 - 150 - Now, it's time for you to upgrade the database schema -- see 151 - @{article:Upgrading Schema}. You'll also need to do this after you update the 152 - code in the future. 153 - 154 174 = Setup = 155 175 156 176 Now, restart your webserver and navigate to whichever subdomain you set up. You ··· 200 220 201 221 Continue by: 202 222 203 - - upgrading the database schema with @{article:Upgrading Schema}; or 204 223 - setting up your admin account and login/registration with 205 224 @{article:Configuring Accounts and Registration}; or 206 225 - configuring where uploaded fils and attachments will be stored with
-36
src/docs/configuration/upgrade_schema.diviner
··· 1 - @title Upgrading Schema 2 - @group config 3 - 4 - This document describes how to upgrade the database schema. 5 - 6 - = Prerequisites = 7 - 8 - This document assumes you've already initialized the MySQL database and 9 - configured your Phabricator environment. If you haven't, see 10 - @{article:Configuration Guide}. 11 - 12 - = Loading patches = 13 - 14 - To upgrade your database schema to the latest version, just run this command: 15 - 16 - PHABRICATOR_ENV=<your_config> path/to/phabricator/scripts/sql/upgrade_schema.php 17 - 18 - NOTE: Make sure you put 'PHABRICATOR_ENV=...' at the beginning of the line, not 19 - in the middle. The shell won't parse environmental variables declared after the 20 - command. You can also ##export PHABRICATOR_ENV=...## in your ~/.bashrc or 21 - ~/.profile or similar, depending on which shell you use and how your system is 22 - configured. 23 - 24 - This will install all the patches that are new since you installed, or since the 25 - last time you ran this script. 26 - 27 - If your configuration uses an unprivileged user to connect to the database, you 28 - may have to override the default user so the schema changes can be applied with 29 - root or some other admin user: 30 - 31 - PHABRICATOR_ENV=<your_config> path/to/phabricator/scripts/sql/upgrade_schema.php -u <user> -p <pass> 32 - 33 - You can avoid the prompt the script issues by passing the ##-f## flag (for 34 - example, if you are scripting the upgrade process). 35 - 36 - PHABRICATOR_ENV=<your_config> path/to/phabricator/scripts/sql/upgrade_schema.php -f
+11 -2
src/docs/installation_guide.diviner
··· 118 118 119 119 = Updating Phabricator = 120 120 121 - Since Phabricator is under active development, you should update frequently. 122 - You can use a script similar to this one to automate the process: 121 + Since Phabricator is under active development, you should update frequently. To 122 + update Phabricator: 123 + 124 + - Stop the webserver. 125 + - Run `git pull && git submodule update --init` in `libphutil/`, 126 + `arcanist/` and `phabricator/`. 127 + - Run `phabricator/bin/storage upgrade`. 128 + - Restart the webserver. 129 + 130 + For more details, see @{article:Configuration Guide}. You can use a script 131 + similar to this one to automate the process: 123 132 124 133 http://www.phabricator.com/rsrc/install/update_phabricator.sh 125 134
+2 -21
src/infrastructure/setup/PhabricatorSetup.php
··· 519 519 if (empty($databases['phabricator_meta_data'])) { 520 520 self::writeFailure(); 521 521 self::write( 522 - "Setup failure! You haven't loaded the 'initialize.sql' file into ". 523 - "MySQL. This file initializes necessary databases. See this guide for ". 524 - "instructions:\n"); 522 + "Setup failure! You haven't run 'bin/storage upgrade'. See this ". 523 + "article for instructions:\n"); 525 524 self::writeDoc('article/Configuration_Guide.html'); 526 525 return; 527 526 } else { 528 527 self::write(" okay Databases have been initialized.\n"); 529 - } 530 - 531 - $schema_version = queryfx_one( 532 - $conn_raw, 533 - 'SELECT version FROM phabricator_meta_data.schema_version'); 534 - $schema_version = idx($schema_version, 'version', 'null'); 535 - 536 - $expect = PhabricatorSQLPatchList::getExpectedSchemaVersion(); 537 - if ($schema_version != $expect) { 538 - self::writeFailure(); 539 - self::write( 540 - "Setup failure! You haven't upgraded your database schema to the ". 541 - "latest version. Expected version is '{$expect}', but your local ". 542 - "version is '{$schema_version}'. See this guide for instructions:\n"); 543 - self::writeDoc('article/Upgrading_Schema.html'); 544 - return; 545 - } else { 546 - self::write(" okay Database schema are up to date (v{$expect}).\n"); 547 528 } 548 529 549 530 $index_min_length = queryfx_one(
-1
src/infrastructure/setup/__init__.php
··· 7 7 8 8 9 9 phutil_require_module('phabricator', 'infrastructure/env'); 10 - phutil_require_module('phabricator', 'infrastructure/setup/sql'); 11 10 phutil_require_module('phabricator', 'storage/queryfx'); 12 11 13 12 phutil_require_module('phutil', 'filesystem');
-62
src/infrastructure/setup/sql/PhabricatorSQLPatchList.php
··· 1 - <?php 2 - 3 - /* 4 - * Copyright 2012 Facebook, Inc. 5 - * 6 - * Licensed under the Apache License, Version 2.0 (the "License"); 7 - * you may not use this file except in compliance with the License. 8 - * You may obtain a copy of the License at 9 - * 10 - * http://www.apache.org/licenses/LICENSE-2.0 11 - * 12 - * Unless required by applicable law or agreed to in writing, software 13 - * distributed under the License is distributed on an "AS IS" BASIS, 14 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 - * See the License for the specific language governing permissions and 16 - * limitations under the License. 17 - */ 18 - 19 - final class PhabricatorSQLPatchList { 20 - 21 - public static function getPatchList() { 22 - $root = dirname(phutil_get_library_root('phabricator')); 23 - 24 - // Find the patch files 25 - $patches_dir = $root.'/resources/sql/patches/'; 26 - $finder = new FileFinder($patches_dir); 27 - $results = $finder->find(); 28 - 29 - $versions = array(); 30 - $patches = array(); 31 - foreach ($results as $path) { 32 - $matches = array(); 33 - if (!preg_match('/(\d+)\..*\.(sql|php)$/', $path, $matches)) { 34 - continue; 35 - } 36 - $version = (int)$matches[1]; 37 - $patches[] = array( 38 - 'version' => $version, 39 - 'path' => $patches_dir.$path, 40 - ); 41 - if (empty($versions[$version])) { 42 - $versions[$version] = true; 43 - } else { 44 - throw new Exception("Two patches have version {$version}!"); 45 - } 46 - } 47 - 48 - // Files are in some 'random' order returned by the operating system 49 - // We need to apply them in proper order 50 - $patches = isort($patches, 'version'); 51 - 52 - return $patches; 53 - } 54 - 55 - public static function getExpectedSchemaVersion() { 56 - $patches = self::getPatchList(); 57 - $versions = ipull($patches, 'version'); 58 - $max_version = max($versions); 59 - return $max_version; 60 - } 61 - 62 - }
-5
src/infrastructure/setup/sql/__init__.php
··· 6 6 7 7 8 8 9 - phutil_require_module('phutil', 'filesystem/filefinder'); 10 - phutil_require_module('phutil', 'moduleutils'); 11 - phutil_require_module('phutil', 'utils'); 12 9 13 - 14 - phutil_require_source('PhabricatorSQLPatchList.php');
+164
src/infrastructure/setup/sql/base/PhabricatorSQLPatchList.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + abstract class PhabricatorSQLPatchList { 20 + 21 + abstract function getNamespace(); 22 + abstract function getPatches(); 23 + 24 + final public static function buildAllPatches() { 25 + $patch_lists = id(new PhutilSymbolLoader()) 26 + ->setAncestorClass('PhabricatorSQLPatchList') 27 + ->setConcreteOnly(true) 28 + ->selectAndLoadSymbols(); 29 + 30 + $specs = array(); 31 + $seen_namespaces = array(); 32 + 33 + foreach ($patch_lists as $patch_class) { 34 + $patch_class = $patch_class['name']; 35 + $patch_list = newv($patch_class, array()); 36 + 37 + $namespace = $patch_list->getNamespace(); 38 + if (isset($seen_namespaces[$namespace])) { 39 + $prior = $seen_namespaces[$namespace]; 40 + throw new Exception( 41 + "PatchList '{$patch_class}' has the same namespace, '{$namespace}', ". 42 + "as another patch list class, '{$prior}'. Each patch list MUST have ". 43 + "a unique namespace."); 44 + } 45 + 46 + $last_key = null; 47 + foreach ($patch_list->getPatches() as $key => $patch) { 48 + if (!is_array($patch)) { 49 + throw new Exception( 50 + "PatchList '{$patch_class}' has a patch '{$key}' which is not ". 51 + "an array."); 52 + } 53 + 54 + $valid = array( 55 + 'type' => true, 56 + 'name' => true, 57 + 'after' => true, 58 + 'legacy' => true, 59 + ); 60 + 61 + foreach ($patch as $pkey => $pval) { 62 + if (empty($valid[$pkey])) { 63 + throw new Exception( 64 + "PatchList '{$patch_class}' has a patch, '{$key}', with an ". 65 + "unknown property, '{$pkey}'. Patches must have only valid ". 66 + "keys: ".implode(', ', array_keys($valid)).'.'); 67 + } 68 + } 69 + 70 + if (is_numeric($key)) { 71 + throw new Exception( 72 + "PatchList '{$patch_class}' has a patch with a numeric key, ". 73 + "'{$key}'. Patches must use string keys."); 74 + } 75 + 76 + if (strpos($key, ':') !== false) { 77 + throw new Exception( 78 + "PatchList '{$patch_class}' has a patch with a colon in the ". 79 + "key name, '{$key}'. Patch keys may not contain colons."); 80 + } 81 + 82 + $full_key = "{$namespace}:{$key}"; 83 + 84 + if (isset($specs[$full_key])) { 85 + throw new Exception( 86 + "PatchList '{$patch_class}' has a patch '{$key}' which ". 87 + "duplicates an existing patch key."); 88 + } 89 + 90 + $patch['key'] = $key; 91 + $patch['fullKey'] = $full_key; 92 + 93 + if (isset($patch['legacy'])) { 94 + if ($namespace != 'phabricator') { 95 + throw new Exception( 96 + "Only patches in the 'phabricator' namespace may contain ". 97 + "'legacy' keys."); 98 + } 99 + } else { 100 + $patch['legacy'] = false; 101 + } 102 + 103 + if (!array_key_exists('after', $patch)) { 104 + if ($last_key === null) { 105 + throw new Exception( 106 + "Patch '{$full_key}' is missing key 'after', and is the first ". 107 + "patch in the patch list '{$patch_class}', so its application ". 108 + "order can not be determined implicitly. The first patch in a ". 109 + "patch list must list the patch or patches it depends on ". 110 + "explicitly."); 111 + } else { 112 + $patch['after'] = array($last_key); 113 + } 114 + } 115 + $last_key = $full_key; 116 + 117 + foreach ($patch['after'] as $after_key => $after) { 118 + if (strpos($after, ':') === false) { 119 + $patch['after'][$after_key] = $namespace.':'.$after; 120 + } 121 + } 122 + 123 + $type = idx($patch, 'type'); 124 + if (!$type) { 125 + throw new Exception( 126 + "Patch '{$namespace}:{$key}' is missing key 'type'. Every patch ". 127 + "must have a type."); 128 + } 129 + 130 + switch ($type) { 131 + case 'db': 132 + case 'sql': 133 + case 'php': 134 + break; 135 + default: 136 + throw new Exception( 137 + "Patch '{$namespace}:{$key}' has unknown patch type '{$type}'."); 138 + } 139 + 140 + $specs[$full_key] = $patch; 141 + } 142 + } 143 + 144 + foreach ($specs as $key => $patch) { 145 + foreach ($patch['after'] as $after) { 146 + if (empty($specs[$after])) { 147 + throw new Exception( 148 + "Patch '{$key}' references nonexistent dependency, '{$after}'. ". 149 + "Patches may only depend on patches which actually exist."); 150 + } 151 + } 152 + } 153 + 154 + $patches = array(); 155 + foreach ($specs as $full_key => $spec) { 156 + $patches[$full_key] = new PhabricatorStoragePatch($spec); 157 + } 158 + 159 + // TODO: Detect cycles? 160 + 161 + return $patches; 162 + } 163 + 164 + }
+15
src/infrastructure/setup/sql/base/__init__.php
··· 1 + <?php 2 + /** 3 + * This file is automatically generated. Lint this module to rebuild it. 4 + * @generated 5 + */ 6 + 7 + 8 + 9 + phutil_require_module('phabricator', 'infrastructure/setup/storage/patch'); 10 + 11 + phutil_require_module('phutil', 'symbols'); 12 + phutil_require_module('phutil', 'utils'); 13 + 14 + 15 + phutil_require_source('PhabricatorSQLPatchList.php');
+853
src/infrastructure/setup/sql/phabricator/PhabricatorBuiltinPatchList.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList { 20 + 21 + public function getNamespace() { 22 + return 'phabricator'; 23 + } 24 + 25 + private function getPatchPath($file) { 26 + $root = dirname(phutil_get_library_root('phabricator')); 27 + $path = $root.'/resources/sql/patches/'.$file; 28 + 29 + // Make sure it exists. 30 + Filesystem::readFile($path); 31 + 32 + return $path; 33 + } 34 + 35 + public function getPatches() { 36 + return array( 37 + 'db.audit' => array( 38 + 'type' => 'db', 39 + 'name' => 'audit', 40 + 'after' => array( /* First Patch */ ), 41 + ), 42 + 'db.chatlog' => array( 43 + 'type' => 'db', 44 + 'name' => 'chatlog', 45 + ), 46 + 'db.conduit' => array( 47 + 'type' => 'db', 48 + 'name' => 'conduit', 49 + ), 50 + 'db.countdown' => array( 51 + 'type' => 'db', 52 + 'name' => 'countdown', 53 + ), 54 + 'db.daemon' => array( 55 + 'type' => 'db', 56 + 'name' => 'daemon', 57 + ), 58 + 'db.differential' => array( 59 + 'type' => 'db', 60 + 'name' => 'differential', 61 + ), 62 + 'db.draft' => array( 63 + 'type' => 'db', 64 + 'name' => 'draft', 65 + ), 66 + 'db.drydock' => array( 67 + 'type' => 'db', 68 + 'name' => 'drydock', 69 + ), 70 + 'db.feed' => array( 71 + 'type' => 'db', 72 + 'name' => 'feed', 73 + ), 74 + 'db.file' => array( 75 + 'type' => 'db', 76 + 'name' => 'file', 77 + ), 78 + 'db.flag' => array( 79 + 'type' => 'db', 80 + 'name' => 'flag', 81 + ), 82 + 'db.herald' => array( 83 + 'type' => 'db', 84 + 'name' => 'herald', 85 + ), 86 + 'db.maniphest' => array( 87 + 'type' => 'db', 88 + 'name' => 'maniphest', 89 + ), 90 + 'db.meta_data' => array( 91 + 'type' => 'db', 92 + 'name' => 'meta_data', 93 + ), 94 + 'db.metamta' => array( 95 + 'type' => 'db', 96 + 'name' => 'metamta', 97 + ), 98 + 'db.oauth_server' => array( 99 + 'type' => 'db', 100 + 'name' => 'oauth_server', 101 + ), 102 + 'db.owners' => array( 103 + 'type' => 'db', 104 + 'name' => 'owners', 105 + ), 106 + 'db.pastebin' => array( 107 + 'type' => 'db', 108 + 'name' => 'pastebin', 109 + ), 110 + 'db.phame' => array( 111 + 'type' => 'db', 112 + 'name' => 'phame', 113 + ), 114 + 'db.phid' => array( 115 + 'type' => 'db', 116 + 'name' => 'phid', 117 + ), 118 + 'db.phriction' => array( 119 + 'type' => 'db', 120 + 'name' => 'phriction', 121 + ), 122 + 'db.project' => array( 123 + 'type' => 'db', 124 + 'name' => 'project', 125 + ), 126 + 'db.repository' => array( 127 + 'type' => 'db', 128 + 'name' => 'repository', 129 + ), 130 + 'db.search' => array( 131 + 'type' => 'db', 132 + 'name' => 'search', 133 + ), 134 + 'db.slowvote' => array( 135 + 'type' => 'db', 136 + 'name' => 'slowvote', 137 + ), 138 + 'db.timeline' => array( 139 + 'type' => 'db', 140 + 'name' => 'timeline', 141 + ), 142 + 'db.user' => array( 143 + 'type' => 'db', 144 + 'name' => 'user', 145 + ), 146 + 'db.worker' => array( 147 + 'type' => 'db', 148 + 'name' => 'worker', 149 + ), 150 + 'db.xhpastview' => array( 151 + 'type' => 'db', 152 + 'name' => 'xhpastview', 153 + ), 154 + '0000.legacy.sql' => array( 155 + 'type' => 'sql', 156 + 'name' => $this->getPatchPath('0000.legacy.sql'), 157 + 'legacy' => 0, 158 + ), 159 + '000.project.sql' => array( 160 + 'type' => 'sql', 161 + 'name' => $this->getPatchPath('000.project.sql'), 162 + 'legacy' => 0, 163 + ), 164 + '001.maniphest_projects.sql' => array( 165 + 'type' => 'sql', 166 + 'name' => $this->getPatchPath('001.maniphest_projects.sql'), 167 + 'legacy' => 1, 168 + ), 169 + '002.oauth.sql' => array( 170 + 'type' => 'sql', 171 + 'name' => $this->getPatchPath('002.oauth.sql'), 172 + 'legacy' => 2, 173 + ), 174 + '003.more_oauth.sql' => array( 175 + 'type' => 'sql', 176 + 'name' => $this->getPatchPath('003.more_oauth.sql'), 177 + 'legacy' => 3, 178 + ), 179 + '004.daemonrepos.sql' => array( 180 + 'type' => 'sql', 181 + 'name' => $this->getPatchPath('004.daemonrepos.sql'), 182 + 'legacy' => 4, 183 + ), 184 + '005.workers.sql' => array( 185 + 'type' => 'sql', 186 + 'name' => $this->getPatchPath('005.workers.sql'), 187 + 'legacy' => 5, 188 + ), 189 + '006.repository.sql' => array( 190 + 'type' => 'sql', 191 + 'name' => $this->getPatchPath('006.repository.sql'), 192 + 'legacy' => 6, 193 + ), 194 + '007.daemonlog.sql' => array( 195 + 'type' => 'sql', 196 + 'name' => $this->getPatchPath('007.daemonlog.sql'), 197 + 'legacy' => 7, 198 + ), 199 + '008.repoopt.sql' => array( 200 + 'type' => 'sql', 201 + 'name' => $this->getPatchPath('008.repoopt.sql'), 202 + 'legacy' => 8, 203 + ), 204 + '009.repo_summary.sql' => array( 205 + 'type' => 'sql', 206 + 'name' => $this->getPatchPath('009.repo_summary.sql'), 207 + 'legacy' => 9, 208 + ), 209 + '010.herald.sql' => array( 210 + 'type' => 'sql', 211 + 'name' => $this->getPatchPath('010.herald.sql'), 212 + 'legacy' => 10, 213 + ), 214 + '011.badcommit.sql' => array( 215 + 'type' => 'sql', 216 + 'name' => $this->getPatchPath('011.badcommit.sql'), 217 + 'legacy' => 11, 218 + ), 219 + '012.dropphidtype.sql' => array( 220 + 'type' => 'sql', 221 + 'name' => $this->getPatchPath('012.dropphidtype.sql'), 222 + 'legacy' => 12, 223 + ), 224 + '013.commitdetail.sql' => array( 225 + 'type' => 'sql', 226 + 'name' => $this->getPatchPath('013.commitdetail.sql'), 227 + 'legacy' => 13, 228 + ), 229 + '014.shortcuts.sql' => array( 230 + 'type' => 'sql', 231 + 'name' => $this->getPatchPath('014.shortcuts.sql'), 232 + 'legacy' => 14, 233 + ), 234 + '015.preferences.sql' => array( 235 + 'type' => 'sql', 236 + 'name' => $this->getPatchPath('015.preferences.sql'), 237 + 'legacy' => 15, 238 + ), 239 + '016.userrealnameindex.sql' => array( 240 + 'type' => 'sql', 241 + 'name' => $this->getPatchPath('016.userrealnameindex.sql'), 242 + 'legacy' => 16, 243 + ), 244 + '017.sessionkeys.sql' => array( 245 + 'type' => 'sql', 246 + 'name' => $this->getPatchPath('017.sessionkeys.sql'), 247 + 'legacy' => 17, 248 + ), 249 + '018.owners.sql' => array( 250 + 'type' => 'sql', 251 + 'name' => $this->getPatchPath('018.owners.sql'), 252 + 'legacy' => 18, 253 + ), 254 + '019.arcprojects.sql' => array( 255 + 'type' => 'sql', 256 + 'name' => $this->getPatchPath('019.arcprojects.sql'), 257 + 'legacy' => 19, 258 + ), 259 + '020.pathcapital.sql' => array( 260 + 'type' => 'sql', 261 + 'name' => $this->getPatchPath('020.pathcapital.sql'), 262 + 'legacy' => 20, 263 + ), 264 + '021.xhpastview.sql' => array( 265 + 'type' => 'sql', 266 + 'name' => $this->getPatchPath('021.xhpastview.sql'), 267 + 'legacy' => 21, 268 + ), 269 + '022.differentialcommit.sql' => array( 270 + 'type' => 'sql', 271 + 'name' => $this->getPatchPath('022.differentialcommit.sql'), 272 + 'legacy' => 22, 273 + ), 274 + '023.dxkeys.sql' => array( 275 + 'type' => 'sql', 276 + 'name' => $this->getPatchPath('023.dxkeys.sql'), 277 + 'legacy' => 23, 278 + ), 279 + '024.mlistkeys.sql' => array( 280 + 'type' => 'sql', 281 + 'name' => $this->getPatchPath('024.mlistkeys.sql'), 282 + 'legacy' => 24, 283 + ), 284 + '025.commentopt.sql' => array( 285 + 'type' => 'sql', 286 + 'name' => $this->getPatchPath('025.commentopt.sql'), 287 + 'legacy' => 25, 288 + ), 289 + '026.diffpropkey.sql' => array( 290 + 'type' => 'sql', 291 + 'name' => $this->getPatchPath('026.diffpropkey.sql'), 292 + 'legacy' => 26, 293 + ), 294 + '027.metamtakeys.sql' => array( 295 + 'type' => 'sql', 296 + 'name' => $this->getPatchPath('027.metamtakeys.sql'), 297 + 'legacy' => 27, 298 + ), 299 + '028.systemagent.sql' => array( 300 + 'type' => 'sql', 301 + 'name' => $this->getPatchPath('028.systemagent.sql'), 302 + 'legacy' => 28, 303 + ), 304 + '029.cursors.sql' => array( 305 + 'type' => 'sql', 306 + 'name' => $this->getPatchPath('029.cursors.sql'), 307 + 'legacy' => 29, 308 + ), 309 + '030.imagemacro.sql' => array( 310 + 'type' => 'sql', 311 + 'name' => $this->getPatchPath('030.imagemacro.sql'), 312 + 'legacy' => 30, 313 + ), 314 + '031.workerrace.sql' => array( 315 + 'type' => 'sql', 316 + 'name' => $this->getPatchPath('031.workerrace.sql'), 317 + 'legacy' => 31, 318 + ), 319 + '032.viewtime.sql' => array( 320 + 'type' => 'sql', 321 + 'name' => $this->getPatchPath('032.viewtime.sql'), 322 + 'legacy' => 32, 323 + ), 324 + '033.privtest.sql' => array( 325 + 'type' => 'sql', 326 + 'name' => $this->getPatchPath('033.privtest.sql'), 327 + 'legacy' => 33, 328 + ), 329 + '034.savedheader.sql' => array( 330 + 'type' => 'sql', 331 + 'name' => $this->getPatchPath('034.savedheader.sql'), 332 + 'legacy' => 34, 333 + ), 334 + '035.proxyimage.sql' => array( 335 + 'type' => 'sql', 336 + 'name' => $this->getPatchPath('035.proxyimage.sql'), 337 + 'legacy' => 35, 338 + ), 339 + '036.mailkey.sql' => array( 340 + 'type' => 'sql', 341 + 'name' => $this->getPatchPath('036.mailkey.sql'), 342 + 'legacy' => 36, 343 + ), 344 + '037.setuptest.sql' => array( 345 + 'type' => 'sql', 346 + 'name' => $this->getPatchPath('037.setuptest.sql'), 347 + 'legacy' => 37, 348 + ), 349 + '038.admin.sql' => array( 350 + 'type' => 'sql', 351 + 'name' => $this->getPatchPath('038.admin.sql'), 352 + 'legacy' => 38, 353 + ), 354 + '039.userlog.sql' => array( 355 + 'type' => 'sql', 356 + 'name' => $this->getPatchPath('039.userlog.sql'), 357 + 'legacy' => 39, 358 + ), 359 + '040.transform.sql' => array( 360 + 'type' => 'sql', 361 + 'name' => $this->getPatchPath('040.transform.sql'), 362 + 'legacy' => 40, 363 + ), 364 + '041.heraldrepetition.sql' => array( 365 + 'type' => 'sql', 366 + 'name' => $this->getPatchPath('041.heraldrepetition.sql'), 367 + 'legacy' => 41, 368 + ), 369 + '042.commentmetadata.sql' => array( 370 + 'type' => 'sql', 371 + 'name' => $this->getPatchPath('042.commentmetadata.sql'), 372 + 'legacy' => 42, 373 + ), 374 + '043.pastebin.sql' => array( 375 + 'type' => 'sql', 376 + 'name' => $this->getPatchPath('043.pastebin.sql'), 377 + 'legacy' => 43, 378 + ), 379 + '044.countdown.sql' => array( 380 + 'type' => 'sql', 381 + 'name' => $this->getPatchPath('044.countdown.sql'), 382 + 'legacy' => 44, 383 + ), 384 + '045.timezone.sql' => array( 385 + 'type' => 'sql', 386 + 'name' => $this->getPatchPath('045.timezone.sql'), 387 + 'legacy' => 45, 388 + ), 389 + '046.conduittoken.sql' => array( 390 + 'type' => 'sql', 391 + 'name' => $this->getPatchPath('046.conduittoken.sql'), 392 + 'legacy' => 46, 393 + ), 394 + '047.projectstatus.sql' => array( 395 + 'type' => 'sql', 396 + 'name' => $this->getPatchPath('047.projectstatus.sql'), 397 + 'legacy' => 47, 398 + ), 399 + '048.relationshipkeys.sql' => array( 400 + 'type' => 'sql', 401 + 'name' => $this->getPatchPath('048.relationshipkeys.sql'), 402 + 'legacy' => 48, 403 + ), 404 + '049.projectowner.sql' => array( 405 + 'type' => 'sql', 406 + 'name' => $this->getPatchPath('049.projectowner.sql'), 407 + 'legacy' => 49, 408 + ), 409 + '050.taskdenormal.sql' => array( 410 + 'type' => 'sql', 411 + 'name' => $this->getPatchPath('050.taskdenormal.sql'), 412 + 'legacy' => 50, 413 + ), 414 + '051.projectfilter.sql' => array( 415 + 'type' => 'sql', 416 + 'name' => $this->getPatchPath('051.projectfilter.sql'), 417 + 'legacy' => 51, 418 + ), 419 + '052.pastelanguage.sql' => array( 420 + 'type' => 'sql', 421 + 'name' => $this->getPatchPath('052.pastelanguage.sql'), 422 + 'legacy' => 52, 423 + ), 424 + '053.feed.sql' => array( 425 + 'type' => 'sql', 426 + 'name' => $this->getPatchPath('053.feed.sql'), 427 + 'legacy' => 53, 428 + ), 429 + '054.subscribers.sql' => array( 430 + 'type' => 'sql', 431 + 'name' => $this->getPatchPath('054.subscribers.sql'), 432 + 'legacy' => 54, 433 + ), 434 + '055.add_author_to_files.sql' => array( 435 + 'type' => 'sql', 436 + 'name' => $this->getPatchPath('055.add_author_to_files.sql'), 437 + 'legacy' => 55, 438 + ), 439 + '056.slowvote.sql' => array( 440 + 'type' => 'sql', 441 + 'name' => $this->getPatchPath('056.slowvote.sql'), 442 + 'legacy' => 56, 443 + ), 444 + '057.parsecache.sql' => array( 445 + 'type' => 'sql', 446 + 'name' => $this->getPatchPath('057.parsecache.sql'), 447 + 'legacy' => 57, 448 + ), 449 + '058.missingkeys.sql' => array( 450 + 'type' => 'sql', 451 + 'name' => $this->getPatchPath('058.missingkeys.sql'), 452 + 'legacy' => 58, 453 + ), 454 + '059.engines.php' => array( 455 + 'type' => 'php', 456 + 'name' => $this->getPatchPath('059.engines.php'), 457 + 'legacy' => 59, 458 + ), 459 + '060.phriction.sql' => array( 460 + 'type' => 'sql', 461 + 'name' => $this->getPatchPath('060.phriction.sql'), 462 + 'legacy' => 60, 463 + ), 464 + '061.phrictioncontent.sql' => array( 465 + 'type' => 'sql', 466 + 'name' => $this->getPatchPath('061.phrictioncontent.sql'), 467 + 'legacy' => 61, 468 + ), 469 + '062.phrictionmenu.sql' => array( 470 + 'type' => 'sql', 471 + 'name' => $this->getPatchPath('062.phrictionmenu.sql'), 472 + 'legacy' => 62, 473 + ), 474 + '063.pasteforks.sql' => array( 475 + 'type' => 'sql', 476 + 'name' => $this->getPatchPath('063.pasteforks.sql'), 477 + 'legacy' => 63, 478 + ), 479 + '064.subprojects.sql' => array( 480 + 'type' => 'sql', 481 + 'name' => $this->getPatchPath('064.subprojects.sql'), 482 + 'legacy' => 64, 483 + ), 484 + '065.sshkeys.sql' => array( 485 + 'type' => 'sql', 486 + 'name' => $this->getPatchPath('065.sshkeys.sql'), 487 + 'legacy' => 65, 488 + ), 489 + '066.phrictioncontent.sql' => array( 490 + 'type' => 'sql', 491 + 'name' => $this->getPatchPath('066.phrictioncontent.sql'), 492 + 'legacy' => 66, 493 + ), 494 + '067.preferences.sql' => array( 495 + 'type' => 'sql', 496 + 'name' => $this->getPatchPath('067.preferences.sql'), 497 + 'legacy' => 67, 498 + ), 499 + '068.maniphestauxiliarystorage.sql' => array( 500 + 'type' => 'sql', 501 + 'name' => $this->getPatchPath('068.maniphestauxiliarystorage.sql'), 502 + 'legacy' => 68, 503 + ), 504 + '069.heraldxscript.sql' => array( 505 + 'type' => 'sql', 506 + 'name' => $this->getPatchPath('069.heraldxscript.sql'), 507 + 'legacy' => 69, 508 + ), 509 + '070.differentialaux.sql' => array( 510 + 'type' => 'sql', 511 + 'name' => $this->getPatchPath('070.differentialaux.sql'), 512 + 'legacy' => 70, 513 + ), 514 + '071.contentsource.sql' => array( 515 + 'type' => 'sql', 516 + 'name' => $this->getPatchPath('071.contentsource.sql'), 517 + 'legacy' => 71, 518 + ), 519 + '072.blamerevert.sql' => array( 520 + 'type' => 'sql', 521 + 'name' => $this->getPatchPath('072.blamerevert.sql'), 522 + 'legacy' => 72, 523 + ), 524 + '073.reposymbols.sql' => array( 525 + 'type' => 'sql', 526 + 'name' => $this->getPatchPath('073.reposymbols.sql'), 527 + 'legacy' => 73, 528 + ), 529 + '074.affectedpath.sql' => array( 530 + 'type' => 'sql', 531 + 'name' => $this->getPatchPath('074.affectedpath.sql'), 532 + 'legacy' => 74, 533 + ), 534 + '075.revisionhash.sql' => array( 535 + 'type' => 'sql', 536 + 'name' => $this->getPatchPath('075.revisionhash.sql'), 537 + 'legacy' => 75, 538 + ), 539 + '076.indexedlanguages.sql' => array( 540 + 'type' => 'sql', 541 + 'name' => $this->getPatchPath('076.indexedlanguages.sql'), 542 + 'legacy' => 76, 543 + ), 544 + '077.originalemail.sql' => array( 545 + 'type' => 'sql', 546 + 'name' => $this->getPatchPath('077.originalemail.sql'), 547 + 'legacy' => 77, 548 + ), 549 + '078.nametoken.sql' => array( 550 + 'type' => 'sql', 551 + 'name' => $this->getPatchPath('078.nametoken.sql'), 552 + 'legacy' => 78, 553 + ), 554 + '079.nametokenindex.php' => array( 555 + 'type' => 'php', 556 + 'name' => $this->getPatchPath('079.nametokenindex.php'), 557 + 'legacy' => 79, 558 + ), 559 + '080.filekeys.sql' => array( 560 + 'type' => 'sql', 561 + 'name' => $this->getPatchPath('080.filekeys.sql'), 562 + 'legacy' => 80, 563 + ), 564 + '081.filekeys.php' => array( 565 + 'type' => 'php', 566 + 'name' => $this->getPatchPath('081.filekeys.php'), 567 + 'legacy' => 81, 568 + ), 569 + '082.xactionkey.sql' => array( 570 + 'type' => 'sql', 571 + 'name' => $this->getPatchPath('082.xactionkey.sql'), 572 + 'legacy' => 82, 573 + ), 574 + '083.dxviewtime.sql' => array( 575 + 'type' => 'sql', 576 + 'name' => $this->getPatchPath('083.dxviewtime.sql'), 577 + 'legacy' => 83, 578 + ), 579 + '084.pasteauthorkey.sql' => array( 580 + 'type' => 'sql', 581 + 'name' => $this->getPatchPath('084.pasteauthorkey.sql'), 582 + 'legacy' => 84, 583 + ), 584 + '085.packagecommitrelationship.sql' => array( 585 + 'type' => 'sql', 586 + 'name' => $this->getPatchPath('085.packagecommitrelationship.sql'), 587 + 'legacy' => 85, 588 + ), 589 + '086.formeraffil.sql' => array( 590 + 'type' => 'sql', 591 + 'name' => $this->getPatchPath('086.formeraffil.sql'), 592 + 'legacy' => 86, 593 + ), 594 + '087.phrictiondelete.sql' => array( 595 + 'type' => 'sql', 596 + 'name' => $this->getPatchPath('087.phrictiondelete.sql'), 597 + 'legacy' => 87, 598 + ), 599 + '088.audit.sql' => array( 600 + 'type' => 'sql', 601 + 'name' => $this->getPatchPath('088.audit.sql'), 602 + 'legacy' => 88, 603 + ), 604 + '089.projectwiki.sql' => array( 605 + 'type' => 'sql', 606 + 'name' => $this->getPatchPath('089.projectwiki.sql'), 607 + 'legacy' => 89, 608 + ), 609 + '090.forceuniqueprojectnames.php' => array( 610 + 'type' => 'php', 611 + 'name' => $this->getPatchPath('090.forceuniqueprojectnames.php'), 612 + 'legacy' => 90, 613 + ), 614 + '091.uniqueslugkey.sql' => array( 615 + 'type' => 'sql', 616 + 'name' => $this->getPatchPath('091.uniqueslugkey.sql'), 617 + 'legacy' => 91, 618 + ), 619 + '092.dropgithubnotification.sql' => array( 620 + 'type' => 'sql', 621 + 'name' => $this->getPatchPath('092.dropgithubnotification.sql'), 622 + 'legacy' => 92, 623 + ), 624 + '093.gitremotes.php' => array( 625 + 'type' => 'php', 626 + 'name' => $this->getPatchPath('093.gitremotes.php'), 627 + 'legacy' => 93, 628 + ), 629 + '094.phrictioncolumn.sql' => array( 630 + 'type' => 'sql', 631 + 'name' => $this->getPatchPath('094.phrictioncolumn.sql'), 632 + 'legacy' => 94, 633 + ), 634 + '095.directory.sql' => array( 635 + 'type' => 'sql', 636 + 'name' => $this->getPatchPath('095.directory.sql'), 637 + 'legacy' => 95, 638 + ), 639 + '096.filename.sql' => array( 640 + 'type' => 'sql', 641 + 'name' => $this->getPatchPath('096.filename.sql'), 642 + 'legacy' => 96, 643 + ), 644 + '097.heraldruletypes.sql' => array( 645 + 'type' => 'sql', 646 + 'name' => $this->getPatchPath('097.heraldruletypes.sql'), 647 + 'legacy' => 97, 648 + ), 649 + '098.heraldruletypemigration.php' => array( 650 + 'type' => 'php', 651 + 'name' => $this->getPatchPath('098.heraldruletypemigration.php'), 652 + 'legacy' => 98, 653 + ), 654 + '099.drydock.sql' => array( 655 + 'type' => 'sql', 656 + 'name' => $this->getPatchPath('099.drydock.sql'), 657 + 'legacy' => 99, 658 + ), 659 + '100.projectxaction.sql' => array( 660 + 'type' => 'sql', 661 + 'name' => $this->getPatchPath('100.projectxaction.sql'), 662 + 'legacy' => 100, 663 + ), 664 + '101.heraldruleapplied.sql' => array( 665 + 'type' => 'sql', 666 + 'name' => $this->getPatchPath('101.heraldruleapplied.sql'), 667 + 'legacy' => 101, 668 + ), 669 + '102.heraldcleanup.php' => array( 670 + 'type' => 'php', 671 + 'name' => $this->getPatchPath('102.heraldcleanup.php'), 672 + 'legacy' => 102, 673 + ), 674 + '103.heraldedithistory.sql' => array( 675 + 'type' => 'sql', 676 + 'name' => $this->getPatchPath('103.heraldedithistory.sql'), 677 + 'legacy' => 103, 678 + ), 679 + '104.searchkey.sql' => array( 680 + 'type' => 'sql', 681 + 'name' => $this->getPatchPath('104.searchkey.sql'), 682 + 'legacy' => 104, 683 + ), 684 + '105.mimetype.sql' => array( 685 + 'type' => 'sql', 686 + 'name' => $this->getPatchPath('105.mimetype.sql'), 687 + 'legacy' => 105, 688 + ), 689 + '106.chatlog.sql' => array( 690 + 'type' => 'sql', 691 + 'name' => $this->getPatchPath('106.chatlog.sql'), 692 + 'legacy' => 106, 693 + ), 694 + '107.oauthserver.sql' => array( 695 + 'type' => 'sql', 696 + 'name' => $this->getPatchPath('107.oauthserver.sql'), 697 + 'legacy' => 107, 698 + ), 699 + '108.oauthscope.sql' => array( 700 + 'type' => 'sql', 701 + 'name' => $this->getPatchPath('108.oauthscope.sql'), 702 + 'legacy' => 108, 703 + ), 704 + '109.oauthclientphidkey.sql' => array( 705 + 'type' => 'sql', 706 + 'name' => $this->getPatchPath('109.oauthclientphidkey.sql'), 707 + 'legacy' => 109, 708 + ), 709 + '110.commitaudit.sql' => array( 710 + 'type' => 'sql', 711 + 'name' => $this->getPatchPath('110.commitaudit.sql'), 712 + 'legacy' => 110, 713 + ), 714 + '111.commitauditmigration.php' => array( 715 + 'type' => 'php', 716 + 'name' => $this->getPatchPath('111.commitauditmigration.php'), 717 + 'legacy' => 111, 718 + ), 719 + '112.oauthaccesscoderedirecturi.sql' => array( 720 + 'type' => 'sql', 721 + 'name' => $this->getPatchPath('112.oauthaccesscoderedirecturi.sql'), 722 + 'legacy' => 112, 723 + ), 724 + '113.lastreviewer.sql' => array( 725 + 'type' => 'sql', 726 + 'name' => $this->getPatchPath('113.lastreviewer.sql'), 727 + 'legacy' => 113, 728 + ), 729 + '114.auditrequest.sql' => array( 730 + 'type' => 'sql', 731 + 'name' => $this->getPatchPath('114.auditrequest.sql'), 732 + 'legacy' => 114, 733 + ), 734 + '115.prepareutf8.sql' => array( 735 + 'type' => 'sql', 736 + 'name' => $this->getPatchPath('115.prepareutf8.sql'), 737 + 'legacy' => 115, 738 + ), 739 + '116.utf8-backup-first-expect-wait.sql' => array( 740 + 'type' => 'sql', 741 + 'name' => 742 + $this->getPatchPath('116.utf8-backup-first-expect-wait.sql'), 743 + 'legacy' => 116, 744 + ), 745 + '117.repositorydescription.php' => array( 746 + 'type' => 'php', 747 + 'name' => $this->getPatchPath('117.repositorydescription.php'), 748 + 'legacy' => 117, 749 + ), 750 + '118.auditinline.sql' => array( 751 + 'type' => 'sql', 752 + 'name' => $this->getPatchPath('118.auditinline.sql'), 753 + 'legacy' => 118, 754 + ), 755 + '119.filehash.sql' => array( 756 + 'type' => 'sql', 757 + 'name' => $this->getPatchPath('119.filehash.sql'), 758 + 'legacy' => 119, 759 + ), 760 + '120.noop.sql' => array( 761 + 'type' => 'sql', 762 + 'name' => $this->getPatchPath('120.noop.sql'), 763 + 'legacy' => 120, 764 + ), 765 + '121.drydocklog.sql' => array( 766 + 'type' => 'sql', 767 + 'name' => $this->getPatchPath('121.drydocklog.sql'), 768 + 'legacy' => 121, 769 + ), 770 + '122.flag.sql' => array( 771 + 'type' => 'sql', 772 + 'name' => $this->getPatchPath('122.flag.sql'), 773 + 'legacy' => 122, 774 + ), 775 + '123.heraldrulelog.sql' => array( 776 + 'type' => 'sql', 777 + 'name' => $this->getPatchPath('123.heraldrulelog.sql'), 778 + 'legacy' => 123, 779 + ), 780 + '124.subpriority.sql' => array( 781 + 'type' => 'sql', 782 + 'name' => $this->getPatchPath('124.subpriority.sql'), 783 + 'legacy' => 124, 784 + ), 785 + '125.ipv6.sql' => array( 786 + 'type' => 'sql', 787 + 'name' => $this->getPatchPath('125.ipv6.sql'), 788 + 'legacy' => 125, 789 + ), 790 + '126.edges.sql' => array( 791 + 'type' => 'sql', 792 + 'name' => $this->getPatchPath('126.edges.sql'), 793 + 'legacy' => 126, 794 + ), 795 + '127.userkeybody.sql' => array( 796 + 'type' => 'sql', 797 + 'name' => $this->getPatchPath('127.userkeybody.sql'), 798 + 'legacy' => 127, 799 + ), 800 + '128.phabricatorcom.sql' => array( 801 + 'type' => 'sql', 802 + 'name' => $this->getPatchPath('128.phabricatorcom.sql'), 803 + 'legacy' => 128, 804 + ), 805 + '129.savedquery.sql' => array( 806 + 'type' => 'sql', 807 + 'name' => $this->getPatchPath('129.savedquery.sql'), 808 + 'legacy' => 129, 809 + ), 810 + '130.denormalrevisionquery.sql' => array( 811 + 'type' => 'sql', 812 + 'name' => $this->getPatchPath('130.denormalrevisionquery.sql'), 813 + 'legacy' => 130, 814 + ), 815 + '131.migraterevisionquery.php' => array( 816 + 'type' => 'php', 817 + 'name' => $this->getPatchPath('131.migraterevisionquery.php'), 818 + 'legacy' => 131, 819 + ), 820 + '132.phame.sql' => array( 821 + 'type' => 'sql', 822 + 'name' => $this->getPatchPath('132.phame.sql'), 823 + 'legacy' => 132, 824 + ), 825 + '133.imagemacro.sql' => array( 826 + 'type' => 'sql', 827 + 'name' => $this->getPatchPath('133.imagemacro.sql'), 828 + 'legacy' => 133, 829 + ), 830 + '134.emptysearch.sql' => array( 831 + 'type' => 'sql', 832 + 'name' => $this->getPatchPath('134.emptysearch.sql'), 833 + 'legacy' => 134, 834 + ), 835 + '135.datecommitted.sql' => array( 836 + 'type' => 'sql', 837 + 'name' => $this->getPatchPath('135.datecommitted.sql'), 838 + 'legacy' => 135, 839 + ), 840 + '136.sex.sql' => array( 841 + 'type' => 'sql', 842 + 'name' => $this->getPatchPath('136.sex.sql'), 843 + 'legacy' => 136, 844 + ), 845 + '137.auditmetadata.sql' => array( 846 + 'type' => 'sql', 847 + 'name' => $this->getPatchPath('137.auditmetadata.sql'), 848 + 'legacy' => 137, 849 + ), 850 + ); 851 + } 852 + 853 + }
+15
src/infrastructure/setup/sql/phabricator/__init__.php
··· 1 + <?php 2 + /** 3 + * This file is automatically generated. Lint this module to rebuild it. 4 + * @generated 5 + */ 6 + 7 + 8 + 9 + phutil_require_module('phabricator', 'infrastructure/setup/sql/base'); 10 + 11 + phutil_require_module('phutil', 'filesystem'); 12 + phutil_require_module('phutil', 'moduleutils'); 13 + 14 + 15 + phutil_require_source('PhabricatorBuiltinPatchList.php');
+193
src/infrastructure/setup/storage/management/PhabricatorStorageManagementAPI.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PhabricatorStorageManagementAPI { 20 + 21 + private $host; 22 + private $user; 23 + private $password; 24 + private $namespace; 25 + 26 + public function setNamespace($namespace) { 27 + $this->namespace = $namespace; 28 + PhabricatorLiskDAO::setApplicationNamespace($namespace); 29 + return $this; 30 + } 31 + 32 + public function getNamespace() { 33 + return $this->namespace; 34 + } 35 + 36 + public function setUser($user) { 37 + $this->user = $user; 38 + return $this; 39 + } 40 + 41 + public function getUser() { 42 + return $this->user; 43 + } 44 + 45 + public function setPassword($password) { 46 + $this->password = $password; 47 + return $this; 48 + } 49 + 50 + public function getPassword() { 51 + return $this->password; 52 + } 53 + 54 + public function setHost($host) { 55 + $this->host = $host; 56 + return $this; 57 + } 58 + 59 + public function getHost() { 60 + return $this->host; 61 + } 62 + 63 + public function getDatabaseName($fragment) { 64 + return $this->namespace.'_'.$fragment; 65 + } 66 + 67 + public function getDatabaseList(array $patches) { 68 + assert_instances_of($patches, 'PhabricatorStoragePatch'); 69 + 70 + $list = array(); 71 + 72 + foreach ($patches as $patch) { 73 + if ($patch->getType() == 'db') { 74 + $list[] = $this->getDatabaseName($patch->getName()); 75 + } 76 + } 77 + 78 + return $list; 79 + } 80 + 81 + public function getConn($fragment, $select_database = true) { 82 + return PhabricatorEnv::newObjectFromConfig( 83 + 'mysql.implementation', 84 + array( 85 + array( 86 + 'user' => $this->user, 87 + 'pass' => $this->password, 88 + 'host' => $this->host, 89 + 'database' => $select_database 90 + ? $this->getDatabaseName($fragment) 91 + : null, 92 + ), 93 + )); 94 + } 95 + 96 + public function getAppliedPatches() { 97 + try { 98 + $applied = queryfx_all( 99 + $this->getConn('meta_data'), 100 + 'SELECT patch FROM patch_status'); 101 + return ipull($applied, 'patch'); 102 + } catch (AphrontQueryException $ex) { 103 + return null; 104 + } 105 + } 106 + 107 + public function createDatabase($fragment) { 108 + queryfx( 109 + $this->getConn($fragment, $select_database = false), 110 + 'CREATE DATABASE IF NOT EXISTS %T COLLATE utf8_general_ci', 111 + $this->getDatabaseName($fragment)); 112 + } 113 + 114 + public function createTable($fragment, $table, array $cols) { 115 + queryfx( 116 + $this->getConn($fragment), 117 + 'CREATE TABLE IF NOT EXISTS %T.%T (%Q) '. 118 + 'ENGINE=InnoDB, COLLATE utf8_general_ci', 119 + $this->getDatabaseName($fragment), 120 + $table, 121 + implode(', ', $cols)); 122 + } 123 + 124 + public function getLegacyPatches(array $patches) { 125 + assert_instances_of($patches, 'PhabricatorStoragePatch'); 126 + 127 + try { 128 + $row = queryfx_one( 129 + $this->getConn('meta_data'), 130 + 'SELECT version FROM %T', 131 + 'schema_version'); 132 + $version = $row['version']; 133 + } catch (AphrontQueryException $ex) { 134 + return array(); 135 + } 136 + 137 + $legacy = array(); 138 + foreach ($patches as $key => $patch) { 139 + if ($patch->getLegacy() !== false && $patch->getLegacy() <= $version) { 140 + $legacy[] = $key; 141 + } 142 + } 143 + 144 + return $legacy; 145 + } 146 + 147 + public function markPatchApplied($patch) { 148 + queryfx( 149 + $this->getConn('meta_data'), 150 + 'INSERT INTO %T (patch, applied) VALUES (%s, %d)', 151 + 'patch_status', 152 + $patch, 153 + time()); 154 + } 155 + 156 + public function applyPatch(PhabricatorStoragePatch $patch) { 157 + $type = $patch->getType(); 158 + $name = $patch->getName(); 159 + switch ($type) { 160 + case 'db': 161 + $this->createDatabase($name); 162 + break; 163 + case 'sql': 164 + $this->applyPatchSQL($name); 165 + break; 166 + case 'php': 167 + $this->applyPatchPHP($name); 168 + break; 169 + default: 170 + throw new Exception("Unable to apply patch of type '{$type}'."); 171 + } 172 + } 173 + 174 + public function applyPatchSQL($sql) { 175 + $sql = Filesystem::readFile($sql); 176 + $queries = preg_split('/;\s+/', $sql); 177 + $queries = array_filter($queries); 178 + 179 + foreach ($queries as $query) { 180 + $query = str_replace('{$NAMESPACE}', $this->namespace, $query); 181 + queryfx( 182 + $this->getConn('meta_data', $select_database = false), 183 + '%Q', 184 + $query); 185 + } 186 + } 187 + 188 + public function applyPatchPHP($script) { 189 + $schema_conn = $this->getConn('meta_data', $select_database = false); 190 + require_once $script; 191 + } 192 + 193 + }
+17
src/infrastructure/setup/storage/management/__init__.php
··· 1 + <?php 2 + /** 3 + * This file is automatically generated. Lint this module to rebuild it. 4 + * @generated 5 + */ 6 + 7 + 8 + 9 + phutil_require_module('phabricator', 'applications/base/storage/lisk'); 10 + phutil_require_module('phabricator', 'infrastructure/env'); 11 + phutil_require_module('phabricator', 'storage/queryfx'); 12 + 13 + phutil_require_module('phutil', 'filesystem'); 14 + phutil_require_module('phutil', 'utils'); 15 + 16 + 17 + phutil_require_source('PhabricatorStorageManagementAPI.php');
+61
src/infrastructure/setup/storage/patch/PhabricatorStoragePatch.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PhabricatorStoragePatch { 20 + 21 + private $key; 22 + private $fullKey; 23 + private $name; 24 + private $type; 25 + private $after; 26 + private $legacy; 27 + 28 + public function __construct(array $dict) { 29 + $this->key = $dict['key']; 30 + $this->type = $dict['type']; 31 + $this->fullKey = $dict['fullKey']; 32 + $this->legacy = $dict['legacy']; 33 + $this->name = $dict['name']; 34 + $this->after = $dict['after']; 35 + } 36 + 37 + public function getLegacy() { 38 + return $this->legacy; 39 + } 40 + 41 + public function getAfter() { 42 + return $this->after; 43 + } 44 + 45 + public function getType() { 46 + return $this->type; 47 + } 48 + 49 + public function getName() { 50 + return $this->name; 51 + } 52 + 53 + public function getFullKey() { 54 + return $this->fullKey; 55 + } 56 + 57 + public function getKey() { 58 + return $this->key; 59 + } 60 + 61 + }
+10
src/infrastructure/setup/storage/patch/__init__.php
··· 1 + <?php 2 + /** 3 + * This file is automatically generated. Lint this module to rebuild it. 4 + * @generated 5 + */ 6 + 7 + 8 + 9 + 10 + phutil_require_source('PhabricatorStoragePatch.php');
+48
src/infrastructure/setup/storage/workflow/base/PhabricatorStorageManagementWorkflow.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + abstract class PhabricatorStorageManagementWorkflow 20 + extends PhutilArgumentWorkflow { 21 + 22 + private $patches; 23 + private $api; 24 + 25 + public function setPatches(array $patches) { 26 + assert_instances_of($patches, 'PhabricatorStoragePatch'); 27 + $this->patches = $patches; 28 + return $this; 29 + } 30 + 31 + public function getPatches() { 32 + return $this->patches; 33 + } 34 + 35 + final public function setAPI(PhabricatorStorageManagementAPI $api) { 36 + $this->api = $api; 37 + return $this; 38 + } 39 + 40 + final public function getAPI() { 41 + return $this->api; 42 + } 43 + 44 + public function isExecutable() { 45 + return true; 46 + } 47 + 48 + }
+13
src/infrastructure/setup/storage/workflow/base/__init__.php
··· 1 + <?php 2 + /** 3 + * This file is automatically generated. Lint this module to rebuild it. 4 + * @generated 5 + */ 6 + 7 + 8 + 9 + phutil_require_module('phutil', 'parser/argument/workflow/base'); 10 + phutil_require_module('phutil', 'utils'); 11 + 12 + 13 + phutil_require_source('PhabricatorStorageManagementWorkflow.php');
+39
src/infrastructure/setup/storage/workflow/databases/PhabricatorStorageManagementDatabasesWorkflow.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PhabricatorStorageManagementDatabasesWorkflow 20 + extends PhabricatorStorageManagementWorkflow { 21 + 22 + public function didConstruct() { 23 + $this 24 + ->setName('databases') 25 + ->setExamples('**databases** [__options__]') 26 + ->setSynopsis('List Phabricator databases.'); 27 + } 28 + 29 + public function execute(PhutilArgumentParser $args) { 30 + $api = $this->getAPI(); 31 + $patches = $this->getPatches(); 32 + 33 + $databases = $api->getDatabaseList($patches); 34 + echo implode("\n", $databases)."\n"; 35 + 36 + return 0; 37 + } 38 + 39 + }
+12
src/infrastructure/setup/storage/workflow/databases/__init__.php
··· 1 + <?php 2 + /** 3 + * This file is automatically generated. Lint this module to rebuild it. 4 + * @generated 5 + */ 6 + 7 + 8 + 9 + phutil_require_module('phabricator', 'infrastructure/setup/storage/workflow/base'); 10 + 11 + 12 + phutil_require_source('PhabricatorStorageManagementDatabasesWorkflow.php');
+74
src/infrastructure/setup/storage/workflow/destroy/PhabricatorStorageManagementDestroyWorkflow.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PhabricatorStorageManagementDestroyWorkflow 20 + extends PhabricatorStorageManagementWorkflow { 21 + 22 + public function didConstruct() { 23 + $this 24 + ->setName('destroy') 25 + ->setExamples('**destroy** [__options__]') 26 + ->setSynopsis('Permanently destroy all storage and data.'); 27 + } 28 + 29 + public function execute(PhutilArgumentParser $args) { 30 + $is_dry = $args->getArg('dryrun'); 31 + $is_force = $args->getArg('force'); 32 + 33 + if (!$is_dry && !$is_force) { 34 + echo phutil_console_wrap( 35 + "Are you completely sure you really want to permanently destroy all ". 36 + "storage for Phabricator data? This operation can not be undone and ". 37 + "your data will not be recoverable if you proceed."); 38 + 39 + if (!phutil_console_confirm('Permanently destroy all data?')) { 40 + echo "Cancelled.\n"; 41 + exit(1); 42 + } 43 + 44 + if (!phutil_console_confirm('Really destroy all data forever?')) { 45 + echo "Cancelled.\n"; 46 + exit(1); 47 + } 48 + } 49 + 50 + $api = $this->getAPI(); 51 + $patches = $this->getPatches(); 52 + 53 + $databases = $api->getDatabaseList($patches); 54 + $databases[] = $api->getDatabaseName('meta_data'); 55 + foreach ($databases as $database) { 56 + if ($is_dry) { 57 + echo "DRYRUN: Would drop database '{$database}'.\n"; 58 + } else { 59 + echo "Dropping database '{$database}'...\n"; 60 + queryfx( 61 + $api->getConn('meta_data', $select_database = false), 62 + 'DROP DATABASE IF EXISTS %T', 63 + $database); 64 + } 65 + } 66 + 67 + if (!$is_dry) { 68 + echo "Storage was destroyed.\n"; 69 + } 70 + 71 + return 0; 72 + } 73 + 74 + }
+15
src/infrastructure/setup/storage/workflow/destroy/__init__.php
··· 1 + <?php 2 + /** 3 + * This file is automatically generated. Lint this module to rebuild it. 4 + * @generated 5 + */ 6 + 7 + 8 + 9 + phutil_require_module('phabricator', 'infrastructure/setup/storage/workflow/base'); 10 + phutil_require_module('phabricator', 'storage/queryfx'); 11 + 12 + phutil_require_module('phutil', 'console'); 13 + 14 + 15 + phutil_require_source('PhabricatorStorageManagementDestroyWorkflow.php');
+83
src/infrastructure/setup/storage/workflow/dump/PhabricatorStorageManagementDumpWorkflow.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PhabricatorStorageManagementDumpWorkflow 20 + extends PhabricatorStorageManagementWorkflow { 21 + 22 + public function didConstruct() { 23 + $this 24 + ->setName('dump') 25 + ->setExamples('**dump** [__options__]') 26 + ->setSynopsis('Dump all data in storage to stdout.'); 27 + } 28 + 29 + public function execute(PhutilArgumentParser $args) { 30 + $api = $this->getAPI(); 31 + $patches = $this->getPatches(); 32 + 33 + $applied = $api->getAppliedPatches(); 34 + if ($applied === null) { 35 + $namespace = $api->getNamespace(); 36 + echo phutil_console_wrap( 37 + phutil_console_format( 38 + "**No Storage**: There is no database storage initialized in this ". 39 + "storage namespace ('{$namespace}'). Use '**storage upgrade**' to ". 40 + "initialize storage.\n")); 41 + return 1; 42 + } 43 + 44 + $databases = $api->getDatabaseList($patches); 45 + 46 + list($host, $port) = $this->getBareHostAndPort($api->getHost()); 47 + 48 + $flag_password = $api->getPassword() 49 + ? csprintf('-p %s', $api->getPassword()) 50 + : ''; 51 + 52 + $flag_port = $port 53 + ? csprintf('--port %d', $port) 54 + : ''; 55 + 56 + return phutil_passthru( 57 + 58 + 'mysqldump --default-character-set=utf8 '. 59 + '-u %s %C -h %s %C --databases %Ls', 60 + 61 + $api->getUser(), 62 + $flag_password, 63 + $host, 64 + $flag_port, 65 + $databases); 66 + } 67 + 68 + private function getBareHostAndPort($host) { 69 + // Split out port information, since the command-line client requires a 70 + // separate flag for the port. 71 + $uri = new PhutilURI('mysql://'.$host); 72 + if ($uri->getPort()) { 73 + $port = $uri->getPort(); 74 + $bare_hostname = $uri->getDomain(); 75 + } else { 76 + $port = null; 77 + $bare_hostname = $host; 78 + } 79 + 80 + return array($bare_hostname, $port); 81 + } 82 + 83 + }
+17
src/infrastructure/setup/storage/workflow/dump/__init__.php
··· 1 + <?php 2 + /** 3 + * This file is automatically generated. Lint this module to rebuild it. 4 + * @generated 5 + */ 6 + 7 + 8 + 9 + phutil_require_module('phabricator', 'infrastructure/setup/storage/workflow/base'); 10 + 11 + phutil_require_module('phutil', 'console'); 12 + phutil_require_module('phutil', 'future/exec'); 13 + phutil_require_module('phutil', 'parser/uri'); 14 + phutil_require_module('phutil', 'xsprintf/csprintf'); 15 + 16 + 17 + phutil_require_source('PhabricatorStorageManagementDumpWorkflow.php');
+67
src/infrastructure/setup/storage/workflow/status/PhabricatorStorageManagementStatusWorkflow.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PhabricatorStorageManagementStatusWorkflow 20 + extends PhabricatorStorageManagementWorkflow { 21 + 22 + public function didConstruct() { 23 + $this 24 + ->setName('status') 25 + ->setExamples('**status** [__options__]') 26 + ->setSynopsis('Show patch application status.'); 27 + } 28 + 29 + public function execute(PhutilArgumentParser $args) { 30 + $api = $this->getAPI(); 31 + $patches = $this->getPatches(); 32 + 33 + $applied = $api->getAppliedPatches(); 34 + 35 + if ($applied === null) { 36 + echo phutil_console_format( 37 + "**Database Not Initialized**: Run **storage upgrade** to ". 38 + "initialize.\n"); 39 + 40 + return 1; 41 + } 42 + 43 + $len = 0; 44 + foreach ($patches as $patch) { 45 + $len = max($len, strlen($patch->getFullKey())); 46 + } 47 + 48 + foreach ($patches as $patch) { 49 + printf( 50 + 51 + "% -".($len + 2)."s ". 52 + "%-".strlen("Not Applied")."s ". 53 + "%-4s ". 54 + "%s\n", 55 + 56 + $patch->getFullKey(), 57 + in_array($patch->getFullKey(), $applied) 58 + ? 'Applied' 59 + : 'Not Applied', 60 + $patch->getType(), 61 + $patch->getName()); 62 + } 63 + 64 + return 0; 65 + } 66 + 67 + }
+14
src/infrastructure/setup/storage/workflow/status/__init__.php
··· 1 + <?php 2 + /** 3 + * This file is automatically generated. Lint this module to rebuild it. 4 + * @generated 5 + */ 6 + 7 + 8 + 9 + phutil_require_module('phabricator', 'infrastructure/setup/storage/workflow/base'); 10 + 11 + phutil_require_module('phutil', 'console'); 12 + 13 + 14 + phutil_require_source('PhabricatorStorageManagementStatusWorkflow.php');
+209
src/infrastructure/setup/storage/workflow/upgrade/PhabricatorStorageManagementUpgradeWorkflow.php
··· 1 + <?php 2 + 3 + /* 4 + * Copyright 2012 Facebook, Inc. 5 + * 6 + * Licensed under the Apache License, Version 2.0 (the "License"); 7 + * you may not use this file except in compliance with the License. 8 + * You may obtain a copy of the License at 9 + * 10 + * http://www.apache.org/licenses/LICENSE-2.0 11 + * 12 + * Unless required by applicable law or agreed to in writing, software 13 + * distributed under the License is distributed on an "AS IS" BASIS, 14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + * See the License for the specific language governing permissions and 16 + * limitations under the License. 17 + */ 18 + 19 + final class PhabricatorStorageManagementUpgradeWorkflow 20 + extends PhabricatorStorageManagementWorkflow { 21 + 22 + public function didConstruct() { 23 + $this 24 + ->setName('upgrade') 25 + ->setExamples('**upgrade** [__options__]') 26 + ->setSynopsis("Upgrade database schemata.") 27 + ->setArguments( 28 + array( 29 + array( 30 + 'name' => 'apply', 31 + 'param' => 'patch', 32 + 'help' => 'Apply __patch__ explicitly. This is an advanced '. 33 + 'feature for development and debugging; you should '. 34 + 'not normally use this flag.', 35 + ), 36 + array( 37 + 'name' => 'no-quickstart', 38 + 'help' => 'Build storage patch-by-patch from scatch, even if it '. 39 + 'could be loaded from the quickstart template.', 40 + ), 41 + array( 42 + 'name' => 'init-only', 43 + 'help' => 'Initialize storage only; do not apply patches.', 44 + ), 45 + )); 46 + } 47 + 48 + public function execute(PhutilArgumentParser $args) { 49 + $is_dry = $args->getArg('dryrun'); 50 + $is_force = $args->getArg('force'); 51 + 52 + $api = $this->getAPI(); 53 + $patches = $this->getPatches(); 54 + 55 + if (!$is_dry && !$is_force) { 56 + echo phutil_console_wrap( 57 + "Before running storage upgrades, you should take down the ". 58 + "Phabricator web interface and stop any running Phabricator ". 59 + "daemons (you can disable this warning with --force)."); 60 + 61 + if (!phutil_console_confirm('Are you ready to continue?')) { 62 + echo "Cancelled.\n"; 63 + return 1; 64 + } 65 + } 66 + 67 + $apply_only = $args->getArg('apply'); 68 + if ($apply_only) { 69 + if (empty($patches[$apply_only])) { 70 + throw new PhutilArgumentUsageException( 71 + "--apply argument '{$apply_only}' is not a valid patch. Use ". 72 + "'storage status' to show patch status."); 73 + } 74 + } 75 + 76 + $no_quickstart = $args->getArg('no-quickstart'); 77 + $init_only = $args->getArg('init-only'); 78 + 79 + $applied = $api->getAppliedPatches(); 80 + if ($applied === null) { 81 + 82 + if ($is_dry) { 83 + echo "DRYRUN: Patch metadata storage doesn't exist yet, it would ". 84 + "be created.\n"; 85 + return 0; 86 + } 87 + 88 + if ($apply_only) { 89 + throw new PhutilArgumentUsageException( 90 + "Storage has not been initialized yet, you must initialize storage ". 91 + "before selectively applying patches."); 92 + return 1; 93 + } 94 + 95 + $legacy = $api->getLegacyPatches($patches); 96 + if ($legacy || $no_quickstart || $init_only) { 97 + 98 + // If we have legacy patches, we can't quickstart. 99 + 100 + $api->createDatabase('meta_data'); 101 + $api->createTable( 102 + 'meta_data', 103 + 'patch_status', 104 + array( 105 + 'patch VARCHAR(255) NOT NULL PRIMARY KEY COLLATE utf8_general_ci', 106 + 'applied INT UNSIGNED NOT NULL', 107 + )); 108 + 109 + foreach ($legacy as $patch) { 110 + $api->markPatchApplied($patch); 111 + } 112 + } else { 113 + echo "Loading quickstart template...\n"; 114 + $root = dirname(phutil_get_library_root('phabricator')); 115 + $sql = $root.'/resources/sql/quickstart.sql'; 116 + $api->applyPatchSQL($sql); 117 + } 118 + } 119 + 120 + if ($init_only) { 121 + echo "Storage initialized.\n"; 122 + return 0; 123 + } 124 + 125 + $applied = $api->getAppliedPatches(); 126 + $applied = array_fill_keys($applied, true); 127 + 128 + $skip_mark = false; 129 + if ($apply_only) { 130 + if (isset($applied[$apply_only])) { 131 + 132 + unset($applied[$apply_only]); 133 + $skip_mark = true; 134 + 135 + if (!$is_force && !$is_dry) { 136 + echo phutil_console_wrap( 137 + "Patch '{$apply_only}' has already been applied. Are you sure ". 138 + "you want to apply it again? This may put your storage in a state ". 139 + "that the upgrade scripts can not automatically manage."); 140 + if (!phutil_console_confirm('Apply patch again?')) { 141 + echo "Cancelled.\n"; 142 + return 1; 143 + } 144 + } 145 + } 146 + } 147 + 148 + while (true) { 149 + $applied_something = false; 150 + foreach ($patches as $key => $patch) { 151 + if (isset($applied[$key])) { 152 + unset($patches[$key]); 153 + continue; 154 + } 155 + 156 + if ($apply_only && $apply_only != $key) { 157 + unset($patches[$key]); 158 + continue; 159 + } 160 + 161 + $can_apply = true; 162 + foreach ($patch->getAfter() as $after) { 163 + if (empty($applied[$after])) { 164 + if ($apply_only) { 165 + echo "Unable to apply patch '{$apply_only}' because it depends ". 166 + "on patch '{$after}', which has not been applied.\n"; 167 + return 1; 168 + } 169 + $can_apply = false; 170 + break; 171 + } 172 + } 173 + 174 + if (!$can_apply) { 175 + continue; 176 + } 177 + 178 + $applied_something = true; 179 + 180 + if ($is_dry) { 181 + echo "DRYRUN: Would apply patch '{$key}'.\n"; 182 + } else { 183 + echo "Applying patch '{$key}'...\n"; 184 + $api->applyPatch($patch); 185 + if (!$skip_mark) { 186 + $api->markPatchApplied($key); 187 + } 188 + } 189 + 190 + unset($patches[$key]); 191 + $applied[$key] = true; 192 + } 193 + 194 + if (!$applied_something) { 195 + if (count($patches)) { 196 + throw new Exception( 197 + "Some patches could not be applied: ". 198 + implode(', ', array_keys($patches))); 199 + } else if (!$is_dry && !$apply_only) { 200 + echo "Storage is up to date. Use 'storage status' for details.\n"; 201 + } 202 + break; 203 + } 204 + } 205 + 206 + return 0; 207 + } 208 + 209 + }
+16
src/infrastructure/setup/storage/workflow/upgrade/__init__.php
··· 1 + <?php 2 + /** 3 + * This file is automatically generated. Lint this module to rebuild it. 4 + * @generated 5 + */ 6 + 7 + 8 + 9 + phutil_require_module('phabricator', 'infrastructure/setup/storage/workflow/base'); 10 + 11 + phutil_require_module('phutil', 'console'); 12 + phutil_require_module('phutil', 'moduleutils'); 13 + phutil_require_module('phutil', 'parser/argument/exception/usage'); 14 + 15 + 16 + phutil_require_source('PhabricatorStorageManagementUpgradeWorkflow.php');
+1 -1
src/storage/exception/schema/AphrontQuerySchemaException.php
··· 25 25 $message .= 26 26 "\n\n". 27 27 "NOTE: This usually indicates that the MySQL schema has not been ". 28 - "properly upgraded. Run scripts/sql/upgrade_schema.php to ensure your ". 28 + "properly upgraded. Run 'bin/storage upgrade' to ensure your ". 29 29 "schema is up to date."; 30 30 31 31 parent::__construct($message);