Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
6f141dc
feat: add tracking
shyim Aug 19, 2025
e0bc008
feat: add tests for one-time tasks table creation and MySQL version v…
shyim Aug 20, 2025
c538c8e
fix: initialize responses array in TrackingService
shyim Aug 20, 2025
2dbc751
fix: add error handling for tracking MySQL version and deployment hel…
shyim Aug 20, 2025
2efedc7
fix: update RunCommand to track MySQL version conditionally and adjus…
shyim Aug 20, 2025
50f0692
fix: add CLAUDE_CONFIG_DIR to container environment in devcontainer c…
shyim Aug 20, 2025
dc0d8f0
fix: add CLAUDE_CONFIG_DIR and RW_DIRECTORIES to container environmen…
shyim Aug 20, 2025
d88094e
fix: refactor tracking in RunCommand to ensure PHP and MySQL versions…
shyim Aug 20, 2025
ff29402
fix: remove unused variable comment in track method to improve code c…
shyim Aug 20, 2025
a184281
fix: ensure id is set correctly in getId method of TrackingService
shyim Aug 20, 2025
a9c284f
fix: add check for id existence before setting it in persistId method…
shyim Aug 20, 2025
383f45e
fix: update tracking endpoint in track method of TrackingService to u…
shyim Aug 20, 2025
7d66169
feat: add ConsoleOutputInterface and DefaultConsoleOutput for improve…
shyim Dec 24, 2025
01df54c
feat: disable telemetry during test runs by adding DO_NOT_TRACK varia…
shyim Dec 24, 2025
30cbd25
feat: update paths in integration.yml to use static-plugins directory…
shyim Dec 24, 2025
d36337e
feat: update integration.yml and shopware-deployment-helper to suppor…
shyim Dec 24, 2025
94a06f0
fix: remove devcontainer.json
shyim Dec 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ env:
APP_ENV: prod
SALES_CHANNEL_URL: http://localhost:8000
DATABASE_URL: mysql://root@127.0.0.1/shopware
# Disable our own telemetry to not fill test runs
DO_NOT_TRACK: 1

jobs:
installation:
Expand All @@ -37,10 +39,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
with:
path: ./custom/plugins/deployment-helper
path: ./custom/static-plugins/deployment-helper

- name: Set fake version into deployment helper
run: composer -d custom/plugins/deployment-helper config version 999.9.9
run: composer -d custom/static-plugins/deployment-helper config version 999.9.9

- name: Install Deployment Helper
run: composer require --dev 'shopware/deployment-helper:*'
Expand Down Expand Up @@ -86,10 +88,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
with:
path: ./custom/plugins/deployment-helper
path: ./custom/static-plugins/deployment-helper

- name: Set fake version into deployment helper
run: composer -d custom/plugins/deployment-helper config version 999.9.9
run: composer -d custom/static-plugins/deployment-helper config version 999.9.9

- name: Install Deployment Helper
run: composer require --dev 'shopware/deployment-helper:*'
Expand Down Expand Up @@ -146,10 +148,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
with:
path: ./custom/plugins/deployment-helper
path: ./custom/static-plugins/deployment-helper

- name: Set fake version into deployment helper
run: composer -d custom/plugins/deployment-helper config version 999.9.9
run: composer -d custom/static-plugins/deployment-helper config version 999.9.9

- name: Install Deployment Helper
run: composer require --dev 'shopware/deployment-helper:*'
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
/.idea
/.claude
/vendor
/composer.lock
/var/cache
/build/coverage
/coverage
/.junie
2 changes: 1 addition & 1 deletion bin/shopware-deployment-helper
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ $includables = [
__DIR__ . '/vendor/autoload.php',
];

