diff --git a/config/shared/bin/24-bit-color.sh b/config/shared/bin/24-bit-color.sh deleted file mode 100644 index 81db6af..0000000 --- a/config/shared/bin/24-bit-color.sh +++ /dev/null @@ -1,99 +0,0 @@ -#!/bin/bash -# -# This file echoes a bunch of 24-bit color codes -# to the terminal to demonstrate its functionality. -# The foreground escape sequence is ^[38;2;;;m -# The background escape sequence is ^[48;2;;;m -# range from 0 to 255 inclusive. -# The escape sequence ^[0m returns output to default - -setBackgroundColor() -{ - echo -en "\x1b[48;2;$1;$2;$3""m" -} - -resetOutput() -{ - echo -en "\x1b[0m\n" -} - -# Gives a color $1/255 % along HSV -# Who knows what happens when $1 is outside 0-255 -# Echoes "$red $green $blue" where -# $red $green and $blue are integers -# ranging between 0 and 255 inclusive -rainbowColor() -{ - let h=$1/43 - let f=$1-43*$h - let t=$f*255/43 - let q=255-t - - if [ $h -eq 0 ] - then - echo "255 $t 0" - elif [ $h -eq 1 ] - then - echo "$q 255 0" - elif [ $h -eq 2 ] - then - echo "0 255 $t" - elif [ $h -eq 3 ] - then - echo "0 $q 255" - elif [ $h -eq 4 ] - then - echo "$t 0 255" - elif [ $h -eq 5 ] - then - echo "255 0 $q" - else - # execution should never reach here - echo "0 0 0" - fi -} - -for i in `seq 0 127`; do - setBackgroundColor $i 0 0 - echo -en " " -done -resetOutput -for i in `seq 255 128`; do - setBackgroundColor $i 0 0 - echo -en " " -done -resetOutput - -for i in `seq 0 127`; do - setBackgroundColor 0 $i 0 - echo -n " " -done -resetOutput -for i in `seq 255 128`; do - setBackgroundColor 0 $i 0 - echo -n " " -done -resetOutput - -for i in `seq 0 127`; do - setBackgroundColor 0 0 $i - echo -n " " -done -resetOutput -for i in `seq 255 128`; do - setBackgroundColor 0 0 $i - echo -n " " -done -resetOutput - -for i in `seq 0 127`; do - setBackgroundColor `rainbowColor $i` - echo -n " " -done -resetOutput -for i in `seq 255 128`; do - setBackgroundColor `rainbowColor $i` - echo -n " " -done -resetOutput - diff --git a/config/shared/bin/colortest b/config/shared/bin/colortest deleted file mode 100755 index 25b2516..0000000 --- a/config/shared/bin/colortest +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env bash - -# Test black and white -echo "=== Black and White ===" -printf "Normal text\n" -printf "\e[1mBold text\e[0m\n" -printf "\e[7mReverse text\e[0m\n\n" - -# Test 4-bit ANSI (16 colors) -echo "=== 4-bit ANSI Colors (16 colors) ===" -echo "Foreground colors:" -for i in {30..37}; do - printf "\e[${i}m\\e[${i}m\e[0m " -done -echo -e "\n" - -echo "Background colors:" -for i in {40..47}; do - printf "\e[${i}m\\e[${i}m\e[0m " -done -echo -e "\n" - -echo "Bright foreground colors:" -for i in {90..97}; do - printf "\e[${i}m\\e[${i}m\e[0m " -done -echo -e "\n" - -echo "Bright background colors:" -for i in {100..107}; do - printf "\e[${i}m\\e[${i}m\e[0m " -done -echo -e "\n\n" - -# Test 8-bit ANSI (256 colors) -echo "=== 8-bit ANSI Colors (256 colors) ===" -echo "16 System Colors:" -for i in {0..15}; do - printf "\e[48;5;${i}m \e[0m" - if [ $((($i + 1) % 8)) == 0 ]; then - echo - fi -done -echo - -echo "216 RGB Colors:" -for i in {16..231}; do - printf "\e[48;5;${i}m \e[0m" - if [ $((($i - 15) % 36)) == 0 ]; then - echo - fi -done -echo - -echo "24 Grayscale Colors:" -for i in {232..255}; do - printf "\e[48;5;${i}m \e[0m" - if [ $((($i - 231) % 12)) == 0 ]; then - echo - fi -done -echo -e "\n" - -# Test 24-bit true color -echo "=== 24-bit True Color (16.7 million colors) ===" -echo "RGB Color Gradient:" -awk 'BEGIN{ - s="/\\"; - for (colnum = 0; colnum<77; colnum++) { - r = 255-(colnum*255/76); - g = (colnum*510/76); - b = (colnum*255/76); - if (g>255) g = 510-g; - printf "\033[48;2;%d;%d;%dm", r,g,b; - printf "\033[38;2;%d;%d;%dm", 255-r,255-g,255-b; - printf "%s\033[0m", substr(s,colnum%2+1,1); - } - printf "\n"; -}' - -echo "RGB Color Bars:" -for r in 0 127 255; do - for g in 0 127 255; do - for b in 0 127 255; do - printf "\e[48;2;${r};${g};${b}m \e[0m" - done - printf " " - done - echo -done -echo - -# Print terminal information -echo "=== Terminal Information ===" -echo "TERM: $TERM" -echo "COLORTERM: $COLORTERM" -echo "Reported colors (tput colors): $(tput colors)" diff --git a/config/shared/bin/dev b/config/shared/bin/dev index cfd05c7..3c71099 100755 --- a/config/shared/bin/dev +++ b/config/shared/bin/dev @@ -1,327 +1,456 @@ #!/usr/bin/env bash -set -euo pipefail -source './parse_image_ref.sh' +set -e -REGISTRY="registry.tomastm.com" -REGISTRY_ABBR="tm0" +source "/home/tomas/.local/bin/barg" + +# shellcheck disable=SC2034 +SPEC=( + "command;flow;DevFlow CLI - Manage instances and development containers" + "note;Use 'flow --help' for command-specific options" + + "command;enter;Connect to a development instance via SSH" + "note;Target format: [user@]namespace@platform (e.g., 'personal@orb' or 'root@personal@orb')" + "argument;user,u;type:option;help:SSH user (overrides user in target)" + "argument;namespace,n;type:option;help:Instance namespace (overrides namespace in target)" + "argument;platform,p;type:option;help:Platform name (overrides platform in target)" + "argument;session,s;type:option;default:default;help:Development session name (default: 'default')" + "argument;no-tmux;type:flag;dest:no_tmux;default:false;help:Skip tmux attachment on connection" + "argument;dry-run,d;type:flag;dest:dry_run;default:false;help:Show SSH command without executing" + "argument;target,t;required;help:Target instance in format [user@]namespace@platform" + "argument;ssh-args;type:rest;dest:ssh_args;help:Additional SSH arguments (after --)" + "end" + + "command;create;Create and start a new development container" + "argument;image,i;required;type:option;help:Container image to use (with optional tag)" + "argument;project,p;type:option;help:Path to local project directory" + "argument;name;required;help:Container name" + "end" + + "command;exec;Execute a command or open a shell in a container" + "argument;name;required;help:Container name" + "argument;cmd;type:rest;help:Command to execute inside container (after --)" + "end" + + "command;connect;Attach or switch to the container’s tmux session" + "note;When already inside tmux, switches to the target session instead of reattaching." + "note;New tmux panes or windows in the session automatically start inside the container." + "argument;from,f;type:option;dest:name;help:Optional source container name" + "argument;name;required;help:Target container name" + "end" + + "command;list;Display all development containers and their status" + "end" + + "command;stop;Stop or kill a running development container" + "argument;from;type:option;dest:name;help:Optional source container name" + "argument;kill;type:flag;help:Use kill instead of graceful stop" + "argument;name;required;help:Target container name" + "end" + + "command;remove,rm;Remove a development container" + "argument;from;type:option;dest:name;help:Optional source container name" + "argument;force,f;type:flag;help:Force removal of container" + "argument;name;required;help:Target container name" + "end" + + "command;respawn;Restart all tmux panes for a development session" + "argument;from;type:option;dest:name;help:Optional source container name" + "argument;name;required;help:Session or container name" + "end" + + "command;test;Verify that the dev script is functioning" + "argument;from;type:option;dest:name;help:Optional source container name" + "argument;name;help:Target container name" + "end" + + "end" +) + +DEFAULT_REGISTRY="registry.tomastm.com" +DEFAULT_TAG="latest" PROJECT_DIR="$HOME/projects" PROJECT_ABBR="p" -usage() { - cat <<'EOF' -Usage: dev [options] - -Commands: - create -i, --image -p, --project - exec [-- ...] - connect - list - info - stop [--kill] - rm [--force|-f] - -Notes: - - 'exec' treats the LAST argument as ; everything before it is the command to run. - - If already inside tmux, 'connect' switches to the session; otherwise it attaches. - - New tmux panes/windows created in a session always run inside the container. - - within tmux that need : info, stop, rm, restore, connect -EOF - exit 1 -} - fail() { - printf 'Error: %s\n' "$*" >&2 - exit 1 + printf 'Error: %b\n' "$*" >&2 + exit 1 } resolve_path() { - local path="$1" - if command -v realpath >/dev/null 2>&1; then - realpath "$path" - else - echo "$(cd "$(dirname "$path")" && pwd)/$(basename "$path")" + local path="${1:-$(dirname "${BASH_SOURCE[0]}")}" + if command -v realpath >/dev/null 2>&1; then + realpath "$path" + else + echo "$(cd "$(dirname "$path")" && pwd)/$(basename "$path")" + fi +} + +# shellcheck disable=SC2178,SC2128 +parse_image_ref() { + local input="$1" + + local image_ref registry repo tag label + + if [[ $input == */* ]]; then + local prefix="${input%%/*}" + if [[ "$prefix" == "docker" ]]; then + input="docker.io/library/${input#*/}" + elif [[ "$prefix" == "tm0" ]]; then + input="${DEFAULT_REGISTRY}/${input#*/}" fi + + registry="${input%%/*}" + input=${input#*/} + else + registry="$DEFAULT_REGISTRY" + fi + + if [[ "${input##*/}" == *:* ]]; then + tag="${input##*:}" + input="${input%:*}" + else + tag="$DEFAULT_TAG" + fi + + repo="${registry}/${input}" + repo="${repo#*/}" + image_ref="${registry}/${repo}:${tag}" + + label="${registry%.*}" + label="${label##*.}/${repo##*/}" + + echo "$image_ref $repo $tag $label" } +# shellcheck disable=SC2154,SC2155 docker_container_exists() { - local name="$1" - docker container ls -a --format '{{.Names}}' | grep -Fqx "$name" + local cname="$(get_cname)" + docker container ls -a --format '{{.Names}}' | grep -Fqx "$cname" } +# shellcheck disable=SC2154,SC2155 docker_container_running() { - local name="$1" - docker container ls --format '{{.Names}}' | grep -Fqx "$name" + local cname="$(get_cname)" + docker container ls --format '{{.Names}}' | grep -Fqx "$cname" } docker_image_present() { - local ref="$1" - docker image inspect "$ref" >/dev/null 2>&1 + local ref="$1" + docker image inspect "$ref" >/dev/null 2>&1 } +# shellcheck disable=SC2154,SC2155 +get_cname() { + printf "%s" "dev-${name_arg#dev-}" +} + +cmd() { + barg_usage +} + +# shellcheck disable=SC2154,SC2155 +cmd_enter() { + # VARS: user_arg, namespace_arg, platform_arg, target_arg, session_arg, no_tmux_arg, dry_run_arg, ssh_args_arg + + # Do not run inside instance + if [[ -n "$DF_NAMESPACE" && -n "$DF_PLATFORM" ]]; then + fail "It is not recommended to run this command inside an instance.\nCurrently inside: $(tput bold)${DF_NAMESPACE}@${DF_PLATFORM}$(tput sgr0)" + fi + + local -A CONFIG_HOST=( + [orb.host]="@orb" + [utm.host]="@utm.local" + [core.host]="@core.lan" + ) + + local df_platform="" + local df_namespace="" + local df_user="" + + # Parse target: get user, namespace, platform + if [[ "$target_arg" == "*@*" ]]; then + df_platform="${target_arg##*@}" + target_arg="${target_arg%@*}" + fi + + if [[ "$target_arg" == "*@*" ]]; then + df_namespace="${target_arg##*@}" + df_user="${target_arg%@*}" + else + df_namespace="${target_arg}" + df_user="${USER}" + fi + + if [[ -n "$platform_arg" ]]; then + df_platform="$platform_arg" + fi + if [[ -n "$namespace_arg" ]]; then + df_namespace="$namespace_arg" + fi + if [[ -n "$user_arg" ]]; then + df_user="$user_arg" + fi + + # Resolve host, identity (maybe check what would the host be in order to use .ssh/config) + local host_config="${CONFIG_HOST[${df_platform}.host]}" + local ssh_host="${host_config///$df_namespace}" + if [[ -z "$ssh_host" ]]; then + fail "Invalid platform: ${df_platform}" + fi + + # Build ssh cmd: ssh + identity + tmux + envs + local ssh_cmd=(ssh -tt "${df_user}@${ssh_host}") + + if [[ "$no_tmux_arg" == "false" ]]; then + # TODO: instead of tmux,maybe use "flow" in order to attach to dev container too + ssh_cmd+=("tmux" "new-session" "-As" "$session_arg" + "-e" "DF_NAMESPACE=$df_namespace" + "-e" "DF_PLATFORM=$df_platform") + fi + + # Run or dryrun? + if [[ "$dry_run_arg" == "true" ]]; then + echo "Dry run command:" + printf '%q ' "${ssh_cmd[@]}" + echo + exit 0 + fi + + exec "${ssh_cmd[@]}" +} + +# shellcheck disable=SC2154,SC2155 cmd_create() { - local image_arg="" project_arg="" + # VARS: name_arg, image_arg, project_arg - while [[ $# -gt 0 ]]; do - case "$1" in - -i | --image) - [[ $# -ge 2 ]] || fail "Missing value for $1" - image_arg="$2" - shift 2 - ;; - -p | --project) - [[ $# -ge 2 ]] || fail "Missing value for $1" - project_arg="$2" - shift 2 - ;; - -*) usage ;; - *) break ;; - esac - done + # Check if container name already exists + local cname="$(get_cname)" + if docker_container_exists "$cname"; then + printf -v msg 'Container already exists: "%s" (from name "%s")' "$cname" "$name_arg" + fail "$msg" + fi - # Check args - local name_arg="${1:-}" - if [[ -z "$name_arg" || -z "$image_arg" || -z "$project_arg" ]]; then - fail "Missing arguments" - fi + # Check if project path is valid + local project_path + project_path="$(resolve_path "$project_arg")" + if [[ ! -d "$project_path" ]]; then + fail "Invalid project path: $project_path" + fi - # Check container name - local cname="dev-$name_arg" - if docker_container_exists "$cname"; then - fail "Container already exists: "$cname" (from name "$name_arg")" - fi + # Check image + IFS=' ' read -r image_ref _ _ _ <<<"$(parse_image_ref "$image_arg")" + if ! docker_image_present "$image_ref"; then + printf -v msg 'Image not found locally.\nTry:\n\t- docker pull %s' "$image_ref" + fail "$msg" + fi - # Check project path - local project_path - project_path="$(resolve_path "$project_arg")" - if [[ ! -d "$project_path" ]]; then - fail "Invalid project path: $project_path" - fi + # Run (= create and start container) + cmd=( + docker run -d + --name "$cname" + --label dev=true + --label "dev.name=$name_arg" + --label "dev.project_path=$project_path" + --label "dev.image_ref=$image_ref" + --network host + --init # run tini as PID 1 to handle signals & reap zombies for cleaner container shutdown + -v "$project_path:/workspace" + -v /var/run/docker.sock:/var/run/docker.sock + ) - # Check image - IFS=' ' read -r image_ref image_repo image_tag image_label <<<"$(parse_image_ref "$image_arg")" - if ! docker_image_present "$image_ref"; then - fail $'Image not found locally.\nTry:\n\t- docker pull '"$image_ref" - fi + [[ -d "$HOME/.ssh" ]] && cmd+=(-v "$HOME/.ssh:$CONTAINER_HOME/.ssh:ro") + [[ -f "$HOME/.npmrc" ]] && cmd+=(-v "$HOME/.npmrc:$CONTAINER_HOME/.npmrc:ro") + [[ -d "$HOME/.npm" ]] && cmd+=(-v "$HOME/.npm:$CONTAINER_HOME/.npm") - # Run (= create and start container) - cmd=( - docker run -d - --name "$cname" - --label dev=true - --network host - --init # run tini as PID 1 to handle signals & reap zombies for cleaner container shutdown - -v "$project_path:/workspace" - -v /var/run/docker.sock:/var/run/docker.sock - ) + docker_gid="$(getent group docker | cut -d: -f3 || true)" + [[ -n "$docker_gid" ]] && cmd+=(--group-add "$docker_gid") - [[ -d "$HOME/.ssh" ]] && cmd+=(-v "$HOME/.ssh:$CONTAINER_HOME/.ssh:ro") - [[ -f "$HOME/.npmrc" ]] && cmd+=(-v "$HOME/.npmrc:$CONTAINER_HOME/.npmrc:ro") - [[ -d "$HOME/.npm" ]] && cmd+=(-v "$HOME/.npm:$CONTAINER_HOME/.npm") + cmd+=("$image_ref" sleep infinity) + "${cmd[@]}" - docker_gid="$(getent group docker | cut -d: -f3 || true)" - [[ -n "$docker_gid" ]] && cmd+=(--group-add "$docker_gid") - - cmd+=("$image_ref" sleep infinity) - "${cmd[@]}" - - echo "$cname" -} - -cmd_exec() { - # usage: exec [-- ...] - - local name="$1" - [[ -n "$name" ]] || fail "Missing project name" - shift - - local cname="dev-$name" - if ! docker_container_running "$cname"; then - fail "Container $cname not running" - fi - - if [[ "$1" == "--" ]]; then - shift - local args=("$@") - if [[ -t 1 ]]; then - docker exec -it "$cname" "${args[@]}" - else - docker exec "$cname" "${args[@]}" - fi - return - fi - - # No command provided -> open a shell - docker exec -it "$cname" zsh -l || - docker exec -it "$cname" bash -l || - docker exec -it "$cname" sh + printf "Created and started container: %s" "$cname" } +# shellcheck disable=SC2154,SC2155 cmd_connect() { - # usage: connect [--from] + # VARS: name_arg - local from_name="" - while [[ $# -gt 0 ]]; do - case "$1" in - -f | --from) - [[ $# -ge 2 ]] || fail "Missing value for $1" - from_name="$2" - shift 2 - ;; - -*) usage ;; - *) break ;; - esac - done + local cname="$(get_cname)" + if ! docker_container_exists "$cname"; then + fail "Container does not exist: ${cname}. Run: dev create ..." + fi - local name="${1:from_name}" - [[ -n "$name" ]] || fail "Missing project name" + if ! docker_container_running "$cname"; then + docker start "$cname" >/dev/null + fi - local cname="dev-$name" - if ! docker_container_exists "$cname"; then - fail "Container does not exist: ${cname}. Run: dev create ..." - fi + if ! command -v tmux >/dev/null 2>&1; then + echo "tmux not found; falling back to direct exec" + exec "$0" exec "$cname" + fi - if ! docker_container_running "$cname"; then - docker start "$cname" >/dev/null - fi + local image_ref + image_ref="$(docker container inspect "$cname" --format '{{ .Config.Image }}')" + IFS=' ' read -r _image_ref _ _ image_label <<<"$(parse_image_ref "$image_ref")" - if ! command -v tmux >/dev/null 2>&1; then - echo "tmux not found; falling back to direct exec" - exec "$0" exec "$name" - fi + if ! tmux has-session -t "$cname" 2>/dev/null; then + tmux new-session -ds "$cname" \ + -e "DF_IMAGE=$image_label" \ + -e "DF_NAMESPACE=$DF_NAMESPACE" \ + -e "DF_PLATFORM=$DF_PLATFORM" \ + "$0 exec \"$name_arg\"" + tmux set-option -t "$cname" default-command "$0 exec \"$name_arg\"" + fi - local image_ref - image_ref="$(docker container inspect "$cname" --format '{{ .Config.Image }}')" - IFS=' ' read -r _image_ref image_repo image_tag image_label <<<"$(parse_image_ref "$image_ref")" + if [[ -n "${TMUX-}" ]]; then + tmux switch-client -t "$cname" + else + tmux attach -t "$cname" + fi +} - local tname="dev:$name" - if ! tmux has-session -t "$tname" 2>/dev/null; then - tmux new-session -ds "$tname" -e "DEV_IMAGE=$image_label" "$0 exec \"$name\"" - tmux set-option -t "$tname" default-command "$0 exec \"$name\"" - fi +# shellcheck disable=SC2154,SC2155 +cmd_exec() { + # VARS: name_arg, cmd_arg - if [[ -n "${TMUX-}" ]]; then - tmux switch-client -t "$tname" + local cname="$(get_cname)" + if ! docker_container_running "$cname"; then + fail "Container $cname not running" + fi + + if [[ -n "$cmd_arg" ]]; then + if [[ -t 0 ]]; then + docker exec -it "$cname" "${cmd_arg}" else - tmux attach -t "$tname" + docker exec "$cname" "${cmd_arg}" fi + return + fi + + # No command provided -> open a shell + docker exec --detach-keys "ctrl-q,ctrl-p" -it "$cname" zsh -l || + docker exec --detach-keys "ctrl-q,ctrl-p" -it "$cname" bash -l || + docker exec --detach-keys "ctrl-q,ctrl-p" -it "$cname" sh } shorten_project_path() { - local project="$1" + local project=$1 + local home=${HOME%/} + local projdir=${PROJECT_DIR%/} - # Case 1: path is under PROJECT_DIR - if [[ "$project" == "$PROJECT_DIR"* ]]; then - project="~/$PROJECT_ABBR${project#$PROJECT_DIR}" - # Case 2: path is under HOME (but not PROJECT_DIR) - elif [[ "$project" == "$HOME"* ]]; then - project="~${project#$HOME}" - fi + # Case 1: under PROJECT_DIR + if [[ -n ${projdir} && $project == "$projdir"/* ]]; then + # shellcheck disable=SC2088 + project="~/$PROJECT_ABBR${project#"$projdir"}" - echo "$project" + # Case 2: equals HOME + elif [[ $project == "$home" ]]; then + project="~" + + # Case 3: under HOME (but not PROJECT_DIR) + elif [[ $project == "$home"/* ]]; then + project="~${project#"$home"}" + fi + + printf '%s\n' "$project" } +# shellcheck disable=SC2154,SC2155 cmd_list() { - { - echo "NAME|IMAGE|PROJECT|STATUS" - docker ps -a --filter "label=dev=true" \ - --format '{{.Label "dev.name"}}|{{.Image}}|{{.Label "dev.project_path"}}|{{.Status}}' - } | while IFS='|' read -r fname image project status; do - # Shorten registry prefix - image="${image/$REGISTRY\//$REGISTRY_ABBR/}" + # VARS: - # Shorten project path - project="$(shorten_project_path "$project")" + { + echo "NAME|IMAGE|PROJECT|STATUS" + docker ps -a --filter "label=dev=true" \ + --format '{{.Label "dev.name"}}|{{.Image}}|{{.Label "dev.project_path"}}|{{.Status}}' + } | while IFS='|' read -r fname image project status; do + # Shorten registry prefix + image="${image/$REGISTRY\//$REGISTRY_ABBR/}" - echo "$fname|$image|$project|$status" - done | column -t -s '|' + # Shorten project path + project="$(shorten_project_path "$project")" + + echo "$fname|$image|$project|$status" + done | column -t -s '|' } +tmux_fallback_to_default_if_in_session() { + # If inside tmux and current session matches the given one, + # switch to or create 'default' before proceeding. + local target_session="$1" + + [[ -z "${TMUX-}" ]] && return 0 # not in tmux, nothing to do + + local current_session + current_session="$(tmux display-message -p '#S')" + + if [[ "$current_session" == "$target_session" ]]; then + if ! tmux has-session -t default 2>/dev/null; then + tmux new-session -ds default + fi + tmux switch-client -t default + fi +} + +# shellcheck disable=SC2154,SC2155 cmd_stop() { - local kill_flag=0 - while [[ $# -gt 0 ]]; do - case "$1" in - --kill) - kill_flag=1 - shift - ;; - -*) usage ;; - *) break ;; - esac - done - local name="${1:-}" - [[ -n "$name" ]] || fail "Missing project name" - local cname="dev-$name" + # VARS: kill_arg name_arg + local cname + cname="$(get_cname)" + docker_container_exists "$cname" || fail "Container $cname does not exist" - docker_container_exists "$cname" || fail "Container $cname does not exist" + if [[ "$kill_arg" == "true" ]]; then + echo "Killing container $cname..." + docker kill "$cname" + else + echo "Stopping container $cname..." + docker stop "$cname" + fi - if ((kill_flag)); then - echo "Killing container $cname..." - docker kill "$cname" - else - echo "Stopping container $cname..." - docker stop "$cname" - fi + tmux_fallback_to_default_if_in_session "$cname" } -cmd_rm() { - local force_flag=0 - while [[ $# -gt 0 ]]; do - case "$1" in - --force | -f) - force_flag=1 - shift - ;; - -*) usage ;; - *) break ;; - esac - done - local name="${1:-}" - [[ -n "$name" ]] || fail "Missing project name" - local cname="dev-$name" +# shellcheck disable=SC2154,SC2155 +cmd_remove() { + # VARS: force_arg name_arg + local cname + cname="$(get_cname)" + docker_container_exists "$cname" || fail "Container $cname does not exist" - docker_container_exists "$cname" || fail "Container $cname does not exist" + if [[ "$force_arg" == "true" ]]; then + echo "Removing container $cname (force)..." + docker rm -f "$cname" + else + echo "Removing container $cname..." + docker rm "$cname" + fi - if ((force_flag)); then - echo "Removing container $cname (force)..." - docker rm -f "$cname" - else - echo "Removing container $cname..." - docker rm "$cname" - fi + tmux_fallback_to_default_if_in_session "$cname" } +# shellcheck disable=SC2154,SC2155 cmd_respawn() { - local name="${1:-}" - [[ -n "$name" ]] || fail "Missing project name" - local cname="dev-$name" + # VARS: name_arg + local cname + cname="$(get_cname)" + panes=$(tmux list-panes -t "$cname" -s -F "#{session_name}:#{window_index}.#{pane_index}") - panes=$(tmux list-panes -t "$cname" -s -F "#{session_name}:#{window_index}.#{pane_index}") - - for pane in $panes; do - echo "Respawning $pane..." - tmux respawn-pane -t "$pane" - done + for pane in $panes; do + echo "Respawning $pane..." + tmux respawn-pane -t "$pane" + done } +# shellcheck disable=SC2154,SC2155 cmd_test() { - echo "Script dev is working fine!" + # VARS: name_arg + + echo "Script dev is working fine!" + if [[ -n "$name_arg" ]]; then + get_cname + fi + echo } -main() { - local cmd="${1:-}" - shift || true - case "$cmd" in - create) cmd_create "$@" ;; - connect) cmd_connect "$@" ;; - exec) cmd_exec "$@" ;; - list) cmd_list ;; - stop) cmd_stop "$@" ;; - rm) cmd_rm "$@" ;; - respawn) cmd_respawn "$@" ;; - test) cmd_test "$@" ;; - *) usage ;; - esac -} - -main "$@" +barg_run SPEC[@] "$@" diff --git a/config/shared/bin/dev-temp b/config/shared/bin/dev-temp deleted file mode 100755 index b2b66d7..0000000 --- a/config/shared/bin/dev-temp +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -set -e - -REGISTRY="registry.tomastm.com" -DEFAULT_IMAGE="base-debian" - -usage() { - echo "Usage: $0 -i " - exit 1 -} - -# Parse arguments -while getopts ":i:" opt; do - case ${opt} in - i ) - IMAGE="${OPTARG}" - ;; - \? ) - usage - ;; - esac -done - -IMAGE="${IMAGE:-$DEFAULT_IMAGE}" -FULL_IMAGE_NAME="${REGISTRY}/${IMAGE}" - -docker run --rm -it \ - --network host \ - -v "$HOME/.ssh:/home/dev/.ssh" \ - -v "$PWD:/workspace" \ - --init \ - --entrypoint /bin/zsh \ - "$FULL_IMAGE_NAME" diff --git a/config/shared/bin/dev-tmux-wrapper.sh b/config/shared/bin/dev-tmux-wrapper.sh new file mode 100755 index 0000000..d6870e6 --- /dev/null +++ b/config/shared/bin/dev-tmux-wrapper.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +/home/tomas/bin/dev "$@" 2>&1 +exit_code=$? + +if [ $exit_code -ne 0 ]; then + echo "" + echo "Command: $*" + echo "Failed: exit $exit_code" +fi + +exit $exit_code diff --git a/config/shared/bin/print-underline.sh b/config/shared/bin/print-underline.sh deleted file mode 100755 index d425d17..0000000 --- a/config/shared/bin/print-underline.sh +++ /dev/null @@ -1,7 +0,0 @@ -# tempfile=$(mktemp) \ -# && curl -o $tempfile https://raw.githubusercontent.com/wezterm/wezterm/master/termwiz/data/wezterm.terminfo \ -# && tic -x -o ~/.terminfo $tempfile \ -# && rm $tempfile - -printf "\x1b[58:2::255:0:0m\x1b[4:1msingle\x1b[4:2mdouble\x1b[4:3mcurly\x1b[4:4mdotted\x1b[4:5mdashed\x1b[0m\n" - diff --git a/config/shared/bin/test-true-color.sh b/config/shared/bin/test-true-color.sh index 681285c..8b46ef9 100644 --- a/config/shared/bin/test-true-color.sh +++ b/config/shared/bin/test-true-color.sh @@ -1,117 +1,48 @@ -#!/bin/bash -# -# This file echoes a bunch of 24-bit color codes -# to the terminal to demonstrate its functionality. -# The foreground escape sequence is ^[38;2;;;m -# The background escape sequence is ^[48;2;;;m -# range from 0 to 255 inclusive. -# The escape sequence ^[0m returns output to default +#!/usr/bin/env bash -setBackgroundColor() -{ - echo -en "\x1b[48;2;$1;$2;$3""m" -} - -resetOutput() -{ - echo -en "\x1b[0m\n" -} - -# Gives a color $1/255 % along HSV -# Who knows what happens when $1 is outside 0-255 -# Echoes "$red $green $blue" where -# $red $green and $blue are integers -# ranging between 0 and 255 inclusive -rainbowColor() -{ - let h=$1/43 - let f=$1-43*$h - let t=$f*255/43 - let q=255-t - - if [ $h -eq 0 ] - then - echo "255 $t 0" - elif [ $h -eq 1 ] - then - echo "$q 255 0" - elif [ $h -eq 2 ] - then - echo "0 255 $t" - elif [ $h -eq 3 ] - then - echo "0 $q 255" - elif [ $h -eq 4 ] - then - echo "$t 0 255" - elif [ $h -eq 5 ] - then - echo "255 0 $q" - else - # execution should never reach here - echo "0 0 0" - fi -} - -for i in `seq 0 127`; do - setBackgroundColor $i 0 0 - echo -en " " -done -resetOutput -for i in `seq 255 128`; do - setBackgroundColor $i 0 0 - echo -en " " -done -resetOutput - -for i in `seq 0 127`; do - setBackgroundColor 0 $i 0 - echo -n " " -done -resetOutput -for i in `seq 255 128`; do - setBackgroundColor 0 $i 0 - echo -n " " -done -resetOutput - -for i in `seq 0 127`; do - setBackgroundColor 0 0 $i - echo -n " " -done -resetOutput -for i in `seq 255 128`; do - setBackgroundColor 0 0 $i - echo -n " " -done -resetOutput - -for i in `seq 0 127`; do - setBackgroundColor `rainbowColor $i` - echo -n " " -done -resetOutput -for i in `seq 255 128`; do - setBackgroundColor `rainbowColor $i` - echo -n " " -done -resetOutput - -echo -e "Another test:" +echo awk 'BEGIN{ - s="/\\/\\/\\/\\/"; - for (colnum = 0; colnum < 77; colnum++) { + s="/\\/\\/\\/\\/\\"; s=s s s s s s s s; + for (colnum = 0; colnum<77; colnum++) { r = 255-(colnum*255/76); g = (colnum*510/76); b = (colnum*255/76); if (g>255) g = 510-g; printf "\033[48;2;%d;%d;%dm", r,g,b; - printf "%s\033[0m", substr(s,(colnum%4)+1,1); + printf "\033[38;2;%d;%d;%dm", 255-r,255-g,255-b; + printf "%s\033[0m", substr(s,colnum+1,1); } printf "\n"; }' -echo -e "\nTerm: $(echo "$TERM")" -echo "$(tmux info | grep -e RGB -e Tc)" -echo -e "\nTmux server options:\n$(tmux show-options -s | grep terminal)" -echo "" +# --- Environment diagnostics ------------------------------------------------- +echo +echo "──────────────────────────────" +echo " Environment and Tmux Details " +echo "──────────────────────────────" +echo "TERM: ${TERM}" +echo "COLORTERM: ${COLORTERM:-undefined}" +echo + +if command -v tmux >/dev/null && tmux info &>/dev/null; then + echo "Tmux RGB/Tc capabilities:" + tmux info | grep -E "RGB|Tc" || echo "(none found)" + echo + echo "Tmux server terminal options:" + tmux show-options -s | grep terminal || echo "(none found)" +else + echo "Tmux not running or unavailable." +fi + +# --- Underline capability test ----------------------------------------------- +echo +echo "Underline styles test:" +printf '\x1b[58:2::255:0:0m' # red underline color +printf '\x1b[4:1msingle ' # single underline +printf '\x1b[4:2mdouble ' # double underline +printf '\x1b[4:3mcurly ' # curly underline +printf '\x1b[4:4mdotted ' # dotted underline +printf '\x1b[4:5mdashed ' # dashed underline +printf '\x1b[0m\n' + +echo diff --git a/config/shared/nvim b/config/shared/nvim index 173ff5e..8687a1c 160000 --- a/config/shared/nvim +++ b/config/shared/nvim @@ -1 +1 @@ -Subproject commit 173ff5e47a9b780a25e021efb451a6a3c8669cea +Subproject commit 8687a1cf31bb6b28d3c234ffb308c25f14c02f52 diff --git a/config/shared/tmux b/config/shared/tmux index 93c930d..2bf9ef3 100644 --- a/config/shared/tmux +++ b/config/shared/tmux @@ -20,14 +20,16 @@ set -g mouse on set -g set-titles on set -g set-titles-string "#S" +# set -g remain-on-exit on + set -gw pane-base-index 1 # pane index ##### Appearance ##### set -g status-style fg=black,bg=default set -g window-status-current-style fg=blue,bold set -g message-style fg=blue,bg=default -set -g status-left "#[fg=blue,bold][#S] " -set -g status-right "host:#H | machine:#{?TARGET_MACHINE,#{TARGET_MACHINE},local}" +set -g status-left "#[fg=blue,bold][#{s/^dev-//:#{session_name}}] " +set -g status-right " #{?DF_IMAGE,#{DF_IMAGE} | ,}#{?DF_NAMESPACE,#{DF_NAMESPACE},#H}@#{?DF_PLATFORM,#{DF_PLATFORM},local}" set -g status-left-length 50 set -g status-right-length 50 set -g pane-active-border-style fg=blue @@ -58,3 +60,9 @@ bind c confirm-before -p "kill-pane \#P? (y/n)" kill-pane ##### Misc ##### bind r source-file ~/.tmux.conf \; display "Reloaded!" +unbind d +bind e detach + +bind d command-prompt -I "dev " 'run-shell "/home/tomas/bin/dev-tmux-wrapper.sh %1 --from #{session_name}"' + +