github / github/spec-kit

[Bug] create-new-feature.sh uses ${word^^} (bash 4+) and silently drops acronyms on macOS (bash 3.2)

Đang mở Phù hợp với người mới
#3,927 2 bình luận 0 reaction 0 người được giao Xem trên GitHub
bug-assess invalid
Ngôn ngữ chính
Python
Star
137k
Fork
12.3k
Merge trung bình
2 ngày 12 giờ
Pull request đã merge (30 ngày)
159

Mô tả

## Summary

`create-new-feature.sh` (line 231) uses `${word^^}`, a **bash 4+ uppercase expansion**. macOS ships bash 3.2.57 (Apple's last GPLv2 build) and the script **fails silently** on that line: stderr is full of `bad substitution` warnings, the script keeps running, and the generated branch name silently **drops acronyms** that were short (≤2 chars) and uppercase in the original description.

Because the script still emits valid JSON and the resulting branch name is plausible, end users on macOS get a quietly truncated branch without realizing it.

## Environment

- macOS (any current version; bash 3.2.57 has been the system bash on every macOS since 2007)
- Any git repo initialised with `specify init`
- No `bash` from Homebrew installed (or installed but not first in `PATH` for the running shell)

```sh
$ /bin/bash --version
GNU bash, version 3.2.57(1)-release (arm64-apple-darwin24)

$ bash .specify/scripts/bash/create-new-feature.sh --json --dry-run \
"Transparencia Art. 50 AI Act: aviso de interaccion con IA"
{"BRANCH_NAME":"001-transparencia-art-act", ...} # ← missing "AI"

$ bash .specify/scripts/bash/create-new-feature.sh --json --dry-run "AI Act transparency"
{"BRANCH_NAME":"001-act-transparency", ...} # ← missing "AI" (top-3 cutoff also affected)
bad substitution # (only visible in stderr; the JSON looks fine)
```

The same inputs against bash 5.x preserve the acronym:

```sh
$ bash .specify/scripts/bash/create-new-feature.sh --json --dry-run "AI Act transparency"
{"BRANCH_NAME":"001-ai-act-transparency", ...} # ← "ai" preserved
```

## Root cause

`create-new-feature.sh`, line 231:

```sh
elif echo "$description" | grep -q "\b${word^^}\b"; then
# Keep short words if they appear as uppercase in original (likely acronyms)
meaningful_words+=("$word")
fi
```

`${word^^}` is case-conversion syntax introduced in bash 4.0 (2009). It is not available in bash 3.2. On bash 3.2 the line errors out, `grep` runs with the literal placeholder string, the grep returns non-zero, the elif is false, and the short word is silently dropped from `meaningful_words[]`. The same `${var^^}` / `${var,,}` pattern likely exists in other vendored scripts; I have not audited them all.

The shebang `#!/usr/bin/env bash` does not protect against this — `env` picks whatever `bash` resolves to in the current `PATH`, and on a stock macOS that is the system's bash 3.2.

## Suggested fix

Two options, in order of preference:

1. **POSIX-portable** — replace `${word^^}` with a portable uppercase conversion, e.g. `echo "$word" | tr '[:lower:]' '[:upper:]'`, so the script works on bash 3.2 too. This is a one-line change per occurrence and removes the silent failure mode entirely.
2. **Bash 4+ required** — if the project decides to require bash 4+ intentionally, make the failure loud: at the top of every vendored script, add a runtime check that fails fast with a clear message and a hint to install bash from Homebrew (`brew install bash`). Currently the failure is silent and the user has no way to know their branch name is wrong.

I'd recommend option 1 (POSIX-portable) — the function is a small, isolated transformation and the project already has a `clean_branch_name` that uses `tr '[:upper:]' '[:lower:]'` to do the inverse. Symmetry argues for replacing the bash-only expansion with `tr` as well.

## Workaround on macOS today

Install bash 5.x via Homebrew and ensure it is first in `PATH` for the shell that runs Spec-Kit:

```sh
brew install bash
# ~/.zshrc or ~/.bashrc, idempotent:
case "$PATH" in
/opt/homebrew/bin:*) ;;
/opt/homebrew/bin) ;;
*) export PATH="/opt/homebrew/bin${PATH:+:$PATH}" ;;
esac
```

Note that on login shells, `/etc/zprofile` runs `path_helper` which re-derives `PATH` from `/etc/paths` + `/etc/paths.d/*` and puts `/opt/homebrew/bin` at the *end* — so the prefix must be re-asserted in a file that runs after `/etc/zprofile` (e.g. `~/.zshrc`).

## Impact

This affects every macOS developer or agent using Spec-Kit with a description containing acronyms (e.g. "AI Act", "RGPD", "GDPR", "API", "OAuth", "HIPAA", "PCI"). The "feature name" used downstream in `specs/NNN-short-name/spec.md` is the same truncated string, so the spec file itself also loses the acronym, and any tooling that relies on the branch/spec name to understand the feature (e.g. CI labels, dashboards, search) gets the wrong subject.

Discovered while piloting Spec-Kit on ada-legal-ai (PR #11 in the HOILAB org). Same root cause likely affects any other team running Spec-Kit on macOS.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Hướng nghiên cứu

Start at .specify/scripts/bash/create-new-feature.sh around line 231 and inspect the acronym branch-name path. Run the supplied --json --dry-run commands under macOS’s /bin/bash 3.2 and compare with Bash 5.x. Done means short uppercase acronyms remain in BRANCH_NAME and stderr has no bad substitution warning.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
bash
Lĩnh vực
cli, tooling
Loại issue
Lỗi
Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức độ hoạt động
Ít trao đổi
Độ rõ ràng
Đặc tả rõ ràng
Mức phù hợp với người mới
84/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.