Skip to content

Commit f6249fd

Browse files
committed
Merge branch '1.x' into 2.x
* 1.x: fix: Recipe update skip ignored files [recipes:update] Fixing bug where files failed to delete that were modified previously
2 parents 40d9d49 + c0c28f8 commit f6249fd

File tree

7 files changed

+139
-83
lines changed

7 files changed

+139
-83
lines changed

.github/workflows/ci.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ jobs:
2020
matrix:
2121
include:
2222
- php: '8.0'
23+
composer: 2.2.x
2324
- php: '8.1'
2425
mode: low-deps
2526

@@ -28,11 +29,11 @@ jobs:
2829
uses: actions/[email protected]
2930

3031
- name: "Install PHP with extensions"
31-
uses: shivammathur/setup-php@2.7.0
32+
uses: shivammathur/setup-php@2.18.0
3233
with:
3334
coverage: "none"
3435
php-version: ${{ matrix.php }}
35-
tools: composer:v2
36+
tools: composer:${{ matrix.composer }}
3637

3738
- name: "Validate composer.json"
3839
run: "composer validate --strict --no-check-lock"

src/Configurator/BundlesConfigurator.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,20 @@ public function unconfigure(Recipe $recipe, $bundles, Lock $lock)
4444

4545
public function update(RecipeUpdate $recipeUpdate, array $originalConfig, array $newConfig): void
4646
{
47-
$originalBundles = $this->configureBundles($originalConfig);
47+
$originalBundles = $this->configureBundles($originalConfig, true);
4848
$recipeUpdate->setOriginalFile(
4949
$this->getLocalConfFile(),
5050
$this->buildContents($originalBundles)
5151
);
5252

53-
$newBundles = $this->configureBundles($newConfig);
53+
$newBundles = $this->configureBundles($newConfig, true);
5454
$recipeUpdate->setNewFile(
5555
$this->getLocalConfFile(),
5656
$this->buildContents($newBundles)
5757
);
5858
}
5959

