feat: add bats tests, docs (#139)

* fix(test): ensure bats file list uses xargs

* docs(readme): use yarn for testing instructions

* fix(test): ensure pipelines fail properly

* docs(alias): fix table header

---------

Signed-off-by: Ismo Vuorinen <ismo@ivuorinen.net>
This commit is contained in:
2025-06-30 04:30:06 +03:00
committed by GitHub
parent 1531647e01
commit cf7ca2109f
82 changed files with 1368 additions and 16 deletions

5
.gitattributes vendored
View File

@@ -85,6 +85,7 @@ LICENSE text
NEWS text NEWS text
readme text readme text
*README* text *README* text
# Files literally named "TODO", not a todo list item
TODO text TODO text
# Templates # Templates
@@ -122,7 +123,8 @@ package.json text eol=lf
package-lock.json text eol=lf -diff package-lock.json text eol=lf -diff
pnpm-lock.yaml text eol=lf -diff pnpm-lock.yaml text eol=lf -diff
.prettierrc text .prettierrc text
yarn.lock text -diff # Ensure yarn.lock shows textual diffs
yarn.lock text eol=lf
*.toml text *.toml text
*.yaml text *.yaml text
*.yml text *.yml text
@@ -251,3 +253,4 @@ install text eol=lf diff=shell
*.snippets text eol=lf *.snippets text eol=lf
*.theme text eol=lf *.theme text eol=lf
*.yamlfmt text eol=lf *.yamlfmt text eol=lf
*.bats text eol=lf diff=shell

30
.github/AGENTS.md vendored Normal file
View File

@@ -0,0 +1,30 @@
# Guidelines for AI contributors
These instructions help language models work with this repository.
## Setup
1. Run `npm install` to get linting tools and the Bats test framework.
## Formatting
- Format code and docs with Prettier and markdownlint:
```bash
npm run fix:prettier
npm run fix:markdown
```
- Shell scripts should pass `shellcheck`.
## Testing
- When code changes, run `npm test` to execute Bats tests.
- If only comments or documentation change, tests may be skipped.
## Commits and PRs
- Use Semantic Commit messages: `type(scope): summary`.
- Keep PR titles in the same format.
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -60,7 +60,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at reported to the community leaders responsible for enforcement at
ismo@ivuorinen.net. <ismo@ivuorinen.net>.
All complaints will be reviewed and investigated promptly and fairly. All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the All community leaders are obligated to respect the privacy and security of the
@@ -116,7 +116,7 @@ the community.
This Code of Conduct is adapted from the [Contributor Covenant][homepage], This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. <https://www.contributor-covenant.org/version/2/0/code_of_conduct.html>.
Community Impact Guidelines were inspired by [Mozilla's code of conduct Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity). enforcement ladder](https://github.com/mozilla/diversity).
@@ -124,5 +124,5 @@ enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org [homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at <https://www.contributor-covenant.org/faq>. Translations are available at
https://www.contributor-covenant.org/translations. <https://www.contributor-covenant.org/translations>.

46
.github/README.md vendored
View File

@@ -43,6 +43,12 @@ see what interesting stuff you've done with it. Sharing is caring.
| `local/bin` | Helper scripts that I've collected or wrote. | | `local/bin` | Helper scripts that I've collected or wrote. |
| `scripts` | Setup scripts. | | `scripts` | Setup scripts. |
### Host specific configuration
Configurations under `hosts/<hostname>` are applied only when running on the
matching machine. Each host folder contains its own `install.conf.yaml` that
is processed by Dotbot during installation.
### dotfile folders ### dotfile folders
| Repo | Destination | Description | | Repo | Destination | Description |
@@ -58,6 +64,21 @@ see what interesting stuff you've done with it. Sharing is caring.
Running `dfm` gives you a list of available commands. Running `dfm` gives you a list of available commands.
#### Documentation generation
`dfm docs` generates Markdown documentation under the `docs/` directory. The
subcommands are:
```bash
dfm docs alias # regenerate alias table
dfm docs folders # document interesting folders
dfm docs keybindings # update keybinding docs for tmux, nvim and others
dfm docs all # run every docs task
```
The `docs/` folder contains generated cheat sheets, keybindings and other
reference files. New documentation can be added without modifying this README.
## Configuration ## Configuration
The folder structure follows [XDG Base Directory Specification][xdg] where possible. The folder structure follows [XDG Base Directory Specification][xdg] where possible.
@@ -73,6 +94,31 @@ The folder structure follows [XDG Base Directory Specification][xdg] where possi
Please see [docs/folders.md][docs-folders] for more information. Please see [docs/folders.md][docs-folders] for more information.
## Managing submodules
This repository uses Git submodules for external dependencies. After cloning,
run:
```bash
git submodule update --init --recursive
```
To pull submodule updates later use:
```bash
git submodule update --remote --merge
```
The helper script `add-submodules.sh` documents how each submodule is added and
configured. Submodules are automatically updated by the
[update-submodules.yml](.github/workflows/update-submodules.yml) workflow.
## Testing
Shell scripts under `local/bin` are validated with [Bats](https://github.com/bats-core/bats-core).
Run `yarn test` to execute every test file. Bats is installed as a development
dependency, so run `yarn install` first if needed.
[dfm]: https://github.com/ivuorinen/dotfiles/blob/main/local/bin/dfm [dfm]: https://github.com/ivuorinen/dotfiles/blob/main/local/bin/dfm
[docs-folders]: https://github.com/ivuorinen/dotfiles/blob/main/docs/folders.md [docs-folders]: https://github.com/ivuorinen/dotfiles/blob/main/docs/folders.md
[xdg]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html [xdg]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html

View File

@@ -1 +1,7 @@
[{"account_id":"S5Z2DMNFKJEZBPCWRHRWC4DCGI","vault_id":"injcin7obv3jdet3r2u3kfihfy","item_id":"f6vinbnc6l7ngdzvlw66ayewlq"}] [
{
"account_id": "S5Z2DMNFKJEZBPCWRHRWC4DCGI",
"vault_id": "injcin7obv3jdet3r2u3kfihfy",
"item_id": "f6vinbnc6l7ngdzvlw66ayewlq"
}
]

11
hosts/README.md Normal file
View File

@@ -0,0 +1,11 @@
# Host specific directories
Host folders contain machine specific overrides and an `install.conf.yaml` file that Dotbot processes during setup.
Current hosts:
- **air** personal computer
- **lakka** remote server
- **s** work laptop
- **tunkki** local server
- **v** work desktop

29
local/bin/a.md Normal file
View File

@@ -0,0 +1,29 @@
# a
Encrypt or decrypt files and directories using `age` and your GitHub SSH keys.
## Usage
```bash
a encrypt <file|dir>
a decrypt <file.age|dir>
```
Options:
- `-v`, `--verbose` show log output
Environment variables:
- `AGE_KEYSFILE` location of the keys file
- `AGE_KEYSSOURCE` URL to fetch keys if missing
- `AGE_LOGFILE` log file path
## Example
```bash
a encrypt secret.txt
a decrypt secret.txt.age
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

19
local/bin/ad.md Normal file
View File

@@ -0,0 +1,19 @@
# ad
Decrypt a file encrypted with `age` using your GitHub SSH keys.
## Usage
```bash
ad <file.age>
```
Uses `AGE_KEYSFILE` and `AGE_KEYSSOURCE` if keys are missing.
## Example
```bash
ad secret.txt.age
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

19
local/bin/ae.md Normal file
View File

@@ -0,0 +1,19 @@
# ae
Encrypt a file with `age` using your GitHub SSH keys.
## Usage
```bash
ae <file>
```
Uses `AGE_KEYSFILE` and `AGE_KEYSSOURCE` if keys are missing.
## Example
```bash
ae secret.txt
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

26
local/bin/dfm.md Normal file
View File

@@ -0,0 +1,26 @@
# dfm
Dotfiles manager and installation helper. Provides wrappers for many
setup tasks defined in this repository.
## Usage
```bash
dfm <command> [options]
```
Common commands include:
- `install` install tools or run platform specific setup
- `brew` manage Homebrew packages
- `docs` regenerate markdown documentation
Set `VERBOSE=1` to see debug output.
### Example
```bash
dfm install all
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

21
local/bin/fzf-tmux.md Normal file
View File

@@ -0,0 +1,21 @@
# fzf-tmux
Wrapper around [`fzf`](https://github.com/junegunn/fzf) that opens the
interface inside a tmux pane or popup.
## Usage
```bash
fzf-tmux [layout options] [--] [fzf options]
```
Layout flags like `-p` or `-d` control popup and split behaviour. Use
`--` to pass arguments directly to `fzf`.
### Example
```bash
fzf-tmux -p 80%,60% -- --reverse
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

16
local/bin/fzf.md Normal file
View File

@@ -0,0 +1,16 @@
# fzf
Binary of the fuzzy finder [fzf](https://github.com/junegunn/fzf).
Use `fzf` as you would normally; this wrapper ships the prebuilt
binary in the dotfiles.
## Usage
```bash
fzf [options]
```
Refer to the upstream `fzf` documentation for all available
flags and features.
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,25 @@
# git-attributes
Checks that every tracked file has a matching pattern in `.gitattributes`.
Can optionally suggest or write missing rules.
## Usage
```bash
git-attributes [options]
```
Options include:
- `-v, --verbose` show progress information
- `-e, --exit` exit with non-zero status if missing rules
- `-p, --pattern <glob>` pattern to check (default: `text: auto`)
- `-w, --write` append suggestions to `.gitattributes`
### Example
```bash
git-attributes -v --write
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,15 @@
# iterm2_shell_integration.zsh
Official iTerm2 shell integration script for zsh. Source this file to
enable prompt tracking and command notifications in iTerm2.
## Usage
```bash
source iterm2_shell_integration.zsh
```
No parameters are required. The script modifies your prompt to work
with iTerm2 features such as badges and profile switching.
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

20
local/bin/msgr.md Normal file
View File

@@ -0,0 +1,20 @@
# msgr
Helper library for printing colorized log messages from shell scripts.
## Usage
```bash
msgr <type> "message" [extra]
```
Message types include `ok`, `warn`, `err`, `run` and many more. The
script is primarily sourced by other scripts.
### Example
```bash
msgr ok "Installation complete"
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

23
local/bin/php-switcher.md Normal file
View File

@@ -0,0 +1,23 @@
# php-switcher
Switch between Homebrew-installed PHP versions or list installed versions.
## Usage
```bash
php-switcher <version>|--auto [options]
```
Options:
- `--installed` list versions installed via Homebrew
- `--current` print currently active PHP version
- `--auto` read version from `.php-version` in current directory
### Example
```bash
php-switcher 8.3
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -56,22 +56,31 @@ __pushover_send_message()
response="$(eval "${curl_cmd}")" response="$(eval "${curl_cmd}")"
printf "%s\n" "$response" printf "%s\n" "$response"
# TODO: Parse response
r="${?}" # Parse response status. Expect JSON like: {"status":1,"request":"..."}
if [ "${r}" -ne 0 ]; then if echo "$response" | grep -q '"status"[[:space:]]*:[[:space:]]*1'; then
printf "%s: Failed to send message\n" "${0}" >&2 r=0
else
r=1
fi fi
return "${r}" if [ "$r" -ne 0 ]; then
# Extract possible error message from JSON
err=$(echo "$response" | grep -o '"errors".*' | sed 's/"errors"[:,\[]//g' | tr -d '[]"')
[ -n "$err" ] && printf "%s: %s\n" "$0" "$err" >&2
printf "%s: Failed to send message\n" "$0" >&2
fi
return "$r"
} }
CURL="$(which curl)" CURL="$(command -v curl)"
PUSHOVER_URL="https://api.pushover.net/1/messages.json" PUSHOVER_URL="https://api.pushover.net/1/messages.json"
TOKEN=$PUSHOVER_TOKEN TOKEN=$PUSHOVER_TOKEN
USER=$PUSHOVER_USER USER=$PUSHOVER_USER
CURL_OPTS="" CURL_OPTS=""
devices="${devices} ${device}" devices=""
optstring="c:d:D:e:f:p:r:t:T:s:u:U:a:h" optstring="c:d:D:e:p:r:t:T:s:u:U:a:h"
OPTIND=1 OPTIND=1
while getopts ${optstring} c; do while getopts ${optstring} c; do
@@ -97,7 +106,7 @@ while getopts ${optstring} c; do
t) t)
title="${OPTARG}" title="${OPTARG}"
;; ;;
k) T)
TOKEN="${OPTARG}" TOKEN="${OPTARG}"
;; ;;
s) s)

