Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 106 |
|
0.00% |
0 / 13 |
CRAP | |
0.00% |
0 / 1 |
| InstallPreConfigured | |
0.00% |
0 / 103 |
|
0.00% |
0 / 13 |
702 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
2 | |||
| getDbType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| finalSetup | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
| execute | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
20 | |||
| parseKeyValue | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
| getTaskContext | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| createTaskContext | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
12 | |||
| getSubclassDefaultOptions | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| createTaskList | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
6 | |||
| getExtraTaskSpecs | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| createTaskRunner | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
20 | |||
| getTaskSkips | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| createTaskFactory | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
| 1 | <?php |
| 2 | |
| 3 | use MediaWiki\Installer\Installer; |
| 4 | use MediaWiki\Installer\Task\AddWikiTaskContext; |
| 5 | use MediaWiki\Installer\Task\CannedProvider; |
| 6 | use MediaWiki\Installer\Task\ITaskContext; |
| 7 | use MediaWiki\Installer\Task\Task; |
| 8 | use MediaWiki\Installer\Task\TaskFactory; |
| 9 | use MediaWiki\Installer\Task\TaskList; |
| 10 | use MediaWiki\Installer\Task\TaskRunner; |
| 11 | use MediaWiki\Maintenance\Maintenance; |
| 12 | use MediaWiki\Registration\ExtensionRegistry; |
| 13 | use MediaWiki\Settings\SettingsBuilder; |
| 14 | use Symfony\Component\Yaml\Yaml; |
| 15 | |
| 16 | require_once __DIR__ . '/Maintenance.php'; |
| 17 | |
| 18 | /** |
| 19 | * @since 1.44 |
| 20 | * @stable to extend |
| 21 | */ |
| 22 | class InstallPreConfigured extends Maintenance { |
| 23 | /** @var ITaskContext|null */ |
| 24 | private $taskContext; |
| 25 | |
| 26 | public function __construct() { |
| 27 | parent::__construct(); |
| 28 | $this->addDescription( 'Create the database and tables for a new wiki, ' . |
| 29 | 'using a pre-existing LocalSettings.php' ); |
| 30 | $this->addOption( 'task', |
| 31 | 'Execute only the specified task', false, true ); |
| 32 | $this->addOption( 'skip', |
| 33 | 'Skip the specified task', false, true, false, true ); |
| 34 | $this->addOption( 'override-config', |
| 35 | 'Specify a configuration variable with name=value. The value is in YAML format.', |
| 36 | false, true, 'c', true ); |
| 37 | $this->addOption( 'override-option', |
| 38 | 'Specify an installer option with name=value. The value is in YAML format.', |
| 39 | false, true, 'o', true ); |
| 40 | $this->addOption( 'show-tasks', |
| 41 | 'Show the list of tasks to be executed, do not actually install' ); |
| 42 | } |
| 43 | |
| 44 | /** @inheritDoc */ |
| 45 | public function getDbType() { |
| 46 | return Maintenance::DB_ADMIN; |
| 47 | } |
| 48 | |
| 49 | public function finalSetup( SettingsBuilder $settingsBuilder ) { |
| 50 | parent::finalSetup( $settingsBuilder ); |
| 51 | |
| 52 | // Apply override-config options. Doing this here instead of in |
| 53 | // AddWikiTaskContext::setConfigVar() allows the options to be available |
| 54 | // globally for use in LoadExtensionSchemaUpdates. |
| 55 | foreach ( $this->getOption( 'override-config' ) ?? [] as $str ) { |
| 56 | [ $name, $value ] = $this->parseKeyValue( $str ); |
| 57 | if ( str_starts_with( $name, 'wg' ) ) { |
| 58 | $name = substr( $name, 2 ); |
| 59 | } |
| 60 | $settingsBuilder->putConfigValue( $name, $value ); |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | /** @inheritDoc */ |
| 65 | public function execute() { |
| 66 | $context = $this->getTaskContext(); |
| 67 | $taskFactory = $this->createTaskFactory( $context ); |
| 68 | $taskList = $this->createTaskList( $taskFactory ); |
| 69 | $taskRunner = $this->createTaskRunner( $taskList, $taskFactory ); |
| 70 | |
| 71 | Installer::disableStorage( $this->getConfig(), 'en' ); |
| 72 | |
| 73 | if ( $this->hasOption( 'show-tasks' ) ) { |
| 74 | $taskRunner->loadExtensions(); |
| 75 | echo $taskRunner->dumpTaskList(); |
| 76 | return false; |
| 77 | } |
| 78 | |
| 79 | if ( $this->hasOption( 'task' ) ) { |
| 80 | $status = $taskRunner->runNamedTask( $this->getOption( 'task' ) ); |
| 81 | } else { |
| 82 | $status = $taskRunner->execute(); |
| 83 | } |
| 84 | |
| 85 | if ( $status->isOK() ) { |
| 86 | $this->output( "Installation complete.\n" ); |
| 87 | return true; |
| 88 | } else { |
| 89 | $this->error( "Installation failed at task \"" . |
| 90 | $taskRunner->getCurrentTaskName() . '"' ); |
| 91 | return false; |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | /** |
| 96 | * Split a string into a key and a value, and parse the value as YAML. |
| 97 | * |
| 98 | * @param string $str |
| 99 | * @return array A list where the first element is the name, and the |
| 100 | * second is the decoded value |
| 101 | */ |
| 102 | private function parseKeyValue( string $str ) { |
| 103 | $parts = explode( '=', $str, 2 ); |
| 104 | if ( count( $parts ) !== 2 ) { |
| 105 | $this->fatalError( "Invalid configuration variable \"$str\"" ); |
| 106 | } |
| 107 | return [ $parts[0], Yaml::parse( $parts[1], Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE ) ]; |
| 108 | } |
| 109 | |
| 110 | /** |
| 111 | * @return AddWikiTaskContext |
| 112 | */ |
| 113 | protected function getTaskContext() { |
| 114 | if ( !$this->taskContext ) { |
| 115 | $this->taskContext = $this->createTaskContext(); |
| 116 | } |
| 117 | return $this->taskContext; |
| 118 | } |
| 119 | |
| 120 | /** |
| 121 | * Get the context for running tasks, with config overrides from the |
| 122 | * command line. |
| 123 | * |
| 124 | * @return AddWikiTaskContext |
| 125 | */ |
| 126 | private function createTaskContext() { |
| 127 | $context = new AddWikiTaskContext( |
| 128 | $this->getConfig(), |
| 129 | $this->getServiceContainer()->getDBLoadBalancerFactory() |
| 130 | ); |
| 131 | |
| 132 | // Make sure ExtensionTablesTask isn't skipped |
| 133 | $context->setOption( 'Extensions', |
| 134 | array_keys( ExtensionRegistry::getInstance()->getAllThings() ) ); |
| 135 | |
| 136 | foreach ( $this->getOption( 'override-option' ) ?? [] as $str ) { |
| 137 | [ $name, $value ] = $this->parseKeyValue( $str ); |
| 138 | $context->setOption( $name, $value ); |
| 139 | } |
| 140 | foreach ( $this->getSubclassDefaultOptions() as $name => $value ) { |
| 141 | $context->setOption( $name, $value ); |
| 142 | } |
| 143 | |
| 144 | return $context; |
| 145 | } |
| 146 | |
| 147 | /** |
| 148 | * Get installer options overridden by a subclass |
| 149 | * |
| 150 | * @stable to override |
| 151 | * @return array |
| 152 | */ |
| 153 | protected function getSubclassDefaultOptions() { |
| 154 | return []; |
| 155 | } |
| 156 | |
| 157 | /** |
| 158 | * Get the full list of tasks, before skipping is applied. |
| 159 | * |
| 160 | * @param TaskFactory $taskFactory |
| 161 | * @return TaskList |
| 162 | */ |
| 163 | private function createTaskList( TaskFactory $taskFactory ) { |
| 164 | $taskList = new TaskList; |
| 165 | $taskFactory->registerMainTasks( $taskList, TaskFactory::PROFILE_ADD_WIKI ); |
| 166 | $reg = ExtensionRegistry::getInstance(); |
| 167 | $taskList->add( $taskFactory->create( |
| 168 | [ |
| 169 | 'class' => CannedProvider::class, |
| 170 | 'args' => [ |
| 171 | 'extensions', |
| 172 | [ |
| 173 | 'HookContainer' => $this->getHookContainer(), |
| 174 | 'VirtualDomains' => $reg->getAttribute( 'DatabaseVirtualDomains' ), |
| 175 | 'ExtensionTaskSpecs' => $reg->getAttribute( 'InstallerTasks' ), |
| 176 | ] |
| 177 | ] |
| 178 | ] |
| 179 | ) ); |
| 180 | foreach ( $this->getExtraTaskSpecs() as $spec ) { |
| 181 | $taskList->add( $taskFactory->create( $spec ) ); |
| 182 | } |
| 183 | return $taskList; |
| 184 | } |
| 185 | |
| 186 | /** |
| 187 | * Subclasses can override this to provide specification arrays for extra |
| 188 | * tasks to run during install. |
| 189 | * |
| 190 | * @see TaskFactory::create() |
| 191 | * @stable to override |
| 192 | * |
| 193 | * @return array |
| 194 | */ |
| 195 | protected function getExtraTaskSpecs() { |
| 196 | return []; |
| 197 | } |
| 198 | |
| 199 | /** |
| 200 | * Create and configure a TaskRunner |
| 201 | * |
| 202 | * @param TaskList $taskList |
| 203 | * @param TaskFactory $taskFactory |
| 204 | * @return TaskRunner |
| 205 | */ |
| 206 | private function createTaskRunner( TaskList $taskList, TaskFactory $taskFactory ) { |
| 207 | $taskRunner = new TaskRunner( $taskList, $taskFactory, TaskFactory::PROFILE_ADD_WIKI ); |
| 208 | $skippedTasks = array_merge( $this->getOption( 'skip' ) ?? [], $this->getTaskSkips() ); |
| 209 | $taskRunner->setSkippedTasks( $skippedTasks ); |
| 210 | |
| 211 | $taskRunner->addTaskStartListener( function ( Task $task ) { |
| 212 | $name = $task->getName(); |
| 213 | $desc = $task->getDescriptionMessage()->plain(); |
| 214 | $this->output( "[$name] $desc... " ); |
| 215 | } ); |
| 216 | |
| 217 | $taskRunner->addTaskEndListener( function ( $task, StatusValue $status ) { |
| 218 | if ( $status->isOK() ) { |
| 219 | $this->output( "done\n" ); |
| 220 | } else { |
| 221 | $this->output( "\n" ); |
| 222 | } |
| 223 | if ( !$status->isGood() ) { |
| 224 | try { |
| 225 | $this->error( $status ); |
| 226 | } catch ( InvalidArgumentException ) { |
| 227 | $this->error( (string)$status ); |
| 228 | } |
| 229 | } |
| 230 | } ); |
| 231 | |
| 232 | return $taskRunner; |
| 233 | } |
| 234 | |
| 235 | /** |
| 236 | * Subclasses can override this to provide specification arrays for |
| 237 | * tasks to skip during install. |
| 238 | * |
| 239 | * @stable to override |
| 240 | * |
| 241 | * @return array<string> |
| 242 | */ |
| 243 | protected function getTaskSkips(): array { |
| 244 | return []; |
| 245 | } |
| 246 | |
| 247 | /** |
| 248 | * Get the factory used to create tasks |
| 249 | * |
| 250 | * @param ITaskContext $context |
| 251 | * @return TaskFactory |
| 252 | */ |
| 253 | private function createTaskFactory( ITaskContext $context ) { |
| 254 | return new TaskFactory( |
| 255 | $this->getServiceContainer()->getObjectFactory(), |
| 256 | $context |
| 257 | ); |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | $maintClass = InstallPreConfigured::class; |
| 262 | require_once RUN_MAINTENANCE_IF_MAIN; |