diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 84074b4791..e85802a53d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -54,3 +54,15 @@ jobs: # (notably SC2155). Tighten in a follow-up after cleanup. - name: Run shellcheck on shell scripts run: git ls-files -z -- '*.sh' | xargs -0 shellcheck --severity=error + + # macOS ships bash 3.2, where ${var^^} / ${var,,} error with "bad + # substitution". shellcheck assumes bash 4+ from the shebang and cannot + # flag these, so guard explicitly; use tr for portable case conversion. + - name: Reject bash 4+ case-modification expansions + run: | + matches=$(git ls-files -z -- '*.sh' | xargs -0 grep -nE '\$\{[A-Za-z_][A-Za-z0-9_]*(\[[^]]*\])?(\^|,)' || true) + if [ -n "$matches" ]; then + echo "Found bash 4+ case-modification expansion(s); use tr for portability (macOS ships bash 3.2):" + echo "$matches" + exit 1 + fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c036a1884..9695b5f669 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ +- fix(scripts): make branch-name acronym retention portable (uppercase via `tr` instead of bash-4-only `${word^^}`, whole-word match via `grep -w` instead of GNU/BSD-only `\b`), fixing acronym/short-word retention on macOS's default bash 3.2 + ## [0.11.9] - 2026-06-26 ### Changed diff --git a/extensions/git/scripts/bash/create-new-feature-branch.sh b/extensions/git/scripts/bash/create-new-feature-branch.sh index d638b048c9..ba1c4a7220 100755 --- a/extensions/git/scripts/bash/create-new-feature-branch.sh +++ b/extensions/git/scripts/bash/create-new-feature-branch.sh @@ -288,7 +288,9 @@ generate_branch_name() { if ! echo "$word" | grep -qiE "$stop_words"; then if [ ${#word} -ge 3 ]; then meaningful_words+=("$word") - elif echo "$description" | grep -qw -- "${word^^}"; then + # Uppercase via tr (portable) rather than bash's 4+ "^^" case + # expansion, which breaks on macOS's default bash 3.2 (bad substitution). + elif echo "$description" | grep -qw -- "$(printf '%s' "$word" | tr '[:lower:]' '[:upper:]')"; then meaningful_words+=("$word") fi fi diff --git a/extensions/git/scripts/powershell/create-new-feature-branch.ps1 b/extensions/git/scripts/powershell/create-new-feature-branch.ps1 index 6a4417f8b9..cd41232e3b 100644 --- a/extensions/git/scripts/powershell/create-new-feature-branch.ps1 +++ b/extensions/git/scripts/powershell/create-new-feature-branch.ps1 @@ -253,9 +253,10 @@ function Get-BranchName { if ($word.Length -ge 3) { $meaningfulWords += $word } elseif ($Description -cmatch "\b$($word.ToUpper())\b") { - # Case-sensitive (-cmatch) to mirror the bash twin's `grep -qw -- "${word^^}"`: - # keep a short word only when its UPPERCASE form appears in the original - # (an acronym). -match is case-insensitive and would keep every short word. + # Case-sensitive (-cmatch) to mirror the bash twin's case-sensitive + # whole-word acronym match: keep a short word only when its UPPERCASE + # form appears in the original (an acronym). -match is case-insensitive + # and would keep every short word. $meaningfulWords += $word } } diff --git a/scripts/bash/create-new-feature.sh b/scripts/bash/create-new-feature.sh index c9609764f7..8887f39399 100644 --- a/scripts/bash/create-new-feature.sh +++ b/scripts/bash/create-new-feature.sh @@ -152,8 +152,10 @@ generate_branch_name() { if ! echo "$word" | grep -qiE "$stop_words"; then if [ ${#word} -ge 3 ]; then meaningful_words+=("$word") - elif echo "$description" | grep -q "\b${word^^}\b"; then - # Keep short words if they appear as uppercase in original (likely acronyms) + # Keep short words that appear as an uppercase acronym in the original. + # Uppercase via tr and match with grep -w (both portable) rather than + # bash's 4+ "^^" case expansion (breaks on macOS bash 3.2) and \b (non-POSIX). + elif echo "$description" | grep -qw -- "$(printf '%s' "$word" | tr '[:lower:]' '[:upper:]')"; then meaningful_words+=("$word") fi fi