diff --git a/bin/compose-core b/bin/compose-core index 1e67af8..ffec7dc 100755 --- a/bin/compose-core +++ b/bin/compose-core @@ -159,6 +159,82 @@ wyq-r() { } +err-d () { + local msg="$*" + shift + err "$msg" + print:traceback 1 +} + +print:traceback() { + local omit_level="${1:-0}" + if [ -z "$DEBUG" ]; then + echo " Note: traceback available if you provide {--debug|-d} option." >&2 + return 0 + fi + echo "${WHITE}Traceback (most recent call last):${NORMAL}" >&2 + local i + for ((i=${#FUNCNAME[@]} - 1; i > "$omit_level"; i--)); do + local file="${BASH_SOURCE[$i]}" + local line="${BASH_LINENO[$i - 1]}" + local func="${FUNCNAME[$i]}" + + if [[ -f "$file" ]]; then + # Get total number of lines in the file + local total_lines + total_lines=$(wc -l < "$file") + + # Calculate start and end lines for context + local start_line=$((line - 2)) + local end_line=$((line + 2)) + [[ $start_line -lt 1 ]] && start_line=1 + [[ $end_line -gt $total_lines ]] && end_line=$total_lines + + # Extract context lines + mapfile -s $((start_line - 1)) -n $((end_line - start_line + 1)) context_lines < "$file" + + # Calculate minimal indentation + local min_indent=9999 + for line_text in "${context_lines[@]}"; do + if [[ -n "$line_text" ]]; then + # Get leading whitespace + local leading_whitespace="${line_text%%[![:space:]]*}" + local indent=${#leading_whitespace} + if [[ $indent -lt $min_indent ]]; then + min_indent=$indent + fi + fi + done + + # Remove minimal indentation from each line + for idx in "${!context_lines[@]}"; do + context_lines[$idx]="${context_lines[$idx]:$min_indent}" + done + else + context_lines=("") + min_indent=0 + start_line=1 + end_line=1 + fi + + # Print the traceback frame + echo " File \"$file\", line $line, in ${WHITE}$func${NORMAL}:" + + # Print the context with line numbers + local current_line=$start_line + for context_line in "${context_lines[@]}"; do + context_line="${context_line%$'\n'}" + if [[ $current_line -eq $line ]]; then + echo " ${DARKYELLOW}$current_line${NORMAL} ${context_line}" + else + echo " ${DARKGRAY}$current_line${NORMAL} ${context_line}" + fi + ((current_line++)) + done + done >&2 +} + + clean_cache() { local i=0 for f in $(ls -t "$CACHEDIR/"*.cache.* 2>/dev/null | tail -n +500); do @@ -2407,7 +2483,7 @@ get_all_services() { fi if [ -z "$GLOBAL_ALL_RELATIONS" ]; then - err "Can't access global \$GLOBAL_ALL_RELATIONS" + err-d "Can't access global \$GLOBAL_ALL_RELATIONS" return 1 fi @@ -2435,7 +2511,7 @@ get_service_relations () { fi if [ -z "$GLOBAL_ALL_RELATIONS" ]; then - err "Can't access global \$GLOBAL_ALL_RELATIONS" + err-d "Can't access global \$GLOBAL_ALL_RELATIONS" return 1 fi @@ -2485,6 +2561,10 @@ export -f get_service_relation get_service_incoming_relations() { local service="$1" relation="$2" cache_file="$state_tmpdir/$FUNCNAME.cache.$(H "$@" "$GLOBAL_ALL_RELATIONS_HASH")" \ s rn ts rc td + if [ -z "$GLOBAL_ALL_RELATIONS" ]; then + err-d "Can't access global \$GLOBAL_ALL_RELATIONS" + return 1 + fi if [ -e "$cache_file" ]; then #debug "$FUNCNAME: SESSION cache hit $1" cat "$cache_file" @@ -4915,7 +4995,8 @@ if [ "$is_docker_compose_action" -a "${#services_args[@]}" -gt 0 ]; then services=($(get_master_services "${services_args[@]}")) || exit 1 if [ "$action" == "up" ]; then declare -A seen - for service in $(get_ordered_service_dependencies "${services_args[@]}"); do + services=($(get_ordered_service_dependencies "${services_args[@]}")) || exit 1 + for service in "${services[@]}"; do mservice=$(get_master_service_for_service "$service") || exit 1 [ "${seen[$mservice]}" ] && continue type="$(get_service_type "$mservice")" || exit 1