Última atividade 1789532044

Customization of synth-shell for personal use

shell-prompt.sh Bruto
1#!/usr/bin/env bash
2
3##
4##
5## Visit for instructions and more information:
6## https://github.com/andresgongora/synth-shell/
7##
8##
9
10##
11##==================================================================================================
12## CODE PARSERS
13##==================================================================================================
14##------------------------------------------------------------------------------
15##
16get8bitCode()
17{
18 CODE=$1
19 case $CODE in
20 default) echo 9 ;;
21 none) echo 9 ;;
22 black) echo 0 ;;
23 red) echo 1 ;;
24 green) echo 2 ;;
25 yellow) echo 3 ;;
26 blue) echo 4 ;;
27 magenta|purple|pink) echo 5 ;;
28 cyan) echo 6 ;;
29 light-gray) echo 7 ;;
30 dark-gray) echo 60 ;;
31 light-red) echo 61 ;;
32 light-green) echo 62 ;;
33 light-yellow) echo 63 ;;
34 light-blue) echo 64 ;;
35 light-magenta|light-purple) echo 65 ;;
36 light-cyan) echo 66 ;;
37 white) echo 67 ;;
38 *) echo 0 ;;
39 esac
40}
41
42getColorCode()
43{
44 COLOR=$1
45 if [ "$COLOR" -eq "$COLOR" ] 2>/dev/null; then
46 if [ "$COLOR" -gt 0 ] && [ "$COLOR" -lt 256 ]; then
47 echo "38;5;$COLOR"
48 else
49 echo 0
50 fi
51 else
52 BITCODE=$(get8bitCode "$COLOR")
53 COLORCODE=$((BITCODE + 30))
54 echo "$COLORCODE"
55 fi
56}
57
58getBackgroundCode()
59{
60 COLOR=$1
61 if [ "$COLOR" -eq "$COLOR" ] 2>/dev/null; then
62 if [ "$COLOR" -gt 0 ] && [ "$COLOR" -lt 256 ]; then
63 echo "48;5;$COLOR"
64 else
65 echo 0
66 fi
67 else
68 BITCODE=$(get8bitCode "$COLOR")
69 COLORCODE=$((BITCODE + 40))
70 echo "$COLORCODE"
71 fi
72}
73
74getEffectCode()
75{
76 EFFECT=$1
77 NONE=0
78 case $EFFECT in
79 none|default) echo "$NONE" ;;
80 bold|bright) echo 1 ;;
81 dim) echo 2 ;;
82 underline) echo 4 ;;
83 blink) echo 5 ;;
84 reverse) echo 7 ;;
85 hidden) echo 8 ;;
86 strikeout) echo 9 ;;
87 *) echo "$NONE" ;;
88 esac
89}
90
91getFormattingSequence()
92{
93 START='\e[0;'
94 MIDLE=$1
95 END='m'
96 echo -n "$START$MIDLE$END"
97}
98
99##==================================================================================================
100## AUX
101##==================================================================================================
102applyCodeToText()
103{
104 local RESET
105 RESET=$(getFormattingSequence "$(getEffectCode none)")
106 TEXT=$1
107 CODE=$2
108 echo -n "$CODE$TEXT$RESET"
109}
110
111##==================================================================================================
112## MAIN FUNCTIONS
113##==================================================================================================
114getFormatCode()
115{
116 local RESET
117 RESET=$(getFormattingSequence "$(getEffectCode none)")
118
119 if [ "$#" -eq 0 ]; then
120 echo -n "$RESET"
121 elif [ "$#" -eq 1 ]; then
122 TEXT_COLOR=$(getFormattingSequence "$(getColorCode "$1")")
123 echo -n "$TEXT_COLOR"
124 else
125 FORMAT=""
126 while [ "$1" != "" ]; do
127 TYPE=$1
128 ARGUMENT=$2
129 case $TYPE in
130 -c) CODE=$(getColorCode "$ARGUMENT") ;;
131 -b) CODE=$(getBackgroundCode "$ARGUMENT") ;;
132 -e) CODE=$(getEffectCode "$ARGUMENT") ;;
133 *) CODE="" ;;
134 esac
135
136 if [ "$FORMAT" != "" ]; then
137 FORMAT="$FORMAT;"
138 fi
139 FORMAT="$FORMAT$CODE"
140 shift
141 shift
142 done
143
144 FORMAT_CODE=$(getFormattingSequence "$FORMAT")
145 echo -n "${FORMAT_CODE}"
146 fi
147}
148
149formatText()
150{
151 local RESET
152 RESET=$(getFormattingSequence "$(getEffectCode none)")
153
154 if [ "$#" -eq 0 ]; then
155 echo -n "${RESET}"
156 elif [ "$#" -eq 1 ]; then
157 TEXT=$1
158 echo -n "${TEXT}${RESET}"
159 else
160 TEXT=$1
161 FORMAT_CODE=$(getFormatCode "${@:2}")
162 applyCodeToText "$TEXT" "$FORMAT_CODE"
163 fi
164}
165
166removeColorCodes()
167{
168 printf "%s" "$1" | sed 's/\x1b\[[0-9;]*m//g'
169}
170
171##==============================================================================
172## MAIN FORMAT
173##==============================================================================
174format="USER HOST PWD GIT PYENV TF KUBE DATE CLOCK"
175separator_char='\uE0B0'
176separator_padding_left=''
177separator_padding_right=''
178segment_padding=' '
179enable_vertical_padding=true
180enable_command_on_new_line=true
181
182# Nerd Font icons.
183# Font hanya perlu tersedia pada terminal/client yang merender sesi SSH
184# (misalnya Termius). Tidak perlu install Nerd Font di setiap server Debian.
185icon_user=$'\uf007'
186icon_folder=$'\uf07b'
187icon_calendar=$'\uf073'
188icon_clock=$'\uf017'
189icon_debian=$'\ue77d'
190icon_linux=$'\uf17c'
191
192##==============================================================================
193## USER
194##==============================================================================
195font_color_user="black"
196background_user="green"
197texteffect_user="bold"
198
199##==============================================================================
200## HOST
201##==============================================================================
202font_color_host="white"
203background_host="light-blue"
204texteffect_host="bold"
205
206##==============================================================================
207## PWD (working dir)
208##==============================================================================
209font_color_pwd="dark-gray"
210background_pwd="white"
211texteffect_pwd="bold"
212max_pwd_char="25"
213
214##==============================================================================
215## GIT
216##==============================================================================
217font_color_git="light-gray"
218background_git="dark-gray"
219texteffect_git="bold"
220git_symbol_synced=''
221git_symbol_unpushed=' ▲'
222git_symbol_unpulled=' ▼'
223git_symbol_unpushedunpulled=' ◆'
224git_symbol_dirty=' ◔'
225git_symbol_dirty_unpushed=' ◔ △'
226git_symbol_dirty_unpulled=' ◔ ▽'
227git_symbol_dirty_unpushedunpulled=' ◔ ◇'
228git_symbol_stash='🗎'
229git_update_period_minutes=15
230
231##==============================================================================
232## PYENV
233##==============================================================================
234font_color_pyenv="white"
235background_pyenv="blue"
236texteffect_pyenv="bold"
237
238##==============================================================================
239## KUBERNETES
240##==============================================================================
241font_color_kube="white"
242background_kube="purple"
243texteffect_kube="bold"
244
245##==============================================================================
246## TERRAFORM WORKSPACE
247##==============================================================================
248font_color_tf="purple"
249background_tf="light-purple"
250texteffect_tf="bold"
251
252##==============================================================================
253## CLOCK
254##==============================================================================
255font_color_clock="white"
256background_clock="black"
257texteffect_clock="bold"
258clock_format="%H:%M:%S"
259
260##==============================================================================
261## DATE
262##==============================================================================
263font_color_date="white"
264background_date="light-purple"
265texteffect_date="bold"
266date_format="${hari}, %d ${bulan} %Y"
267
268##==============================================================================
269## INPUT (user typed command)
270##==============================================================================
271font_color_input="45"
272background_input="none"
273texteffect_input="bold"
274
275##==============================================================================
276## ROOT WARNING PALETTE
277##==============================================================================
278# Jika shell berjalan sebagai root, pertahankan layout/informasi prompt,
279# tetapi gunakan warna warning agar sesi privileged langsung terlihat.
280if [ "${EUID:-$(id -u)}" -eq 0 ]; then
281 font_color_user="white"
282 background_user="red"
283 texteffect_user="bold"
284
285 font_color_host="white"
286 background_host="light-red"
287 texteffect_host="bold"
288
289 font_color_pwd="black"
290 background_pwd="yellow"
291 texteffect_pwd="bold"
292
293 font_color_git="white"
294 background_git="red"
295 texteffect_git="bold"
296
297 font_color_pyenv="white"
298 background_pyenv="light-red"
299 texteffect_pyenv="bold"
300
301 font_color_kube="black"
302 background_kube="light-yellow"
303 texteffect_kube="bold"
304
305 font_color_tf="white"
306 background_tf="red"
307 texteffect_tf="bold"
308
309 font_color_date="white"
310 background_date="light-red"
311 texteffect_date="bold"
312
313 font_color_clock="white"
314 background_clock="red"
315 texteffect_clock="bold"
316
317 font_color_input="light-red"
318 background_input="none"
319 texteffect_input="bold"
320fi
321
322##==============================================================================
323## EXTERNAL DEPENDENCIES
324##==============================================================================
325[ "$(type -t include)" != 'function' ] && {
326 include() {
327 {
328 [ -z "$_IR" ] &&
329 _IR="$PWD" &&
330 cd "$(dirname "${BASH_SOURCE[0]}")" &&
331 include "$1" &&
332 cd "$_IR" &&
333 unset _IR
334 } || {
335 local d="$PWD" &&
336 cd "$(dirname "$PWD/$1")" &&
337 . "$(basename "$1")" &&
338 cd "$d"
339 } || {
340 echo "Include failed $PWD->$1" &&
341 exit 1
342 }
343 }
344}
345
346synth_shell_prompt()
347{
348##==============================================================================
349## FUNCTIONS
350##==============================================================================
351
352getOSIcon()
353{
354 if [ -r /etc/os-release ]; then
355 local os_id
356 os_id=$(. /etc/os-release; printf '%s' "${ID:-}")
357
358 case "$os_id" in
359 debian)
360 printf '%s' "$SSP_ICON_DEBIAN"
361 ;;
362 *)
363 printf '%s' "$SSP_ICON_LINUX"
364 ;;
365 esac
366 else
367 printf '%s' "$SSP_ICON_LINUX"
368 fi
369}
370
371getGitBranch()
372{
373 if ! command -v git >/dev/null 2>&1; then
374 echo ""
375 return
376 fi
377
378 local branch
379 branch=$(git branch 2>/dev/null | sed -n '/^[^*]/d;s/*\s*\(.*\)/\1/p')
380 if [[ -z "$branch" ]]; then
381 echo ""
382 return
383 fi
384
385 if [ "$SSP_GIT_UPDATE_PERIOD_MINUTES" -ge 0 ]; then
386 local d="$PWD"
387 local max_lvls=25
388
389 while [ ! -e "./.git" ] && [ "$max_lvls" -gt 0 ]; do
390 cd ..
391 max_lvls=$((max_lvls - 1))
392 done
393
394 local dot_git="${PWD}/.git"
395 cd "$d" || return
396
397 if [ -f "$dot_git" ]; then
398 dot_git=$(grep 'gitdir' "$dot_git" | sed 's/gitdir:\ //g')
399 fi
400
401 local git_last_update=""
402 if [ -d "$dot_git" ] && [ -e "${dot_git}/FETCH_HEAD" ]; then
403 git_last_update=$(stat -c "%Y" "${dot_git}/FETCH_HEAD")
404 fi
405
406 if [ -n "$git_last_update" ]; then
407 local current_timestamp
408 local elapsed_minutes
409 current_timestamp=$(date +%s)
410 elapsed_minutes=$(((current_timestamp-git_last_update)/60))
411
412 if [ "$elapsed_minutes" -ge "$SSP_GIT_UPDATE_PERIOD_MINUTES" ]; then
413 git fetch --recurse-submodules >/dev/null 2>&1 &
414 fi
415 fi
416 fi
417
418 local is_dirty=false
419 [[ -n "$(git status --porcelain 2>/dev/null)" ]] && is_dirty=true
420
421 local is_ahead=false
422 [[ "$(git status --porcelain -u no -b 2>/dev/null)" == *"ahead"* ]] && is_ahead=true
423
424 local is_behind=false
425 [[ "$(git status --porcelain -u no -b 2>/dev/null)" == *"behind"* ]] && is_behind=true
426
427 local symbol
428 if $is_dirty && $is_ahead && $is_behind; then
429 symbol=$SSP_GIT_DIRTY_DIVERGED
430 elif $is_dirty && $is_ahead; then
431 symbol=$SSP_GIT_DIRTY_AHEAD
432 elif $is_dirty && $is_behind; then
433 symbol=$SSP_GIT_DIRTY_BEHIND
434 elif $is_dirty; then
435 symbol=$SSP_GIT_DIRTY
436 elif $is_ahead && $is_behind; then
437 symbol=$SSP_GIT_DIVERGED
438 elif $is_ahead; then
439 symbol=$SSP_GIT_AHEAD
440 elif $is_behind; then
441 symbol=$SSP_GIT_BEHIND
442 else
443 symbol=$SSP_GIT_SYNCED
444 fi
445
446 local tag=""
447 if [[ -n "$(git tag --points-at HEAD 2>/dev/null)" ]]; then
448 tag=" $(git tag --points-at HEAD 2>/dev/null)"
449 fi
450
451 local git_stash=""
452 local stashed_elements
453 stashed_elements=$(git stash list 2>/dev/null | wc -l)
454
455 if [ "$stashed_elements" -gt 0 ]; then
456 git_stash=" ${stashed_elements}${SSP_GIT_STASH}"
457 fi
458
459 echo "${branch}${symbol}${git_stash}${tag}"
460}
461
462getTerraform()
463{
464 if ! command -v terraform >/dev/null 2>&1; then
465 echo ""
466 return
467 fi
468
469 if [ ! -d .terraform ]; then
470 echo ""
471 return
472 fi
473
474 local tf
475 tf="$(terraform workspace show 2>/dev/null | tr -d '\n')"
476 echo "$tf"
477}
478
479getPyenv()
480{
481 if [ -n "${CONDA_DEFAULT_ENV:-}" ]; then
482 echo "$CONDA_DEFAULT_ENV"
483 elif [ -n "${VIRTUAL_ENV:-}" ]; then
484 local regex='PS1=\"\((.*?)\)\s\$\{PS1'
485 local pyenv
486 pyenv=$(perl -n -e"/$regex/ && print \$1" "$VIRTUAL_ENV/bin/activate" 2>/dev/null)
487
488 if [ -z "${pyenv}" ]; then
489 pyenv=$(basename "$VIRTUAL_ENV")
490 fi
491
492 echo "$pyenv"
493 fi
494}
495
496getKube()
497{
498 type kubectl &>/dev/null &&
499 type yq &>/dev/null &&
500 echo -n "$(kubectl config view | yq '.contexts[].context.cluster |select(.contexts[].name == .current-context)' | head -n 1)"
501}
502
503printSegment()
504{
505 local text=$1
506 local font_color=$2
507 local background_color=$3
508 local next_background_color=$4
509 local font_effect=$5
510
511 local no_color="\[$(getFormatCode -e reset)\]"
512 local text_format="\[$(getFormatCode -c "$font_color" -b "$background_color" -e "$font_effect")\]"
513 local separator_format="\[$(getFormatCode -c "$background_color" -b "$next_background_color")\]"
514
515 printf "${text_format}${segment_padding}${text}${segment_padding}${separator_padding_left}${separator_format}${separator_char}${separator_padding_right}${no_color}"
516}
517
518get_colors_for_element()
519{
520 case $1 in
521 "USER") echo "${SSP_COLORS_USER[@]}" ;;
522 "HOST") echo "${SSP_COLORS_HOST[@]}" ;;
523 "PWD") echo "${SSP_COLORS_PWD[@]}" ;;
524 "GIT") echo "${SSP_COLORS_GIT[@]}" ;;
525 "PYENV") echo "${SSP_COLORS_PYENV[@]}" ;;
526 "KUBE") echo "${SSP_COLORS_KUBE[@]}" ;;
527 "TF") echo "${SSP_COLORS_TF[@]}" ;;
528 "CLOCK") echo "${SSP_COLORS_CLOCK[@]}" ;;
529 "DATE") echo "${SSP_COLORS_DATE[@]}" ;;
530 "INPUT") echo "${SSP_COLORS_INPUT[@]}" ;;
531 esac
532}
533
534combine_elements()
535{
536 local first=$1
537 local second=$2
538 local colors_first=($(get_colors_for_element "$first"))
539 local colors_second=($(get_colors_for_element "$second"))
540
541 case $first in
542 "USER") local text="$user" ;;
543 "HOST") local text="$host" ;;
544 "PWD") local text="$path" ;;
545 "GIT") local text="$git_branch" ;;
546 "PYENV") local text="$pyenv" ;;
547 "KUBE") local text="$kube" ;;
548 "TF") local text="$tf" ;;
549 "CLOCK") local text="$clock" ;;
550 "DATE") local text="$date" ;;
551 "INPUT") local text="" ;;
552 *) local text="" ;;
553 esac
554
555 local text_color=${colors_first[0]}
556 local bg_color=${colors_first[1]}
557 local next_bg_color=${colors_second[1]}
558 local text_effect=${colors_first[2]}
559
560 printSegment "$text" "$text_color" "$bg_color" "$next_bg_color" "$text_effect"
561}
562
563prompt_command_hook()
564{
565 local elements=(${SSP_ELEMENTS[@]})
566
567 local user="${SSP_ICON_USER} ${USER}"
568 local host="$(getOSIcon) ${HOSTNAME}"
569 local path="${SSP_ICON_FOLDER} ${PWD}"
570
571 local git_branch
572 local pyenv
573 local kube
574 local tf
575 local clock
576 local date
577
578 git_branch="$(getGitBranch)"
579 pyenv="$(getPyenv)"
580 kube="$(getKube)"
581 tf="$(getTerraform)"
582 clock="${SSP_ICON_CLOCK} $(date +"${SSP_CLOCK_FORMAT}")"
583 date="${SSP_ICON_CALENDAR} $(date +"${SSP_DATE_FORMAT}")"
584
585 if [ -z "$git_branch" ]; then
586 elements=( ${elements[@]/"GIT"} )
587 fi
588 if [ -z "$pyenv" ]; then
589 elements=( ${elements[@]/"PYENV"} )
590 fi
591 if [ -z "$tf" ]; then
592 elements=( ${elements[@]/"TF"} )
593 fi
594 if [ -z "$kube" ]; then
595 elements=( ${elements[@]/"KUBE"} )
596 fi
597
598 case $TERM in
599 xterm*|rxvt*)
600 SSP_PWD="$PWD"
601 local titlebar="\[\033]0;\${USER}@\${HOSTNAME}: \${SSP_PWD}\007\]"
602 ;;
603 *)
604 local titlebar=""
605 ;;
606 esac
607
608 PS1="${titlebar}${SSP_VERTICAL_PADDING}${SSP_NEW_LINE_LINK_TOP}"
609
610 while [ "${#elements[@]}" -gt 1 ]; do
611 local current=${elements[0]}
612 local next=${elements[1]}
613 elements=("${elements[@]:1}")
614 PS1="$PS1$(combine_elements "$current" "$next")"
615 done
616
617 local input_colors=($(get_colors_for_element "${elements[0]}"))
618 local input_color=${input_colors[0]}
619 local input_bg=${input_colors[1]}
620 local input_effect=${input_colors[2]}
621 local input_format="\[$(getFormatCode -c "$input_color" -b "$input_bg" -e "$input_effect")\]"
622 local command_start_symbol="${input_format}${SSP_BASH_SYMBOL}"
623
624 PS1="${PS1}${SSP_PROMPT_COMM_SEP}${SSP_NEW_LINE_LINK_BOTTOM}${command_start_symbol} $input_format"
625}
626
627##==============================================================================
628## MAIN
629##==============================================================================
630 if $enable_vertical_padding; then
631 local vertical_padding="\n"
632 else
633 local vertical_padding=""
634 fi
635
636 if $enable_command_on_new_line; then
637 local new_line_link_top="╭"
638 local new_line_link_bottom="╰>"
639 local prompt_command_separation="\n"
640 if [ "${EUID:-$(id -u)}" -eq 0 ]; then
641 local bash_symbol="#"
642 else
643 local bash_symbol="\$"
644 fi
645 else
646 local new_line_link_top=""
647 local new_line_link_bottom=""
648 local prompt_command_separation=""
649 local bash_symbol=""
650 fi
651
652 SSP_ELEMENTS=($format "INPUT")
653
654 SSP_COLORS_USER=($font_color_user $background_user $texteffect_user)
655 SSP_COLORS_HOST=($font_color_host $background_host $texteffect_host)
656 SSP_COLORS_PWD=($font_color_pwd $background_pwd $texteffect_pwd)
657 SSP_COLORS_GIT=($font_color_git $background_git $texteffect_git)
658 SSP_COLORS_PYENV=($font_color_pyenv $background_pyenv $texteffect_pyenv)
659 SSP_COLORS_KUBE=($font_color_kube $background_kube $texteffect_kube)
660 SSP_COLORS_TF=($font_color_tf $background_tf $texteffect_tf)
661 SSP_COLORS_CLOCK=($font_color_clock $background_clock $texteffect_clock)
662 SSP_COLORS_DATE=($font_color_date $background_date $texteffect_date)
663 SSP_COLORS_INPUT=($font_color_input $background_input $texteffect_input)
664
665 SSP_VERTICAL_PADDING=$vertical_padding
666 SSP_NEW_LINE_LINK_TOP=$new_line_link_top
667 SSP_NEW_LINE_LINK_BOTTOM=$new_line_link_bottom
668 SSP_PROMPT_COMM_SEP=$prompt_command_separation
669 SSP_BASH_SYMBOL=$bash_symbol
670 SSP_MAX_PWD_CHAR=${max_pwd_char:-25}
671
672 SSP_GIT_SYNCED=$git_symbol_synced
673 SSP_GIT_AHEAD=$git_symbol_unpushed
674 SSP_GIT_BEHIND=$git_symbol_unpulled
675 SSP_GIT_DIVERGED=$git_symbol_unpushedunpulled
676 SSP_GIT_DIRTY=$git_symbol_dirty
677 SSP_GIT_DIRTY_AHEAD=$git_symbol_dirty_unpushed
678 SSP_GIT_DIRTY_BEHIND=$git_symbol_dirty_unpulled
679 SSP_GIT_DIRTY_DIVERGED=$git_symbol_dirty_unpushedunpulled
680 SSP_GIT_STASH=$git_symbol_stash
681 SSP_GIT_UPDATE_PERIOD_MINUTES=$git_update_period_minutes
682
683 SSP_CLOCK_FORMAT=${clock_format:-"%H:%M:%S"}
684 SSP_DATE_FORMAT=${date_format:-"%A, %d %B %Y"}
685
686 SSP_ICON_USER=$icon_user
687 SSP_ICON_FOLDER=$icon_folder
688 SSP_ICON_CALENDAR=$icon_calendar
689 SSP_ICON_CLOCK=$icon_clock
690 SSP_ICON_DEBIAN=$icon_debian
691 SSP_ICON_LINUX=$icon_linux
692
693 none="$(tput sgr0)"
694 trap 'echo -ne "${none}"' DEBUG
695
696 PROMPT_COMMAND=prompt_command_hook
697}
698
699if [ "${BASH_SOURCE[0]}" == "${0}" ]; then
700 echo -e "Do not run this script, it will do nothing.\nPlease source it instead by running:\n"
701 echo -e "\t. ${BASH_SOURCE[0]}\n"
702 exit
703elif [[ $- == *i* ]]; then
704 if command -v tput >/dev/null 2>&1 && tput setaf 1 >&/dev/null; then
705 synth_shell_prompt
706 fi
707 unset synth_shell_prompt
708 unset include
709fi
710