25
local/bin/pushover.md Normal file
View File

@@ -0,0 +1,25 @@
# pushover
Send notifications via the Pushover API.
## Usage
```bash
pushover -T <token> -U <user> [-t title] [-p priority] message
```
Common options:
- `-c <callback>` callback URL
- `-d <device>` target device
- `-s <sound>` notification sound name
- `-T <token>` application token (or `PUSHOVER_TOKEN` env)
- `-U <user>` user key (or `PUSHOVER_USER` env)
## Example
```bash
pushover -T $TOKEN -U $USER -t "Build" "Finished successfully"
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

23
local/bin/t.md Normal file
View File

@@ -0,0 +1,23 @@
# t
Launch or switch to a tmux session based on a directory selected with
`fzf`. Inspired by scripts from ThePrimeagen and Jess Archer.
## Usage
```bash
t
```
Environment variables:
- `T_ROOT` base directory to search (default: `~/Code`)
- `T_MAX_DEPTH` recursion depth for directory search
### Example
```bash
T_ROOT=~/projects t
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,20 @@
# x-backup-folder
Create a compressed archive of a folder with a timestamped name.
## Usage
```bash
x-backup-folder <folder> [archive-name]
```
- `folder` directory to back up
- `archive-name` optional prefix for the generated tar.gz
## Example
```bash
x-backup-folder ~/Documents Notes
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,21 @@
# x-backup-mysql-with-prefix
Dump MySQL tables matching a prefix to a timestamped file.
## Usage
```bash
x-backup-mysql-with-prefix <prefix> <name> [database]
```
- `prefix` table prefix to match (e.g. `wp_`)
- `name` file name prefix
- `database` database name (default: `wordpress`)
## Example
```bash
x-backup-mysql-with-prefix wp_ blog
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,20 @@
# x-change-alacritty-theme
Adapted from <https://gist.github.com/xqm32/17777d035930d622d0ff7530bfab61fd>
## Usage
```bash
x-change-alacritty-theme <day|night>
```
Switches Alacritty's theme by copying a theme file under
`~/.config/alacritty/`.
### Example
```bash
x-change-alacritty-theme night
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,19 @@
# x-clean-vendordirs
Remove `vendor` and `node_modules` directories recursively.
## Usage
```bash
x-clean-vendordirs [directory]
```
- `directory` root directory to clean (default: current directory)
## Example
```bash
x-clean-vendordirs ~/projects
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,14 @@
# x-compare-versions.py
Compare version strings using Python's packaging library.
## Usage
```bash
echo "1.2.3 >= 1.0.0" | x-compare-versions.py
```
The script reads comparison expressions from standard input and exits
with status 0 if all comparisons are true.
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

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

