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 | /** @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 | $taskRunner->setSkippedTasks( $this->getOption( 'skip' ) ?? [] ); |
209 | |
210 | $taskRunner->addTaskStartListener( function ( Task $task ) { |
211 | $name = $task->getName(); |
212 | $desc = $task->getDescriptionMessage()->plain(); |
213 | $this->output( "[$name] $desc... " ); |
214 | } ); |
215 | |
216 | $taskRunner->addTaskEndListener( function ( $task, StatusValue $status ) { |
217 | if ( $status->isOK() ) { |
218 | $this->output( "done\n" ); |
219 | } else { |
220 | $this->output( "\n" ); |
221 | } |
222 | if ( !$status->isGood() ) { |
223 | try { |
224 | $this->error( $status ); |
225 | } catch ( InvalidArgumentException ) { |
226 | $this->error( (string)$status ); |
227 | } |
228 | } |
229 | } ); |
230 | |
231 | return $taskRunner; |
232 | } |
233 | |
234 | /** |
235 | * Get the factory used to create tasks |
236 | * |
237 | * @param ITaskContext $context |
238 | * @return TaskFactory |
239 | */ |
240 | private function createTaskFactory( ITaskContext $context ) { |
241 | return new TaskFactory( |
242 | $this->getServiceContainer()->getObjectFactory(), |
243 | $context |
244 | ); |
245 | } |
246 | } |
247 | |
248 | $maintClass = InstallPreConfigured::class; |
249 | require_once RUN_MAINTENANCE_IF_MAIN; |