Skip to content

Commit 1b9bc0d

Browse files
committed
Ensure incomplete state is saved in case of patching application failure, improve error message during patching failure
1 parent fa9d917 commit 1b9bc0d

File tree

4 files changed

+103
-10
lines changed

4 files changed

+103
-10
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace Creativestyle\Composer\Patchset\Exception;
4+
5+
use Throwable;
6+
7+
class PatchApplicationFailedException extends PatchingException
8+
{
9+
/**
10+
* @var string
11+
*/
12+
private $cmd;
13+
14+
/**
15+
* @var string
16+
*/
17+
private $cmdOutput;
18+
19+
public function __construct($cmd, $cmdOutput, $message = "", $code = 0, Throwable $previous = null)
20+
{
21+
if (empty($message)) {
22+
$message = "Could not apply patch - command \"$cmd\" failed with: \n$cmdOutput";
23+
}
24+
25+
parent::__construct($message, $code, $previous);
26+
27+
$this->cmd = $cmd;
28+
$this->cmdOutput = $cmdOutput;
29+
}
30+
31+
/**
32+
* @return string
33+
*/
34+
public function getCmd()
35+
{
36+
return $this->cmd;
37+
}
38+
39+
/**
40+
* @return string
41+
*/
42+
public function getCmdOutput()
43+
{
44+
return $this->cmdOutput;
45+
}
46+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Creativestyle\Composer\Patchset\Exception;
4+
5+
class PatchingException extends \RuntimeException
6+
{
7+
8+
}

src/PatchApplicator.php

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Composer\Util\Filesystem;
88
use Composer\Util\ProcessExecutor;
99

10+
use Creativestyle\Composer\Patchset\Exception\PatchApplicationFailedException;
1011
use Psr\Log\LoggerInterface;
1112

1213
class PatchApplicator
@@ -47,7 +48,12 @@ class PatchApplicator
4748
/**
4849
* @var string
4950
*/
50-
private $cmdErr;
51+
private $lastCmd;
52+
53+
/**
54+
* @var string
55+
*/
56+
private $lastCmdOutput;
5157

5258
/**
5359
* @var Filesystem
@@ -82,8 +88,16 @@ private function executeCommand($cmd, $cwd = null)
8288
$cmd = $cmd[0] . ' ' .implode(' ', array_map([ProcessExecutor::class, 'escape'], array_slice($cmd, 1)));
8389
}
8490

85-
$returnCode = $this->executor->execute($cmd, $output, $cwd);
86-
$this->cmdErr = $this->executor->getErrorOutput();
91+
$output = '';
92+
93+
$outputHandler = function($type, $buffer) use (&$output) {
94+
$output .= $buffer;
95+
};
96+
97+
$returnCode = $this->executor->execute($cmd, $outputHandler, $cwd);
98+
99+
$this->lastCmd = $cmd;
100+
$this->lastCmdOutput = $output;
87101

88102
return $returnCode;
89103
}
@@ -145,7 +159,7 @@ public function applyPatch(Patch $patch, PackageInterface $sourcePackage, Packag
145159
$patchFilename = $this->pathResolver->getPatchSourceFilePath($sourcePackage, $patch);
146160

147161
if (!$this->executePatchCommand($patch->getMethod(), $targetDirectory, $patchFilename, $patch->getStripPathComponents())) {
148-
throw new \RuntimeException('Could not apply patch: ' . $this->cmdErr);
162+
throw new PatchApplicationFailedException($patchFilename, $this->lastCmdOutput);
149163
}
150164

151165
$this->logger->notice(sprintf('Applied patch <info>%s:%s</info> [<comment>%s</comment>] (<comment>%s</comment>) using <comment>%s</comment> method',

src/Patcher.php

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Composer\Repository\ArrayRepository;
1212
use Composer\Repository\RepositoryManager;
1313
use Composer\Util\ProcessExecutor;
14+
use Creativestyle\Composer\Patchset\Exception\PatchApplicationFailedException;
1415
use Psr\Log\LoggerInterface;
1516

1617
class Patcher
@@ -290,15 +291,39 @@ private function applyPatches()
290291
$packagePatchApplication->getTargetPackage()->getPrettyVersion()
291292
));
292293

294+
/** @var PatchApplicationFailedException|null $applicationFailedException */
295+
$applicationFailedException = null;
296+
297+
/** @var PatchApplication[] $processedPatchApplications */
298+
$processedPatchApplications = [];
299+
293300
foreach ($packagePatchApplication->getApplications() as $patchApplication) {
294-
$this->applicator->applyPatch(
295-
$patchApplication->getPatch(),
296-
$patchApplication->getSourcePackage(),
297-
$patchApplication->getTargetPackage()
298-
);
301+
try {
302+
$this->applicator->applyPatch(
303+
$patchApplication->getPatch(),
304+
$patchApplication->getSourcePackage(),
305+
$patchApplication->getTargetPackage()
306+
);
307+
} catch (PatchApplicationFailedException $exception) {
308+
// Break out of the loop to save current state (already applied patches).
309+
$applicationFailedException = $exception;
310+
break;
311+
}
312+
313+
$processedPatchApplications[] = $patchApplication;
299314
}
300315

301-
$this->packageApplicationRepository->savePackageApplication($packagePatchApplication);
316+
$this->packageApplicationRepository->savePackageApplication(
317+
new PackagePatchApplication(
318+
$packagePatchApplication->getTargetPackage(),
319+
$processedPatchApplications
320+
)
321+
);
322+
323+
if ($applicationFailedException) {
324+
// We still rethrow the exception after handling it gracefully
325+
throw $applicationFailedException;
326+
}
302327
}
303328
}
304329

0 commit comments

Comments
 (0)