diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index a187422efb..a6d280cba3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -36,48 +36,32 @@ body: Provide a link to a live example, or an unambiguous set of steps to reproduce this bug. Include code to reproduce, if relevant. validations: required: true - - type: input - attributes: - label: Bash-it version - placeholder: "How to get: bash-it version" - validations: - required: true - - type: input - attributes: - label: List of enabled plugins, themes and aliases - placeholder: "How to get: bash-it show plugins|themes|aliases (it is not a pipe)" - validations: - required: true - - type: input - attributes: - label: Bash version - placeholder: "How to get: bash --version" - validations: - required: true - - type: input - attributes: - label: Operating system and version - placeholder: "How to get: neofetch (or another command)" - validations: - required: true - type: textarea attributes: - label: "bash-it doctor output" + label: "Diagnostic Information" + description: > + **Please run `bash-it doctor` and paste the complete output below.** + This single command provides all the diagnostic information we need including: + bash-it version, enabled components, bash version, OS version, and configuration. + placeholder: "Run: bash-it doctor" value: | ``` - # How to get: bash-it doctor + # Paste the output of: bash-it doctor + + ``` validations: - required: false + required: true - type: textarea attributes: - label: Your ~/.bashrc - value: | - ```bash - # How to get: cat ~/.bashrc - ``` + label: "Additional Context (Optional)" + description: > + Any additional information that might help diagnose the issue. + This could include specific error messages, relevant parts of your ~/.bashrc, + or other configuration details not captured by `bash-it doctor`. + placeholder: "Paste any additional relevant information here" validations: - required: true + required: false - type: textarea attributes: label: Notes diff --git a/docs/README.md b/docs/README.md index 44c438babe..dd89a9956e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -21,6 +21,7 @@ Stop polluting your `~/bin` directory and your `.bashrc` file, fork/clone Bash-i - [via Docker](https://bash-it.readthedocs.io/en/latest/installation/#install-using-docker) - [Updating](https://bash-it.readthedocs.io/en/latest/installation/#updating) - [Help](https://bash-it.readthedocs.io/en/latest/misc/#help-screens) +- [Diagnostics](#diagnostics) - [Search](https://bash-it.readthedocs.io/en/latest/commands/search) - [Syntax](https://bash-it.readthedocs.io/en/latest/commands/search/#syntax) - [Searching with Negations]( @@ -54,10 +55,30 @@ If this is undesirable, you can create another file, by run the installer: BASH_IT_CONFIG_FILE=path/to/my/custom/location.bash ~/.bash_it/install.sh ``` +## Diagnostics + +If you're experiencing issues with Bash-it or need to report a bug, use the built-in diagnostics tool: + +```bash +bash-it doctor +``` + +This command provides a comprehensive summary including: +- Environment information (OS, Bash version) +- Bash-it version and update status +- Configuration file locations and how Bash-it is loaded +- List of enabled components (aliases, plugins, completions) + +**When reporting bugs**, please include the full output of `bash-it doctor` in your issue report. + +The doctor command can also help you update Bash-it - if you're behind the latest version and it's safe to update, you'll be prompted to merge the latest changes. + ## Contributing Please take a look at the [Contribution Guidelines](https://bash-it.readthedocs.io/en/latest/contributing) before reporting a bug or providing a new feature. +**When reporting bugs**, always run `bash-it doctor` and include its output in your issue report to help maintainers diagnose the problem quickly. + The [Development Guidelines](https://bash-it.readthedocs.io/en/latest/development) have more information on some of the internal workings of Bash-it, please feel free to read through this page if you're interested in how Bash-it loads its components. diff --git a/lib/helpers.bash b/lib/helpers.bash index 919540bea6..8c18ec2bd4 100644 --- a/lib/helpers.bash +++ b/lib/helpers.bash @@ -72,7 +72,7 @@ function bash-it() { example '$ bash-it reload' example '$ bash-it restart' example '$ bash-it profile list|save|load|rm [profile_name]' - example '$ bash-it doctor errors|warnings|all' + example '$ bash-it doctor errors|warnings|all|summary' local verb=${1:-} shift local component=${1:-} @@ -414,11 +414,197 @@ function _bash-it-doctor-errors() { _bash-it-doctor "${BASH_IT_LOG_LEVEL_ERROR?}" } +function _bash-it-doctor-summary() { + _about 'shows a comprehensive diagnostic summary for bug reports' + _group 'lib' + + local component_type enabled_count enabled_list f component_name + + # Color definitions + local BOLD CYAN GREEN YELLOW RESET + BOLD=$(tput bold 2> /dev/null || echo "") + CYAN=$(tput setaf 6 2> /dev/null || echo "") + GREEN=$(tput setaf 2 2> /dev/null || echo "") + YELLOW=$(tput setaf 3 2> /dev/null || echo "") + RESET=$(tput sgr0 2> /dev/null || echo "") + + echo "${BOLD}${CYAN}Bash-it Doctor Summary${RESET}" + echo "${CYAN}======================${RESET}" + echo "" + + # Environment Information + echo "${BOLD}## Environment${RESET}" + echo "${GREEN}OS:${RESET} $(uname -s) $(uname -r)" + echo "${GREEN}Bash Version:${RESET} ${BASH_VERSION}" + echo "${GREEN}Bash-it Location:${RESET} ${BASH_IT}" + + # Check which config file is used + local config_file + if [[ -n "${BASH_IT_BASHRC:-}" ]]; then + config_file="${BASH_IT_BASHRC}" + elif [[ -f "${HOME}/.bashrc" ]]; then + config_file="${HOME}/.bashrc" + elif [[ -f "${HOME}/.bash_profile" ]]; then + config_file="${HOME}/.bash_profile" + else + config_file="unknown" + fi + echo "${GREEN}Config File:${RESET} ${config_file}" + echo "" + + # Bash-it Version Information + echo "${BOLD}## Bash-it Version${RESET}" + pushd "${BASH_IT}" > /dev/null 2>&1 || { + echo "Error: Cannot access Bash-it directory" + return 1 + } + + local current_commit current_tag commits_behind latest_tag commits_since_tag + current_commit="$(git rev-parse --short HEAD 2> /dev/null || echo 'unknown')" + current_tag="$(git describe --exact-match --tags 2> /dev/null || echo 'none')" + + if [[ -z "${BASH_IT_REMOTE:-}" ]]; then + BASH_IT_REMOTE="origin" + fi + + # Get version info relative to tags + latest_tag="$(git describe --tags --abbrev=0 2> /dev/null || echo 'none')" + commits_since_tag="$(git rev-list --count "${latest_tag}..HEAD" 2> /dev/null || echo '0')" + + if [[ "${current_tag}" != "none" ]]; then + echo "${GREEN}Current Version:${RESET} ${current_tag} (${current_commit})" + elif [[ "${latest_tag}" != "none" && "${commits_since_tag}" != "0" ]]; then + echo "${GREEN}Current Version:${RESET} ${latest_tag} +${commits_since_tag} (${current_commit})" + else + echo "${GREEN}Current Commit:${RESET} ${current_commit}" + fi + + # Check how far behind we are + git fetch "${BASH_IT_REMOTE}" --quiet 2> /dev/null + if [[ -z "${BASH_IT_DEVELOPMENT_BRANCH:-}" ]]; then + BASH_IT_DEVELOPMENT_BRANCH="master" + fi + commits_behind="$(git rev-list --count HEAD.."${BASH_IT_REMOTE}/${BASH_IT_DEVELOPMENT_BRANCH}" 2> /dev/null || echo 'unknown')" + + if [[ "${commits_behind}" == "0" ]]; then + echo "${GREEN}Status:${RESET} Up to date with ${BASH_IT_REMOTE}/${BASH_IT_DEVELOPMENT_BRANCH} ✓" + elif [[ "${commits_behind}" != "unknown" ]]; then + echo "${YELLOW}Status:${RESET} ${commits_behind} commits behind ${BASH_IT_REMOTE}/${BASH_IT_DEVELOPMENT_BRANCH}" + + # Offer to update if behind and it's safe to do so + local git_status untracked_files merge_base can_ff + git_status="$(git status --porcelain 2> /dev/null)" + untracked_files="$(echo "$git_status" | grep -c '^??' || true)" + + # Check if we can fast-forward + merge_base="$(git merge-base HEAD "${BASH_IT_REMOTE}/${BASH_IT_DEVELOPMENT_BRANCH}" 2> /dev/null)" + can_ff=false + if [[ "$(git rev-parse HEAD 2> /dev/null)" == "$merge_base" ]]; then + can_ff=true + fi + + # Only offer merge if: + # 1. No modified/staged files (untracked are OK) + # 2. Can fast-forward OR no untracked files that would conflict + if ! echo "$git_status" | grep -v '^??' -q; then + if [[ "$can_ff" == "true" ]] || [[ "$untracked_files" == "0" ]]; then + echo "" + echo "Would you like to update now? This will merge ${BASH_IT_REMOTE}/${BASH_IT_DEVELOPMENT_BRANCH} into your current branch." + read -r -p "Update? [y/N] " response + case "$response" in + [yY] | [yY][eE][sS]) + echo "Updating bash-it..." + if git merge "${BASH_IT_REMOTE}/${BASH_IT_DEVELOPMENT_BRANCH}" --ff-only 2> /dev/null; then + echo "✓ Successfully updated to latest version!" + echo "" + echo "Please restart your shell or run: source ~/.bashrc" + else + echo "✗ Fast-forward merge failed. Please run 'bash-it update' for a guided update." + fi + ;; + *) + echo "Skipping update. You can update later with: bash-it update" + ;; + esac + else + echo "" + echo "Note: Cannot safely auto-update (untracked files may conflict). Use: bash-it update" + fi + else + echo "" + echo "Note: Cannot auto-update (uncommitted changes present). Use: bash-it update" + fi + fi + + popd > /dev/null 2>&1 || true + echo "" + + # Bash-it Loading Configuration + echo "${BOLD}## Bash-it Loading${RESET}" + local config_files_to_check=() + local config_file_path + + # Check all common config files + for config_file_path in "${HOME}/.bashrc" "${HOME}/.bash_profile" "${HOME}/.profile"; do + [[ -f "$config_file_path" ]] && config_files_to_check+=("$config_file_path") + done + + if [[ ${#config_files_to_check[@]} -gt 0 ]]; then + for config_file_path in "${config_files_to_check[@]}"; do + if grep -i "bash.it\|bash_it" "$config_file_path" > /dev/null 2>&1; then + echo "From ${config_file_path}:" + grep -n -i "bash.it\|bash_it" -B2 -A2 "$config_file_path" 2> /dev/null + echo "" + fi + done + else + echo "No config files found (.bashrc, .bash_profile, .profile)" + fi + + # Enabled Components Summary + echo "${BOLD}## Enabled Components${RESET}" + + # Process each component type + for component_type in aliases plugins completion; do + enabled_count=0 + enabled_list=() + + # Get singular form for display + local display_type="${component_type}" + if [[ "$component_type" == "aliases" ]]; then + display_type="Aliases" + elif [[ "$component_type" == "plugins" ]]; then + display_type="Plugins" + else + display_type="Completions" + fi + + # Count and collect enabled components + for f in "${BASH_IT?}/$component_type/available"/*.*.bash; do + [[ -f "$f" ]] || continue + component_name="$(_bash-it-get-component-name-from-path "$f")" + if _bash-it-component-item-is-enabled "$f"; then + enabled_list+=("$component_name") + ((enabled_count++)) + fi + done + + # Display the summary with colors + if [[ $enabled_count -eq 0 ]]; then + printf '%s%s%s (%s): %s\n' "$CYAN" "$display_type" "$RESET" "$enabled_count" "${YELLOW}none${RESET}" + else + printf '%s%s%s (%s): %s\n' "$CYAN" "$display_type" "$RESET" "$enabled_count" "${enabled_list[*]}" + fi + done + echo "" + echo "${YELLOW}Tip:${RESET} To copy this report: ${CYAN}bash-it doctor${RESET} | pbcopy (macOS) or xclip (Linux)" +} + function _bash-it-doctor-() { - _about 'default bash-it doctor behavior, behaves like bash-it doctor all' + _about 'default bash-it doctor behavior, shows component summary' _group 'lib' - _bash-it-doctor-all + _bash-it-doctor-summary } function _bash-it-profile-save() {