migration

This commit is contained in:
2026-02-14 08:35:47 +02:00
commit f1df2dd897
46 changed files with 2595 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
echo "Usage: $(basename "$0") -c COMMENT_SYMBOL [-e EXCLUDE_PATTERN]... TARGET"
echo " -c Comment symbol (e.g., '#' or '//')"
echo " -e Exclude pattern (can be specified multiple times)"
echo " TARGET File or directory to process"
exit 1
}
comment_sym=""
excludes=()
while getopts "c:e:h" opt; do
case $opt in
c) comment_sym="$OPTARG" ;;
e) excludes+=("$OPTARG") ;;
h) usage ;;
*) usage ;;
esac
done
shift $((OPTIND - 1))
[[ $# -ne 1 ]] && usage
[[ -z "$comment_sym" ]] && usage
target="$(realpath "$1")"
base_dir="$(pwd)"
process_file() {
local file="$1"
# shellcheck disable=SC2295
local rel_path="${file#$base_dir/}"
local path_comment="$comment_sym path: $rel_path"
# Read first two lines
local line1 line2
IFS= read -r line1 <"$file" 2>/dev/null || line1=""
IFS= read -r line2 < <(tail -n +2 "$file") 2>/dev/null || line2=""
# Handle shebang case
if [[ "$line1" =~ ^#! ]]; then
if [[ "$line2" == *"path: "* ]]; then
# Replace existing path comment on line 2
{
echo "$line1"
echo "$path_comment"
tail -n +3 "$file"
} >"$file.tmp"
else
# Insert new path comment after shebang
{
echo "$line1"
echo "$path_comment"
tail -n +2 "$file"
} >"$file.tmp"
fi
else
if [[ "$line1" == *"path: "* ]]; then
# Replace existing path comment on line 1
{
echo "$path_comment"
tail -n +2 "$file"
} >"$file.tmp"
else
# Insert new path comment at top
{
echo "$path_comment"
cat "$file"
} >"$file.tmp"
fi
fi
mv "$file.tmp" "$file"
}
if [[ -f "$target" ]]; then
process_file "$target"
elif [[ -d "$target" ]]; then
find_cmd=(find "$target")
# Always exclude hidden files and directories
find_cmd+=(\( -name ".*" -prune \))
if [[ ${#excludes[@]} -gt 0 ]]; then
find_cmd+=(-o \()
for i in "${!excludes[@]}"; do
[[ $i -gt 0 ]] && find_cmd+=(-o)
find_cmd+=(-path "*/${excludes[$i]}" -prune)
find_cmd+=(-o -path "*/${excludes[$i]}/*" -prune)
done
find_cmd+=(\))
fi
find_cmd+=(-o -type f -print0)
while IFS= read -r -d '' file; do
process_file "$file"
done < <("${find_cmd[@]}")
else
echo "Error: $target is not a file or directory" >&2
exit 1
fi

View File

@@ -0,0 +1,3 @@
source: git@gitea.tomastm.com:tomas.mirchev/barg-parser.git
ref:
branch: main

View 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
View 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 containers 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
View 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
View 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

View 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

View File

@@ -0,0 +1,3 @@
192.168.64.2 personal.utm.local
192.168.64.3 university.utm.local
192.168.50.2 personal.workstation.lan

View File

@@ -0,0 +1,12 @@
# flow-managed dnsmasq configuration
domain-needed
bogus-priv
# Keep loopback for local resolver usage.
listen-address=127.0.0.1
# Local VM and workstation aliases.
address=/personal.utm.local/192.168.64.2
address=/university.utm.local/192.168.64.3
address=/personal.workstation.lan/192.168.50.2

View File

@@ -0,0 +1,16 @@
repository:
dotfiles-url: git@gitea.tomastm.com:tomas.mirchev/dotfiles.git
dotfiles-branch: main
paths:
projects-dir: ~/projects
defaults:
container-registry: registry.tomastm.com
container-tag: latest
tmux-session: default
targets:
personal: orb personal.orb
# Add more SSH targets as needed
# Format: <name>: <host> <address> [ssh-key-path]

View File

@@ -0,0 +1,136 @@
packages:
- name: git
type: pkg
sources:
apt: git
dnf: git
brew: git
- name: tmux
type: pkg
sources:
apt: tmux
dnf: tmux
brew: tmux
- name: zsh
type: pkg
sources:
apt: zsh
dnf: zsh
brew: zsh
- name: dnsmasq
type: pkg
sources:
brew: dnsmasq
- name: brave
type: cask
sources:
brew: brave-browser
- name: bruno
type: cask
sources:
brew: bruno
- name: dbeaver
type: cask
sources:
brew: dbeaver-community
- name: discord
type: cask
sources:
brew: discord
- name: firefox
type: cask
sources:
brew: firefox
- name: font-maple-mono
type: cask
sources:
brew: font-maple-mono
- name: font-maple-mono-nf
type: cask
sources:
brew: font-maple-mono-nf
- name: ghostty
type: cask
sources:
brew: ghostty
- name: chrome
type: cask
sources:
brew: google-chrome
- name: karabiner
type: cask
sources:
brew: karabiner-elements
- name: linearmouse
type: cask
sources:
brew: linearmouse
- name: macfuse
type: cask
sources:
brew: macfuse
- name: orbstack
type: cask
sources:
brew: orbstack
- name: proton-drive
type: cask
sources:
brew: proton-drive
- name: protonvpn
type: cask
sources:
brew: protonvpn
- name: rectangle
type: cask
sources:
brew: rectangle
- name: slack
type: cask
sources:
brew: slack
- name: sol
type: cask
sources:
brew: sol
- name: spotify
type: cask
sources:
brew: spotify
- name: sublime-text
type: cask
sources:
brew: sublime-text
- name: utm
type: cask
sources:
brew: utm@beta
- name: zoom
type: cask
sources:
brew: zoom

View File

@@ -0,0 +1,78 @@
profiles:
macos:
os: macos
package-manager: brew
shell: zsh
requires: [HOSTNAME, USER_EMAIL]
hostname: "{{ env.HOSTNAME }}"
packages:
- git
- tmux
- zsh
- dnsmasq
- cask/brave
- cask/bruno
- cask/dbeaver
- cask/discord
- cask/firefox
- cask/font-maple-mono
- cask/font-maple-mono-nf
- cask/ghostty
- cask/chrome
- cask/karabiner
- cask/linearmouse
- cask/macfuse
- cask/orbstack
- cask/proton-drive
- cask/protonvpn
- cask/rectangle
- cask/slack
- cask/sol
- cask/spotify
- cask/sublime-text
- cask/utm
- cask/zoom
- name: setup
post-install: |
chmod +x ~/.local/share/setup/* 2>/dev/null || true
if [ -f ~/.local/share/setup/macos-defaults ]; then
~/.local/share/setup/macos-defaults
fi
- name: dnsmasq-service
allow_sudo: true
post-install: |
sudo brew services restart dnsmasq || sudo brew services start dnsmasq
ssh-keygen:
- type: ed25519
filename: id_ed25519
comment: "{{ env.USER_EMAIL }}"
- type: ed25519
filename: id_ed25519_git
comment: "{{ env.USER_EMAIL }}"
runcmd:
- mkdir -p ~/projects
- mkdir -p ~/.bin
- git config --global user.email "{{ env.USER_EMAIL }}"
- git config --global user.name "Tomas Mirchev"
- chmod +x ~/.bin/* 2>/dev/null || true
post-link: |
echo "macOS dotfiles linked."
echo "Import Rectangle config from ~/.config/rectangle/RectangleConfig.json"
linux:
os: linux
requires: [USER_EMAIL]
shell: zsh
packages:
- name: window-tagger
post-install: |
echo "Window Tagger config linked to ~/.local/share/kwin/scripts/window-tagger"
ssh-keygen:
- type: ed25519
filename: id_ed25519
comment: "{{ env.USER_EMAIL }}"
runcmd:
- mkdir -p ~/projects
- mkdir -p ~/.bin
- git config --global user.email "{{ env.USER_EMAIL }}"
- git config --global user.name "Tomas Mirchev"

24
_shared/git/.gitconfig Normal file
View File

@@ -0,0 +1,24 @@
[user]
name = Tomas Mirchev
email = contact@tomastm.com
[core]
editor = nvim
excludesfile = ~/.gitignore
[init]
defaultBranch = main
[pull]
rebase = true
[push]
default = current
autoSetupRemote = true
[remote]
pushDefault = origin
[alias]
amend = commit --amend --no-edit
rename = branch -m
st = status
unstage = reset HEAD
last = log -1 HEAD
tags = tag -l
undo = reset --mixed HEAD~1

21
_shared/gitignore/.gitignore vendored Normal file
View File

@@ -0,0 +1,21 @@
.DS_Store
*.swp
*.swo
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
.env
.env.*
build/
dist/
build/
tmp/
temp/
logs/
*.log
.cache/
coverage/
.devflow/
.dev-flow/

View File

@@ -0,0 +1,63 @@
# Beware! This file is rewritten by htop when settings are changed in the interface.
# The parser is also very primitive, and not human-friendly.
htop_version=3.2.2
config_reader_min_version=3
fields=0 48 17 18 38 39 40 2 46 47 49 1
hide_kernel_threads=1
hide_userland_threads=1
hide_running_in_container=0
shadow_other_users=0
show_thread_names=0
show_program_path=1
highlight_base_name=0
highlight_deleted_exe=1
shadow_distribution_path_prefix=0
highlight_megabytes=1
highlight_threads=1
highlight_changes=0
highlight_changes_delay_secs=5
find_comm_in_cmdline=1
strip_exe_from_cmdline=1
show_merged_command=0
header_margin=1
screen_tabs=1
detailed_cpu_time=0
cpu_count_from_one=0
show_cpu_usage=1
show_cpu_frequency=0
show_cpu_temperature=0
degree_fahrenheit=0
update_process_names=0
account_guest_in_cpu_meter=0
color_scheme=0
enable_mouse=1
delay=15
hide_function_bar=0
header_layout=two_50_50
column_meters_0=AllCPUs Memory Swap
column_meter_modes_0=1 1 1
column_meters_1=Tasks LoadAverage Uptime
column_meter_modes_1=2 2 2
tree_view=0
sort_key=47
tree_sort_key=0
sort_direction=-1
tree_sort_direction=1
tree_view_always_by_pid=0
all_branches_collapsed=0
screen:Main=PID USER PRIORITY NICE M_VIRT M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command
.sort_key=PERCENT_MEM
.tree_sort_key=PID
.tree_view=0
.tree_view_always_by_pid=0
.sort_direction=-1
.tree_sort_direction=1
.all_branches_collapsed=0
screen:I/O=PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY Command
.sort_key=IO_RATE
.tree_sort_key=PID
.tree_view=0
.tree_view_always_by_pid=0
.sort_direction=-1
.tree_sort_direction=1
.all_branches_collapsed=0

View File

@@ -0,0 +1,3 @@
source: git@gitea.tomastm.com:tomas.mirchev/nvim-config.git
ref:
branch: main

76
_shared/tmux/.tmux.conf Normal file
View File

@@ -0,0 +1,76 @@
##### Prefix #####
unbind C-b
set -g prefix C-Space
bind C-Space send-prefix
##### General #####
# set -s default-terminal "tmux-256color"
# set -sa terminal-overrides "$term:rgb"
set -s escape-time 10
set -s focus-events on
set -s set-clipboard on
# set -g default-command "${SHELL}"
set -g base-index 1 # window index
set -g renumber-windows on # window index
set -g history-limit 10000
set -g repeat-time 0
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/^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
##### Vim-like #####
set -gw mode-keys vi
bind -T copy-mode-vi v send-keys -X begin-selection
bind -T copy-mode-vi WheelUpPane send -N1 -X scroll-up
bind -T copy-mode-vi WheelDownPane send -N1 -X scroll-down
bind -r h select-pane -L
bind -r j select-pane -D
bind -r k select-pane -U
bind -r l select-pane -R
# Resize Opt/Alt-hjkl
bind -n M-h resize-pane -L 5
bind -n M-j resize-pane -D 5
bind -n M-k resize-pane -U 5
bind -n M-l resize-pane -R 5
# Last window instead of session
bind ';' last-window
unbind '"'
unbind "%"
unbind s
unbind c
unbind n
unbind x
bind s split-window -v -c "#{pane_current_path}"
bind v split-window -h -c "#{pane_current_path}"
bind o choose-session
bind n new-window
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 "flow " 'run-shell "~/.bin/dev-tmux-wrapper %1 --from #{session_name}"'

82
_shared/zsh/.zshrc Normal file
View File

@@ -0,0 +1,82 @@
export PATH="$PATH:$HOME/.bin:$HOME/.local/bin:$HOME/bin"
export LANG=en_US.UTF-8
export LC_CTYPE=en_US.UTF-8
export LC_COLLATE=C
# eval "$(dircolors)"; echo "$LS_COLORS"
export LS_COLORS='rs=0:di=01;34:ln=01;33:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=00:tw=30;42:ow=34;42:st=37;44:ex=01;32'
HISTFILE=$HOME/.zsh_history
HISTSIZE=10000
SAVEHIST=10000
setopt auto_cd interactive_comments prompt_subst share_history
setopt append_history hist_ignore_dups hist_ignore_all_dups hist_reduce_blanks
autoload -Uz compinit
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' # Case-insensitive
zstyle ':completion:*' use-cache on
zstyle ':completion:*' cache-path ~/.zsh/cache
compinit
git_prompt_info() {
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
local branch=$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD)
echo " %F{green}($branch)%f"
fi
}
abbrev_path() {
local pwd="${PWD/#$HOME/~}"
local parts=("${(@s:/:)pwd}")
local len=${#parts}
if (( len <= 1 )); then
echo "$pwd"
return
fi
local result=""
for (( i=1; i<len; i++ )); do
result+="${parts[i]:0:1}/"
done
result+="${parts[len]}"
echo "$result"
}
PROMPT='%n@%m%f %F{blue}$(abbrev_path)%f$(git_prompt_info) $ '
# PROMPT='%n@%m%f %F{blue}%~%f$(git_prompt_info) $ '
autoload -U up-line-or-beginning-search down-line-or-beginning-search
zle -N up-line-or-beginning-search
zle -N down-line-or-beginning-search
bindkey '^[[A' up-line-or-beginning-search
bindkey '^[OA' up-line-or-beginning-search
bindkey '^[[B' down-line-or-beginning-search
bindkey '^[OB' down-line-or-beginning-search
bindkey '^U' backward-kill-line
if command -v nvim >/dev/null 2>&1; then
alias vim='nvim'
fi
case "$OSTYPE" in
linux*) alias ls='ls --color=auto --group-directories-first' ;;
darwin*) alias ls='ls --color=auto' ;;
esac
h() {
history 0 | grep -iE --color=always "$@" | tail -20
}
alias ll='ls -lF'
alias lla='ll -a'
alias ld='ls -ld */'
alias ga='git add'
alias gcm='git commit -m'
alias gp='git push'
alias gst='git status'
alias gd='git diff --patience --color-moved=dimmed-zebra --word-diff=plain --function-context --ignore-space-change -U3'
alias glg='git log --oneline --graph --decorate --all'
alias k='kubectl'