@@ -0,0 +1,19 @@
# x-dc
Create a directory if it does not exist.
## Usage
```bash
x-dc <directory>
```
Set `VERBOSE=1` to see log messages.
## Example
```bash
x-dc ~/tmp/mydir
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,14 @@
# x-dfm-docs-xterm-keybindings
Generate `docs/tmux-keybindings.md` using tmux's key list.
## Usage
```bash
x-dfm-docs-xterm-keybindings
```
No parameters are needed. The script writes the file under `docs/` and
overwrites any existing version.
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

21
local/bin/x-env-list.md Normal file
View File

@@ -0,0 +1,21 @@
# x-env-list
Lists environment variables grouped by their prefix. Sensitive values
are hidden by default.
## Usage
```bash
x-env-list [options]
```
Use `--json` for machine readable output or specify
`X_ENV_GROUPING` with a YAML file to override the default groups.
### Example
```bash
X_ENV_GROUPING=~/env-groups.yaml x-env-list --json
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

20
local/bin/x-foreach.md Normal file
View File

@@ -0,0 +1,20 @@
# x-foreach
Run a command in each directory produced by another command.
## Usage
```bash
x-foreach "<list-cmd>" "<cmd>"
```
- `list-cmd` command that outputs directories
- `cmd` command to run inside each directory
## Example
```bash
x-foreach "ls -d */" "git status"
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,19 @@
# x-gh-get-latest-release-targz
Fetch the tarball URL of the latest GitHub release or download it directly.
## Usage
```bash
x-gh-get-latest-release-targz <owner/repo> [--get]
```
- `--get` download and extract the tarball instead of printing the URL
## Example
```bash
x-gh-get-latest-release-targz ivuorinen/dotfiles --get
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,21 @@
# x-git-largest-files.py
Lists the largest files in a git repository.
```bash
x-git-largest-files.py [options]
```
Options:
- `-c NUM` number of files to show (default: 10)
- `--files-exceeding N` list files larger than N KB
- `-p` sort by on-disk size instead of pack size
## Example
```bash
x-git-largest-files.py -c 5
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

18
local/bin/x-have.md Normal file
View File

@@ -0,0 +1,18 @@
# x-have
Check if a command exists on the system. Exits with status 0 if found
and 1 otherwise.
## Usage
```bash
x-have <command>
```
### Example
```bash
x-have git && echo "git installed"
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

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

