├── .gitignore
├── README.md
├── composer.json
├── composer.lock
└── src
└── Inviqa
├── Command.php
├── EnvChecker.php
├── Patch
├── DotPatch.php
├── Factory.php
├── Patch.php
└── Shell.php
└── Patcher.php
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | bin/composer
3 | bin/jsonlint
4 | bin/validate-json
5 | public/
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Patcher
2 | Applying generic patches using the `patch` tool using Composer's `script` feature.
3 | The patching is idempotent as much as the `patch` tool is, meaning patches will _not_ be re-applied if `patch` decides not to.
4 |
5 | ## Project setup
6 |
7 | a) Patches need to be declared in the `extra` config area of Composer (root package only):
8 | ```json
9 | "extra": {
10 | "magento-root-dir": "public",
11 | "patches": {
12 | "patch-group-1": {
13 | "patch-name-1": {
14 | "type": "patch",
15 | "title": "Allow composer autoloader to be applied to Mage.php",
16 | "url": "https://url/to/file1.patch"
17 | }
18 | },
19 | "patch-group-2": {
20 | "patch-name-1": {
21 | "title": "Fixes Windows 8.1",
22 | "url": "https://url/to/file2.patch"
23 | }
24 | },
25 | "shell-patch-group-1": {
26 | "magento-shell-patch-name-1": {
27 | "type": "shell",
28 | "title": "Magento security fix",
29 | "url": "https://url/to/magento/shell/patch.sh"
30 | }
31 | }
32 | }
33 | }
34 | ```
35 |
36 | There are two types of patches:
37 | - type **"patch"** - generic patch/diff files, applied using the patch tool;
38 | - type **"shell"** - official Magento shell patches, which are able to apply and/or revert themselves and are self-contained.
39 |
40 | If no type is declared, **"patch"** is assumed. If you have such a patch type declared, you **must** set the **"magento-root-dir"**
41 | extra config, pointing to the Mage root folder, or else it will fail with an error.
42 |
43 | "Shell" patches will be copied in the Mage root (set by the **"magento-root-dir"** extra config), triggered, then removed.
44 |
45 | 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:
46 | - `patch-group-1` MUST be unique in the `patches` object literal
47 | - `patch-name-1` MUST be unique in its patch _group_
48 |
49 | Examples of patch groups: "magento", "drupal", "security".
50 | Examples of patch names: "CVS-1", "composer-autoloader".
51 |
52 | b) Additional scripts callbacks need to be added for automatic patching on `install` or `update` (root package only):
53 | ```json
54 | "scripts": {
55 | "post-install-cmd": "Inviqa\\Command::patch",
56 | "post-update-cmd": "Inviqa\\Command::patch"
57 | }
58 | ```
59 | You can use whatever [Composer *Command* event](https://getcomposer.org/doc/articles/scripts.md#event-names) you want,
60 | or even [trigger the events manually](https://getcomposer.org/doc/articles/scripts.md#running-scripts-manually).
61 | Again, note that only *Command events* are supported. Please check the above link to see which ones are they.
62 |
63 | c) the `patch` tool must be available
64 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jamescowie/composer-patcher",
3 | "description": "Apply patches using composer",
4 | "license": "MIT",
5 | "authors": [
6 | {
7 | "name": "jamescowie",
8 | "email": "james@jcowie.co.uk"
9 | }
10 | ],
11 | "require": {
12 | "symfony/console": "2.6.*",
13 | "symfony/process": "*"
14 | },
15 | "autoload": {
16 | "psr-0": {
17 | "Inviqa\\": "src/"
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 | "This file is @generated automatically"
6 | ],
7 | "hash": "5ab4e12e229358060d467af160824a21",
8 | "packages": [
9 | {
10 | "name": "eloquent/composer-config-reader",
11 | "version": "2.0.0",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/eloquent/composer-config-reader.git",
15 | "reference": "b79319806e1bcc101c89a023b3aeee9e5931e463"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/eloquent/composer-config-reader/zipball/b79319806e1bcc101c89a023b3aeee9e5931e463",
20 | "reference": "b79319806e1bcc101c89a023b3aeee9e5931e463",
21 | "shasum": ""
22 | },
23 | "require": {
24 | "eloquent/enumeration": "~5",
25 | "eloquent/liberator": "~1",
26 | "icecave/isolator": "~2",
27 | "justinrainbow/json-schema": "~1",
28 | "php": ">=5.3"
29 | },
30 | "require-dev": {
31 | "icecave/archer": "~1"
32 | },
33 | "type": "library",
34 | "autoload": {
35 | "psr-4": {
36 | "Eloquent\\Composer\\Configuration\\": "src"
37 | }
38 | },
39 | "notification-url": "https://packagist.org/downloads/",
40 | "license": [
41 | "MIT"
42 | ],
43 | "authors": [
44 | {
45 | "name": "Erin Millard",
46 | "email": "ezzatron@gmail.com",
47 | "homepage": "http://ezzatron.com/"
48 | }
49 | ],
50 | "description": "A light-weight component for reading Composer configuration files.",
51 | "homepage": "https://github.com/eloquent/composer-config-reader",
52 | "keywords": [
53 | "composer",
54 | "configuration",
55 | "parser",
56 | "reader"
57 | ],
58 | "time": "2014-01-22 01:05:31"
59 | },
60 | {
61 | "name": "eloquent/enumeration",
62 | "version": "5.1.0",
63 | "source": {
64 | "type": "git",
65 | "url": "https://github.com/eloquent/enumeration.git",
66 | "reference": "9c50109cf25b2008b9b8233b814e50e0cdf8b737"
67 | },
68 | "dist": {
69 | "type": "zip",
70 | "url": "https://api.github.com/repos/eloquent/enumeration/zipball/9c50109cf25b2008b9b8233b814e50e0cdf8b737",
71 | "reference": "9c50109cf25b2008b9b8233b814e50e0cdf8b737",
72 | "shasum": ""
73 | },
74 | "require": {
75 | "php": ">=5.3"
76 | },
77 | "require-dev": {
78 | "icecave/archer": "~1"
79 | },
80 | "type": "library",
81 | "autoload": {
82 | "psr-4": {
83 | "Eloquent\\Enumeration\\": "src"
84 | }
85 | },
86 | "notification-url": "https://packagist.org/downloads/",
87 | "license": [
88 | "MIT"
89 | ],
90 | "authors": [
91 | {
92 | "name": "Erin Millard",
93 | "email": "ezzatron@gmail.com",
94 | "homepage": "http://ezzatron.com/"
95 | }
96 | ],
97 | "description": "An enumeration implementation for PHP.",
98 | "homepage": "https://github.com/eloquent/enumeration",
99 | "keywords": [
100 | "class",
101 | "enum",
102 | "enumeration",
103 | "multiton",
104 | "set",
105 | "type"
106 | ],
107 | "time": "2014-03-13 01:16:59"
108 | },
109 | {
110 | "name": "eloquent/liberator",
111 | "version": "1.1.1",
112 | "source": {
113 | "type": "git",
114 | "url": "https://github.com/eloquent/liberator.git",
115 | "reference": "a85435066850ab47acc61296c5d20732ff4c9655"
116 | },
117 | "dist": {
118 | "type": "zip",
119 | "url": "https://api.github.com/repos/eloquent/liberator/zipball/a85435066850ab47acc61296c5d20732ff4c9655",
120 | "reference": "a85435066850ab47acc61296c5d20732ff4c9655",
121 | "shasum": ""
122 | },
123 | "require": {
124 | "eloquent/pops": "~3",
125 | "php": ">=5.3.0"
126 | },
127 | "require-dev": {
128 | "icecave/archer": "~0.2"
129 | },
130 | "type": "library",
131 | "autoload": {
132 | "psr-0": {
133 | "Eloquent\\Liberator": "src"
134 | }
135 | },
136 | "notification-url": "https://packagist.org/downloads/",
137 | "license": [
138 | "MIT"
139 | ],
140 | "authors": [
141 | {
142 | "name": "Erin Millard",
143 | "email": "ezzatron@gmail.com",
144 | "homepage": "http://ezzatron.com/"
145 | }
146 | ],
147 | "description": "A proxy for circumventing PHP access modifier restrictions.",
148 | "homepage": "https://github.com/eloquent/liberator",
149 | "keywords": [
150 | "access",
151 | "modifier",
152 | "object",
153 | "private",
154 | "protected",
155 | "proxy",
156 | "reflection"
157 | ],
158 | "time": "2013-03-03 23:02:47"
159 | },
160 | {
161 | "name": "eloquent/pops",
162 | "version": "3.1.1",
163 | "source": {
164 | "type": "git",
165 | "url": "https://github.com/eloquent/pops.git",
166 | "reference": "b14090a3478f544d1b3a3b9389cb03415cf4f37f"
167 | },
168 | "dist": {
169 | "type": "zip",
170 | "url": "https://api.github.com/repos/eloquent/pops/zipball/b14090a3478f544d1b3a3b9389cb03415cf4f37f",
171 | "reference": "b14090a3478f544d1b3a3b9389cb03415cf4f37f",
172 | "shasum": ""
173 | },
174 | "require": {
175 | "php": ">=5.3.0"
176 | },
177 | "require-dev": {
178 | "icecave/archer": "~0.2"
179 | },
180 | "type": "library",
181 | "autoload": {
182 | "psr-0": {
183 | "Eloquent\\Pops": "src"
184 | }
185 | },
186 | "notification-url": "https://packagist.org/downloads/",
187 | "license": [
188 | "MIT"
189 | ],
190 | "authors": [
191 | {
192 | "name": "Erin Millard",
193 | "email": "ezzatron@gmail.com",
194 | "homepage": "http://ezzatron.com/"
195 | }
196 | ],
197 | "description": "PHP object proxy system.",
198 | "homepage": "https://github.com/eloquent/pops",
199 | "keywords": [
200 | "escaping",
201 | "object",
202 | "proxy"
203 | ],
204 | "time": "2013-03-04 10:10:43"
205 | },
206 | {
207 | "name": "icecave/isolator",
208 | "version": "2.3.0",
209 | "source": {
210 | "type": "git",
211 | "url": "https://github.com/IcecaveStudios/isolator.git",
212 | "reference": "97c51fafa39c57a8f1a31f978a48fbe6cea4a5d5"
213 | },
214 | "dist": {
215 | "type": "zip",
216 | "url": "https://api.github.com/repos/IcecaveStudios/isolator/zipball/97c51fafa39c57a8f1a31f978a48fbe6cea4a5d5",
217 | "reference": "97c51fafa39c57a8f1a31f978a48fbe6cea4a5d5",
218 | "shasum": ""
219 | },
220 | "require": {
221 | "php": ">=5.3"
222 | },
223 | "require-dev": {
224 | "icecave/archer": "~1"
225 | },
226 | "suggest": {
227 | "eloquent/asplode": "Drop-in exception-based error handling."
228 | },
229 | "type": "library",
230 | "autoload": {
231 | "psr-4": {
232 | "Icecave\\Isolator\\": "src"
233 | }
234 | },
235 | "notification-url": "https://packagist.org/downloads/",
236 | "license": [
237 | "MIT"
238 | ],
239 | "authors": [
240 | {
241 | "name": "James Harris",
242 | "email": "james.harris@icecave.com.au",
243 | "homepage": "https://github.com/jmalloc"
244 | }
245 | ],
246 | "description": "Dependency injection for global functions.",
247 | "homepage": "https://github.com/IcecaveStudios/isolator",
248 | "keywords": [
249 | "fake",
250 | "mock",
251 | "phake",
252 | "phpunit",
253 | "test",
254 | "unit"
255 | ],
256 | "time": "2014-08-12 03:16:11"
257 | },
258 | {
259 | "name": "justinrainbow/json-schema",
260 | "version": "1.4.1",
261 | "source": {
262 | "type": "git",
263 | "url": "https://github.com/justinrainbow/json-schema.git",
264 | "reference": "2465fe486c864e30badaa4d005ebdf89dbc503f3"
265 | },
266 | "dist": {
267 | "type": "zip",
268 | "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2465fe486c864e30badaa4d005ebdf89dbc503f3",
269 | "reference": "2465fe486c864e30badaa4d005ebdf89dbc503f3",
270 | "shasum": ""
271 | },
272 | "require": {
273 | "php": ">=5.3.0"
274 | },
275 | "require-dev": {
276 | "json-schema/json-schema-test-suite": "1.1.0",
277 | "phpdocumentor/phpdocumentor": "~2",
278 | "phpunit/phpunit": "~3.7"
279 | },
280 | "bin": [
281 | "bin/validate-json"
282 | ],
283 | "type": "library",
284 | "extra": {
285 | "branch-alias": {
286 | "dev-master": "1.4.x-dev"
287 | }
288 | },
289 | "autoload": {
290 | "psr-0": {
291 | "JsonSchema": "src/"
292 | }
293 | },
294 | "notification-url": "https://packagist.org/downloads/",
295 | "license": [
296 | "BSD-3-Clause"
297 | ],
298 | "authors": [
299 | {
300 | "name": "Bruno Prieto Reis",
301 | "email": "bruno.p.reis@gmail.com"
302 | },
303 | {
304 | "name": "Justin Rainbow",
305 | "email": "justin.rainbow@gmail.com"
306 | },
307 | {
308 | "name": "Igor Wiedler",
309 | "email": "igor@wiedler.ch"
310 | },
311 | {
312 | "name": "Robert Schönthal",
313 | "email": "seroscho@googlemail.com"
314 | }
315 | ],
316 | "description": "A library to validate a json schema.",
317 | "homepage": "https://github.com/justinrainbow/json-schema",
318 | "keywords": [
319 | "json",
320 | "schema"
321 | ],
322 | "time": "2015-03-27 16:41:39"
323 | },
324 | {
325 | "name": "symfony/console",
326 | "version": "v2.6.6",
327 | "target-dir": "Symfony/Component/Console",
328 | "source": {
329 | "type": "git",
330 | "url": "https://github.com/symfony/Console.git",
331 | "reference": "5b91dc4ed5eb08553f57f6df04c4730a73992667"
332 | },
333 | "dist": {
334 | "type": "zip",
335 | "url": "https://api.github.com/repos/symfony/Console/zipball/5b91dc4ed5eb08553f57f6df04c4730a73992667",
336 | "reference": "5b91dc4ed5eb08553f57f6df04c4730a73992667",
337 | "shasum": ""
338 | },
339 | "require": {
340 | "php": ">=5.3.3"
341 | },
342 | "require-dev": {
343 | "psr/log": "~1.0",
344 | "symfony/event-dispatcher": "~2.1",
345 | "symfony/phpunit-bridge": "~2.7",
346 | "symfony/process": "~2.1"
347 | },
348 | "suggest": {
349 | "psr/log": "For using the console logger",
350 | "symfony/event-dispatcher": "",
351 | "symfony/process": ""
352 | },
353 | "type": "library",
354 | "extra": {
355 | "branch-alias": {
356 | "dev-master": "2.6-dev"
357 | }
358 | },
359 | "autoload": {
360 | "psr-0": {
361 | "Symfony\\Component\\Console\\": ""
362 | }
363 | },
364 | "notification-url": "https://packagist.org/downloads/",
365 | "license": [
366 | "MIT"
367 | ],
368 | "authors": [
369 | {
370 | "name": "Symfony Community",
371 | "homepage": "http://symfony.com/contributors"
372 | },
373 | {
374 | "name": "Fabien Potencier",
375 | "email": "fabien@symfony.com"
376 | }
377 | ],
378 | "description": "Symfony Console Component",
379 | "homepage": "http://symfony.com",
380 | "time": "2015-03-30 15:54:10"
381 | },
382 | {
383 | "name": "symfony/process",
384 | "version": "v2.6.6",
385 | "target-dir": "Symfony/Component/Process",
386 | "source": {
387 | "type": "git",
388 | "url": "https://github.com/symfony/Process.git",
389 | "reference": "a8bebaec1a9dc6cde53e0250e32917579b0be552"
390 | },
391 | "dist": {
392 | "type": "zip",
393 | "url": "https://api.github.com/repos/symfony/Process/zipball/a8bebaec1a9dc6cde53e0250e32917579b0be552",
394 | "reference": "a8bebaec1a9dc6cde53e0250e32917579b0be552",
395 | "shasum": ""
396 | },
397 | "require": {
398 | "php": ">=5.3.3"
399 | },
400 | "require-dev": {
401 | "symfony/phpunit-bridge": "~2.7"
402 | },
403 | "type": "library",
404 | "extra": {
405 | "branch-alias": {
406 | "dev-master": "2.6-dev"
407 | }
408 | },
409 | "autoload": {
410 | "psr-0": {
411 | "Symfony\\Component\\Process\\": ""
412 | }
413 | },
414 | "notification-url": "https://packagist.org/downloads/",
415 | "license": [
416 | "MIT"
417 | ],
418 | "authors": [
419 | {
420 | "name": "Symfony Community",
421 | "homepage": "http://symfony.com/contributors"
422 | },
423 | {
424 | "name": "Fabien Potencier",
425 | "email": "fabien@symfony.com"
426 | }
427 | ],
428 | "description": "Symfony Process Component",
429 | "homepage": "http://symfony.com",
430 | "time": "2015-03-30 15:54:10"
431 | }
432 | ],
433 | "packages-dev": [],
434 | "aliases": [],
435 | "minimum-stability": "stable",
436 | "stability-flags": [],
437 | "prefer-stable": false,
438 | "prefer-lowest": false,
439 | "platform": [],
440 | "platform-dev": []
441 | }
442 |
--------------------------------------------------------------------------------
/src/Inviqa/Command.php:
--------------------------------------------------------------------------------
1 | patch($event);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Inviqa/EnvChecker.php:
--------------------------------------------------------------------------------
1 | event = $event;
17 | }
18 |
19 | public function check()
20 | {
21 | $this->clientDeclaredMageRootOnShellPatches();
22 | }
23 |
24 | private function clientDeclaredMageRootOnShellPatches()
25 | {
26 | $extra = $this->event->getComposer()->getPackage()->getExtra();
27 |
28 | if (empty($extra['patches'])) {
29 | return;
30 | }
31 |
32 | $containsShellPatches = false;
33 |
34 | foreach ($extra['patches'] as $patchGroupName => $patchGroup) {
35 | foreach ($patchGroup as $patchName => $patchDetails) {
36 | $patch = Factory::create(
37 | $patchName,
38 | $patchGroupName,
39 | $patchDetails,
40 | array()
41 | );
42 |
43 | if ($patch instanceof Shell) {
44 | $containsShellPatches = true;
45 | break 2;
46 | }
47 | }
48 | }
49 |
50 | if ($containsShellPatches && empty($extra[Patcher::EXTRA_KEY_MAGE_ROOT_DIR])) {
51 | throw new \Exception(
52 | 'When using shell patches, you must declare the Mage root using the extra key: ' .
53 | Patcher::EXTRA_KEY_MAGE_ROOT_DIR
54 | );
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Inviqa/Patch/DotPatch.php:
--------------------------------------------------------------------------------
1 | getPatchTemporaryPath());
20 | $process = new Process("patch -p 1 < $patchPath");
21 | $process->mustRun();
22 | return $process->getExitCode() === 0;
23 | }
24 |
25 | protected function canApply()
26 | {
27 | $patchPath = ProcessUtils::escapeArgument($this->getPatchTemporaryPath());
28 | $process = new Process("patch --dry-run -p 1 < $patchPath");
29 | try {
30 | $process->mustRun();
31 | return $process->getExitCode() === 0;
32 | } catch (\Exception $e) {
33 | return false;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Inviqa/Patch/Factory.php:
--------------------------------------------------------------------------------
1 | setName($name);
47 | $this->setGroup($group);
48 | $this->setComposerExtra($composerExtra);
49 |
50 | if (!empty($details['url'])) {
51 | $this->setUrl($details['url']);
52 | }
53 | }
54 |
55 | /**
56 | * @return boolean|null
57 | * @throws \Exception
58 | */
59 | public final function apply()
60 | {
61 | $namespace = $this->getNamespace();
62 | if ($this->canApply()) {
63 | $this->beforeApply();
64 | $res = (bool) $this->doApply();
65 |
66 | if ($res) {
67 | $this->getOutput()->writeln("Patch $namespace successfully applied.");
68 | } else {
69 | $this->getOutput()->writeln("Patch $namespace was not applied.");
70 | }
71 |
72 | $this->afterApply($res);
73 |
74 | return $res;
75 | }
76 | $this->getOutput()->writeln("Patch $namespace skipped. Patch was already applied?");
77 | return null;
78 | }
79 |
80 | protected function beforeApply()
81 | {}
82 |
83 | protected function afterApply($patchingWasSuccessful)
84 | {}
85 |
86 | /**
87 | * @return string
88 | */
89 | public function getNamespace()
90 | {
91 | return $this->getGroup() . '/' . $this->getName();
92 | }
93 |
94 | /**
95 | * @return string
96 | */
97 | public function getGroup()
98 | {
99 | return $this->group;
100 | }
101 |
102 | /**
103 | * @param string $group
104 | */
105 | protected function setGroup($group)
106 | {
107 | $this->group = $group;
108 | }
109 |
110 | /**
111 | * @return string
112 | */
113 | public function getName()
114 | {
115 | return $this->name;
116 | }
117 |
118 | /**
119 | * @param string $name
120 | */
121 | protected function setName($name)
122 | {
123 | $this->name = $name;
124 | }
125 |
126 | /**
127 | * @return string
128 | */
129 | public function getTitle()
130 | {
131 | return $this->title;
132 | }
133 |
134 | /**
135 | * @param string $title
136 | */
137 | protected function setTitle($title)
138 | {
139 | $this->title = $title;
140 | }
141 |
142 | /**
143 | * @return string
144 | */
145 | public function getDescription()
146 | {
147 | return $this->description;
148 | }
149 |
150 | /**
151 | * @param string $description
152 | */
153 | protected function setDescription($description)
154 | {
155 | $this->description = $description;
156 | }
157 |
158 | /**
159 | * @return string
160 | */
161 | public function getUrl()
162 | {
163 | return $this->url;
164 | }
165 |
166 | /**
167 | * @param string $url
168 | */
169 | protected function setUrl($url)
170 | {
171 | $this->url = $url;
172 | }
173 |
174 | /**
175 | * @return string
176 | * @throws \Exception
177 | */
178 | protected function getPatchTemporaryPath()
179 | {
180 | if (is_null($this->tempPatchFilePath)) {
181 | $this->getOutput()->writeln("Fetching patch {$this->getNamespace()}");
182 |
183 | if (!$this->getUrl()) {
184 | return $this->tempPatchFilePath = '';
185 | }
186 |
187 | if (!$patch = file_get_contents($this->getUrl())) {
188 | throw new \Exception("Could not get contents from {$this->getUrl()}");
189 | }
190 |
191 | $patchFilePath = $this->getPatchTempAbsolutePath();
192 | if (!file_put_contents($patchFilePath, $patch)) {
193 | throw new \Exception("Could not save patch content to $patchFilePath");
194 | }
195 |
196 | $this->tempPatchFilePath = $patchFilePath;
197 | }
198 |
199 | return $this->tempPatchFilePath;
200 | }
201 |
202 | /**
203 | * @return string
204 | */
205 | private function getPatchTempAbsolutePath()
206 | {
207 | // digest unsafe characters
208 | return sys_get_temp_dir() . '/mage_patch_' . md5($this->getGroup() . $this->getName()) . '.tmp';
209 | }
210 |
211 | /**
212 | * @return Output
213 | */
214 | public function getOutput()
215 | {
216 | if (!$this->output) {
217 | $this->output = new ConsoleOutput();
218 | }
219 | return $this->output;
220 | }
221 |
222 | /**
223 | * @param Output $output
224 | */
225 | public function setOutput(Output $output)
226 | {
227 | $this->output = $output;
228 | }
229 |
230 | /**
231 | * @return Output
232 | */
233 | public function getComposerExtra()
234 | {
235 | return $this->composerExtra;
236 | }
237 |
238 | /**
239 | * @param array $composerExtra
240 | */
241 | private function setComposerExtra(array $composerExtra)
242 | {
243 | $this->composerExtra = $composerExtra;
244 | }
245 | }
246 |
--------------------------------------------------------------------------------
/src/Inviqa/Patch/Shell.php:
--------------------------------------------------------------------------------
1 | shellScriptTmpPath);
25 | $process = new Process("sh $patchPath");
26 | $process->mustRun();
27 | return $process->getExitCode() === 0;
28 | }
29 |
30 | /**
31 | * Official Magento patches check if they can be applied beforehand,
32 | * so no need to check it ourselves.
33 | *
34 | * @return bool
35 | */
36 | protected function canApply()
37 | {
38 | return true;
39 | }
40 |
41 | /**
42 | * Magento "sh" patch-scripts need to be in the Mage root when applying.
43 | *
44 | * @throws \Exception
45 | */
46 | protected function beforeApply()
47 | {
48 | $tempPath = $this->getPatchTemporaryPath();
49 | $extra = $this->getComposerExtra();
50 |
51 | $mageDir = $extra[Patcher::EXTRA_KEY_MAGE_ROOT_DIR];
52 |
53 | $destinationFilePath = realpath("./$mageDir") . '/' . self::SHELL_SCRIPT_TMP_NAME;
54 |
55 | if (!@rename($tempPath, $destinationFilePath)) {
56 | throw new \Exception("Could not move form $tempPath to $destinationFilePath");
57 | }
58 |
59 | if ($this->getOutput()->isDebug()) {
60 | $this->getOutput()->writeln("Shell script moved from $tempPath to $destinationFilePath");
61 | }
62 |
63 | $this->shellScriptTmpPath = $destinationFilePath;
64 | }
65 |
66 | protected function afterApply($patchWasOk)
67 | {
68 | if (file_exists($this->shellScriptTmpPath)) {
69 | if ($this->getOutput()->isDebug()) {
70 | $this->getOutput()->writeln("Deleting {$this->shellScriptTmpPath}");
71 | }
72 | @unlink($this->shellScriptTmpPath);
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Inviqa/Patcher.php:
--------------------------------------------------------------------------------
1 | init($event);
23 |
24 | $extraTmp = $extra = $this->event->getComposer()->getPackage()->getExtra();
25 |
26 | if (empty($extra['patches'])) {
27 | $this->output->writeln('No Magento patches were found');
28 | return;
29 | }
30 |
31 | // don't pass the patch information
32 | unset($extraTmp['patches']);
33 |
34 | foreach ($extra['patches'] as $patchGroupName => $patchGroup) {
35 | foreach ($patchGroup as $patchName => $patchDetails) {
36 | $patch = Factory::create(
37 | $patchName,
38 | $patchGroupName,
39 | $patchDetails,
40 | $extraTmp
41 | );
42 | $patch->setOutput($this->output);
43 | $this->applyPatch($patch);
44 | }
45 | }
46 | }
47 |
48 | /**
49 | * @param \Composer\Script\Event $event
50 | */
51 | private function init(\Composer\Script\Event $event)
52 | {
53 | $this->output = new ConsoleOutput();
54 |
55 | if ($event->getIo()->isDebug()) {
56 | $this->output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
57 | }
58 |
59 | $this->event = $event;
60 |
61 | $checker = new EnvChecker($event);
62 | $checker->check();
63 | }
64 |
65 | private function applyPatch(Patch $patch)
66 | {
67 | try {
68 | $patch->apply();
69 | } catch (\Exception $e) {
70 | $this->output->writeln("Error applying patch {$patch->getNamespace()}:");
71 | $this->output->writeln("{$e->getMessage()}");
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------