penguin/utils

my env utils

bash/rbashrc

raw ยท 10325 bytes

#!/usr/bin/env bash

case $- in
  *i*) ;;
  *) return ;;
esac

__RBASH_SCRIPT="${BASH_SOURCE[0]}"

RBASH_HOME="${RBASH_HOME:-$HOME/.rbash}"
RBASH_REMOTE="${RBASH_REMOTE:-https://sgit.k8s.astropenguin.net}"
RBASH_REPO="${RBASH_REPO:-penguin/utils}"
RBASH_REF="${RBASH_REF:-master}"
RBASH_PATH="${RBASH_PATH:-bash}"

RBASH_CACHE_DIR="$RBASH_HOME/cache"
RBASH_CONFIG="$RBASH_HOME/config"
RBASH_ENV="$RBASH_HOME/env"
RBASH_MERGED="$RBASH_CACHE_DIR/merged"
RBASH_BIN="$RBASH_HOME/bin"

# ---- rbash: bash/core/00_env.bash ----

# Host prompt placeholders can be replaced by rbash-upgrade/install.
RHOSTNAME="${RHOSTNAME:-<HOSTNAME>}"
RDOMAIN="${RDOMAIN:-<DOMAIN>}"
RCOLOR="${RCOLOR:-<RCOLOR>}"

if [ "$RHOSTNAME" = "<HOSTNAME>" ]; then
	RHOSTNAME="$(whoami)"
	RDOMAIN="@HOST_NAME"
	RCOLOR=$(( RANDOM * 6 / 32767 + 1 ))
fi

_RBASH_LOADED=


# ---- rbash: bash/core/05_config.bash ----

__rbash_init_config() {
  if [ -f "$RBASH_CONFIG" ]; then
    return 0
  fi

  rbash_download "$RBASH_REMOTE/$RBASH_REPO/raw/$RBASH_REF/$RBASH_PATH/defaults.conf" \
    > "$RBASH_CONFIG" || {
      cat > "$RBASH_CONFIG" <<'EOF'
rbash_load "sources/10_aliases"
rbash_load "sources/12_shortcuts"
rbash_load "sources/20_fast-greps"
rbash_load "sources/40_go-command"
rbash_load "sources/41_pivot-command"
EOF
    }

  chmod 600 "$RBASH_CONFIG"
}


# ---- rbash: bash/core/10_shell.bash ----

# Source global definitions.
if [ -f /etc/bashrc ]; then
  # shellcheck source=/etc/bashrc
  source /etc/bashrc
fi

HISTCONTROL=ignoreboth
HISTSIZE=1000
HISTFILESIZE=2000

shopt -s histappend
shopt -s checkwinsize

if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
  debian_chroot="$(cat /etc/debian_chroot)"
fi

if [ -f "$HOME/.bash_aliases" ]; then
  # shellcheck source=/dev/null
  source "$HOME/.bash_aliases"
fi

if ! shopt -oq posix; then
  if [ -f /usr/share/bash-completion/bash_completion ]; then
    # shellcheck source=/usr/share/bash-completion/bash_completion
    source /usr/share/bash-completion/bash_completion
  elif [ -f /etc/bash_completion ]; then
    # shellcheck source=/etc/bash_completion
    source /etc/bash_completion
  fi
fi

__func_head() {
  echo "Usage: ${FUNCNAME[1]} $*"
}

__func_help() {
  echo "       ${FUNCNAME[1]} $*"
}

# ---- rbash: bash/core/20_io.bash ----

rbash_url() {
  local path="$1"
  printf '%s/%s/raw/%s/%s/%s\n' \
    "$RBASH_REMOTE" "$RBASH_REPO" "$RBASH_REF" "$RBASH_PATH" "$path"
}

rbash_download() {
  local url="$1"

  if command -v curl >/dev/null 2>&1; then
    curl -fsSL "$url" || {
      local rc=$?
      echo "rbash: download failed: $url" >&2
      echo "rbash: curl exit code: $rc" >&2
      return "$rc"
    }
  elif command -v wget >/dev/null 2>&1; then
    wget -qO- "$url" || {
      local rc=$?
      echo "rbash: download failed: $url" >&2
      echo "rbash: wget exit code: $rc" >&2
      return "$rc"
    }
  else
    echo "rbash: curl or wget is required" >&2
    return 127
  fi
}