@@ -0,0 +1,19 @@
# x-hr
Print a horizontal rule. Useful for visually separating log output.
## Usage
```bash
x-hr [character]
```
If no character is given a red `-` is used.
### Example
```bash
x-hr "="
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

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

@@ -0,0 +1,19 @@
# x-ip
Fetch your public IP address using `curl`.
## Usage
```bash
x-ip [curl-options]
```
Any arguments are passed directly to `curl`.
### Example
```bash
x-ip -4
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,21 @@
# x-load-configs
Source shell configuration files for aliases and exports. Intended to
be run after `dfm install` or when switching hosts.
## Usage
```bash
x-load-configs
```
Set `VERBOSE=1` to print each file as it is sourced. Use `DEBUG=1` to
enable tracing.
### Example
```bash
VERBOSE=1 x-load-configs
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

25
local/bin/x-localip.md Normal file
View File

@@ -0,0 +1,25 @@
# x-localip
Display local IPv4 and IPv6 addresses with optional interface filtering.
## Usage
```bash
x-localip [--ipv4] [--ipv6] [interface]
```
- `--ipv4` show only IPv4 addresses
- `--ipv6` show only IPv6 addresses
- `interface` limit output to the named interface
## Example
```bash
# Show all addresses
x-localip
# IPv4 for wlan0
x-localip --ipv4 wlan0
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

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 : -->

