Compare commits

..

11 Commits

Author SHA1 Message Date
a47ce85991 chore: remove hammerspoon type generator and types 2026-02-06 09:12:06 +02:00
13dd701eb7 feat(a): improve encryption script with better error handling
- Add dependency check for age and curl with install instructions
- Add --delete flag to remove originals after encryption
- Add -f/--force flag to control overwrite behavior
- Skip already-encrypted .age files during encryption
- Include hidden files (dotglob) when encrypting directories
- Handle empty directories gracefully with nullglob
- Allow flags in any position (proper option parsing)
- Add set -euo pipefail for better error handling
- Update documentation with all features and examples
- Bump version to 1.1.0
2026-02-06 01:51:01 +02:00
cfde007494 fix(shell): clean up rcfiles and remove redundancies
- Remove deprecated GREP_OPTIONS (handled via alias)
- Quote $ZSH_COMPDUMP to prevent word splitting
- Remove duplicate vim alias (nvim alias takes precedence)
- Consolidate completion path to ZSH_CUSTOM_COMPLETION_PATH
- Simplify PATH setup in rcfiles, centralize in exports
- Move LM Studio PATH from rcfiles to exports
- Add clarifying comments for macOS-specific ssh-add
2026-02-06 00:09:03 +02:00
ed4aa2ffe1 feat(scripts): add install-dnf-packages.sh for Fedora/RHEL 2026-02-05 23:46:10 +02:00
bcf11406b6 feat(scripts): add install-apt-packages.sh for Debian/Ubuntu
Install essential developer packages via apt:
- Build tools: build-essential, cmake, pkg-config, autoconf, automake, libtool
- Dev libraries: libssl-dev, libffi-dev, zlib1g-dev, libreadline-dev, etc.
- CLI utilities: jq, tmux, tree, unzip, shellcheck, socat, gnupg

Curated to avoid duplicates with cargo/go installs (ripgrep, fd, fzf, etc.).
Uses batched apt install for efficiency, exits gracefully on non-Debian systems.
2026-02-05 23:42:16 +02:00
443361cddb chore(scripts): remove unused VERBOSE declarations
Remove VERBOSE="${VERBOSE:-0}" from scripts that never reference
$VERBOSE after setting it. The variable is already set in
scripts/shared.sh line 5.
2026-02-05 22:57:20 +02:00
083d30a0c3 fix(scripts): fix shared.sh guard logic and echo -e portability
- shared.sh: simplify guard logic, remove misleading warning message,
  use ${VAR:-} pattern to avoid unbound variable error
- install-cheat-purebashbible.sh: replace echo -e with printf for
  POSIX portability
2026-02-05 22:55:27 +02:00
81190c051a fix(scripts): standardize source paths and quoting
- install-composer.sh: use $DOTFILES instead of $HOME/.dotfiles
- install-macos-defaults.sh: use $DOTFILES, replace which with command -v
- install-xcode-cli-tools.sh: quote command substitution
- create-nvim-keymaps.sh: quote $DEST in nvim redir command
2026-02-05 22:53:04 +02:00
de773ad68f refactor(scripts): add set -euo pipefail to all installer scripts
Add strict error handling to all scripts:
- 13 scripts get `set -euo pipefail`
- install-macos-defaults.sh gets `set -uo pipefail` (without -e) because
  defaults write commands may fail on newer macOS versions
- install-cargo-packages.sh: also add missing source of shared.sh
2026-02-05 22:51:40 +02:00
e8725c4b47 fix(scripts): use safe temp directories and fix composer exit code
- install-ntfy.sh: use mktemp -d with cleanup trap instead of /tmp/ntfy_*
- install-git-crypt.sh: use mktemp -d with cleanup trap instead of /tmp/git-crypt
- install-composer.sh: only move composer.phar if installation succeeded
2026-02-05 22:49:36 +02:00
b8070e2815 fix(scripts): add missing exit after error handlers
Critical bugs where error paths print a message but don't stop execution:
- install-fonts.sh: cd failure now exits properly
- install-ntfy.sh: unsupported OS case now exits with error
- install-git-crypt.sh: git clone and cd failures now exit properly
2026-02-05 22:44:44 +02:00
26 changed files with 482 additions and 19019 deletions

View File

@@ -1 +1 @@
3.14.3 3.14.2

View File