rbash_cache() {
  local url="$1"
  local key file

  if [ -z "${RBASH_CACHE_DIR:-}" ]; then
    echo "rbash: RBASH_CACHE_DIR is not set" >&2
    return 1
  fi

  mkdir -p "$RBASH_CACHE_DIR" || {
    echo "rbash: failed to create cache dir: $RBASH_CACHE_DIR" >&2
    return 1
  }

  if command -v md5sum >/dev/null 2>&1; then
    key="$(printf '%s' "$url" | md5sum | cut -d' ' -f1)"
  else
    key="$(printf '%s' "$url" | md5 | cut -d' ' -f1)"
  fi

  file="$RBASH_CACHE_DIR/$key"

  if [ ! -f "$file" ]; then
    rbash_download "$url" > "$file" || {
      local rc=$?
      rm -f "$file"
      echo "rbash: download failed: $url" >&2
      return "$rc"
    }
    chmod 700 "$file"
  fi

  printf '%s\n' "$file"
}

_require_jq() {
  if ! command -v jq >/dev/null 2>&1; then
    echo "This command requires jq." >&2
    return 1
  fi
}


# ---- rbash: bash/core/30_loader.bash ----

rbash-reload() {
  _RBASH_LOADED=
  _RBASH_MERGE_START=1

  mkdir -p "$RBASH_CACHE_DIR"

  rm -f "$RBASH_MERGED"
  : > "$RBASH_MERGED" || {
    echo "rbash: failed to write merged cache: $RBASH_MERGED" >&2
    _RBASH_MERGE_START=
    return 1
  }

  # shellcheck source=/dev/null
  source "$RBASH_CONFIG"

  _RBASH_MERGE_START=
  _RBASH_LOADED=1
}

__rbash_autoload() {
  if [ -n "${_RBASH_LOADED:-}" ]; then
    return 0
  fi

  if [ -f "$RBASH_MERGED" ]; then
    # shellcheck source=/dev/null
    source "$RBASH_MERGED" && {
      _RBASH_LOADED=1
      return 0
    }

    echo "rbash: merged cache failed, rebuilding" >&2
    rm -f "$RBASH_MERGED"
  fi

  rbash-reload
}

rbash_load() {
  local rel="$1"
  local url file

  # Normal startup should not individually fetch sources.
  # It should source RBASH_MERGED via __rbash_autoload.
  if [ -z "${_RBASH_MERGE_START:-}" ]; then
    __rbash_autoload
    return 0
  fi

  echo "Load: $rel"

  url="$(rbash_url "$rel")"
  file="$(rbash_cache "$url")" || {
    echo "    ..download failed" >&2
    return 1
  }

  # shellcheck source=/dev/null
  if source "$file"; then
    {
      printf 'echo "Load: %s"\n' "$rel"
      cat "$file"
      printf '\n'
    } >> "$RBASH_MERGED"
  else
    echo "    ..failed" >&2
    printf 'echo "Skipped: %s. Use rbash-reload to try again."\n' "$rel" >> "$RBASH_MERGED"
  fi

  chmod 700 "$RBASH_MERGED"
}

rbash-run() {
  local rel="$1"
  local file

  shift || true

  echo "Getting: $rel"

  file="$(rbash_cache "$(rbash_url "$rel")")" || return 1
  "$file" "$@"
}


# ---- rbash: bash/core/40_sources.bash ----

rbash_sources_json_url() {
  printf '%s/%s/tree/%s/tree.json\n' \
    "$RBASH_REMOTE" "$RBASH_REPO" "$RBASH_REF"
}

declare -gA _RBASH_DICT

rbash-list() {
  _require_jq || return 1

  local item key enabled_marker
  echo "Available sources:"

  while IFS= read -r item; do
    key="$(printf '%s' "$item" | grep -o '^[0-9]\+' || true)"
    [ -n "$key" ] && _RBASH_DICT["s$key"]="$item"

    enabled_marker=" "
    if grep -q -F "rbash_load \"sources/$item\"" "$RBASH_CONFIG"; then
      enabled_marker="*"
    fi

    printf '  %s %s\n' "$enabled_marker" "$item"
  done < <(
    rbash_download "$(rbash_sources_json_url)" |
      jq -r '
        .[]
        | select(.path == "bash/sources" or (.path | startswith("bash/sources/")))
        | .path
        | sub("^bash/sources/?"; "")
        | select(length > 0)
      '
  )
}

r2ensource() {
  rbash-list || return 1

  local ids id selected tmp
  local -a enable=()

  read -r -p "Enter the script id XX, delimited by space: " ids

  for id in $ids; do
    selected="${_RBASH_DICT["s$id"]}"

    if [ -z "$selected" ]; then
      echo "No such id: $id" >&2
      return 1
    fi

    if grep -q -F "rbash_load \"sources/$selected\"" "$RBASH_CONFIG"; then
      echo "$selected is already enabled."
      continue
    fi

    enable+=("$selected")
  done

  tmp="$(mktemp)"
  cp "$RBASH_CONFIG" "$tmp"

  for selected in "${enable[@]}"; do
    echo "rbash_load \"sources/$selected\"" >> "$tmp"
    echo "Enabling $selected"
  done

  sort -u "$tmp" > "$RBASH_CONFIG"
  rm -f "$tmp"
  rm -rf "$RBASH_CACHE_DIR"
}

