Skip to content

Commit

Permalink
Merge pull request jamescowie#7 from nevvermind/shell-patching
Browse files Browse the repository at this point in the history
Shell patching
  • Loading branch information
James Cowie committed May 15, 2015
2 parents acd6e01 + 4005354 commit 9ad55eb
Show file tree
Hide file tree
Showing 8 changed files with 500 additions and 68 deletions.
29 changes: 22 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ The patching is idempotent as much as the `patch` tool is, meaning patches will
a) Patches need to be declared in the `extra` config area of Composer (root package only):
```json
"extra": {
"magento-root-dir": "public",
"patches": {
"patch-group-1": {
"patch-name-1": {
"type": "patch",
"title": "Allow composer autoloader to be applied to Mage.php",
"url": "https://url/to/file1.patch"
}
Expand All @@ -19,10 +21,27 @@ a) Patches need to be declared in the `extra` config area of Composer (root pack
"title": "Fixes Windows 8.1",
"url": "https://url/to/file2.patch"
}
}
},
"shell-patch-group-1": {
"magento-shell-patch-name-1": {
"type": "shell",
"title": "Magento security fix",
"url": "https://url/to/magento/shell/patch.sh"
}
}
}
}
```

There are two types of patches:
- type **"patch"** - generic patch/diff files, applied using the patch tool;
- type **"shell"** - official Magento shell patches, which are able to apply and/or revert themselves and are self-contained.

If no type is declared, **"patch"** is assumed. If you have such a patch type declared, you **must** set the **"magento-root-dir"**
extra config, pointing to the Mage root folder, or else it will fail with an error.

"Shell" patches will be copied in the Mage root (set by the **"magento-root-dir"** extra config), triggered, then removed.

A patch's _group_ and _name_ will create its ID, used internally (i.e. `patch-group-1/patch-name-1`), so make sure you follow these 2 rules:
- `patch-group-1` MUST be unique in the `patches` object literal
- `patch-name-1` MUST be unique in its patch _group_
Expand All @@ -33,12 +52,8 @@ Examples of patch names: "CVS-1", "composer-autoloader".
b) Additional scripts callbacks need to be added for automatic patching on `install` or `update` (root package only):
```json
"scripts": {
"post-install-cmd": [
"Inviqa\\Command::patch"
],
"post-update-cmd": [
"Inviqa\\Command::patch"
]
"post-install-cmd": "Inviqa\\Command::patch",
"post-update-cmd": "Inviqa\\Command::patch"
}
```
You can use whatever [Composer *Command* event](https://getcomposer.org/doc/articles/scripts.md#event-names) you want,
Expand Down
16 changes: 0 additions & 16 deletions src/Inviqa/Downloader/Composer.php

This file was deleted.

57 changes: 57 additions & 0 deletions src/Inviqa/EnvChecker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace Inviqa;
use Inviqa\Patch\Factory;
use Inviqa\Patch\Shell;

/**
* Checks the validity of the environment before applying the patches.
*/
class EnvChecker
{
private $event;

public function __construct(\Composer\Script\Event $event)
{
$this->event = $event;
}

public function check()
{
$this->clientDeclaredMageRootOnShellPatches();
}

private function clientDeclaredMageRootOnShellPatches()
{
$extra = $this->event->getComposer()->getPackage()->getExtra();

if (empty($extra['patches'])) {
return;
}

$containsShellPatches = false;

foreach ($extra['patches'] as $patchGroupName => $patchGroup) {
foreach ($patchGroup as $patchName => $patchDetails) {
$patch = Factory::create(
$patchName,
$patchGroupName,
$patchDetails,
array()
);

if ($patch instanceof Shell) {
$containsShellPatches = true;
break 2;
}
}
}

if ($containsShellPatches && empty($extra[Patcher::EXTRA_KEY_MAGE_ROOT_DIR])) {
throw new \Exception(
'When using shell patches, you must declare the Mage root using the extra key: ' .
Patcher::EXTRA_KEY_MAGE_ROOT_DIR
);
}
}
}
36 changes: 36 additions & 0 deletions src/Inviqa/Patch/DotPatch.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Inviqa\Patch;

use Symfony\Component\Process\Process;
use Symfony\Component\Process\ProcessUtils;
use Symfony\Component\Process\Exception\ProcessFailedException;

class DotPatch extends Patch
{
const TYPE = 'patch';

/**
* @throws ProcessFailedException
* @return boolean
*/
protected function doApply()
{
$patchPath = ProcessUtils::escapeArgument($this->getPatchTemporaryPath());
$process = new Process("patch -p 1 < $patchPath");
$process->mustRun();
return $process->getExitCode() === 0;
}

protected function canApply()
{
$patchPath = ProcessUtils::escapeArgument($this->getPatchTemporaryPath());
$process = new Process("patch --dry-run -p 1 < $patchPath");
try {
$process->mustRun();
return $process->getExitCode() === 0;
} catch (\Exception $e) {
return false;
}
}
}
27 changes: 27 additions & 0 deletions src/Inviqa/Patch/Factory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Inviqa\Patch;

class Factory
{
/**
* @param string $patchName
* @param string $patchGroup
* @param array $patchDetails
* @param array $composerExtra
* @return Patch
*/
public static function create($patchName, $patchGroup, array $patchDetails, array $composerExtra)
{
if (empty($patchDetails['type'])) {
$patchDetails['type'] = DotPatch::TYPE;
}

$type = $patchDetails['type'] === DotPatch::TYPE ? 'DotPatch' : 'Shell';
$patchClass = '\\Inviqa\\Patch\\' . $type;

$patch = new $patchClass($patchName, $patchGroup, $patchDetails, $composerExtra);

return $patch;
}
}
Loading

0 comments on commit 9ad55eb

Please sign in to comment.