ci: Put space and non-ASCII char in scratch dir
What changed, and why it matters
This commit changes Bitcoin Core's continuous integration (CI) test setup to use a temporary directory name containing a space and non-ASCII characters (₿🧪). It also fixes shell quoting in CI scripts and two functional tests so they work correctly when paths contain spaces or special characters. There is no security vulnerability being fixed or introduced here; it is purely a hardening of the test infrastructure to catch quoting bugs in the future.
No security action required. Treat as a normal CI/test-quality improvement. Reviewers may optionally verify the new quoting is correct, but the change is defensive and does not affect shipped Bitcoin Core binaries.
Security signals we found
No security-relevant code changes
CI/test infrastructure hardening only
No product code or consensus code modified
No vulnerability class fixed or introduced
Evidence from the diff
The commit modifies ci/test/00_setup_env.sh to set BASE_SCRATCH_DIR to a path containing a space and Unicode characters, explicitly to verify that the build and tests handle word-splitting and UTF-8. It quotes -DCMAKE_INSTALL_PREFIX in ci/test/03_test_script.sh to prevent word splitting. It relaxes two functional test assertions (feature_logging.py and feature_notifications.py) so they tolerate spaces in generated paths. No product code is changed, no bug class in the Bitcoin Core runtime is addressed, and no security boundary is crossed.
Changed components
ci/test/00_setup_env.shci/test/03_test_script.shtest/functional/feature_logging.pytest/functional/feature_notifications.pyInspect captured patch +20 / −9
diff --git a/ci/test/00_setup_env.sh b/ci/test/00_setup_env.sh
index 39ab931a..4ca660a5 100755
--- a/ci/test/00_setup_env.sh
+++ b/ci/test/00_setup_env.sh
@@ -20,8 +20,10 @@ export BASE_ROOT_DIR="${BASE_ROOT_DIR:-/ci_container_base}"
# This folder exists only on the ci guest, and on the ci host as a volume.
export DEPENDS_DIR=${DEPENDS_DIR:-$BASE_ROOT_DIR/depends}
# A folder for the ci system to put temporary files (build result, datadirs for tests, ...)
+# The name contains a space and a non-ASCII symbol to confirm the build and
+# tests handle word-splitting and UTF8 correctly.
# This folder only exists on the ci guest.
-export BASE_SCRATCH_DIR=${BASE_SCRATCH_DIR:-$BASE_ROOT_DIR/ci/scratch}
+export BASE_SCRATCH_DIR=${BASE_SCRATCH_DIR:-$BASE_ROOT_DIR/ci/scratch_ ₿🧪_}
echo "Setting specific values in env"
# shellcheck disable=SC1090
diff --git a/ci/test/03_test_script.sh b/ci/test/03_test_script.sh
index 4e4d9757..85c12b80 100755
--- a/ci/test/03_test_script.sh
+++ b/ci/test/03_test_script.sh
@@ -109,7 +109,7 @@ ccache --zero-stats
# Folder where the build is done.
BASE_BUILD_DIR=${BASE_BUILD_DIR:-$BASE_SCRATCH_DIR/build-$HOST}
-BITCOIN_CONFIG_ALL="$BITCOIN_CONFIG_ALL -DCMAKE_INSTALL_PREFIX=$BASE_OUTDIR -Werror=dev"
+BITCOIN_CONFIG_ALL="$BITCOIN_CONFIG_ALL '-DCMAKE_INSTALL_PREFIX=$BASE_OUTDIR' -Werror=dev"
if [[ "${RUN_IWYU}" == true || "${RUN_TIDY}" == true ]]; then
BITCOIN_CONFIG_ALL="$BITCOIN_CONFIG_ALL -DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py
index 07420733..fd3b8097 100755
--- a/test/functional/feature_logging.py
+++ b/test/functional/feature_logging.py
@@ -37,8 +37,8 @@ class LoggingTest(BitcoinTestFramework):
invdir = self.relative_log_path("foo")
invalidname = os.path.join("foo", "foo.log")
self.stop_node(0)
- exp_stderr = r"Error: Could not open debug log file \S+$"
- self.nodes[0].assert_start_raises_init_error([f"-debuglogfile={invalidname}"], exp_stderr, match=ErrorMatch.FULL_REGEX)
+ exp_stderr = "Error: Could not open debug log file "
+ self.nodes[0].assert_start_raises_init_error([f"-debuglogfile={invalidname}"], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
assert not os.path.isfile(os.path.join(invdir, "foo.log"))
# check that invalid log (relative) works after path exists
@@ -51,7 +51,7 @@ class LoggingTest(BitcoinTestFramework):
self.stop_node(0)
invdir = os.path.join(self.options.tmpdir, "foo")
invalidname = os.path.join(invdir, "foo.log")
- self.nodes[0].assert_start_raises_init_error([f"-debuglogfile={invalidname}"], exp_stderr, match=ErrorMatch.FULL_REGEX)
+ self.nodes[0].assert_start_raises_init_error([f"-debuglogfile={invalidname}"], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
assert not os.path.isfile(os.path.join(invdir, "foo.log"))
# check that invalid log (absolute) works after path exists
diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py
index 71500cc6..9c409b32 100755
--- a/test/functional/feature_notifications.py
+++ b/test/functional/feature_notifications.py
@@ -31,6 +31,10 @@ LARGE_WORK_INVALID_CHAIN_WARNING = (
def notify_outputname(walletname, txid):
return txid if platform.system() == 'Windows' else f'{walletname}_{txid}'
+def shell_escape_posix(arg):
+ # Identical to ShellEscape() in the C++ code
+ return "'" + arg.replace("'", "'\"'\"'") + "'"
+
class NotificationsTest(BitcoinTestFramework):
def set_test_params(self):
@@ -51,13 +55,18 @@ class NotificationsTest(BitcoinTestFramework):
os.mkdir(self.walletnotify_dir)
os.mkdir(self.shutdownnotify_dir)
+ if platform.system() == 'Windows':
+ walletnotify_path = f"\"{os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s'))}\""
+ else:
+ walletnotify_path = f"{shell_escape_posix(os.path.join(self.walletnotify_dir, ''))}{notify_outputname('%w', '%s')}"
+
# -alertnotify and -blocknotify on node0, walletnotify on node1
self.extra_args = [[
- f"-alertnotify=echo %s >> {self.alertnotify_file}",
- f"-blocknotify=echo > {os.path.join(self.blocknotify_dir, '%s')}",
- f"-shutdownnotify=echo > {self.shutdownnotify_file}",
+ f"-alertnotify=echo %s >> \"{self.alertnotify_file}\"",
+ f"-blocknotify=echo > \"{os.path.join(self.blocknotify_dir, '%s')}\"",
+ f"-shutdownnotify=echo > \"{self.shutdownnotify_file}\"",
], [
- f"-walletnotify=echo %h_%b > {os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s'))}",
+ f"-walletnotify=echo %h_%b > {walletnotify_path}",
]]
self.wallet_names = [self.default_wallet_name, self.wallet]
super().setup_network()
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.