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