Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 95 |
|
0.00% |
0 / 8 |
CRAP | |
0.00% |
0 / 1 |
InstallPreConfigured | |
0.00% |
0 / 92 |
|
0.00% |
0 / 8 |
342 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
2 | |||
finalSetup | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
execute | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
20 | |||
parseKeyValue | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
createTaskContext | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
6 | |||
createTaskList | |
0.00% |
0 / 17 |
|
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\MainConfigNames; |
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 | class InstallPreConfigured extends Maintenance { |
19 | public function __construct() { |
20 | parent::__construct(); |
21 | $this->addDescription( 'Create the database and tables for a new wiki, ' . |
22 | 'using a pre-existing LocalSettings.php' ); |
23 | $this->addOption( 'task', |
24 | 'Execute only the specified task', false, true ); |
25 | $this->addOption( 'skip', |
26 | 'Skip the specified task', false, true, false, true ); |
27 | $this->addOption( 'override-config', |
28 | 'Specify a configuration variable with name=value. The value is in YAML format.', |
29 | false, true, 'c', true ); |
30 | $this->addOption( 'override-option', |
31 | 'Specify an installer option with name=value. The value is in YAML format.', |
32 | false, true, 'o', true ); |
33 | $this->addOption( 'show-tasks', |
34 | 'Show the list of tasks to be executed, do not actually install' ); |
35 | } |
36 | |
37 | public function finalSetup( SettingsBuilder $settingsBuilder ) { |
38 | parent::finalSetup( $settingsBuilder ); |
39 | |
40 | // Load installer i18n |
41 | $settingsBuilder->putConfigValue( MainConfigNames::MessagesDirs, |
42 | [ MW_INSTALL_PATH . '/includes/installer/i18n' ] ); |
43 | |
44 | // Apply override-config options. Doing this here instead of in |
45 | // AddWikiTaskContext::setConfigVar() allows the options to be available |
46 | // globally for use in LoadExtensionSchemaUpdates. |
47 | foreach ( $this->getOption( 'override-config' ) ?? [] as $str ) { |
48 | [ $name, $value ] = $this->parseKeyValue( $str ); |
49 | if ( str_starts_with( $name, 'wg' ) ) { |
50 | $name = substr( $name, 2 ); |
51 | } |
52 | $settingsBuilder->putConfigValue( $name, $value ); |
53 | } |
54 | } |
55 | |
56 | public function execute() { |
57 | $context = $this->createTaskContext(); |
58 | $taskFactory = $this->createTaskFactory( $context ); |
59 | $taskList = $this->createTaskList( $taskFactory ); |
60 | $taskRunner = $this->createTaskRunner( $taskList, $taskFactory ); |
61 | |
62 | if ( $this->hasOption( 'show-tasks' ) ) { |
63 | $taskRunner->loadExtensions(); |
64 | echo $taskRunner->dumpTaskList(); |
65 | return true; |
66 | } |
67 | |
68 | Installer::disableStorage( $this->getConfig(), 'en' ); |
69 | if ( $this->hasOption( 'task' ) ) { |
70 | $status = $taskRunner->runNamedTask( $this->getOption( 'task' ) ); |
71 | } else { |
72 | $status = $taskRunner->execute(); |
73 | } |
74 | |
75 | if ( $status->isOK() ) { |
76 | return true; |
77 | } else { |
78 | $this->error( "Installation failed at task \"" . |
79 | $taskRunner->getCurrentTaskName() . '"' ); |
80 | return false; |
81 | } |
82 | } |
83 | |
84 | /** |
85 | * Split a string into a key and a value, and parse the value as YAML. |
86 | * |
87 | * @param string $str |
88 | * @return array A list where the first element is the name, and the |
89 | * second is the decoded value |
90 | */ |
91 | private function parseKeyValue( string $str ) { |
92 | $parts = explode( '=', $str, 2 ); |
93 | if ( count( $parts ) !== 2 ) { |
94 | $this->fatalError( "Invalid configuration variable \"$str\"" ); |
95 | } |
96 | return [ $parts[0], Yaml::parse( $parts[1], Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE ) ]; |
97 | } |
98 | |
99 | /** |
100 | * Get the context for running tasks, with config overrides from the |
101 | * command line. |
102 | * |
103 | * @return AddWikiTaskContext |
104 | */ |
105 | private function createTaskContext() { |
106 | $context = new AddWikiTaskContext( |
107 | $this->getConfig(), |
108 | $this->getServiceContainer()->getDBLoadBalancerFactory() |
109 | ); |
110 | |
111 | // Make sure ExtensionTablesTask isn't skipped |
112 | $context->setOption( 'Extensions', |
113 | array_keys( ExtensionRegistry::getInstance()->getAllThings() ) ); |
114 | |
115 | foreach ( $this->getOption( 'override-option' ) ?? [] as $str ) { |
116 | [ $name, $value ] = $this->parseKeyValue( $str ); |
117 | $context->setOption( $name, $value ); |
118 | } |
119 | return $context; |
120 | } |
121 | |
122 | /** |
123 | * Get the full list of tasks, before skipping is applied. |
124 | * |
125 | * @param TaskFactory $taskFactory |
126 | * @return TaskList |
127 | */ |
128 | private function createTaskList( TaskFactory $taskFactory ) { |
129 | $taskList = new TaskList; |
130 | $taskFactory->registerMainTasks( $taskList, TaskFactory::PROFILE_ADD_WIKI ); |
131 | $reg = ExtensionRegistry::getInstance(); |
132 | $taskList->add( $taskFactory->create( |
133 | [ |
134 | 'class' => CannedProvider::class, |
135 | 'args' => [ |
136 | 'extensions', |
137 | [ |
138 | 'HookContainer' => $this->getHookContainer(), |
139 | 'VirtualDomains' => $reg->getAttribute( 'DatabaseVirtualDomains' ), |
140 | 'ExtensionTaskSpecs' => $reg->getAttribute( 'InstallerTasks' ), |
141 | ] |
142 | ] |
143 | ] |
144 | ) ); |
145 | return $taskList; |
146 | } |
147 | |
148 | /** |
149 | * Create and configure a TaskRunner |
150 | * |
151 | * @param TaskList $taskList |
152 | * @param TaskFactory $taskFactory |
153 | * @return TaskRunner |
154 | */ |
155 | private function createTaskRunner( TaskList $taskList, TaskFactory $taskFactory ) { |
156 | $taskRunner = new TaskRunner( $taskList, $taskFactory, TaskFactory::PROFILE_ADD_WIKI ); |
157 | $taskRunner->setSkippedTasks( $this->getOption( 'skip' ) ?? [] ); |
158 | |
159 | $taskRunner->addTaskStartListener( function ( Task $task ) { |
160 | $name = $task->getName(); |
161 | $desc = $task->getDescriptionMessage()->plain(); |
162 | $this->output( "[$name] $desc... " ); |
163 | } ); |
164 | |
165 | $taskRunner->addTaskEndListener( function ( $task, StatusValue $status ) { |
166 | if ( $status->isOK() ) { |
167 | $this->output( "done\n" ); |
168 | } else { |
169 | $this->output( "\n" ); |
170 | } |
171 | if ( !$status->isGood() ) { |
172 | try { |
173 | $this->error( $status ); |
174 | } catch ( InvalidArgumentException $e ) { |
175 | $this->error( (string)$status ); |
176 | } |
177 | } |
178 | } ); |
179 | |
180 | return $taskRunner; |
181 | } |
182 | |
183 | /** |
184 | * Get the factory used to create tasks |
185 | * |
186 | * @param ITaskContext $context |
187 | * @return TaskFactory |
188 | */ |
189 | private function createTaskFactory( ITaskContext $context ) { |
190 | return new TaskFactory( |
191 | $this->getServiceContainer()->getObjectFactory(), |
192 | $context |
193 | ); |
194 | } |
195 | } |
196 | |
197 | $maintClass = InstallPreConfigured::class; |
198 | require_once RUN_MAINTENANCE_IF_MAIN; |