diff --git a/src/freedombone-powerline b/src/freedombone-powerline new file mode 100755 index 00000000..149397a2 --- /dev/null +++ b/src/freedombone-powerline @@ -0,0 +1,357 @@ +#!/usr/bin/env bash + +# Based on https://github.com/undu/bash-powerline + +__powerline() { + + # User config variables, + # it's recommended to override those variables through .bashrc or similar + # + # Use powerline mode + # readonly POWERLINE_FONT='' + # + # Always show user in the prompt + # readonly SHOW_USER='' + # + # Never show a default user + # readonly DEFAULT_USER='user' + + # Default background and foreground ANSI colours + readonly DEFAULT_BG=0 + readonly DEFAULT_FG=7 + + # Max length of full path + readonly MAX_PATH_LENGTH=30 + + # Unicode symbols + if [ -z "${POWERLINE_FONT+x}" ]; then + readonly GIT_BRANCH_SYMBOL='⑂' + else + readonly GIT_BRANCH_SYMBOL='' + fi + readonly GIT_BRANCH_CHANGED_SYMBOL='Δ' + readonly GIT_NEED_PUSH_SYMBOL='↑' + readonly GIT_NEED_PULL_SYMBOL='↓' + + # Powerline symbols + readonly BLOCK_START='' + + # ANSI Colours + readonly BLACK=0 + readonly RED=1 + readonly GREEN=2 + readonly YELLOW=3 + readonly BLUE=4 + readonly MAGENTA=5 + readonly CYAN=6 + readonly WHITE=7 + + readonly BLACK_BRIGHT=8 + readonly RED_BRIGHT=9 + readonly GREEN_BRIGHT=10 + readonly YELLOW_BRIGHT=11 + readonly BLUE_BRIGHT=12 + readonly MAGENTA_BRIGHT=13 + readonly CYAN_BRIGHT=14 + readonly WHITE_BRIGHT=15 + + # Font effects + readonly DIM="\[$(tput dim)\]" + readonly REVERSE="\[$(tput rev)\]" + readonly RESET="\[$(tput sgr0)\]" + readonly BOLD="\[$(tput bold)\]" + + # Generate terminal colour codes + # $1 is an int (a colour) and $2 must be 'fg' or 'bg' + __colour() { + case "$2" in + 'fg'*) + echo "\[$(tput setaf "$1")\]" + ;; + 'bg'*) + echo "\[$(tput setab "$1")\]" + ;; + *) + echo "\[$(tput setab "$1")\]" + ;; + esac + } + + # Generate a single-coloured block for the prompt + __prompt_block() { + local bg; local fg + if [ ! -z "${1+x}" ]; then + bg=$1 + else + if [ ! -z "$last_bg" ]; then + bg=$last_bg + else + bg=$DEFAULT_BG + fi + fi + if [ ! -z "${2+x}" ]; then + fg=$2 + else + fg=$DEFAULT_FG + fi + + local block + + # Need to generate a separator if the background changes + if [[ ! -z "$last_bg" && "$bg" != "$last_bg" && ! -z "${POWERLINE_FONT+x}" ]]; then + block+="$(__colour "$bg" 'bg')" + block+="$(__colour "$last_bg" 'fg')" + block+="$BLOCK_START $RESET" + block+="$(__colour "$bg" 'bg')" + block+="$(__colour "$fg" 'fg')" + else + block+="$(__colour "$bg" 'bg')" + block+="$(__colour "$fg" 'fg')" + block+=" " + fi + + if [ ! -z "${3+x}" ]; then + block+="$3 $RESET" + fi + + last_bg=$bg + + __block_text="$block" + } + + function __end_block() { + __block_text='' + if [ ! -z "$last_bg" ]; then + if [ ! -z "${POWERLINE_FONT+x}" ]; then + __block_text+="$(__colour $DEFAULT_BG 'bg')" + __block_text+="$(__colour "$last_bg" 'fg')" + __block_text+="$BLOCK_START$RESET" + __block_text+="$(__colour $DEFAULT_BG 'bg')" + __block_text+="$(__colour "$DEFAULT_FG" 'fg')" + else + __block_text+="$(__colour $DEFAULT_BG 'bg')" + __block_text+="$(__colour "$DEFAULT_FG" 'fg')" + fi + fi + __block_text+=' ' + } + + ### Prompt components + + __git_block() { + if ! command -V git > /dev/null; then + # git not found + __block_text='' + return + fi + # force git output in English to make our work easier + local git_eng="env LANG=C git" + + # check if pwd is under git + if ! git rev-parse --is-inside-git-dir > /dev/null 2> /dev/null; then + # not in a git repo, bail out + __block_text='' + return + fi + + # get current branch name or short SHA1 hash for detached head + local branch; local ref_symbol + branch="$($git_eng symbolic-ref --short HEAD 2>/dev/null)" + # shellcheck disable=SC2181 + if [ $? != 0 ]; then + branch="$($git_eng describe --tags --always 2>/dev/null)" + ref_symbol='➦' + else + ref_symbol=$GIT_BRANCH_SYMBOL + fi + + # In pcmode (and only pcmode) the contents of + # $gitstring are subject to expansion by the shell. + # Avoid putting the raw ref name in the prompt to + # protect the user from arbitrary code execution via + # specially crafted ref names (e.g., a ref named + # '$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)' would execute + # 'sudo rm -rf /' when the prompt is drawn). Instead, + # put the ref name in a new global variable (in the + # __git_ps1_* namespace to avoid colliding with the + # user's environment) and reference that variable from + # PS1. + # note that the $ is escaped -- the variable will be + # expanded later (when it's time to draw the prompt) + if shopt -q promptvars; then + export __git_ps1_block="$branch" + ref="$ref_symbol \${__git_ps1_block}" + else + ref="$ref_symbol $branch" + fi + + local marks + + # check if HEAD is dirty + if [ -n "$($git_eng status --porcelain 2>/dev/null)" ]; then + dirty='y' + marks+=" $GIT_BRANCH_CHANGED_SYMBOL" + fi + + # how many commits local branch is ahead/behind of remote? + local stat; local aheadN; local behindN + stat="$($git_eng status --porcelain --branch 2>/dev/null | grep '^##' | grep -o '\[.\+\]$')" + aheadN="$(echo "$stat" | grep -o 'ahead [[:digit:]]\+' | grep -o '[[:digit:]]\+')" + behindN="$(echo "$stat" | grep -o 'behind [[:digit:]]\+' | grep -o '[[:digit:]]\+')" + [ -n "$aheadN" ] && marks+=" $GIT_NEED_PUSH_SYMBOL$aheadN" + [ -n "$behindN" ] && marks+=" $GIT_NEED_PULL_SYMBOL$behindN" + + local bg; local fg + fg=$BLACK + if [ -z "$dirty" ]; then + bg=$GREEN + else + bg=$YELLOW + fi + + __prompt_block $bg $fg "$ref$marks" + } + + __virtualenv_block() { + # Copied from Python virtualenv's activate.sh script. + # https://github.com/pypa/virtualenv/blob/a9b4e673559a5beb24bac1a8fb81446dd84ec6ed/virtualenv_embedded/activate.sh#L62 + # License: MIT + if [ -n "$VIRTUAL_ENV" ]; then + local venv + # In pcmode (and only pcmode) the contents of + # $gitstring are subject to expansion by the shell. + # Avoid putting the raw ref name in the prompt to + # protect the user from arbitrary code execution via + # specially crafted ref names (e.g., a ref named + # '$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)' would execute + # 'sudo rm -rf /' when the prompt is drawn). Instead, + # put the ref name in a new global variable (in the + # __git_ps1_* namespace to avoid colliding with the + # user's environment) and reference that variable from + # PS1. + # note that the $ is escaped -- the variable will be + # expanded later (when it's time to draw the prompt) + if shopt -q promptvars; then + export __venv_ps1_block + __venv_ps1_block=$(basename "$VIRTUAL_ENV") + venv="$ref_symbol \${__venv_ps1_block}" + else + venv="$(basename "$VIRTUAL_ENV")" + fi + __prompt_block $WHITE $BLACK "$venv" + else + __block_text='' + fi + } + + __pwd_block() { + # Use ~ to represent $HOME prefix + local pwd; pwd=$(pwd | sed -e "s|^$HOME|~|") + # shellcheck disable=SC1001,SC2088 + if [[ ( $pwd = ~\/*\/* || $pwd = \/*\/*/* ) && ${#pwd} -gt $MAX_PATH_LENGTH ]]; then + local IFS='/' + read -ra split <<< "$pwd" + if [[ $pwd = ~* ]]; then + pwd="~/${split[1]}/.../${split[*]:(-2):1}/${split[*]:(-1)}" + else + pwd="/${split[1]}/.../${split[*]:(-2):1}/${split[*]:(-1)}" + fi + fi + __prompt_block $BLACK_BRIGHT $WHITE_BRIGHT "$pwd" + } + + # superuser or not, here I go! + __user_block() { + # Colours to use + local fg=$WHITE_BRIGHT + local bg=$BLUE + + if [[ ! -z "$SSH_CLIENT" ]]; then + local show_host="y" + bg=$CYAN + fi + + if [ -z "$(id -u "$USER")" ]; then + bg=$RED + fi + + # shellcheck disable=SC2153 + if [[ ! -z "${SHOW_USER+x}" || ( ! -z "${DEFAULT_USER+x}" && "$DEFAULT_USER" != "$(whoami)" ) ]]; then + local show_user="y" + fi + + local text + if [ ! -z ${show_user+x} ]; then + text+="$BOLD$(whoami)" + fi + if [ ! -z ${show_host+x} ]; then + if [ ! -z "${text+x}" ]; then + text+="@" + fi + text+="\h" + fi + + if [ ! -z ${text+x} ]; then + __prompt_block $bg $fg $text + fi + } + + __status_block() { + local text + if [ "$exit_code" != 0 ]; then + __prompt_block $BLACK $RED '✘' + text+=$__block_text + fi + + if [ "$(id -u "$USER")" == 0 ]; then + __prompt_block $BLACK $YELLOW '⚡' + text+=$__block_text + fi + + if [ "$(jobs -l | wc -l)" != 0 ]; then + __prompt_block $BLACK $CYAN '⚙' + text+=$__block_text + fi + + if [ ! -z "$text" ]; then + __block_text=$text + else + __block_text='' + fi + } + + # Build the prompt + prompt() { + # I don't like bash; execute first to capture correct status code + local exit_code=$? + # shellcheck disable=SC2091 + $(history -a ; history -n) + + last_bg='' + + PS1='' + + __status_block + PS1+=$__block_text + + __virtualenv_block + PS1+=$__block_text + + __user_block + PS1+=$__block_text + + __pwd_block + PS1+=$__block_text + + __git_block + PS1+=$__block_text + + __end_block + PS1+=$__block_text + } + + PROMPT_COMMAND=prompt +} + +__powerline +unset __powerline diff --git a/src/freedombone-utils-setup b/src/freedombone-utils-setup index 1d68153d..0e0966ac 100755 --- a/src/freedombone-utils-setup +++ b/src/freedombone-utils-setup @@ -627,6 +627,28 @@ function setup_firewall { firewall_block_bad_ip_ranges } +function setup_powerline { + if [ -d ~/${PROJECT_NAME}/src/${PROJECT_NAME}-powerline ]; then + cp ~/${PROJECT_NAME}/src/${PROJECT_NAME}-powerline ~/.powerline.bash + cp ~/${PROJECT_NAME}/src/${PROJECT_NAME}-powerline /etc/skel/.powerline.bash + else + if [ -d /home/${MY_USERNAME}/${PROJECT_NAME}/src/${PROJECT_NAME}-powerline ]; then + cp /home/${MY_USERNAME}/${PROJECT_NAME}/src/${PROJECT_NAME}-powerline ~/.powerline.bash + cp /home/${MY_USERNAME}/${PROJECT_NAME}/src/${PROJECT_NAME}-powerline /etc/skel/.powerline.bash + fi + fi + if ! grep -q "powerline" ~/.bashrc; then + if [ -f ~/.powerline.bash ]; then + echo 'source ~/.powerline.bash' >> ~/.bashrc + fi + fi + if ! grep -q "powerline" /etc/skel/.bashrc; then + if [ -f /etc/skel/.powerline.bash ]; then + echo 'source ~/.powerline.bash' >> /etc/skel/.bashrc + fi + fi +} + function setup_utils { read_config_param "PROJECT_REPO" write_config_param "PROJECT_REPO" "$PROJECT_REPO" @@ -828,6 +850,9 @@ function setup_utils { function_check create_usb_canary create_usb_canary + + function_check setup_powerline + setup_powerline } function setup_email {