Compare commits

..

1 Commits

Author SHA1 Message Date
renovate[bot]
788d7ec259 chore(deps): update image python to v3.14.3
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-04 22:43:15 +00:00
65 changed files with 19554 additions and 794 deletions

View File

@@ -35,4 +35,4 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
- name: Run PR Lint - name: Run PR Lint
uses: ivuorinen/actions/pr-lint@f371da218e9152e7d29ee39358454e41010c36dc # v2026.02.03 uses: ivuorinen/actions/pr-lint@f98ae7cd7d0feb1f9d6b01de0addbb11414cfc73 # v2026.01.21

View File

@@ -29,4 +29,4 @@ jobs:
issues: write issues: write
steps: steps:
- uses: ivuorinen/actions/sync-labels@f371da218e9152e7d29ee39358454e41010c36dc # v2026.02.03 - uses: ivuorinen/actions/sync-labels@f98ae7cd7d0feb1f9d6b01de0addbb11414cfc73 # v2026.01.21

12
.gitmodules vendored
View File

@@ -4,6 +4,11 @@
url = https://github.com/anishathalye/dotbot.git url = https://github.com/anishathalye/dotbot.git
ignore = dirty ignore = dirty
[submodule "dotbot-brew"]
path = tools/dotbot-brew
url = https://github.com/wren/dotbot-brew.git
ignore = dirty
[submodule "dotbot-include"] [submodule "dotbot-include"]
path = tools/dotbot-include path = tools/dotbot-include
url = https://gitlab.com/gnfzdz/dotbot-include.git url = https://gitlab.com/gnfzdz/dotbot-include.git
@@ -24,6 +29,11 @@
url = https://github.com/tmux-plugins/tmux-sessionist.git url = https://github.com/tmux-plugins/tmux-sessionist.git
ignore = dirty ignore = dirty
[submodule "dotbot-pip"]
path = tools/dotbot-pip
url = https://github.com/sobolevn/dotbot-pip.git
ignore = dirty
[submodule "tmux/tmux-suspend"] [submodule "tmux/tmux-suspend"]
path = config/tmux/plugins/tmux-suspend path = config/tmux/plugins/tmux-suspend
url = https://github.com/MunifTanjim/tmux-suspend.git url = https://github.com/MunifTanjim/tmux-suspend.git
@@ -53,8 +63,6 @@
[submodule "tmux/tmux-resurrect"] [submodule "tmux/tmux-resurrect"]
path = config/tmux/plugins/tmux-resurrect path = config/tmux/plugins/tmux-resurrect
url = https://github.com/tmux-plugins/tmux-resurrect.git url = https://github.com/tmux-plugins/tmux-resurrect.git
ignore = dirty
[submodule "tmux/catppuccin"] [submodule "tmux/catppuccin"]
path = config/tmux/plugins/catppuccin path = config/tmux/plugins/catppuccin
url = https://github.com/catppuccin/tmux.git url = https://github.com/catppuccin/tmux.git
ignore = dirty

View File