60-
private function configureBundles(array $bundles): array
60+
private function configureBundles(array $bundles, bool $resetEnvironments = false): array
6161
{
6262
$file = $this->getConfFile();
6363
$registered = $this->load($file);
@@ -70,7 +70,15 @@ private function configureBundles(array $bundles): array
7070
}
7171
foreach ($classes as $class => $envs) {
7272
// do not override existing configured envs for a bundle
73-
if (!isset($registered[$class])) {
73+
if (!isset($registered[$class]) || $resetEnvironments) {
74+
if ($resetEnvironments) {
75+
// used during calculating an "upgrade"
76+
// here, we want to "undo" the bundle's configuration entirely
77+
// then re-add it fresh, in case some environments have been
78+
// removed in an updated version of the recipe
79+
$registered[$class] = [];
80+
}
81+
7482
foreach ($envs as $env) {
7583
$registered[$class][$env] = true;
7684
}

src/Update/RecipePatch.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ class RecipePatch
1515
{
1616
private $patch;
1717
private $blobs;
18+
private $deletedFiles;
1819
private $removedPatches;
1920

20-
public function __construct(string $patch, array $blobs, array $removedPatches = [])
21+
public function __construct(string $patch, array $blobs, array $deletedFiles, array $removedPatches = [])
2122
{
2223
$this->patch = $patch;
2324
$this->blobs = $blobs;
25+
$this->deletedFiles = $deletedFiles;
2426
$this->removedPatches = $removedPatches;
2527
}
2628

@@ -34,6 +36,11 @@ public function getBlobs(): array
3436
return $this->blobs;
3537
}
3638

39+
public function getDeletedFiles(): array
40+
{
41+
return $this->deletedFiles;
42+
}
43+
3744
/**
3845
* Patches for modified files that were removed because the file
3946
* has been deleted in the user's project.

src/Update/RecipePatcher.php

Lines changed: 69 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -38,56 +38,37 @@ public function __construct(string $rootDir, IOInterface $io)
3838
*/
3939
public function applyPatch(RecipePatch $patch): bool
4040
{
41-
if (!$patch->getPatch()) {
42-
// nothing to do!
43-
return true;
44-
}
45-
46-
$addedBlobs = $this->addMissingBlobs($patch->getBlobs());
47-
48-
$patchPath = $this->rootDir.'/_flex_recipe_update.patch';
49-
file_put_contents($patchPath, $patch->getPatch());
50-
51-
try {
52-
$this->execute('git update-index --refresh', $this->rootDir);
53-
54-
$output = '';
55-
$statusCode = $this->processExecutor->execute('git apply "_flex_recipe_update.patch" -3', $output, $this->rootDir);
41+
$withConflicts = $this->_applyPatchFile($patch);
5642

57-
if (0 === $statusCode) {
58-
// successful with no conflicts
59-
return true;
60-
}
61-
62-
if (false !== strpos($this->processExecutor->getErrorOutput(), 'with conflicts')) {
63-
// successful with conflicts
64-
return false;
65-
}
66-
67-
throw new \LogicException('Error applying the patch: '.$this->processExecutor->getErrorOutput());
68-
} finally {
69-
unlink($patchPath);
70-
// clean up any temporary blobs
71-
foreach ($addedBlobs as $filename) {
72-
unlink($filename);
43+
foreach ($patch->getDeletedFiles() as $deletedFile) {
44+
if (file_exists($this->rootDir.'/'.$deletedFile)) {
45+
$this->execute(sprintf('git rm %s', ProcessExecutor::escape($deletedFile)), $this->rootDir);
7346
}
7447
}
48+
49+
return $withConflicts;
7550
}
7651

7752
public function generatePatch(array $originalFiles, array $newFiles): RecipePatch
7853
{
54+
$ignoredFiles = $this->getIgnoredFiles(array_keys($originalFiles) + array_keys($newFiles));
55+
7956
// null implies "file does not exist"
80-
$originalFiles = array_filter($originalFiles, function ($file) {
81-
return null !== $file;
82-
});
83-
$newFiles = array_filter($newFiles, function ($file) {
84-
return null !== $file;
85-
});
86-
87-
// find removed files and add them so they will be deleted
57+
$originalFiles = array_filter($originalFiles, function ($file, $fileName) use ($ignoredFiles) {
58+
return null !== $file && !\in_array($fileName, $ignoredFiles);
59+
}, \ARRAY_FILTER_USE_BOTH);
60+
61+
$newFiles = array_filter($newFiles, function ($file, $fileName) use ($ignoredFiles) {
62+
return null !== $file && !\in_array($fileName, $ignoredFiles);
63+
}, \ARRAY_FILTER_USE_BOTH);
64+
65+
$deletedFiles = [];
66+
// find removed files & record that they are deleted
67+
// unset them from originalFiles to avoid unnecessary blobs being added
8868
foreach ($originalFiles as $file => $contents) {
8969
if (!isset($newFiles[$file])) {
90-
$newFiles[$file] = null;
70+
$deletedFiles[] = $file;
71+
unset($originalFiles[$file]);
9172
}
9273
}
9374

@@ -130,6 +111,7 @@ public function generatePatch(array $originalFiles, array $newFiles): RecipePatc
130111
return new RecipePatch(
131112
$patchString,
132113
$blobs,
114+
$deletedFiles,
133115
$removedPatches
134116
);
135117
} finally {
@@ -223,4 +205,51 @@ private function getBlobPath(string $hash): string
223205

224206
return '.git/objects/'.$hashStart.'/'.$hashEnd;
225207
}
208+
209+
private function _applyPatchFile(RecipePatch $patch)
210+
{
211+
if (!$patch->getPatch()) {
212+
// nothing to do!
213+
return true;
214+
}
215+
216+
$addedBlobs = $this->addMissingBlobs($patch->getBlobs());
217+
218+
$patchPath = $this->rootDir.'/_flex_recipe_update.patch';
219+
file_put_contents($patchPath, $patch->getPatch());
220+
221+
try {
222+
$this->execute('git update-index --refresh', $this->rootDir);
223+
224+
$output = '';
225+
$statusCode = $this->processExecutor->execute('git apply "_flex_recipe_update.patch" -3', $output, $this->rootDir);
226+
227+
if (0 === $statusCode) {
228+
// successful with no conflicts
229+
return true;
230+
}
231+
232+
if (false !== strpos($this->processExecutor->getErrorOutput(), 'with conflicts')) {
233+
// successful with conflicts
234+
return false;
235+
}
236+
237+
throw new \LogicException('Error applying the patch: '.$this->processExecutor->getErrorOutput());
238+
} finally {
239+
unlink($patchPath);
240+
// clean up any temporary blobs
241+
foreach ($addedBlobs as $filename) {
242+
unlink($filename);
243+
}
244+
}
245+
}
246+
247+
private function getIgnoredFiles(array $fileNames): array
248+
{
249+
$args = implode(' ', array_map([ProcessExecutor::class, 'escape'], $fileNames));
250+
$output = '';
251+
$this->processExecutor->execute(sprintf('git check-ignore %s', $args), $output, $this->rootDir);
252+
253+
return $this->processExecutor->splitLines($output);
254+
}
226255
}

tests/Configurator/BundlesConfiguratorTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ public function testUpdate()
176176
177177
return [
178178
BarBundle::class => ['prod' => false, 'all' => true],
179-
FooBundle::class => ['dev' => true, 'test' => true],
179+
FooBundle::class => ['all' => true],
180180
BazBundle::class => ['all' => true],
181181
NewBundle::class => ['all' => true],
182182
];

tests/Update/RecipePatchTest.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ public function testBasicFunctioning()
2020
{
2121
$thePatch = 'the patch';
2222
$blobs = ['blob1', 'blob2', 'beware of the blob'];
23+
$deletedFiles = ['old_file.txt'];
2324
$removedPatches = ['foo' => 'some diff'];
2425

25-
$patch = new RecipePatch($thePatch, $blobs, $removedPatches);
26+
$patch = new RecipePatch($thePatch, $blobs, $deletedFiles, $removedPatches);
2627

2728
$this->assertSame($thePatch, $patch->getPatch());
2829
$this->assertSame($blobs, $patch->getBlobs());
30+
$this->assertSame($deletedFiles, $patch->getDeletedFiles());
2931
$this->assertSame($removedPatches, $patch->getRemovedPatches());
3032
}
3133
}

0 commit comments

Comments
 (0)