20
local/bin/x-multi-ping.md Normal file
View File

@@ -0,0 +1,20 @@
# x-multi-ping
Multi-protocol ping helper supporting IPv4 and IPv6.
## Usage
```bash
x-multi-ping [--loop] [--sleep=N] host1 host2...
```
- `--loop` ping continuously
- `--sleep` seconds to wait between iterations
## Example
```bash
x-multi-ping --loop --sleep=5 example.com
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,20 @@
# x-multi-ping.pl
Ping multiple hosts with IPv4/IPv6 support.
## Usage
```bash
x-multi-ping.pl [--loop|--forever] [--sleep N] host1 host2 ...
```
`--loop` keeps pinging each host until interrupted. `--sleep` controls
the delay between attempts.
### Example
```bash
x-multi-ping.pl --loop --sleep 2 example.com 1.1.1.1
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

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

@@ -0,0 +1,19 @@
# x-open-ports
List listening ports using `lsof` or `ss`.
## Usage
```bash
x-open-ports [--json]
```
- `--json` output as JSON instead of Markdown
## Example
```bash
x-open-ports --json
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,19 @@
# x-path-append
Append directories to the `PATH` variable without duplicates.
## Usage
```bash
x-path-append <dir1> [dir2 ...]
```
Set `VERBOSE=1` for verbose logging.
## Example
```bash
x-path-append /usr/local/sbin "$HOME/bin"
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,19 @@
# x-path-prepend
Prepend directories to the `PATH` variable without duplicates.
## Usage
```bash
x-path-prepend <dir1> [dir2 ...]
```
Set `VERBOSE=1` for verbose logging.
## Example
```bash
x-path-prepend "$HOME/bin" /opt/tools
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,19 @@
# x-path-remove
Remove directories from the `PATH` variable.
## Usage
```bash
x-path-remove <dir1> [dir2 ...]
```
Set `VERBOSE=1` for verbose logging.
## Example
```bash
x-path-remove /usr/local/bin
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

30
local/bin/x-path.md Normal file
View File