@@ -6,5 +6,6 @@ config/tmux/plugins/**
config/vim/plugged/** config/vim/plugged/**
node_modules node_modules
tools/antidote/** tools/antidote/**
tools/dotbot-brew/**
tools/dotbot-include/** tools/dotbot-include/**
tools/dotbot/** tools/dotbot/**

View File

@@ -1 +1 @@
3.14.2 3.14.3

View File

@@ -5,8 +5,12 @@ git submodule sync --recursive
# dotbot and plugins # dotbot and plugins
git submodule add --name dotbot \ git submodule add --name dotbot \
-f https://github.com/anishathalye/dotbot.git tools/dotbot -f https://github.com/anishathalye/dotbot.git tools/dotbot
git submodule add --name dotbot-brew \
-f https://github.com/wren/dotbot-brew.git tools/dotbot-brew
git submodule add --name dotbot-include \ git submodule add --name dotbot-include \
-f https://gitlab.com/gnfzdz/dotbot-include.git tools/dotbot-include -f https://gitlab.com/gnfzdz/dotbot-include.git tools/dotbot-include
git submodule add --name dotbot-pip \
-f https://github.com/sobolevn/dotbot-pip.git tools/dotbot-pip
# other repos # other repos
git submodule add --name cheat-community \ git submodule add --name cheat-community \
@@ -42,64 +46,26 @@ done
# Mark certain repositories shallow # Mark certain repositories shallow
git config -f .gitmodules submodule.antidote.shallow true git config -f .gitmodules submodule.antidote.shallow true
_log() { # remove old submodules
if command -v msgr > /dev/null 2>&1; then folders=(
msgr run_done "$1" "config/tmux/plugins/tpm"
else "config/tmux/plugins/tmux"
echo " [ok] $1" "config/tmux/plugins/tmux-menus"
fi "tools/dotbot-crontab"
} "tools/dotbot-snap"
"config/tmux/plugins/tmux-window-name"
remove_old_submodule() { "config/tmux/plugins/tmux-sensible"
local name="$1" path="$2" "config/tmux/plugins/tmux-mode-indicator"
"config/tmux/plugins/tmux-yank"
# Remove working tree "config/tmux/plugins/tmux-fzf-url"
if [ -d "$path" ]; then "config/nvim-kickstart"
rm -rf "$path" "local/bin/asdf"
_log "Removed $path" "local/asdf"
fi "tools/dotbot-asdf"
# Remove stale git index entry
git rm --cached "$path" 2> /dev/null || true
# Remove .git/config section keyed by path
git config --remove-section "submodule.$path" 2> /dev/null || true
# Skip name-based cleanup if no submodule name provided
[ -z "$name" ] && return 0
# Remove .git/config section keyed by name
git config --remove-section "submodule.$name" 2> /dev/null || true
# Remove .git/modules/<name>/ cached repository
if [ -d ".git/modules/$name" ]; then
rm -rf ".git/modules/$name"
_log "Removed .git/modules/$name"
fi
}
# remove old submodules (name:path pairs)
old_submodules=(
"tmux/tpm:config/tmux/plugins/tpm"
":config/tmux/plugins/tmux"
"tmux/tmux-menus:config/tmux/plugins/tmux-menus"
"dotbot-crontab:tools/dotbot-crontab"
"dotbot-snap:tools/dotbot-snap"
"tmux/tmux-window-name:config/tmux/plugins/tmux-window-name"
"tmux/tmux-sensible:config/tmux/plugins/tmux-sensible"
"tmux/tmux-mode-indicator:config/tmux/plugins/tmux-mode-indicator"
"tmux/tmux-yank:config/tmux/plugins/tmux-yank"
":config/tmux/plugins/tmux-fzf-url"
"nvim-kickstart:config/nvim-kickstart"
"asdf:local/bin/asdf"
"asdf:local/asdf"
"dotbot-asdf:tools/dotbot-asdf"
"dotbot-pip:tools/dotbot-pip"
"dotbot-brew:tools/dotbot-brew"
) )
for entry in "${old_submodules[@]}"; do for folder in "${folders[@]}"; do
name="${entry%%:*}" [ -d "$folder" ] \
path="${entry#*:}" && rm -rf "$folder" \
remove_old_submodule "$name" "$path" && msgr run_done "Removed old submodule $folder"
done done

View File

@@ -2,7 +2,6 @@
# shellcheck shell=bash # shellcheck shell=bash
export DOTFILES="$HOME/.dotfiles" export DOTFILES="$HOME/.dotfiles"
# Minimal PATH for x-have and utilities; full PATH set in shared.sh/exports
export PATH="$HOME/.local/bin:$DOTFILES/local/bin:$PATH" export PATH="$HOME/.local/bin:$DOTFILES/local/bin:$PATH"
export SHARED_SCRIPTS_SOURCED=0 export SHARED_SCRIPTS_SOURCED=0
@@ -12,7 +11,7 @@ source "$DOTFILES/config/shared.sh"
[ -f "${DOTFILES}/config/fzf/fzf.bash" ] && [ -f "${DOTFILES}/config/fzf/fzf.bash" ] &&
source "${DOTFILES}/config/fzf/fzf.bash" source "${DOTFILES}/config/fzf/fzf.bash"
# Import ssh keys in keychain (macOS-specific -A flag, silently fails on Linux) # Import ssh keys in keychain
ssh-add -A 2>/dev/null ssh-add -A 2>/dev/null
x-have antidot && { x-have antidot && {
@@ -23,3 +22,5 @@ PROMPT_DIRTRIM=3
PROMPT_COMMAND='PS1_CMD1=$(git branch --show-current 2>/dev/null)' PROMPT_COMMAND='PS1_CMD1=$(git branch --show-current 2>/dev/null)'
PS1='\[\e[95m\]\u\[\e[0m\]@\[\e[38;5;22;2m\]\h\[\e[0m\] \[\e[38;5;33m\]\w\[\e[0m\] \[\e[92;2m\]${PS1_CMD1}\n\[\e[39m\]➜\[\e[0m\] ' PS1='\[\e[95m\]\u\[\e[0m\]@\[\e[38;5;22;2m\]\h\[\e[0m\] \[\e[38;5;33m\]\w\[\e[0m\] \[\e[92;2m\]${PS1_CMD1}\n\[\e[39m\]➜\[\e[0m\] '
# Added by LM Studio CLI (lms)
export PATH="$PATH:$HOME/.lmstudio/bin"

View File

@@ -2,14 +2,14 @@
-- These globals can be set and accessed: -- These globals can be set and accessed:
-- --
globals = { globals = {
"rawrequire", "rawrequire",
} }
-- --
-- These globals can only be accessed: -- These globals can only be accessed:
-- --
read_globals = { read_globals = {
"hs", "hs",
"ls", "ls",
"spoon", "spoon",
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -7,13 +7,18 @@
autoload -U promptinit; promptinit autoload -U promptinit; promptinit
export DOTFILES="$HOME/.dotfiles" export DOTFILES="$HOME/.dotfiles"
# Minimal PATH for x-have and utilities; full PATH set in shared.sh/exports LOCAL_SHARE="$HOME/.local/share"
export PATH="$HOME/.local/bin:$DOTFILES/local/bin:$PATH" export PATH="$HOME/.local/bin:$DOTFILES/local/bin:$LOCAL_SHARE/nvim/mason/bin:$LOCAL_SHARE/bob/nvim-bin:$LOCAL_SHARE/cargo/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"
export SHARED_SCRIPTS_SOURCED=0 export SHARED_SCRIPTS_SOURCED=0
source "$DOTFILES/config/shared.sh" source "$DOTFILES/config/shared.sh"
# zsh completions directory (ZSH_CUSTOM_COMPLETION_PATH set in shared.sh) # zsh completions directory
[ -z "$ZSH_COMPLETIONS" ] && export ZSH_COMPLETIONS="$XDG_CONFIG_HOME/zsh/completion"
# Add zsh completions to FPATH, compinit will be called later
FPATH="$ZSH_COMPLETIONS:$FPATH"
ZSH_COMPDUMP="$XDG_CACHE_HOME/zsh/zcompdump-${SHORT_HOST}-${ZSH_VERSION}" ZSH_COMPDUMP="$XDG_CACHE_HOME/zsh/zcompdump-${SHORT_HOST}-${ZSH_VERSION}"
source "$DOTFILES/config/zsh/antidote.zsh" source "$DOTFILES/config/zsh/antidote.zsh"
@@ -32,10 +37,12 @@ source_fzf_config
x-have antidot && eval "$(antidot init)" x-have antidot && eval "$(antidot init)"
autoload -Uz compinit bashcompinit autoload -Uz compinit bashcompinit
compinit -d "$ZSH_COMPDUMP" compinit -d $ZSH_COMPDUMP
bashcompinit bashcompinit
# To customize prompt, run `p10k configure` or edit ~/.p10k.zsh. # To customize prompt, run `p10k configure` or edit ~/.p10k.zsh.
export P10K_CONFIG="$DOTFILES/config/zsh/p10k.zsh" export P10K_CONFIG="$DOTFILES/config/zsh/p10k.zsh"
[[ ! -f "$P10K_CONFIG" ]] || source "$P10K_CONFIG" [[ ! -f "$P10K_CONFIG" ]] || source "$P10K_CONFIG"
# Added by LM Studio CLI (lms)
export PATH="$PATH:$HOME/.lmstudio/bin"

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "https://biomejs.dev/schemas/2.3.11/schema.json", "$schema": "https://biomejs.dev/schemas/2.3.1/schema.json",
"vcs": { "vcs": {
"enabled": true, "enabled": true,
"clientKind": "git", "clientKind": "git",

View File

@@ -7,6 +7,8 @@ x-have eza && {
alias ls="eza -h -s=type --git --icons --group-directories-first" alias ls="eza -h -s=type --git --icons --group-directories-first"
} }
alias vim='vim -u "$XDG_CONFIG_HOME/vim/vimrc"'
# Easier navigation: .., ..., .... # Easier navigation: .., ..., ....
alias ..="cd .." alias ..="cd .."
alias ...="cd ../.." alias ...="cd ../.."

View File

@@ -282,8 +282,7 @@ export LESSHISTFILE="$XDG_STATE_HOME"/less/history
export MANPAGER="less -X" export MANPAGER="less -X"
# Always enable colored `grep` output # Always enable colored `grep` output
# Note: GREP_OPTIONS is deprecated since GNU grep 2.21 export GREP_OPTIONS="--color=auto"
# Color is handled via alias in config/alias
# check the window size after each command and, if necessary, # check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS. # update the values of LINES and COLUMNS.
@@ -437,10 +436,6 @@ msg "Setting up Wakatime configuration"
export WAKATIME_HOME="$XDG_STATE_HOME/wakatime" export WAKATIME_HOME="$XDG_STATE_HOME/wakatime"
x-dc "$WAKATIME_HOME" x-dc "$WAKATIME_HOME"
# LM Studio CLI
msg "Setting up LM Studio configuration"
export PATH="$PATH:$HOME/.lmstudio/bin"
# Misc # Misc
msg "Setting up miscellaneous configuration" msg "Setting up miscellaneous configuration"
export ZSHZ_DATA="$XDG_STATE_HOME/z" export ZSHZ_DATA="$XDG_STATE_HOME/z"

View File

@@ -0,0 +1 @@
/Applications/OrbStack.app/Contents/MacOS/../Resources/completions/fish/kubectl.fish

View File

@@ -0,0 +1 @@
/Applications/OrbStack.app/Contents/MacOS/../Resources/completions/fish/orbctl.fish

View File

@@ -260,6 +260,8 @@ brew "php@8.2", link: true
brew "php@8.3" brew "php@8.3"
# Pins GitHub Actions to full hashes and versions # Pins GitHub Actions to full hashes and versions
brew "pinact" brew "pinact"
# Execute binaries from Python packages in isolated environments
brew "pipx"
# Python version management # Python version management
brew "pyenv" brew "pyenv"
# Migrate pip packages from one Python version to another # Migrate pip packages from one Python version to another

View File

@@ -40,8 +40,7 @@ return {
operators = {}, operators = {},
-- miscs = {}, -- Uncomment to turn off hard-coded styles -- miscs = {}, -- Uncomment to turn off hard-coded styles
}, },
-- Style of specific lsp hl groups (`:h lsp-highlight`) lsp_styles = { -- Handles the style of specific lsp hl groups (see `:h lsp-highlight`).
lsp_styles = {
virtual_text = { virtual_text = {
errors = { 'italic' }, errors = { 'italic' },
hints = { 'italic' }, hints = { 'italic' },
@@ -73,8 +72,7 @@ return {
enabled = true, enabled = true,
indentscope_color = '', indentscope_color = '',
}, },
-- More integrations: -- For more plugins integrations please scroll down (https://github.com/catppuccin/nvim#integrations)
-- github.com/catppuccin/nvim#integrations
}, },
} }
@@ -117,8 +115,7 @@ return {
{ {
'dmtrKovalenko/fff.nvim', 'dmtrKovalenko/fff.nvim',
build = function() build = function()
-- Downloads prebuild binary or uses rustup -- this will download prebuild binary or try to use existing rustup toolchain to build from source
-- toolchain to build from source
-- (if you are using lazy you can use gb for rebuilding a plugin if needed) -- (if you are using lazy you can use gb for rebuilding a plugin if needed)
require('fff.download').download_or_build_binary() require('fff.download').download_or_build_binary()
end, end,
@@ -127,8 +124,7 @@ return {
opts = { -- (optional) opts = { -- (optional)
debug = { debug = {
enabled = true, -- we expect your collaboration at least during the beta enabled = true, -- we expect your collaboration at least during the beta
-- Share scores to help optimize scoring show_scores = true, -- to help us optimize the scoring system, feel free to share your scores!
show_scores = true,
}, },
}, },
-- No need to lazy-load with lazy.nvim. -- No need to lazy-load with lazy.nvim.

View File

@@ -15,7 +15,10 @@ git submodule update --init --recursive "${DOTBOT_DIR}"
"${DOTBOT_BIN_PATH}" \ "${DOTBOT_BIN_PATH}" \
-d "${BASEDIR}" \ -d "${BASEDIR}" \
--plugin-dir=tools/dotbot-asdf \
--plugin-dir=tools/dotbot-brew \
--plugin-dir=tools/dotbot-include \ --plugin-dir=tools/dotbot-include \
--plugin-dir=tools/dotbot-pip \
-c "${CONFIG}" \ -c "${CONFIG}" \
"${@}" "${@}"
@@ -26,7 +29,10 @@ if [ "${DOTBOT_HOST}" != "" ]; then
echo "(!) Found $DOTBOT_HOST_CONFIG" && echo "(!) Found $DOTBOT_HOST_CONFIG" &&
"$DOTBOT_BIN_PATH" \ "$DOTBOT_BIN_PATH" \
-d "$BASEDIR" \ -d "$BASEDIR" \
--plugin-dir=tools/dotbot-asdf \
--plugin-dir=tools/dotbot-brew \
--plugin-dir=tools/dotbot-include \ --plugin-dir=tools/dotbot-include \
--plugin-dir=tools/dotbot-pip \
-c "$DOTBOT_HOST_CONFIG" \ -c "$DOTBOT_HOST_CONFIG" \
"${@}" "${@}"
fi fi

View File

@@ -78,3 +78,8 @@
- shell: - shell:
# Use my dotfiles manager to install everything # Use my dotfiles manager to install everything
- bash local/bin/dfm install all - bash local/bin/dfm install all
- pipx:
file: tools/requirements-pipx.txt
stdout: true
stderr: true

View File

@@ -1,9 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# A script for encrypting and decrypting files or directories with age and SSH keys # A script for encrypting and decrypting files or directories with age and SSH keys
set -euo pipefail VERSION="1.0.0"
VERSION="1.1.0"
# Default ENV values # Default ENV values
KEYS_FILE="${AGE_KEYSFILE:-$HOME/.ssh/keys.txt}" KEYS_FILE="${AGE_KEYSFILE:-$HOME/.ssh/keys.txt}"
@@ -11,49 +9,14 @@ KEYS_SOURCE="${AGE_KEYSSOURCE:-https://github.com/ivuorinen.keys}"
LOG_FILE="${AGE_LOGFILE:-$HOME/.cache/a.log}" LOG_FILE="${AGE_LOGFILE:-$HOME/.cache/a.log}"
VERBOSE=false VERBOSE=false
DELETE_ORIGINAL=false
FORCE=false
# Check for required dependencies # Parse flags for verbosity
check_dependencies() for arg in "$@"; do
{ if [[ "$arg" == "-v" || "$arg" == "--verbose" ]]; then
if ! command -v age &> /dev/null; then VERBOSE=true
echo "Error: 'age' is not installed. Please install it first." >&2 break
echo " brew install age # macOS" >&2
echo " apt install age # Debian/Ubuntu" >&2
echo " dnf install age # Fedora" >&2
exit 1
fi fi
done
if ! command -v curl &> /dev/null; then
echo "Error: 'curl' is not installed." >&2
exit 1
fi
}
# Parse flags
parse_flags()
{
local args=()
for arg in "$@"; do
case "$arg" in
-v | --verbose)
VERBOSE=true
;;
--delete)
DELETE_ORIGINAL=true
;;
-f | --force)
FORCE=true
;;
*)
args+=("$arg")
;;
esac
done
# Return remaining arguments
printf '%s\n' "${args[@]}"
}
# Ensure log directory and file exist with correct permissions # Ensure log directory and file exist with correct permissions
prepare_log_file() prepare_log_file()
@@ -75,6 +38,8 @@ prepare_log_file()
chmod 0600 "$LOG_FILE" chmod 0600 "$LOG_FILE"
} }
prepare_log_file
# Logging function # Logging function
log_message() log_message()
{ {
@@ -91,7 +56,7 @@ log_message()
print_help() print_help()
{ {
cat << EOF cat << EOF
Usage: a [options] [command] [file_or_directory] Usage: a [command] [file_or_directory] [options]
Commands: Commands:
e, enc, encrypt Encrypt the specified file or directory e, enc, encrypt Encrypt the specified file or directory
@@ -100,14 +65,12 @@ Commands:
version, --version Show version information version, --version Show version information
Options: Options:
-v, --verbose Print log messages to console -v, --verbose Print log messages to console in addition to writing to log file
--delete Delete original files after successful encryption
-f, --force Overwrite existing output files without prompting
Environment Variables: Environment Variables:
AGE_KEYSFILE Path to the SSH keys file (default: \$HOME/.ssh/keys.txt) AGE_KEYSFILE Path to the SSH keys file (default: $HOME/.ssh/keys.txt)
AGE_KEYSSOURCE URL to fetch SSH keys if keys file does not exist AGE_KEYSSOURCE URL to fetch SSH keys if keys file does not exist
AGE_LOGFILE Path to the log file (default: \$HOME/.cache/a.log) AGE_LOGFILE Path to the log file (default: $HOME/.cache/a.log)
Examples: Examples:
Encrypt a file: Encrypt a file:
@@ -116,21 +79,14 @@ Examples:
Encrypt a directory: Encrypt a directory:
a e /path/to/directory a e /path/to/directory
Encrypt and delete originals:
a --delete e file.txt
Decrypt a file: Decrypt a file:
a d file.txt.age a d file.txt.age
Force overwrite existing files:
a -f e file.txt
Specify a custom keys file: Specify a custom keys file:
AGE_KEYSFILE=/path/to/keys.txt a e file.txt AGE_KEYSFILE=/path/to/keys.txt a e file.txt
Requirements: Specify a custom keys source and log file:
- age (encryption tool): https://github.com/FiloSottile/age AGE_KEYSSOURCE=https://example.com/keys.txt AGE_LOGFILE=/tmp/a.log a d file.txt.age
- curl (for fetching keys)
EOF EOF
} }
@@ -159,104 +115,26 @@ fetch_keys_if_missing()
fi fi
} }
# Function to encrypt a single file
encrypt_single_file()
{
local file="$1"
# Skip already encrypted files
if [[ "$file" == *.age ]]; then
log_message "Skipping already encrypted file: $file"
return 0
fi
local output_file="${file}.age"
# Check if output file exists
if [[ -f "$output_file" && "$FORCE" != true ]]; then
log_message "Error: Output file '$output_file' already exists. Use --force to overwrite."
return 1
fi
fetch_keys_if_missing
local temp_file
temp_file="$(mktemp -p "$(dirname "$file")")"
if age -R "$KEYS_FILE" "$file" > "$temp_file" && mv "$temp_file" "$output_file"; then
log_message "File encrypted successfully: $output_file"
if [[ "$DELETE_ORIGINAL" == true ]]; then
rm -f "$file"
log_message "Original file deleted: $file"
fi
else
rm -f "$temp_file"
log_message "Error: Failed to encrypt file '$file'."
return 1
fi
}
# Function to encrypt files or directories # Function to encrypt files or directories
encrypt_file_or_directory() encrypt_file_or_directory()
{ {
local file="$1" local file="$1"
if [[ -d "$file" ]]; then if [[ -d "$file" ]]; then
# Enable dotglob to include hidden files for f in "$file"/*; do
shopt -s dotglob nullglob
local files=("$file"/*)
shopt -u dotglob nullglob
if [[ ${#files[@]} -eq 0 ]]; then
log_message "Warning: Directory '$file' is empty."
return 0
fi
for f in "${files[@]}"; do
encrypt_file_or_directory "$f" encrypt_file_or_directory "$f"
done done
elif [[ -f "$file" ]]; then elif [[ -f "$file" ]]; then
encrypt_single_file "$file" fetch_keys_if_missing
else local output_file="${file}.age"
log_message "Warning: '$file' is not a file or directory, skipping." local temp_file
fi temp_file="$(mktemp -p "$(dirname "$file")")"
} if age -R "$KEYS_FILE" "$file" > "$temp_file" && mv "$temp_file" "$output_file"; then
log_message "File encrypted successfully: $output_file"
# Function to decrypt a single file else
decrypt_single_file() rm -f "$temp_file"
{ log_message "Error: Failed to encrypt file '$file'."
local file="$1" exit 1
if [[ ! "$file" == *.age ]]; then
log_message "Skipping non-.age file: $file"
return 0
fi
local output_file="${file%.age}"
# Check if output file exists
if [[ -f "$output_file" && "$FORCE" != true ]]; then
log_message "Error: Output file '$output_file' already exists. Use --force to overwrite."
return 1
fi
fetch_keys_if_missing
local temp_file
temp_file="$(mktemp -p "$(dirname "$file")")"
if age -d -i "$KEYS_FILE" "$file" > "$temp_file" && mv "$temp_file" "$output_file"; then
log_message "File decrypted successfully: $output_file"
if [[ "$DELETE_ORIGINAL" == true ]]; then
rm -f "$file"
log_message "Encrypted file deleted: $file"
fi fi
else
rm -f "$temp_file"
log_message "Error: Failed to decrypt file '$file'."
return 1
fi fi
} }
@@ -264,76 +142,54 @@ decrypt_single_file()
decrypt_file_or_directory() decrypt_file_or_directory()
{ {
local file="$1" local file="$1"
if [[ -d "$file" ]]; then if [[ -d "$file" ]]; then
# Enable nullglob to handle no matches gracefully for f in "$file"/*.age; do
shopt -s nullglob decrypt_file_or_directory "$f"
local files=("$file"/*.age)
shopt -u nullglob
if [[ ${#files[@]} -eq 0 ]]; then
log_message "Warning: No .age files found in directory '$file'."
return 0
fi
for f in "${files[@]}"; do
decrypt_single_file "$f"
done done
elif [[ -f "$file" ]]; then elif [[ -f "$file" ]]; then
decrypt_single_file "$file" fetch_keys_if_missing
else local output_file="${file%.age}"
log_message "Warning: '$file' is not a file or directory, skipping." local temp_file
temp_file="$(mktemp -p "$(dirname "$file")")"
if age -d -i "$KEYS_FILE" "$file" > "$temp_file" && mv "$temp_file" "$output_file"; then
log_message "File decrypted successfully: $output_file"
else
rm -f "$temp_file"
log_message "Error: Failed to decrypt file '$file'."
exit 1
fi
fi fi
} }
# Main entry point # Main logic
main() case "$1" in
{ e | enc | encrypt)
check_dependencies if [[ $# -lt 2 ]]; then
log_message "Error: No file or directory specified for encryption."
# Parse flags and get remaining arguments
mapfile -t ARGS < <(parse_flags "$@")
prepare_log_file
local command="${ARGS[0]:-}"
local target="${ARGS[1]:-}"
case "$command" in
e | enc | encrypt)
if [[ -z "$target" ]]; then
log_message "Error: No file or directory specified for encryption."
print_help
exit 1
fi
encrypt_file_or_directory "$target"
;;
d | dec | decrypt)
if [[ -z "$target" ]]; then
log_message "Error: No file or directory specified for decryption."
print_help
exit 1
fi
decrypt_file_or_directory "$target"
;;
help | --help | -h)
print_help
;;
version | --version)
print_version
;;
"")
print_help print_help
exit 1 exit 1
;; fi
*) encrypt_file_or_directory "$2"
log_message "Error: Unknown command '$command'" ;;
d | dec | decrypt)
if [[ $# -lt 2 ]]; then
log_message "Error: No file or directory specified for decryption."
print_help print_help
exit 1 exit 1
;; fi
esac decrypt_file_or_directory "$2"
} ;;
help | --help)
main "$@" print_help
;;
version | --version)
print_version
;;
*)
log_message "Error: Unknown command '$1'"
print_help
exit 1
;;
esac
# vim: ft=bash:syn=sh:ts=2:sw=2:et:ai:nowrap # vim: ft=bash:syn=sh:ts=2:sw=2:et:ai:nowrap

View File

@@ -2,76 +2,28 @@
Encrypt or decrypt files and directories using `age` and your GitHub SSH keys. Encrypt or decrypt files and directories using `age` and your GitHub SSH keys.
## Requirements
- [age](https://github.com/FiloSottile/age) - encryption tool
- curl - for fetching SSH keys
Install age:
```bash
brew install age # macOS
apt install age # Debian/Ubuntu
dnf install age # Fedora
```
## Usage ## Usage
```bash ```bash
a [options] <command> <file|directory> a encrypt <file|dir>
a decrypt <file.age|dir>
``` ```
Commands:
- `e`, `enc`, `encrypt` - encrypt files
- `d`, `dec`, `decrypt` - decrypt files
- `help`, `--help`, `-h` - show help
- `version`, `--version` - show version
Options: Options:
- `-v`, `--verbose` - show log output - `-v`, `--verbose` show log output
- `--delete` - delete original files after successful operation
- `-f`, `--force` - overwrite existing output files
Environment variables: Environment variables:
- `AGE_KEYSFILE` - location of the keys file (default: `~/.ssh/keys.txt`) - `AGE_KEYSFILE` location of the keys file
- `AGE_KEYSSOURCE` - URL to fetch keys if missing (default: GitHub keys) - `AGE_KEYSSOURCE` URL to fetch keys if missing
- `AGE_LOGFILE` - log file path (default: `~/.cache/a.log`) - `AGE_LOGFILE` log file path
## Examples ## Example
```bash ```bash
# Encrypt a file
a encrypt secret.txt a encrypt secret.txt
# Encrypt with short command
a e secret.txt
# Decrypt a file
a decrypt secret.txt.age a decrypt secret.txt.age
a d secret.txt.age
# Encrypt a directory (includes hidden files)
a e /path/to/secrets/
# Encrypt and delete originals
a --delete e secret.txt
# Force overwrite existing .age file
a -f e secret.txt
# Verbose output
a -v e secret.txt
``` ```
## Behavior
- Encrypting a directory processes all files recursively, including hidden files
- Already encrypted files (`.age`) are skipped during encryption
- Only `.age` files are processed during directory decryption
- Original files are preserved by default (use `--delete` to remove them)
- Output files are not overwritten by default (use `--force` to overwrite)
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : --> <!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -15,37 +15,38 @@
SCRIPT=$(basename "$0") SCRIPT=$(basename "$0")
# Require bash 4.0+ for associative arrays and mapfile # Detect the current shell
if ((BASH_VERSINFO[0] < 4)); then CURRENT_SHELL=$(ps -p $$ -ocomm= | awk -F/ '{print $NF}')
echo "dfm requires bash 4.0+, found ${BASH_VERSION}"
if [[ "$(uname)" == "Darwin" ]]; then
if ! command -v brew &> /dev/null; then
echo "Installing Homebrew..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
fi
echo "Installing modern bash via Homebrew..."
brew install bash
echo "Done. Restart your shell and run dfm again."
else
echo "Install bash 4.0+ and try again."
fi
exit 1
fi
# shellcheck disable=SC1091 # Function to source files based on the shell
source "$DOTFILES/config/shared.sh" source_file()
# shellcheck disable=SC1090
source "${DOTFILES}/local/bin/msgr"
# Get description from a script file's @description tag
get_script_description()
{ {
local file="$1" local file=$1
local desc case "$CURRENT_SHELL" in
desc=$(sed -n '/@description/s/.*@description *\(.*\)/\1/p' "$file" | head -1) fish)
echo "${desc:-No description available}" if [[ -f "$file.fish" ]]; then
# shellcheck disable=SC1090
source "$file.fish"
else
echo "Fish shell file not found: $file.fish"
exit 1
fi
;;
sh | bash | zsh)
# shellcheck disable=SC1090
source "$file"
;;
*)
echo "Unsupported shell: $CURRENT_SHELL"
exit 1
;;
esac
} }
# Modify the source commands to use the new function
source_file "$DOTFILES/config/shared.sh"
source_file "${DOTFILES}/local/bin/msgr"
# Menu builder # Menu builder
menu_builder() menu_builder()
{ {
@@ -53,9 +54,9 @@ menu_builder()
local commands=("${@:2}") local commands=("${@:2}")
local width=60 local width=60
printf "\n%s\n" "$(printf '%.s─' $(seq 1 "$width"))" printf "\n%s\n" "$(printf '%.s─' $(seq 1 $width))"
printf "%-${width}s\n" " $title" printf "%-${width}s\n" " $title"
printf "%s\n" "$(printf '%.s─' $(seq 1 "$width"))" printf "%s\n" "$(printf '%.s─' $(seq 1 $width))"
for cmd in "${commands[@]}"; do for cmd in "${commands[@]}"; do
local name=${cmd%%:*} local name=${cmd%%:*}
@@ -79,6 +80,7 @@ section_install()
"imagick:Install ImageMagick CLI" "imagick:Install ImageMagick CLI"
"macos:Setup nice macOS defaults" "macos:Setup nice macOS defaults"
"npm-packages:Install NPM Packages" "npm-packages:Install NPM Packages"
"ntfy:Install ntfy"
"nvm-latest:Install latest lts node using nvm" "nvm-latest:Install latest lts node using nvm"
"nvm:Install Node Version Manager (nvm)" "nvm:Install Node Version Manager (nvm)"
"z:Install z" "z:Install z"
@@ -98,7 +100,6 @@ section_install()
$0 install npm-packages $0 install npm-packages
$0 install z $0 install z
msgr msg "Reloading configurations again..." msgr msg "Reloading configurations again..."
# shellcheck disable=SC1091
source "$DOTFILES/config/shared.sh" source "$DOTFILES/config/shared.sh"
msgr yay "All done!" msgr yay "All done!"
;; ;;
@@ -207,88 +208,87 @@ section_brew()
"untracked:List untracked brew packages" "untracked:List untracked brew packages"
) )
if ! x-have brew; then x-have brew && {
msgr warn "brew not available, skipping" case "$1" in
return 0 install)
fi brew bundle install --file="$BREWFILE" --force --quiet && msgr yay "Done!"
;;
case "$1" in update)
install) brew update && brew outdated && brew upgrade && brew cleanup
brew bundle install --file="$BREWFILE" --force --quiet && msgr yay "Done!" msgr yay "Done!"
;; ;;
update) updatebundle)
brew update && brew outdated && brew upgrade && brew cleanup # Updates .dotfiles/homebrew/Brewfile with descriptions
msgr yay "Done!" brew bundle dump \
;; --force \
--file="$BREWFILE" \
--cleanup \
--tap \
--formula \
--cask \
--describe && msgr yay "Done!"
;;
updatebundle) leaves)
# Updates .dotfiles/homebrew/Brewfile with descriptions brew leaves --installed-on-request
brew bundle dump \ ;;
--force \
--file="$BREWFILE" \
--cleanup \
--tap \
--formula \
--cask \
--describe && msgr yay "Done!"
;;
leaves) untracked)
brew leaves --installed-on-request declare -a BREW_LIST_ALL
;; while IFS= read -r line; do
BREW_LIST_ALL+=("$line")
done < <(brew list --formula --installed-on-request -1 --full-name)
while IFS= read -r c; do
BREW_LIST_ALL+=("$c")
done < <(brew list --cask -1 --full-name)
untracked) # Remove entries that are installed as dependencies
declare -a BREW_LIST_ALL declare -a BREW_LIST_DEPENDENCIES
while IFS= read -r line; do while IFS= read -r l; do
BREW_LIST_ALL+=("$line") BREW_LIST_DEPENDENCIES+=("$l")
done < <(brew list --formula --installed-on-request -1 --full-name) done < <(brew list -1 --installed-as-dependency)
while IFS= read -r c; do
BREW_LIST_ALL+=("$c")
done < <(brew list --cask -1 --full-name)
# Remove entries that are installed as dependencies declare -a BREW_LIST_BUNDLED
declare -a BREW_LIST_DEPENDENCIES while IFS= read -r b; do
while IFS= read -r l; do BREW_LIST_BUNDLED+=("$b")
BREW_LIST_DEPENDENCIES+=("$l") done < <(brew bundle list --all --file="$BREWFILE")
done < <(brew list -1 --installed-as-dependency)
declare -a BREW_LIST_BUNDLED declare -a BREW_LIST_TRACKED_WITHOUT_DEPS
while IFS= read -r b; do for f in "${BREW_LIST_ALL[@]}"; do
BREW_LIST_BUNDLED+=("$b") # shellcheck disable=SC2199
done < <(brew bundle list --all --file="$BREWFILE") if [[ " ${BREW_LIST_DEPENDENCIES[@]} " != *" ${f} "* ]]; then
BREW_LIST_TRACKED_WITHOUT_DEPS+=("$f")
fi
done
declare -a BREW_LIST_TRACKED_WITHOUT_DEPS array_diff BREW_LIST_UNTRACKED BREW_LIST_TRACKED_WITHOUT_DEPS BREW_LIST_BUNDLED
for f in "${BREW_LIST_ALL[@]}"; do
# shellcheck disable=SC2199 # If there are no untracked packages, exit
if [[ " ${BREW_LIST_DEPENDENCIES[@]} " != *" ${f} "* ]]; then if [ ${#BREW_LIST_UNTRACKED[@]} -eq 0 ]; then
BREW_LIST_TRACKED_WITHOUT_DEPS+=("$f") msgr yay "No untracked packages found!"
exit 0
fi fi
done
array_diff BREW_LIST_UNTRACKED BREW_LIST_TRACKED_WITHOUT_DEPS BREW_LIST_BUNDLED echo "Untracked:"
for f in "${BREW_LIST_UNTRACKED[@]}"; do
echo " $f"
done
;;
# If there are no untracked packages, return autoupdate)
if [ ${#BREW_LIST_UNTRACKED[@]} -eq 0 ]; then brew autoupdate delete
msgr yay "No untracked packages found!" brew autoupdate start 43200 --upgrade --cleanup --immediate
return 0 ;;
fi
echo "Untracked:" clean) brew bundle cleanup --file="$BREWFILE" && msgr yay "Done!" ;;
for f in "${BREW_LIST_UNTRACKED[@]}"; do
echo " $f"
done
;;
autoupdate) *) menu_builder "$USAGE_PREFIX" "${MENU[@]}" ;;
brew autoupdate delete esac
brew autoupdate start 43200 --upgrade --cleanup --immediate }
;;
clean) brew bundle cleanup --file="$BREWFILE" && msgr yay "Done!" ;; ! x-have brew && menu_builder "$USAGE_PREFIX" "brew not available on this system"
*) menu_builder "$USAGE_PREFIX" "${MENU[@]}" ;;
esac
} }
section_helpers() section_helpers()
@@ -305,10 +305,10 @@ section_helpers()
"wezterm:Show wezterm keybindings" "wezterm:Show wezterm keybindings"
) )
CMD="${1:-}" CMD="$1"
[[ $# -gt 0 ]] && shift shift
SECTION="${1:-}" SECTION="$1"
[[ $# -gt 0 ]] && shift shift
case "$CMD" in case "$CMD" in
path) path)
@@ -379,60 +379,55 @@ section_apt()
"clean:Clean apt cache" "clean:Clean apt cache"
) )
if ! x-have apt; then x-have apt && {
msgr warn "apt not available, skipping" case "$1" in
return 0 upkeep)
fi sudo apt update \
&& sudo apt upgrade -y \
&& sudo apt autoremove -y \
&& sudo apt clean
;;
case "$1" in install)
upkeep) # if apt.txt is not found, exit
sudo apt update \ [ ! -f "$DOTFILES/tools/apt.txt" ] && msgr err "apt.txt not found" && exit 0
&& sudo apt upgrade -y \
&& sudo apt autoremove -y \
&& sudo apt clean
;;
install) # Load apt.txt, remove comments (even if trailing comment) and empty lines.
# if apt.txt is not found, return with error #
if [ ! -f "$DOTFILES/tools/apt.txt" ]; then # Ignoring "Quote this to prevent word splitting."
msgr err "apt.txt not found"
return 1
fi
# Load apt.txt, remove comments (even if trailing comment) and empty lines.
#
# Ignoring "Quote this to prevent word splitting."
# shellcheck disable=SC2046
sudo apt install \
-y $(
grep -vE '^\s*#' "$DOTFILES/tools/apt.txt" \
| sed -e 's/#.*//' \
| tr '\n' ' '
)
# If there's a apt.txt file under hosts/$hostname/apt.txt,
# run install on those lines too.
HOSTNAME=$(hostname -s)
HOST_APT="$DOTFILES/hosts/$HOSTNAME/apt.txt"
[[ -f $HOST_APT ]] && {
# shellcheck disable=SC2046 # shellcheck disable=SC2046
sudo apt install -y $( sudo apt install \
grep -vE '^\s*#' "$HOST_APT" \ -y $(
| sed -e 's/#.*//' \ grep -vE '^\s*#' "$DOTFILES/tools/apt.txt" \
| tr '\n' ' ' | sed -e 's/#.*//' \
) | tr '\n' ' '
} )
# Try this for an alternative way to install packages # If there's a apt.txt file under hosts/$hostname/apt.txt,
# xargs -a <(awk '! /^ *(#|$)/' "$packagelist") -r -- sudo apt-get install -y # run install on those lines too.
;; HOSTNAME=$(hostname -s)
HOST_APT="$DOTFILES/hosts/$HOSTNAME/apt.txt"
[[ -f $HOST_APT ]] && {
# shellcheck disable=SC2046
sudo apt install -y $(
grep -vE '^\s*#' "$HOST_APT" \
| sed -e 's/#.*//' \
| tr '\n' ' '
)
}
update) sudo apt update ;; # Try this for an alternative way to install packages
upgrade) sudo apt upgrade -y ;; # xargs -a <(awk '! /^ *(#|$)/' "$packagelist") -r -- sudo apt-get install -y
autoremove) sudo apt autoremove -y ;; ;;
clean) sudo apt clean ;;
*) menu_builder "$USAGE_PREFIX" "${MENU[@]}" ;; update) sudo apt update ;;
esac upgrade) sudo apt upgrade -y ;;
autoremove) sudo apt autoremove -y ;;
clean) sudo apt clean ;;
*) menu_builder "$USAGE_PREFIX" "${MENU[@]}" ;;
esac
}
! x-have apt && menu_builder "$USAGE_PREFIX" "apt not available on this system"
} }
section_docs() section_docs()
@@ -539,13 +534,13 @@ section_check()
case "$1" in case "$1" in
a | arch) a | arch)
[[ $2 == "" ]] && echo "$X_ARCH" && return 0 [[ $2 == "" ]] && echo "$X_ARCH" && exit 0
[[ $X_ARCH == "$2" ]] && return 0 || return 1 [[ $X_ARCH == "$2" ]] && exit 0 || exit 1
;; ;;
h | host | hostname) h | host | hostname)
[[ $2 == "" ]] && echo "$X_HOSTNAME" && return 0 [[ $2 == "" ]] && echo "$X_HOSTNAME" && exit 0
[[ $X_HOSTNAME == "$2" ]] && return 0 || return 1 [[ $X_HOSTNAME == "$2" ]] && exit 0 || exit 1
;; ;;
*) menu_builder "$USAGE_PREFIX" "${MENU[@]}" ;; *) menu_builder "$USAGE_PREFIX" "${MENU[@]}" ;;
@@ -556,18 +551,33 @@ section_scripts()
{ {
USAGE_PREFIX="$SCRIPT scripts <command>" USAGE_PREFIX="$SCRIPT scripts <command>"
# Get description from a file
get_script_description()
{
local file
local desc
file="$1"
desc=$(sed -n '/@description/s/.*@description *\(.*\)/\1/p' "$file" | head -1)
echo "${desc:-No description available}"
}
# Collect scripts and their descriptions # Collect scripts and their descriptions
local menu_items=() declare -A SCRIPT_MENU
for script in "$DOTFILES/scripts/install-"*.sh; do for script in "$DOTFILES/scripts/install-"*.sh; do
if [ -f "$script" ]; then if [ -f "$script" ]; then
name=$(basename "$script" .sh | sed 's/install-//') name=$(basename "$script" .sh | sed 's/install-//')
desc=$(get_script_description "$script") desc=$(get_script_description "$script")
menu_items+=("$name:$desc") SCRIPT_MENU[$name]="$desc"
fi fi
done done
case "$1" in case "$1" in
"") "")
# Show the menu
local menu_items=()
for name in "${!SCRIPT_MENU[@]}"; do
menu_items+=("$name:${SCRIPT_MENU[$name]}")
done
menu_builder "$USAGE_PREFIX" "${menu_items[@]}" menu_builder "$USAGE_PREFIX" "${menu_items[@]}"
;; ;;
*) *)
@@ -599,7 +609,7 @@ section_tests()
echo " $i" echo " $i"
done done
;; ;;
msgr) msg)
# shellcheck disable=SC1010 # shellcheck disable=SC1010
msgr done "msgr done" msgr done "msgr done"
msgr done_suffix "msgr done_suffix" msgr done_suffix "msgr done_suffix"
@@ -623,7 +633,7 @@ usage()
{ {
echo "" echo ""
msgr prompt "Usage: $SCRIPT <section> <command>" msgr prompt "Usage: $SCRIPT <section> <command>"
echo " Empty <command> prints <section> help." echo $" Empty <command> prints <section> help."
echo "" echo ""
section_install section_install
echo "" echo ""
@@ -644,8 +654,8 @@ usage()
main() main()
{ {
SECTION="${1:-}" SECTION="$1"
[[ $# -gt 0 ]] && shift shift
# The main loop. The first keyword after $0 triggers section, or help. # The main loop. The first keyword after $0 triggers section, or help.
case "$SECTION" in case "$SECTION" in
install) section_install "$@" ;; install) section_install "$@" ;;
@@ -657,7 +667,7 @@ main()
docs) section_docs "$@" ;; docs) section_docs "$@" ;;
scripts) section_scripts "$@" ;; scripts) section_scripts "$@" ;;
tests) section_tests "$@" ;; tests) section_tests "$@" ;;
*) usage && return 0 ;; *) usage && exit 0 ;;
esac esac
} }

