migration
This commit is contained in:
12
_shared/bin/.bin/dev-tmux-wrapper
Executable file
12
_shared/bin/.bin/dev-tmux-wrapper
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
"$HOME/.bin/flow" "$@" 2>&1
|
||||
exit_code=$?
|
||||
|
||||
if [ $exit_code -ne 0 ]; then
|
||||
echo ""
|
||||
echo "Command: $*"
|
||||
echo "Failed: exit $exit_code"
|
||||
fi
|
||||
|
||||
exit $exit_code
|
||||
613
_shared/bin/.bin/flow
Executable file
613
_shared/bin/.bin/flow
Executable file
@@ -0,0 +1,613 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
source "$HOME/.bin/barg"
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
SPEC=(
|
||||
"command;flow;DevFlow CLI - Manage instances and development containers"
|
||||
"note;Use 'flow <command> --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;sync;Git tools"
|
||||
"command;check;Check all projects status"
|
||||
"end"
|
||||
"end"
|
||||
|
||||
"command;dotfiles;Manage repository dotfiles and git submodules"
|
||||
"command;init;Initialize and set up all submodules"
|
||||
"note;Equivalent to: git submodule update --init --recursive"
|
||||
"end"
|
||||
"command;pull;Update all submodules to latest remote commits"
|
||||
"note;Equivalent to: git submodule update --remote --recursive"
|
||||
"end"
|
||||
"command;urls;Synchronize submodule URLs"
|
||||
"note;Equivalent to: git submodule sync --recursive"
|
||||
"end"
|
||||
"command;reset;Reset submodules to the recorded commits"
|
||||
"note;Equivalent to: git submodule update --init --recursive --force"
|
||||
"end"
|
||||
"command;status;Show current submodule status"
|
||||
"note;Equivalent to: git submodule status --recursive"
|
||||
"end"
|
||||
"command;all;Run URLs sync, initialization, and remote update in one step"
|
||||
"note;Equivalent to: git submodule sync --recursive && git submodule update --init --recursive && git submodule update --remote --recursive"
|
||||
"end"
|
||||
"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"
|
||||
CONTAINER_HOME="/home/dev"
|
||||
|
||||
fail() {
|
||||
printf 'Error: %b\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
resolve_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 cname="$(get_cname)"
|
||||
docker container ls -a --format '{{.Names}}' | grep -Fqx "$cname"
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2154,SC2155
|
||||
docker_container_running() {
|
||||
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
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2154,SC2155
|
||||
get_cname() {
|
||||
printf "%s" "dev-${name_arg#dev-}"
|
||||
}
|
||||
|
||||
cmd() {
|
||||
barg_usage
|
||||
}
|
||||
|
||||
cmd_dotfiles_init() {
|
||||
echo "[dotfiles] Initializing submodules..."
|
||||
git submodule update --init --recursive
|
||||
}
|
||||
|
||||
cmd_dotfiles_pull() {
|
||||
echo "[dotfiles] Updating submodules to latest remote commits..."
|
||||
git submodule update --remote --recursive
|
||||
}
|
||||
|
||||
cmd_dotfiles_urls() {
|
||||
echo "[dotfiles] Syncing submodule URLs..."
|
||||
git submodule sync --recursive
|
||||
}
|
||||
|
||||
cmd_dotfiles_reset() {
|
||||
echo "[dotfiles] Resetting submodules to recorded commits..."
|
||||
git submodule update --init --recursive --force
|
||||
}
|
||||
|
||||
cmd_dotfiles_status() {
|
||||
echo "[dotfiles] Submodule status:"
|
||||
git submodule status --recursive
|
||||
}
|
||||
|
||||
cmd_dotfiles_all() {
|
||||
echo "[dotfiles] Syncing URLs..."
|
||||
git submodule sync --recursive
|
||||
|
||||
echo "[dotfiles] Initializing submodules..."
|
||||
git submodule update --init --recursive
|
||||
|
||||
echo "[dotfiles] Updating submodules to latest remote commits..."
|
||||
git submodule update --remote --recursive
|
||||
}
|
||||
|
||||
# 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]="<namespace>@orb"
|
||||
[utm.host]="<namespace>.utm.local"
|
||||
[core.host]="<namespace>.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//<namespace>/$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_sync_check() {
|
||||
local base_dir="$HOME/projects"
|
||||
local -a needs_action=()
|
||||
local -a not_git=()
|
||||
|
||||
for repo in "$base_dir"/*; do
|
||||
[ -d "$repo" ] || continue
|
||||
local git_dir="$repo/.git"
|
||||
|
||||
if [ ! -d "$git_dir" ]; then
|
||||
not_git+=("$(basename "$repo")")
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "=== $(basename "$repo") ==="
|
||||
cd "$repo" || continue
|
||||
|
||||
local action_required=0
|
||||
|
||||
git fetch --all --quiet || true
|
||||
local branch
|
||||
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "HEAD")
|
||||
|
||||
# --- Uncommitted or untracked changes ---
|
||||
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||||
echo "Uncommitted changes:"
|
||||
git status --short
|
||||
action_required=1
|
||||
elif [ -n "$(git ls-files --others --exclude-standard)" ]; then
|
||||
echo "Untracked files:"
|
||||
git ls-files --others --exclude-standard
|
||||
action_required=1
|
||||
else
|
||||
echo "No uncommitted or untracked changes."
|
||||
fi
|
||||
|
||||
# --- Unpushed commits on current branch ---
|
||||
if git rev-parse --abbrev-ref "${branch}@{u}" >/dev/null 2>&1; then
|
||||
local unpushed
|
||||
unpushed=$(git rev-list --oneline "${branch}@{u}..${branch}")
|
||||
if [ -n "$unpushed" ]; then
|
||||
echo "Unpushed commits on ${branch}:"
|
||||
echo "$unpushed"
|
||||
action_required=1
|
||||
else
|
||||
echo "No unpushed commits on ${branch}."
|
||||
fi
|
||||
else
|
||||
echo "No upstream set for ${branch}."
|
||||
action_required=1
|
||||
fi
|
||||
|
||||
# --- Unpushed branches ---
|
||||
local unpushed_branches=()
|
||||
while IFS= read -r b; do
|
||||
if git rev-parse --abbrev-ref "${b}@{u}" >/dev/null 2>&1; then
|
||||
local ahead
|
||||
ahead=$(git rev-list --count "${b}@{u}..${b}")
|
||||
if [ "$ahead" -gt 0 ]; then
|
||||
unpushed_branches+=("$b ($ahead ahead)")
|
||||
fi
|
||||
else
|
||||
unpushed_branches+=("$b (no upstream)")
|
||||
fi
|
||||
done < <(git for-each-ref --format='%(refname:short)' refs/heads)
|
||||
|
||||
if [ "${#unpushed_branches[@]}" -gt 0 ]; then
|
||||
echo "Unpushed branches:"
|
||||
printf ' %s\n' "${unpushed_branches[@]}"
|
||||
action_required=1
|
||||
else
|
||||
echo "No unpushed branches."
|
||||
fi
|
||||
|
||||
echo
|
||||
((action_required)) && needs_action+=("$(basename "$repo")")
|
||||
done
|
||||
|
||||
echo "=== SUMMARY ==="
|
||||
if [ "${#needs_action[@]}" -gt 0 ]; then
|
||||
echo "Projects needing action:"
|
||||
printf ' %s\n' "${needs_action[@]}" | sort -u
|
||||
else
|
||||
echo "All repositories clean and synced."
|
||||
fi
|
||||
|
||||
if [ "${#not_git[@]}" -gt 0 ]; then
|
||||
echo
|
||||
echo "Directories without Git repositories:"
|
||||
printf ' %s\n' "${not_git[@]}" | sort -u
|
||||
fi
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2154,SC2155
|
||||
cmd_create() {
|
||||
# VARS: name_arg, image_arg, project_arg
|
||||
|
||||
# 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 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 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
|
||||
|
||||
# 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
|
||||
)
|
||||
|
||||
[[ -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")
|
||||
|
||||
docker_gid="$(getent group docker | cut -d: -f3 || true)"
|
||||
[[ -n "$docker_gid" ]] && cmd+=(--group-add "$docker_gid")
|
||||
|
||||
cmd+=("$image_ref" sleep infinity)
|
||||
"${cmd[@]}"
|
||||
|
||||
printf "Created and started container: %s" "$cname"
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2154,SC2155
|
||||
cmd_connect() {
|
||||
# VARS: name_arg
|
||||
|
||||
local cname="$(get_cname)"
|
||||
if ! docker_container_exists "$cname"; then
|
||||
fail "Container does not exist: ${cname}. Run: dev create ..."
|
||||
fi
|
||||
|
||||
if ! docker_container_running "$cname"; then
|
||||
docker start "$cname" >/dev/null
|
||||
fi
|
||||
|
||||
if ! command -v tmux >/dev/null 2>&1; then
|
||||
echo "tmux not found; falling back to direct exec"
|
||||
exec "$0" exec "$cname"
|
||||
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 ! 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
|
||||
|
||||
if [[ -n "${TMUX-}" ]]; then
|
||||
tmux switch-client -t "$cname"
|
||||
else
|
||||
tmux attach -t "$cname"
|
||||
fi
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2154,SC2155
|
||||
cmd_exec() {
|
||||
# VARS: name_arg, cmd_arg
|
||||
|
||||
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
|
||||
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 home=${HOME%/}
|
||||
local projdir=${PROJECT_DIR%/}
|
||||
|
||||
# Case 1: under PROJECT_DIR
|
||||
if [[ -n ${projdir} && $project == "$projdir"/* ]]; then
|
||||
# shellcheck disable=SC2088
|
||||
project="~/$PROJECT_ABBR${project#"$projdir"}"
|
||||
|
||||
# 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() {
|
||||
# VARS:
|
||||
|
||||
{
|
||||
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/}"
|
||||
|
||||
# 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() {
|
||||
# VARS: kill_arg name_arg
|
||||
local cname
|
||||
cname="$(get_cname)"
|
||||
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
|
||||
|
||||
tmux_fallback_to_default_if_in_session "$cname"
|
||||
}
|
||||
|
||||
# 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"
|
||||
|
||||
if [[ "$force_arg" == "true" ]]; 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() {
|
||||
# VARS: name_arg
|
||||
local cname
|
||||
cname="$(get_cname)"
|
||||
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
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2154,SC2155
|
||||
cmd_test() {
|
||||
# VARS: name_arg
|
||||
|
||||
echo "Script dev is working fine!"
|
||||
if [[ -n "$name_arg" ]]; then
|
||||
get_cname
|
||||
fi
|
||||
echo
|
||||
}
|
||||
|
||||
barg_run SPEC[@] "$@"
|
||||
29
_shared/bin/.bin/ssh-forward
Executable file
29
_shared/bin/.bin/ssh-forward
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Function to display usage information
|
||||
usage() {
|
||||
echo "Usage: $0 <user@host> <port1> [port2] [port3] ..."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Ensure at least two arguments are provided: host and one port
|
||||
if [ "$#" -lt 2 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
# Extract the host from the first argument
|
||||
HOST="$1"
|
||||
shift # Shift the arguments so that $@ contains the remaining ports
|
||||
|
||||
# Initialize the PORTS variable
|
||||
PORTS=""
|
||||
|
||||
# Iterate over the remaining arguments, which are the ports
|
||||
for port in "$@"; do
|
||||
PORTS="$PORTS -L ${port}:localhost:${port}"
|
||||
done
|
||||
|
||||
# Construct and run the SSH command
|
||||
SSH_CMD="ssh -N -T -o ExitOnForwardFailure=yes $HOST $PORTS"
|
||||
echo "Running: $SSH_CMD"
|
||||
$SSH_CMD
|
||||
39
_shared/bin/.bin/sync-theme
Executable file
39
_shared/bin/.bin/sync-theme
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
declare -A THEME=(
|
||||
["ghostty.url"]="https://raw.githubusercontent.com/triimdev/invero.nvim/refs/heads/main/extras/ghostty/invero_day"
|
||||
["ghostty.dir"]="$HOME/.config/ghostty/themes"
|
||||
["ghostty.name"]="Invero Day"
|
||||
|
||||
["wezterm.url"]="https://raw.githubusercontent.com/triimdev/invero.nvim/refs/heads/main/extras/wezterm/invero_day.toml"
|
||||
["wezterm.dir"]="$HOME/.config/wezterm/colors"
|
||||
["wezterm.name"]="Invero Day.toml"
|
||||
)
|
||||
|
||||
theme="${1:-}"
|
||||
|
||||
if [[ -z "$theme" ]]; then
|
||||
echo "Usage: $0 <theme>"
|
||||
echo "Available themes: $(printf '%s\n' "${!THEME[@]}" | cut -d. -f1 | sort -u | tr '\n' ' ')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${THEME[$theme.url]+x}" ]]; then
|
||||
echo "Unknown theme '$theme'. Available: $(printf '%s\n' "${!THEME[@]}" | cut -d. -f1 | sort -u | tr '\n' ' ')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
url="${THEME[$theme.url]}"
|
||||
dir="${THEME[$theme.dir]}"
|
||||
name="${THEME[$theme.name]}"
|
||||
path="${dir}/${name}"
|
||||
|
||||
mkdir -p "$dir"
|
||||
|
||||
if curl -fsSL -o "$path" "$url"; then
|
||||
echo "Theme downloaded to $path"
|
||||
else
|
||||
echo "Failed to download theme for '$theme'."
|
||||
exit 1
|
||||
fi
|
||||
48
_shared/bin/.bin/test-true-color
Executable file
48
_shared/bin/.bin/test-true-color
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo
|
||||
awk 'BEGIN{
|
||||
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 "\033[38;2;%d;%d;%dm", 255-r,255-g,255-b;
|
||||
printf "%s\033[0m", substr(s,colnum+1,1);
|
||||
}
|
||||
printf "\n";
|
||||
}'
|
||||
|
||||
# --- 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
|
||||
Reference in New Issue
Block a user