Passed
Push — trunk ( 47d189...bed162 )
by Christian
10:24 queued 13s
created

UpdateController::getLatestVersions()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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