Hardening and bugfix pass over the server-to-server (remote) deploy path, plus admin fixes.
Server-to-server staging: full remote sync, Git-based deploys, and a hardening pass over the remote deploy path.
- Fix cache:clear failed: Could not open input file: bin/console after a successful remote_dump deploy: the rsync invocation in RsyncDeployer::deploy combined a --include-whitelist (/custom/apps, /custom/plugins, /public) with --exclude=* AND --delete-excluded — that doesn't mean "delete what no longer exists in the whitelisted dirs", it means "delete everything on the target that is not in the whitelist". The first deploy therefore wiped bin/, vendor/, src/, config/, var/, composer.json and .env* on the staging server, leaving only custom/apps, custom/plugins and public. The subsequent bin/console cache:clear step then crashed because the binary was gone. Removed --delete-excluded; plain --delete still removes files that no longer exist on production inside the whitelisted directories, which is what was actually intended
- Fix post-import SQL crashing with ERROR 1146 (42S02) Table 'db.dead_message' doesn't exist on Shopware 6.5+ databases: the unconditional TRUNCATE TABLE dead_message; references a Symfony Messenger table that was removed in Shopware 6.5 (replaced by messenger_messages). Both truncates are now wrapped in information_schema-conditional dynamic SQL so the deploy doesn't crash when a particular table is missing — messenger_messages is always truncated on a fresh 6.7 install, dead_message only on legacy databases that still carry it
- Fix ERROR 1227 (42000) Access denied; you need (at least one of) the SUPER or SET_USER_ID privilege(s) during the dump import on the staging server: every mysqldump from a production database with stored routines, triggers, views or events embeds DEFINER= clauses that the target server only accepts from a SUPER-privileged user — typical managed MySQL / shared-hosting setups don't grant SUPER. RemoteDatabaseImporter::applyDomainReplacements now strips DEFINER clauses (both the /*!50017 DEFINER=...*/ and the plain DEFINER=user@host`` forms, plus the SQL SECURITY DEFINER qualifier) before piping the dump into mysql on the target, so the target server falls back to the current connection user as DEFINER. Also adds set -o pipefail to the gunzip/sed pipeline so a failing step can't silently produce an empty intermediate file
- Fix mysqldump: [ERROR] unknown variable 'net-write-timeout=600' aborting every remote_dump against MySQL 8.0+: --net-write-timeout and --net-read-timeout are session variables for the mysql server and the mysql client but were never valid mysqldump options — MySQL ≤ 5.7 silently accepted them, 8.0 errors out. Removed both flags; the outer 4 h process timeout (DEFAULT_TIMEOUT_SECONDS) is the real safety net for a stalled dump
- Declare shopware/storefront: ~6.7.0 as a composer dependency — the StagingBarSubscriber introduced in 1.9.3 listens to StorefrontRenderEvent from that package, but without the explicit require the Shopware Store's PHPStan run cannot resolve the symbol and emits Class Shopware\Storefront\Event\StorefrontRenderEvent was not found while trying to analyse it
- Fix silent mysqldump failures during remote_dump: the production-side dump piped mysqldump | gzip > file without set -o pipefail, so bash returned the exit code of gzip (always 0) even when mysqldump failed with a privilege / connection / lock error. The result was a 20-byte gzip-header-only "dump" that imported as an empty schema and then crashed the post-import SQL with Table 'db.dead_message' doesn't exist. The dump shell now starts with set -o pipefail, and RemoteDatabaseImporter::dumpProduction enforces a 1 KB minimum gz-size floor + a gunzip -t integrity check before SCP-transferring the dump — a missing PROCESS/SELECT/LOCK TABLES privilege or any other mysqldump failure now aborts the deploy with a clear message instead of silently wiping the staging schema
- Fix TypeError on first request after install when the emz_remote_deploy Monolog channel is not registered: constructors of ProcessSshClient, RemoteDeploymentService, and RemoteDeployHandler now accept a nullable LoggerInterface and fall back to NullLogger — the previous promoted-property default never fired because the DI container injected an explicit null (services.xml uses on-invalid="null"), bypassing the parameter default
- Auto-register the emz_remote_deploy Monolog channel and rotating-file handler via prependExtensionConfig in the plugin bootstrap — Shopware does not auto-load Resources/config/packages/*.yaml from plugins, so the channel was previously never created and the file under var/log/emz_remote_deploy.log was missing
- Fix MySQL server has gone away (SQLSTATE[HY000] 2006) at the end of a remote deploy: the long-running rsync/mysqldump/scp/import steps let the production MySQL wait_timeout close the connection underneath the PHP process, so the very first DB write after a step (emz_pse_log insert, emz_pse_environment progress update) failed and aborted the deploy. Introduces ConnectionGuard::ensureAlive() which is invoked before each post-step DB access in RemoteDeploymentService::runStep() and in LogServiceProgressReporter::report()/setProgress(), transparently reconnecting on a dead socket
- Fix admin save dialog blocking remote_dump environments: the front-end onClickSave always called the validate_db endpoint which opens a direct pdo_mysql connection to the target DB. For remote_dump the target DB lives on the staging server (typically reachable only via 127.0.0.1 on that host through SSH) and cannot be opened from production, so the save dialog stayed stuck on dbConnectionError. Validation is now skipped when syncMode === 'remote_dump' — the SSH-based preflight covers this case before the actual deploy
- Add remoteHostId single-select to the environment-detail form (under the remote_dump card) — the field is required for remote_dump but was previously not editable in the admin UI, forcing operators to write the FK via SQL
- Add dedicated SSH-Hosts admin module (list + create/edit) with new fields name (display label, defaults to login on migration), description, sshIdentityFile (per-host SSH key path that overrides the container-level default), and sshPort — previously the only field was login and CRUD was only possible via direct SQL
- RemoteDeploymentService::buildTargetFromEnvironment now prefers $host->getSshIdentityFile() over the container-level $defaultIdentityFile so different staging targets can use different SSH keys (one key per customer)
- Migration Migration1763000000StagingEnvironment13HostFields adds the four new columns to emz_pse_host and back-fills name from login for pre-existing rows
- Architecture refactoring (round 2 — Interface-Hygiene)
- Extract SafetyCheckerInterface, RemoteDeploymentServiceInterface; both implementations are now final and decoratable via DI alias
- All consumers (Controller, Handler, 6 Console-Commands) hang at RemoteDeploymentServiceInterface
- Context is now propagated into buildTargetFromEnvironment (was hard-coded Context::createDefaultContext() — closes potential ACL bypass during host lookup)
- Remove dead urlOverride field from RemoteDeployOptions, RemoteDeploy message, controller dispatch, commands, and frontend API service (was never read by any service implementation)
- Drop duplicated hasFailingCheck() helper in orchestrator — SafetyCheckerInterface::hasAnyFailure() is the single source of truth
- SshTarget::fromLogin accepts IPv6 hosts in user@[2001:db8::1]:port form, validates via FILTER_VALIDATE_IP; userAtHost() brackets v6 addresses for SCP/rsync compatibility
- SSH ControlPath enforces a %C/%h/%p/%r uniqueness token even for custom paths to prevent cross-target socket collisions
- ProcessSshClient::rsync/copyTo/copyFrom now log to the dedicated emz_remote_deploy channel (was missing — rsync errors were invisible)
- RollbackTargetCommand follows the same --defaults-extra-file password handling as the other DB clients (was the last MYSQL_PWD= leak)
- AbstractRemoteDumpCommand accepts UUID strings with dashes in --env=
- Architecture refactoring (round 1)
- Extract RsyncDeployerInterface, RemoteDatabaseImporterInterface, PostImportConfiguratorInterface, RemotePostSetupInterface; sub-services are now final and decoratable via DI aliases
- ProgressReporter is passed as method argument (no more setter on the interfaces, no more instanceof leak in sub-services); setProgress is part of the interface
- SSH ControlMaster opt-in (default on), ControlPath defaults to sys_get_temp_dir()/emz_pse_cm_%C so a writable ~/.ssh is no longer required
- mysql import / post-import SQL pass the password via base64 + 0600 .cnf file, never on the command line — eliminates leaks via /proc//cmdline and the SSH-debug-log
- Mailer DSN is transported as base64 in the bash invocation, killing $VAR/backtick expansion attacks
- Test-suite migrated from anonymous subclasses to dedicated Fake* test doubles (FakeRsyncDeployer, FakeDatabaseImporter, FakePostImportConfigurator, FakeRemotePostSetup); behavior-oriented test names
- Add new remote_dump sync mode for server-to-server staging deployments
- rsync (whitelist custom/apps, custom/plugins, public) from production to target
- mysqldump on production with --single-transaction --quick --hex-blob for consistent, low-impact dumps
- SCP transfer of the gzipped dump
- mysql < import on the remote target (no slow tunnel-based INSERT loop)
- Post-import SQL: license host, secret cleanup (clear / delete), domain replacements (with //host-anchored boundary match), mailer/PayPal flags, message-queue truncation
- Remote .env.local MAILER_DSN override (default null://null) — script is sent via streamStdin, DSN passed as quoted argument (no shell-interpolation)
- Remote bin/console cache:clear
- Granular progress reporter writes step logs into emz_pse_log and updates dbProgress/filesProgress (5/25/45/70/95/100)
- Local + remote temp directories cleaned up via finally-blocks even if a step fails
- Add 6 console commands under emzpse:remote-dump:* (preflight, test-connection, files, database, run, rollback-target) with dry-run as default
- Add admin UI: sync-mode selector, target-server card, sensitive-data card with PayPal / Adyen / Klaviyo / Mailer presets, optional custom overrides repeater (expert mode)
- Add live progress notifications during remote-dump deploys via /remote_deploy/progress polling endpoint with frontend deduplication by log id
- Add EnvironmentSecretEncoder (AES-256-GCM, key derived from APP_SECRET via HKDF, per-row IV, minimum APP_SECRET length 16 bytes) — production passwords are migrated automatically to target_db_password_encrypted on plugin update; the plain-text database_password field is cleared after re-encryption
- Add dedicated Monolog channel emz_remote_deploy with rotating-file handler under var/log/emz_remote_deploy.log (14 day retention) — ProcessSshClient writes structured debug logs of every SSH/SCP/rsync invocation with truncated stdout/stderr
- Security hardening
- SSH StrictHostKeyChecking=yes is the default for both ssh and scp (was accept-new); known_hosts must be pre-populated
- Path validation on targetShopwarePath and remote temp directories (absolute path required, no .. traversal, no control characters, / rejected outright in rsync target)
- mysqldump SQL identifier whitelist on rollback-target table names + backtick escaping
- sed pattern/replacement escaping uses dedicated sedEscapePattern / sedEscapeReplacement helpers (was preg_quote/addcslashes)
- Migration column-name whitelist (^[A-Za-z_][A-Za-z0-9_]*$) before any DDL
- dryRun request flag is parsed via filter_var(..., FILTER_VALIDATE_BOOL) (no more (bool)"false" === true)
- urlOverride request parameter validated for max length, control characters, and parseable host
- RemoteDeployHandler re-throws after logging so failed deploys end up in the messenger failure transport
- Throwable::__toString() (with full trace) no longer ends up in emz_pse_log description; only getMessage() is persisted
- Fix remote_host_id FkField referencing StagingEnvironmentLogDefinition instead of StagingEnvironmentHostDefinition
- Fix StagingEnvironmentEntity::getDirectory / buildCommand raising a clear RuntimeException when the remoteHost association is not loaded (was an undefined-method call)
- Add primary key on emz_pse_environment_user mapping table (with deduplication of pre-existing rows)
- Remove dead Services\Helper\HelperService container entry
- Add PHPUnit test suite (92 tests, ~91% line coverage on new code) with FakeSshClient / FakeProcessRunner test doubles, including controller, security-hardening and edge-case tests
- Fix Cannot delete or update a parent row (FK constraint 1451) when clearing a staging database with referential integrity by wrapping DROP TABLE with unsafeSqlWrapper
- Remove leftover debug echo of the dump command in DatabaseService::cloneDatabase
- Restore red staging topbar in storefront and administration (regression fix from 1.9.2)
- Restore STAGING_ENVIRONMENT=true flag in synced .env files
- Restore StagingBarSubscriber and is_staging_environment API endpoint
- Compatible for Shopware 6.5
- Bump shopware/core dependency to ~6.5.0
- Compatible for Shopware 6.6
- Migrate administration template slots to Vue 3 shorthand syntax (#slot-name)
- Bump shopware/core dependency to ~6.6.0
- Compatible for Shopware 6.7
- Migrate administration form fields to Vue 3 binding syntax (v-model)
- Bump shopware/core dependency to ~6.7.0
Add SalesChannel Domain Mapping
- Configuration for RabbitMQ and Elasticsearch for the staging environment if in use
- Export mode with new configurations
- Add hint topbar in storefront and administration
- Add postfix for env variable SHOPWARE_ES_INDEX_PREFIX
- Add hint topbar in storefront and administration
- Add postfix for env variable SHOPWARE_ES_INDEX_PREFIX
Shopware 6.7 compatibility implemented
- Bugfixing: Shop not available
- Bugfixing: Shop not available
- Added Shopware 6.5 Compatibility
- add miscellaneous console commands
- Environment synchronization happens via queue
- add progressbars for synchronization
- add links to to storefront and admin of environment
- add Logs page
- add automatic activation of paypal sandbox
- add selection of users to be included in environment
- ui/ux-improvments
Add .env.local which caused errors in enviroments when missing
- Fix for Update Config Service
Fix compile error while file sync
- Granted rights to config/jwt/public.pem and private.pem
- Symlink from Staging public is created on the Production public
- Compatible for view database columns in DatabaseSyncService
- Add database limit to increase synchronization
- Exclude individual database tables
- Add GDPR to remove all relevant customer data
- Symlink created
- Optionally deactivate email delivery
- Fix database synchronization error
- Fix for 6.4.7
- Change menu position to extensions subnavigation
- Compatibility for Shopware 6.4
- Split the console commands, to run them individually
- Added console commands to sync existing staging environments
- A staging environment can only be deleted if the database and folder are empty