CLI-1631: handling silent failure of db failure#2007
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2007 +/- ##
=========================================
Coverage 92.44% 92.45%
Complexity 1960 1960
=========================================
Files 123 123
Lines 7109 7114 +5
=========================================
+ Hits 6572 6577 +5
Misses 537 537 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Try the dev build for this PR: https://acquia-cli.s3.amazonaws.com/build/pr/2007/acli.phar |
There was a problem hiding this comment.
Pull request overview
This PR aims to prevent “silent success” when importing gzipped MySQL dumps by ensuring pipeline failures (e.g., pv/gunzip) propagate a non-zero exit code during pull/push database imports.
Changes:
- Adds
pipefailto the local DB import pipeline used bypullcommands. - Adds
pipefailto the remote DB import pipeline used bypush:database. - Updates PHPUnit expectations for the updated shell commands.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
src/Command/Pull/PullCommandBase.php |
Updates the local import pipeline command to try to propagate failures from piped commands. |
src/Command/Push/PushDatabaseCommand.php |
Updates the remote import pipeline command to try to propagate failures from piped commands. |
tests/phpunit/src/Commands/Pull/PullCommandTestBase.php |
Updates test expectations for the local import command string. |
tests/phpunit/src/Commands/Push/PushDatabaseCommandTest.php |
Updates test expectations for the remote import command string. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Testing Evidance , As exact scenario of mimicing this command failure can not be demonstrated , I have given the proof of regression testing that even with this happy path will not be tempered. Similar solution is also implemented by dane before for other file #1770 |
| { | ||
| $this->logger->debug("Importing $remoteDumpFilepath to MySQL on remote machine"); | ||
| $command = "pv $remoteDumpFilepath --bytes --rate | gunzip | MYSQL_PWD=$database->password mysql --host={$this->getHostFromDatabaseResponse($environment, $database)} --user=$database->user_name {$this->getNameFromDatabaseResponse($database)}"; | ||
| $command = "bash -o pipefail -c 'pv $remoteDumpFilepath --bytes --rate | gunzip | MYSQL_PWD=$database->password mysql --host={$this->getHostFromDatabaseResponse($environment, $database)} --user=$database->user_name {$this->getNameFromDatabaseResponse($database)}'"; |
There was a problem hiding this comment.
If $database->password (or any other interpolated value) contains a single quote, this will break the bash command or cause unexpected behaviour. The original had this same issue, but wrapping in bash -c '...' makes it more brittle.
There was a problem hiding this comment.
or you can use escapeshellarg() to wrap each value in its own properly-escaped single quotes,
|
Also i see proposed changes in the PR differs from what is actually implemented, could you please update the description |
|
Testing result after accommodating @deepakmishra2 suggestion Box Requirements Checker
[OK] Your system is ready to run the application. Acquia CLI version: dev-2007/merge-fb51b124f7755b94b4c0ceef1d5530d2fc4ce0a6 Choose a Cloud Platform environment [Stage, devxpipe10novstage (branch: 2024-10-25.2-multisite)]:
[debug] Using environment Dev29apr d5aa0229-871d-4fcf-8251-e680f78ed0b9
[INFO] Using a database backup that is 3 hours old. Backup #9516774 was created at Mon Jun 15 8:57:13 UTC 2026. ! [NOTE] Cloud IDE only supports importing into the default Drupal database. Acquia CLI will import the NON-DEFAULT Dropping tables from database drupal [notice] Command: 'which' 'drush' [Exit: 0] |

Motivation
This PR addresses silent failures in database import operations where pipeline commands could fail without being detected, potentially leading to incomplete or corrupted database imports.
Fixes #1631
Proposed changes
bash -o pipefail -c "..."to wrap database import pipeline commands, ensuring that if any command in the pipeline (pv, gunzip, or mysql) fails, the entire pipeline fails properlyimportDatabaseDumpmethod in PullCommandBase.php for local database imports:bash -o pipefail -cwith double-quoted command string$envarray parameter toexecuteFromCmd()$VAR) for proper substitution from environmentcreateMySqlDumpOnLocalmethod in CommandBase.php for local database exports:bash -c "set -o pipefail; ..."with double-quoted command string$envarray$VAR) for proper substitution from environmentimportDatabaseDumpOnRemotemethod in PushDatabaseCommand.php for remote database imports:bash -o pipefail -cwith single-quoted command string to prevent variable expansionstr_replace("'", "'\\''")) properly escapes all database credentials and file paths before string interpolationescapeshellarg())@infection-ignore-allannotation for filepath escaping (system-generated paths under our control)bash -o pipefailand proper escapingImpact: Error detection now works correctly!
Before this change, pipeline failures could be silent - if gunzip failed or mysql encountered an error, the command might still appear successful. Now with pipefail enabled, all pipeline failures are properly detected and reported, making database operations much more reliable.
Security improvements:
executeFromCmd()with environment variable substitution for safe value passingescapeshellarg()Technical details:
$VAR). Variables are passed via$envarray parameter and accessed as environment variables inside bash subprocess.str_replace("'", "'\\''")before string interpolation into command.$envparameter toexecuteFromCmd()rather than embedded directly in command strings, providing an extra layer of security.Why standard bash syntax ($VAR) instead of placeholder syntax ($ {:VAR})?
The
${:VAR}placeholder syntax is designed for direct command execution, but when wrapped insidebash -c "...", the shell tries to interpret it before the substitution can happen, causing "Bad substitution" errors. Using standard bash variable syntax ($VAR) allows the bash subprocess to correctly access variables from its environment.Alternatives considered
set -o pipefail &&directly without bash wrapper - doesn't work in POSIX sh environments, which some systems default toescapeshellarg()- forbidden by codebase standards (phpcs rules)${:VAR}placeholder syntax inside bash -c commands - causes "Bad substitution" errors because shell interprets before substitutionTesting steps
./bin/acli ckctestPushDatabaseWithSpecialCharsInPasswordverifies that database credentials containing single quotes are handled correctly