Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
36.20% |
59 / 163 |
|
11.11% |
1 / 9 |
CRAP | |
0.00% |
0 / 1 |
| CommandLineInstaller | |
36.20% |
59 / 163 |
|
11.11% |
1 / 9 |
353.18 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
53 / 53 |
|
100.00% |
1 / 1 |
1 | |||
| canExecuteWithoutLocalSettings | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| finalSetup | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| getDbType | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
| execute | |
5.33% |
4 / 75 |
|
0.00% |
0 / 1 |
262.18 | |||
| generateStrongPassword | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
| setDbPassOption | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
20 | |||
| setPassOption | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
30 | |||
| validateParamsAndArgs | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
| 1 | <?php |
| 2 | /** |
| 3 | * CLI-based MediaWiki installation and configuration. |
| 4 | * |
| 5 | * @license GPL-2.0-or-later |
| 6 | * @file |
| 7 | * @ingroup Maintenance |
| 8 | */ |
| 9 | |
| 10 | use MediaWiki\Installer\Installer; |
| 11 | use MediaWiki\Installer\InstallerOverrides; |
| 12 | use MediaWiki\Installer\InstallException; |
| 13 | use MediaWiki\Maintenance\Maintenance; |
| 14 | use MediaWiki\Settings\SettingsBuilder; |
| 15 | |
| 16 | // @codeCoverageIgnoreStart |
| 17 | require_once __DIR__ . '/Maintenance.php'; |
| 18 | |
| 19 | define( 'MW_CONFIG_CALLBACK', [ Installer::class, 'overrideConfig' ] ); |
| 20 | define( 'MEDIAWIKI_INSTALL', true ); |
| 21 | // @codeCoverageIgnoreEnd |
| 22 | |
| 23 | /** |
| 24 | * Maintenance script to install and configure MediaWiki |
| 25 | * |
| 26 | * Default values for the options are defined in MainConfigSchema.php |
| 27 | * (see the mapping in includes/installer/CliInstaller.php) |
| 28 | * Default for --dbpath (SQLite-specific) is defined in SqliteInstaller::getGlobalDefaults |
| 29 | * |
| 30 | * @ingroup Maintenance |
| 31 | */ |
| 32 | class CommandLineInstaller extends Maintenance { |
| 33 | public function __construct() { |
| 34 | parent::__construct(); |
| 35 | $this->addDescription( "CLI-based MediaWiki installation and configuration.\n" . |
| 36 | "Default options are indicated in parentheses.\n" . |
| 37 | "If no options are provided, the script will run in interactive mode." ); |
| 38 | |
| 39 | $this->addArg( 'name', 'The name of the wiki', false ); |
| 40 | $this->addArg( 'admin', 'The username of the wiki administrator.', false ); |
| 41 | |
| 42 | $this->addOption( 'pass', 'The password for the wiki administrator.', false, true ); |
| 43 | $this->addOption( |
| 44 | 'passfile', |
| 45 | 'An alternative way to provide pass option, as the contents of this file', |
| 46 | false, |
| 47 | true |
| 48 | ); |
| 49 | $this->addOption( |
| 50 | 'scriptpath', |
| 51 | 'The relative path of the wiki in the web server (/' . basename( dirname( __DIR__ ) ) . ')', |
| 52 | false, |
| 53 | true |
| 54 | ); |
| 55 | $this->addOption( |
| 56 | 'server', |
| 57 | 'The base URL of the web server the wiki will be on (http://localhost)', |
| 58 | false, |
| 59 | true |
| 60 | ); |
| 61 | |
| 62 | $this->addOption( 'lang', 'The language to use (en)', false, true ); |
| 63 | |
| 64 | $this->addOption( 'dbtype', 'The type of database (mysql)', false, true ); |
| 65 | $this->addOption( 'dbserver', 'The database host (localhost)', false, true ); |
| 66 | $this->addOption( 'dbssl', 'Connect to the database over SSL' ); |
| 67 | $this->addOption( 'dbport', 'The database port; only for PostgreSQL (5432)', false, true ); |
| 68 | $this->addOption( 'dbname', 'The database name (my_wiki)', false, true ); |
| 69 | $this->addOption( 'dbpath', 'The path for the SQLite DB (MW_INSTALL_PATH/data)', false, true ); |
| 70 | $this->addOption( 'dbprefix', 'Optional database table name prefix', false, true ); |
| 71 | $this->addOption( 'installdbuser', 'The user to use for installing (root)', false, true ); |
| 72 | $this->addOption( 'installdbpass', 'The password for the DB user to install as.', false, true ); |
| 73 | $this->addOption( 'dbuser', 'The user to use for normal operations (wikiuser)', false, true ); |
| 74 | $this->addOption( 'dbpass', 'The password for the DB user for normal operations', false, true ); |
| 75 | $this->addOption( |
| 76 | 'dbpassfile', |
| 77 | 'An alternative way to provide dbpass option, as the contents of this file', |
| 78 | false, |
| 79 | true |
| 80 | ); |
| 81 | $this->addOption( 'confpath', 'Path to write LocalSettings.php to (' . MW_INSTALL_PATH . ')', false, true ); |
| 82 | $this->addOption( 'dbschema', 'The schema for the MediaWiki DB in ' |
| 83 | . 'PostgreSQL (mediawiki)', false, true ); |
| 84 | |
| 85 | $this->addOption( 'env-checks', "Run environment checks only, don't change anything" ); |
| 86 | |
| 87 | $this->addOption( 'with-extensions', "Detect and include extensions" ); |
| 88 | $this->addOption( 'extensions', 'Comma-separated list of extensions to install', |
| 89 | false, true, false, true ); |
| 90 | $this->addOption( 'skins', 'Comma-separated list of skins to install (default: all)', |
| 91 | false, true, false, true ); |
| 92 | $this->addOption( 'with-developmentsettings', 'Load DevelopmentSettings.php in LocalSettings.php' ); |
| 93 | } |
| 94 | |
| 95 | public function canExecuteWithoutLocalSettings(): bool { |
| 96 | return true; |
| 97 | } |
| 98 | |
| 99 | public function finalSetup( SettingsBuilder $settingsBuilder ) { |
| 100 | parent::finalSetup( $settingsBuilder ); |
| 101 | Installer::overrideConfig( $settingsBuilder ); |
| 102 | } |
| 103 | |
| 104 | /** @inheritDoc */ |
| 105 | public function getDbType() { |
| 106 | if ( $this->hasOption( 'env-checks' ) ) { |
| 107 | return Maintenance::DB_NONE; |
| 108 | } |
| 109 | return parent::getDbType(); |
| 110 | } |
| 111 | |
| 112 | /** @inheritDoc */ |
| 113 | public function execute() { |
| 114 | if ( $this->hasOption( 'help' ) ) { |
| 115 | $this->maybeHelp(); |
| 116 | return; |
| 117 | } |
| 118 | |
| 119 | // Manually check for required arguments, as 0 arguments allows interactive mode to be used |
| 120 | if ( $this->getArg( 0 ) && !$this->getArg( 1 ) ) { |
| 121 | $this->fatalError( 'Argument <' . $this->getArgName( 1 ) . '> is required!' ); |
| 122 | } |
| 123 | |
| 124 | // No arguments, means interactive mode |
| 125 | if ( !$this->getArg( 0 ) || !$this->getArg( 1 ) ) { |
| 126 | $this->output( "Hello, I'm the MediaWiki installer!\n\n" ); |
| 127 | $this->output( "This script will guide you through the process of installing MediaWiki.\n" ); |
| 128 | $this->output( "If you actually want to see the help for this script, use --help.\n\n" ); |
| 129 | |
| 130 | $siteName = $this->prompt( 'Enter the name of the wiki:', "Wiki" ); |
| 131 | $language = $this->prompt( 'Enter the language code of the wiki:', 'en' ); |
| 132 | $server = $this->prompt( |
| 133 | 'Enter the base URL of the web server the wiki will be on (without a path):', |
| 134 | 'http://localhost' |
| 135 | ); |
| 136 | $adminName = $this->prompt( 'Enter the username of the MediaWiki account that will be created ' . |
| 137 | 'at the end of the installation and given administrator rights:', "Admin" ); |
| 138 | $adminPass = $this->prompt( |
| 139 | 'Enter the password for the wiki administrator:', |
| 140 | $this->generateStrongPassword() |
| 141 | ); |
| 142 | $dbType = $this->prompt( 'Enter the type of database (for example mysql or sqlite):', 'mysql' ); |
| 143 | // Assume that sqlite is the only db type that needs a path |
| 144 | $dbPath = $dbType == 'sqlite' ? |
| 145 | $this->prompt( |
| 146 | 'Enter the path for the SQLite DB (advised to be outside the web root):', |
| 147 | MW_INSTALL_PATH . '/data' |
| 148 | ) : |
| 149 | null; |
| 150 | $dbName = $this->prompt( 'Enter the name of the database:', 'my_wiki' ); |
| 151 | // Assume that everything other than sqlite needs a server and credentials |
| 152 | $dbUser = $dbType == 'sqlite' ? null : $this->prompt( 'Enter the database user:', 'wikiuser' ); |
| 153 | $dbPass = $dbType == 'sqlite' ? null : $this->prompt( 'Enter the database password:', '' ); |
| 154 | $dbServer = $dbType == 'sqlite' ? null : $this->prompt( 'Enter the database server:', 'localhost' ); |
| 155 | |
| 156 | $this->output( "\n" ); |
| 157 | $this->setArg( 0, $siteName ); |
| 158 | $this->setArg( 1, $adminName ); |
| 159 | $this->setOption( 'lang', $language ); |
| 160 | $this->setOption( 'server', $server ); |
| 161 | $this->setOption( 'pass', $adminPass ); |
| 162 | $this->setOption( 'dbtype', $dbType ); |
| 163 | $this->setOption( 'dbpath', $dbPath ); |
| 164 | $this->setOption( 'dbname', $dbName ); |
| 165 | $this->setOption( 'dbuser', $dbUser ); |
| 166 | $this->setOption( 'dbpass', $dbPass ); |
| 167 | $this->setOption( 'dbserver', $dbServer ); |
| 168 | if ( !$this->promptYesNo( 'Do you want to continue with the installation?', true ) ) { |
| 169 | $this->output( "Installation aborted.\n" ); |
| 170 | return false; |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | $siteName = $this->getArg( 0, 'MediaWiki' ); // Will not be set if used with --env-checks |
| 175 | $adminName = $this->getArg( 1 ); |
| 176 | $envChecksOnly = $this->hasOption( 'env-checks' ); |
| 177 | |
| 178 | $scriptpath = $this->getOption( 'scriptpath', false ); |
| 179 | if ( $scriptpath === false ) { |
| 180 | $this->setOption( 'scriptpath', '/' . basename( dirname( __DIR__ ) ) ); |
| 181 | } |
| 182 | |
| 183 | $this->setDbPassOption(); |
| 184 | if ( !$envChecksOnly ) { |
| 185 | $this->setPassOption(); |
| 186 | } |
| 187 | |
| 188 | try { |
| 189 | $installer = InstallerOverrides::getCliInstaller( $siteName, $adminName, $this->parameters->getOptions() ); |
| 190 | } catch ( InstallException $e ) { |
| 191 | $this->error( $e->getStatus() ); |
| 192 | return false; |
| 193 | } |
| 194 | |
| 195 | $status = $installer->doEnvironmentChecks(); |
| 196 | if ( $status->isGood() ) { |
| 197 | $installer->showMessage( 'config-env-good' ); |
| 198 | } else { |
| 199 | return false; |
| 200 | } |
| 201 | if ( !$envChecksOnly ) { |
| 202 | $status = $installer->execute(); |
| 203 | if ( !$status->isGood() ) { |
| 204 | return false; |
| 205 | } |
| 206 | $installer->writeConfigurationFile( $this->getOption( 'confpath', MW_INSTALL_PATH ) ); |
| 207 | $installer->showMessage( |
| 208 | 'config-install-success', |
| 209 | $installer->getVar( 'wgServer' ), |
| 210 | $installer->getVar( 'wgScriptPath' ) |
| 211 | ); |
| 212 | } |
| 213 | return true; |
| 214 | } |
| 215 | |
| 216 | private function generateStrongPassword(): string { |
| 217 | $strongPassword = ''; |
| 218 | $strongPasswordLength = 20; |
| 219 | $strongPasswordChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-={}|;:,.<>?'; |
| 220 | $strongPasswordCharsLength = strlen( $strongPasswordChars ); |
| 221 | for ( $i = 0; $i < $strongPasswordLength; $i++ ) { |
| 222 | $strongPassword .= $strongPasswordChars[ rand( 0, $strongPasswordCharsLength - 1 ) ]; |
| 223 | } |
| 224 | return $strongPassword; |
| 225 | } |
| 226 | |
| 227 | private function setDbPassOption() { |
| 228 | $dbpassfile = $this->getOption( 'dbpassfile' ); |
| 229 | if ( $dbpassfile !== null ) { |
| 230 | if ( $this->getOption( 'dbpass' ) !== null ) { |
| 231 | $this->error( 'WARNING: You have provided the options "dbpass" and "dbpassfile". ' |
| 232 | . 'The content of "dbpassfile" overrides "dbpass".' ); |
| 233 | } |
| 234 | // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged |
| 235 | $dbpass = @file_get_contents( $dbpassfile ); // returns false on failure |
| 236 | if ( $dbpass === false ) { |
| 237 | $this->fatalError( "Couldn't open $dbpassfile" ); |
| 238 | } |
| 239 | $this->setOption( 'dbpass', trim( $dbpass, "\r\n" ) ); |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | private function setPassOption() { |
| 244 | $passfile = $this->getOption( 'passfile' ); |
| 245 | if ( $passfile !== null ) { |
| 246 | if ( $this->getOption( 'pass' ) !== null ) { |
| 247 | $this->error( 'WARNING: You have provided the option --pass or --passfile. ' |
| 248 | . 'The content of "passfile" overrides "pass".' ); |
| 249 | } |
| 250 | // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged |
| 251 | $pass = @file_get_contents( $passfile ); // returns false on failure |
| 252 | if ( $pass === false ) { |
| 253 | $this->fatalError( "Couldn't open $passfile" ); |
| 254 | } |
| 255 | $this->setOption( 'pass', trim( $pass, "\r\n" ) ); |
| 256 | } elseif ( $this->getOption( 'pass' ) === null ) { |
| 257 | $this->fatalError( 'You need to provide the option "pass" or "passfile"' ); |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | public function validateParamsAndArgs() { |
| 262 | if ( !$this->hasOption( 'env-checks' ) ) { |
| 263 | $this->parameters->validate(); |
| 264 | } |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | // @codeCoverageIgnoreStart |
| 269 | $maintClass = CommandLineInstaller::class; |
| 270 | |
| 271 | require_once RUN_MAINTENANCE_IF_MAIN; |
| 272 | // @codeCoverageIgnoreEnd |