@@ -0,0 +1,30 @@
# x-path
Manage entries in the `PATH` variable through subcommands.
## Usage
```bash
x-path <command> <directory1> [directory2 ...]
```
### Commands
- `append` / `a` Append directories to `PATH`
- `prepend` / `p` Prepend directories to `PATH`
- `remove` Remove directories from `PATH`
- `check` Validate directories (default: all in `PATH`)
Set `VERBOSE=1` for progress output.
## Examples
```bash
# Prepend /opt/bin to PATH
x-path prepend /opt/bin
# Remove /usr/local/bin from PATH
x-path remove /usr/local/bin
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,14 @@
# x-quota-usage.php
Display filesystem quota usage in a human readable table.
## Usage
```bash
x-quota-usage.php
```
Runs the `quota` command and formats the output. Requires PHP with the
`shell_exec` function enabled.
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

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

@@ -0,0 +1,19 @@
# x-record
Interactive screen recording wrapper around `giph`.
## Usage
```bash
x-record <gif|mkv|webm|mp4> <fullscreen|set>
```
The script asks for file type and area when omitted.
## Example
```bash
x-record gif fullscreen
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,14 @@
# x-set-php-aliases
---
## Usage
```bash
source x-set-php-aliases
```
Generates shell aliases (`php80`, `php81` ...) for each Homebrew PHP
installation. Caches the list under `$XDG_CACHE_HOME/x-set-php-aliases`.
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,22 @@
# x-sha256sum-matcher
Compare two files by calculating their SHA256 checksums.
## Usage
```bash
x-sha256sum-matcher [options] file1 file2
```
Options:
- `-v` verbose output
- `-h, --help` show help
### Example
```bash
x-sha256sum-matcher original.iso download.iso
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,22 @@
# x-ssl-expiry-date
Check the expiry date of an SSL certificate for one or more hosts.
## Usage
```bash
x-ssl-expiry-date [-d] [-p PORT] host1 host2 ...
```
Options:
- `-d` show days left instead of the full date
- `-p <port>` use custom port (default: 443)
### Example
```bash
x-ssl-expiry-date -d github.com
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,14 @@
# x-term-colors
Display a table of 24bit color codes for testing terminal color
support.
## Usage
```bash
x-term-colors
```
Pipe the output to `less -R` to view with color highlighting.
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

26
local/bin/x-thumbgen.md Normal file
View File

@@ -0,0 +1,26 @@
# x-thumbgen
Generate thumbnails using ImageMagick (magick) with MIME type filtering.
## Usage
```bash
x-thumbgen [options] source_directory
```
Options:
- `-o DIR` output directory (default: same as source)
- `-s STR` suffix for thumbnails
- `-h` show help
Environment variables like `THUMB_BACKGROUND` control the background
color.
### Example
```bash
THUMB_BACKGROUND=black x-thumbgen -o ~/thumbs ~/images
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,20 @@
# x-until-error
Repeatedly execute a command until it returns a non-zero exit status.
## Usage
```bash
x-until-error [--sleep SECONDS] command [args...]
```
Use `--sleep` to wait between runs. The command is executed at least
once.
### Example
```bash
x-until-error --sleep 2 ping -c1 example.com
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -0,0 +1,20 @@
# x-until-success
Repeat a command until it succeeds (exit status 0). The command is
always executed at least once.
## Usage
```bash
x-until-success [--sleep SECONDS] command [args...]
```
Use `--sleep` to control the delay between attempts.
### Example
```bash
x-until-success --sleep 5 curl -I https://example.com
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

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

@@ -0,0 +1,13 @@
# x-welcome-banner
Print a colorful MOTD with greeting, system info and today's weather.
## Usage
```bash
x-welcome-banner
```
Requires optional tools: `neofetch`, `figlet`, `lolcat` and `curl` for extra info.
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

17
local/bin/x-when-down.md Normal file
View File

@@ -0,0 +1,17 @@
# x-when-down
Wait until a host stops responding to ping, then run a command.
## Usage
```bash
x-when-down <host> <command...>
```
## Example
```bash
x-when-down 1.2.3.4 echo "server down"
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

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

@@ -0,0 +1,19 @@
# x-when-up
Wait for a host to respond to ping before running a command.
## Usage
```bash
x-when-up <host> <command...>
```
If the command is `ssh`, the host argument may be omitted.
## Example
```bash
x-when-up 1.2.3.4 ssh 1.2.3.4
```
<!-- vim: set ft=markdown spell spelllang=en_us cc=80 : -->