if (str_contains(__DIR__, 'custom/plugins')) {
if (str_contains(__DIR__, 'custom/plugins') || str_contains(__DIR__, 'custom/static-plugins')) {
$includables[] = __DIR__ . '/../../../../vendor/autoload.php';
}

Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"php": ">=8.2",
"ext-dom": "*",
"ext-pdo": "*",
"ext-sockets": "*",
"digilist/dependency-graph": ">=0.4.1",
"doctrine/dbal": "^3.0 || ^4.0",
"symfony/config": "^7.0 || ^6.0",
Expand Down
23 changes: 17 additions & 6 deletions src/Command/RunCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Shopware\Deployment\Services\HookExecutor;
use Shopware\Deployment\Services\InstallationManager;
use Shopware\Deployment\Services\ShopwareState;
use Shopware\Deployment\Services\TrackingService;
use Shopware\Deployment\Services\UpgradeManager;
use Shopware\Deployment\Struct\RunConfiguration;
use Symfony\Component\Console\Attribute\AsCommand;
Expand All @@ -27,6 +28,7 @@ public function __construct(
private readonly UpgradeManager $upgradeManager,
private readonly HookExecutor $hookExecutor,
private readonly EventDispatcherInterface $eventDispatcher,
private readonly TrackingService $trackingService,
) {
parent::__construct();
}
Expand All @@ -41,6 +43,7 @@ protected function configure(): void

protected function execute(InputInterface $input, OutputInterface $output): int
{
$installed = $this->state->isInstalled();
$timeout = $input->getOption('timeout');

$config = new RunConfiguration(
Expand All @@ -50,18 +53,26 @@ protected function execute(InputInterface $input, OutputInterface $output): int
forceReinstallation: EnvironmentHelper::getVariable('SHOPWARE_DEPLOYMENT_FORCE_REINSTALL', '0') === '1',
);

$installed = $this->state->isInstalled();

if ($config->forceReinstallation && $this->state->getPreviousVersion() === 'unknown') {
$installed = false;
}

$this->hookExecutor->execute(HookExecutor::HOOK_PRE);

if ($installed) {
$this->upgradeManager->run($config, $output);
} else {
$this->installationManager->run($config, $output);
try {
if ($installed) {
$this->upgradeManager->run($config, $output);
} else {
$this->installationManager->run($config, $output);
}
} finally {
$this->trackingService->track('php_version', [
'php_version' => \PHP_MAJOR_VERSION . '.' . \PHP_MINOR_VERSION,
]);

$this->trackingService->track('mysql_version', [
'mysql_version' => $this->state->getMySqlVersion(),
]);
}

$this->eventDispatcher->dispatch(new PostDeploy($config, $output));
Expand Down
12 changes: 12 additions & 0 deletions src/Helper/ConsoleOutputInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Shopware\Deployment\Helper;

interface ConsoleOutputInterface
{
public function writeStdout(string $message): void;

public function writeStderr(string $message): void;
}
18 changes: 18 additions & 0 deletions src/Helper/DefaultConsoleOutput.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Shopware\Deployment\Helper;

class DefaultConsoleOutput implements ConsoleOutputInterface
{
public function writeStdout(string $message): void
{
fwrite(\STDOUT, $message);
}

public function writeStderr(string $message): void
{
fwrite(\STDERR, $message);
}
}
70 changes: 31 additions & 39 deletions src/Helper/ProcessHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public function __construct(
#[Autowire('%kernel.project_dir%')]
private readonly string $projectDir,
private ?float $timeout = null,
private readonly ConsoleOutputInterface $output = new DefaultConsoleOutput(),
) {
$this->timeout = $this->validateTimeout($timeout ?? (float) EnvironmentHelper::getVariable('SHOPWARE_DEPLOYMENT_TIMEOUT', '60'));
}
Expand All @@ -32,17 +33,11 @@ public function shell(array $command): void

$startTime = $this->printPreStart($command);

if (\function_exists('stream_isatty') && stream_isatty(\STDOUT)) {
if ($this->output instanceof DefaultConsoleOutput && \function_exists('stream_isatty') && stream_isatty(\STDOUT)) {
$process->setTty(true);
}

$process->run(function (string $type, string $buffer): void {
if ($type === Process::ERR) {
fwrite(\STDERR, $buffer);
} else {
fwrite(\STDOUT, $buffer);
}
});
$process->run($this->output(...));

if (!$process->isSuccessful()) {
throw new \RuntimeException('Execution of ' . implode(' ', $command) . ' failed');
Expand All @@ -63,17 +58,11 @@ public function run(array $args): void

$startTime = $this->printPreStart($completeCmd);

if (\function_exists('stream_isatty') && stream_isatty(\STDOUT)) {
if ($this->output instanceof DefaultConsoleOutput && \function_exists('stream_isatty') && stream_isatty(\STDOUT)) {
$process->setTty(true);
}

$process->run(function (string $type, string $buffer): void {
if ($type === Process::ERR) {
fwrite(\STDERR, $buffer);
} else {
fwrite(\STDOUT, $buffer);
}
});
$process->run($this->output(...));

if (!$process->isSuccessful()) {
throw new \RuntimeException('Execution of ' . implode(' ', $args) . ' failed');
Expand All @@ -100,17 +89,11 @@ public function runAndTail(string $code): void
$process = new Process(['sh', '-c', $code], $this->projectDir);
$process->setTimeout($this->timeout);

if (\function_exists('stream_isatty') && stream_isatty(\STDOUT)) {
if ($this->output instanceof DefaultConsoleOutput && \function_exists('stream_isatty') && stream_isatty(\STDOUT)) {
$process->setTty(true);
}

$process->run(function (string $type, string $buffer): void {
if ($type === Process::ERR) {
fwrite(\STDERR, $buffer);
} else {
fwrite(\STDOUT, $buffer);
}
});
$process->run($this->output(...));

if (!$process->isSuccessful()) {
throw new \RuntimeException('Execution of ' . $originalCode . ' failed');
Expand Down Expand Up @@ -140,6 +123,15 @@ public function setTimeout(?float $timeout): static
return $this;
}

private function output(string $type, string $buffer): void
{
if ($type === Process::ERR) {
$this->output->writeStderr($buffer);
} else {
$this->output->writeStdout($buffer);
}
}

/**
* @param array<string> $cmd
*/
Expand All @@ -148,14 +140,14 @@ private function printPreStart(array $cmd): float
$cmdString = implode(' ', $cmd);
$startTime = microtime(true);

fwrite(\STDOUT, \PHP_EOL);
fwrite(\STDOUT, "=================================================\n");
fwrite(\STDOUT, "============== [deployment-helper] ==============\n");
fwrite(\STDOUT, "=================================================\n");
fwrite(\STDOUT, \sprintf("Start: %s\n", $cmdString));
fwrite(\STDOUT, \sprintf("Time limit: %s seconds\n", $this->timeout));
fwrite(\STDOUT, "=================================================\n");
fwrite(\STDOUT, \PHP_EOL);
$this->output->writeStdout(\PHP_EOL);
$this->output->writeStdout("=================================================\n");
$this->output->writeStdout("============== [deployment-helper] ==============\n");
$this->output->writeStdout("=================================================\n");
$this->output->writeStdout(\sprintf("Start: %s\n", $cmdString));
$this->output->writeStdout(\sprintf("Time limit: %s seconds\n", $this->timeout));
$this->output->writeStdout("=================================================\n");
$this->output->writeStdout(\PHP_EOL);

return $startTime;
}
Expand All @@ -165,17 +157,17 @@ private function printPreStart(array $cmd): float
*/
private function printPostStart(array $cmd, float $startTime): void
{
fwrite(\STDOUT, "=================================================\n");
fwrite(\STDOUT, "============== [deployment-helper] ==============\n");
fwrite(\STDOUT, "=================================================\n");
fwrite(\STDOUT, \sprintf("End: %s\n", implode(' ', $cmd)));
fwrite(\STDOUT, \sprintf(
$this->output->writeStdout("=================================================\n");
$this->output->writeStdout("============== [deployment-helper] ==============\n");
$this->output->writeStdout("=================================================\n");
$this->output->writeStdout(\sprintf("End: %s\n", implode(' ', $cmd)));
$this->output->writeStdout(\sprintf(
"> Time: %sms\n",
number_format((microtime(true) - $startTime) * 1000, 2, '.', ''),
));

fwrite(\STDOUT, "=================================================\n");
fwrite(\STDOUT, \PHP_EOL);
$this->output->writeStdout("=================================================\n");
$this->output->writeStdout(\PHP_EOL);
}

/**
Expand Down
6 changes: 6 additions & 0 deletions src/Services/InstallationManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public function __construct(
private readonly HookExecutor $hookExecutor,
private readonly ProjectConfiguration $configuration,
private readonly AccountService $accountService,
private readonly TrackingService $trackingService,
) {
}

Expand Down Expand Up @@ -56,7 +57,12 @@ public function run(RunConfiguration $configuration, OutputInterface $output): v
$additionalInstallParameters[] = '--drop-database';
}

$took = microtime(true);
$this->processHelper->console(['system:install', '--create-database', '--shop-locale=' . $shopLocale, '--shop-currency=' . $shopCurrency, '--force', ...$additionalInstallParameters]);

$this->trackingService->persistId();
$this->trackingService->track('installed', ['took' => microtime(true) - $took, 'shopware_version' => $this->state->getCurrentVersion()]);

$this->processHelper->console(['user:create', $adminUser, '--password=' . $adminPassword]);

$this->processHelper->console(['messenger:setup-transports']);
Expand Down
46 changes: 46 additions & 0 deletions src/Services/ShopwareState.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,50 @@ public function disableMaintenanceMode(): void
$this->connection->executeStatement('UPDATE sales_channel SET maintenance = ? WHERE id = UNHEX(?)', [$maintenance, $id]);
}
}

public function getMySqlVersion(): string
{
$version = $this->extractMySQLVersion($this->connection->fetchOne('SELECT VERSION()'));

if (isset($version['mariadb'])) {
return 'mariadb-' . $version['mariadb'];
}

if (isset($version['mysql'])) {
return 'mysql-' . $version['mysql'];
}

return 'unknown';
}

/**
* @return array{mysql?: string, mariadb?: string}
*/
private function extractMySQLVersion(string $versionString): array
{
if (stripos($versionString, 'mariadb') === false) {
$pos = strpos($versionString, '-');
if (\is_int($pos)) {
$versionString = substr($versionString, 0, $pos);
}

return ['mysql' => $versionString];
}

return ['mariadb' => self::getVersionNumber($versionString)];
}

private static function getVersionNumber(string $versionString): string
{
$match = preg_match(
'/^(?:5\.5\.5-)?(mariadb-)?(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)/i',
$versionString,
$versionParts,
);
if ($match === 0 || \is_bool($match)) {
throw new \RuntimeException(\sprintf('Invalid version string: %s', $versionString));
}

return $versionParts['major'] . '.' . $versionParts['minor'];
}
}
Loading