diff --git a/extensions/git/scripts/bash/auto-commit.sh b/extensions/git/scripts/bash/auto-commit.sh index f0b423187b..4955352f0f 100755 --- a/extensions/git/scripts/bash/auto-commit.sh +++ b/extensions/git/scripts/bash/auto-commit.sh @@ -94,7 +94,12 @@ if [ -f "$_config_file" ]; then [ "$_val" = "false" ] && _enabled=false fi if echo "$_line" | grep -Eq '[[:space:]]+message:'; then - _commit_msg=$(echo "$_line" | sed 's/^[^:]*:[[:space:]]*//' | sed 's/^["'\'']//' | sed 's/["'\'']*$//') + # Trim trailing whitespace before stripping the closing quote: + # a value like `message: "Done" ` (trailing spaces after the + # quote) would otherwise leave the quote dangling (`Done" `), + # since the closing-quote strip is anchored to end-of-string. + # The PowerShell twin .Trim()s first; match it for parity. + _commit_msg=$(echo "$_line" | sed 's/^[^:]*:[[:space:]]*//' | sed 's/[[:space:]]*$//' | sed 's/^["'\'']//' | sed 's/["'\'']*$//') fi fi fi diff --git a/extensions/git/scripts/python/auto_commit.py b/extensions/git/scripts/python/auto_commit.py index ebf23a9454..c6692895b8 100644 --- a/extensions/git/scripts/python/auto_commit.py +++ b/extensions/git/scripts/python/auto_commit.py @@ -33,7 +33,15 @@ def _value_after_colon(line: str) -> str: def _strip_quotes(value: str) -> str: - """Strip one leading quote and all trailing quotes, mirroring the bash sed.""" + """Strip surrounding whitespace, then one leading quote and all trailing quotes. + + Trimming first matters when the YAML value has trailing whitespace after a + closing quote (``message: "Done" ``): stripping quotes anchored to the end + of string would leave the closing quote dangling (``Done" ``) because the + quote is no longer at the end. The PowerShell twin ``.Trim()``s before + stripping, so trim here too to keep all three script variants in parity. + """ + value = value.strip() value = re.sub(r"^[\"']", "", value) return re.sub(r"[\"']*$", "", value) diff --git a/tests/extensions/git/test_git_extension_python_parity.py b/tests/extensions/git/test_git_extension_python_parity.py index 894571fcb3..faeef00e7f 100644 --- a/tests/extensions/git/test_git_extension_python_parity.py +++ b/tests/extensions/git/test_git_extension_python_parity.py @@ -515,6 +515,32 @@ def test_enabled_per_command_with_custom_message(self, tmp_path: Path): assert p.stderr.strip() == b.stderr.strip() assert self._last_message(bash_proj) == self._last_message(py_proj) == "spec done" + def test_custom_message_with_trailing_whitespace_after_quote(self, tmp_path: Path): + """Trailing whitespace after a closing quote must not leave the quote + dangling in the commit message. A raw close-quote strip anchored to + end-of-string skips the quote when spaces follow it (``spec done" ``); + trimming first (matching the PowerShell twin) yields a clean message and + keeps bash/python in parity.""" + bash_proj, py_proj = _twin_projects(tmp_path) + config = ( + "auto_commit:\n" + " default: false\n" + " after_specify:\n" + " enabled: true\n" + ' message: "spec done" \n' # trailing spaces after the closing quote + ) + for proj in (bash_proj, py_proj): + _write_config(proj, config) + self._dirty(proj) + b = _run_bash("auto-commit.sh", bash_proj, "after_specify") + p = _run_py("auto-commit", py_proj, "after_specify") + _assert_parity(b, p) + assert ( + self._last_message(bash_proj) + == self._last_message(py_proj) + == "spec done" + ) + def test_default_true_applies_to_unlisted_event(self, tmp_path: Path): bash_proj, py_proj = _twin_projects(tmp_path) for proj in (bash_proj, py_proj):