Added "check_cd" function to "fx doctor"
verify "cd" does not output anything to stdout, since many
other scripts assume this.
Note that check_cd optionally prints recommendations for
resolving the issue within the developer's shell environment.
For bash users only, doctor calls
scripts/devshell/lib/bashrc_checkup.sh,
which will attempt to load their ~/.bashrc
file, to allow doctor to check for conflicts in the interactive
shell environment (e.g., functions like overriding "cd").
For users using non-bash shells (like zsh) this current iteration
of "doctor" will still check environment variables and files
through the non-interactive bash script, but we may want to add
a "zshrc_checkup.zsh" script as well if we want to check for zsh-specific
interactive shell conflicts. (The bash "doctor" could still
launch other non-bash shell checks.)
Updated "doctor" script format and error handling a bit. Needs more
work to be easier to use and easier to add more checks.
Added --indent flag to style.sh and moved common terminal output
style functions, originally in doctor, to
devshell/lib/common_term_styles.sh, for reusability.
Change-Id: I3b054609e87274f49479c93c3a6c2e29bb7136eb
diff --git a/devshell/doctor b/devshell/doctor
index ed7ef38..d264754 100755
--- a/devshell/doctor
+++ b/devshell/doctor
@@ -8,42 +8,93 @@
## usage: fx doctor
# The goal of this script is to detect common issues with a Fuchsia
-# checkout. For example, on OS X the xcode command line tool
+# checkout and potential conflicts in the user's shell environment.
+#
+# For example, on OS X the xcode command line tool
# installation often lapses. Ensuring that `xcode select --install` is
# run as part of a checkout or build is problematic: the step involves
# manual input. Detecting that it needs to be run, however, is
# perfectly mechanizable.
+#
+# For potential issues in the user's shell initialization script
+# (such as ~/.bashrc), this script will also run a shell checkup
+# script (for example, devshell/lib/bashrc_checkup.sh)
+# under the user's bash "${SHELL}" (if different from /bin/bash),
+# load the user's shell settings, and check for any known issues.
-source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"/lib/vars.sh
-fx-config-read
+source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/lib/vars.sh || exit $?
+source "${FUCHSIA_DIR}/scripts/devshell/lib/style.sh" || exit $?
+source "${FUCHSIA_DIR}/scripts/devshell/lib/common_term_styles.sh" || exit $?
-function dr-mac {
- # TODO actually check the need for this
- echo "A common issue with Fuchsia development on macOS is needing to"
- echo "re-run the \`xcode-select install\` step. The typical symptom is"
- echo "failure to find system C or C++ headers after a reboot or update."
- return
+fx-config-read || exit $?
+
+dr_mac() {
+ local status=0
+ # TODO actually check the need for this
+
+ info "Make sure you've run \`xcode-select install\`"
+
+ details << EOF
+A common issue with Fuchsia development on macOS is needing to
+re-run the \`xcode-select install\` step. The typical symptom is
+failure to find system C or C++ headers after a reboot or update.
+
+See $(link 'https://fuchsia.googlesource.com/docs/getting_started.md#macos')
+for more details.
+EOF
+
+ return ${status}
}
-function dr-linux {
- return
+dr_linux() {
+ local status=0
+ return ${status}
}
-function dr {
- return
+shell_checkup() {
+ local status=0
+
+ # If the user is using bash, their default interactive "${SHELL}"
+ # may differ from the script-standard "/bin/bash", and their ~/.bashrc
+ # may depend on features of their shell that are not present in
+ # /bin/bash, so launch the shell checkup script using "${SHELL}".
+ #
+ # For example, since MacOS includes only bash version 3, Homebrew users
+ # may install bash 4 in /usr/local/bin/bash, and then select
+ # bash 4 by adding it to /etc/shells, and running the "chsh" command.
+
+ local shell_type="$(basename "${SHELL}")"
+ case "${shell_type}" in
+ bash)
+ local current_debug_flag="$(echo $-|sed -n 's/.*x.*/-x/p')"
+ eval "${SHELL}" "${current_debug_flag}" "${FUCHSIA_DIR}/scripts/devshell/lib/bashrc_checkup.sh" || status=$?
+ ;;
+ *)
+ info "No shell checkup for ${shell_type}"
+ ;;
+ esac
+
+ return ${status}
}
-function main {
- case $(uname) in
- Darwin)
- dr-mac
- ;;
- Linux)
- dr-linux
- ;;
- esac
- dr
- return
+dr_all() {
+ local status=0
+ shell_checkup || status=$?
+ return ${status}
}
-main "$@"
+main() {
+ local status=0
+ case $(uname) in
+ Darwin)
+ dr_mac || status=$?
+ ;;
+ Linux)
+ dr_linux || status=$?
+ ;;
+ esac
+ dr_all || status=$?
+ return ${status}
+}
+
+main "$@" || exit $?
diff --git a/devshell/lib/bashrc_checkup.sh b/devshell/lib/bashrc_checkup.sh
new file mode 100644
index 0000000..33915d0
--- /dev/null
+++ b/devshell/lib/bashrc_checkup.sh
@@ -0,0 +1,129 @@
+# No #!/bin/bash - See "usage"
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+### check the health of a user's interactive bash environment (from "fx doctor")
+
+## usage:
+## "${SHELL}" [-flags] "${FUCHSIA_DIR}/scripts/devshell/lib/bashrc_checkup.sh" || status=$?
+## (Valid only for bash ${SHELL} since this script is bash.)
+
+# Detect potential problems for Fuchsia development from settings specific
+# to the user's interactive shell environment. Potential customizations can
+# include bash version and settings introduced in the user's ~/.bashrc file
+# such as bash functions, aliases, and non-exported variables such as
+# "${CDPATH}" and "${PATH}" that can impact how bash executes some commands
+# from the command line.
+#
+
+source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/vars.sh || exit $?
+source "${FUCHSIA_DIR}/scripts/devshell/lib/style.sh" || exit $?
+source "${FUCHSIA_DIR}/scripts/devshell/lib/common_term_styles.sh" || exit $?
+
+fx-config-read || exit $?
+
+# For bash users, this script also attempts to load your preferred
+# bash interpreter (if different from the default, such as Homebrew
+# bash on Mac), and load your ~/.bashrc settings, as would happen
+# in an interactive shell. This allows doctor to check for
+# potential issues with settings that don't normally propagate to
+# bash scripts (unless executed with "source"), such as bash
+# functions, aliases, and unexported variables.
+
+# For bash users, load settings that would exist in the user's
+# interactive bash shells as per
+# [The GNU Bash Reference Manual, for Bash, Version 4.4](https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html)
+if [ -f ~/.bashrc ]; then
+ source ~/.bashrc
+fi
+
+check_cd() {
+ # Returns an error status if the current definition of "cd"
+ # writes anything to the stdout stream, which would break common bash
+ # script lines similar to the following:
+ #
+ # SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+ local cd_output=$(
+ CDPATH=""
+ cd "${FUCHSIA_DIR}" 2>/dev/null
+ cd scripts 2>/dev/null
+ )
+
+ local cdpath_output=$(
+ if [ -z "${cd_output}" ] && [ "${CDPATH}" != "" ]; then
+ CDPATH="${FUCHSIA_DIR}"
+ cd scripts 2>/dev/null
+ fi
+ )
+
+ if [ -z "${cdpath_output}" ] && [ -z "${cd_output}" ]; then
+ return 0 # The check passed!
+ fi
+
+ # The check failed. Print recommendations based on what we found.
+
+ local status=1
+
+ warn 'Your implementation of the "cd" command writes to stdout.'
+
+ details << EOF
+Many common developer scripts and tools use "cd" to find relative
+file paths and will fail in unpredictable ways.
+EOF
+
+ if [ ! -z "${cdpath_output}" ]; then
+ details << EOF
+
+The "cd" command writes to stdout based on your CDPATH environment variable.
+
+You can remove or unset CDPATH in your shell initialization script, or
+define a cd wrapper function.
+EOF
+ fi
+
+ details << EOF
+
+If you have not redefined "cd", and the builtin "cd" is writing to stdout,
+define a wrapper function and redirect the output to /dev/null or stderr.
+
+EOF
+ code << EOF
+cd() {
+ builtin cd "\$@"
+}
+EOF
+ details << EOF
+
+If you already redefine "cd" during shell initialization, find the alias,
+function, or script, and either remove it, or redirect the output to stderr
+by appending "", as in this example:
+
+EOF
+ code << EOF
+cd() {
+ builtin cd "\$@" >/dev/null
+ update_terminal_cwd
+}
+EOF
+ details << EOF
+
+(Note, in this example, "update_terminal_cwd" is a common MacOS function
+to call when changing directories. Other common "cd" overrides may invoke
+"pwd", "print", or other commands.)
+
+EOF
+
+ return ${status}
+}
+
+main() {
+ local status=0
+
+ check_cd || status=$?
+
+ return ${status}
+}
+
+main "$@" || exit $?
diff --git a/devshell/lib/common_term_styles.sh b/devshell/lib/common_term_styles.sh
new file mode 100644
index 0000000..26940f4
--- /dev/null
+++ b/devshell/lib/common_term_styles.sh
@@ -0,0 +1,64 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+### add-on functions for common styling of text messages to the terminal
+
+## "source style.sh" before sourcing this script.
+## Functions include:
+##
+## * info
+## * warn
+## * error
+## * link
+## * code
+## * details
+
+## usage examples:
+##
+## # First import style.sh
+##
+## source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/lib/vars.sh || exit $?
+## source "${FUCHSIA_DIR}/scripts/devshell/lib/style.sh" || exit $?
+## source "${FUCHSIA_DIR}/scripts/devshell/lib/common_term_styles.sh" || exit $?
+##
+## warn 'The warning message.'
+##
+## details << EOF
+##A multi-line message with bash ${variable} expansion.
+##Excape dollars with backslash \$
+##See $(link 'https://some/hyper/link') to insert a link.
+##EOF
+##
+## Visual tests (and demonstration of capabilities) can be run from:
+## //scripts/tests/common_term_styles-test-visually
+
+info() {
+ style::info --stdout "
+INFO: $@
+"
+}
+
+warn() {
+ style::warning --stdout "
+WARNING: $@
+"
+}
+
+error() {
+ style::error --stdout "
+ERROR: $@
+"
+}
+
+details() {
+ style::cat --indent 2
+}
+
+code() {
+ style::cat --bold --magenta --indent 4
+}
+
+link() {
+ style::link "$@"
+}
diff --git a/devshell/lib/style.sh b/devshell/lib/style.sh
index 7d9264c..8e0a2cf 100644
--- a/devshell/lib/style.sh
+++ b/devshell/lib/style.sh
@@ -11,6 +11,11 @@
# with underline. The text is written to stderr instead of the default
# stdout.
#
+# style::cat --color black --background cyan --indent 4 <<EOF
+# Multi-line text with expanded bash ${variables}
+# can be styled and indented.
+# EOF
+#
# style::info, style::warning, and style::error use echo to stderr
# with default color and bold text. For example:
#
@@ -98,6 +103,7 @@
--bold, --faint, --underline, etc.
--color <color_name>
--background <color_name>
+ --indent <spaces_count>
--stderr (output to standard error instead of standard out)
echo "This is \$(style::echo -f --bold LOUD) and soft."
@@ -168,7 +174,9 @@
local styles
local semicolon
local name
- local -i code
+ local -i indent=0
+ local prefix
+ local -i code=0
while $get_flags; do
case "$1" in
@@ -190,6 +198,10 @@
styles="${styles}${semicolon}$(style::background $name || exit $?)" || return $?
semicolon=';'
;;
+ --indent)
+ shift; indent=$1; shift
+ prefix="$(printf "%${indent}s")"
+ ;;
--*)
name="${1:2}"
code=$(style::attribute $name 0)
@@ -216,7 +228,14 @@
if [ ! -t ${fd} ] && ${STYLE_TO_TTY_ONLY}; then
# Output is not to a TTY so don't stylize
- >&${fd} "${command}" "$@" || return $?
+ if [[ "${prefix}" == "" ]]; then
+ >&${fd} "${command}" "$@" || status=$?
+ else
+ >&${fd} "${command}" "$@" | sed "s/^/${prefix}/"
+ if (( ${PIPESTATUS[0]} != 0 )); then
+ status=${PIPESTATUS[0]}
+ fi
+ fi
return 0
fi
@@ -225,6 +244,9 @@
# Add placeholder (.) so command substitution doesn't strip trailing newlines
text="$("${command}" "$@" || exit $?;echo -n '.')" || return $?
+ if [[ "${prefix}" != "" ]]; then
+ text="$(echo "${text}" | sed "s/^/${prefix}/;\$s/^${prefix}[.]\$/./")"
+ fi
local -i len=$((${#text}-2))
if [[ "${text:$len:1}" == $'\n' ]]; then
diff --git a/tests/common_term_styles-test-visually b/tests/common_term_styles-test-visually
new file mode 100755
index 0000000..32d8f37
--- /dev/null
+++ b/tests/common_term_styles-test-visually
@@ -0,0 +1,32 @@
+#!/bin/bash
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Visual tests for //scripts/devshell/lib/common_term_styles.sh
+
+# This is not an automated unit test.
+# It prints stylized to demonstrate the styles on a terminal.
+
+source "$(cd "$(dirname "${BASH_SOURCE[0]}")"/../devshell/lib >/dev/null 2>&1 && pwd)"/style.sh || exit $?
+source "$(cd "$(dirname "${BASH_SOURCE[0]}")"/../devshell/lib >/dev/null 2>&1 && pwd)"/common_term_styles.sh || exit $?
+
+runtest() {
+ command="$1"; shift
+ echo "${command}" "$@"
+ ${command} "$@"
+}
+
+runtest info 'This is informational'
+runtest warn 'This is your last warning'
+runtest error 'Danger! Danger Will Robinson!'
+runtest details <<EOF
+This detail will be
+indented from the error.
+and could have a link like $(link 'https://some/url/here')
+EOF
+runtest code <<EOF
+for ( line in lines_of_code ) {
+ print "this is the demon of code style"
+end
+EOF
diff --git a/tests/common_term_styles-tests b/tests/common_term_styles-tests
new file mode 100755
index 0000000..b51550d
--- /dev/null
+++ b/tests/common_term_styles-tests
@@ -0,0 +1,40 @@
+#!/bin/bash
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Automated tests for //scripts/devshell/lib/common_term_styles.sh
+#
+# Usage: common_term_styles-tests
+#
+# Returns: Error status if actual output does not match expected.
+
+TEST_NAME="$(basename "${BASH_SOURCE[0]}")"
+TESTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
+
+verbose() {
+ echo
+ echo "======================================================="
+ echo
+ echo "$@"
+ echo
+ "$@"
+ echo
+}
+
+test_main() {
+ local expected_out="${TESTS_DIR}/expected/${TEST_NAME}.out"
+ local expected_err="${TESTS_DIR}/expected/${TEST_NAME}.err"
+ local capture_dir=$(mktemp -d)
+ local actual_out="${capture_dir}/${TEST_NAME}.out"
+ local actual_err="${capture_dir}/${TEST_NAME}.err"
+ ${TESTS_DIR}/common_term_styles-test-visually 1> "${actual_out}" 2> "${actual_err}"
+
+ local status=0
+ verbose diff "${expected_out}" "${actual_out}" || status=$?
+ verbose diff "${expected_err}" "${actual_err}" || status=$?
+
+ return $status
+}
+
+test_main "$@" || return $?
diff --git a/tests/expected/common_term_styles-tests.err b/tests/expected/common_term_styles-tests.err
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/expected/common_term_styles-tests.err
diff --git a/tests/expected/common_term_styles-tests.out b/tests/expected/common_term_styles-tests.out
new file mode 100644
index 0000000..4cd26e1
--- /dev/null
+++ b/tests/expected/common_term_styles-tests.out
@@ -0,0 +1,20 @@
+info This is informational
+[1;32m[0m
+[1;32mINFO: This is informational[0m
+[1;32m[0m
+warn This is your last warning
+[1;33m[0m
+[1;33mWARNING: This is your last warning[0m
+[1;33m[0m
+error Danger! Danger Will Robinson!
+[1;91m[0m
+[1;91mERROR: Danger! Danger Will Robinson![0m
+[1;91m[0m
+details
+[m This detail will be[0m
+[m indented from the error.[0m
+[m and could have a link like [4;34mhttps://some/url/here[0m[0m
+code
+[1;95m for ( line in lines_of_code ) {[0m
+[1;95m print "this is the demon of code style"[0m
+[1;95m end[0m
diff --git a/tests/expected/style-tests.err b/tests/expected/style-tests.err
index b921ea7..c7e4d76 100644
--- a/tests/expected/style-tests.err
+++ b/tests/expected/style-tests.err
@@ -8,6 +8,7 @@
--bold, --faint, --underline, etc.
--color <color_name>
--background <color_name>
+ --indent <spaces_count>
--stderr (output to standard error instead of standard out)
echo "This is $(style::echo -f --bold LOUD) and soft."
@@ -23,6 +24,7 @@
--bold, --faint, --underline, etc.
--color <color_name>
--background <color_name>
+ --indent <spaces_count>
--stderr (output to standard error instead of standard out)
echo "This is $(style::echo -f --bold LOUD) and soft."
@@ -41,6 +43,7 @@
--bold, --faint, --underline, etc.
--color <color_name>
--background <color_name>
+ --indent <spaces_count>
--stderr (output to standard error instead of standard out)
echo "This is $(style::echo -f --bold LOUD) and soft."
@@ -59,6 +62,7 @@
--bold, --faint, --underline, etc.
--color <color_name>
--background <color_name>
+ --indent <spaces_count>
--stderr (output to standard error instead of standard out)
echo "This is $(style::echo -f --bold LOUD) and soft."
diff --git a/tests/expected/style-tests.out b/tests/expected/style-tests.out
index 87424d7..2652af8 100644
--- a/tests/expected/style-tests.out
+++ b/tests/expected/style-tests.out
@@ -21,6 +21,9 @@
[43;30mNow is the time for all good[0m
[43;30mpeople to come to the[0m
[43;30maid of their country and world.[0m
+[106;30m Now is the time for all good[0m
+[106;30m people to come to the[0m
+[106;30m aid of their country and world.[0m
[4;34mhttp://wikipedia.com[0m
STYLE_TO_TTY_ONLY=true
diff --git a/tests/style-test-visually b/tests/style-test-visually
index 876ea64..1209d0e 100755
--- a/tests/style-test-visually
+++ b/tests/style-test-visually
@@ -51,6 +51,12 @@
aid of their country and world.
EOF
+style::cat --background cyan --color black --indent 4 << EOF
+Now is the time for all good
+people to come to the
+aid of their country and world.
+EOF
+
style::info 'INFO: Info here'
style::warning 'WARNING: Watch out!'
style::error 'ERROR: What went wrong now?'