@@ -49,11 +49,74 @@ public function setHttpDownloader(HttpDownloader $httpDownloader): void
49
49
}
50
50
51
51
/**
52
+ * Runs a set of commands using the $url or a variation of it (with auth, ssh, ..)
53
+ *
54
+ * Commands should use %url% placeholders for the URL instead of inlining it to allow this function to do its job
55
+ * %sanitizedUrl% is also automatically replaced by the url without user/pass
56
+ *
57
+ * As soon as a single command fails it will halt, so assume the commands are run as && in bash
58
+ *
59
+ * @param non-empty-array<non-empty-list<string>> $commands
60
+ * @param mixed $commandOutput the output will be written into this var if passed by ref
61
+ * if a callable is passed it will be used as output handler
62
+ */
63
+ public function runCommands (array $ commands , string $ url , ?string $ cwd , bool $ initialClone = false , &$ commandOutput = null ): void
64
+ {
65
+ $ callables = [];
66
+ foreach ($ commands as $ cmd ) {
67
+ $ callables [] = static function (string $ url ) use ($ cmd ): array {
68
+ $ map = [
69
+ '%url% ' => $ url ,
70
+ '%sanitizedUrl% ' => Preg::replace ('{://([^@]+?):(.+?)@} ' , ':// ' , $ url ),
71
+ ];
72
+
73
+ return array_map (static function ($ value ) use ($ map ): string {
74
+ return $ map [$ value ] ?? $ value ;
75
+ }, $ cmd );
76
+ };
77
+ }
78
+
79
+ // @phpstan-ignore method.deprecated
80
+ $ this ->runCommand ($ callables , $ url , $ cwd , $ initialClone , $ commandOutput );
81
+ }
82
+
83
+ /**
84
+ * @param callable|array<callable> $commandCallable
52
85
* @param mixed $commandOutput the output will be written into this var if passed by ref
53
86
* if a callable is passed it will be used as output handler
87
+ * @deprecated Use runCommands with placeholders instead of callbacks for simplicity
54
88
*/
55
- public function runCommand (callable $ commandCallable , string $ url , ?string $ cwd , bool $ initialClone = false , &$ commandOutput = null ): void
89
+ public function runCommand ($ commandCallable , string $ url , ?string $ cwd , bool $ initialClone = false , &$ commandOutput = null ): void
56
90
{
91
+ $ commandCallables = is_callable ($ commandCallable ) ? [$ commandCallable ] : $ commandCallable ;
92
+ $ lastCommand = '' ;
93
+
94
+ $ runCommands = function ($ url ) use ($ commandCallables , $ cwd , &$ commandOutput , &$ lastCommand ) {
95
+ $ collectOutputs = !is_callable ($ commandOutput );
96
+ $ outputs = [];
97
+
98
+ $ status = 0 ;
99
+ foreach ($ commandCallables as $ callable ) {
100
+ $ lastCommand = $ callable ($ url );
101
+ if ($ collectOutputs ) {
102
+ $ outputs [] = '' ;
103
+ $ output = &$ outputs [count ($ outputs ) - 1 ];
104
+ } else {
105
+ $ output = &$ commandOutput ;
106
+ }
107
+ $ status = $ this ->process ->execute ($ lastCommand , $ output , $ cwd );
108
+ if ($ status !== 0 ) {
109
+ break ;
110
+ }
111
+ }
112
+
113
+ if ($ collectOutputs ) {
114
+ $ commandOutput = implode ('' , $ outputs );
115
+ }
116
+
117
+ return $ status ;
118
+ };
119
+
57
120
// Ensure we are allowed to use this URL by config
58
121
$ this ->config ->prohibitUrlByConfig ($ url , $ this ->io );
59
122
@@ -86,7 +149,7 @@ public function runCommand(callable $commandCallable, string $url, ?string $cwd,
86
149
$ protoUrl = $ protocol . ":// " . $ match [1 ] . "/ " . $ match [2 ];
87
150
}
88
151
89
- if (0 === $ this -> process -> execute ( $ commandCallable ( $ protoUrl), $ commandOutput , $ cwd )) {
152
+ if (0 === $ runCommands ( $ protoUrl )) {
90
153
return ;
91
154
}
92
155
$ messages [] = '- ' . $ protoUrl . "\n" . Preg::replace ('#^#m ' , ' ' , $ this ->process ->getErrorOutput ());
@@ -105,11 +168,9 @@ public function runCommand(callable $commandCallable, string $url, ?string $cwd,
105
168
// if we have a private github url and the ssh protocol is disabled then we skip it and directly fallback to https
106
169
$ bypassSshForGitHub = Preg::isMatch ('{^git@ ' . self ::getGitHubDomainsRegex ($ this ->config ) . ':(.+?)\.git$}i ' , $ url ) && !in_array ('ssh ' , $ protocols , true );
107
170
108
- $ command = $ commandCallable ($ url );
109
-
110
171
$ auth = null ;
111
172
$ credentials = [];
112
- if ($ bypassSshForGitHub || 0 !== $ this -> process -> execute ( $ command , $ commandOutput , $ cwd )) {
173
+ if ($ bypassSshForGitHub || 0 !== $ runCommands ( $ url )) {
113
174
$ errorMsg = $ this ->process ->getErrorOutput ();
114
175
// private github repository without ssh key access, try https with auth
115
176
// @phpstan-ignore composerPcre.maybeUnsafeStrictGroups
@@ -129,8 +190,7 @@ public function runCommand(callable $commandCallable, string $url, ?string $cwd,
129
190
if ($ this ->io ->hasAuthentication ($ match [1 ])) {
130
191
$ auth = $ this ->io ->getAuthentication ($ match [1 ]);
131
192
$ authUrl = 'https:// ' . rawurlencode ($ auth ['username ' ]) . ': ' . rawurlencode ($ auth ['password ' ]) . '@ ' . $ match [1 ] . '/ ' . $ match [2 ] . '.git ' ;
132
- $ command = $ commandCallable ($ authUrl );
133
- if (0 === $ this ->process ->execute ($ command , $ commandOutput , $ cwd )) {
193
+ if (0 === $ runCommands ($ authUrl )) {
134
194
return ;
135
195
}
136
196
@@ -166,8 +226,7 @@ public function runCommand(callable $commandCallable, string $url, ?string $cwd,
166
226
$ auth = $ this ->io ->getAuthentication ($ domain );
167
227
$ authUrl = 'https:// ' . rawurlencode ($ auth ['username ' ]) . ': ' . rawurlencode ($ auth ['password ' ]) . '@ ' . $ domain . '/ ' . $ repo_with_git_part ;
168
228
169
- $ command = $ commandCallable ($ authUrl );
170
- if (0 === $ this ->process ->execute ($ command , $ commandOutput , $ cwd )) {
229
+ if (0 === $ runCommands ($ authUrl )) {
171
230
// Well if that succeeded on our first try, let's just
172
231
// take the win.
173
232
return ;
@@ -185,8 +244,7 @@ public function runCommand(callable $commandCallable, string $url, ?string $cwd,
185
244
if ($ this ->io ->hasAuthentication ($ domain )) {
186
245
$ auth = $ this ->io ->getAuthentication ($ domain );
187
246
$ authUrl = 'https:// ' . rawurlencode ($ auth ['username ' ]) . ': ' . rawurlencode ($ auth ['password ' ]) . '@ ' . $ domain . '/ ' . $ repo_with_git_part ;
188
- $ command = $ commandCallable ($ authUrl );
189
- if (0 === $ this ->process ->execute ($ command , $ commandOutput , $ cwd )) {
247
+ if (0 === $ runCommands ($ authUrl )) {
190
248
return ;
191
249
}
192
250
@@ -195,8 +253,7 @@ public function runCommand(callable $commandCallable, string $url, ?string $cwd,
195
253
//Falling back to ssh
196
254
$ sshUrl = 'git@bitbucket.org: ' . $ repo_with_git_part ;
197
255
$ this ->io ->writeError (' No bitbucket authentication configured. Falling back to ssh. ' );
198
- $ command = $ commandCallable ($ sshUrl );
199
- if (0 === $ this ->process ->execute ($ command , $ commandOutput , $ cwd )) {
256
+ if (0 === $ runCommands ($ sshUrl )) {
200
257
return ;
201
258
}
202
259
@@ -228,8 +285,7 @@ public function runCommand(callable $commandCallable, string $url, ?string $cwd,
228
285
$ authUrl = $ match [1 ] . ':// ' . rawurlencode ((string ) $ auth ['username ' ]) . ': ' . rawurlencode ((string ) $ auth ['password ' ]) . '@ ' . $ match [2 ] . '/ ' . $ match [3 ];
229
286
}
230
287
231
- $ command = $ commandCallable ($ authUrl );
232
- if (0 === $ this ->process ->execute ($ command , $ commandOutput , $ cwd )) {
288
+ if (0 === $ runCommands ($ authUrl )) {
233
289
return ;
234
290
}
235
291
@@ -266,8 +322,7 @@ public function runCommand(callable $commandCallable, string $url, ?string $cwd,
266
322
if (null !== $ auth ) {
267
323
$ authUrl = $ match [1 ] . rawurlencode ((string ) $ auth ['username ' ]) . ': ' . rawurlencode ((string ) $ auth ['password ' ]) . '@ ' . $ match [2 ] . $ match [3 ];
268
324
269
- $ command = $ commandCallable ($ authUrl );
270
- if (0 === $ this ->process ->execute ($ command , $ commandOutput , $ cwd )) {
325
+ if (0 === $ runCommands ($ authUrl )) {
271
326
$ this ->io ->setAuthentication ($ match [2 ], $ auth ['username ' ], $ auth ['password ' ]);
272
327
$ authHelper = new AuthHelper ($ this ->io , $ this ->config );
273
328
$ authHelper ->storeAuth ($ match [2 ], $ storeAuth );
@@ -285,10 +340,10 @@ public function runCommand(callable $commandCallable, string $url, ?string $cwd,
285
340
}
286
341
287
342
if (count ($ credentials ) > 0 ) {
288
- $ command = $ this ->maskCredentials ($ command , $ credentials );
343
+ $ lastCommand = $ this ->maskCredentials ($ lastCommand , $ credentials );
289
344
$ errorMsg = $ this ->maskCredentials ($ errorMsg , $ credentials );
290
345
}
291
- $ this ->throwException ('Failed to execute ' . $ command . "\n\n" . $ errorMsg , $ url );
346
+ $ this ->throwException ('Failed to execute ' . $ lastCommand . "\n\n" . $ errorMsg , $ url );
292
347
}
293
348
}
294
349
@@ -310,21 +365,7 @@ public function syncMirror(string $url, string $dir): bool
310
365
['git ' , 'gc ' , '--auto ' ],
311
366
];
312
367
313
- $ command = [];
314
- $ commandCallable = static function (string $ url ) use (&$ command ): array {
315
- $ map = [
316
- '%url% ' => $ url ,
317
- '%sanitizedUrl% ' => Preg::replace ('{://([^@]+?):(.+?)@} ' , ':// ' , $ url ),
318
- ];
319
-
320
- return array_map (static function ($ value ) use ($ map ): string {
321
- return $ map [$ value ] ?? $ value ;
322
- }, $ command );
323
- };
324
-
325
- foreach ($ commands as $ command ) {
326
- $ this ->runCommand ($ commandCallable , $ url , $ dir );
327
- }
368
+ $ this ->runCommands ($ commands , $ url , $ dir );
328
369
} catch (\Exception $ e ) {
329
370
$ this ->io ->writeError ('<error>Sync mirror failed: ' . $ e ->getMessage () . '</error> ' , true , IOInterface::DEBUG );
330
371
@@ -337,11 +378,7 @@ public function syncMirror(string $url, string $dir): bool
337
378
// clean up directory and do a fresh clone into it
338
379
$ this ->filesystem ->removeDirectory ($ dir );
339
380
340
- $ commandCallable = static function ($ url ) use ($ dir ): array {
341
- return ['git ' , 'clone ' , '--mirror ' , '-- ' , $ url , $ dir ];
342
- };
343
-
344
- $ this ->runCommand ($ commandCallable , $ url , $ dir , true );
381
+ $ this ->runCommands ([['git ' , 'clone ' , '--mirror ' , '-- ' , '%url% ' , $ dir ]], $ url , $ dir , true );
345
382
346
383
return true ;
347
384
}
@@ -391,6 +428,9 @@ public static function getNoShowSignatureFlag(ProcessExecutor $process): string
391
428
return '' ;
392
429
}
393
430
431
+ /**
432
+ * @return list<string>
433
+ */
394
434
public static function getNoShowSignatureFlags (ProcessExecutor $ process ): array
395
435
{
396
436
return explode (' ' , substr (static ::getNoShowSignatureFlag ($ process ), 1 ));
@@ -451,25 +491,10 @@ public function getMirrorDefaultBranch(string $url, string $dir, bool $isLocalPa
451
491
['git ' , 'remote ' , 'set-url ' , 'origin ' , '-- ' , '%sanitizedUrl% ' ],
452
492
];
453
493
454
- $ output = [];
455
- $ command = [];
456
- $ commandCallable = static function (string $ url ) use (&$ command ): array {
457
- $ map = [
458
- '%url% ' => $ url ,
459
- '%sanitizedUrl% ' => Preg::replace ('{://([^@]+?):(.+?)@} ' , ':// ' , $ url ),
460
- ];
461
-
462
- return array_map (static function ($ value ) use ($ map ): string {
463
- return $ map [$ value ] ?? $ value ;
464
- }, $ command );
465
- };
466
-
467
- foreach ($ commands as $ command ) {
468
- $ this ->runCommand ($ commandCallable , $ url , $ dir , false , $ output []);
469
- }
494
+ $ this ->runCommands ($ commands , $ url , $ dir , false , $ output );
470
495
}
471
496
472
- $ lines = $ this ->process ->splitLines (implode ( '' , $ output) );
497
+ $ lines = $ this ->process ->splitLines ($ output );
473
498
foreach ($ lines as $ line ) {
474
499
if (Preg::isMatch ('{^\s*HEAD branch:\s(.+)\s*$}m ' , $ line , $ matches )) {
475
500
return $ matches [1 ];
0 commit comments