r2dissource() {
  local ids id tmp pattern

  echo "Currently enabled sources:"
  grep -E '^rbash_load "sources/' "$RBASH_CONFIG" |
    sed 's/^rbash_load "sources\///; s/"$//' |
    sed 's/^/    /'

  read -r -p "Enter the script id XX, delimited by space: " ids

  tmp="$(mktemp)"
  cp "$RBASH_CONFIG" "$tmp"

  for id in $ids; do
    pattern="rbash_load \"sources/${id}_"

    if ! grep -q -F "$pattern" "$tmp"; then
      echo "No such id: $id" >&2
      rm -f "$tmp"
      return 1
    fi

    echo "Disabling $id"
    grep -v -F "$pattern" "$tmp" > "$tmp.next"
    mv "$tmp.next" "$tmp"
  done

  sort -u "$tmp" > "$RBASH_CONFIG"
  rm -f "$tmp"
  rm -rf "$RBASH_CACHE_DIR"
}

# ---- rbash: bash/core/50_upgrade.bash ----

rbash-upgrade() {
  local target url tmp backup

  target="$__RBASH_SCRIPT"
  url="$(rbash_url "rbashrc")"

  echo "Updating $target"

  tmp="$(mktemp "${TMPDIR:-/tmp}/rbashrc.download.XXXXXX")" || return 1

  if ! rbash_download "$url" > "$tmp"; then
    echo "rbash: upgrade download failed: $url" >&2
    rm -f "$tmp"
    return 1
  fi

  if [ ! -s "$tmp" ]; then
    echo "rbash: downloaded rbashrc is empty; refusing to install" >&2
    rm -f "$tmp"
    return 1
  fi

  if ! grep -q 'RBASH_HOME' "$tmp"; then
    echo "rbash: downloaded file does not look like rbashrc; refusing to install" >&2
    rm -f "$tmp"
    return 1
  fi

  if ! bash -n "$tmp"; then
    echo "rbash: downloaded rbashrc has syntax errors; refusing to install" >&2
    rm -f "$tmp"
    return 1
  fi

  backup="$target.bak.$(date +%Y%m%d%H%M%S)"

  if [ -f "$target" ]; then
    cp "$target" "$backup" || {
      echo "rbash: failed to create backup: $backup" >&2
      rm -f "$tmp"
      return 1
    }
    echo "Backup: $backup"
  fi

  chmod 700 "$tmp"

  if ! mv "$tmp" "$target"; then
    echo "rbash: failed to replace $target" >&2
    echo "rbash: backup remains at $backup" >&2
    rm -f "$tmp"
    return 1
  fi

  rm -rf "$RBASH_CACHE_DIR"
  mkdir -p "$RBASH_CACHE_DIR"

  # shellcheck source=/dev/null
  source "$target"
}

# ---- rbash: bash/core/80_public_api.bash ----

rbash-host() {
  local host="${1:-}"
  local domain="${2:-}"
  local color="${3:-}"

  if [ -z "$host" ] || [ -z "$domain" ]; then
    echo "Usage: rbash-host HOST DOMAIN [COLOR]" >&2
    echo "Example: rbash-host penguin @macstudio 6" >&2
    return 1
  fi

  mkdir -p "$RBASH_HOME"

  {
    printf 'RHOSTNAME=%q\n' "$host"
    printf 'RDOMAIN=%q\n' "$domain"
    if [ -n "$color" ]; then
      printf 'RCOLOR=%q\n' "$color"
    fi
  } > "$RBASH_ENV"

  echo "Wrote $RBASH_ENV"

  # shellcheck source=/dev/null
  source "$RBASH_ENV"

  __rbash_setup_prompt
}

# ---- rbash: bash/core/90_prompt.bash ----

__rbash_setup_prompt() {
  local shelld="\$"

  case "$OSTYPE" in
    cygwin*)
      if net session >/dev/null 2>&1; then
        shelld="#"
      fi
      ;;
  esac

  export PS1="This is ${RHOSTNAME}\e[1;3${RCOLOR}m${RDOMAIN}\e[0m: \w\n$shelld "
  export EDITOR="${EDITOR:-vim}"
  export PATH="$RBASH_BIN:$PATH"
}


# ---- rbash: bash/core/99_main.bash ----

__rbash_main() {
  mkdir -p "$RBASH_HOME" "$RBASH_CACHE_DIR" "$RBASH_BIN"

  __rbash_init_config

  if [ -f "$RBASH_ENV" ]; then
    # shellcheck source=/dev/null
    source "$RBASH_ENV"
  fi

  __rbash_autoload
  __rbash_setup_prompt
}

__rbash_main
unset -f __rbash_main