diff --git a/src/Command/RunCommand.php b/src/Command/RunCommand.php index 2745585..87e7f97 100644 --- a/src/Command/RunCommand.php +++ b/src/Command/RunCommand.php @@ -6,9 +6,11 @@ use Shopware\Deployment\Services\InstallationManager; use Shopware\Deployment\Services\ShopwareState; use Shopware\Deployment\Services\UpgradeManager; +use Shopware\Deployment\Struct\RunConfiguration; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; #[AsCommand('run', description: 'Install or Update Shopware')] @@ -23,16 +25,28 @@ public function __construct( parent::__construct(); } + protected function configure(): void + { + $this->addOption('skip-theme-compile', null, InputOption::VALUE_OPTIONAL, 'Skip theme compile (should be used when the Theme has been compiled before in the CI/CD)', false); + $this->addOption('skip-asset-install', null, InputOption::VALUE_OPTIONAL, 'Skip asset install (should be used when the Assets has been copied before in the CI/CD)', false); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $config = new RunConfiguration( + skipThemeCompile: (bool) $input->getOption('skip-theme-compile'), + skipAssetInstall: (bool) $input->getOption('skip-asset-install'), + ); + $installed = $this->state->isInstalled(); $this->hookExecutor->execute(HookExecutor::PRE); if ($installed) { - $this->upgradeManager->run($output); + $this->upgradeManager->run($config, $output); } else { - $this->installationManager->run($output); + $this->installationManager->run($config, $output); } $this->hookExecutor->execute(HookExecutor::POST); diff --git a/src/Services/InstallationManager.php b/src/Services/InstallationManager.php index d15cee8..9d804ad 100644 --- a/src/Services/InstallationManager.php +++ b/src/Services/InstallationManager.php @@ -5,6 +5,7 @@ use Doctrine\DBAL\Connection; use Shopware\Deployment\Helper\EnvironmentHelper; use Shopware\Deployment\Helper\ProcessHelper; +use Shopware\Deployment\Struct\RunConfiguration; use Symfony\Component\Console\Output\OutputInterface; class InstallationManager @@ -18,7 +19,7 @@ public function __construct( private readonly HookExecutor $hookExecutor, ) {} - public function run(OutputInterface $output): void + public function run(RunConfiguration $configuration, OutputInterface $output): void { $output->writeln('Shopware is not installed, starting installation'); @@ -30,20 +31,41 @@ public function run(OutputInterface $output): void $adminPassword = EnvironmentHelper::getVariable('INSTALL_ADMIN_PASSWORD', 'shopware'); $appUrl = EnvironmentHelper::getVariable('APP_URL', 'http://localhost'); - $this->processHelper->console(['system:install', '--create-database', '--shop-locale=' . $shopLocale, '--shop-currency=' . $shopCurrency, '--force']); + $additionalInstallParameters = []; + + if ($configuration->skipThemeCompile) { + $additionalInstallParameters[] = '--no-assign-theme'; + } + + if ($configuration->skipAssetInstall) { + $additionalInstallParameters[] = '--skip-assets-install'; + } + + $this->processHelper->console(['system:install', '--create-database', '--shop-locale=' . $shopLocale, '--shop-currency=' . $shopCurrency, '--force', ...$additionalInstallParameters]); $this->processHelper->console(['user:create', (string) $adminUser, '--password=' . $adminPassword]); if ($this->state->isStorefrontInstalled()) { $this->removeExistingHeadlessSalesChannel(); $this->processHelper->console(['sales-channel:create:storefront', '--name=Storefront', '--url=' . $appUrl]); - $this->processHelper->console(['theme:change', '--all', 'Storefront']); + + $themeChangeParameters = []; + if ($configuration->skipThemeCompile) { + $themeChangeParameters[] = '--no-compile'; + } + + $this->processHelper->console(['theme:change', '--all', 'Storefront', ...$themeChangeParameters]); + + if ($configuration->skipThemeCompile) { + $this->processHelper->console(['theme:dump']); + } } + $this->state->disableFirstRunWizard(); $this->state->setVersion($this->state->getCurrentVersion()); $this->processHelper->console(['plugin:refresh']); - $this->pluginHelper->installPlugins(); - $this->pluginHelper->updatePlugins(); + $this->pluginHelper->installPlugins($configuration->skipAssetInstall); + $this->pluginHelper->updatePlugins($configuration->skipAssetInstall); $this->appHelper->installApps(); $this->appHelper->updateApps(); diff --git a/src/Services/PluginHelper.php b/src/Services/PluginHelper.php index eb3eaa7..610e901 100644 --- a/src/Services/PluginHelper.php +++ b/src/Services/PluginHelper.php @@ -17,8 +17,14 @@ public function __construct( private readonly ProjectConfiguration $configuration, ) {} - public function installPlugins(): void + public function installPlugins(bool $skipAssetInstall = false): void { + $additionalParameters = []; + + if ($skipAssetInstall) { + $additionalParameters[] = '--skip-asset-build'; + } + foreach ($this->pluginLoader->all() as $plugin) { if (!$this->configuration->isExtensionManaged($plugin['name'])) { continue; @@ -30,17 +36,23 @@ public function installPlugins(): void // plugin is installed, but not active if ($plugin['installedAt'] !== null) { - $this->processHelper->console(['plugin:activate', $plugin['name']]); + $this->processHelper->console(['plugin:activate', $plugin['name'], ...$additionalParameters]); continue; } - $this->processHelper->console(['plugin:install', $plugin['name'], '--activate']); + $this->processHelper->console(['plugin:install', $plugin['name'], '--activate', ...$additionalParameters]); } } - public function updatePlugins(): void + public function updatePlugins(bool $skipAssetInstall = false): void { + $additionalParameters = []; + + if ($skipAssetInstall) { + $additionalParameters[] = '--skip-asset-build'; + } + foreach ($this->pluginLoader->all() as $plugin) { if (!$this->configuration->isExtensionManaged($plugin['name'])) { continue; @@ -50,7 +62,7 @@ public function updatePlugins(): void continue; } - $this->processHelper->console(['plugin:update', $plugin['name']]); + $this->processHelper->console(['plugin:update', $plugin['name'], ...$additionalParameters]); } } } diff --git a/src/Services/ShopwareState.php b/src/Services/ShopwareState.php index 9abd927..fba0ede 100644 --- a/src/Services/ShopwareState.php +++ b/src/Services/ShopwareState.php @@ -52,6 +52,12 @@ public function setVersion(string $version): void } } + public function disableFirstRunWizard(): void + { + $payload = json_encode(['_value' => '2021-01-01 00:00:00'], JSON_THROW_ON_ERROR); + $this->connection->executeStatement('INSERT INTO system_config (id, configuration_key, configuration_value, sales_channel_id, created_at) VALUES (0x0353f2502acd5dbdfe797c1cc4af9bfc, "core.frw.completedAt", ?, NULL, NOW())', [$payload]); + } + public function getCurrentVersion(): string { if (InstalledVersions::isInstalled('shopware/platform')) { diff --git a/src/Services/UpgradeManager.php b/src/Services/UpgradeManager.php index 20dfcfe..f2c9584 100644 --- a/src/Services/UpgradeManager.php +++ b/src/Services/UpgradeManager.php @@ -3,6 +3,7 @@ namespace Shopware\Deployment\Services; use Shopware\Deployment\Helper\ProcessHelper; +use Shopware\Deployment\Struct\RunConfiguration; use Symfony\Component\Console\Output\OutputInterface; class UpgradeManager @@ -16,7 +17,7 @@ public function __construct( private readonly OneTimeTasks $oneTimeTasks, ) {} - public function run(OutputInterface $output): void + public function run(RunConfiguration $configuration, OutputInterface $output): void { $this->hookExecutor->execute(HookExecutor::PRE_UPDATE); @@ -24,18 +25,27 @@ public function run(OutputInterface $output): void if ($this->state->getPreviousVersion() !== $this->state->getCurrentVersion()) { $output->writeln(sprintf('Updating Shopware from %s to %s', $this->state->getPreviousVersion(), $this->state->getCurrentVersion())); - $this->processHelper->console(['system:update:finish']); + + $additionalUpdateParameters = []; + + if ($configuration->skipAssetInstall) { + $additionalUpdateParameters[] = '--skip-asset-build'; + } + + $this->processHelper->console(['system:update:finish', ...$additionalUpdateParameters]); $this->state->setVersion($this->state->getCurrentVersion()); } $this->processHelper->console(['plugin:refresh']); - $this->pluginHelper->installPlugins(); - $this->pluginHelper->updatePlugins(); + $this->pluginHelper->installPlugins($configuration->skipAssetInstall); + $this->pluginHelper->updatePlugins($configuration->skipAssetInstall); $this->appHelper->installApps(); $this->appHelper->updateApps(); - $this->processHelper->console(['theme:compile', '--active-only']); + if (!$configuration->skipThemeCompile) { + $this->processHelper->console(['theme:compile', '--active-only']); + } $this->oneTimeTasks->execute($output); diff --git a/src/Struct/RunConfiguration.php b/src/Struct/RunConfiguration.php new file mode 100644 index 0000000..b5d3ad4 --- /dev/null +++ b/src/Struct/RunConfiguration.php @@ -0,0 +1,8 @@ +run($this->createMock(OutputInterface::class)); + $manager->run(new RunConfiguration(), $this->createMock(OutputInterface::class)); } public function testRunNoStorefront(): void @@ -56,6 +57,36 @@ public function testRunNoStorefront(): void $this->createMock(HookExecutor::class), ); - $manager->run($this->createMock(OutputInterface::class)); + $manager->run(new RunConfiguration(), $this->createMock(OutputInterface::class)); + } + + public function testRunDisabledAssetCopyAndThemeCompile(): void + { + $state = $this->createMock(ShopwareState::class); + $state->method('isStorefrontInstalled') + ->willReturn(true); + + $processHelper = $this->createMock(ProcessHelper::class); + $consoleCommands = []; + + $processHelper + ->method('console') + ->willReturnCallback(function (array $command) use (&$consoleCommands): void { + $consoleCommands[] = $command; + }); + + $manager = new InstallationManager( + $state, + $this->createMock(Connection::class), + $processHelper, + $this->createMock(PluginHelper::class), + $this->createMock(AppHelper::class), + $this->createMock(HookExecutor::class), + ); + + $manager->run(new RunConfiguration(true, true), $this->createMock(OutputInterface::class)); + + static::assertCount(6, $consoleCommands); + static::assertSame(['system:install', '--create-database', '--shop-locale=en-GB', '--shop-currency=EUR', '--force', '--no-assign-theme', '--skip-assets-install'], $consoleCommands[0]); } } diff --git a/tests/Services/PluginHelperTest.php b/tests/Services/PluginHelperTest.php index 809a426..695bf37 100644 --- a/tests/Services/PluginHelperTest.php +++ b/tests/Services/PluginHelperTest.php @@ -58,6 +58,20 @@ public function testInstallNotInstalled(): void $helper->installPlugins(); } + public function testInstallNotInstalledSkipAssets(): void + { + $processHelper = $this->createMock(ProcessHelper::class); + $processHelper->expects(static::once())->method('console')->with(['plugin:install', 'TestPlugin', '--activate', '--skip-asset-build']); + + $helper = new PluginHelper( + $this->getPluginLoader(active:false, installedAt: null), + $processHelper, + new ProjectConfiguration(), + ); + + $helper->installPlugins(true); + } + public function testInstalledButNotActive(): void { $processHelper = $this->createMock(ProcessHelper::class); @@ -117,6 +131,20 @@ public function testUpdate(): void $helper->updatePlugins(); } + public function testUpdateDisableAssetBuild(): void + { + $processHelper = $this->createMock(ProcessHelper::class); + $processHelper->expects(static::once())->method('console')->with(['plugin:update', 'TestPlugin', '--skip-asset-build']); + + $helper = new PluginHelper( + $this->getPluginLoader(upgradeVersion: '1.0.1'), + $processHelper, + new ProjectConfiguration(), + ); + + $helper->updatePlugins(true); + } + public function getPluginLoader(bool $active = true, ?string $installedAt = 'test', ?string $upgradeVersion = null): PluginLoader&MockObject { $loader = $this->createMock(PluginLoader::class); diff --git a/tests/Services/ShopwareStateTest.php b/tests/Services/ShopwareStateTest.php index 78772b7..8f96494 100644 --- a/tests/Services/ShopwareStateTest.php +++ b/tests/Services/ShopwareStateTest.php @@ -90,6 +90,16 @@ public function testSetVersion(): void $this->state->setVersion('v1.0.0'); } + public function testDisableFRW(): void + { + $this->connection + ->expects(static::once()) + ->method('executeStatement') + ->with('INSERT INTO system_config (id, configuration_key, configuration_value, sales_channel_id, created_at) VALUES (0x0353f2502acd5dbdfe797c1cc4af9bfc, "core.frw.completedAt", ?, NULL, NOW())', ['{"_value":"2021-01-01 00:00:00"}']); + + $this->state->disableFirstRunWizard(); + } + public function testSetVersionInsert(): void { $this->connection diff --git a/tests/Services/UpgradeManagerTest.php b/tests/Services/UpgradeManagerTest.php index efe498c..a605625 100644 --- a/tests/Services/UpgradeManagerTest.php +++ b/tests/Services/UpgradeManagerTest.php @@ -11,9 +11,11 @@ use Shopware\Deployment\Services\PluginHelper; use Shopware\Deployment\Services\ShopwareState; use Shopware\Deployment\Services\UpgradeManager; +use Shopware\Deployment\Struct\RunConfiguration; use Symfony\Component\Console\Output\OutputInterface; #[CoversClass(UpgradeManager::class)] +#[CoversClass(RunConfiguration::class)] class UpgradeManagerTest extends TestCase { public function testRun(): void @@ -37,7 +39,7 @@ public function testRun(): void $oneTimeTasks, ); - $manager->run($this->createMock(OutputInterface::class)); + $manager->run(new RunConfiguration(), $this->createMock(OutputInterface::class)); } public function testRunUpdatesVersion(): void @@ -67,6 +69,48 @@ public function testRunUpdatesVersion(): void $this->createMock(OneTimeTasks::class), ); - $manager->run($this->createMock(OutputInterface::class)); + $manager->run(new RunConfiguration(), $this->createMock(OutputInterface::class)); + } + + public function testRunUpdatesVersionNoAssetCompile(): void + { + $state = $this->createMock(ShopwareState::class); + $state + ->expects(static::exactly(3)) + ->method('getCurrentVersion') + ->willReturn('1.0.0'); + + $state + ->expects(static::exactly(2)) + ->method('getPreviousVersion') + ->willReturn('0.0.0'); + + $state + ->expects(static::once()) + ->method('setVersion') + ->with('1.0.0'); + + $processHelper = $this->createMock(ProcessHelper::class); + $consoleCommands = []; + + $processHelper + ->method('console') + ->willReturnCallback(function (array $command) use (&$consoleCommands): void { + $consoleCommands[] = $command; + }); + + $manager = new UpgradeManager( + $state, + $processHelper, + $this->createMock(PluginHelper::class), + $this->createMock(AppHelper::class), + $this->createMock(HookExecutor::class), + $this->createMock(OneTimeTasks::class), + ); + + $manager->run(new RunConfiguration(true, true), $this->createMock(OutputInterface::class)); + + static::assertCount(2, $consoleCommands); + static::assertSame(['system:update:finish', '--skip-asset-build'], $consoleCommands[0]); } }