@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
at upstream/main 249 lines 7.4 kB view raw
1#!/usr/bin/env php 2<?php 3 4$root = dirname(dirname(dirname(__FILE__))); 5require_once $root.'/scripts/init/init-setup.php'; 6 7$args = new PhutilArgumentParser($argv); 8$args->setTagline(pht('manage storage and schemata')); 9$args->setSynopsis(pht(<<<EOHELP 10**storage** __workflow__ [__options__] 11Manage database storage and schema versioning. 12 13**storage** upgrade 14Initialize or upgrade storage. 15 16**storage** upgrade --user __root__ --password __hunter2__ 17Use administrative credentials for schema changes. 18EOHELP 19)); 20$args->parseStandardArguments(); 21 22$default_namespace = PhabricatorLiskDAO::getDefaultStorageNamespace(); 23 24try { 25 $args->parsePartial( 26 array( 27 array( 28 'name' => 'force', 29 'short' => 'f', 30 'help' => pht( 31 'Do not prompt before performing dangerous operations.'), 32 ), 33 array( 34 'name' => 'host', 35 'param' => 'hostname', 36 'help' => pht( 37 'Operate on the database server identified by __hostname__.'), 38 ), 39 array( 40 'name' => 'ref', 41 'param' => 'ref', 42 'help' => pht( 43 'Operate on the database identified by __ref__.'), 44 ), 45 array( 46 'name' => 'user', 47 'short' => 'u', 48 'param' => 'username', 49 'help' => pht( 50 'Connect with __username__ instead of the configured default.'), 51 ), 52 array( 53 'name' => 'password', 54 'short' => 'p', 55 'param' => 'password', 56 'help' => pht('Use __password__ instead of the configured default.'), 57 ), 58 array( 59 'name' => 'namespace', 60 'param' => 'name', 61 'default' => $default_namespace, 62 'help' => pht( 63 "Use namespace __namespace__ instead of the configured ". 64 "default ('%s'). This is an advanced feature used by unit tests; ". 65 "you should not normally use this flag.", 66 $default_namespace), 67 ), 68 array( 69 'name' => 'dryrun', 70 'help' => pht( 71 'Do not actually change anything, just show what would be changed.'), 72 ), 73 array( 74 'name' => 'disable-utf8mb4', 75 'help' => pht( 76 'Disable %s, even if the database supports it. This is an '. 77 'advanced feature used for testing internal changes; you '. 78 'should not normally use this flag.', 79 'utf8mb4'), 80 ), 81 )); 82} catch (PhutilArgumentUsageException $ex) { 83 $args->printUsageException($ex); 84 exit(77); 85} 86 87// First, test that the Phabricator configuration is set up correctly. After 88// we know this works we'll test any administrative credentials specifically. 89 90$refs = PhabricatorDatabaseRef::getActiveDatabaseRefs(); 91if (!$refs) { 92 throw new PhutilArgumentUsageException( 93 pht('No databases are configured.')); 94} 95 96$host = $args->getArg('host'); 97$ref_key = $args->getArg('ref'); 98if (($host !== null) || ($ref_key !== null)) { 99 if ($host && $ref_key) { 100 throw new PhutilArgumentUsageException( 101 pht( 102 'Use "--host" or "--ref" to select a database, but not both.')); 103 } 104 105 $refs = PhabricatorDatabaseRef::getActiveDatabaseRefs(); 106 107 $possible_refs = array(); 108 foreach ($refs as $possible_ref) { 109 if ($host && ($possible_ref->getHost() == $host)) { 110 $possible_refs[] = $possible_ref; 111 break; 112 } 113 if ($ref_key && ($possible_ref->getRefKey() == $ref_key)) { 114 $possible_refs[] = $possible_ref; 115 break; 116 } 117 } 118 119 if (!$possible_refs) { 120 if ($host) { 121 throw new PhutilArgumentUsageException( 122 pht( 123 'There is no configured database on host "%s". This command can '. 124 'only interact with configured databases.', 125 $host)); 126 } else { 127 throw new PhutilArgumentUsageException( 128 pht( 129 'There is no configured database with ref "%s". This command can '. 130 'only interact with configured databases.', 131 $ref_key)); 132 } 133 } 134 135 if (count($possible_refs) > 1) { 136 throw new PhutilArgumentUsageException( 137 pht( 138 'Host "%s" identifies more than one database. Use "--ref" to select '. 139 'a specific database.', 140 $host)); 141 } 142 143 $refs = $possible_refs; 144} 145 146$apis = array(); 147foreach ($refs as $ref) { 148 $default_user = $ref->getUser(); 149 $default_host = $ref->getHost(); 150 $default_port = $ref->getPort(); 151 152 $test_api = id(new PhabricatorStorageManagementAPI()) 153 ->setUser($default_user) 154 ->setHost($default_host) 155 ->setPort($default_port) 156 ->setPassword($ref->getPass()) 157 ->setNamespace($args->getArg('namespace')); 158 159 try { 160 queryfx( 161 $test_api->getConn(null), 162 'SELECT 1'); 163 } catch (AphrontQueryException $ex) { 164 $message = phutil_console_format( 165 "**%s**\n\n%s\n\n%s\n\n%s\n\n**%s**: %s\n", 166 pht('MySQL Credentials Not Configured'), 167 pht( 168 'Unable to connect to MySQL using the configured credentials. '. 169 'You must configure standard credentials before you can upgrade '. 170 'storage. Run these commands to set up credentials:'), 171 " $ ./bin/config set mysql.host __host__\n". 172 " $ ./bin/config set mysql.user __username__\n". 173 " $ ./bin/config set mysql.pass __password__", 174 pht( 175 'These standard credentials are separate from any administrative '. 176 'credentials provided to this command with __%s__ or '. 177 '__%s__, and must be configured correctly before you can proceed.', 178 '--user', 179 '--password'), 180 pht('Raw MySQL Error'), 181 $ex->getMessage()); 182 echo phutil_console_wrap($message); 183 exit(1); 184 } 185 186 if ($args->getArg('password') === null) { 187 // This is already a PhutilOpaqueEnvelope. 188 $password = $ref->getPass(); 189 } else { 190 // Put this in a PhutilOpaqueEnvelope. 191 $password = new PhutilOpaqueEnvelope($args->getArg('password')); 192 PhabricatorEnv::overrideConfig('mysql.pass', $args->getArg('password')); 193 } 194 195 $selected_user = $args->getArg('user'); 196 if ($selected_user === null) { 197 $selected_user = $default_user; 198 } 199 200 $api = id(new PhabricatorStorageManagementAPI()) 201 ->setUser($selected_user) 202 ->setHost($default_host) 203 ->setPort($default_port) 204 ->setPassword($password) 205 ->setNamespace($args->getArg('namespace')) 206 ->setDisableUTF8MB4($args->getArg('disable-utf8mb4')); 207 PhabricatorEnv::overrideConfig('mysql.user', $api->getUser()); 208 209 $ref->setUser($selected_user); 210 $ref->setPass($password); 211 212 try { 213 queryfx( 214 $api->getConn(null), 215 'SELECT 1'); 216 } catch (AphrontQueryException $ex) { 217 $message = phutil_console_format( 218 "**%s**\n\n%s\n\n**%s**: %s\n", 219 pht('Bad Administrative Credentials'), 220 pht( 221 'Unable to connect to MySQL using the administrative credentials '. 222 'provided with the __%s__ and __%s__ flags. Check that '. 223 'you have entered them correctly.', 224 '--user', 225 '--password'), 226 pht('Raw MySQL Error'), 227 $ex->getMessage()); 228 echo phutil_console_wrap($message); 229 exit(1); 230 } 231 232 $api->setRef($ref); 233 $apis[] = $api; 234} 235 236$workflows = id(new PhutilClassMapQuery()) 237 ->setAncestorClass(PhabricatorStorageManagementWorkflow::class) 238 ->execute(); 239 240$patches = PhabricatorSQLPatchList::buildAllPatches(); 241 242foreach ($workflows as $workflow) { 243 $workflow->setAPIs($apis); 244 $workflow->setPatches($patches); 245} 246 247$workflows[] = new PhutilHelpArgumentWorkflow(); 248 249$args->parseWorkflows($workflows);