Passed
Push — 6.5.0.0 ( 149d7e...f7649e )
by Christian
11:39 queued 13s
created

UpdateController::prepare()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 8
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 11
rs 10
1
<?php
2
declare(strict_types=1);
3
4
namespace App\Controller;
5
6
use App\Services\FlexMigrator;
7
use App\Services\ProjectComposerJsonUpdater;
8
use App\Services\RecoveryManager;
9
use App\Services\ReleaseInfoProvider;
10
use App\Services\StreamedCommandResponseGenerator;
11
use Shopware\Core\Framework\Log\Package;
12
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
13
use Symfony\Component\HttpFoundation\Request;
14
use Symfony\Component\HttpFoundation\Response;
15
use Symfony\Component\Routing\Annotation\Route;
16
17
/**
18
 * @internal
19
 */
20
#[Package('core')]
21
class UpdateController extends AbstractController
22
{
23
    public function __construct(
24
        private readonly RecoveryManager $recoveryManager,
25
        private readonly ReleaseInfoProvider $releaseInfoProvider,
26
        private readonly FlexMigrator $flexMigrator,
27
        private readonly StreamedCommandResponseGenerator $streamedCommandResponseGenerator
28
    ) {
29
    }
30
31
    #[Route('/update', name: 'update', defaults: ['step' => 2], methods: ['GET'])]
32
    public function index(Request $request): Response
33
    {
34
        try {
35
            $shopwarePath = $this->recoveryManager->getShopwareLocation();
36
        } catch (\RuntimeException) {
37
            return $this->redirectToRoute('configure');
38
        }
39
40
        $currentShopwareVersion = $this->recoveryManager->getCurrentShopwareVersion($shopwarePath);
41
        $latestVersions = $this->getLatestVersions($request);
42
43
        if (empty($latestVersions)) {
44
            return $this->redirectToRoute('finish');
45
        }
46
47
        return $this->render('update.html.twig', [
48
            'shopwarePath' => $shopwarePath,
49
            'currentShopwareVersion' => $currentShopwareVersion,
50
            'isFlexProject' => $this->recoveryManager->isFlexProject($shopwarePath),
51
            'versions' => $latestVersions,
52
        ]);
53
    }
54
55
    #[Route('/update/_migrate-template', name: 'migrate-template', methods: ['POST'])]
56
    public function migrateTemplate(): Response
57
    {
58
        $shopwarePath = $this->recoveryManager->getShopwareLocation();
59
60
        $this->flexMigrator->cleanup($shopwarePath);
61
        $this->flexMigrator->patchRootComposerJson($shopwarePath);
62
        $this->flexMigrator->copyNewTemplateFiles($shopwarePath);
63
        $this->flexMigrator->migrateEnvFile($shopwarePath);
64
65
        return new Response('', Response::HTTP_NO_CONTENT);
66
    }
67
68
    #[Route('/update/_run', name: 'update_run', methods: ['POST'])]
69
    public function run(Request $request): Response
70
    {
71
        $version = $request->query->get('shopwareVersion', '');
72
73
        $shopwarePath = $this->recoveryManager->getShopwareLocation();
74
75
        ProjectComposerJsonUpdater::update(
76
            $shopwarePath . '/composer.json',
77
            $version
78
        );
79
80
        return $this->streamedCommandResponseGenerator->runJSON([
81
            $this->recoveryManager->getPhpBinary($request),
82
            '-dmemory_limit=1G',
83
            $this->recoveryManager->getBinary(),
84
            'composer',
85
            'update',
86
            '-d',
87
            $shopwarePath,
88
            '--no-interaction',
89
            '--no-ansi',
90
            '--no-scripts',
91
            '-v',
92
            '--with-all-dependencies', // update all packages
93
        ]);
94
    }
95
96
    #[Route('/update/_reset_config', name: 'update_reset_config', methods: ['POST'])]
97
    public function resetConfig(Request $request): Response
98
    {
99
        if (\function_exists('opcache_reset')) {
100
            opcache_reset();
101
        }
102
103
        $shopwarePath = $this->recoveryManager->getShopwareLocation();
104
105
        $this->patchSymfonyFlex($shopwarePath);
106
107
        return $this->streamedCommandResponseGenerator->runJSON([
108
            $this->recoveryManager->getPhpBinary($request),
109
            '-dmemory_limit=1G',
110
            $this->recoveryManager->getBinary(),
111
            'composer',
112
            '-d',
113
            $shopwarePath,
114
            'symfony:recipes:install',
115
            '--force',
116
            '--reset',
117
            '--no-interaction',
118
            '--no-ansi',
119
            '-v',
120
        ]);
121
    }
122
123
    #[Route('/update/_prepare', name: 'update_prepare', methods: ['POST'])]
124
    public function prepare(Request $request): Response
125
    {
126
        $shopwarePath = $this->recoveryManager->getShopwareLocation();
127
128
        return $this->streamedCommandResponseGenerator->runJSON([
129
            $this->recoveryManager->getPhpBinary($request),
130
            '-dmemory_limit=1G',
131
            $shopwarePath . '/bin/console',
132
            'system:update:prepare',
133
            '--no-interaction',
134
        ]);
135
    }
136
137
    #[Route('/update/_finish', name: 'update_finish', methods: ['POST'])]
138
    public function finish(Request $request): Response
139
    {
140
        $shopwarePath = $this->recoveryManager->getShopwareLocation();
141
142
        return $this->streamedCommandResponseGenerator->runJSON([
143
            $this->recoveryManager->getPhpBinary($request),
144
            '-dmemory_limit=1G',
145
            $shopwarePath . '/bin/console',
146
            'system:update:finish',
147
            '--no-interaction',
148
        ]);
149
    }
150
151
    /**
152
     * @see https://github.com/symfony/flex/pull/963
153
     */
154
    public function patchSymfonyFlex(string $shopwarePath): void
155
    {
156
        $optionsPhp = (string) file_get_contents($shopwarePath . '/vendor/symfony/flex/src/Options.php');
157
158
        $optionsPhp = str_replace(
159
            'return $this->io && $this->io->askConfirmation(sprintf(\'Cannot determine the state of the "%s" file, overwrite anyway? [y/N] \', $file), false);',
160
            'return $this->io && $this->io->askConfirmation(sprintf(\'Cannot determine the state of the "%s" file, overwrite anyway? [y/N] \', $file));',
161
            $optionsPhp
162
        );
163
164
        $optionsPhp = str_replace(
165
            'return $this->io && $this->io->askConfirmation(sprintf(\'File "%s" has uncommitted changes, overwrite? [y/N] \', $name), false);',
166
            'return $this->io && $this->io->askConfirmation(sprintf(\'File "%s" has uncommitted changes, overwrite? [y/N] \', $name));',
167
            $optionsPhp
168
        );
169
170
        file_put_contents($shopwarePath . '/vendor/symfony/flex/src/Options.php', $optionsPhp);
171
    }
172
173
    /**
174
     * @return array<string>
175
     */
176
    private function getLatestVersions(Request $request): array
177
    {
178
        if ($request->getSession()->has('latestVersions')) {
179
            $sessionValue = $request->getSession()->get('latestVersions');
180
            \assert(\is_array($sessionValue));
181
182
            return $sessionValue;
183
        }
184
185
        $shopwarePath = $this->recoveryManager->getShopwareLocation();
186
        \assert(\is_string($shopwarePath));
187
188
        $currentVersion = $this->recoveryManager->getCurrentShopwareVersion($shopwarePath);
189
        $latestVersions = $this->releaseInfoProvider->fetchUpdateVersions($currentVersion);
190
191
        $request->getSession()->set('latestVersions', $latestVersions);
192
193
        return $latestVersions;
194
    }
195
}
196