12
12
'SYNOPSIS ' ,
13
13
<<<SYNOPSIS
14
14
Updates Magento with 2.3 requirements that can't be done by `composer update` or `bin/magento setup:upgrade`.
15
- This should be run prior to running `composer update` or `bin/magento setup:upgrade`.
15
+ Run this script after upgrading to PHP 7.1/7.2 and before running `composer update` or `bin/magento setup:upgrade`.
16
16
17
17
Steps included:
18
18
- Require new version of the metapackage
19
19
- Update "require-dev" section
20
20
- Add "Zend\\Mvc\\Controller\\": "setup/src/Zend/Mvc/Controller/" to composer.json "autoload":"psr-4" section
21
21
- Update Magento/Updater if it's installed
22
- - Update root version label
22
+ - Update name, version, and description fields in the root composer.json
23
23
24
24
Usage: php -f $ _scriptName -- --root='</path/to/magento/root/>' [--composer='</path/to/composer/executable>']
25
25
[--edition='<community|enterprise>'] [--repo='<composer_repo_url>'] [--version='<version_constraint>']
32
32
Optional:
33
33
--composer='</path/to/composer/executable>'
34
34
Path to the composer executable
35
- - Default: The composer found in PATH
35
+ - Default: The composer found in the system PATH
36
36
37
37
--edition='<community|enterprise>'
38
38
Target Magento edition for the update. Open Source = 'community', Commerce = 'enterprise'
72
72
}
73
73
74
74
try {
75
+ if (version_compare (PHP_VERSION , '7.1 ' , '< ' ) || version_compare (PHP_VERSION , '7.3 ' , '>= ' )) {
76
+ preg_match ('/^\d+\.\d+\.\d+/ ' ,PHP_VERSION , $ matches );
77
+ $ phpVersion = $ matches [0 ];
78
+ throw new Exception ("Invalid PHP version ' $ phpVersion'. Magento 2.3 requires PHP 7.1 or 7.2 " );
79
+ }
80
+
75
81
/**** Populate and Validate Settings ****/
76
82
77
- if (empty ($ opts ['root ' ])) {
78
- throw new BadMethodCallException ('Magento root must be supplied with --root ' );
83
+ if (empty ($ opts ['root ' ]) || ! is_dir ( $ opts [ ' root ' ]) ) {
84
+ throw new BadMethodCallException ('Existing Magento root directory must be supplied with --root ' );
79
85
}
80
-
81
86
$ rootDir = $ opts ['root ' ];
82
- if (!is_dir ($ rootDir )) {
83
- throw new InvalidArgumentException ("Supplied Magento root directory ' $ rootDir' does not exist " );
84
- }
85
-
86
- $ tempDir = findUnusedFilename ($ rootDir , "temp_project " );
87
87
88
- // The composer command uses the Magento root as the working directory so this script can be run from anywhere
89
- $ cmd = (!empty ($ opts ['composer ' ]) ? $ opts ['composer ' ] : 'composer ' ) . " --working-dir=' $ rootDir' " ;
88
+ $ composerFile = "$ rootDir/composer.json " ;
89
+ if (!file_exists ($ composerFile )) {
90
+ throw new InvalidArgumentException ("Supplied Magento root directory ' $ rootDir' does not contain composer.json " );
91
+ }
90
92
91
- // Set the version constraint to any 2.3 package if not specified
92
- $ constraint = !empty ($ opts ['version ' ]) ? $ opts ['version ' ] : '2.3.* ' ;
93
+ $ composerData = json_decode (file_get_contents ($ composerFile ), true );
93
94
94
- // Grab the root composer.json contents to pull defaults or other data from when necessary
95
- $ composer = json_decode (file_get_contents ("$ rootDir/composer.json " ), true );
95
+ $ metapackageMatcher = '/^magento\/product\-(?<edition>community|enterprise)\-edition$/ ' ;
96
+ foreach (array_keys ($ composerData ['require ' ]) as $ requiredPackage ) {
97
+ if (preg_match ($ metapackageMatcher , $ requiredPackage , $ matches )) {
98
+ $ edition = $ matches ['edition ' ];
99
+ break ;
100
+ }
101
+ }
102
+ if (empty ($ edition )) {
103
+ throw new InvalidArgumentException ("No Magento metapackage found in $ composerFile " );
104
+ }
96
105
97
- // Get the target Magento edition
106
+ // Override composer.json edition if one is passed to the script
98
107
if (!empty ($ opts ['edition ' ])) {
99
108
$ edition = $ opts ['edition ' ];
100
109
}
101
- else {
102
- $ metapackageMatcher = '|^magento/product\-(?<edition>[a-z]+)\-edition$| ' ;
103
-
104
- foreach (array_keys ($ composer ['require ' ]) as $ requiredPackage ) {
105
- if (preg_match ($ metapackageMatcher , $ requiredPackage , $ matches )) {
106
- $ edition = $ matches ['edition ' ];
107
- break ;
108
- }
109
- }
110
- if (empty ($ edition )) {
111
- throw new InvalidArgumentException ('No Magento metapackage found in composer.json requirements ' );
112
- }
113
- }
114
110
$ edition = strtolower ($ edition );
115
111
116
112
if ($ edition !== 'community ' && $ edition !== 'enterprise ' ) {
117
113
throw new InvalidArgumentException ("Only 'community' and 'enterprise' editions allowed; ' $ edition' given " );
118
114
}
119
115
116
+ $ composerExec = (!empty ($ opts ['composer ' ]) ? $ opts ['composer ' ] : 'composer ' );
117
+ if (basename ($ composerExec , '.phar ' ) != 'composer ' ) {
118
+ throw new InvalidArgumentException ("' $ composerExec' is not a composer executable " );
119
+ }
120
+
121
+ // Use 'command -v' to check if composer is executable
122
+ exec ("command -v $ composerExec " , $ out , $ composerFailed );
123
+ if ($ composerFailed ) {
124
+ if ($ composerExec == 'composer ' ) {
125
+ $ message = 'Composer executable is not available in the system PATH ' ;
126
+ }
127
+ else {
128
+ $ message = "Invalid composer executable ' $ composerExec' " ;
129
+ }
130
+ throw new InvalidArgumentException ($ message );
131
+ }
132
+
133
+ // The composer command uses the Magento root as the working directory so this script can be run from anywhere
134
+ $ composerExec = "$ composerExec --working-dir=' $ rootDir' " ;
135
+
136
+ // Set the version constraint to any 2.3 package if not specified
137
+ $ constraint = !empty ($ opts ['version ' ]) ? $ opts ['version ' ] : '2.3.* ' ;
138
+
120
139
// Composer package names
121
140
$ project = "magento/project- $ edition-edition " ;
122
141
$ metapackage = "magento/product- $ edition-edition " ;
123
142
124
143
// Get the list of potential Magento repositories to search for the update package
125
- $ repoUrls = array_map (function ($ r ) { return $ r ['url ' ]; }, $ composer ['repositories ' ]);
126
144
$ mageUrls = [];
145
+ $ authFailed = [];
127
146
if (!empty ($ opts ['repo ' ])) {
128
147
$ mageUrls [] = $ opts ['repo ' ];
129
148
}
130
149
else {
131
- $ mageUrls = array_filter ($ repoUrls , function ($ u ) { return strpos ($ u , '.mage ' ) !== false ; });
150
+ foreach ($ composerData ['repositories ' ] as $ label => $ repo ) {
151
+ if (strpos (strtolower ($ label ), 'mage ' ) !== false || strpos ($ repo ['url ' ], '.mage ' ) !== false ) {
152
+ $ mageUrls [] = $ repo ['url ' ];
153
+ }
154
+ }
132
155
133
156
if (count ($ mageUrls ) == 0 ) {
134
157
throw new InvalidArgumentException ('No Magento repository urls found in composer.json ' );
135
158
}
136
159
}
137
160
161
+ $ tempDir = findUnusedFilename ($ rootDir , 'temp_project ' );
138
162
$ projectConstraint = "$ project=' $ constraint' " ;
139
163
$ version = null ;
140
164
$ description = null ;
141
- $ versionValidator = '/^2\.3\.\d/ ' ;
165
+
166
+ output ("**** Searching for a matching version of $ project **** " );
142
167
143
168
// Try to retrieve a 2.3 package from each Magento repository until one is found
144
169
foreach ($ mageUrls as $ repoUrl ) {
145
170
try {
146
- output ("Checking $ repoUrl for a matching version of $ project " );
171
+ output ("\\ nChecking $ repoUrl " );
147
172
deleteFilepath ($ tempDir );
148
173
runComposer ("create-project --repository= $ repoUrl $ projectConstraint $ tempDir --no-install " );
149
174
152
177
$ version = $ newComposer ['version ' ];
153
178
$ description = $ newComposer ['description ' ];
154
179
155
- if (! preg_match ( $ versionValidator , $ version ) ) {
180
+ if (strpos ( $ version , ' 2.3. ' ) !== 0 ) {
156
181
throw new InvalidArgumentException ("Bad 2.3 version constraint ' $ constraint'; version $ version found " );
157
182
}
158
183
159
- // If no errors occur , set this as the correct repo, forget errors from previous repos, and move forward
160
- output ("Found $ project version $ version " );
184
+ // If no errors occurred , set this as the correct repo, forget errors from previous repos, and move forward
185
+ output ("\\ n**** Found compatible $ project version: $ version **** " );
161
186
$ repo = $ repoUrl ;
162
187
unset($ exception );
163
188
break ;
174
199
throw $ exception ;
175
200
}
176
201
177
- / **** Execute Updates ****/
202
+ output ( "\\ n **** Executing Updates ****" );
178
203
179
- $ composerBackup = findUnusedFilename ($ rootDir , " composer.json.bak " );
180
- output ("Backing up $ rootDir /composer.json to $ composerBackup " );
181
- copy (" $ rootDir /composer.json " , $ composerBackup );
204
+ $ composerBackup = findUnusedFilename ($ rootDir , ' composer.json.bak ' );
205
+ output ("\\ nBacking up $ composerFile to $ composerBackup " );
206
+ copy ($ composerFile , $ composerBackup );
182
207
183
208
// Add the repository to composer.json if needed without overwriting any existing ones
209
+ $ repoUrls = array_map (function ($ r ) { return $ r ['url ' ]; }, $ composerData ['repositories ' ]);
184
210
if (!in_array ($ repo , $ repoUrls )) {
185
- $ repoLabels = array_map ('strtolower ' ,array_keys ($ composer ['repositories ' ]));
211
+ $ repoLabels = array_map ('strtolower ' ,array_keys ($ composerData ['repositories ' ]));
186
212
$ newLabel = 'magento ' ;
187
213
if (in_array ($ newLabel , $ repoLabels )) {
188
214
$ count = count ($ repoLabels );
193
219
}
194
220
}
195
221
}
196
- output ("Adding $ repo to composer repositories under label ' $ newLabel' " );
222
+ output ("\\ nAdding $ repo to composer repositories under label ' $ newLabel' " );
197
223
runComposer ("config repositories. $ newLabel composer $ repo " );
198
224
}
199
225
200
- output ("Updating Magento metapackage requirement to $ metapackage= $ version " );
226
+ output ("\\ nUpdating Magento metapackage requirement to $ metapackage= $ version " );
201
227
if ($ edition == 'enterprise ' ) {
202
228
// Community -> Enterprise upgrades need to remove the community edition metapackage
203
229
runComposer ('remove magento/product-community-edition --no-update ' );
230
+ output ('' );
204
231
}
205
232
runComposer ("require $ metapackage= $ version --no-update " );
206
233
207
- output ('Updating "require-dev" section of composer.json ' );
234
+ output ('\nUpdating "require-dev" section of composer.json ' );
208
235
runComposer ('require --dev ' .
209
236
'phpunit/phpunit:~6.2.0 ' .
210
237
'friendsofphp/php-cs-fixer:~2.10.1 ' .
211
238
'lusitanian/oauth:~0.8.10 ' .
212
239
'pdepend/pdepend:2.5.2 ' .
213
240
'sebastian/phpcpd:~3.0.0 ' .
214
241
'squizlabs/php_codesniffer:3.2.2 --no-update ' );
242
+ output ('' );
215
243
runComposer ('remove --dev sjparkinson/static-review fabpot/php-cs-fixer --no-update ' );
216
244
217
- output ('Adding "Zend \\\\Mvc \\\\Controller \\\\": "setup/src/Zend/Mvc/Controller/" to "autoload": "psr-4" ' );
218
- $ composer ['autoload ' ]['psr-4 ' ]['Zend \\Mvc \\Controller \\' ] = 'setup/src/Zend/Mvc/Controller/ ' ;
219
-
220
- output ('Updating root version label from ' . $ composer ['version ' ] . " to $ version " );
221
- $ composer ['version ' ] = $ version ;
245
+ output ('\nAdding "Zend \\\\Mvc \\\\Controller \\\\": "setup/src/Zend/Mvc/Controller/" to "autoload": "psr-4" ' );
246
+ $ composerData ['autoload ' ]['psr-4 ' ]['Zend \\Mvc \\Controller \\' ] = 'setup/src/Zend/Mvc/Controller/ ' ;
222
247
223
- if ($ composer ['name ' ] !== $ project ) {
224
- output ('Updating root project name and description from ' . $ composer ['name ' ] . " to $ project " );
225
- $ composer ['name ' ] = $ project ;
226
- $ composer ['description ' ] = $ description ;
248
+ if (preg_match ('/^magento\/project\-(community|enterprise)\-edition$/ ' , $ composerData ['name ' ])) {
249
+ output ('\nUpdating project name, version, and description ' );
250
+ $ composerData ['name ' ] = $ project ;
251
+ $ composerData ['version ' ] = $ version ;
252
+ $ composerData ['description ' ] = $ description ;
227
253
}
228
254
229
- file_put_contents (" $ rootDir /composer.json " , json_encode ($ composer , JSON_UNESCAPED_SLASHES |JSON_PRETTY_PRINT ));
255
+ file_put_contents ($ composerFile , json_encode ($ composerData , JSON_UNESCAPED_SLASHES |JSON_PRETTY_PRINT ));
230
256
231
257
// Update Magento/Updater if it's installed
232
- if (file_exists ("$ rootDir/update " )) {
233
- $ updateBackup = findUnusedFilename ($ rootDir , "update.bak " );
234
- output ("Backing up Magento/Updater directory $ rootDir/update to $ updateBackup " );
235
- rename ("$ rootDir/update " , $ updateBackup );
236
- output ('Updating Magento/Updater ' );
237
- rename ("$ tempDir/update " , "$ rootDir/update " );
258
+ $ updateDir = "$ rootDir/update " ;
259
+ if (file_exists ($ updateDir )) {
260
+ $ updateBackup = findUnusedFilename ($ rootDir , 'update.bak ' );
261
+ output ("\\nBacking up Magento/Updater directory $ updateDir to $ updateBackup " );
262
+ rename ($ updateDir , $ updateBackup );
263
+ output ('\nUpdating Magento/Updater ' );
264
+ rename ("$ tempDir/update " , $ updateDir );
238
265
}
239
266
240
267
// Remove temp project directory that was used for repo/version validation and new source for Magento/Updater
241
268
deleteFilepath ($ tempDir );
242
269
243
- output ('\n**** Script Complete! **** ' );
270
+ output ("\\n**** Script Complete! $ composerFile updated to Magento version $ version **** " );
271
+ if (count ($ authFailed ) > 0 ) {
272
+ output ('Repository authentication failures occurred! ' , WARN );
273
+ output (' * Failed authentication could result in incorrect package versions ' , WARN );
274
+ output (' * To resolve, add credentials for the repositories to auth.json ' , WARN );
275
+ output (' * URL(s) failing authentication: ' . join (', ' , array_keys ($ authFailed )), WARN );
276
+ }
244
277
} catch (Exception $ e ) {
245
278
if ($ e ->getPrevious ()) {
246
- $ message = (string )$ e ->getPrevious ();
247
- } else {
248
- $ message = $ e ->getMessage ();
279
+ $ e = $ e ->getPrevious ();
249
280
}
250
281
251
282
try {
252
- output ($ message . '\n\nScript failed! See usage information with --help ' , ERROR );
283
+ output ($ e ->getMessage (), ERROR , get_class ($ e ));
284
+ output ('Script failed! See usage information with --help ' , ERROR );
253
285
254
286
if (isset ($ composerBackup ) && file_exists ($ composerBackup )) {
255
- output (' Resetting composer.json backup ' );
256
- deleteFilepath (" $ rootDir /composer.json " );
257
- rename ($ composerBackup , " $ rootDir /composer.json " );
287
+ output (" Resetting $ composerFile backup " );
288
+ deleteFilepath ($ composerFile );
289
+ rename ($ composerBackup , $ composerFile );
258
290
}
259
291
if (isset ($ updateBackup ) && file_exists ($ updateBackup )) {
260
- output (' Resetting Magento/Updater backup ' );
261
- deleteFilepath (" $ rootDir /update " );
262
- rename ($ updateBackup , " $ rootDir /update " );
292
+ output (" Resetting $ updateDir backup " );
293
+ deleteFilepath ($ updateDir );
294
+ rename ($ updateBackup , $ updateDir );
263
295
}
264
296
if (isset ($ tempDir ) && file_exists ($ tempDir )) {
265
297
output ('Removing temporary project directory ' );
266
298
deleteFilepath ($ tempDir );
267
299
}
268
300
}
269
301
catch (Exception $ e2 ) {
270
- output ($ e2 ->getMessage () . '\n\nBackup restoration/directory cleanup failed ' , ERROR );
302
+ output ($ e2 ->getMessage (), ERROR , get_class ($ e2 ));
303
+ output ('Backup restoration or directory cleanup failed ' , ERROR );
271
304
}
272
305
273
306
exit ($ e ->getCode () == 0 ? 1 : $ e ->getCode ());
@@ -290,28 +323,36 @@ function findUnusedFilename($dir, $filename) {
290
323
}
291
324
292
325
/**
293
- * Execute a composer command and output the results
326
+ * Execute a composer command, reload $composerData afterwards, and check for repo authentication warnings
294
327
*
295
328
* @param string $command
296
329
* @return array Command output split by lines
297
330
* @throws RuntimeException
298
331
*/
299
332
function runComposer ($ command )
300
333
{
301
- global $ cmd , $ composer , $ rootDir ;
302
- $ command = "$ cmd $ command " ;
303
- output (' Running command: \ n ' . $ command );
334
+ global $ composerExec , $ composerData , $ composerFile , $ authFailed ;
335
+ $ command = "$ composerExec $ command --no-interaction " ;
336
+ output (" Running command: \\ n $ command" );
304
337
exec ("$ command 2>&1 " , $ lines , $ exitCode );
305
- $ output = join (PHP_EOL , $ lines );
338
+ $ output = ' ' . join (' \n ' , $ lines );
306
339
307
340
// Reload composer object from the updated composer.json
308
- $ composer = json_decode (file_get_contents (" $ rootDir /composer.json " ), true );
341
+ $ composerData = json_decode (file_get_contents ($ composerFile ), true );
309
342
310
343
if (0 !== $ exitCode ) {
311
- $ output = ' Error encountered running command: ' . PHP_EOL . " $ command" . PHP_EOL . $ output ;
344
+ $ output = " Error encountered running command: \\ n $ command\\ n $ output" ;
312
345
throw new RuntimeException ($ output , $ exitCode );
313
346
}
314
347
output ($ output );
348
+
349
+ if (strpos ($ output , 'URL required authentication. ' ) !== false ) {
350
+ preg_match ("/'(https?:\/\/)?(?<url>[^\/']+)(\/[^']*)?' URL required authentication/ " , $ output , $ matches );
351
+ $ authUrl = $ matches ['url ' ];
352
+ $ authFailed [$ authUrl ] = 1 ;
353
+ output ("Repository authentication failed; make sure ' $ authUrl' exists in auth.json " , WARN );
354
+ }
355
+
315
356
return $ lines ;
316
357
}
317
358
@@ -345,17 +386,29 @@ function deleteFilepath($path) {
345
386
*
346
387
* @param string $string Text to log
347
388
* @param int $level One of INFO, WARN, or ERROR
389
+ * @param string $label Optional message label; defaults to WARNING for $level = WARN and ERROR for $level = ERROR
348
390
*/
349
- function output ($ string , $ level = INFO ) {
350
- $ string = str_replace ('\n ' , PHP_EOL , $ string ) . PHP_EOL ;
391
+ function output ($ string , $ level = INFO , $ label = '' ) {
392
+ $ string = str_replace ('\n ' , PHP_EOL , $ string );
393
+
394
+ if (!empty ($ label )) {
395
+ $ label = "$ label: " ;
396
+ }
397
+ else if ($ level == WARN ) {
398
+ $ label = 'WARNING: ' ;
399
+ }
400
+ else if ($ level == ERROR ) {
401
+ $ label = 'ERROR: ' ;
402
+ }
403
+ $ string = "$ label$ string " ;
351
404
352
405
if ($ level == WARN ) {
353
- error_log (" WARNING: $ string" );
406
+ error_log ($ string );
354
407
}
355
408
elseif ($ level == ERROR ) {
356
- error_log (PHP_EOL . " ERROR: $ string" );
409
+ error_log (PHP_EOL . $ string );
357
410
}
358
411
else {
359
- echo $ string ;
412
+ echo $ string . PHP_EOL ;
360
413
}
361
414
}
0 commit comments