@@ -2,6 +2,7 @@
# 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
@@ -11,7 +12,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 # Import ssh keys in keychain (macOS-specific -A flag, silently fails on Linux)
ssh-add -A 2>/dev/null ssh-add -A 2>/dev/null
x-have antidot && { x-have antidot && {
@@ -22,5 +23,3 @@ 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"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -7,18 +7,13 @@
autoload -U promptinit; promptinit autoload -U promptinit; promptinit
export DOTFILES="$HOME/.dotfiles" export DOTFILES="$HOME/.dotfiles"
LOCAL_SHARE="$HOME/.local/share" # Minimal PATH for x-have and utilities; full PATH set in shared.sh/exports
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 PATH="$HOME/.local/bin:$DOTFILES/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 completions directory (ZSH_CUSTOM_COMPLETION_PATH set in shared.sh)
[ -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"
@@ -37,12 +32,10 @@ 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

@@ -7,8 +7,6 @@ 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,7 +282,8 @@ 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
export GREP_OPTIONS="--color=auto" # Note: GREP_OPTIONS is deprecated since GNU grep 2.21
# 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.
@@ -436,6 +437,10 @@ 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

@@ -1,7 +1,9 @@
#!/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
VERSION="1.0.0" set -euo pipefail
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}"
@@ -9,14 +11,49 @@ 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
# Parse flags for verbosity # Check for required dependencies
for arg in "$@"; do check_dependencies()
if [[ "$arg" == "-v" || "$arg" == "--verbose" ]]; then {
VERBOSE=true if ! command -v age &> /dev/null; then
break echo "Error: 'age' is not installed. Please install it first." >&2
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()
@@ -38,8 +75,6 @@ prepare_log_file()
chmod 0600 "$LOG_FILE" chmod 0600 "$LOG_FILE"
} }
prepare_log_file
# Logging function # Logging function
log_message() log_message()
{ {
@@ -56,7 +91,7 @@ log_message()
print_help() print_help()
{ {
cat << EOF cat << EOF
Usage: a [command] [file_or_directory] [options] Usage: a [options] [command] [file_or_directory]
Commands: Commands:
e, enc, encrypt Encrypt the specified file or directory e, enc, encrypt Encrypt the specified file or directory
@@ -65,12 +100,14 @@ Commands:
version, --version Show version information version, --version Show version information
Options: Options:
-v, --verbose Print log messages to console in addition to writing to log file -v, --verbose Print log messages to console
--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:
@@ -79,14 +116,21 @@ 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
Specify a custom keys source and log file: Requirements:
AGE_KEYSSOURCE=https://example.com/keys.txt AGE_LOGFILE=/tmp/a.log a d file.txt.age - age (encryption tool): https://github.com/FiloSottile/age
- curl (for fetching keys)
EOF EOF
} }
@@ -115,26 +159,104 @@ 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
for f in "$file"/*; do # Enable dotglob to include hidden files
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"
else
log_message "Warning: '$file' is not a file or directory, skipping."
fi
}
# Function to decrypt a single file
decrypt_single_file()
{
local file="$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 fetch_keys_if_missing
local output_file="${file}.age"
local temp_file local temp_file
temp_file="$(mktemp -p "$(dirname "$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 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
else else
rm -f "$temp_file" rm -f "$temp_file"
log_message "Error: Failed to encrypt file '$file'." log_message "Error: Failed to decrypt file '$file'."
exit 1 return 1
fi
fi fi
} }
@@ -142,54 +264,76 @@ encrypt_file_or_directory()
decrypt_file_or_directory() decrypt_file_or_directory()
{ {
local file="$1" local file="$1"
if [[ -d "$file" ]]; then if [[ -d "$file" ]]; then
for f in "$file"/*.age; do # Enable nullglob to handle no matches gracefully
decrypt_file_or_directory "$f" shopt -s nullglob
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
fetch_keys_if_missing decrypt_single_file "$file"
local output_file="${file%.age}"
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 else
rm -f "$temp_file" log_message "Warning: '$file' is not a file or directory, skipping."
log_message "Error: Failed to decrypt file '$file'."
exit 1
fi
fi fi
} }
# Main logic # Main entry point
case "$1" in main()
{
check_dependencies
# 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) e | enc | encrypt)
if [[ $# -lt 2 ]]; then if [[ -z "$target" ]]; then
log_message "Error: No file or directory specified for encryption." log_message "Error: No file or directory specified for encryption."
print_help print_help
exit 1 exit 1
fi fi
encrypt_file_or_directory "$2" encrypt_file_or_directory "$target"
;; ;;
d | dec | decrypt) d | dec | decrypt)
if [[ $# -lt 2 ]]; then if [[ -z "$target" ]]; then
log_message "Error: No file or directory specified for decryption." log_message "Error: No file or directory specified for decryption."
print_help print_help
exit 1 exit 1
fi fi
decrypt_file_or_directory "$2" decrypt_file_or_directory "$target"
;; ;;
help | --help) help | --help | -h)
print_help print_help
;; ;;
version | --version) version | --version)
print_version print_version
;; ;;
*) "")
log_message "Error: Unknown command '$1'"
print_help print_help
exit 1 exit 1
;; ;;
esac *)
log_message "Error: Unknown command '$command'"
print_help
exit 1
;;
esac
}
main "$@"
# 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,28 +2,76 @@
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 encrypt <file|dir> a [options] <command> <file|directory>
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 - `AGE_KEYSFILE` - location of the keys file (default: `~/.ssh/keys.txt`)
- `AGE_KEYSSOURCE` URL to fetch keys if missing - `AGE_KEYSSOURCE` - URL to fetch keys if missing (default: GitHub keys)
- `AGE_LOGFILE` log file path - `AGE_LOGFILE` - log file path (default: `~/.cache/a.log`)
## Example ## Examples
```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

@@ -1,4 +1,5 @@
#!/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
# #
@@ -15,7 +16,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"

75
scripts/install-apt-packages.sh Executable file
View File

@@ -0,0 +1,75 @@
#!/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

@@ -1,5 +1,9 @@
#!/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,4 +1,5 @@
#!/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
@@ -85,7 +86,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"
echo -e "---\n$metadata---\n$(cat "$cheat_file")" > "$cheat_file" printf '%s\n%b%s\n%s' "---" "$metadata" "---" "$(cat "$cheat_file")" > "$cheat_file"
fi fi
done done
} }

View File

@@ -1,8 +1,9 @@
#!/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 "$HOME/.dotfiles/config/shared.sh" source "$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"
@@ -22,5 +23,7 @@ fi
php composer-setup.php --quiet php composer-setup.php --quiet
RESULT=$? RESULT=$?
rm composer-setup.php rm composer-setup.php
mv composer.phar ~/.local/bin/composer if [ $RESULT -eq 0 ]; then
mv composer.phar ~/.local/bin/composer
fi
exit $RESULT exit $RESULT

83
scripts/install-dnf-packages.sh Executable file
View File

@@ -0,0 +1,83 @@
#!/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,4 +1,5 @@
#!/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"
@@ -21,7 +22,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" cd "$TMP_PATH" || { msgr err "No such folder $TMP_PATH"; exit 1; }
} }
# Function to add fonts to sparse-checkout # Function to add fonts to sparse-checkout

View File

@@ -1,12 +1,10 @@
#!/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,4 +1,5 @@
#!/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
@@ -6,21 +7,17 @@
# 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="/tmp/git-crypt" BUILD_PATH="$(mktemp -d)"
trap 'rm -rf "$BUILD_PATH"' EXIT
rm -rf "$BUILD_PATH" if [ ! -f "$CHECK_PATH" ]; then
git clone --depth 1 "$REPO_URL" "$BUILD_PATH" || { msgr err "Failed to clone $REPO_URL"; exit 1; }
if [ ! -d "$CHECK_PATH" ]; then cd "$BUILD_PATH" || { msgr err "$BUILD_PATH not found"; exit 1; }
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,12 +1,10 @@
#!/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,4 +1,5 @@
#!/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:
@@ -7,7 +8,7 @@
[ "$(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 "$HOME/.dotfiles/config/shared.sh" source "$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:"
@@ -23,12 +24,12 @@ while true; do
done 2> /dev/null & done 2> /dev/null &
# Skip when shell is fish # Skip when shell is fish
if [[ $SHELL != $(which fish) ]]; then if [[ $SHELL != "$(command -v 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 != $(which zsh) ]] && chsh -s "$(which zsh)" [[ $SHELL != "$(command -v zsh)" ]] && chsh -s "$(command -v zsh)"
fi fi
fi fi

View File

@@ -1,12 +1,10 @@
#!/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,12 +1,10 @@
#!/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"
@@ -23,28 +21,31 @@ 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_DEST="/tmp/ntfy_${NTFY_VERSION}_${NTFY_ARCH}" NTFY_TARBALL="ntfy_${NTFY_VERSION}_${NTFY_ARCH}.tar.gz"
NTFY_DIR="ntfy_${NTFY_VERSION}_${NTFY_ARCH}"
# Download and extract ntfy # Download and extract ntfy
install_ntfy() install_ntfy()
{ {
curl -L "$NTFY_URL/releases/download/v${NTFY_VERSION}/${NTFY_DEST}.tar.gz" -o "${NTFY_DEST}.tar.gz" local tmpdir
tar zxvf "${NTFY_DEST}.tar.gz" tmpdir="$(mktemp -d)"
cp -a "${NTFY_DEST}/ntfy" ~/.local/bin/ntfy trap 'rm -rf "$tmpdir"' EXIT
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 "${NTFY_DEST}/client/client.yml" ~/.config/ntfy/client.yml cp "$tmpdir/${NTFY_DIR}/client/client.yml" ~/.config/ntfy/client.yml
fi fi
# Clean up
rm -rf "${NTFY_DEST}" "${NTFY_DEST}.tar.gz"
} }
main() main()

View File

@@ -1,12 +1,10 @@
#!/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

View File

@@ -1,11 +1,9 @@
#!/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"
@@ -31,7 +29,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,4 +1,5 @@
#!/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,17 +4,8 @@
# Helper env variables. Use like this: VERBOSE=1 ./script.sh # Helper env variables. Use like this: VERBOSE=1 ./script.sh
: "${VERBOSE:=0}" : "${VERBOSE:=0}"
# Set variable that checks if the shared.sh script has been # Source the main shared config if not already loaded
# sourced only once If the script has been sourced more than once, if [ -z "${SHARED_SCRIPTS_SOURCED:-}" ]; then
# 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