View File

@@ -8,7 +8,7 @@ set -euo pipefail
# Enable verbosity with VERBOSE=1 # Enable verbosity with VERBOSE=1
VERBOSE="${VERBOSE:-0}" VERBOSE="${VERBOSE:-0}"
A_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/alacritty" A_DIR="$HOME/.config/alacritty"
# Function to print usage information # Function to print usage information
usage() usage()

View File

@@ -7,15 +7,13 @@
# Author: Ismo Vuorinen 2025 # Author: Ismo Vuorinen 2025
# License: MIT # License: MIT
set -euo pipefail
# Check if the user has provided a directory as an argument # Check if the user has provided a directory as an argument
if [ "${1:-}" ]; then if [ "$1" ]; then
# Check if the directory exists # Check if the directory exists
if [ -d "$1" ]; then if [ -d "$1" ]; then
CLEANDIR="$1" CLEANDIR="$1"
else else
echo "Error: Directory $1 does not exist." >&2 msgr err "Directory $1 does not exist."
exit 1 exit 1
fi fi
else else
@@ -29,7 +27,7 @@ remove_node_modules_vendor()
# If the directory is a symlink, skip it # If the directory is a symlink, skip it
if [ -L "$dir" ]; then if [ -L "$dir" ]; then
echo "Skipping symlink $dir" msgr msg "Skipping symlink $dir"
return return
fi fi
@@ -37,18 +35,18 @@ remove_node_modules_vendor()
if [ -d "$dir" ]; then if [ -d "$dir" ]; then
# If node_modules or vendor folder exists, remove it and all its contents # If node_modules or vendor folder exists, remove it and all its contents
if [ -d "$dir/node_modules" ]; then if [ -d "$dir/node_modules" ]; then
echo "Removing $dir/node_modules" msgr run "Removing $dir/node_modules"
rm -rf "$dir/node_modules" rm -rf "$dir/node_modules"
fi fi
if [ -d "$dir/vendor" ]; then if [ -d "$dir/vendor" ]; then
echo "Removing $dir/vendor" msgr run "Removing $dir/vendor"
rm -rf "$dir/vendor" rm -rf "$dir/vendor"
fi fi
# Recursively check subdirectories # Recursively check subdirectories
for item in "$dir"/*; do for item in "$dir"/*; do
[ -d "$item" ] && remove_node_modules_vendor "$item" remove_node_modules_vendor "$item"
done done
fi fi
} }

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/bin/bash
# #
# List environment variables grouped by the first part before underscore # List environment variables grouped by the first part before underscore
# protecting environment variables that possibly contain sensitive information. # protecting environment variables that possibly contain sensitive information.

View File

@@ -1,26 +1,18 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# #
# Run a command in each directory matching a pattern. # foreach <folder> <commands that should be run to each file>
# # foreach "ls -d */" "git status" # run git status in each folder
# Usage: x-foreach <listing-command> <command> [args...]
# x-foreach "ls -d */" "git status"
# #
# Source: https://github.com/mvdan/dotfiles/blob/master/.bin/foreach # Source: https://github.com/mvdan/dotfiles/blob/master/.bin/foreach
set -euo pipefail cmd=$1
if [ $# -lt 2 ]; then
echo "Usage: $0 <listing-command> <command> [args...]"
exit 1
fi
listing=$1
shift shift
for dir in $(eval "$listing"); do for dir in $($cmd); do
( (
echo "$dir" echo "$dir"
cd "$dir" || exit 1 cd "$dir" || exit 1
"$@" # shellcheck disable=SC2294,SC2034
eval "$@" # allow multiple commands like "foo && bar"
) )
done done

View File

@@ -1,14 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env bash
#
# Display external IP address.
#
# Source: https://github.com/thirtythreeforty/dotfiles/blob/master/bin/extip # Source: https://github.com/thirtythreeforty/dotfiles/blob/master/bin/extip
set -euo pipefail curl icanhazip.com "${@}"
if ! command -v curl &> /dev/null; then
echo "Error: curl is required" >&2
exit 1
fi
curl -sf icanhazip.com

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/bin/bash
# #
# x-localip: script to display the local IP addresses of the system # x-localip: script to display the local IP addresses of the system
# #

50
local/bin/x-mkd Executable file
View File

@@ -0,0 +1,50 @@
#!/usr/bin/env bash
#
# Create a directory and cd into it
# Usage: x-mkd <dir>
set -euo pipefail
# Set verbosity with VERBOSE=1
VERBOSE="${VERBOSE:-0}"
# Function to print usage information
usage()
{
echo "Usage: $0 <dir>"
exit 1
}
# Function to print messages if VERBOSE is enabled
# $1 - message (string)
msg()
{
[[ "$VERBOSE" -eq 1 ]] && echo "$1"
}
# Function to create a directory and cd into it
# $1 - directory to create and cd into (string)
mkcd()
{
local dir=$1
mkdir -p "$dir" && msg "Directory $dir created"
cd "$dir" || {
msg "Failed to cd into $dir"
exit 1
}
msg "Changed directory to $dir"
}
# Main function
main()
{
if [ "$#" -ne 1 ]; then
usage
fi
mkcd "$1"
}
main "$@"

19
local/bin/x-mkd.md Normal file
View File

@@ -0,0 +1,19 @@
# x-mkd
Create a directory and immediately `cd` into it.
## Usage
```bash
x-mkd <dir>
```
Set `VERBOSE=1` for status messages.
## Example
```bash
x-mkd project && git init
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -39,6 +39,7 @@
# Defaults # Defaults
LOOP=0 LOOP=0
SLEEP=1 SLEEP=1
VERBOSE=0
TIMEOUT=5 TIMEOUT=5
usage() usage()
@@ -59,6 +60,8 @@ while [[ $# -gt 0 ]]; do
exit 0 exit 0
;; ;;
--verbose) --verbose)
# shellcheck disable=SC2034
VERBOSE=1
shift shift
;; ;;
--loop | --forever) --loop | --forever)

View File

@@ -227,9 +227,6 @@ do_check()
fi fi
} }
# If sourced, provide functions without executing main logic
(return 0 2> /dev/null) && return
####################################### #######################################
# Main routine: Parse subcommand and arguments, normalize PATH, # Main routine: Parse subcommand and arguments, normalize PATH,
# and dispatch to the appropriate functionality. # and dispatch to the appropriate functionality.

View File

@@ -1,17 +1,44 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# #
# Thin wrapper — delegates to x-path append. # Optimized script to append directories to PATH.
# Can be sourced (PATH changes propagate) or executed. # For each given directory, it removes all duplicate occurrences from PATH
# and then appends it if the directory exists.
# #
# Usage: x-path-append <directory1> [<directory2> ...] # Usage: x-path-append <directory1> [<directory2> ...]
# #
# Enable verbose output by setting the environment variable VERBOSE=1.
#
# Author: Ismo Vuorinen <https://github.com/ivuorinen> 2024 # Author: Ismo Vuorinen <https://github.com/ivuorinen> 2024
# License: MIT # License: MIT
VERBOSE="${VERBOSE:-0}" VERBOSE="${VERBOSE:-0}"
# shellcheck source=./x-path # Ensure that at least one directory is provided.
. "$(dirname "${BASH_SOURCE[0]:-$0}")/x-path" [ "$#" -lt 1 ] && {
echo "Usage: $0 <directory> [<directory> ...]"
exit 1
}
normalize_path_var for dir in "$@"; do
do_append "$@" # Check if the specified directory exists.
if [ ! -d "$dir" ]; then
[ "$VERBOSE" -eq 1 ] && echo "(?) Directory '$dir' does not exist. Skipping."
continue
fi
# Remove all duplicate occurrences of the directory from PATH.
case ":$PATH:" in
*":$dir:"*)
PATH=":${PATH}:"
PATH="${PATH//:$dir:/:}"
PATH="${PATH#:}"
PATH="${PATH%:}"
[ "$VERBOSE" -eq 1 ] && echo "Removed previous occurrences of '$dir' from PATH."
;;
*) ;;
esac
# Append the directory to PATH.
export PATH="${PATH:+$PATH:}$dir"
[ "$VERBOSE" -eq 1 ] && echo "Appended '$dir' to PATH."
done

View File

@@ -1,17 +1,50 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# #
# Thin wrapper — delegates to x-path prepend. # Optimized script to batch prepend directories to PATH.
# Can be sourced (PATH changes propagate) or executed. # For each given directory, it removes all duplicate occurrences from PATH
# and then prepends it. Directories that do not exist are skipped.
# #
# Usage: x-path-prepend <directory1> [<directory2> ...] # Usage: x-path-prepend <directory1> [<directory2> ...]
# #
# Enable verbose output by setting the environment variable VERBOSE=1.
#
# Author: Ismo Vuorinen <https://github.com/ivuorinen> 2024 # Author: Ismo Vuorinen <https://github.com/ivuorinen> 2024
# License: MIT # License: MIT
VERBOSE="${VERBOSE:-0}" VERBOSE="${VERBOSE:-0}"
# shellcheck source=./x-path # Ensure that at least one argument is provided.
. "$(dirname "${BASH_SOURCE[0]:-$0}")/x-path" [ "$#" -lt 1 ] && {
echo "Usage: $0 <directory> [<directory> ...]"
exit 1
}
normalize_path_var # Save the arguments in an array.
do_prepend "$@" dirs=("$@")
# Process the directories in reverse order so that the first argument ends up leftmost in PATH.
for ((idx = ${#dirs[@]} - 1; idx >= 0; idx--)); do
dir="${dirs[idx]}"
# Check if the specified directory exists.
if [ ! -d "$dir" ]; then
[ "$VERBOSE" -eq 1 ] && echo "(?) Directory '$dir' does not exist. Skipping."
continue
fi
# Remove all duplicate occurrences of the directory from PATH using built-in string operations.
case ":$PATH:" in
*":$dir:"*)
PATH=":${PATH}:"
PATH="${PATH//:$dir:/:}"
PATH="${PATH#:}"
PATH="${PATH%:}"
[ "$VERBOSE" -eq 1 ] && echo "Removed duplicate occurrences of '$dir' from PATH."
;;
*) ;;
esac
# Prepend the directory to PATH.
export PATH="$dir${PATH:+":$PATH"}"
[ "$VERBOSE" -eq 1 ] && echo "Prepended '$dir' to PATH."
done

View File

@@ -1,17 +1,41 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# #
# Thin wrapper — delegates to x-path remove. # Optimized script to remove directories from PATH.
# Can be sourced (PATH changes propagate) or executed. # For each specified directory, all occurrences are removed from PATH.
# #
# Usage: x-path-remove <directory1> [<directory2> ...] # Usage: x-path-remove <directory1> [<directory2> ...]
# #
# Enable verbose output by setting the environment variable VERBOSE=1.
#
# Author: Ismo Vuorinen <https://github.com/ivuorinen> 2024 # Author: Ismo Vuorinen <https://github.com/ivuorinen> 2024
# License: MIT # License: MIT
VERBOSE="${VERBOSE:-0}" VERBOSE="${VERBOSE:-0}"
# shellcheck source=./x-path # Ensure that at least one directory is provided.
. "$(dirname "${BASH_SOURCE[0]:-$0}")/x-path" [ "$#" -lt 1 ] && {
echo "Usage: $0 <directory> [<directory> ...]"
exit 1
}
normalize_path_var for dir in "$@"; do
do_remove "$@" # Remove trailing slash if present, unless the directory is "/"
[ "$dir" != "/" ] && dir="${dir%/}"
# Check if the directory is present in PATH.
case ":$PATH:" in
*":$dir:"*)
# Remove all occurrences of the directory from PATH using parameter expansion.
PATH=":${PATH}:"
PATH="${PATH//:$dir:/:}"
PATH="${PATH#:}"
PATH="${PATH%:}"
[ "$VERBOSE" -eq 1 ] && echo "Removed '$dir' from PATH."
;;
*)
[ "$VERBOSE" -eq 1 ] && echo "(?) '$dir' is not in PATH."
;;
esac
done
export PATH

View File

@@ -35,7 +35,7 @@ msg()
# Notify function # Notify function
notify() notify()
{ {
notify-send.sh --replace-file "$replace_id" "$@" notify-call --replace-file "$replace_id" "$@"
} }
# Stop recording function # Stop recording function

View File

@@ -37,7 +37,7 @@ The script automatically tries authentication methods in this order:
1. **Specific key** (if provided in host file) 1. **Specific key** (if provided in host file)
2. **Auto-detected default keys** (`~/.ssh/id_ed25519`, `id_rsa`, `id_ecdsa`, 2. **Auto-detected default keys** (`~/.ssh/id_ed25519`, `id_rsa`, `id_ecdsa`,
`id_dsa`) `id_dsa`)
3. **SSH agent or system default authentication** 3. **SSH agent or system default authentication**
This means you can mix hosts with and without specific keys, and the script will This means you can mix hosts with and without specific keys, and the script will
@@ -178,7 +178,7 @@ SSH_RETRIES=3
3. **Staged Rollout**: Test on non-critical hosts first 3. **Staged Rollout**: Test on non-critical hosts first
4. **Review Logs**: Check log files for detailed information 4. **Review Logs**: Check log files for detailed information
5. **Preserve Access**: Script ensures key-based auth works before disabling 5. **Preserve Access**: Script ensures key-based auth works before disabling
passwords passwords
## Version ## Version

View File

@@ -1,13 +1,11 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# #
# Display 24-bit terminal color test. # This file echoes a bunch of 24-bit color codes
# # to the terminal to demonstrate its functionality.
# Usage: x-term-colors # The foreground escape sequence is ^[38;2;<r>;<g>;<b>m
# # The background escape sequence is ^[48;2;<r>;<g>;<b>m
# The foreground escape sequence is ^[38;2;<r>;<g>;<b>m # <r> <g> <b> range from 0 to 255 inclusive.
# The background escape sequence is ^[48;2;<r>;<g>;<b>m # The escape sequence ^[0m returns output to default
# <r> <g> <b> range from 0 to 255 inclusive.
# The escape sequence ^[0m returns output to default
setBackgroundColor() setBackgroundColor()
{ {

View File

@@ -0,0 +1,68 @@
#!/usr/bin/env bash
#
# This script contains a helper for sha256 validating your downloads
#
# Source: https://gist.github.com/onnimonni/b49779ebc96216771a6be3de46449fa1
# Author: Onni Hakala
# License: MIT
#
# Updated by Ismo Vuorinen <https://github.com/ivuorinen> 2022
##
set -euo pipefail
# Stop program and give error message
# $1 - error message (string)
error()
{
echo "(!) ERROR: $1" >&2
exit 1
}
# Check for sha256sum command
if ! command -v sha256sum &> /dev/null; then
error "sha256sum could not be found, please install it first"
fi
# Return sha256sum for file
# $1 - filename (string)
get_sha256sum()
{
sha256sum "$1" | head -c 64
}
# Validate input arguments
validate_inputs()
{
if [ -z "${filename:-}" ]; then
error "You need to provide filename as the first parameter"
fi
if [ -z "${file_hash:-}" ]; then
error "You need to provide sha256sum as the second parameter"
fi
}
# Main validation logic
validate_file()
{
if [ ! -f "$filename" ]; then
error "File $filename doesn't exist"
elif [ "$(get_sha256sum "$filename")" = "$file_hash" ]; then
echo "(*) Success: $filename matches provided sha256sum"
else
error "$filename doesn't match provided sha256sum"
fi
}
# Main function
main()
{
filename=$1
file_hash=$2
validate_inputs
validate_file
}
main "$@"

View File

@@ -0,0 +1,14 @@
# x-validate-sha256sum.sh
This script contains a helper for sha256 validating your downloads
## Usage
```bash
x-validate-sha256sum.sh file sha256sum
```
The script computes the SHA256 hash of `file` and compares it to the
expected value. It exits non-zero if the sums differ.
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail
# #
# Wait until a given host is down (determined by ping) then execute the # Wait until a given host is down (determined by ping) then execute the
# given command # given command

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail
# #
# Wait until a given host is online (determined by ping) then execute the # Wait until a given host is online (determined by ping) then execute the
# given command # given command

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail
# @description Create file containing key mappings for Neovim # @description Create file containing key mappings for Neovim
# Usage: ./create-nvim-keymaps.sh # Usage: ./create-nvim-keymaps.sh
# #
@@ -16,7 +15,7 @@ main()
printf "\`\`\`txt" printf "\`\`\`txt"
} > "$DEST" } > "$DEST"
nvim -c "redir! >> \"$DEST\"" -c 'silent verbose map' -c 'redir END' -c 'q' nvim -c "redir! >> $DEST" -c 'silent verbose map' -c 'redir END' -c 'q'
printf "\n\`\`\`\n\n- Generated on %s\n" "$(date)" >> "$DEST" printf "\n\`\`\`\n\n- Generated on %s\n" "$(date)" >> "$DEST"

View File

@@ -1,75 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# @description Install essential apt packages for development.
#
# shellcheck source=shared.sh
source "$DOTFILES/config/shared.sh"
msgr run "Starting to install apt packages"
if ! command -v apt &> /dev/null; then
msgr warn "apt not found (not a Debian-based system)"
exit 0
fi
packages=(
# Build essentials
build-essential # gcc, g++, make
cmake # Cross-platform build system
pkg-config # Helper for compiling against libraries
autoconf # Automatic configure script builder
automake # Makefile generator
libtool # Generic library support script
# Libraries for compiling languages
libssl-dev # SSL development headers
libffi-dev # Foreign function interface
zlib1g-dev # Compression library
libreadline-dev # Command-line editing
libbz2-dev # Bzip2 compression
libsqlite3-dev # SQLite database
libncurses-dev # Terminal UI library
# CLI utilities (not in cargo/go/npm)
jq # JSON processor
tmux # Terminal multiplexer
tree # Directory listing
unzip # Archive extraction
shellcheck # Shell script linter
socat # Multipurpose network relay
gnupg # GPG encryption/signing
software-properties-common # add-apt-repository command
)
install_packages()
{
local to_install=()
for pkg in "${packages[@]}"; do
pkg="${pkg%%#*}"
pkg="${pkg// /}"
[[ -z "$pkg" ]] && continue
if dpkg -s "$pkg" &> /dev/null; then
msgr ok "$pkg already installed"
else
to_install+=("$pkg")
fi
done
if [[ ${#to_install[@]} -gt 0 ]]; then
msgr run "Installing ${#to_install[@]} packages: ${to_install[*]}"
sudo apt update
sudo apt install -y "${to_install[@]}"
else
msgr ok "All packages already installed"
fi
}
main()
{
install_packages
msgr yay "apt package installations complete"
}
main "$@"

View File

@@ -11,10 +11,10 @@ scripts/install-cargo-packages.sh
## What it does ## What it does
1. If `cargo-install-update` is available, updates all existing packages first 1. If `cargo-install-update` is available, updates all existing packages first
and tracks which packages are already installed. and tracks which packages are already installed.
2. Installs each package from the inline list using `cargo install`, 2. Installs each package from the inline list using `cargo install`,
skipping any already handled by the update step. skipping any already handled by the update step.
Builds run in parallel using available CPU cores (minus two). Builds run in parallel using available CPU cores (minus two).
3. Runs package-specific post-install steps. 3. Runs package-specific post-install steps.
4. Cleans the cargo cache with `cargo cache --autoclean`. 4. Cleans the cargo cache with `cargo cache --autoclean`.

View File

@@ -1,9 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail
# @description Install cargo/rust packages. # @description Install cargo/rust packages.
#
# shellcheck source=shared.sh
source "$DOTFILES/config/shared.sh"
msgr run "Starting to install rust/cargo packages" msgr run "Starting to install rust/cargo packages"

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail
# @description Update pure-bash-bible cheatsheets # @description Update pure-bash-bible cheatsheets
# shellcheck disable=SC2231,SC2034,SC2181,SC2068 # shellcheck disable=SC2231,SC2034,SC2181,SC2068
# shellcheck source=shared.sh # shellcheck source=shared.sh
@@ -86,7 +85,7 @@ process_chapters()
if [ '---' != "$(head -1 < "$cheat_file")" ]; then if [ '---' != "$(head -1 < "$cheat_file")" ]; then
local metadata local metadata
metadata="$PBB_SYNTAX\n$PBB_TAGS\n$PBB_SOURCE\n" metadata="$PBB_SYNTAX\n$PBB_TAGS\n$PBB_SOURCE\n"
printf '%s\n%b%s\n%s' "---" "$metadata" "---" "$(cat "$cheat_file")" > "$cheat_file" echo -e "---\n$metadata---\n$(cat "$cheat_file")" > "$cheat_file"
fi fi
done done
} }

View File

@@ -1,9 +1,8 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail
# @description Install PHP Package Manager Composer # @description Install PHP Package Manager Composer
# #
# shellcheck source="shared.sh" # shellcheck source="shared.sh"
source "$DOTFILES/config/shared.sh" source "$HOME/.dotfiles/config/shared.sh"
if ! command -v php &> /dev/null; then if ! command -v php &> /dev/null; then
msg_err "PHP Not Available, cannot install composer" msg_err "PHP Not Available, cannot install composer"
@@ -23,7 +22,5 @@ fi
php composer-setup.php --quiet php composer-setup.php --quiet
RESULT=$? RESULT=$?
rm composer-setup.php rm composer-setup.php
if [ $RESULT -eq 0 ]; then mv composer.phar ~/.local/bin/composer
mv composer.phar ~/.local/bin/composer
fi
exit $RESULT exit $RESULT

View File

@@ -1,83 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# @description Install essential dnf packages for development.
#
# shellcheck source=shared.sh
source "$DOTFILES/config/shared.sh"
msgr run "Starting to install dnf packages"
if ! command -v dnf &> /dev/null; then
msgr warn "dnf not found (not a Fedora/RHEL-based system)"
exit 0
fi
packages=(
# Build essentials (individual packages, group handled separately)
cmake # Cross-platform build system
pkgconfig # Helper for compiling against libraries
autoconf # Automatic configure script builder
automake # Makefile generator
libtool # Generic library support script
# Libraries for compiling languages
openssl-devel # SSL development headers
libffi-devel # Foreign function interface
zlib-devel # Compression library
readline-devel # Command-line editing
bzip2-devel # Bzip2 compression
sqlite-devel # SQLite database
ncurses-devel # Terminal UI library
# CLI utilities (not in cargo/go/npm)
jq # JSON processor
tmux # Terminal multiplexer
tree # Directory listing
unzip # Archive extraction
ShellCheck # Shell script linter
socat # Multipurpose network relay
gnupg2 # GPG encryption/signing
)
install_dev_tools_group()
{
if dnf group list installed 2>/dev/null | grep -q "Development Tools"; then
msgr ok "@development-tools group already installed"
else
msgr run "Installing @development-tools group"
sudo dnf group install -y "Development Tools"
fi
}
install_packages()
{
local to_install=()
for pkg in "${packages[@]}"; do
pkg="${pkg%%#*}"
pkg="${pkg// /}"
[[ -z "$pkg" ]] && continue
if rpm -q "$pkg" &> /dev/null; then
msgr ok "$pkg already installed"
else
to_install+=("$pkg")
fi
done
if [[ ${#to_install[@]} -gt 0 ]]; then
msgr run "Installing ${#to_install[@]} packages: ${to_install[*]}"
sudo dnf install -y "${to_install[@]}"
else
msgr ok "All packages already installed"
fi
}
main()
{
install_dev_tools_group
install_packages
msgr yay "dnf package installations complete"
}
main "$@"

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail
# @description Install NerdFonts # @description Install NerdFonts
# #
# shellcheck source="shared.sh" # shellcheck source="shared.sh"
@@ -22,7 +21,7 @@ clone_or_update_repo()
git clone --quiet --filter=blob:none --sparse --depth=1 "$GIT_REPO" "$TMP_PATH" git clone --quiet --filter=blob:none --sparse --depth=1 "$GIT_REPO" "$TMP_PATH"
fi fi
cd "$TMP_PATH" || { msgr err "No such folder $TMP_PATH"; exit 1; } cd "$TMP_PATH" || msgr err "No such folder $TMP_PATH"
} }
# Function to add fonts to sparse-checkout # Function to add fonts to sparse-checkout

View File

@@ -1,10 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail
# @description Install GitHub CLI extensions # @description Install GitHub CLI extensions
# #
# shellcheck source="shared.sh" # shellcheck source="shared.sh"
source "${DOTFILES}/config/shared.sh" source "${DOTFILES}/config/shared.sh"
# Enable verbosity with VERBOSE=1
VERBOSE="${VERBOSE:-0}"
msgr run "Installing gh (GitHub Client) extensions" msgr run "Installing gh (GitHub Client) extensions"
if ! command -v gh &> /dev/null; then if ! command -v gh &> /dev/null; then

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail
# @description Install git-crypt # @description Install git-crypt
# #
# NOTE: Experimental, wip # NOTE: Experimental, wip
@@ -7,17 +6,21 @@ set -euo pipefail
# shellcheck source=shared.sh # shellcheck source=shared.sh
source "${DOTFILES}/config/shared.sh" source "${DOTFILES}/config/shared.sh"
# Enable verbosity with VERBOSE=1
VERBOSE="${VERBOSE:-0}"
msgr run "Installing git-crypt" msgr run "Installing git-crypt"
if ! command -v git-crypt &> /dev/null; then if ! command -v git-crypt &> /dev/null; then
REPO_URL="https://github.com/AGWA/git-crypt.git" REPO_URL="https://github.com/AGWA/git-crypt.git"
CHECK_PATH="${XDG_BIN_HOME}/git-crypt" CHECK_PATH="${XDG_BIN_HOME}/git-crypt"
BUILD_PATH="$(mktemp -d)" BUILD_PATH="/tmp/git-crypt"
trap 'rm -rf "$BUILD_PATH"' EXIT
if [ ! -f "$CHECK_PATH" ]; then rm -rf "$BUILD_PATH"
git clone --depth 1 "$REPO_URL" "$BUILD_PATH" || { msgr err "Failed to clone $REPO_URL"; exit 1; }
cd "$BUILD_PATH" || { msgr err "$BUILD_PATH not found"; exit 1; } if [ ! -d "$CHECK_PATH" ]; then
git clone --depth 1 "$REPO_URL" "$BUILD_PATH" || true
cd "$BUILD_PATH" || msg_err "$BUILD_PATH not found"
make && make install PREFIX="$HOME/.local" make && make install PREFIX="$HOME/.local"
else else
msgr run_done "git-crypt ($CHECK_PATH) already installed" msgr run_done "git-crypt ($CHECK_PATH) already installed"

View File

@@ -1,10 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail
# @description Install Go packages # @description Install Go packages
# #
# shellcheck source=shared.sh # shellcheck source=shared.sh
source "$DOTFILES/config/shared.sh" source "$DOTFILES/config/shared.sh"
# Enable verbosity with VERBOSE=1
VERBOSE="${VERBOSE:-0}"
msgr run "Installing go packages" msgr run "Installing go packages"
! x-have "go" && msgr err "go hasn't been installed yet." && exit 0 ! x-have "go" && msgr err "go hasn't been installed yet." && exit 0

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -uo pipefail
# @description Sets macOS Defaults that I like # @description Sets macOS Defaults that I like
# #
# This script contains large portions from following scripts: # This script contains large portions from following scripts:
@@ -8,7 +7,7 @@ set -uo pipefail
[ "$(uname)" != "Darwin" ] && echo "Not a macOS system" && exit 0 [ "$(uname)" != "Darwin" ] && echo "Not a macOS system" && exit 0
# shellcheck source=shared.sh # shellcheck source=shared.sh
source "$DOTFILES/config/shared.sh" source "$HOME/.dotfiles/config/shared.sh"
msgr run "Starting to set macOS defaults, these require sudo privileges:" msgr run "Starting to set macOS defaults, these require sudo privileges:"
@@ -24,12 +23,12 @@ while true; do
done 2> /dev/null & done 2> /dev/null &
# Skip when shell is fish # Skip when shell is fish
if [[ $SHELL != "$(command -v fish)" ]]; then if [[ $SHELL != $(which fish) ]]; then
msgr nested "Change user shell to zsh if it is available and not the current" msgr nested "Change user shell to zsh if it is available and not the current"
# Change user shell to zsh if not that already. # Change user shell to zsh if not that already.
if hash zsh 2> /dev/null; then if hash zsh 2> /dev/null; then
[[ $SHELL != "$(command -v zsh)" ]] && chsh -s "$(command -v zsh)" [[ $SHELL != $(which zsh) ]] && chsh -s "$(which zsh)"
fi fi
fi fi

View File

@@ -1,10 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail
# @description Install npm packages globally. # @description Install npm packages globally.
# #
# shellcheck source=shared.sh # shellcheck source=shared.sh
source "$DOTFILES/config/shared.sh" source "$DOTFILES/config/shared.sh"
# Enable verbosity with VERBOSE=1
VERBOSE="${VERBOSE:-0}"
msgr msg "Starting to install npm packages" msgr msg "Starting to install npm packages"
if ! command -v npm &> /dev/null; then if ! command -v npm &> /dev/null; then

View File

@@ -1,10 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail
# @description Install ntfy # @description Install ntfy
# #
# shellcheck source=shared.sh # shellcheck source=shared.sh
source "$DOTFILES/config/shared.sh" source "$DOTFILES/config/shared.sh"
# Enable verbosity with VERBOSE=1
VERBOSE="${VERBOSE:-0}"
# Check if ntfy is already installed # Check if ntfy is already installed
if x-have "ntfy"; then if x-have "ntfy"; then
msgr "done" "ntfy already installed" msgr "done" "ntfy already installed"
@@ -21,31 +23,28 @@ case $(dfm check arch) in
;; ;;
*) *)
msgr err "Unsupported OS" msgr err "Unsupported OS"
exit 1
;; ;;
esac esac
NTFY_VERSION="$(x-gh-get-latest-version binwiederhier/ntfy)" NTFY_VERSION="$(x-gh-get-latest-version binwiederhier/ntfy)"
NTFY_URL="https://github.com/binwiederhier/ntfy" NTFY_URL="https://github.com/binwiederhier/ntfy"
NTFY_TARBALL="ntfy_${NTFY_VERSION}_${NTFY_ARCH}.tar.gz" NTFY_DEST="/tmp/ntfy_${NTFY_VERSION}_${NTFY_ARCH}"
NTFY_DIR="ntfy_${NTFY_VERSION}_${NTFY_ARCH}"
# Download and extract ntfy # Download and extract ntfy
install_ntfy() install_ntfy()
{ {
local tmpdir curl -L "$NTFY_URL/releases/download/v${NTFY_VERSION}/${NTFY_DEST}.tar.gz" -o "${NTFY_DEST}.tar.gz"
tmpdir="$(mktemp -d)" tar zxvf "${NTFY_DEST}.tar.gz"
trap 'rm -rf "$tmpdir"' EXIT cp -a "${NTFY_DEST}/ntfy" ~/.local/bin/ntfy
curl -L "$NTFY_URL/releases/download/v${NTFY_VERSION}/${NTFY_TARBALL}" -o "$tmpdir/${NTFY_TARBALL}"
tar zxvf "$tmpdir/${NTFY_TARBALL}" -C "$tmpdir"
cp -a "$tmpdir/${NTFY_DIR}/ntfy" ~/.local/bin/ntfy
mkdir -p ~/.config/ntfy mkdir -p ~/.config/ntfy
# Copy config only if it does not exist # Copy config only if it does not exist
if [ ! -f "$HOME/.config/ntfy/client.yml" ]; then if [ ! -f "$HOME/.config/ntfy/client.yml" ]; then
cp "$tmpdir/${NTFY_DIR}/client/client.yml" ~/.config/ntfy/client.yml cp "${NTFY_DEST}/client/client.yml" ~/.config/ntfy/client.yml
fi fi
# Clean up
rm -rf "${NTFY_DEST}" "${NTFY_DEST}.tar.gz"
} }
main() main()

View File

@@ -1,10 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail
# @description Install Python packages using uv. # @description Install Python packages using uv.
# #
# shellcheck source=shared.sh # shellcheck source=shared.sh
source "$DOTFILES/config/shared.sh" source "$DOTFILES/config/shared.sh"
# Enable verbosity with VERBOSE=1
VERBOSE="${VERBOSE:-0}"
msgr run "Starting to install Python packages" msgr run "Starting to install Python packages"
# Ensure uv is available # Ensure uv is available
@@ -16,8 +18,7 @@ fi
# CLI tools — installed isolated with `uv tool install` # CLI tools — installed isolated with `uv tool install`
tools=( tools=(
ansible # IT automation and configuration management ansible # IT automation and configuration management
openapi-python-client # Generate Python API clients from OpenAPI specs
) )
# Library packages — installed into system Python with `uv pip install --system` # Library packages — installed into system Python with `uv pip install --system`

View File

@@ -1,9 +1,11 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail
# @description Install XCode CLI Tools with osascript magic. # @description Install XCode CLI Tools with osascript magic.
# Ismo Vuorinen <https://github.com/ivuorinen> 2018 # Ismo Vuorinen <https://github.com/ivuorinen> 2018
# #
# Enable verbosity with VERBOSE=1
VERBOSE="${VERBOSE:-0}"
# Check if the script is running on macOS # Check if the script is running on macOS
if [ "$(uname)" != "Darwin" ]; then if [ "$(uname)" != "Darwin" ]; then
msgr warn "Not a macOS system" msgr warn "Not a macOS system"
@@ -29,7 +31,7 @@ keep_alive_sudo()
done 2> /dev/null & done 2> /dev/null &
} }
XCODE_TOOLS_PATH="$(xcode-select -p)" XCODE_TOOLS_PATH=$(xcode-select -p)
XCODE_SWIFT_PATH="$XCODE_TOOLS_PATH/usr/bin/swift" XCODE_SWIFT_PATH="$XCODE_TOOLS_PATH/usr/bin/swift"
# Function to prompt for XCode CLI Tools installation # Function to prompt for XCode CLI Tools installation

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail
# @description Install z # @description Install z
# #
# shellcheck source=shared.sh # shellcheck source=shared.sh

View File

@@ -4,8 +4,17 @@
# Helper env variables. Use like this: VERBOSE=1 ./script.sh # Helper env variables. Use like this: VERBOSE=1 ./script.sh
: "${VERBOSE:=0}" : "${VERBOSE:=0}"
# Source the main shared config if not already loaded # Set variable that checks if the shared.sh script has been
if [ -z "${SHARED_SCRIPTS_SOURCED:-}" ]; then # sourced only once If the script has been sourced more than once,
# the script not be sourced again.
[ -z "$SHARED_SCRIPTS_SOURCED" ] && {
source "${DOTFILES}/config/shared.sh" source "${DOTFILES}/config/shared.sh"
# Warn the user if the shared configuration hasn't been loaded yet
msgr warn "(!) shared.sh not sourced"
# Set variable that checks if the shared.sh script has been
# sourced only once.
# shellcheck disable=SC2034
export SHARED_SCRIPTS_SOURCED=1 export SHARED_SCRIPTS_SOURCED=1
fi }

14
tests/x-mkd.bats Executable file
View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bats
@test "x-mkd creates directory" {
dir="$BATS_TMPDIR/mkd-test"
run env VERBOSE=1 bash local/bin/x-mkd "$dir"
[ "$status" -eq 0 ]
[ -d "$dir" ]
}
@test "x-mkd with no args shows usage" {
run bash local/bin/x-mkd
[ "$status" -eq 1 ]
[[ "$output" == "Usage:"* ]]
}

1
tools/dotbot-brew Submodule

Submodule tools/dotbot-brew added at 98e346360b

1
tools/dotbot-pip Submodule

Submodule tools/dotbot-pip added at 4d0cc116e8

View File

@@ -0,0 +1 @@
openapi-python-client