-
Notifications
You must be signed in to change notification settings - Fork 61
GH#2993: Fix ShellCheck memory explosion regression — PATH shim for bash-language-server #2995
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -458,18 +458,26 @@ add_local_bin_to_path() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # GH#2915: Configure SHELLCHECK_PATH to use the safe wrapper that strips | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # --external-sources. The bash language server hardcodes --external-sources | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # in every ShellCheck invocation, causing exponential memory growth (11 GB+) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # when source chains span 463+ scripts. The wrapper intercepts this. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # GH#2915, GH#2993: Ensure all processes use the safe ShellCheck wrapper. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Three layers ensure all processes see the wrapper: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # The bash language server hardcodes --external-sources in every ShellCheck | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # invocation, causing exponential memory growth (9+ GB) when source chains | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # span 463+ scripts. The wrapper strips --external-sources and enforces ulimit. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # GH#2915 set SHELLCHECK_PATH env var, but bash-language-server ignores it — | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # it resolves `shellcheck` via PATH lookup, finding /opt/homebrew/bin/shellcheck | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # directly. GH#2993 fixes this by placing a shim on PATH ahead of the real binary. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Four layers ensure all processes use the wrapper: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # 0. PATH shim — ~/.aidevops/bin/shellcheck symlink + PATH prepend (GH#2993) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # 1. launchctl setenv (macOS) — GUI-launched apps (current boot only) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # 2. .zshenv — ALL zsh processes including non-interactive (persists) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # 3. Shell rc files (.zshrc, .bash_profile) — interactive terminals | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Layer 2 is critical: opencode spawns bash-language-server as a non-interactive | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # child process. .zshrc is NOT sourced for non-interactive shells, so without | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # .zshenv the wrapper is invisible to the LSP and shellcheck runs unbounded. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Layer 0 is the primary fix: bash-language-server does a PATH lookup for | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # "shellcheck". By placing ~/.aidevops/bin first on PATH with a symlink to | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # the wrapper, the language server finds the wrapper instead of the real binary. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Layers 1-3 are retained for tools that honour SHELLCHECK_PATH. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setup_shellcheck_wrapper() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| local wrapper_path="$HOME/.aidevops/agents/scripts/shellcheck-wrapper.sh" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -492,9 +500,39 @@ setup_shellcheck_wrapper() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| local env_line | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # shellcheck disable=SC2016 # env_line is written to rc files; must expand at shell startup | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env_line='export SHELLCHECK_PATH="$HOME/.aidevops/agents/scripts/shellcheck-wrapper.sh"' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # shellcheck disable=SC2016 # path_line is written to rc files; must expand at shell startup | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| local path_line='export PATH="$HOME/.aidevops/bin:$PATH"' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| local added_to="" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| local already_in="" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Layer 0: PATH shim (GH#2993) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Create ~/.aidevops/bin/shellcheck as a symlink to the wrapper. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # This is the primary fix: bash-language-server resolves `shellcheck` via | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # PATH, so the symlink must appear on PATH before /opt/homebrew/bin. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| local shim_dir="$HOME/.aidevops/bin" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| local shim_path="$shim_dir/shellcheck" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mkdir -p "$shim_dir" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Create or update the symlink | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| local wrapper_realpath | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| wrapper_realpath="$(realpath "$wrapper_path" 2>/dev/null || readlink -f "$wrapper_path" 2>/dev/null || echo "$wrapper_path")" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ -L "$shim_path" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| local current_target | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| current_target="$(realpath "$shim_path" 2>/dev/null || readlink -f "$shim_path" 2>/dev/null || echo "")" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ "$current_target" != "$wrapper_realpath" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ln -sf "$wrapper_path" "$shim_path" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print_info "Updated shellcheck shim symlink: $shim_path → $wrapper_path" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| elif [[ -e "$shim_path" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Regular file exists — back up and replace with symlink | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mv "$shim_path" "${shim_path}.bak.$(date +%Y%m%d_%H%M%S)" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ln -sf "$wrapper_path" "$shim_path" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print_info "Replaced shellcheck shim with symlink: $shim_path → $wrapper_path" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ln -sf "$wrapper_path" "$shim_path" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print_success "Created shellcheck shim: $shim_path → $wrapper_path" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Layer 1: launchctl setenv (macOS) — affects all GUI-launched processes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ "$PLATFORM_MACOS" == "true" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if launchctl setenv SHELLCHECK_PATH "$wrapper_path" 2>/dev/null; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -506,17 +544,30 @@ setup_shellcheck_wrapper() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # This is critical because opencode spawns bash-language-server as a | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # non-interactive child process. .zshrc is NOT sourced for non-interactive | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # shells, so SHELLCHECK_PATH set only in .zshrc is invisible to the LSP. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # GH#2993: Also prepend ~/.aidevops/bin to PATH here so the shim is found. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| local zshenv="$HOME/.zshenv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ -f "$zshenv" ]] || command -v zsh >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| touch "$zshenv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # SHELLCHECK_PATH env var (for tools that honour it) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if grep -q 'SHELLCHECK_PATH' "$zshenv" 2>/dev/null; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| already_in="${already_in:+$already_in, }$zshenv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| touch "$zshenv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "" >>"$zshenv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "# Added by aidevops setup (GH#2915: prevent ShellCheck memory explosion)" >>"$zshenv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "$env_line" >>"$zshenv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| added_to="${added_to:+$added_to, }$zshenv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # PATH prepend for ~/.aidevops/bin (GH#2993: shim must be on PATH) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ! grep -q '\.aidevops/bin' "$zshenv" 2>/dev/null; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "" >>"$zshenv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "# Added by aidevops setup (GH#2993: shellcheck shim on PATH)" >>"$zshenv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "$path_line" >>"$zshenv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print_success "Prepended $shim_dir to PATH in .zshenv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print_info "$shim_dir already on PATH in .zshenv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The script calls
Suggested change
References
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Layer 3: Shell rc files — affects interactive terminal sessions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -529,16 +580,22 @@ setup_shellcheck_wrapper() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| touch "$rc_file" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Check if already added | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # SHELLCHECK_PATH env var | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if grep -q 'SHELLCHECK_PATH' "$rc_file" 2>/dev/null; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| already_in="${already_in:+$already_in, }$rc_file" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "" >>"$rc_file" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "# Added by aidevops setup (GH#2915: prevent ShellCheck memory explosion)" >>"$rc_file" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "$env_line" >>"$rc_file" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| added_to="${added_to:+$added_to, }$rc_file" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "" >>"$rc_file" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "# Added by aidevops setup (GH#2915: prevent ShellCheck memory explosion)" >>"$rc_file" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "$env_line" >>"$rc_file" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| added_to="${added_to:+$added_to, }$rc_file" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # PATH prepend for ~/.aidevops/bin (GH#2993) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ! grep -q '\.aidevops/bin' "$rc_file" 2>/dev/null; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "" >>"$rc_file" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "# Added by aidevops setup (GH#2993: shellcheck shim on PATH)" >>"$rc_file" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "$path_line" >>"$rc_file" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the
Suggested change
References
coderabbitai[bot] marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| done < <(get_all_shell_rcs) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ -n "$added_to" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -556,6 +613,7 @@ setup_shellcheck_wrapper() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Also export for current session | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export SHELLCHECK_PATH="$wrapper_path" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export PATH="$HOME/.aidevops/bin:$PATH" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.