Skip to content

Commit 0e00601

Browse files
vijaycs85webflo
authored andcommitted
#71 - Robustness: Have fallbacks if http://cgit.drupalcode.org is not available (#100)
1 parent 756910b commit 0e00601

File tree

9 files changed

+226
-119
lines changed

9 files changed

+226
-119
lines changed

.scenarios.lock/phpunit4/composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"type": "composer-plugin",
55
"license": "GPL-2.0-or-later",
66
"require": {
7-
"php": ">=5.4.5",
7+
"php": "^5.5.9|>=7.0.8",
88
"composer-plugin-api": "^1.0.0",
99
"composer/semver": "^1.4"
1010
},

.scenarios.lock/phpunit4/composer.lock

+96-92
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ install:
3737
before_script:
3838
- git config --global user.email "[email protected]"
3939
- git config --global user.name "Travis CI Test"
40+
- export COMPOSER_PROCESS_TIMEOUT=600
4041

4142
script:
4243
- composer test

README.md

+15
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,21 @@ The `source` option may be used to specify the URL to download the
5151
scaffold files from; the default source is drupal.org. The literal string
5252
`{version}` in the `source` option is replaced with the current version of
5353
Drupal core being updated prior to download.
54+
You can also define `source` as an array to have fallbacks in case of
55+
any HTTP issues.
56+
57+
```json
58+
{
59+
"extra": {
60+
"drupal-scaffold": {
61+
"source": [
62+
"https://cgit.drupalcode.org/drupal/plain/{path}?h={version}",
63+
"https://raw.githubusercontent.com/drupal/drupal/{version}/{path}"
64+
]
65+
}
66+
}
67+
}
68+
```
5469

5570
With the `drupal-scaffold` option `excludes`, you can provide additional paths
5671
that should not be copied or overwritten. The plugin provides no excludes by

composer.lock

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/FileFetcher.php