View File

@@ -10,7 +10,7 @@
"fix:markdown": "npx markdownlint -df .", "fix:markdown": "npx markdownlint -df .",
"lint:prettier": "npx prettier . --check", "lint:prettier": "npx prettier . --check",
"fix:prettier": "npx prettier . --write", "fix:prettier": "npx prettier . --write",
"test": "echo \"Error: no test specified\" && exit 0" "test": "bash test-all.sh"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -29,6 +29,7 @@
"devDependencies": { "devDependencies": {
"@ivuorinen/base-configs": "^2.0.0", "@ivuorinen/base-configs": "^2.0.0",
"@types/node": "^24.0.1", "@types/node": "^24.0.1",
"bats": "^1.12.0",
"typescript": "^5.8.3" "typescript": "^5.8.3"
}, },
"packageManager": "yarn@1.22.22" "packageManager": "yarn@1.22.22"

View File

@@ -0,0 +1,11 @@
# create-aerospace-keymaps
Generates `docs/aerospace-keybindings.md` using `aerospace config --json`.
## Usage
```bash
scripts/create-aerospace-keymaps.php
```
Requires the `aerospace` CLI tool to be installed.

View File

@@ -0,0 +1,11 @@
# create-nvim-keymaps
Outputs current Neovim key mappings to `docs/nvim-keybindings.md`.
## Usage
```bash
scripts/create-nvim-keymaps.sh
```
Requires Neovim to be installed.

View File

@@ -0,0 +1,11 @@
# create-wezterm-keymaps
Generates `docs/wezterm-keybindings.md` by invoking `wezterm show-keys`.
## Usage
```bash
scripts/create-wezterm-keymaps.sh
```
Requires wezterm to be installed.

View File

@@ -0,0 +1,12 @@
# install-cargo-packages
Install Rust packages listed in `config/asdf/cargo-packages`.
## Usage
```bash
scripts/install-cargo-packages.sh
```
The script installs each package with `cargo install` and runs
`cargo-install-update` when available to update existing packages.

View File

@@ -0,0 +1,12 @@
# install-cheat-purebashbible
Fetches the Pure Bash Bible repository and installs its cheatsheets for the
`cheat` utility.
## Usage
```bash
scripts/install-cheat-purebashbible.sh
```
Requires `git` and `cheat` to be available in PATH.

View File

