1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Copyright © Vaimo Group. All rights reserved. |
4
|
|
|
* See LICENSE_VAIMO.txt for license details. |
5
|
|
|
*/ |
6
|
|
|
namespace Vaimo\ComposerPatches\Composer\Commands; |
7
|
|
|
|
8
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
9
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
10
|
|
|
use Symfony\Component\Console\Input\InputOption; |
11
|
|
|
use Symfony\Component\Console\Input\InputArgument; |
12
|
|
|
|
13
|
|
|
use Composer\Script\ScriptEvents; |
14
|
|
|
|
15
|
|
|
use Vaimo\ComposerPatches\Patch\Definition as Patch; |
16
|
|
|
use Vaimo\ComposerPatches\Repository\PatchesApplier\ListResolvers; |
17
|
|
|
use Vaimo\ComposerPatches\Config; |
18
|
|
|
use Vaimo\ComposerPatches\Interfaces\ListResolverInterface; |
19
|
|
|
use Vaimo\ComposerPatches\Composer\Plugin\Behaviour; |
20
|
|
|
use Vaimo\ComposerPatches\Environment; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) |
24
|
|
|
*/ |
25
|
|
|
class PatchCommand extends \Composer\Command\BaseCommand |
26
|
|
|
{ |
27
|
|
|
protected function configure() |
28
|
|
|
{ |
29
|
|
|
$this->setName('patch'); |
30
|
|
|
$this->setDescription('Apply registered patches to current project'); |
31
|
|
|
|
32
|
|
|
$this->addArgument( |
33
|
|
|
'targets', |
34
|
|
|
InputArgument::IS_ARRAY, |
35
|
|
|
'Packages for the patcher to target', |
36
|
|
|
array() |
37
|
|
|
); |
38
|
|
|
|
39
|
|
|
$this->addOption( |
40
|
|
|
'--redo', |
41
|
|
|
null, |
42
|
|
|
InputOption::VALUE_NONE, |
43
|
|
|
'Re-patch all packages or a specific package when targets defined' |
44
|
|
|
); |
45
|
|
|
|
46
|
|
|
$this->addOption( |
47
|
|
|
'--undo', |
48
|
|
|
null, |
49
|
|
|
InputOption::VALUE_NONE, |
50
|
|
|
'Remove all patches or a specific patch when targets defined' |
51
|
|
|
); |
52
|
|
|
|
53
|
|
|
$this->addOption( |
54
|
|
|
'--no-dev', |
55
|
|
|
null, |
56
|
|
|
InputOption::VALUE_NONE, |
57
|
|
|
'Disables installation of require-dev packages' |
58
|
|
|
); |
59
|
|
|
|
60
|
|
|
$this->addOption( |
61
|
|
|
'--filter', |
62
|
|
|
null, |
63
|
|
|
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, |
64
|
|
|
'Apply only those patch files/sources that match with provided filter' |
65
|
|
|
); |
66
|
|
|
|
67
|
|
|
$this->addOption( |
68
|
|
|
'--explicit', |
69
|
|
|
null, |
70
|
|
|
InputOption::VALUE_NONE, |
71
|
|
|
'Show information for every patch that gets re-applied (due to package reset)' |
72
|
|
|
); |
73
|
|
|
|
74
|
|
|
$this->addOption( |
75
|
|
|
'--show-reapplies', |
76
|
|
|
null, |
77
|
|
|
InputOption::VALUE_NONE, |
78
|
|
|
'Alias for \'explicit\' argument' |
79
|
|
|
); |
80
|
|
|
|
81
|
|
|
$this->addOption( |
82
|
|
|
'--from-source', |
83
|
|
|
null, |
84
|
|
|
InputOption::VALUE_NONE, |
85
|
|
|
'Apply patches based on information directly from packages in vendor folder' |
86
|
|
|
); |
87
|
|
|
|
88
|
|
|
$this->addOption( |
89
|
|
|
'--graceful', |
90
|
|
|
null, |
91
|
|
|
InputOption::VALUE_NONE, |
92
|
|
|
'Continue even when some patch fails to apply' |
93
|
|
|
); |
94
|
|
|
|
95
|
|
|
$this->addOption( |
96
|
|
|
'--force', |
97
|
|
|
null, |
98
|
|
|
InputOption::VALUE_NONE, |
99
|
|
|
'Force package reset even when it has local change' |
100
|
|
|
); |
101
|
|
|
|
102
|
|
|
$this->addOption( |
103
|
|
|
'--no-scripts', |
104
|
|
|
null, |
105
|
|
|
InputOption::VALUE_NONE, |
106
|
|
|
'Skips the execution of all scripts defined in composer.json file.' |
107
|
|
|
); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* @SuppressWarnings(PHPMD.UnusedFormalParameter) |
112
|
|
|
* |
113
|
|
|
* @param InputInterface $input |
114
|
|
|
* @param OutputInterface $output |
115
|
|
|
* @return int|void|null |
116
|
|
|
* @throws \Exception |
117
|
|
|
*/ |
118
|
|
|
protected function execute(InputInterface $input, OutputInterface $output) |
119
|
|
|
{ |
120
|
|
|
$composer = $this->getComposer(); |
121
|
|
|
|
122
|
|
|
$appIO = $this->getIO(); |
123
|
|
|
|
124
|
|
|
$isDevMode = !$input->getOption('no-dev'); |
125
|
|
|
|
126
|
|
|
$behaviourFlags = $this->getBehaviourFlags($input); |
127
|
|
|
|
128
|
|
|
$shouldUndo = !$behaviourFlags[Behaviour::REDO] && $behaviourFlags[Behaviour::UNDO]; |
129
|
|
|
|
130
|
|
|
$contextFactory = new \Vaimo\ComposerPatches\Factories\ComposerContextFactory($composer); |
131
|
|
|
$composerContext = $contextFactory->create(); |
132
|
|
|
|
133
|
|
|
$configFactory = new \Vaimo\ComposerPatches\Factories\ConfigFactory($composerContext, array( |
134
|
|
|
Config::PATCHER_FORCE_REAPPLY => $behaviourFlags[Behaviour::REDO], |
135
|
|
|
Config::PATCHER_FROM_SOURCE => (bool)$input->getOption('from-source'), |
136
|
|
|
Config::PATCHER_GRACEFUL => (bool)$input->getOption('graceful') |
137
|
|
|
|| $behaviourFlags[Behaviour::REDO] |
138
|
|
|
|| $behaviourFlags[Behaviour::UNDO], |
139
|
|
|
Config::PATCHER_SOURCES => $this->createSourcesEnablerConfig() |
140
|
|
|
)); |
141
|
|
|
|
142
|
|
|
$filters = $this->resolveActiveFilters($input, $behaviourFlags); |
143
|
|
|
|
144
|
|
|
$listResolver = $this->createListResolver($behaviourFlags, $filters); |
145
|
|
|
$this->configureEnvironmentForBehaviour($behaviourFlags); |
146
|
|
|
|
147
|
|
|
$outputTriggers = $this->resolveOutputTriggers($filters, $behaviourFlags); |
148
|
|
|
$bootstrapFactory = new \Vaimo\ComposerPatches\Factories\BootstrapFactory($composerContext, $appIO); |
149
|
|
|
|
150
|
|
|
$outputStrategy = new \Vaimo\ComposerPatches\Strategies\OutputStrategy($outputTriggers); |
151
|
|
|
$bootstrap = $bootstrapFactory->create($configFactory, $listResolver, $outputStrategy); |
152
|
|
|
$compatExecutor = new \Vaimo\ComposerPatches\Compatibility\Executor(); |
153
|
|
|
|
154
|
|
|
$runtimeUtils = new \Vaimo\ComposerPatches\Utils\RuntimeUtils(); |
155
|
|
|
$lockSanitizer = new \Vaimo\ComposerPatches\Repository\Lock\Sanitizer($appIO); |
156
|
|
|
$repository = $composer->getRepositoryManager()->getLocalRepository(); |
157
|
|
|
$installationManager = $composer->getInstallationManager(); |
158
|
|
|
|
159
|
|
|
$result = $runtimeUtils->executeWithPostAction( |
160
|
|
|
function () use ($shouldUndo, $filters, $bootstrap, $isDevMode) { |
161
|
|
|
if ($shouldUndo && !array_filter($filters)) { |
162
|
|
|
$bootstrap->stripPatches($isDevMode); |
163
|
|
|
|
164
|
|
|
return true; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
return $bootstrap->applyPatches($isDevMode); |
168
|
|
|
}, |
169
|
|
|
function () use ($installationManager, $repository, $lockSanitizer, $isDevMode, $compatExecutor) { |
170
|
|
|
$compatExecutor->repositoryWrite($repository, $installationManager, $isDevMode); |
171
|
|
|
$lockSanitizer->sanitize(); |
172
|
|
|
} |
173
|
|
|
); |
174
|
|
|
|
175
|
|
|
if (!$input->getOption('no-scripts')) { |
176
|
|
|
$composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_INSTALL_CMD, $isDevMode); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
return (int)!$result; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
private function createSourcesEnablerConfig() |
183
|
|
|
{ |
184
|
|
|
$configDefaults = new \Vaimo\ComposerPatches\Config\Defaults(); |
185
|
|
|
|
186
|
|
|
$defaultValues = $configDefaults->getPatcherConfig(); |
187
|
|
|
|
188
|
|
|
if (isset($defaultValues[Config::PATCHER_SOURCES]) && is_array($defaultValues[Config::PATCHER_SOURCES])) { |
189
|
|
|
$sourceTypes = array_keys((array)$defaultValues[Config::PATCHER_SOURCES]); |
190
|
|
|
|
191
|
|
|
return array_fill_keys($sourceTypes, true); |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
return array(); |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
protected function getBehaviourFlags(InputInterface $input) |
198
|
|
|
{ |
199
|
|
|
return array( |
200
|
|
|
Behaviour::REDO => $this->getOptionGraceful($input, 'redo'), |
201
|
|
|
Behaviour::UNDO => $this->getOptionGraceful($input, 'undo'), |
202
|
|
|
Behaviour::FORCE => $this->getOptionGraceful($input, 'force'), |
203
|
|
|
Behaviour::EXPLICIT => $this->getOptionGraceful($input, 'explicit') |
204
|
|
|
|| $this->getOptionGraceful($input, 'show-reapplies') |
205
|
|
|
); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
private function getOptionGraceful(InputInterface $input, $name) |
209
|
|
|
{ |
210
|
|
|
return $input->hasOption($name) && $input->getOption($name); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
private function resolveActiveFilters(InputInterface $input, array $behaviourFlags) |
214
|
|
|
{ |
215
|
|
|
$filters = array( |
216
|
|
|
Patch::SOURCE => $input->getOption('filter'), |
217
|
|
|
Patch::TARGETS => $input->getArgument('targets') |
218
|
|
|
); |
219
|
|
|
|
220
|
|
|
$hasFilers = (bool)array_filter($filters); |
221
|
|
|
|
222
|
|
|
if (!$hasFilers && $behaviourFlags[Behaviour::REDO]) { |
223
|
|
|
$filters[Patch::SOURCE] = array('*'); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
return $filters; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
private function configureEnvironmentForBehaviour(array $behaviourFlags) |
230
|
|
|
{ |
231
|
|
|
$runtimeUtils = new \Vaimo\ComposerPatches\Utils\RuntimeUtils(); |
232
|
|
|
|
233
|
|
|
$runtimeUtils->setEnvironmentValues(array( |
234
|
|
|
Environment::FORCE_RESET => (int)$behaviourFlags[Behaviour::FORCE] |
235
|
|
|
)); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
private function resolveOutputTriggers(array $filters, array $behaviourFlags) |
239
|
|
|
{ |
240
|
|
|
$hasFilers = (bool)array_filter($filters); |
241
|
|
|
|
242
|
|
|
$isExplicit = $behaviourFlags[Behaviour::EXPLICIT]; |
243
|
|
|
|
244
|
|
|
if (!$hasFilers && $behaviourFlags[Behaviour::REDO]) { |
245
|
|
|
$isExplicit = true; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
$outputTriggerFlags = array( |
249
|
|
|
Patch::STATUS_NEW => !$hasFilers, |
250
|
|
|
Patch::STATUS_CHANGED => !$hasFilers, |
251
|
|
|
Patch::STATUS_MATCH => true, |
252
|
|
|
Patch::SOURCE => $isExplicit, |
253
|
|
|
Patch::URL => $isExplicit |
254
|
|
|
); |
255
|
|
|
|
256
|
|
|
return array_keys( |
257
|
|
|
array_filter($outputTriggerFlags) |
258
|
|
|
); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
private function createListResolver(array $behaviourFlags, array $filters) |
262
|
|
|
{ |
263
|
|
|
$listResolver = new ListResolvers\FilteredListResolver($filters); |
264
|
|
|
|
265
|
|
|
$isDefaultBehaviour = !$behaviourFlags[Behaviour::REDO] && !$behaviourFlags[Behaviour::UNDO]; |
266
|
|
|
|
267
|
|
|
$listResolver = $this->attachBehaviourToListResolver($listResolver, $behaviourFlags); |
268
|
|
|
|
269
|
|
|
if ($isDefaultBehaviour) { |
270
|
|
|
$listResolver = new ListResolvers\ChangesListResolver($listResolver); |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
return $listResolver; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
private function attachBehaviourToListResolver(ListResolverInterface $listResolver, array $behaviourFlags) |
277
|
|
|
{ |
278
|
|
|
$shouldUndo = !$behaviourFlags[Behaviour::REDO] && $behaviourFlags[Behaviour::UNDO]; |
279
|
|
|
|
280
|
|
|
if ($shouldUndo) { |
281
|
|
|
return new ListResolvers\InvertedListResolver($listResolver); |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
return new ListResolvers\InclusiveListResolver($listResolver); |
285
|
|
|
} |
286
|
|
|
} |
287
|
|
|
|