+49-6
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,19 @@ class FileFetcher {
4949
*/
5050
protected $fs;
5151

52+
/**
53+
* @var array
54+
*
55+
* A list of potential errors.
56+
*/
57+
protected $errors = [];
58+
5259
/**
5360
* Constructs this FileFetcher object.
5461
*/
55-
public function __construct(RemoteFilesystem $remoteFilesystem, $source, IOInterface $io, $progress = TRUE) {
62+
public function __construct(RemoteFilesystem $remoteFilesystem, IOInterface $io, $progress = TRUE) {
5663
$this->remoteFilesystem = $remoteFilesystem;
5764
$this->io = $io;
58-
$this->source = $source;
5965
$this->fs = new Filesystem();
6066
$this->progress = $progress;
6167
}
@@ -64,23 +70,39 @@ public function __construct(RemoteFilesystem $remoteFilesystem, $source, IOInter
6470
* Downloads all required files and writes it to the file system.
6571
*/
6672
public function fetch($version, $destination, $override) {
73+
$errors = [];
74+
6775
foreach ($this->filenames as $sourceFilename => $filename) {
6876
$target = "$destination/$filename";
6977
if ($override || !file_exists($target)) {
7078
$url = $this->getUri($sourceFilename, $version);
7179
$this->fs->ensureDirectoryExists($destination . '/' . dirname($filename));
80+
7281
if ($this->progress) {
7382
$this->io->writeError(" - <info>$filename</info> (<comment>$url</comment>): ", FALSE);
74-
$this->remoteFilesystem->copy($url, $url, $target, $this->progress);
75-
// Used to put a new line because the remote file system does not put
76-
// one.
83+
try {
84+
$this->remoteFilesystem->copy($url, $url, $target, $this->progress);
85+
} catch(\Exception $e) {
86+
$errors[] = $url;
87+
}
88+
// New line because the remoteFilesystem does not put one.
7789
$this->io->writeError('');
7890
}
7991
else {
80-
$this->remoteFilesystem->copy($url, $url, $target, $this->progress);
92+
try {
93+
$this->remoteFilesystem->copy($url, $url, $target, $this->progress);
94+
} catch(\Exception $e) {
95+
$errors[] = $url;
96+
}
8197
}
8298
}
8399
}
100+
101+
if ($errors) {
102+
$this->addError('Failed to download: ' . "\r\n" . implode("\r\n", $errors));
103+
return FALSE;
104+
}
105+
return TRUE;
84106
}
85107

86108
/**
@@ -90,6 +112,27 @@ public function setFilenames(array $filenames) {
90112
$this->filenames = $filenames;
91113
}
92114

115+
/**
116+
* Set source.
117+
*/
118+
public function setSource($source) {
119+
$this->source = $source;
120+
}
121+
122+
/**
123+
* Set error.
124+
*/
125+
public function addError($error) {
126+
$this->errors[] = $error;
127+
}
128+
129+
/**
130+
* Get errors.
131+
*/
132+
public function getErrors() {
133+
return $this->errors;
134+
}
135+
93136
/**
94137
* Replace filename and version in the source pattern with their values.
95138
*/

src/Handler.php

+37-7
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ public function downloadScaffold() {
149149
// Collect options, excludes and settings files.
150150
$options = $this->getOptions();
151151
$files = array_diff($this->getIncludes(), $this->getExcludes());
152+
$files = array_combine($files, $files);
152153

153154
// Call any pre-scaffold scripts that may be defined.
154155
$dispatcher = new EventDispatcher($this->composer, $this->io);
@@ -158,12 +159,39 @@ public function downloadScaffold() {
158159

159160
$remoteFs = new RemoteFilesystem($this->io);
160161

161-
$fetcher = new PrestissimoFileFetcher($remoteFs, $options['source'], $this->io, $this->progress, $this->composer->getConfig());
162-
$fetcher->setFilenames(array_combine($files, $files));
163-
$fetcher->fetch($version, $webroot, TRUE);
162+
$fetcher = new PrestissimoFileFetcher($remoteFs, $this->io, $this->progress, $this->composer->getConfig());
163+
$sources = (array) $options['source'];
164+
$all_succeeded = FALSE;
164165

165-
$fetcher->setFilenames($this->getInitial());
166-
$fetcher->fetch($version, $webroot, FALSE);
166+
do {
167+
$source = current($sources);
168+
169+
$fetcher->setSource($source);
170+
171+
$fetcher->setFilenames($files);
172+
if ($fetcher->fetch($version, $webroot, TRUE)) {
173+
$fetcher->setFilenames($this->getInitial());
174+
if ($fetcher->fetch($version, $webroot, FALSE)) {
175+
$all_succeeded = TRUE;
176+
break;
177+
}
178+
}
179+
180+
// If here, it means that the fetch for this source has failed.
181+
$next_source = next($sources);
182+
183+
$this->io->writeError('');
184+
$this->io->writeError(" - Has failed with the " . (!$next_source ? 'last ' : '') . "source: <error>$source</error>", TRUE);
185+
if ($next_source) {
186+
$this->io->writeError(" - Now trying with the source: <warning>$next_source</warning>", TRUE);
187+
}
188+
$this->io->writeError('');
189+
190+
} while($next_source);
191+
192+
if (!$all_succeeded) {
193+
throw new \Exception(implode("\r\n\r\n", $fetcher->getErrors()));
194+
}
167195

168196
// Call post-scaffold scripts.
169197
$dispatcher->dispatch(self::POST_DRUPAL_SCAFFOLD_CMD);
@@ -343,8 +371,10 @@ protected function getOptions() {
343371
'excludes' => [],
344372
'includes' => [],
345373
'initial' => [],
346-
'source' => 'https://cgit.drupalcode.org/drupal/plain/{path}?h={version}',
347-
// Github: https://raw.githubusercontent.com/drupal/drupal/{version}/{path}
374+
'source' => [
375+
'https://cgit.drupalcode.org/drupal/plain/{path}?h={version}',
376+
'https://raw.githubusercontent.com/drupal/drupal/{version}/{path}'
377+
],
348378
];
349379
return $options;
350380
}

src/PrestissimoFileFetcher.php

+21-9
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ class PrestissimoFileFetcher extends FileFetcher {
2222
/**
2323
* Constructs this PrestissimoFileFetcher object.
2424
*/
25-
public function __construct(RemoteFilesystem $remoteFilesystem, $source, IOInterface $io, $progress = TRUE, Config $config) {
26-
parent::__construct($remoteFilesystem, $source, $io, $progress);
25+
public function __construct(RemoteFilesystem $remoteFilesystem, IOInterface $io, $progress = TRUE, Config $config) {
26+
parent::__construct($remoteFilesystem, $io, $progress);
2727
$this->config = $config;
2828
}
2929

@@ -32,10 +32,9 @@ public function __construct(RemoteFilesystem $remoteFilesystem, $source, IOInter
3232
*/
3333
public function fetch($version, $destination, $override) {
3434
if (class_exists(CurlMulti::class)) {
35-
$this->fetchWithPrestissimo($version, $destination, $override);
36-
return;
35+
return $this->fetchWithPrestissimo($version, $destination, $override);
3736
}
38-
parent::fetch($version, $destination, $override);
37+
return parent::fetch($version, $destination, $override);
3938
}
4039

4140
/**
@@ -57,7 +56,7 @@ protected function fetchWithPrestissimo($version, $destination, $override) {
5756
$errors = [];
5857
$totalCnt = count($requests);
5958
if ($totalCnt == 0) {
60-
return;
59+
return TRUE;
6160
}
6261

6362
$multi = new CurlMulti();
@@ -70,6 +69,9 @@ protected function fetchWithPrestissimo($version, $destination, $override) {
7069
$failureCnt += $result['failureCnt'];
7170
if (isset($result['errors'])) {
7271
$errors += $result['errors'];
72+
foreach ($result['errors'] as $url => $error) {
73+
$this->io->writeError(" - Downloading <comment>$successCnt</comment>/<comment>$totalCnt</comment>: <info>$url</info> (<error>failed</error>)", TRUE);
74+
}
7375
}
7476
if ($this->progress) {
7577
foreach ($result['urls'] as $url) {
@@ -78,10 +80,20 @@ protected function fetchWithPrestissimo($version, $destination, $override) {
7880
}
7981
} while ($multi->remain());
8082

81-
$urls = array_keys($errors);
82-
if ($urls) {
83-
throw new \Exception('Failed to download ' . implode(", ", $urls));
83+
if ($errors) {
84+
$this->addError('Failed to download: ' . "\r\n" . implode("\r\n", array_keys($errors)));
85+
$errors_extra = [];
86+
foreach($errors as $error) {
87+
if ($error !== "0: " && !isset($errors_extra[$error])) {
88+
$errors_extra[$error] = $error;
89+
}
90+
}
91+
if ($errors_extra) {
92+
$this->addError(implode("\r\n", $errors_extra));
93+
}
94+
return FALSE;
8495
}
96+
return TRUE;
8597
}
8698

8799
}

tests/FetcherTest.php

+4-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ protected function ensureDirectoryExistsAndClear($directory) {
5757
}
5858

5959
public function testFetch() {
60-
$fetcher = new FileFetcher(new RemoteFilesystem(new NullIO()), 'https://cgit.drupalcode.org/drupal/plain/{path}?h={version}', new NullIO());
60+
$fetcher = new FileFetcher(new RemoteFilesystem(new NullIO()), new NullIO());
61+
$fetcher->setSource('https://cgit.drupalcode.org/drupal/plain/{path}?h={version}');
6162
$fetcher->setFilenames([
6263
'.htaccess' => '.htaccess',
6364
'sites/default/default.settings.php' => 'sites/default/default.settings.php',
@@ -68,7 +69,8 @@ public function testFetch() {
6869
}
6970

7071
public function testInitialFetch() {
71-
$fetcher = new FileFetcher(new RemoteFilesystem(new NullIO()), 'https://cgit.drupalcode.org/drupal/plain/{path}?h={version}', new NullIO());
72+
$fetcher = new FileFetcher(new RemoteFilesystem(new NullIO()), new NullIO());
73+
$fetcher->setSource('https://cgit.drupalcode.org/drupal/plain/{path}?h={version}');
7274
$fetcher->setFilenames([
7375
'sites/default/default.settings.php' => 'sites/default/settings.php',
7476
]);

0 commit comments

Comments
 (0)