@@ -0,0 +1,11 @@
# install-composer
Installs the PHP package manager [Composer](https://getcomposer.org/).
## Usage
```bash
scripts/install-composer.sh
```
The script downloads the latest Composer PHAR and places it in `$HOME/.local/bin`.

11
scripts/install-fonts.md Normal file
View File

@@ -0,0 +1,11 @@
# install-fonts
Installs Nerd Fonts used by various terminal and editor setups.
## Usage
```bash
scripts/install-fonts.sh
```
Fonts are downloaded to `$HOME/.local/share/fonts` and refreshed automatically.

View File

@@ -0,0 +1,12 @@
# install-gh-extensions
Installs a curated set of GitHub CLI extensions defined in
`config/gh/extensions`.
## Usage
```bash
scripts/install-gh-extensions.sh
```
The script installs each extension using the `gh extension install` command.

View File

@@ -0,0 +1,12 @@
# install-git-crypt
Installs `git-crypt` for transparent encryption of files in Git repositories.
## Usage
```bash
scripts/install-git-crypt.sh
```
After installation you can run `git-crypt init` inside a repository to begin
encrypting files.

View File

@@ -0,0 +1,12 @@
# install-go-packages
Installs Go binaries defined in `config/go/packages`.
## Usage
```bash
scripts/install-go-packages.sh
```
The script uses `go install` for each package path listed in the configuration
file.

View File

@@ -0,0 +1,11 @@
# install-macos-defaults
Applies a set of macOS defaults for a consistent developer environment.
## Usage
```bash
scripts/install-macos-defaults.sh
```
Requires macOS and the `defaults` command.

View File

@@ -0,0 +1,11 @@
# install-npm-packages
Installs global npm packages listed in `config/npm/packages`.
## Usage
```bash
scripts/install-npm-packages.sh
```
Uses `npm install -g` for each package in the configuration file.

11
scripts/install-ntfy.md Normal file
View File

@@ -0,0 +1,11 @@
# install-ntfy
Installs the lightweight notification tool [`ntfy`](https://ntfy.sh/).
## Usage
```bash
scripts/install-ntfy.sh
```
After running you can send notifications using `ntfy publish`.

View File

@@ -0,0 +1,11 @@
# install-pip-packages
Installs Python packages from `config/pip/packages` using `pip`.
## Usage
```bash
scripts/install-pip-packages.sh
```
The script uses `pip install --user` for each package entry.

View File

@@ -0,0 +1,11 @@
# install-xcode-cli-tools
Installs the Xcode Command Line Tools on macOS using `osascript` prompts.
## Usage
```bash
scripts/install-xcode-cli-tools.sh
```
Requires macOS with administrator privileges.

11
scripts/install-z.md Normal file
View File

@@ -0,0 +1,11 @@
# install-z
Installs [z](https://github.com/rupa/z), a directory jumping tool.
## Usage
```bash
scripts/install-z.sh
```
Once installed add `. ~/.z` to your shell startup file to enable the command.

13
scripts/shared.md Normal file
View File

@@ -0,0 +1,13 @@
# shared
Collection of helper functions shared across install scripts.
## Usage
Source the file in your script:
```bash
source "${DOTFILES}/scripts/shared.sh"
```
Provides messaging helpers and common environment checks.

View File

@@ -10,6 +10,7 @@
[ -z "$SHARED_SCRIPTS_SOURCED" ] && { [ -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" msgr warn "(!) shared.sh not sourced"
# Set variable that checks if the shared.sh script has been # Set variable that checks if the shared.sh script has been

View File

@@ -0,0 +1,11 @@
# update-readme-aliases
Regenerates the alias table located at `docs/alias.md`.
## Usage
```bash
scripts/update-readme-aliases.sh
```
This script parses `config/alias` and writes the markdown table to the docs.

15
test-all.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
# Run all bats tests
set -euo pipefail
if [ -x "node_modules/bats/bin/bats" ]; then
git ls-files '*.bats' -z | xargs -0 node_modules/bats/bin/bats
elif command -v npx >/dev/null; then
git ls-files '*.bats' -z | xargs -0 npx --yes bats
elif command -v bats >/dev/null; then
git ls-files '*.bats' -z | xargs -0 bats
else
echo "bats not installed. Run 'yarn install' first." >&2
exit 1
fi

11
tests/dfm.bats Normal file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bats
setup() {
export DOTFILES="$PWD"
}
@test "dfm help shows usage" {
run bash local/bin/dfm help
[ "$status" -eq 0 ]
[[ "$output" == *"Usage: dfm"* ]]
}

View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bats
@test "x-gh-get-latest-version help" {
run bash local/bin/x-gh-get-latest-version --help
[ "$status" -eq 1 ]
[[ "$output" == "Usage: x-gh-get-latest-version"* ]]
}

7
tests/x-localip.bats Normal file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bats
@test "x-localip prints version" {
run bash local/bin/x-localip --version
[ "$status" -eq 0 ]
[[ "$output" == "x-localip version"* ]]
}

8
tests/x-mkd.bats Normal file
View File

@@ -0,0 +1,8 @@
#!/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" ]
}

22
tests/x-path.bats Normal file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env bats
@test "x-path-append adds directory" {
mkdir -p "$BATS_TMPDIR/dir"
PATH="/usr/bin"
VERBOSE=1 source local/bin/x-path-append "$BATS_TMPDIR/dir"
[ "$PATH" = "/usr/bin:$BATS_TMPDIR/dir" ]
}
@test "x-path-prepend adds directory to start" {
mkdir -p "$BATS_TMPDIR/dir"
PATH="/usr/bin:/bin"
VERBOSE=1 source local/bin/x-path-prepend "$BATS_TMPDIR/dir"
[ "$PATH" = "$BATS_TMPDIR/dir:/usr/bin:/bin" ]
}
@test "x-path-remove removes directory" {
mkdir -p "$BATS_TMPDIR/dir"
PATH="$BATS_TMPDIR/dir:/usr/bin"
VERBOSE=1 source local/bin/x-path-remove "$BATS_TMPDIR/dir"
[ "$PATH" = "/usr/bin" ]
}