Compare commits

...

69 Commits

Author SHA1 Message Date
renovate[bot]
daa1253d9c chore(actions): update softprops/action-gh-release action (v2.5.0 → v2.6.0)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-15 21:07:34 +00:00
8a5c9f9089 chore: general maintenance and cleanup (#306) 2026-03-15 19:14:36 +02:00
renovate[bot]
2410f343c2 chore(deps): update pre-commit hook astral-sh/ruff-pre-commit (v0.15.5 → v0.15.6) (#305) 2026-03-13 20:06:51 +02:00
renovate[bot]
d120839830 chore(actions): update ivuorinen/actions action (v2026.03.02 → v2026.03.11) (#304)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-13 08:40:10 +02:00
renovate[bot]
5662d09202 chore(deps): update pre-commit hook johnnymorganz/stylua (v2.3.1 → v2.4.0) (#303)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-08 19:31:45 +02:00
renovate[bot]
076050ad58 chore(deps): update pre-commit hook astral-sh/ruff-pre-commit (v0.15.4 → v0.15.5) (#302) 2026-03-07 03:46:31 +02:00
renovate[bot]
313915e55c chore(deps): update loopwerk/tag-changelog action (v1.3.0 → v1.5.0) (#300) 2026-03-05 22:07:58 +02:00
renovate[bot]
e490a735c8 chore(deps): lock file maintenance (#301) 2026-03-05 22:06:11 +02:00
renovate[bot]
5fe82b2898 chore(deps): lock file maintenance (#299) 2026-03-03 08:19:51 +02:00
renovate[bot]
6621bcb470 chore(deps): update ivuorinen/actions action (v2026.02.24 → v2026.03.02) (#298)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 05:54:49 +00:00
3b6ace12e9 feat(scripts): add shellspec installation to dfm (#296)
* feat(scripts): add shellspec installation to dfm

Add install-shellspec.sh script that clones shellspec to
~/.cache/shellspec and installs via make to ~/.local/bin.
Wire it into dfm install menu and the Tier 4 install-all pipeline.

* fix(scripts): address PR review feedback for shellspec installer

Add companion install-shellspec.md documentation file to match
codebase convention. Add --depth=1 to git pull for consistent
shallow clone behavior.

* fix(scripts): pin shellspec install to latest release tag

Use x-gh-get-latest-version to fetch the latest release tag from
GitHub and clone/checkout that specific version. Addresses supply
chain concern and normalizes --depth=1 style.

* docs(scripts): update shellspec docs to reflect release-tag pinning
2026-03-02 16:39:17 +02:00
renovate[bot]
f92e4a606f chore(deps): lock file maintenance (#295)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-02 00:58:41 +02:00
fae7a8f13f feat(bin): add x-visit-folders script for bulk zoxide registration (#294)
* feat(bin): add x-visit-folders script for bulk zoxide registration

* fix(bin): fix x-visit-folders usage exit code, no-op fallback, and count increment

- usage() now accepts optional exit code; --help exits 0
- Remove no-op subshell cd fallback in visit_dir(), just log failure
- Replace ((count++)) with count=$((count + 1)) to avoid set -e trap
2026-03-01 22:02:49 +02:00
eaa7680671 chore(config): sesh config additions 2026-03-01 21:37:34 +02:00
070c94a244 docs: update CLAUDE.md with biome migrate command and gotchas 2026-02-28 10:50:44 +02:00
70cf8ccf6d style(fish): reformat phpenv files with fish_indent 2026-02-28 10:50:43 +02:00
63faf4c4bf feat(claude): add fish-validate and lua-format skills
Add fish-validate skill for syntax checking and formatting .fish files,
lua-format skill for stylua formatting, and fish_indent PostToolUse hook.
2026-02-28 10:50:43 +02:00
renovate[bot]
d81ff6dabb chore(deps): update pre-commit hook astral-sh/ruff-pre-commit (v0.15.2 → v0.15.4) (#293)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-28 10:44:34 +02:00
ed6bef5c7e fix(deps): resolve minimatch to latest 10.x
Add resolutions field to pin minimatch to ^10.2.4.
2026-02-28 10:40:19 +02:00
46b9e3ebeb build(deps): upgrade devDependencies to latest
Bump @biomejs/biome ^2.4.4, @types/node ^25.3.2, bats ^1.13.0,
editorconfig-checker ^6.1.1, typescript ^5.9.3.
2026-02-28 10:39:50 +02:00
f26303a0ff build(biome): migrate schema to v2.4.4 2026-02-28 10:39:04 +02:00
8379135c81 fix(skills): wrap long lines in shell-validate skill
Break lines exceeding 120 chars in the description frontmatter,
intro paragraph, and shellcheck section.
2026-02-28 10:38:42 +02:00
73d0d6b35f fix(scripts): remove exit-on-error from cargo install script
Remove -e from set -euo pipefail so the script continues past
individual cargo install failures.
2026-02-28 10:37:35 +02:00
6a839eea56 fix(gh): update git protocol to ssh
Switch gh CLI git_protocol from https to ssh. Add hosts.yml to
prettierignore since the file is managed by gh CLI.
2026-02-28 10:37:22 +02:00
28c8332058 fix(wezterm): adjust font size and window padding
Reduce font size from 16 to 12, increase window padding from 5 to 10.
2026-02-28 10:36:58 +02:00
b956119b98 feat(fish): add mise version manager integration
Add mise activate and shims PATH for fish shell, plus rustup
cargo env sourcing in conf.d/rustup.fish.
2026-02-28 10:36:49 +02:00
renovate[bot]
0f82f8e65b chore(deps): update node.js (v24.13.1 → v24.14.0) (#292)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-26 08:37:40 +02:00
renovate[bot]
f1277ddf99 chore(deps): update ivuorinen/actions action (v2026.02.18 → v2026.02.24) (#291)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-26 06:36:36 +00:00
renovate[bot]
c834eab42b chore(deps): lock file maintenance (#290) 2026-02-23 21:34:33 +02:00
renovate[bot]
f93deb8bca chore(deps): update pre-commit hook astral-sh/ruff-pre-commit (v0.15.1 → v0.15.2) (#289) 2026-02-21 18:23:46 +02:00
renovate[bot]
248f57b914 chore(deps): update ivuorinen/actions action (v2026.02.10 → v2026.02.18) (#287)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-20 13:58:20 +02:00
renovate[bot]
86e4f9618c chore(deps): lock file maintenance (#288)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-20 13:40:40 +02:00
renovate[bot]
7eb97392fb chore(deps): lock file maintenance (#286) 2026-02-17 19:09:11 +02:00
renovate[bot]
8be1a9e7e2 chore(deps): update pre-commit hook rhysd/actionlint (v1.7.10 → v1.7.11) (#285)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-16 01:46:56 +00:00
renovate[bot]
a7c03fc3d6 chore(deps): update pre-commit hook astral-sh/ruff-pre-commit (v0.15.0 → v0.15.1) (#284)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-14 10:10:21 +02:00
renovate[bot]
3368e3b6dc chore(deps): update ivuorinen/actions action (v2026.02.03 → v2026.02.10) (#283)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-12 01:51:09 +00:00
renovate[bot]
5d87452224 chore(deps): update node.js (v24.13.0 → v24.13.1) (#282)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-12 01:49:42 +00:00
renovate[bot]
d779d23723 chore(deps): update simek/yarn-lock-changes action (v0.14.0 → v0.14.1) (#281)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-09 14:35:28 +00:00
renovate[bot]
ee812de4af chore(deps): update image python to v3.14.3 (#280)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-09 08:02:46 +02:00
a18c16b0b9 fix(shell): harden shared.sh and dfm for set -euo pipefail
Use ${VAR:-} defaults in shared.sh to prevent set -u failures on
unset variables (DOTFILES, ZSH_CUSTOM_COMPLETION_PATH, FPATH).
Export DOTFILES/BREWFILE/HOSTFILES in dfm so sourced scripts see them.
2026-02-08 01:12:39 +02:00
785a8e8eb7 fix(exports): prevent set -e abort when optional files are missing
Replace `[ -f ] && source` with `if/then/fi` for conditional source
lines so the file returns 0 even when optional exports files don't
exist. Also use `${VAR:-}` for XDG defaults to avoid set -u failures.
2026-02-08 01:11:55 +02:00
1cda859999 docs(claude): expand CLAUDE.md with msgr, dfm commands, gotchas, and hooks 2026-02-08 00:31:33 +02:00
bc69560da4 feat(claude): add shfmt/vendor hooks and shell-validate skill 2026-02-08 00:26:19 +02:00
2ee9407a43 feat(dfm): add 6 install commands and reorder install all into tiers 2026-02-07 23:41:51 +02:00
765c2fce72 test(dfm): expand bats tests from 1 to 16
Add tests for menu output of all sections (install, helpers, docs,
dotfiles, check, scripts, tests), routing of invalid input, install
menu completeness for all 19 entries, and check arch/host commands.
2026-02-07 23:20:02 +02:00
88eceaf194 fix(dfm): restrict cheat-databases glob to .sh files only
The install-cheat-* glob was matching .md documentation files, causing
errors when bash tried to execute them.
2026-02-07 22:45:08 +02:00
6d72003446 fix(lint): fix all sonarcloud detected issues (#279)
* fix(ci): replace broad permissions with specific scopes in workflows

Replace read-all/write-all with minimum required permission scopes
across all GitHub Actions workflows to follow the principle of least
privilege (SonarCloud rule githubactions:S8234).

* fix(shell): use [[ instead of [ for conditional tests

Replace single brackets with double brackets in bash conditional
expressions across 14 files (28 changes). All scripts use bash
shebangs so [[ is safe everywhere (SonarCloud rule shelldre:S7688).

* fix(shell): add explicit return statements to functions

Add return 0 as the last statement in ~46 shell functions across
17 files that previously relied on implicit return codes
(SonarCloud rule shelldre:S7682).

* fix(shell): assign positional parameters to local variables

Replace direct $1/$2/$3 usage with named local variables in _log(),
msg(), msg_err(), msg_done(), msg_run(), msg_ok(), and array_diff()
(SonarCloud rule shelldre:S7679).

* fix(python): replace dict() constructor with literal

Use {} instead of dict() for empty dictionary initialization
(SonarCloud rule python:S7498).

* fix(shell): fix husky shebang and tolerate npm outdated exit code

* docs(shell): add function docstring comments

* fix(shell): fix heredoc indentation in x-sonarcloud

* feat(python): add ruff linter and formatter configuration

* fix(ci): align megalinter config with biome, ruff, and shfmt settings

* fix(ci): disable black and yaml-prettier in megalinter config

* chore(ci): update ruff-pre-commit to v0.15.0 and fix hook name

* fix(scripts): check for .git dir before skipping clone in install-fonts

* fix(shell): address code review issues in scripts and shared.sh

- Guard wezterm show-keys failure in create-wezterm-keymaps.sh
- Stop masking git failures with return 0 in install-cheat-purebashbible.sh
- Add missing shared.sh source in install-xcode-cli-tools.sh
- Replace exit 1 with return 1 in sourced shared.sh

* fix(scripts): address code review and security findings

- Guard wezterm show-keys failure in create-wezterm-keymaps.sh
- Stop masking git failures with return 0 in install-cheat-purebashbible.sh
- Add missing shared.sh source in install-xcode-cli-tools.sh
- Replace exit 1 with return 1 in sourced shared.sh
- Remove shell=True subprocess calls in x-git-largest-files.py

* style(shell): apply shfmt formatting and add args to pre-commit hook

* fix(python): suppress bandit false positives in x-git-largest-files

* fix(python): add nosemgrep suppression for check_output call

* feat(format): add prettier for YAML formatting

Install prettier, add .prettierrc.json config (200-char width, 2-space
indent, LF endings), .prettierignore, yarn scripts (lint:prettier,
fix:prettier, format:yaml), and pre-commit hook scoped to YAML files.

* style(yaml): apply prettier formatting

* fix(scripts): address remaining code review findings

- Python: use list comprehension to filter empty strings instead of
  slicing off the last element
- create-wezterm-keymaps: write to temp file and mv for atomic updates
- install-xcode-cli-tools: fix shellcheck source directive path

* fix(python): sort imports alphabetically in x-git-largest-files

* fix(lint): disable PYTHON_ISORT in MegaLinter, ruff handles it

* chore(git): add __pycache__ to gitignore

* fix(python): rename ambiguous variable l to line (E741)

* style: remove trailing whitespace and blank lines

* style(fzf): apply shfmt formatting

* style(shell): apply shfmt formatting

* docs(plans): add design documents

* style(docs): add language specifier to fenced code block

* feat(lint): add markdown-table-formatter to dev tooling

Add markdown-table-formatter as a dev dependency with yarn scripts
(lint:md-table, fix:md-table) and a local pre-commit hook to
automatically format markdown tables on commit.
2026-02-07 19:01:02 +02:00
cff3d1dd8a feat(scripts): add x-sonarcloud script for LLM-driven issue analysis
Bridges LLM agents with SonarCloud's REST API to fetch and format
code quality issues as structured markdown with processing instructions.
2026-02-07 13:24:29 +02:00
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
9de394d8e9 chore(python): add openapi-python-client to uv tools 2026-02-05 22:07:03 +02:00
08de5ea4a6 refactor(submodules): improve old submodule cleanup in add-submodules.sh 2026-02-05 22:07:03 +02:00
0e69b7cb16 refactor: remove dotbot-brew, dotbot-pip submodules and pipx 2026-02-05 22:07:03 +02:00
7c9096d666 fix: standardize shebangs, error handling, and minor issues in x-* scripts
Normalize shebangs to #!/usr/bin/env bash (x-env-list, x-localip).
Use XDG_CONFIG_HOME in x-change-alacritty-theme. Remove unused
VERBOSE variable in x-multi-ping. Add set -euo pipefail to x-when-down
and x-when-up. Add usage header to x-term-colors. Fix notify-call
to notify-send.sh in x-record.
2026-02-05 22:07:03 +02:00
efd9eebc85 fix: resolve critical issues in x-clean-vendordirs, x-foreach, x-ip
x-clean-vendordirs: remove broken msgr dependency (not sourced),
add set -euo pipefail. x-foreach: replace eval on command args with
direct "$@" execution, add usage guard. x-ip: add set -euo pipefail,
curl dependency check, and silent-fail flag.
2026-02-05 22:07:03 +02:00
fc8db1f5b5 refactor(path): consolidate x-path-{append,prepend,remove} as thin wrappers
Add source guard to x-path so its functions can be loaded without
executing the main logic. Rewrite standalone path scripts to source
x-path and call the appropriate function directly, eliminating code
duplication while preserving source-ability for shell integration.
2026-02-05 22:07:03 +02:00
4414e0c3b6 chore: remove unused x-mkd and x-validate-sha256sum.sh scripts
x-mkd's cd-in-subshell cannot work when executed (only sourced) and
is unused in the repo. x-validate-sha256sum.sh duplicates the
functionality of x-sha256sum-matcher.
2026-02-05 22:07:03 +02:00
abb6c9f615 refactor(dfm): clean up portability, dead code, and error handling
Add bash 4.0+ version check with macOS Homebrew bootstrap. Remove
unreachable fish shell detection and source_file function. Fix bugs:
remove dead ntfy menu entry, fix msg/msgr case mismatch in tests,
guard shift calls against empty args, quote $width, fix $"..." locale
string, fix exit 0 on apt error. Replace declare -A with indexed
array in section_scripts. Use early-return guards with msgr warn for
unavailable brew/apt. Replace exit with return in section functions.
2026-02-05 22:07:03 +02:00
57b566704e fix(lint): resolve all lint errors and remove dangling symlinks
Fix 14 editorconfig/biome errors across 6 files: update biome schema
version, replace tabs with spaces, fix continuation indents, and wrap
long lines. Remove dangling OrbStack fish completion symlinks.
2026-02-05 22:07:03 +02:00
renovate[bot]
4510e62070 chore(deps): update ivuorinen/actions action (v2026.01.21 → v2026.02.03) (#278)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-05 17:56:56 +00:00
156 changed files with 3441 additions and 20573 deletions

View File

@@ -0,0 +1,14 @@
---
name: code-reviewer
description: Reviews shell/fish/lua changes for correctness and style
tools: [Read, Grep, Glob, Bash]
---
Review the changed files for:
1. **Shell scripts**: POSIX compliance for /bin/sh scripts, proper quoting, shellcheck issues
2. **Fish files**: fish syntax correctness, consistent function patterns
3. **Lua files**: stylua compliance, Neovim API usage patterns
4. **All**: EditorConfig compliance (2-space indent, LF endings)
Report only high-confidence issues. Skip vendor files (fzf-tmux).

34
.claude/settings.json Normal file
View File

@@ -0,0 +1,34 @@
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "fp=$(cat | jq -r '.tool_input.file_path // empty') && [ -n \"$fp\" ] && case \"$fp\" in */fzf-tmux|*/yarn.lock|*/.yarn/*) echo \"BLOCKED: $fp is a vendor/lock file — do not edit directly\" >&2; exit 2;; esac; exit 0"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "fp=$(cat | jq -r '.tool_input.file_path // empty') && [ -n \"$fp\" ] && [ -f \"$fp\" ] && case \"$fp\" in *.sh|*/bin/*) head -1 \"$fp\" | grep -qE '^#!.*(ba)?sh' && command -v shfmt > /dev/null && shfmt -i 2 -bn -ci -sr -fn -w \"$fp\";; esac; exit 0"
},
{
"type": "command",
"command": "fp=$(cat | jq -r '.tool_input.file_path // empty') && [ -n \"$fp\" ] && [ -f \"$fp\" ] && case \"$fp\" in *.fish) command -v fish_indent > /dev/null && fish_indent -w \"$fp\";; esac; exit 0"
},
{
"type": "command",
"command": "fp=$(cat | jq -r '.tool_input.file_path // empty') && [ -n \"$fp\" ] && [ -f \"$fp\" ] && case \"$fp\" in *.lua) command -v stylua > /dev/null && stylua \"$fp\";; esac; exit 0"
}
]
}
]
}
}

View File

@@ -0,0 +1,38 @@
---
name: fish-validate
description: >-
Validate fish scripts after editing.
Apply when writing or modifying any .fish file
in config/fish/.
user-invocable: false
allowed-tools: Bash, Read
---
After editing any `.fish` file in `config/fish/`, validate it:
## 1. Syntax check
```bash
fish --no-execute <file>
```
If syntax check fails, fix the issue before proceeding.
## 2. Format check
Run `fish_indent` to verify formatting:
```bash
fish_indent --check <file>
```
If formatting differs, apply it:
```bash
fish_indent -w <file>
```
## Key files to never validate
- Files inside `config/fish/functions/` prefixed with `_tide_`
(managed by the tide prompt plugin)

View File

@@ -0,0 +1,22 @@
---
name: lua-format
description: >-
Format Lua files after editing.
Apply when writing or modifying any .lua file.
user-invocable: false
allowed-tools: Bash
---
After editing any `.lua` file, format it with stylua:
```bash
stylua <file>
```
Project settings are in `stylua.toml` (90-char line length).
If stylua is not available, skip formatting silently.
## Files to never format
- Files inside `config/nvim/` managed by plugins (lazy.nvim lockfile)

View File

@@ -0,0 +1,44 @@
---
name: shell-validate
description: >-
Validate shell scripts after editing.
Apply when writing or modifying any shell script
in local/bin/ or scripts/.
user-invocable: false
allowed-tools: Bash, Read, Grep
---
After editing any shell script in `local/bin/`, `scripts/`, or `config/`
(files with a `#!` shebang or `# shellcheck shell=` directive),
validate it:
## 1. Determine the shell
- `/bin/sh` or `#!/usr/bin/env sh` shebang -> POSIX, use `sh -n`
- `/bin/bash` or `#!/usr/bin/env bash` shebang -> Bash, use `bash -n`
- `# shellcheck shell=bash` directive (no shebang) -> use `bash -n`
- `# shellcheck shell=sh` directive (no shebang) -> use `sh -n`
- No shebang and no directive -> default to `bash -n`
## 2. Syntax check
Run the appropriate syntax checker:
```bash
bash -n <file> # for bash scripts
sh -n <file> # for POSIX sh scripts
```
If syntax check fails, fix the issue before proceeding.
## 3. ShellCheck
Run `shellcheck <file>`. The project `.shellcheckrc` already
disables SC2039, SC2166, SC2154, SC1091, SC2174, SC2016.
Only report and fix warnings that are NOT in that exclude list.
## Key files to never validate (not shell scripts)
- `local/bin/fzf-tmux` (vendor file)
- `*.md` files
- `*.bats` test files (Bats, not plain shell)

View File

@@ -0,0 +1,38 @@
---
name: yaml-validate
description: >-
Validate YAML files after editing.
Apply when writing or modifying any .yml or .yaml file.
user-invocable: false
allowed-tools: Bash, Read
---
After editing any YAML file, validate it:
## 1. Syntax check
Run yamllint on the file:
```bash
yamllint <file>
```
If yamllint is not available, fall back to:
```bash
python3 -c "import yaml; yaml.safe_load(open('<file>'))"
```
## 2. GitHub Actions workflows
If the file is under `.github/workflows/`, also run:
```bash
actionlint <file>
```
If actionlint is not available, skip silently.
## Files to skip
- `config/gh/hosts.yml` — managed by `gh` CLI, not hand-edited

View File

@@ -8,6 +8,10 @@ indent_style = space
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.py]
indent_size = 4
max_line_length = 120
[*.fish] [*.fish]
max_line_length = 120 max_line_length = 120

6
.github/README.md vendored
View File

@@ -37,7 +37,7 @@ see what interesting stuff you've done with it. Sharing is caring.
### Interesting folders ### Interesting folders
| Path | Description | | Path | Description |
| ------------------- | -------------------------------------------- | |---------------------|----------------------------------------------|
| `.github` | GitHub Repository configuration files, meta. | | `.github` | GitHub Repository configuration files, meta. |
| `hosts/{hostname}/` | Configs that should apply to that host only. | | `hosts/{hostname}/` | Configs that should apply to that host only. |
| `local/bin` | Helper scripts that I've collected or wrote. | | `local/bin` | Helper scripts that I've collected or wrote. |
@@ -52,7 +52,7 @@ is processed by Dotbot during installation.
### dotfile folders ### dotfile folders
| Repo | Destination | Description | | Repo | Destination | Description |
| --------- | ----------- | ------------------------------------------- | |-----------|-------------|---------------------------------------------|
| `base/` | `.*` | `$HOME` level files. | | `base/` | `.*` | `$HOME` level files. |
| `config/` | `.config/` | Configurations for applications. | | `config/` | `.config/` | Configurations for applications. |
| `local/` | `.local/` | XDG Base folder: `bin`, `share` and `state` | | `local/` | `.local/` | XDG Base folder: `bin`, `share` and `state` |
@@ -86,7 +86,7 @@ The folder structure follows [XDG Base Directory Specification][xdg] where possi
### XDG Variables ### XDG Variables
| Env | Default | Short description | | Env | Default | Short description |
| ------------------ | -------------------- | ---------------------------------------------- | |--------------------|----------------------|------------------------------------------------|
| `$XDG_BIN_HOME` | `$HOME/.local/bin` | Local binaries | | `$XDG_BIN_HOME` | `$HOME/.local/bin` | Local binaries |
| `$XDG_CONFIG_HOME` | `$HOME/.config` | User-specific configs | | `$XDG_CONFIG_HOME` | `$HOME/.config` | User-specific configs |
| `$XDG_DATA_HOME` | `$HOME/.local/share` | User-specific data files | | `$XDG_DATA_HOME` | `$HOME/.local/share` | User-specific data files |

View File

@@ -9,25 +9,27 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true cancel-in-progress: true
permissions: read-all permissions:
contents: read
jobs: jobs:
debug-changelog: debug-changelog:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: write-all permissions:
contents: read
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Create changelog text - name: Create changelog text
id: changelog id: changelog
uses: loopwerk/tag-changelog@941366edb8920e2071eae0449031830984b9f26e # v1.3.0 uses: loopwerk/tag-changelog@8dd150d55fbf1fe93e0ea00a29a6153aaeb81912 # v1.5.0
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
config_file: .github/tag-changelog-config.js config_file: .github/tag-changelog-config.js
- name: 'Echo results' - name: "Echo results"
id: output-changelog id: output-changelog
run: | run: |
echo "${{ steps.changelog.outputs.changes }}" echo "${{ steps.changelog.outputs.changes }}"

View File

@@ -5,13 +5,14 @@ name: Lint Code Base
# yamllint disable-line # yamllint disable-line
on: on:
pull_request: pull_request:
branches: [master, main] branches: [main]
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true cancel-in-progress: true
permissions: read-all permissions:
contents: read
jobs: jobs:
Linter: Linter:
@@ -30,9 +31,9 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Yarn Lock Changes - name: Yarn Lock Changes
uses: Simek/yarn-lock-changes@c7543145aaafdd8fc925cea5d85b2bd5a73091f8 # v0.14.0 uses: Simek/yarn-lock-changes@59f47ee499424d2c2437c5aebf863b5c6d50a5bc # v0.14.1
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
- name: Run PR Lint - name: Run PR Lint
uses: ivuorinen/actions/pr-lint@f98ae7cd7d0feb1f9d6b01de0addbb11414cfc73 # v2026.01.21 uses: ivuorinen/actions/pr-lint@7f6a23b59316795c4b3cb3b3b28dd53e53655a33 # v2026.03.11

View File

@@ -5,19 +5,21 @@ name: Release Daily State
on: on:
workflow_dispatch: workflow_dispatch:
schedule: schedule:
- cron: '0 21 * * *' # 00:00 at Europe/Helsinki - cron: "0 21 * * *" # 00:00 at Europe/Helsinki
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true cancel-in-progress: true
permissions: read-all permissions:
contents: read
jobs: jobs:
new-daily-release: new-daily-release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: write-all permissions:
contents: write
outputs: outputs:
created: ${{ steps.daily-version.outputs.created }} created: ${{ steps.daily-version.outputs.created }}
@@ -33,14 +35,14 @@ jobs:
- name: Create changelog text - name: Create changelog text
if: steps.daily-version.outputs.created if: steps.daily-version.outputs.created
id: changelog id: changelog
uses: loopwerk/tag-changelog@941366edb8920e2071eae0449031830984b9f26e # v1.3.0 uses: loopwerk/tag-changelog@8dd150d55fbf1fe93e0ea00a29a6153aaeb81912 # v1.5.0
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
config_file: .github/tag-changelog-config.js config_file: .github/tag-changelog-config.js
- name: Create release - name: Create release
if: steps.daily-version.outputs.created if: steps.daily-version.outputs.created
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 uses: softprops/action-gh-release@26e8ad27a09a225049a7075d7ec1caa2df6ff332 # v2.6.0
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
tag_name: ${{ steps.daily-version.outputs.version }} tag_name: ${{ steps.daily-version.outputs.version }}

View File

@@ -5,14 +5,15 @@ name: Pre-commit autoupdate
on: on:
schedule: schedule:
# At 04:00 on Monday and Thursday. # At 04:00 on Monday and Thursday.
- cron: '0 4 * * 1,4' - cron: "0 4 * * 1,4"
workflow_dispatch: workflow_dispatch:
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true cancel-in-progress: true
permissions: read-all permissions:
contents: read
jobs: jobs:
auto-update: auto-update:
@@ -33,6 +34,6 @@ jobs:
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
branch: update/pre-commit-hooks branch: update/pre-commit-hooks
title: 'chore: update pre-commit hooks' title: "chore: update pre-commit hooks"
commit-message: 'chore: update pre-commit hooks' commit-message: "chore: update pre-commit hooks"
body: Update versions of pre-commit hooks to latest version. body: Update versions of pre-commit hooks to latest version.

View File

@@ -14,7 +14,8 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true cancel-in-progress: true
permissions: read-all permissions:
pull-requests: read
jobs: jobs:
semantic-pr: semantic-pr:

View File

@@ -11,7 +11,7 @@ on:
- .github/workflows/sync-labels.yml - .github/workflows/sync-labels.yml
- .github/labels.yml - .github/labels.yml
schedule: schedule:
- cron: '34 5 * * *' - cron: "34 5 * * *"
workflow_call: workflow_call:
workflow_dispatch: workflow_dispatch:
@@ -19,7 +19,8 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true cancel-in-progress: true
permissions: read-all permissions:
contents: read
jobs: jobs:
SyncLabels: SyncLabels:
@@ -29,4 +30,4 @@ jobs:
issues: write issues: write
steps: steps:
- uses: ivuorinen/actions/sync-labels@f98ae7cd7d0feb1f9d6b01de0addbb11414cfc73 # v2026.01.21 - uses: ivuorinen/actions/sync-labels@7f6a23b59316795c4b3cb3b3b28dd53e53655a33 # v2026.03.11

View File

@@ -5,20 +5,22 @@ name: Update submodules
on: on:
schedule: schedule:
# At 04:00 on Monday and Thursday. # At 04:00 on Monday and Thursday.
- cron: '0 4 * * 1' - cron: "0 4 * * 1,4"
workflow_dispatch: workflow_dispatch:
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true cancel-in-progress: true
permissions: read-all permissions:
contents: read
jobs: jobs:
update-submodules: update-submodules:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: write-all permissions:
contents: write
steps: steps:
- name: Checkout repository - name: Checkout repository

1
.gitignore vendored
View File

@@ -56,5 +56,6 @@ local/man/yabai.1
local/share/fonts/* local/share/fonts/*
lock lock
node_modules node_modules
__pycache__
ssh/local.d/* ssh/local.d/*
config/fish/fish_variables* config/fish/fish_variables*

12
.gitmodules vendored
View File

@@ -4,11 +4,6 @@
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
@@ -29,11 +24,6 @@
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
@@ -63,6 +53,8 @@
[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,6 +6,5 @@ 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

@@ -9,16 +9,21 @@ VALIDATE_ALL_CODEBASE: true
FILEIO_REPORTER: false # Generate file.io report FILEIO_REPORTER: false # Generate file.io report
GITHUB_STATUS_REPORTER: true # Generate GitHub status report GITHUB_STATUS_REPORTER: true # Generate GitHub status report
IGNORE_GENERATED_FILES: true # Ignore generated files IGNORE_GENERATED_FILES: true # Ignore generated files
JAVASCRIPT_DEFAULT_STYLE: prettier # Default style for JavaScript
PRINT_ALPACA: false # Print Alpaca logo in console PRINT_ALPACA: false # Print Alpaca logo in console
SARIF_REPORTER: true # Generate SARIF report SARIF_REPORTER: true # Generate SARIF report
SHOW_SKIPPED_LINTERS: false # Show skipped linters in MegaLinter log SHOW_SKIPPED_LINTERS: false # Show skipped linters in MegaLinter log
TYPESCRIPT_DEFAULT_STYLE: prettier # Default style for TypeScript
DISABLE_LINTERS: DISABLE_LINTERS:
- REPOSITORY_DEVSKIM - REPOSITORY_DEVSKIM
- JAVASCRIPT_ES # using biome - JAVASCRIPT_ES # using biome
- JAVASCRIPT_PRETTIER # using biome - JAVASCRIPT_PRETTIER # using biome
- TYPESCRIPT_PRETTIER # using biome
- JSON_PRETTIER # using biome
- PYTHON_BLACK # using ruff
- PYTHON_FLAKE8 # using ruff
- PYTHON_PYLINT # using ruff
- PYTHON_ISORT # using ruff (I rules)
YAML_YAMLLINT_CONFIG_FILE: .yamllint.yml YAML_YAMLLINT_CONFIG_FILE: .yamllint.yml
REPOSITORY_GIT_DIFF_DISABLE_ERRORS: true REPOSITORY_GIT_DIFF_DISABLE_ERRORS: true
BASH_SHFMT_ARGUMENTS: -i 2 -bn -ci -sr -fn
FILTER_REGEX_EXCLUDE: > FILTER_REGEX_EXCLUDE: >
(node_modules|tools|config/cheat/cheatsheets/community|config/cheat/cheatsheets/tldr|config/fzf|config/zsh|config/tmux/plugins) (node_modules|tools|config/cheat/cheatsheets/community|config/cheat/cheatsheets/tldr|config/fzf|config/zsh|config/tmux/plugins)

2
.nvmrc
View File

@@ -1 +1 @@
24.13.0 24.14.0

View File

@@ -28,12 +28,25 @@ repos:
entry: yarn biome check --write --files-ignore-unknown=true --no-errors-on-unmatched entry: yarn biome check --write --files-ignore-unknown=true --no-errors-on-unmatched
language: system language: system
files: \.(js|ts|jsx|tsx|json|md)$ files: \.(js|ts|jsx|tsx|json|md)$
- id: markdown-table-formatter
name: Markdown Table Formatter
entry: yarn markdown-table-formatter
language: system
types: [markdown]
- repo: https://github.com/adrienverge/yamllint - repo: https://github.com/adrienverge/yamllint
rev: v1.38.0 rev: v1.38.0
hooks: hooks:
- id: yamllint - id: yamllint
- repo: local
hooks:
- id: prettier
name: Prettier (YAML)
entry: yarn prettier --write
language: system
types_or: [yaml]
- repo: https://github.com/shellcheck-py/shellcheck-py - repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.11.0.1 rev: v0.11.0.1
hooks: hooks:
@@ -43,14 +56,15 @@ repos:
rev: v3.12.0-2 rev: v3.12.0-2
hooks: hooks:
- id: shfmt - id: shfmt
args: [-i, "2", -bn, -ci, -sr, -fn, -w]
- repo: https://github.com/rhysd/actionlint - repo: https://github.com/rhysd/actionlint
rev: v1.7.10 rev: v1.7.11
hooks: hooks:
- id: actionlint - id: actionlint
- repo: https://github.com/JohnnyMorganz/StyLua - repo: https://github.com/JohnnyMorganz/StyLua
rev: v2.3.1 rev: v2.4.0
hooks: hooks:
- id: stylua # or stylua-system / stylua-github - id: stylua # or stylua-system / stylua-github
exclude: hammerspoon\.types\.lua$ exclude: hammerspoon\.types\.lua$
@@ -60,3 +74,10 @@ repos:
hooks: hooks:
- id: fish_syntax - id: fish_syntax
- id: fish_indent - id: fish_indent
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.6
hooks:
- id: ruff-check
args: [--fix]
- id: ruff-format

18
.prettierignore Normal file
View File

@@ -0,0 +1,18 @@
node_modules
.yarn
.pnp.*
.mypy_cache
Brewfile.lock.json
lazy-lock.json
config/cheat/cheatsheets/community
config/cheat/cheatsheets/tldr
config/fzf
config/nvim
config/op/plugins/used_plugins
config/tmux/plugins
config/vim/plugged
config/zsh
local/bin/antigen.zsh
local/bin/asdf
tools
config/gh/hosts.yml

9
.prettierrc.json Normal file
View File

@@ -0,0 +1,9 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"printWidth": 200,
"tabWidth": 2,
"useTabs": false,
"endOfLine": "lf",
"singleQuote": false,
"proseWrap": "preserve"
}

View File

@@ -1 +1 @@
3.14.2 3.14.3

View File

@@ -13,11 +13,11 @@ ignore_all_files_in_gitignore: true
# Was previously called `ignored_dirs`, please update your config if you are using that. # Was previously called `ignored_dirs`, please update your config if you are using that.
# Added (renamed) on 2025-04-07 # Added (renamed) on 2025-04-07
ignored_paths: ignored_paths:
- '*.swp' - "*.swp"
- '*.tmp' - "*.tmp"
- '*.tmp.*' - "*.tmp.*"
- '.DS_Store' - ".DS_Store"
- '.git/**' - ".git/**"
- /config/cheat/cheatsheets/community/** - /config/cheat/cheatsheets/community/**
- /config/cheat/cheatsheets/pure-bash-bible/** - /config/cheat/cheatsheets/pure-bash-bible/**
- /config/cheat/cheatsheets/tldr/** - /config/cheat/cheatsheets/tldr/**
@@ -85,6 +85,6 @@ excluded_tools: []
# initial prompt for the project. It will always be given to the LLM upon activating the project # initial prompt for the project. It will always be given to the LLM upon activating the project
# (contrary to the memories, which are loaded on demand). # (contrary to the memories, which are loaded on demand).
initial_prompt: '' initial_prompt: ""
project_name: '.dotfiles' project_name: ".dotfiles"

View File

@@ -1,55 +0,0 @@
# Project guidelines
This repository contains configuration files and helper scripts for managing
a development environment.
Dotbot drives installation, and host-specific folders under `hosts/` contain extra configs.
## Setup
1. Run `yarn install` to fetch linting tools and the Bats test framework.
2. Re-run `yarn install` whenever `package.json` changes.
3. Yarn is the package manager of choice; avoid `npm` commands.
## Keeping the repository up to date
1. Update submodules with `git submodule update --remote --merge`.
2. Pull the latest changes and run `./install`.
## Linting and tests
- Format files with:
```bash
yarn fix:prettier
yarn fix:markdown
```
- Shell scripts must pass `shellcheck`.
```bash
find . -path ./node_modules -prune -o -name '*.sh' -print0 | xargs -0 shellcheck
```
- Ensure `.editorconfig` rules pass:
```bash
tools/install-ec.sh
ec
```
- Execute tests with `yarn test` when code changes.
## Debugging lint issues
- `yarn lint:prettier` and `yarn lint:markdown` show formatting errors.
- Ensure shell scripts have a shebang or `# shellcheck shell=bash` directive.
- Consult `.shellcheckrc` for project specific checks.
Scripts rely on helpers in `config/shared.sh` so they run under Bash, Zsh and Fish by default.
## 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 : -->

171
CLAUDE.md Normal file
View File

@@ -0,0 +1,171 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code)
when working with code in this repository.
## Repository Overview
Personal dotfiles repository for Ismo Vuorinen.
Uses **Dotbot** (not GNU Stow) to symlink configuration files into place.
The directory layout follows the XDG Base Directory Specification.
## Directory Layout and Linking
| Source | Destination | Notes |
|---------------------|-------------------|-------------------------------------------|
| `base/*` | `~/.*` | Home-level dotfiles (`.` added by Dotbot) |
| `config/*` | `~/.config/` | Application configurations |
| `local/bin/*` | `~/.local/bin/` | Helper scripts and utilities |
| `local/share/*` | `~/.local/share/` | Data files |
| `local/man/**` | `~/.local/man/` | Manual pages |
| `ssh/*` | `~/.ssh/` | SSH configuration (mode 0600) |
| `hosts/<hostname>/` | Overlays | Host-specific overrides |
Installation: `./install` runs Dotbot with `install.conf.yaml`,
then applies `hosts/<hostname>/install.conf.yaml` if it exists.
## Commands
```bash
# Install dependencies (required before lint/test)
yarn install
# Linting
yarn lint # Run biome + prettier + editorconfig-checker
yarn lint:biome # Biome only
yarn lint:ec # EditorConfig checker only
yarn lint:md-table # Markdown table formatting check
yarn fix:md-table # Auto-fix markdown tables
# Formatting
yarn fix:biome # Autofix with biome (JS/TS/JSON/MD)
yarn fix:prettier # Autofix with prettier (YAML)
yarn format # Format with biome
yarn format:yaml # Format YAML files with prettier
# Testing (Bats - Bash Automated Testing System)
yarn test # Run all tests in tests/
# Run a single test file:
./node_modules/.bin/bats tests/dfm.bats
# Shell linting
shellcheck <script> # Lint shell scripts
# Tooling maintenance
npx @biomejs/biome migrate --write # Update biome schema version
```
## Pre-commit Hooks
Configured in `.pre-commit-config.yaml`: shellcheck, shfmt, biome,
yamllint, prettier, actionlint, stylua, fish_syntax/fish_indent, ruff.
Run `pre-commit run --all-files` to check everything.
## Commit Convention
Semantic Commit messages: `type(scope): summary`
(e.g., `fix(tmux): correct prefix binding`).
Enforced by commitlint extending `@ivuorinen/commitlint-config`.
## Architecture
### Shell Configuration Chain
Both `base/bashrc` and `base/zshrc` source `config/shared.sh`,
which loads:
- `config/exports` — environment variables, XDG dirs, PATH
- `config/alias` — shell aliases
Zsh additionally uses **antidote** (in `tools/antidote/`)
for plugin management and **oh-my-posh** for the prompt.
### msgr — Messaging Helper
`local/bin/msgr` provides colored output functions (`msgr msg`,
`msgr run`, `msgr yay`, `msgr err`, `msgr warn`). Sourced by `dfm`
and most scripts in `local/bin/`.
### dfm — Dotfiles Manager
`local/bin/dfm` is the main management script. Key commands:
- `dfm install all` — install everything in tiered stages
- `dfm brew install` / `dfm brew update` — Homebrew management
- `dfm apt upkeep` — APT package maintenance (Debian/Ubuntu)
- `dfm dotfiles fmt` / `dfm dotfiles shfmt` — format configs/scripts
- `dfm helpers <name>` — inspect aliases, colors, env, functions, path
- `dfm docs all` — regenerate documentation under `docs/`
- `dfm check arch` / `dfm check host` — system info
- `dfm scripts` — run scripts from `scripts/` (discovered via `@description` tags)
- `dfm tests` — test visualization helpers
### Submodules
External dependencies are git submodules (Dotbot, plugins,
tmux plugins, cheatsheets, antidote).
Managed by `add-submodules.sh`. All set to `ignore = dirty`.
Updated automatically via GitHub Actions on a schedule.
### Host-specific Configs
Machine-specific overrides live in `hosts/<hostname>/`
with their own `base/`, `config/`, and `install.conf.yaml`.
These are layered on top of the global config during installation.
## Code Style
- **EditorConfig**: 2-space indent, UTF-8, LF line endings.
See `.editorconfig` for per-filetype overrides
(4-space for PHP/fish, tabs for git config).
- **Shell scripts**: Must have a shebang or
`# shellcheck shell=bash` directive.
Follow shfmt settings in `.editorconfig`
(2-space indent, `binary_next_line`,
`switch_case_indent`, `space_redirects`, `function_next_line`).
- **Lua** (neovim config): Formatted with stylua (`stylua.toml`),
90-char line length.
- **JSON/JS/TS/Markdown**: Formatted with Biome (`biome.json`),
80-char width (Markdown uses 120-char override).
- **YAML**: Formatted with Prettier (`.prettierrc.json`),
validated with yamllint (`.yamllint.yml`).
## ShellCheck Disabled Rules
Defined in `.shellcheckrc`:
SC2039 (POSIX `local`), SC2166 (`-o` in test),
SC2154 (unassigned variables), SC1091 (source following),
SC2174 (mkdir -p -m), SC2016 (single-quote expressions).
## Gotchas
- **POSIX scripts**: `x-ssh-audit`, `x-codeql`, `x-until-error`,
`x-until-success`, `x-ssl-expiry-date` use `/bin/sh`.
Validate with `sh -n`, not `bash -n`.
- **Vendor file**: `local/bin/fzf-tmux` is vendored from
junegunn/fzf — do not modify.
- **Fish config**: `config/fish/` has its own config chain
(`config.fish`, `exports.fish`, `alias.fish`) plus 60+ functions.
- **gh CLI config**: `config/gh/hosts.yml` is managed by `gh` CLI
and excluded from prettier (see `.prettierignore`).
- **Python**: Two scripts (`x-compare-versions.py`,
`x-git-largest-files.py`) linted by Ruff (config in `pyproject.toml`).
## Claude Code Configuration
- **Hooks** (`.claude/settings.json`):
- *PreToolUse*: Blocks edits to `fzf-tmux`, `yarn.lock`, `.yarn/`
- *PostToolUse*: Auto-runs `shfmt` on shell scripts after Edit/Write
- *PostToolUse*: Auto-runs `fish_indent` on `.fish` files after Edit/Write
- *PostToolUse*: Auto-runs `stylua` on `.lua` files after Edit/Write
- **Skills** (`.claude/skills/`):
- `shell-validate`: Auto-validates shell scripts (syntax + shellcheck)
- `fish-validate`: Auto-validates fish scripts (syntax + fish_indent)
- `lua-format`: Auto-formats Lua files with stylua
- `yaml-validate`: Auto-validates YAML files (yamllint + actionlint)
- **Subagents** (`.claude/agents/`):
- `code-reviewer`: Reviews shell/fish/lua changes for correctness and style
- **MCP Servers**:
- `context7`: Live documentation lookup for tools and libraries
## Package Manager
Yarn (v4.12.0) is the package manager. Do not use npm.

View File

@@ -5,12 +5,8 @@ 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 \
@@ -46,26 +42,70 @@ 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
# remove old submodules # Log a message using msgr if available, else echo
folders=( _log()
"config/tmux/plugins/tpm" {
"config/tmux/plugins/tmux" local msg="$1"
"config/tmux/plugins/tmux-menus" if command -v msgr > /dev/null 2>&1; then
"tools/dotbot-crontab" msgr run_done "$msg"
"tools/dotbot-snap" else
"config/tmux/plugins/tmux-window-name" echo " [ok] $msg"
"config/tmux/plugins/tmux-sensible" fi
"config/tmux/plugins/tmux-mode-indicator" return 0
"config/tmux/plugins/tmux-yank" }
"config/tmux/plugins/tmux-fzf-url"
"config/nvim-kickstart" # Remove a stale git submodule and clean up references
"local/bin/asdf" remove_old_submodule()
"local/asdf" {
"tools/dotbot-asdf" local name="$1" path="$2"
# Remove working tree
if [[ -d "$path" ]]; then
rm -rf "$path"
_log "Removed $path"
fi
# 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 folder in "${folders[@]}"; do for entry in "${old_submodules[@]}"; do
[ -d "$folder" ] \ name="${entry%%:*}"
&& rm -rf "$folder" \ path="${entry#*:}"
&& msgr run_done "Removed old submodule $folder" remove_old_submodule "$name" "$path"
done done

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 && {
@@ -21,6 +22,3 @@ x-have antidot && {
PROMPT_DIRTRIM=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"

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,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,9 @@ 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.1/schema.json", "$schema": "https://biomejs.dev/schemas/2.4.4/schema.json",
"vcs": { "vcs": {
"enabled": true, "enabled": true,
"clientKind": "git", "clientKind": "git",

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

@@ -93,13 +93,13 @@ expand-main:
# Note that not all layouts respond to this command. # Note that not all layouts respond to this command.
increase-main: increase-main:
mod: mod1 mod: mod1
key: ',' key: ","
# Decrease the number of windows in the main pane. # Decrease the number of windows in the main pane.
# Note that not all layouts respond to this command. # Note that not all layouts respond to this command.
decrease-main: decrease-main:
mod: mod1 mod: mod1
key: '.' key: "."
# General purpose command for custom layouts. # General purpose command for custom layouts.
# Functionality is layout-dependent. # Functionality is layout-dependent.

View File

@@ -4,15 +4,15 @@
# Set XDG directories if not already set # Set XDG directories if not already set
# https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html # https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
[ -z "$XDG_CONFIG_HOME" ] && export XDG_CONFIG_HOME="$HOME/.config" [ -z "${XDG_CONFIG_HOME:-}" ] && export XDG_CONFIG_HOME="$HOME/.config"
[ -z "$XDG_DATA_HOME" ] && export XDG_DATA_HOME="$HOME/.local/share" [ -z "${XDG_DATA_HOME:-}" ] && export XDG_DATA_HOME="$HOME/.local/share"
[ -z "$XDG_CACHE_HOME" ] && export XDG_CACHE_HOME="$HOME/.cache" [ -z "${XDG_CACHE_HOME:-}" ] && export XDG_CACHE_HOME="$HOME/.cache"
[ -z "$XDG_STATE_HOME" ] && export XDG_STATE_HOME="$HOME/.local/state" [ -z "${XDG_STATE_HOME:-}" ] && export XDG_STATE_HOME="$HOME/.local/state"
[ -z "$XDG_BIN_HOME" ] && export XDG_BIN_HOME="$HOME/.local/bin" [ -z "${XDG_BIN_HOME:-}" ] && export XDG_BIN_HOME="$HOME/.local/bin"
[ -z "$XDG_RUNTIME_DIR" ] && export XDG_RUNTIME_DIR="$HOME/.local/run" [ -z "${XDG_RUNTIME_DIR:-}" ] && export XDG_RUNTIME_DIR="$HOME/.local/run"
# if DOTFILES is not set, set it to the default location # if DOTFILES is not set, set it to the default location
[ -z "$DOTFILES" ] && export DOTFILES="$HOME/.dotfiles" [ -z "${DOTFILES:-}" ] && export DOTFILES="$HOME/.dotfiles"
export PATH="$XDG_BIN_HOME:$DOTFILES/local/bin:$XDG_DATA_HOME/bob/nvim-bin:$XDG_DATA_HOME/cargo/bin:/opt/homebrew/bin:/usr/local/bin:$PATH" export PATH="$XDG_BIN_HOME:$DOTFILES/local/bin:$XDG_DATA_HOME/bob/nvim-bin:$XDG_DATA_HOME/cargo/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"
@@ -150,6 +150,7 @@ commit()
git commit -a -m "$commitMessage" git commit -a -m "$commitMessage"
} }
# Run Laravel scheduler in a loop
scheduler() scheduler()
{ {
while :; do while :; do
@@ -282,7 +283,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,15 +438,19 @@ 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"
export CHEAT_USE_FZF=true export CHEAT_USE_FZF=true
export SQLITE_HISTORY="${XDG_CACHE_HOME}/sqlite_history" export SQLITE_HISTORY="${XDG_CACHE_HOME}/sqlite_history"
[ -f "$XDG_CONFIG_HOME/exports-secret" ] && source "$XDG_CONFIG_HOME/exports-secret" if [ -f "$XDG_CONFIG_HOME/exports-secret" ]; then source "$XDG_CONFIG_HOME/exports-secret"; fi
[ -f "$XDG_CONFIG_HOME/exports-local" ] && source "$XDG_CONFIG_HOME/exports-local" if [ -f "$XDG_CONFIG_HOME/exports-local" ]; then source "$XDG_CONFIG_HOME/exports-local"; fi
# shellcheck source=./exports-lakka # shellcheck source=./exports-lakka
[ -f "$XDG_CONFIG_HOME/exports-$(hostname)" ] && source "$XDG_CONFIG_HOME/exports-$(hostname)" if [ -f "$XDG_CONFIG_HOME/exports-$(hostname)" ]; then source "$XDG_CONFIG_HOME/exports-$(hostname)"; fi
# shellcheck source=./exports-lakka-secret # shellcheck source=./exports-lakka-secret
[ -f "$XDG_CONFIG_HOME/exports-$(hostname)-secret" ] && source "$XDG_CONFIG_HOME/exports-$(hostname)-secret" if [ -f "$XDG_CONFIG_HOME/exports-$(hostname)-secret" ]; then source "$XDG_CONFIG_HOME/exports-$(hostname)-secret"; fi

View File

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

View File

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

View File

@@ -2,21 +2,21 @@
# Place in ~/.config/fish/completions/phpenv.fish # Place in ~/.config/fish/completions/phpenv.fish
# Complete main commands # Complete main commands
complete -c phpenv -f -n "__fish_use_subcommand" -a "install" -d "Install a PHP version" complete -c phpenv -f -n __fish_use_subcommand -a install -d "Install a PHP version"
complete -c phpenv -f -n "__fish_use_subcommand" -a "uninstall" -d "Uninstall a PHP version" complete -c phpenv -f -n __fish_use_subcommand -a uninstall -d "Uninstall a PHP version"
complete -c phpenv -f -n "__fish_use_subcommand" -a "use" -d "Use PHP version for current shell" complete -c phpenv -f -n __fish_use_subcommand -a use -d "Use PHP version for current shell"
complete -c phpenv -f -n "__fish_use_subcommand" -a "local" -d "Set PHP version for current project" complete -c phpenv -f -n __fish_use_subcommand -a local -d "Set PHP version for current project"
complete -c phpenv -f -n "__fish_use_subcommand" -a "global" -d "Set global PHP version" complete -c phpenv -f -n __fish_use_subcommand -a global -d "Set global PHP version"
complete -c phpenv -f -n "__fish_use_subcommand" -a "list" -d "List installed PHP versions" complete -c phpenv -f -n __fish_use_subcommand -a list -d "List installed PHP versions"
complete -c phpenv -f -n "__fish_use_subcommand" -a "ls" -d "List installed PHP versions" complete -c phpenv -f -n __fish_use_subcommand -a ls -d "List installed PHP versions"
complete -c phpenv -f -n "__fish_use_subcommand" -a "current" -d "Show current PHP version" complete -c phpenv -f -n __fish_use_subcommand -a current -d "Show current PHP version"
complete -c phpenv -f -n "__fish_use_subcommand" -a "which" -d "Show path to PHP binary" complete -c phpenv -f -n __fish_use_subcommand -a which -d "Show path to PHP binary"
complete -c phpenv -f -n "__fish_use_subcommand" -a "versions" -d "Show all available versions" complete -c phpenv -f -n __fish_use_subcommand -a versions -d "Show all available versions"
complete -c phpenv -f -n "__fish_use_subcommand" -a "doctor" -d "Check phpenv installation" complete -c phpenv -f -n __fish_use_subcommand -a doctor -d "Check phpenv installation"
complete -c phpenv -f -n "__fish_use_subcommand" -a "config" -d "Manage configuration" complete -c phpenv -f -n __fish_use_subcommand -a config -d "Manage configuration"
complete -c phpenv -f -n "__fish_use_subcommand" -a "extensions" -d "Manage PHP extensions" complete -c phpenv -f -n __fish_use_subcommand -a extensions -d "Manage PHP extensions"
complete -c phpenv -f -n "__fish_use_subcommand" -a "ext" -d "Manage PHP extensions" complete -c phpenv -f -n __fish_use_subcommand -a ext -d "Manage PHP extensions"
complete -c phpenv -f -n "__fish_use_subcommand" -a "help" -d "Show help" complete -c phpenv -f -n __fish_use_subcommand -a help -d "Show help"
# Helper functions for completions # Helper functions for completions
function __phpenv_complete_installed_versions function __phpenv_complete_installed_versions
@@ -26,13 +26,13 @@ end
function __phpenv_complete_available_versions function __phpenv_complete_available_versions
# Try to get dynamic versions first # Try to get dynamic versions first
if command -q curl -a command -q jq; and functions -q __phpenv_parse_version_field if command -q curl -a command -q jq; and functions -q __phpenv_parse_version_field
echo "latest" echo latest
echo "nightly" echo nightly
echo "5.x" echo "5.x"
echo "7.x" echo "7.x"
echo "8.x" echo "8.x"
__phpenv_parse_version_field "latest" "8.4" __phpenv_parse_version_field latest "8.4"
__phpenv_parse_version_field "nightly" "8.5" __phpenv_parse_version_field nightly "8.5"
__phpenv_parse_version_field "5.x" "5.6" __phpenv_parse_version_field "5.x" "5.6"
__phpenv_parse_version_field "7.x" "7.4" __phpenv_parse_version_field "7.x" "7.4"
__phpenv_parse_version_field "8.x" "8.4" __phpenv_parse_version_field "8.x" "8.4"
@@ -64,18 +64,18 @@ complete -c phpenv -f -n "__fish_seen_subcommand_from uninstall use local global
-a "(__phpenv_complete_installed_versions)" -d "Installed PHP version" -a "(__phpenv_complete_installed_versions)" -d "Installed PHP version"
# Add system option for use command # Add system option for use command
complete -c phpenv -f -n "__fish_seen_subcommand_from use" -a "system" -d "Use system PHP" complete -c phpenv -f -n "__fish_seen_subcommand_from use" -a system -d "Use system PHP"
# Complete binaries for which command # Complete binaries for which command
complete -c phpenv -f -n "__fish_seen_subcommand_from which" -a "(__phpenv_complete_binaries)" -d "PHP binary" complete -c phpenv -f -n "__fish_seen_subcommand_from which" -a "(__phpenv_complete_binaries)" -d "PHP binary"
# Complete config subcommands # Complete config subcommands
complete -c phpenv -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from get set list" \ complete -c phpenv -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from get set list" \
-a "get" -d "Get configuration value" -a get -d "Get configuration value"
complete -c phpenv -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from get set list" \ complete -c phpenv -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from get set list" \
-a "set" -d "Set configuration value" -a set -d "Set configuration value"
complete -c phpenv -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from get set list" \ complete -c phpenv -f -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from get set list" \
-a "list" -d "List all configuration" -a list -d "List all configuration"
# Complete config keys # Complete config keys
complete -c phpenv -f -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from get set" \ complete -c phpenv -f -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from get set" \
@@ -91,27 +91,27 @@ complete -c phpenv -f \
complete -c phpenv -f \ complete -c phpenv -f \
-n "__fish_seen_subcommand_from extensions ext" \ -n "__fish_seen_subcommand_from extensions ext" \
-n "not __fish_seen_subcommand_from install uninstall remove list ls available" \ -n "not __fish_seen_subcommand_from install uninstall remove list ls available" \
-a "install" -d "Install PHP extension" -a install -d "Install PHP extension"
complete -c phpenv -f \ complete -c phpenv -f \
-n "__fish_seen_subcommand_from extensions ext" \ -n "__fish_seen_subcommand_from extensions ext" \
-n "not __fish_seen_subcommand_from install uninstall remove list ls available" \ -n "not __fish_seen_subcommand_from install uninstall remove list ls available" \
-a "uninstall" -d "Uninstall PHP extension" -a uninstall -d "Uninstall PHP extension"
complete -c phpenv -f \ complete -c phpenv -f \
-n "__fish_seen_subcommand_from extensions ext" \ -n "__fish_seen_subcommand_from extensions ext" \
-n "not __fish_seen_subcommand_from install uninstall remove list ls available" \ -n "not __fish_seen_subcommand_from install uninstall remove list ls available" \
-a "remove" -d "Remove PHP extension" -a remove -d "Remove PHP extension"
complete -c phpenv -f \ complete -c phpenv -f \
-n "__fish_seen_subcommand_from extensions ext" \ -n "__fish_seen_subcommand_from extensions ext" \
-n "not __fish_seen_subcommand_from install uninstall remove list ls available" \ -n "not __fish_seen_subcommand_from install uninstall remove list ls available" \
-a "list" -d "List installed extensions" -a list -d "List installed extensions"
complete -c phpenv -f \ complete -c phpenv -f \
-n "__fish_seen_subcommand_from extensions ext" \ -n "__fish_seen_subcommand_from extensions ext" \
-n "not __fish_seen_subcommand_from install uninstall remove list ls available" \ -n "not __fish_seen_subcommand_from install uninstall remove list ls available" \
-a "ls" -d "List installed extensions" -a ls -d "List installed extensions"
complete -c phpenv -f \ complete -c phpenv -f \
-n "__fish_seen_subcommand_from extensions ext" \ -n "__fish_seen_subcommand_from extensions ext" \
-n "not __fish_seen_subcommand_from install uninstall remove list ls available" \ -n "not __fish_seen_subcommand_from install uninstall remove list ls available" \
-a "available" -d "Show available extensions" -a available -d "Show available extensions"
# Complete extension names # Complete extension names
complete -c phpenv -f \ complete -c phpenv -f \

View File

@@ -1,22 +0,0 @@
function ___paths_plugin_set_colors
if not set -q ___paths_plugin_colors
set -Ux ___paths_plugin_colors 27e6ff 29e0ff 5cd8ff 77d0ff 8ac8ff 9cbfff afb5ff c5a7ff d99bfe ea8feb f684d5 fe7abd ff73a3 ff708a fa7070 ff708a ff73a3 fe7abd f684d5 ea8feb d99bfe c5a7ff afb5ff 9cbfff 8ac8ff 77d0ff 5cd8ff 29e0ff
end
return 0
end
function _paths_uninstall --on-event paths_uninstall
for i in ___paths_plugin_wrap_color ___paths_plugin_output ___paths_plugin_handle_found_item ___paths_plugin_handle_source ___paths_plugin_cycle_color
functions -e $i
end
set -e ___paths_plugin_colors
set -e ___paths_plugin_current_color
end
function _paths_install --on-event _paths_install
___paths_plugin_set_colors
end
function _paths_update --on-event paths_update
___paths_plugin_set_colors
end

View File

@@ -23,7 +23,7 @@ if not set -q PHPENV_AUTO_SWITCH
end end
if not set -q PHPENV_DEFAULT_EXTENSIONS if not set -q PHPENV_DEFAULT_EXTENSIONS
set -g PHPENV_DEFAULT_EXTENSIONS "opcache" set -g PHPENV_DEFAULT_EXTENSIONS opcache
end end
# Initialize PATH on shell startup if global version is set (less aggressive) # Initialize PATH on shell startup if global version is set (less aggressive)

View File

@@ -0,0 +1 @@
source "/home/ivuorinen/.local/share/cargo/env.fish"

View File

@@ -2,6 +2,8 @@
# │ fish/config.fish # │ fish/config.fish
# ╰──────────────────────────────────────────────────────────╯ # ╰──────────────────────────────────────────────────────────╯
set -g fish_greeting
fish_config theme choose "Catppuccin Mocha" fish_config theme choose "Catppuccin Mocha"
test -e "$HOME/.config/fish/alias.fish" && test -e "$HOME/.config/fish/alias.fish" &&
@@ -21,6 +23,7 @@ if status is-interactive
source "$HOME/.config/op/plugins.sh" source "$HOME/.config/op/plugins.sh"
# version manager initializers # version manager initializers
type -q mise; and source (mise activate fish|psub)
type -q rbenv; and source (rbenv init -|psub) type -q rbenv; and source (rbenv init -|psub)
type -q pyenv; and source (pyenv init -|psub) type -q pyenv; and source (pyenv init -|psub)
type -q pyenv; and source (pyenv virtualenv-init -|psub) type -q pyenv; and source (pyenv virtualenv-init -|psub)
@@ -32,7 +35,7 @@ if status is-interactive
type -q zoxide; and zoxide init fish | source type -q zoxide; and zoxide init fish | source
# Start tmux if not already running and not in SSH # Start tmux if not already running and not in SSH
#open-tmux # defined in functions/open-tmux.fish #.t # defined in functions/.t.fish
end end
# Added by LM Studio CLI (lms) # Added by LM Studio CLI (lms)
@@ -47,3 +50,8 @@ fish_add_path $HOME/.opencode/bin
# Added by OrbStack: command-line tools and integration # Added by OrbStack: command-line tools and integration
# This won't be added again if you remove it. # This won't be added again if you remove it.
source ~/.orbstack/shell/init2.fish 2>/dev/null || : source ~/.orbstack/shell/init2.fish 2>/dev/null || :
# Warn if GITHUB_TOKEN is not set
if status is-interactive; and not set -q GITHUB_TOKEN
echo "Warning: GITHUB_TOKEN is not set" >&2
end

View File

@@ -17,8 +17,11 @@ set -q HOSTNAME; or set -x HOSTNAME (hostname -s)
# Add local bin to path # Add local bin to path
fish_add_path "$XDG_BIN_HOME" fish_add_path "$XDG_BIN_HOME"
# Add mise shims to path
fish_add_path "$XDG_DATA_HOME/mise/shims"
# Add cargo bin to path # Add cargo bin to path
fish_add_path "$XDG_SHARE_HOME/cargo/bin" fish_add_path "$XDG_DATA_HOME/cargo/bin"
# NPM/NVM configuration # NPM/NVM configuration
set -q NVM_DIR; or set -x NVM_DIR "$XDG_DATA_HOME/nvm" set -q NVM_DIR; or set -x NVM_DIR "$XDG_DATA_HOME/nvm"
@@ -125,7 +128,7 @@ set -q CARGO_BIN_HOME; or set -x CARGO_BIN_HOME "$XDG_BIN_HOME"
set -q RUSTUP_HOME; or set -x RUSTUP_HOME "$XDG_DATA_HOME/rustup" set -q RUSTUP_HOME; or set -x RUSTUP_HOME "$XDG_DATA_HOME/rustup"
set -x RUST_WITHOUT "clippy,docs,rls" set -x RUST_WITHOUT "clippy,docs,rls"
fish_add_path "$CARGO_HOME/bin" fish_add_path "$CARGO_HOME/bin"
fish_add_path "$XDG_SHARE_HOME/bob/nvim-bin" fish_add_path "$XDG_DATA_HOME/bob/nvim-bin"
# screen configuration # screen configuration
set -q SCREENRC; or set -x SCREENRC "$XDG_CONFIG_HOME/misc/screenrc" set -q SCREENRC; or set -x SCREENRC "$XDG_CONFIG_HOME/misc/screenrc"

View File

@@ -3,3 +3,4 @@ jethrokuan/z
ivuorinen/phpenv.fish ivuorinen/phpenv.fish
ilancosman/tide@v6 ilancosman/tide@v6
catppuccin/fish catppuccin/fish
edc/bass

View File

@@ -1,7 +1,7 @@
# Description: Open tmux session if not already open # Description: Open tmux session if not already open
# Dependencies: tmux # Dependencies: tmux
# Usage: open-tmux # Usage: .t
function open-tmux --wraps='tmux attach-session -t main || tmux new-session -s main' --description 'open tmux session' function .t --wraps='tmux attach-session -t main || tmux new-session -s main' --description 'open tmux session'
# Check if not in an SSH session and not already in a tmux session # Check if not in an SSH session and not already in a tmux session
if test -z "$SSH_TTY"; and not set -q TMUX if test -z "$SSH_TTY"; and not set -q TMUX
command tmux attach-session -t main || command tmux new-session -s main command tmux attach-session -t main || command tmux new-session -s main

View File

@@ -7,65 +7,67 @@ To be used with a companion fish function like this:
""" """
from __future__ import print_function
import json import json
import os import os
import signal import signal
import subprocess import subprocess
import sys import sys
import traceback
BASH = "bash"
BASH = 'bash'
FISH_READONLY = [ FISH_READONLY = [
'PWD', 'SHLVL', 'history', 'pipestatus', 'status', 'version', "PWD",
'FISH_VERSION', 'fish_pid', 'hostname', '_', 'fish_private_mode' "SHLVL",
"history",
"pipestatus",
"status",
"version",
"FISH_VERSION",
"fish_pid",
"hostname",
"_",
"fish_private_mode",
] ]
IGNORED = [ IGNORED = ["PS1", "XPC_SERVICE_NAME"]
'PS1', 'XPC_SERVICE_NAME'
]
def ignored(name): def ignored(name):
if name == 'PWD': # this is read only, but has special handling if name == "PWD": # this is read only, but has special handling
return False return False
# ignore other read only variables # ignore other read only variables
if name in FISH_READONLY: if name in FISH_READONLY:
return True return True
if name in IGNORED or name.startswith("BASH_FUNC"): if name in IGNORED or name.startswith("BASH_FUNC"):
return True return True
if name.startswith('%'): return name.startswith("%")
return True
return False
def escape(string): def escape(string):
# use json.dumps to reliably escape quotes and backslashes # use json.dumps to reliably escape quotes and backslashes
return json.dumps(string).replace(r'$', r'\$') return json.dumps(string).replace(r"$", r"\$")
def escape_identifier(word): def escape_identifier(word):
return escape(word.replace('?', '\\?')) return escape(word.replace("?", "\\?"))
def comment(string): def comment(string):
return '\n'.join(['# ' + line for line in string.split('\n')]) return "\n".join(["# " + line for line in string.split("\n")])
def gen_script(): def gen_script():
# Use the following instead of /usr/bin/env to read environment so we can # Use the following instead of /usr/bin/env to read environment so we can
# deal with multi-line environment variables (and other odd cases). # deal with multi-line environment variables (and other odd cases).
env_reader = "%s -c 'import os,json; print(json.dumps({k:v for k,v in os.environ.items()}))'" % (sys.executable) env_reader = f"{sys.executable} -c 'import os,json; print(json.dumps({{k:v for k,v in os.environ.items()}}))'"
args = [BASH, '-c', env_reader] args = [BASH, "-c", env_reader]
output = subprocess.check_output(args, universal_newlines=True) output = subprocess.check_output(args, universal_newlines=True)
old_env = output.strip() old_env = output.strip()
pipe_r, pipe_w = os.pipe() pipe_r, pipe_w = os.pipe()
if sys.version_info >= (3, 4): os.set_inheritable(pipe_w, True)
os.set_inheritable(pipe_w, True) command = f"eval $1 && ({env_reader}; alias) >&{pipe_w}"
command = 'eval $1 && ({}; alias) >&{}'.format( args = [BASH, "-c", command, "bass", " ".join(sys.argv[1:])]
env_reader,
pipe_w
)
args = [BASH, '-c', command, 'bass', ' '.join(sys.argv[1:])]
p = subprocess.Popen(args, universal_newlines=True, close_fds=False) p = subprocess.Popen(args, universal_newlines=True, close_fds=False)
os.close(pipe_w) os.close(pipe_w)
with os.fdopen(pipe_r) as f: with os.fdopen(pipe_r) as f:
@@ -73,9 +75,7 @@ def gen_script():
alias_str = f.read() alias_str = f.read()
if p.wait() != 0: if p.wait() != 0:
raise subprocess.CalledProcessError( raise subprocess.CalledProcessError(
returncode=p.returncode, returncode=p.returncode, cmd=" ".join(sys.argv[1:]), output=new_env + alias_str
cmd=' '.join(sys.argv[1:]),
output=new_env + alias_str
) )
new_env = new_env.strip() new_env = new_env.strip()
@@ -89,41 +89,41 @@ def gen_script():
continue continue
v1 = old_env.get(k) v1 = old_env.get(k)
if not v1: if not v1:
script_lines.append(comment('adding %s=%s' % (k, v))) script_lines.append(comment(f"adding {k}={v}"))
elif v1 != v: elif v1 != v:
script_lines.append(comment('updating %s=%s -> %s' % (k, v1, v))) script_lines.append(comment(f"updating {k}={v1} -> {v}"))
# process special variables # process special variables
if k == 'PWD': if k == "PWD":
script_lines.append('cd %s' % escape(v)) script_lines.append(f"cd {escape(v)}")
continue continue
else: else:
continue continue
if k == 'PATH': if k == "PATH": # noqa: SIM108
value = ' '.join([escape(directory) value = " ".join([escape(directory) for directory in v.split(":")])
for directory in v.split(':')])
else: else:
value = escape(v) value = escape(v)
script_lines.append('set -g -x %s %s' % (k, value)) script_lines.append(f"set -g -x {k} {value}")
for var in set(old_env.keys()) - set(new_env.keys()): for var in set(old_env.keys()) - set(new_env.keys()):
script_lines.append(comment('removing %s' % var)) script_lines.append(comment(f"removing {var}"))
script_lines.append('set -e %s' % var) script_lines.append(f"set -e {var}")
script = '\n'.join(script_lines) script = "\n".join(script_lines)
alias_lines = [] alias_lines = []
for line in alias_str.splitlines(): for line in alias_str.splitlines():
_, rest = line.split(None, 1) _, rest = line.split(None, 1)
k, v = rest.split("=", 1) k, v = rest.split("=", 1)
alias_lines.append("alias " + escape_identifier(k) + "=" + v) alias_lines.append("alias " + escape_identifier(k) + "=" + v)
alias = '\n'.join(alias_lines) alias = "\n".join(alias_lines)
return script + '\n' + alias return script + "\n" + alias
script_file = os.fdopen(3, 'w')
script_file = os.fdopen(3, "w")
if not sys.argv[1:]: if not sys.argv[1:]:
print('__bass_usage', file=script_file, end='') print("__bass_usage", file=script_file, end="")
sys.exit(0) sys.exit(0)
try: try:
@@ -131,8 +131,8 @@ try:
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
sys.exit(e.returncode) sys.exit(e.returncode)
except Exception: except Exception:
print('Bass internal error!', file=sys.stderr) print("Bass internal error!", file=sys.stderr)
raise # traceback will output to stderr raise # traceback will output to stderr
except KeyboardInterrupt: except KeyboardInterrupt:
signal.signal(signal.SIGINT, signal.SIG_DFL) signal.signal(signal.SIGINT, signal.SIG_DFL)
os.kill(os.getpid(), signal.SIGINT) os.kill(os.getpid(), signal.SIGINT)

View File

@@ -1,175 +0,0 @@
function ___paths_plugin_wrap_color
set_color normal
set_color "$argv[1]"
echo -n (set_color "$argv[1]")"$argv[2..]"
set_color normal
end
# duplicated in conf.d
function ___paths_plugin_set_colors
if not set -q ___paths_plugin_colors
set -Ux ___paths_plugin_colors 27e6ff 29e0ff 5cd8ff 77d0ff 8ac8ff 9cbfff afb5ff c5a7ff d99bfe ea8feb f684d5 fe7abd ff73a3 ff708a fa7070 ff708a ff73a3 fe7abd f684d5 ea8feb d99bfe c5a7ff afb5ff 9cbfff 8ac8ff 77d0ff 5cd8ff 29e0ff
end
return 0
end
function ___paths_plugin_cycle_color
if not set -q ___paths_plugin_current_color
set -Ux ___paths_plugin_current_color 1
else if test $___paths_plugin_current_color -gt (count $___paths_plugin_colors)
set -Ux ___paths_plugin_current_color 1
end
echo $___paths_plugin_colors[$___paths_plugin_current_color]
set -Ux ___paths_plugin_current_color (math $___paths_plugin_current_color + 1)
end
function ___paths_plugin_handle_found_item -a testName outFlags
set -f flags (string split -n ' ' -- "$outFlags")
set -f options (fish_opt -s c -l clean)
set -a options (fish_opt -s s -l single)
set -a options (fish_opt -s k -l no-color)
set -a options (fish_opt -s n -l inline)
argparse $options -- $flags
set -f arrow "=>"
# check if file exists
if test -e "$testName"
set -f nameOut (string trim -- "$testName")
if not set -q _flag_c # is not clean
if test -L "$testName" # is symlink
set -f __linkname (readlink -f "$testName")
set __linkname (string trim -- "$__linkname")
set testName (string trim -- "$testName")
if not set -q _flag_k # is color
set nameOut (___paths_plugin_wrap_color (___paths_plugin_cycle_color) $testName) (___paths_plugin_wrap_color "yellow" "$arrow") (___paths_plugin_wrap_color (___paths_plugin_cycle_color) $__linkname)
else # is color
set nameOut (echo -n "$testName" "$arrow" "$__linkname")
end
else # is not symlink
if not set -q _flag_k # is color
set testName (string trim -- "$testName")
set nameOut (___paths_plugin_wrap_color (___paths_plugin_cycle_color) "$testName")
else
set testName (string trim -- "$testName")
set nameOut "$testName"
end
end
set nameOut (string trim -- "$nameOut")
# do the tick
if set -q _flag_k # is not color
set nameOut "- $nameOut"
else # is color
set nameOut (___paths_plugin_wrap_color "yellow" "-") "$nameOut"
end
end
set nameOut (string trim -- "$nameOut")
echo -n $nameOut
end
end
function paths --description "Reveal the executable matches in shell paths or fish autoload."
set -f options (fish_opt -s c -l clean)
set -a options (fish_opt -s s -l single)
set -a options (fish_opt -s k -l no-color)
set -a options (fish_opt -s q -l quiet)
set -a options (fish_opt -s n -l inline)
argparse $options -- $argv
if test (count $argv) -lt 1
echo "paths - executable matches in shell paths or fish autoload."
and echo "usage: paths [-c|-s|-k] <name>"
and echo -e "\t-c or --no-color: output without color"
and echo -e "\t-s or --single: output without color or headers, the first result"
and echo -e "\t-k or --clean: output without tick marks or headers"
# and echo -e "\t-n or --inline: output without endline"
and return 1
end
set -f foundStatus 1
set -f input (string trim -- $argv)
# deprecated
if set -q _flag_q
set _flag_c True
end
if set -q _flag_s
set _flag_k True
set _flag_c True
end
set -f outFlags ''
set -q _flag_n; and set -a outFlags -n
set -q _flag_c; and set -a outFlags -c
set -q _flag_k; and set -a outFlags -k
set -q _flag_s; and set -a outFlags -s
set outFlags (string split -n " " -- "$outFlags")
___paths_plugin_set_colors
# loop over list of path lists
for pVar in VIRTUAL_ENV fisher_path fish_function_path fish_user_paths PATH
set -e acc
set -f acc ''
set -e hit
# see if variable is empty
if test -z "$pVar"
continue
end
set -f acc (begin
for t in $$pVar
for snit in "$t/$input.fish" "$t/$input"
set -f found (___paths_plugin_handle_found_item "$snit" "$outFlags")
set found (string trim -- "$found")
if test -n "$found"
set -f hit True
echo "$found"
if set -q _flag_s
break
end
end
end
if set -q _flag_s
if set -q hit
break
end
end
end
end)
# prepend source
if not set -q _flag_c
if set -q hit
set pVar (string trim -- "$pVar")
echo -e -n "$pVar\n"
end
end
if test -n "$acc"
set foundStatus 0
for fk in $acc
echo $fk
if set -q _flag_s
# stop after one
return $foundStatus
end
end
end
end
# check
set -l built (type --type $input 12&>/dev/null)
if test -n "$built"
and test "$built" = builtin
set $foundStatus 0
if not set -q _flag_c
echo -e -n "builtin\n"
if set -q _flag_k
echo - "$input"
else # is color
echo (___paths_plugin_wrap_color "yellow" "-") (___paths_plugin_wrap_color (___paths_plugin_cycle_color) "$input")
end
else
echo "$input"
end
end
return $foundStatus
end

View File

@@ -103,7 +103,7 @@ end
function __phpenv_find_version_file -a phpenv_filename function __phpenv_find_version_file -a phpenv_filename
set -l phpenv_dir (pwd) set -l phpenv_dir (pwd)
while test "$phpenv_dir" != "/" while test "$phpenv_dir" != /
if test -f "$phpenv_dir/$phpenv_filename" if test -f "$phpenv_dir/$phpenv_filename"
echo "$phpenv_dir/$phpenv_filename" echo "$phpenv_dir/$phpenv_filename"
return return
@@ -130,13 +130,13 @@ function __phpenv_parse_composer_version
end end
set -l phpenv_platform_php (jq -r '.config.platform.php // empty' composer.json 2>/dev/null) set -l phpenv_platform_php (jq -r '.config.platform.php // empty' composer.json 2>/dev/null)
if test $status -eq 0 -a -n "$phpenv_platform_php" -a "$phpenv_platform_php" != "null" if test $status -eq 0 -a -n "$phpenv_platform_php" -a "$phpenv_platform_php" != null
echo $phpenv_platform_php echo $phpenv_platform_php
return return
end end
set -l phpenv_require_php (jq -r '.require.php // empty' composer.json 2>/dev/null) set -l phpenv_require_php (jq -r '.require.php // empty' composer.json 2>/dev/null)
if test $status -eq 0 -a -n "$phpenv_require_php" -a "$phpenv_require_php" != "null" if test $status -eq 0 -a -n "$phpenv_require_php" -a "$phpenv_require_php" != null
__phpenv_parse_semver_constraint $phpenv_require_php __phpenv_parse_semver_constraint $phpenv_require_php
return return
end end
@@ -196,7 +196,7 @@ set -g __phpenv_version_cache_time 0
function __phpenv_get_version_info function __phpenv_get_version_info
set -l current_time (date +%s) set -l current_time (date +%s)
set -l cache_duration 300 # 5 minutes set -l cache_duration 300 # 5 minutes
# Return cached version if still valid # Return cached version if still valid
if test -n "$__phpenv_version_cache" if test -n "$__phpenv_version_cache"
@@ -236,12 +236,12 @@ end
# Check if Ondřej PPA is configured on the system # Check if Ondřej PPA is configured on the system
function __phpenv_has_ondrej_ppa function __phpenv_has_ondrej_ppa
if test -d /etc/apt/sources.list.d if test -d /etc/apt/sources.list.d
if grep -rq "ondrej/php" /etc/apt/sources.list.d/ 2>/dev/null if grep -rq ondrej/php /etc/apt/sources.list.d/ 2>/dev/null
return 0 return 0
end end
end end
if test -f /etc/apt/sources.list if test -f /etc/apt/sources.list
if grep -q "ondrej/php" /etc/apt/sources.list 2>/dev/null if grep -q ondrej/php /etc/apt/sources.list 2>/dev/null
return 0 return 0
end end
end end
@@ -262,34 +262,34 @@ function __phpenv_get_provider
end end
# macOS always uses Homebrew # macOS always uses Homebrew
if test (uname -s) = "Darwin" if test (uname -s) = Darwin
echo "homebrew" echo homebrew
return 0 return 0
end end
# Linux: check for apt with Ondřej PPA first # Linux: check for apt with Ondřej PPA first
if test (uname -s) = "Linux" if test (uname -s) = Linux
if command -q apt-get; and __phpenv_has_ondrej_ppa if command -q apt-get; and __phpenv_has_ondrej_ppa
echo "apt" echo apt
return 0 return 0
end end
# Fall back to Homebrew (Linuxbrew) if available # Fall back to Homebrew (Linuxbrew) if available
if command -q brew if command -q brew
echo "homebrew" echo homebrew
return 0 return 0
end end
# If apt is available but no PPA yet, still use apt provider # If apt is available but no PPA yet, still use apt provider
# (it will prompt to add the PPA when needed) # (it will prompt to add the PPA when needed)
if command -q apt-get if command -q apt-get
echo "apt" echo apt
return 0 return 0
end end
end end
# Default fallback # Default fallback
echo "homebrew" echo homebrew
return 0 return 0
end end
@@ -319,7 +319,7 @@ function __phpenv_provider_homebrew_ensure_source
end end
# Check and add required taps only if missing # Check and add required taps only if missing
set -l required_taps "shivammathur/php" "shivammathur/extensions" set -l required_taps shivammathur/php shivammathur/extensions
for tap in $required_taps for tap in $required_taps
if not brew tap | grep -qx $tap 2>/dev/null if not brew tap | grep -qx $tap 2>/dev/null
if not brew tap $tap 2>/dev/null if not brew tap $tap 2>/dev/null
@@ -343,7 +343,7 @@ function __phpenv_provider_homebrew_list_installed
continue continue
end end
if test "$phpenv_basename" = "php" if test "$phpenv_basename" = php
set -l phpenv_latest (__phpenv_parse_version_field "latest" "8.4") set -l phpenv_latest (__phpenv_parse_version_field "latest" "8.4")
set -a phpenv_versions $phpenv_latest set -a phpenv_versions $phpenv_latest
else if echo $phpenv_basename | grep -qE '^php@[0-9]+\.[0-9]+$' else if echo $phpenv_basename | grep -qE '^php@[0-9]+\.[0-9]+$'
@@ -375,7 +375,7 @@ function __phpenv_provider_homebrew_list_available
continue continue
end end
if test "$phpenv_clean_name" = "php" if test "$phpenv_clean_name" = php
set -a phpenv_versions "$phpenv_latest_version (latest)" set -a phpenv_versions "$phpenv_latest_version (latest)"
else if echo $phpenv_clean_name | grep -qE '^php@[0-9]+\.[0-9]+$' else if echo $phpenv_clean_name | grep -qE '^php@[0-9]+\.[0-9]+$'
set -l phpenv_version (echo $phpenv_clean_name | sed 's/php@//') set -l phpenv_version (echo $phpenv_clean_name | sed 's/php@//')
@@ -502,7 +502,7 @@ function __phpenv_provider_homebrew_ext_list -a phpenv_version
for phpenv_ext_dir in $phpenv_cellar_path/*@$phpenv_version for phpenv_ext_dir in $phpenv_cellar_path/*@$phpenv_version
if test -d $phpenv_ext_dir if test -d $phpenv_ext_dir
set -l phpenv_ext_name (basename $phpenv_ext_dir | sed "s/@$phpenv_version//") set -l phpenv_ext_name (basename $phpenv_ext_dir | sed "s/@$phpenv_version//")
if test "$phpenv_ext_name" != "php" if test "$phpenv_ext_name" != php
echo $phpenv_ext_name echo $phpenv_ext_name
end end
end end
@@ -574,7 +574,7 @@ function __phpenv_provider_apt_ensure_source
echo "" echo ""
read -P "Add ppa:ondrej/php? [y/N] " -l confirm read -P "Add ppa:ondrej/php? [y/N] " -l confirm
if test "$confirm" = "y" -o "$confirm" = "Y" if test "$confirm" = y -o "$confirm" = Y
echo "Adding ppa:ondrej/php..." echo "Adding ppa:ondrej/php..."
if command -q add-apt-repository if command -q add-apt-repository
if sudo add-apt-repository -y ppa:ondrej/php if sudo add-apt-repository -y ppa:ondrej/php
@@ -603,8 +603,7 @@ function __phpenv_provider_apt_list_installed
return 1 return 1
end end
dpkg -l 'php[0-9]*-cli' 2>/dev/null | grep '^ii' | \ dpkg -l 'php[0-9]*-cli' 2>/dev/null | grep '^ii' | sed -E 's/^ii\s+php([0-9]+\.[0-9]+)-cli.*/\1/' | sort -V | uniq
sed -E 's/^ii\s+php([0-9]+\.[0-9]+)-cli.*/\1/' | sort -V | uniq
end end
function __phpenv_provider_apt_list_available function __phpenv_provider_apt_list_available
@@ -613,8 +612,7 @@ function __phpenv_provider_apt_list_available
return return
end end
apt-cache search '^php[0-9]+\.[0-9]+-cli$' 2>/dev/null | \ apt-cache search '^php[0-9]+\.[0-9]+-cli$' 2>/dev/null | sed -E 's/^php([0-9]+\.[0-9]+)-cli.*/\1/' | sort -V | uniq
sed -E 's/^php([0-9]+\.[0-9]+)-cli.*/\1/' | sort -V | uniq
end end
function __phpenv_provider_apt_get_php_path -a phpenv_version function __phpenv_provider_apt_get_php_path -a phpenv_version
@@ -644,7 +642,7 @@ function __phpenv_provider_apt_get_php_path -a phpenv_version
set -l temp_link "$target.$fish_pid" set -l temp_link "$target.$fish_pid"
ln -s "$source" "$temp_link" 2>/dev/null ln -s "$source" "$temp_link" 2>/dev/null
and mv -f "$temp_link" "$target" 2>/dev/null and mv -f "$temp_link" "$target" 2>/dev/null
else if test "$binary" = "phar"; and test -x "/usr/bin/phar$phpenv_version" else if test "$binary" = phar; and test -x "/usr/bin/phar$phpenv_version"
set -l temp_link "$target.$fish_pid" set -l temp_link "$target.$fish_pid"
ln -s "/usr/bin/phar$phpenv_version" "$temp_link" 2>/dev/null ln -s "/usr/bin/phar$phpenv_version" "$temp_link" 2>/dev/null
and mv -f "$temp_link" "$target" 2>/dev/null and mv -f "$temp_link" "$target" 2>/dev/null
@@ -819,8 +817,7 @@ function __phpenv_provider_apt_ext_list -a phpenv_version
# Filter out core packages (cli, common, etc.) # Filter out core packages (cli, common, etc.)
set -l core_packages cli common opcache fpm cgi phpdbg set -l core_packages cli common opcache fpm cgi phpdbg
dpkg -l "php$phpenv_version-*" 2>/dev/null | grep '^ii' | awk '{print $2}' | \ dpkg -l "php$phpenv_version-*" 2>/dev/null | grep '^ii' | awk '{print $2}' | sed "s/php$phpenv_version-//" | while read ext
sed "s/php$phpenv_version-//" | while read ext
# Skip core packages # Skip core packages
set -l is_core 0 set -l is_core 0
for core in $core_packages for core in $core_packages
@@ -837,9 +834,7 @@ end
function __phpenv_provider_apt_ext_available -a phpenv_version function __phpenv_provider_apt_ext_available -a phpenv_version
# List available extensions from apt cache # List available extensions from apt cache
apt-cache search "^php$phpenv_version-" 2>/dev/null | \ apt-cache search "^php$phpenv_version-" 2>/dev/null | sed "s/php$phpenv_version-//" | awk '{print $1}' | grep -v -E '^(cli|common|fpm|cgi|phpdbg|dev)$' | sort | uniq
sed "s/php$phpenv_version-//" | awk '{print $1}' | \
grep -v -E '^(cli|common|fpm|cgi|phpdbg|dev)$' | sort | uniq
end end
function __phpenv_provider_apt_get_path_pattern function __phpenv_provider_apt_get_path_pattern
@@ -979,9 +974,9 @@ end
function __phpenv_resolve_version_alias -a phpenv_version function __phpenv_resolve_version_alias -a phpenv_version
switch $phpenv_version switch $phpenv_version
case latest case latest
__phpenv_parse_version_field "latest" "8.4" __phpenv_parse_version_field latest "8.4"
case nightly case nightly
__phpenv_parse_version_field "nightly" "8.5" __phpenv_parse_version_field nightly "8.5"
case '8.x' case '8.x'
__phpenv_parse_version_field "8.x" "8.4" __phpenv_parse_version_field "8.x" "8.4"
case '7.x' case '7.x'
@@ -997,7 +992,7 @@ function __phpenv_get_formula_name -a phpenv_version
set -l phpenv_latest_version (__phpenv_parse_version_field "latest" "8.4") set -l phpenv_latest_version (__phpenv_parse_version_field "latest" "8.4")
if test "$phpenv_version" = "$phpenv_latest_version" if test "$phpenv_version" = "$phpenv_latest_version"
echo "shivammathur/php/php" echo shivammathur/php/php
else else
echo "shivammathur/php/php@$phpenv_version" echo "shivammathur/php/php@$phpenv_version"
end end
@@ -1140,7 +1135,7 @@ function __phpenv_use
set -l phpenv_version $argv[1] set -l phpenv_version $argv[1]
# Handle special case: restore system PHP # Handle special case: restore system PHP
if test "$phpenv_version" = "system" if test "$phpenv_version" = system
__phpenv_restore_system_path __phpenv_restore_system_path
echo "Restored system PHP" echo "Restored system PHP"
return 0 return 0
@@ -1157,7 +1152,7 @@ function __phpenv_use
end end
if not __phpenv_is_version_installed $phpenv_version if not __phpenv_is_version_installed $phpenv_version
if test "$(__phpenv_config_get auto-install)" = "true" if test "$(__phpenv_config_get auto-install)" = true
__phpenv_install $phpenv_version __phpenv_install $phpenv_version
else else
echo "PHP $phpenv_version is not installed. Install with: phpenv install $phpenv_version" echo "PHP $phpenv_version is not installed. Install with: phpenv install $phpenv_version"
@@ -1179,7 +1174,7 @@ function __phpenv_local -a phpenv_version
return 1 return 1
end end
echo $phpenv_version > .php-version echo $phpenv_version >.php-version
echo "Set local PHP version to $phpenv_version" echo "Set local PHP version to $phpenv_version"
end end
@@ -1272,7 +1267,7 @@ function __phpenv_get_tap_versions
continue continue
end end
if test "$phpenv_clean_name" = "php" if test "$phpenv_clean_name" = php
set -a phpenv_versions "$phpenv_latest_version (latest)" set -a phpenv_versions "$phpenv_latest_version (latest)"
else if echo $phpenv_clean_name | grep -qE '^php@[0-9]+\.[0-9]+$' else if echo $phpenv_clean_name | grep -qE '^php@[0-9]+\.[0-9]+$'
set -l phpenv_version (echo $phpenv_clean_name | sed 's/php@//') set -l phpenv_version (echo $phpenv_clean_name | sed 's/php@//')
@@ -1310,7 +1305,7 @@ function __phpenv_doctor
# Show provider information # Show provider information
set -l provider (__phpenv_get_provider) set -l provider (__phpenv_get_provider)
set -l provider_source "auto-detected" set -l provider_source auto-detected
if set -q PHPENV_PROVIDER; and test -n "$PHPENV_PROVIDER" if set -q PHPENV_PROVIDER; and test -n "$PHPENV_PROVIDER"
set provider_source "PHPENV_PROVIDER override" set provider_source "PHPENV_PROVIDER override"
end end
@@ -1429,7 +1424,7 @@ function __phpenv_config_get -a phpenv_key
end end
end end
if test "$argv[2]" = "--verbose" if test "$argv[2]" = --verbose
if test -n "$phpenv_value" if test -n "$phpenv_value"
echo "$phpenv_key = $phpenv_value (from $phpenv_source)" echo "$phpenv_key = $phpenv_value (from $phpenv_source)"
else else
@@ -1581,19 +1576,18 @@ function __phpenv_get_tap_formulas -a tap_name
return 1 return 1
end end
brew tap-info $tap_name --json 2>/dev/null | \ brew tap-info $tap_name --json 2>/dev/null | jq -r '.[]|(.formula_names[]?)' 2>/dev/null
jq -r '.[]|(.formula_names[]?)' 2>/dev/null
end end
function __phpenv_get_available_extensions function __phpenv_get_available_extensions
__phpenv_get_tap_formulas "shivammathur/extensions" __phpenv_get_tap_formulas shivammathur/extensions
end end
function __phpenv_extension_available -a phpenv_extension phpenv_version function __phpenv_extension_available -a phpenv_extension phpenv_version
set -l phpenv_available_extensions (__phpenv_get_available_extensions) set -l phpenv_available_extensions (__phpenv_get_available_extensions)
if test -z "$phpenv_available_extensions" if test -z "$phpenv_available_extensions"
return 0 # Assume available if can't check return 0 # Assume available if can't check
end end
for phpenv_ext_formula in $phpenv_available_extensions for phpenv_ext_formula in $phpenv_available_extensions
@@ -1695,7 +1689,7 @@ function __phpenv_auto_switch --on-variable PWD
end end
set -l phpenv_auto_switch (__phpenv_config_get auto-switch) set -l phpenv_auto_switch (__phpenv_config_get auto-switch)
if test "$phpenv_auto_switch" = "false" if test "$phpenv_auto_switch" = false
return 0 return 0
end end
@@ -1718,7 +1712,7 @@ function __phpenv_auto_switch --on-variable PWD
set -g PHPENV_LAST_SWITCH_TIME $phpenv_current_time set -g PHPENV_LAST_SWITCH_TIME $phpenv_current_time
else else
set -l phpenv_auto_install (__phpenv_config_get auto-install) set -l phpenv_auto_install (__phpenv_config_get auto-install)
if test "$phpenv_auto_install" = "true" if test "$phpenv_auto_install" = true
echo "Auto-installing PHP $phpenv_new_version..." echo "Auto-installing PHP $phpenv_new_version..."
if phpenv install "$phpenv_new_version" if phpenv install "$phpenv_new_version"
set -g PHPENV_LAST_SWITCH_TIME $phpenv_current_time set -g PHPENV_LAST_SWITCH_TIME $phpenv_current_time
@@ -1771,7 +1765,7 @@ function __phpenv_help
end end
function __phpenv_validate_boolean -a phpenv_value function __phpenv_validate_boolean -a phpenv_value
test "$phpenv_value" = "true" -o "$phpenv_value" = "false" test "$phpenv_value" = true -o "$phpenv_value" = false
end end
function __phpenv_validate_version -a phpenv_version function __phpenv_validate_version -a phpenv_version

View File

@@ -58,4 +58,3 @@ fish_pager_color_progress 737994
fish_pager_color_prefix f4b8e4 fish_pager_color_prefix f4b8e4
fish_pager_color_completion c6d0f5 fish_pager_color_completion c6d0f5
fish_pager_color_description 737994 fish_pager_color_description 737994

View File

@@ -58,4 +58,3 @@ fish_pager_color_progress 6e738d
fish_pager_color_prefix f5bde6 fish_pager_color_prefix f5bde6
fish_pager_color_completion cad3f5 fish_pager_color_completion cad3f5
fish_pager_color_description 6e738d fish_pager_color_description 6e738d

View File

@@ -58,4 +58,3 @@ fish_pager_color_progress 6c7086
fish_pager_color_prefix f5c2e7 fish_pager_color_prefix f5c2e7
fish_pager_color_completion cdd6f4 fish_pager_color_completion cdd6f4
fish_pager_color_description 6c7086 fish_pager_color_description 6c7086

View File

@@ -13,32 +13,37 @@
if [[ $- =~ i ]]; then if [[ $- =~ i ]]; then
# To use custom commands instead of find, override _fzf_compgen_{path,dir} # To use custom commands instead of find, override _fzf_compgen_{path,dir}
if ! declare -f _fzf_compgen_path >/dev/null; then if ! declare -f _fzf_compgen_path > /dev/null; then
_fzf_compgen_path() { _fzf_compgen_path()
{
echo "$1" echo "$1"
command find -L "$1" \ command find -L "$1" \
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \ -name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
-a -not -path "$1" -print 2>/dev/null | sed 's@^\./@@' -a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
} }
fi fi
if ! declare -f _fzf_compgen_dir >/dev/null; then if ! declare -f _fzf_compgen_dir > /dev/null; then
_fzf_compgen_dir() { _fzf_compgen_dir()
{
command find -L "$1" \ command find -L "$1" \
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \ -name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
-a -not -path "$1" -print 2>/dev/null | sed 's@^\./@@' -a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
} }
fi fi
########################################################### ###########################################################
# To redraw line after fzf closes (printf '\e[5n') # To redraw line after fzf closes (printf '\e[5n')
bind '"\e[0n": redraw-current-line' 2>/dev/null bind '"\e[0n": redraw-current-line' 2> /dev/null
__fzf_comprun() { __fzf_comprun()
{
if [[ "$(type -t _fzf_comprun 2>&1)" = function ]]; then if [[ "$(type -t _fzf_comprun 2>&1)" = function ]]; then
_fzf_comprun "$@" _fzf_comprun "$@"
elif [[ -n "${TMUX_PANE-}" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "${FZF_TMUX_OPTS-}" ]]; }; then elif [[ -n "${TMUX_PANE-}" ]] && {
[[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "${FZF_TMUX_OPTS-}" ]]
}; then
shift shift
fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- "$@" fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- "$@"
else else
@@ -47,7 +52,8 @@ if [[ $- =~ i ]]; then
fi fi
} }
__fzf_orig_completion() { __fzf_orig_completion()
{
local l comp f cmd local l comp f cmd
while read -r l; do while read -r l; do
if [[ "$l" =~ ^(.*\ -F)\ *([^ ]*).*\ ([^ ]*)$ ]]; then if [[ "$l" =~ ^(.*\ -F)\ *([^ ]*).*\ ([^ ]*)$ ]]; then
@@ -63,7 +69,8 @@ if [[ $- =~ i ]]; then
done done
} }
_fzf_opts_completion() { _fzf_opts_completion()
{
local cur prev opts local cur prev opts
COMPREPLY=() COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}" cur="${COMP_WORDS[COMP_CWORD]}"
@@ -112,18 +119,18 @@ if [[ $- =~ i ]]; then
--sync" --sync"
case "${prev}" in case "${prev}" in
--tiebreak) --tiebreak)
COMPREPLY=($(compgen -W "length begin end index" -- "$cur")) COMPREPLY=($(compgen -W "length begin end index" -- "$cur"))
return 0 return 0
;; ;;
--color) --color)
COMPREPLY=($(compgen -W "dark light 16 bw" -- "$cur")) COMPREPLY=($(compgen -W "dark light 16 bw" -- "$cur"))
return 0 return 0
;; ;;
--history) --history)
COMPREPLY=() COMPREPLY=()
return 0 return 0
;; ;;
esac esac
if [[ "$cur" =~ ^-|\+ ]]; then if [[ "$cur" =~ ^-|\+ ]]; then
@@ -134,7 +141,8 @@ if [[ $- =~ i ]]; then
return 0 return 0
} }
_fzf_handle_dynamic_completion() { _fzf_handle_dynamic_completion()
{
local cmd orig_var orig ret orig_cmd orig_complete local cmd orig_var orig ret orig_cmd orig_complete
cmd="$1" cmd="$1"
shift shift
@@ -142,15 +150,15 @@ if [[ $- =~ i ]]; then
orig_var="_fzf_orig_completion_$cmd" orig_var="_fzf_orig_completion_$cmd"
orig="${!orig_var-}" orig="${!orig_var-}"
orig="${orig##*#}" orig="${orig##*#}"
if [[ -n "$orig" ]] && type "$orig" >/dev/null 2>&1; then if [[ -n "$orig" ]] && type "$orig" > /dev/null 2>&1; then
$orig "$@" $orig "$@"
elif [[ -n "${_fzf_completion_loader-}" ]]; then elif [[ -n "${_fzf_completion_loader-}" ]]; then
orig_complete=$(complete -p "$orig_cmd" 2>/dev/null) orig_complete=$(complete -p "$orig_cmd" 2> /dev/null)
_completion_loader "$@" _completion_loader "$@"
ret=$? ret=$?
# _completion_loader may not have updated completion for the command # _completion_loader may not have updated completion for the command
if [[ "$(complete -p "$orig_cmd" 2>/dev/null)" != "$orig_complete" ]]; then if [[ "$(complete -p "$orig_cmd" 2> /dev/null)" != "$orig_complete" ]]; then
__fzf_orig_completion < <(complete -p "$orig_cmd" 2>/dev/null) __fzf_orig_completion < <(complete -p "$orig_cmd" 2> /dev/null)
if [[ "${__fzf_nospace_commands-}" = *" $orig_cmd "* ]]; then if [[ "${__fzf_nospace_commands-}" = *" $orig_cmd "* ]]; then
eval "${orig_complete/ -F / -o nospace -F }" eval "${orig_complete/ -F / -o nospace -F }"
else else
@@ -161,7 +169,8 @@ if [[ $- =~ i ]]; then
fi fi
} }
__fzf_generic_path_completion() { __fzf_generic_path_completion()
{
local cur base dir leftover matches trigger cmd local cur base dir leftover matches trigger cmd
cmd="${COMP_WORDS[0]}" cmd="${COMP_WORDS[0]}"
if [[ $cmd == \\* ]]; then if [[ $cmd == \\* ]]; then
@@ -207,7 +216,8 @@ if [[ $- =~ i ]]; then
fi fi
} }
_fzf_complete() { _fzf_complete()
{
# Split arguments around -- # Split arguments around --
local args rest str_arg i sep local args rest str_arg i sep
args=("$@") args=("$@")
@@ -231,7 +241,7 @@ if [[ $- =~ i ]]; then
local cur selected trigger cmd post local cur selected trigger cmd post
post="$(caller 0 | awk '{print $2}')_post" post="$(caller 0 | awk '{print $2}')_post"
type -t "$post" >/dev/null 2>&1 || post=cat type -t "$post" > /dev/null 2>&1 || post=cat
cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}" cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
trigger=${FZF_COMPLETION_TRIGGER-'**'} trigger=${FZF_COMPLETION_TRIGGER-'**'}
@@ -253,50 +263,59 @@ if [[ $- =~ i ]]; then
fi fi
} }
_fzf_path_completion() { _fzf_path_completion()
{
__fzf_generic_path_completion _fzf_compgen_path "-m" "" "$@" __fzf_generic_path_completion _fzf_compgen_path "-m" "" "$@"
} }
# Deprecated. No file only completion. # Deprecated. No file only completion.
_fzf_file_completion() { _fzf_file_completion()
{
_fzf_path_completion "$@" _fzf_path_completion "$@"
} }
_fzf_dir_completion() { _fzf_dir_completion()
{
__fzf_generic_path_completion _fzf_compgen_dir "" "/" "$@" __fzf_generic_path_completion _fzf_compgen_dir "" "/" "$@"
} }
_fzf_complete_kill() { _fzf_complete_kill()
{
_fzf_proc_completion "$@" _fzf_proc_completion "$@"
} }
_fzf_proc_completion() { _fzf_proc_completion()
{
_fzf_complete -m --header-lines=1 --preview 'echo {}' --preview-window down:3:wrap --min-height 15 -- "$@" < <( _fzf_complete -m --header-lines=1 --preview 'echo {}' --preview-window down:3:wrap --min-height 15 -- "$@" < <(
command ps -eo user,pid,ppid,start,time,command 2>/dev/null || command ps -eo user,pid,ppid,start,time,command 2> /dev/null \
command ps -eo user,pid,ppid,time,args # For BusyBox || command ps -eo user,pid,ppid,time,args # For BusyBox
) )
} }
_fzf_proc_completion_post() { _fzf_proc_completion_post()
{
awk '{print $2}' awk '{print $2}'
} }
_fzf_host_completion() { _fzf_host_completion()
{
_fzf_complete +m -- "$@" < <( _fzf_complete +m -- "$@" < <(
command cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2>/dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?%]') \ command cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?%]') \
<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \ <(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
<(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') | <(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') \
awk '{if (length($2) > 0) {print $2}}' | sort -u | awk '{if (length($2) > 0) {print $2}}' | sort -u
) )
} }
_fzf_var_completion() { _fzf_var_completion()
{
_fzf_complete -m -- "$@" < <( _fzf_complete -m -- "$@" < <(
declare -xp | sed -En 's|^declare [^ ]+ ([^=]+).*|\1|p' declare -xp | sed -En 's|^declare [^ ]+ ([^=]+).*|\1|p'
) )
} }
_fzf_alias_completion() { _fzf_alias_completion()
{
_fzf_complete -m -- "$@" < <( _fzf_complete -m -- "$@" < <(
alias | sed -En 's|^alias ([^=]+).*|\1|p' alias | sed -En 's|^alias ([^=]+).*|\1|p'
) )
@@ -321,13 +340,14 @@ if [[ $- =~ i ]]; then
svn tar unzip zip" svn tar unzip zip"
# Preserve existing completion # Preserve existing completion
__fzf_orig_completion < <(complete -p $d_cmds $a_cmds 2>/dev/null) __fzf_orig_completion < <(complete -p $d_cmds $a_cmds 2> /dev/null)
if type _completion_loader >/dev/null 2>&1; then if type _completion_loader > /dev/null 2>&1; then
_fzf_completion_loader=1 _fzf_completion_loader=1
fi fi
__fzf_defc() { __fzf_defc()
{
local cmd func opts orig_var orig def local cmd func opts orig_var orig def
cmd="$1" cmd="$1"
func="$2" func="$2"
@@ -354,22 +374,23 @@ if [[ $- =~ i ]]; then
unset cmd d_cmds a_cmds unset cmd d_cmds a_cmds
_fzf_setup_completion() { _fzf_setup_completion()
{
local kind fn cmd local kind fn cmd
kind=$1 kind=$1
fn=_fzf_${1}_completion fn=_fzf_${1}_completion
if [[ $# -lt 2 ]] || ! type -t "$fn" >/dev/null; then if [[ $# -lt 2 ]] || ! type -t "$fn" > /dev/null; then
echo "usage: ${FUNCNAME[0]} path|dir|var|alias|host|proc COMMANDS..." echo "usage: ${FUNCNAME[0]} path|dir|var|alias|host|proc COMMANDS..."
return 1 return 1
fi fi
shift shift
__fzf_orig_completion < <(complete -p "$@" 2>/dev/null) __fzf_orig_completion < <(complete -p "$@" 2> /dev/null)
for cmd in "$@"; do for cmd in "$@"; do
case "$kind" in case "$kind" in
dir) __fzf_defc "$cmd" "$fn" "-o nospace -o dirnames" ;; dir) __fzf_defc "$cmd" "$fn" "-o nospace -o dirnames" ;;
var) __fzf_defc "$cmd" "$fn" "-o default -o nospace -v" ;; var) __fzf_defc "$cmd" "$fn" "-o default -o nospace -v" ;;
alias) __fzf_defc "$cmd" "$fn" "-a" ;; alias) __fzf_defc "$cmd" "$fn" "-a" ;;
*) __fzf_defc "$cmd" "$fn" "-o default -o bashdefault" ;; *) __fzf_defc "$cmd" "$fn" "-o default -o bashdefault" ;;
esac esac
done done
} }

View File

@@ -4,7 +4,7 @@
# Auto-completion # Auto-completion
# --------------- # ---------------
# shellcheck source=completion.bash # shellcheck source=completion.bash
[[ $- == *i* ]] && source "$HOME/.dotfiles/config/fzf/completion.bash" 2>/dev/null [[ $- == *i* ]] && source "$HOME/.dotfiles/config/fzf/completion.bash" 2> /dev/null
# Key bindings # Key bindings
# ------------ # ------------

View File

@@ -13,7 +13,8 @@
# Key bindings # Key bindings
# ------------ # ------------
__fzf_select__() { __fzf_select__()
{
local cmd opts local cmd opts
cmd="${FZF_CTRL_T_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \ cmd="${FZF_CTRL_T_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
-o -type f -print \ -o -type f -print \
@@ -21,27 +22,32 @@ __fzf_select__() {
-o -type l -print 2> /dev/null | cut -b3-"}" -o -type l -print 2> /dev/null | cut -b3-"}"
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse ${FZF_DEFAULT_OPTS-} ${FZF_CTRL_T_OPTS-} -m" opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse ${FZF_DEFAULT_OPTS-} ${FZF_CTRL_T_OPTS-} -m"
# shellcheck disable=SC2091 # Intentionally execute output of __fzfcmd # shellcheck disable=SC2091 # Intentionally execute output of __fzfcmd
eval "$cmd" | FZF_DEFAULT_OPTS="$opts" $(__fzfcmd) "$@" | eval "$cmd" | FZF_DEFAULT_OPTS="$opts" $(__fzfcmd) "$@" \
while read -r item; do | while read -r item; do
printf '%q ' "$item" # escape special chars printf '%q ' "$item" # escape special chars
done done
} }
if [[ $- =~ i ]]; then if [[ $- =~ i ]]; then
__fzfcmd() { __fzfcmd()
[[ -n "${TMUX_PANE-}" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "${FZF_TMUX_OPTS-}" ]]; } && {
echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf" [[ -n "${TMUX_PANE-}" ]] && {
[[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "${FZF_TMUX_OPTS-}" ]]
} \
&& echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf"
} }
fzf-file-widget() { fzf-file-widget()
{
local selected local selected
selected="$(__fzf_select__ "$@")" selected="$(__fzf_select__ "$@")"
READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}" READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
READLINE_POINT=$((READLINE_POINT + ${#selected})) READLINE_POINT=$((READLINE_POINT + ${#selected}))
} }
__fzf_cd__() { __fzf_cd__()
{
local cmd opts dir local cmd opts dir
cmd="${FZF_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \ cmd="${FZF_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
-o -type d -print 2> /dev/null | cut -b3-"}" -o -type d -print 2> /dev/null | cut -b3-"}"
@@ -53,16 +59,17 @@ if [[ $- =~ i ]]; then
) && printf 'builtin cd -- %q' "$dir" ) && printf 'builtin cd -- %q' "$dir"
} }
__fzf_history__() { __fzf_history__()
{
local output opts script local output opts script
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort ${FZF_CTRL_R_OPTS-} +m --read0" opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort ${FZF_CTRL_R_OPTS-} +m --read0"
script='BEGIN { getc; $/ = "\n\t"; $HISTCOUNT = $ENV{last_hist} + 1 } s/^[ *]//; print $HISTCOUNT - $. . "\t$_" if !$seen{$_}++' script='BEGIN { getc; $/ = "\n\t"; $HISTCOUNT = $ENV{last_hist} + 1 } s/^[ *]//; print $HISTCOUNT - $. . "\t$_" if !$seen{$_}++'
# shellcheck disable=SC2091 # Intentionally execute output of __fzfcmd # shellcheck disable=SC2091 # Intentionally execute output of __fzfcmd
output=$( output=$(
set +o pipefail set +o pipefail
builtin fc -lnr -2147483648 | builtin fc -lnr -2147483648 \
last_hist=$(HISTTIMEFORMAT='' builtin history 1) perl -n -l0 -e "$script" | | last_hist=$(HISTTIMEFORMAT='' builtin history 1) perl -n -l0 -e "$script" \
FZF_DEFAULT_OPTS="$opts" $(__fzfcmd) --query "$READLINE_LINE" | FZF_DEFAULT_OPTS="$opts" $(__fzfcmd) --query "$READLINE_LINE"
) || return ) || return
READLINE_LINE=${output#*$'\t'} READLINE_LINE=${output#*$'\t'}
if [[ -z "$READLINE_POINT" ]]; then if [[ -z "$READLINE_POINT" ]]; then

View File

@@ -52,4 +52,4 @@ keybindings:
prs: [] prs: []
repoPaths: {} repoPaths: {}
pager: pager:
diff: '' diff: ""

View File

@@ -1,3 +1,3 @@
--- ---
git_protocol: https git_protocol: https
version: '1' version: "1"

View File

@@ -1,6 +1,5 @@
---
github.com: github.com:
git_protocol: https git_protocol: ssh
users: users:
ivuorinen: ivuorinen:
user: ivuorinen user: ivuorinen

View File

@@ -7,6 +7,7 @@
*-secret *-secret
__secret __secret
__ignored __ignored
__ignored/*
__test_*.php __test_*.php
__test_*.txt __test_*.txt
__test.php __test.php

View File

@@ -19,8 +19,8 @@
logs = log --graph --pretty=format:'%C(magenta)%h%Creset -%C(red)%d%Creset %s %C(dim green)(%cr) %C(cyan)<%an>%Creset' --abbrev-commit logs = log --graph --pretty=format:'%C(magenta)%h%Creset -%C(red)%d%Creset %s %C(dim green)(%cr) %C(cyan)<%an>%Creset' --abbrev-commit
nah = !git reset --hard && git clean -df nah = !git reset --hard && git clean -df
recent = "!r() { count=$1; git for-each-ref --sort=-committerdate refs/heads --format='%(HEAD)%(color:yellow)%(refname:short)|%(color:bold green)%(committerdate:relative)|%(color:blue)%(subject)|%(color:magenta)%(authorname)%(color:reset)' --color=always --count=${count:=10} | column -ts'|';}; r" recent = "!r() { count=$1; git for-each-ref --sort=-committerdate refs/heads --format='%(HEAD)%(color:yellow)%(refname:short)|%(color:bold green)%(committerdate:relative)|%(color:blue)%(subject)|%(color:magenta)%(authorname)%(color:reset)' --color=always --count=${count:=10} | column -ts'|';}; r"
reset-origin = !git fetch origin && git reset --hard origin/master && git clean -f -d reset-origin = !git fetch origin && git reset --hard origin/HEAD && git clean -f -d
reset-upstream = !git fetch upstream && git reset --hard upstream/master && git clean -f -d reset-upstream = !git fetch upstream && git reset --hard upstream/HEAD && git clean -f -d
rl = reflog --format='%C(auto)%h %<|(20)%gd %C(blue)%cr%C(reset) %gs (%s)' rl = reflog --format='%C(auto)%h %<|(20)%gd %C(blue)%cr%C(reset) %gs (%s)'
tagdate = log --date-order --graph --tags --simplify-by-decoration --pretty=format:\"%ai %h %d\" tagdate = log --date-order --graph --tags --simplify-by-decoration --pretty=format:\"%ai %h %d\"
undo = reset --soft HEAD^ undo = reset --soft HEAD^

View File

@@ -260,8 +260,6 @@ 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

@@ -1,4 +1,4 @@
#!/bin/env bash #!/usr/bin/env bash
[ -z "$NVM_DIR" ] && export NVM_DIR="$HOME/.config/nvm" [[ -z "$NVM_DIR" ]] && export NVM_DIR="$HOME/.config/nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm [[ -s "$NVM_DIR/nvm.sh" ]] && \. "$NVM_DIR/nvm.sh" # This loads nvm

View File

@@ -2,7 +2,7 @@
-- │ ivuorinen's Neovim configuration │ -- │ ivuorinen's Neovim configuration │
-- ╰─────────────────────────────────────────────────────────╯ -- ╰─────────────────────────────────────────────────────────╯
-- ── Install lazylazy ──────────────────────────────────────────────── -- ── Install lazy ────────────────────────────────────────────────────
-- https://github.com/folke/lazy.nvim -- https://github.com/folke/lazy.nvim
local lazypath = vim.fn.stdpath 'data' .. '/lazy/lazy.nvim' local lazypath = vim.fn.stdpath 'data' .. '/lazy/lazy.nvim'
if not (vim.uv or vim.loop).fs_stat(lazypath) then if not (vim.uv or vim.loop).fs_stat(lazypath) then
@@ -53,7 +53,7 @@ require('lazy').setup(
path = '~/Code/nvim', -- Load wip plugins from this path path = '~/Code/nvim', -- Load wip plugins from this path
}, },
install = { install = {
colorscheme = { vim.g.colors_theme }, colorscheme = { 'catppuccin' },
}, },
profiling = { profiling = {
loader = true, loader = true,
@@ -61,8 +61,6 @@ require('lazy').setup(
} }
) )
-- require('nvm-default').setup()
require 'keymaps' require 'keymaps'
-- vim: set ts=2 sts=2 sw=2 wrap et : -- vim: set ts=2 sts=2 sw=2 wrap et :

View File

@@ -28,7 +28,6 @@ K.d('<C-k>', { 'n', 'v' }, ":m '<-2<CR>gv=gv", 'Move Block Up')
K.d('<C-j>', { 'n', 'v' }, ":m '>+1<CR>gv=gv", 'Move Block Down') K.d('<C-j>', { 'n', 'v' }, ":m '>+1<CR>gv=gv", 'Move Block Down')
-- ── Other operations ──────────────────────────────────────────────── -- ── Other operations ────────────────────────────────────────────────
K.nl('o', function() require('snacks').gitbrowse() end, 'Open repo in browser')
K.n('<C-s>', ':w!<cr>', { desc = 'Save', noremap = true }) K.n('<C-s>', ':w!<cr>', { desc = 'Save', noremap = true })
K.n('<esc><esc>', ':nohlsearch<cr>', { desc = 'Clear Search Highlighting' }) K.n('<esc><esc>', ':nohlsearch<cr>', { desc = 'Clear Search Highlighting' })
@@ -59,7 +58,6 @@ K.ld('cci', 'n', function() b().lsp_incoming_calls() end, 'Incoming calls')
K.ld('cco', 'n', function() b().lsp_outgoing_calls() end, 'Outgoing calls') K.ld('cco', 'n', function() b().lsp_outgoing_calls() end, 'Outgoing calls')
K.ld('cd', 'n', function() b().lsp_definitions() end, 'Definitions') K.ld('cd', 'n', function() b().lsp_definitions() end, 'Definitions')
K.ld('cf', { 'n', 'x' }, ':lua vim.lsp.buf.format()<CR>', 'Format') K.ld('cf', { 'n', 'x' }, ':lua vim.lsp.buf.format()<CR>', 'Format')
K.ld('cg', 'n', ':lua require("neogen").generate()<CR>', 'Generate annotations')
K.ld('ci', 'n', function() b().lsp_implementations() end, 'Implementations') K.ld('ci', 'n', function() b().lsp_implementations() end, 'Implementations')
K.ld('cp', 'n', function() b().lsp_type_definitions() end, 'Type Definition') K.ld('cp', 'n', function() b().lsp_type_definitions() end, 'Type Definition')
K.ld('cr', 'n', vim.lsp.buf.rename, 'Rename') K.ld('cr', 'n', vim.lsp.buf.rename, 'Rename')

View File

@@ -1,120 +0,0 @@
-- Get nvm default version and use it in node_host_prog
-- and g.copilot_node_command.
--
-- This module automatically configures Neovim to use the default Node.js version
-- from NVM. It requires a working NVM installation and 'default' alias to be set,
-- and also neovim npm package to be installed.
--
-- You can install the neovim package by running:
-- npm i --global neovim
--
-- Usage:
-- require('nvm-default').setup({
-- add_to_path = true, -- optional: add NVM bin directory to PATH
-- nvm_path = "~/.nvm", -- optional: custom NVM installation path
-- notify_level = "info" -- optional: notification level
-- })
local M = {}
M.name = 'nvm-default.nvim'
M.version = '0.1.0' -- x-release-please-version
-- Helper function to run a shell command
---@param cmd string Run a shell command
---@return string? Return the result of the command
local function run_command(cmd)
local result = vim.fn.system(cmd)
return vim.v.shell_error == 0 and result:gsub('%s+$', '') or nil
end
-- Helper function to show a notification
---@param msg string Show a message
---@param level "info"|"warn"|"error"|"trace" Notification level
local function n(msg, level)
if msg == nil then msg = M.name .. ': No message provided' end
if level == nil then level = 'trace' end
local log_level = vim.log.levels.INFO
if level == 'info' then
log_level = vim.log.levels.INFO
elseif level == 'warn' then
log_level = vim.log.levels.WARN
elseif level == 'error' then
log_level = vim.log.levels.ERROR
elseif level == 'trace' then
log_level = vim.log.levels.TRACE
end
vim.notify(M.name .. ': ' .. msg, log_level)
end
---@class NvmDefaultOptions
---@field add_to_path boolean Add found NVM bin directory to PATH
---@field nvm_path string Where nvm installation is located
---@field notify_level number|"info"|"warn"|"error"|"trace" Notification level filter
-- Default options
---@type NvmDefaultOptions
M.defaults = {
add_to_path = vim.g.nvm_default_add_to_path or true,
nvm_path = vim.fn.expand(os.getenv 'NVM_DIR' or '~/.nvm'),
notify_level = vim.g.nvm_default_notify_level or 'info',
}
-- Fetch the NVM default version or fallback to node version
---@param opts? NvmDefaultOptions Plugin options
function M.setup(opts)
local options = vim.tbl_deep_extend('force', M.defaults, opts or {})
local nvm_path = options.nvm_path
local node_version = run_command(
string.format('. %s/nvm.sh && nvm version default', nvm_path)
) or run_command(string.format('. %s/nvm.sh && nvm version node', nvm_path)) or nil
if node_version and node_version:match '^v' then
-- Set vim.g.node_host_prog and vim.g.copilot_node_command
local current_nvm_version_path =
string.format('%s/versions/node/%s', nvm_path, node_version)
local current_nvm_node_bin_path = string.format('%s/bin', current_nvm_version_path)
local current_nvm_node_bin = string.format('%s/node', current_nvm_node_bin_path)
local neovim_node_host_bin_path =
string.format('%s/neovim-node-host', current_nvm_node_bin_path)
-- Collect missing files and directories errors for error output
local missing = {}
-- If node_dir isn't there, stop and show error
if not vim.fn.isdirectory(current_nvm_version_path) then
table.insert(missing, 'Node.js directory: ' .. current_nvm_version_path)
end
-- If node_bin isn't there, stop and show error
if not vim.fn.filereadable(current_nvm_node_bin) then
table.insert(missing, 'Node.js binary: ' .. current_nvm_node_bin)
end
if not vim.fn.filereadable(neovim_node_host_bin_path) then
table.insert(missing, 'Neovim host binary: ' .. neovim_node_host_bin_path)
end
if #missing > 0 then
n('Missing required files:\n- ' .. table.concat(missing, '\n- '), 'error')
return
end
-- Add to PATH if requested. Can be turned off by setting if it messes with
-- other tools.
if options.add_to_path then
vim.env.PATH = current_nvm_node_bin_path .. ':' .. vim.env.PATH
end
vim.g.node_host_prog = neovim_node_host_bin_path
vim.g.copilot_node_command = current_nvm_node_bin
else
n('Unable to determine the Node.js version from nvm.', 'error')
end
end
return M

View File

@@ -13,10 +13,6 @@ local a = vim.api -- A table to store API functions
g.mapleader = ' ' -- Space as the leader key g.mapleader = ' ' -- Space as the leader key
g.maplocalleader = ' ' -- Space as the local leader key g.maplocalleader = ' ' -- Space as the local leader key
-- g.colors_theme = 'onedark' -- Set the colorscheme
-- g.colors_variant_light = 'tokyonight-day' -- Set the light variant
-- g.colors_variant_dark = 'tokyonight-storm' -- Set the dark variant
g.editorconfig = true -- Make sure editorconfig support is enabled g.editorconfig = true -- Make sure editorconfig support is enabled
g.loaded_perl_provider = 0 -- Disable perl provider g.loaded_perl_provider = 0 -- Disable perl provider
g.loaded_ruby_provider = 0 -- Disable ruby provider g.loaded_ruby_provider = 0 -- Disable ruby provider

View File

@@ -1,6 +1,6 @@
return { return {
-- Performant, batteries-included completion plugin for Neovim -- Performant, batteries-included completion plugin for Neovim
-- https:/github.com/saghen/blink.cmp -- https://github.com/saghen/blink.cmp
{ {
'saghen/blink.cmp', 'saghen/blink.cmp',
version = '*', version = '*',

View File

@@ -24,7 +24,14 @@ return {
-- https://github.com/fatih/vim-go -- https://github.com/fatih/vim-go
{ {
'fatih/vim-go', 'fatih/vim-go',
config = function() end, ft = 'go',
config = function()
vim.g.go_def_mode = 'gopls'
vim.g.go_info_mode = 'gopls'
vim.g.go_fmt_autosave = 0
vim.g.go_imports_autosave = 0
vim.g.go_mod_fmt_autosave = 0
end,
}, },
-- Clarify and beautify your comments using boxes and lines. -- Clarify and beautify your comments using boxes and lines.

View File

@@ -223,7 +223,7 @@ return {
vim.env.VIMRUNTIME, vim.env.VIMRUNTIME,
} }
client.config.settings.Lua.runtime = { version = 'LuaJIT' } client.config.settings.Lua.runtime = { version = 'LuaJIT' }
client.notify( client:notify(
'workspace/didChangeConfiguration', 'workspace/didChangeConfiguration',
{ settings = client.config.settings } { settings = client.config.settings }
) )

View File

@@ -40,7 +40,8 @@ return {
operators = {}, operators = {},
-- miscs = {}, -- Uncomment to turn off hard-coded styles -- miscs = {}, -- Uncomment to turn off hard-coded styles
}, },
lsp_styles = { -- Handles the style of specific lsp hl groups (see `:h lsp-highlight`). -- Style of specific lsp hl groups (`:h lsp-highlight`)
lsp_styles = {
virtual_text = { virtual_text = {
errors = { 'italic' }, errors = { 'italic' },
hints = { 'italic' }, hints = { 'italic' },
@@ -72,7 +73,8 @@ return {
enabled = true, enabled = true,
indentscope_color = '', indentscope_color = '',
}, },
-- For more plugins integrations please scroll down (https://github.com/catppuccin/nvim#integrations) -- More integrations:
-- github.com/catppuccin/nvim#integrations
}, },
} }
@@ -87,15 +89,12 @@ return {
'f-person/auto-dark-mode.nvim', 'f-person/auto-dark-mode.nvim',
opts = { opts = {
update_interval = 1000, update_interval = 1000,
-- stylua: ignore
set_dark_mode = function() set_dark_mode = function()
vim.api.nvim_set_option_value('background', 'dark', {}) vim.api.nvim_set_option_value('background', 'dark', {})
-- vim.cmd.colorscheme(vim.g.colors_variant_dark)
-- vim.cmd 'colorscheme rose-pine'
end, end,
set_light_mode = function() set_light_mode = function()
vim.api.nvim_set_option_value('background', 'light', {}) vim.api.nvim_set_option_value('background', 'light', {})
-- vim.cmd.colorscheme(vim.g.colors_variant_light)
-- vim.cmd 'colorscheme rose-pine-dawn'
end, end,
}, },
}, },
@@ -115,7 +114,8 @@ return {
{ {
'dmtrKovalenko/fff.nvim', 'dmtrKovalenko/fff.nvim',
build = function() build = function()
-- this will download prebuild binary or try to use existing rustup toolchain to build from source -- Downloads prebuild binary or uses rustup
-- 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,
@@ -124,7 +124,8 @@ 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
show_scores = true, -- to help us optimize the scoring system, feel free to share your scores! -- Share scores to help optimize scoring
show_scores = true,
}, },
}, },
-- No need to lazy-load with lazy.nvim. -- No need to lazy-load with lazy.nvim.

View File

@@ -100,5 +100,6 @@ function GetIntelephenseLicense()
local f = assert(io.open(p, 'rb')) local f = assert(io.open(p, 'rb'))
local content = f:read '*a' local content = f:read '*a'
f:close() f:close()
return string.gsub(content, '%s+', '')[1] or nil local stripped = string.gsub(content, '%s+', '')
return stripped == '' and nil or stripped
end end

View File

@@ -8,8 +8,11 @@
# #
# Smart session manager for the terminal # Smart session manager for the terminal
# https://github.com/joshmedeski/sesh # https://github.com/joshmedeski/sesh
#:schema https://github.com/joshmedeski/sesh/raw/main/sesh.schema.json
strict_mode = false strict_mode = false
dir_length = 2 # Uses last 2 directories: "projects/sesh" instead of just "sesh"
cache = true
# [marker] # [marker]
# inactivity_threshold = 10 # Seconds before alerts start # inactivity_threshold = 10 # Seconds before alerts start
@@ -52,3 +55,7 @@ disable_startup_command = true
name = "Downloads" name = "Downloads"
path = "~/Downloads" path = "~/Downloads"
startup_command = "lsa" startup_command = "lsa"
[[session]]
name = "Code/ivuorinen"
path = "~/Code/ivuorinen/"

View File

@@ -5,7 +5,7 @@
# shellcheck shell=bash # shellcheck shell=bash
# Defaults # Defaults
[ -z "$DOTFILES" ] && export DOTFILES="$HOME/.dotfiles" [[ -z "${DOTFILES:-}" ]] && export DOTFILES="$HOME/.dotfiles"
DOTFILES_CURRENT_SHELL=$(basename "$SHELL") DOTFILES_CURRENT_SHELL=$(basename "$SHELL")
export DOTFILES_CURRENT_SHELL export DOTFILES_CURRENT_SHELL
@@ -15,7 +15,7 @@ VERBOSE="${VERBOSE:-0}"
DEBUG="${DEBUG:-0}" DEBUG="${DEBUG:-0}"
# Enable debugging with DEBUG=1 # Enable debugging with DEBUG=1
[ "${DEBUG:-0}" -eq 1 ] && set -x [[ "${DEBUG:-0}" -eq 1 ]] && set -x
# Detect the current shell # Detect the current shell
CURRENT_SHELL=$(ps -p $$ -ocomm= | awk -F/ '{print $NF}') CURRENT_SHELL=$(ps -p $$ -ocomm= | awk -F/ '{print $NF}')
@@ -33,9 +33,10 @@ x-path-prepend()
;; ;;
*) *)
echo "Unsupported shell: $CURRENT_SHELL" echo "Unsupported shell: $CURRENT_SHELL"
exit 1 return 1
;; ;;
esac esac
return 0
} }
# Function to set environment variables based on the shell # Function to set environment variables based on the shell
@@ -52,9 +53,10 @@ x-set-env()
;; ;;
*) *)
echo "Unsupported shell: $CURRENT_SHELL" echo "Unsupported shell: $CURRENT_SHELL"
exit 1 return 1
;; ;;
esac esac
return 0
} }
# Explicitly set XDG folders, if not already set # Explicitly set XDG folders, if not already set
@@ -74,16 +76,17 @@ x-path-prepend "$DOTFILES/local/bin"
x-path-prepend "$XDG_BIN_HOME" x-path-prepend "$XDG_BIN_HOME"
# Custom completion paths # Custom completion paths
[ -z "$ZSH_CUSTOM_COMPLETION_PATH" ] && export ZSH_CUSTOM_COMPLETION_PATH="$XDG_CONFIG_HOME/zsh/completion" [[ -z "${ZSH_CUSTOM_COMPLETION_PATH:-}" ]] && export ZSH_CUSTOM_COMPLETION_PATH="$XDG_CONFIG_HOME/zsh/completion"
x-dc "$ZSH_CUSTOM_COMPLETION_PATH" x-dc "$ZSH_CUSTOM_COMPLETION_PATH"
export FPATH="$ZSH_CUSTOM_COMPLETION_PATH:$FPATH" export FPATH="$ZSH_CUSTOM_COMPLETION_PATH:${FPATH:-}"
if ! declare -f msg > /dev/null; then if ! declare -f msg > /dev/null; then
# Function to print messages if VERBOSE is enabled # Function to print messages if VERBOSE is enabled
# $1 - message (string) # $1 - message (string)
msg() msg()
{ {
[ "$VERBOSE" -eq 1 ] && msgr msg "$1" local message="$1"
[[ "$VERBOSE" -eq 1 ]] && msgr msg "$message"
return 0 return 0
} }
msg "msg was not defined, defined it now" msg "msg was not defined, defined it now"
@@ -95,7 +98,8 @@ if ! declare -f msg_err > /dev/null; then
# $1 - error message (string) # $1 - error message (string)
msg_err() msg_err()
{ {
msgr err "$1" >&2 local message="$1"
msgr err "$message" >&2
exit 1 exit 1
} }
fi fi
@@ -106,7 +110,8 @@ if ! declare -f msg_done > /dev/null; then
# $1 - message (string) # $1 - message (string)
msg_done() msg_done()
{ {
msgr "done" "$1" local message="$1"
msgr "done" "$message"
return 0 return 0
} }
fi fi
@@ -117,7 +122,8 @@ if ! declare -f msg_run > /dev/null; then
# $1 - message (string) # $1 - message (string)
msg_run() msg_run()
{ {
msgr run "$1" local message="$1"
msgr run "$message"
return 0 return 0
} }
fi fi
@@ -128,7 +134,8 @@ if ! declare -f msg_ok > /dev/null; then
# $1 - message (string) # $1 - message (string)
msg_ok() msg_ok()
{ {
msgr ok "$1" local message="$1"
msgr ok "$message"
return 0 return 0
} }
fi fi
@@ -143,12 +150,16 @@ if ! declare -f array_diff > /dev/null; then
# Source: https://stackoverflow.com/a/42399479/594940 # Source: https://stackoverflow.com/a/42399479/594940
array_diff() array_diff()
{ {
local result_var="$1"
local arr1_name="$2"
local arr2_name="$3"
# shellcheck disable=SC1083,SC2086 # shellcheck disable=SC1083,SC2086
eval local ARR1=\(\"\${$2[@]}\"\) eval local ARR1=\(\"\${${arr1_name}[@]}\"\)
# shellcheck disable=SC1083,SC2086 # shellcheck disable=SC1083,SC2086
eval local ARR2=\(\"\${$3[@]}\"\) eval local ARR2=\(\"\${${arr2_name}[@]}\"\)
local IFS=$'\n' local IFS=$'\n'
mapfile -t "$1" < <(comm -23 <(echo "${ARR1[*]}" | sort) <(echo "${ARR2[*]}" | sort)) mapfile -t "$result_var" < <(comm -23 <(echo "${ARR1[*]}" | sort) <(echo "${ARR2[*]}" | sort))
return 0
} }
fi fi

View File

@@ -7,13 +7,13 @@ DEFAULT_NAME="main"
CURRENT_SESSION=$(tmux display-message -p "#{session_name}") CURRENT_SESSION=$(tmux display-message -p "#{session_name}")
# Check that the session has a name # Check that the session has a name
if [ "$CURRENT_SESSION" = "#{session_name}" ] || [ "$CURRENT_SESSION" = "0" ]; then if [[ "$CURRENT_SESSION" = "#{session_name}" ]] || [[ "$CURRENT_SESSION" = "0" ]]; then
# Check if the default name is already in use # Check if the default name is already in use
if tmux has-session -t "$DEFAULT_NAME" 2> /dev/null; then if tmux has-session -t "$DEFAULT_NAME" 2> /dev/null; then
# Query the user for a new name # Query the user for a new name
echo "Session name '$DEFAULT_NAME' is already in use. Enter a new name:" echo "Session name '$DEFAULT_NAME' is already in use. Enter a new name:"
read -r NEW_NAME read -r NEW_NAME
while tmux has-session -t "$NEW_NAME" 2> /dev/null || [ -z "$NEW_NAME" ]; do while tmux has-session -t "$NEW_NAME" 2> /dev/null || [[ -z "$NEW_NAME" ]]; do
echo "Name '$NEW_NAME' is invalid or already in use. Enter a new name:" echo "Name '$NEW_NAME' is invalid or already in use. Enter a new name:"
read -r NEW_NAME read -r NEW_NAME
done done

View File

@@ -8,12 +8,14 @@
set -euo pipefail set -euo pipefail
# Fall back to native tmux session picker if sesh is not installed # Fall back to native tmux session picker if sesh is not installed
if ! command -v sesh &>/dev/null; then if ! command -v sesh &> /dev/null; then
tmux choose-tree -Zs tmux choose-tree -Zs
exit 0 exit 0
fi fi
pick_with_gum() { # Pick a sesh session using gum filter
pick_with_gum()
{
sesh list -i \ sesh list -i \
| gum filter \ | gum filter \
--limit 1 \ --limit 1 \
@@ -22,6 +24,7 @@ pick_with_gum() {
--placeholder 'Pick a sesh' \ --placeholder 'Pick a sesh' \
--height 50 \ --height 50 \
--prompt='⚡' --prompt='⚡'
return 0
} }
FZF_COMMON_OPTS=( FZF_COMMON_OPTS=(
@@ -40,15 +43,23 @@ FZF_COMMON_OPTS=(
--preview 'sesh preview {}' --preview 'sesh preview {}'
) )
pick_with_fzf_tmux() { # Pick a sesh session using fzf-tmux popup
pick_with_fzf_tmux()
{
sesh list --icons | fzf-tmux -p 80%,70% "${FZF_COMMON_OPTS[@]}" sesh list --icons | fzf-tmux -p 80%,70% "${FZF_COMMON_OPTS[@]}"
return 0
} }
pick_with_fzf() { # Pick a sesh session using fzf inline
pick_with_fzf()
{
sesh list --icons | fzf "${FZF_COMMON_OPTS[@]}" sesh list --icons | fzf "${FZF_COMMON_OPTS[@]}"
return 0
} }
pick_with_select() { # Pick a sesh session using bash select menu
pick_with_select()
{
local sessions local sessions
mapfile -t sessions < <(sesh list) mapfile -t sessions < <(sesh list)
if [[ ${#sessions[@]} -eq 0 ]]; then if [[ ${#sessions[@]} -eq 0 ]]; then
@@ -64,11 +75,11 @@ pick_with_select() {
} }
# Cascading tool detection # Cascading tool detection
if command -v gum &>/dev/null; then if command -v gum &> /dev/null; then
selection=$(pick_with_gum) selection=$(pick_with_gum)
elif command -v fzf-tmux &>/dev/null; then elif command -v fzf-tmux &> /dev/null; then
selection=$(pick_with_fzf_tmux) selection=$(pick_with_fzf_tmux)
elif command -v fzf &>/dev/null; then elif command -v fzf &> /dev/null; then
selection=$(pick_with_fzf) selection=$(pick_with_fzf)
else else
selection=$(pick_with_select) selection=$(pick_with_select)

View File

@@ -10,7 +10,7 @@ config.color_scheme_dirs = {
} }
-- Font and font size -- Font and font size
config.font_size = 16 config.font_size = 12
config.font = wezterm.font_with_fallback { config.font = wezterm.font_with_fallback {
{ {
family = 'Monaspace Argon NF', family = 'Monaspace Argon NF',
@@ -48,9 +48,9 @@ config.window_background_opacity = 0.97
config.window_decorations = 'RESIZE' config.window_decorations = 'RESIZE'
config.macos_window_background_blur = 10 config.macos_window_background_blur = 10
config.window_padding = { config.window_padding = {
left = 5, left = 10,
right = 5, right = 10,
top = 5, top = 10,
bottom = 5, bottom = 5,
} }

View File

@@ -0,0 +1,40 @@
# Skip Already-Installed Cargo Packages
## Problem
`install-cargo-packages.sh` runs `cargo install-update -a` to update installed
packages, then runs `cargo install` for every package in the list — including
ones that are already installed and up-to-date. This wastes time rebuilding
packages that don't need it.
## Solution
Capture the `cargo install-update -a` output, parse installed package names,
and skip `cargo install` for any package that appeared in the update output.
## Changes
**File:** `scripts/install-cargo-packages.sh`
1. Declare an associative array `installed_packages` at the top.
2. In the `cargo-install-update` section, capture output with `tee /dev/stderr`
so it displays in real-time while also being stored in a variable.
3. Parse the captured output with `awk` — extract the first column from lines
matching a version pattern (`v[0-9]+\.[0-9]+`), skipping the header.
4. Populate `installed_packages` associative array from parsed names.
5. In `install_packages()`, check each package against the array. If found, log
a skip message via `msgr` and continue. If not found, install as before.
6. If `cargo-install-update` is not available, the array stays empty and all
packages install normally (preserves existing behavior).
## Output Parsing
The `cargo install-update -a` output format:
```text
Package Installed Latest Needs update
zoxide v0.9.8 v0.9.9 Yes
bkt v0.8.2 v0.8.2 No
```
Extraction: `awk '/v[0-9]+\.[0-9]+/ { print $1 }'` gets package names.

View File

@@ -0,0 +1,55 @@
# dfm Cleanup Design
## Summary
Clean up `local/bin/dfm` to fix bugs, remove dead code, improve
cross-platform portability, and make error handling consistent.
## Changes
### 1. Bash Version Bootstrap
Add a check at the top of the script (after variable declarations)
that requires bash 4.0+. On macOS, if bash is too old, install
Homebrew (if missing) and bash, then print instructions and exit.
The check itself uses only bash 3.2-compatible syntax.
### 2. Remove Fish Dead Code
Remove `CURRENT_SHELL` detection, `source_file()` function, and all
fish branches. Replace `source_file` calls with direct `source`.
The script has a bash shebang — fish handling was unreachable.
### 3. Bug Fixes
- Remove `ntfy` from install menu (no install script exists)
- Fix `msg)``msgr)` case label in `section_tests`
- Guard all `shift` calls against empty argument lists
- Quote `$width` in `menu_builder` seq calls
- Fix `$"..."` locale string → `"..."` in `usage()`
- Fix `exit 0` on apt.txt error → `return 1`
### 4. Replace `declare -A` in `section_scripts`
Replace associative array with indexed `"name:desc"` array,
matching the pattern used everywhere else in the script.
Move `get_script_description()` to top-level (out of the function).
### 5. Early-Return Guards & exit → return
- `section_brew()`: Early return with `msgr warn` if brew unavailable.
Remove duplicate `! x-have brew` check.
- `section_apt()`: Same pattern for apt.
- `section_check()`: Replace `exit` with `return`.
- `section_apt() install`: Replace `exit` with `return`.
- `section_brew() untracked`: Replace `exit` with `return`.
## Files Changed
- `local/bin/dfm` (all changes)
## Verification
- `yarn test` (existing bats test)
- `shellcheck local/bin/dfm`
- `bash -n local/bin/dfm` (syntax check)

View File

@@ -0,0 +1,46 @@
# x-* Scripts Cleanup Design
## Summary
Comprehensive cleanup of all 34 x-* utility scripts in `local/bin/`.
Fix critical bugs, consolidate duplicates, standardize patterns.
## Changes
### Removals
- `x-mkd`, `x-mkd.md`, `tests/x-mkd.bats` — unused, cd-in-subshell broken
- `x-validate-sha256sum.sh`, `x-validate-sha256sum.sh.md` — duplicates x-sha256sum-matcher
### Thin Wrappers (delegate to x-path)
- `x-path-append` → calls `x-path append "$@"`
- `x-path-prepend` → calls `x-path prepend "$@"`
- `x-path-remove` → calls `x-path remove "$@"`
### Critical Fixes
- `x-clean-vendordirs`: call msgr as command (it's in PATH)
- `x-foreach`: replace eval with direct "$@" execution
- `x-ip`: add error handling, curl check
### Consistency Fixes
- Fix `#!/bin/bash``#!/usr/bin/env bash` (x-env-list, x-localip)
- POSIX scripts keep `#!/bin/sh`
- Add `set -euo pipefail` where missing in bash scripts
- Use XDG variables instead of hardcoded paths (x-change-alacritty-theme)
- Quote unquoted variables
### Minor Fixes
- `x-multi-ping`: remove unused VERBOSE variable
- `x-when-down`, `x-when-up`: add error handling
- `x-term-colors`: add usage message
- `x-record`: fix undefined notify-call reference
## Verification
- `yarn test` — ensure remaining tests pass
- `shellcheck` on modified scripts
- `bash -n` syntax check on all modified bash scripts

View File

@@ -1,5 +1,5 @@
--- ---
- include: 'tools/dotbot-defaults.yaml' - include: "tools/dotbot-defaults.yaml"
- shell: - shell:
- echo "Configuring air" - echo "Configuring air"
- link: - link:
@@ -7,7 +7,7 @@
force: true force: true
glob: true glob: true
path: hosts/air/base/** path: hosts/air/base/**
prefix: '.' prefix: "."
~/.config/: ~/.config/:
glob: true glob: true
force: true force: true

View File

@@ -1,5 +1,5 @@
--- ---
- include: 'tools/dotbot-defaults.yaml' - include: "tools/dotbot-defaults.yaml"
- shell: - shell:
- echo "Configuring lakka" - echo "Configuring lakka"
- link: - link:
@@ -7,7 +7,7 @@
force: true force: true
glob: true glob: true
path: hosts/lakka/base/** path: hosts/lakka/base/**
prefix: '.' prefix: "."
~/.config/: ~/.config/:
glob: true glob: true
force: true force: true

View File

@@ -1,5 +1,5 @@
--- ---
- include: 'tools/dotbot-defaults.yaml' - include: "tools/dotbot-defaults.yaml"
- shell: - shell:
- echo "Configuring s" - echo "Configuring s"
- link: - link:
@@ -7,7 +7,7 @@
force: true force: true
glob: true glob: true
path: hosts/s/base/** path: hosts/s/base/**
prefix: '.' prefix: "."
~/.config/: ~/.config/:
glob: true glob: true
force: true force: true

View File

@@ -1,5 +1,5 @@
--- ---
- include: 'tools/dotbot-defaults.yaml' - include: "tools/dotbot-defaults.yaml"
- shell: - shell:
- echo "Configuring tunkki" - echo "Configuring tunkki"
- link: - link:
@@ -7,7 +7,7 @@
force: true force: true
glob: true glob: true
path: hosts/tunkki/base/** path: hosts/tunkki/base/**
prefix: '.' prefix: "."
~/.config/: ~/.config/:
glob: true glob: true
force: true force: true

12
install
View File

@@ -15,24 +15,18 @@ 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}" \
"${@}" "${@}"
if [ "${DOTBOT_HOST}" != "" ]; then if [ "${DOTBOT_HOST}" != "" ]; then
DOTBOT_HOST_CONFIG="${BASEDIR}/hosts/${DOTBOT_HOST}/${CONFIG}" DOTBOT_HOST_CONFIG="${BASEDIR}/hosts/${DOTBOT_HOST}/${CONFIG}"
echo "-> Trying if host config can be found: ${DOTBOT_HOST_CONFIG}" echo "-> Trying if host config can be found: ${DOTBOT_HOST_CONFIG}"
[ -r "$DOTBOT_HOST_CONFIG" ] && [ -f "$DOTBOT_HOST_CONFIG" ] && [ -r "$DOTBOT_HOST_CONFIG" ] && [ -f "$DOTBOT_HOST_CONFIG" ] \
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

@@ -1,5 +1,5 @@
--- ---
- include: 'tools/dotbot-defaults.yaml' - include: "tools/dotbot-defaults.yaml"
- clean: - clean:
~/: ~/:
@@ -34,7 +34,7 @@
force: true force: true
glob: true glob: true
path: base/* path: base/*
prefix: '.' prefix: "."
# Most of the configs # Most of the configs
~/.config/: ~/.config/:
glob: true glob: true
@@ -78,8 +78,3 @@
- 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

@@ -20,7 +20,7 @@ Some problematic code has been fixed per `shellcheck` suggestions.
## Sourced ## Sourced
| Script | Source | | Script | Source |
| ----------------------- | ----------------- | |-------------------------|-------------------|
| `x-dupes` | skx/sysadmin-util | | `x-dupes` | skx/sysadmin-util |
| `x-foreach` | mvdan/dotfiles | | `x-foreach` | mvdan/dotfiles |
| `x-multi-ping` | skx/sysadmin-util | | `x-multi-ping` | skx/sysadmin-util |

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
fetch_keys_if_missing encrypt_single_file "$file"
local output_file="${file}.age" else
local temp_file log_message "Warning: '$file' is not a file or directory, skipping."
temp_file="$(mktemp -p "$(dirname "$file")")" fi
if age -R "$KEYS_FILE" "$file" > "$temp_file" && mv "$temp_file" "$output_file"; then }
log_message "File encrypted successfully: $output_file"
else # Function to decrypt a single file
rm -f "$temp_file" decrypt_single_file()
log_message "Error: Failed to encrypt file '$file'." {
exit 1 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
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
} }
@@ -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}" else
local temp_file log_message "Warning: '$file' is not a file or directory, skipping."
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 logic # Main entry point
case "$1" in main()
e | enc | encrypt) {
if [[ $# -lt 2 ]]; then check_dependencies
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 ;;
decrypt_file_or_directory "$2" esac
;; }
help | --help)
print_help main "$@"
;;
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,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

@@ -12,41 +12,41 @@
: "${DOTFILES:=$HOME/.dotfiles}" : "${DOTFILES:=$HOME/.dotfiles}"
: "${BREWFILE:=$DOTFILES/config/homebrew/Brewfile}" : "${BREWFILE:=$DOTFILES/config/homebrew/Brewfile}"
: "${HOSTFILES:=$DOTFILES/hosts}" : "${HOSTFILES:=$DOTFILES/hosts}"
export DOTFILES BREWFILE HOSTFILES
SCRIPT=$(basename "$0") SCRIPT=$(basename "$0")
# Detect the current shell # Require bash 4.0+ for associative arrays and mapfile
CURRENT_SHELL=$(ps -p $$ -ocomm= | awk -F/ '{print $NF}') if ((BASH_VERSINFO[0] < 4)); then
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
# Function to source files based on the shell # shellcheck disable=SC1091
source_file() source "$DOTFILES/config/shared.sh"
# 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"
case "$CURRENT_SHELL" in local desc
fish) desc=$(sed -n '/@description/s/.*@description *\(.*\)/\1/p' "$file" | head -1)
if [[ -f "$file.fish" ]]; then echo "${desc:-No description available}"
# 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()
{ {
@@ -54,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%%:*}
@@ -65,41 +65,68 @@ menu_builder()
done done
} }
# Handle install section commands
section_install() section_install()
{ {
USAGE_PREFIX="$SCRIPT install <command>" USAGE_PREFIX="$SCRIPT install <command>"
MENU=( MENU=(
"all:Installs everything in the correct order" "all:Installs everything in the correct order"
"apt-packages:Install apt packages (Debian/Ubuntu)"
"cargo:Install rust/cargo packages" "cargo:Install rust/cargo packages"
"cheat-databases:Install cheat external cheatsheet databases" "cheat-databases:Install cheat external cheatsheet databases"
"composer:Install composer" "composer:Install composer"
"dnf-packages:Install dnf packages (Fedora/RHEL)"
"fonts:Install programming fonts" "fonts:Install programming fonts"
"gh:Install GitHub CLI Extensions" "gh:Install GitHub CLI Extensions"
"git-crypt:Install git-crypt from source"
"go:Install Go Packages" "go:Install Go Packages"
"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" "ntfy:Install ntfy notification tool"
"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)"
"python-packages:Install Python packages via uv"
"shellspec:Install shellspec testing framework"
"xcode-cli-tools:Install Xcode CLI tools (macOS)"
"z:Install z" "z:Install z"
) )
case "$1" in case "$1" in
all) all)
msgr msg "Starting to install all and reloading configurations..." msgr msg "Starting to install all and reloading configurations..."
$0 install macos
$0 install fonts # Tier 0: Platform foundations (OS packages, build tools)
[[ "$(uname)" == "Darwin" ]] && $0 install macos
[[ "$(uname)" == "Darwin" ]] && $0 install xcode-cli-tools
command -v apt &> /dev/null && $0 install apt-packages
command -v dnf &> /dev/null && $0 install dnf-packages
# Tier 1: Package managers & fonts
$0 brew install $0 brew install
$0 install fonts
# Tier 2: Language packages (depend on runtimes from Tier 1)
$0 install cargo $0 install cargo
$0 install go $0 install go
$0 install composer $0 install composer
$0 install cheat-databases
$0 install nvm $0 install nvm
$0 install npm-packages $0 install npm-packages
$0 install python-packages
# Tier 3: Tool-dependent installers
$0 install cheat-databases
$0 install gh
$0 install git-crypt
$0 install ntfy
# Tier 4: Independent utilities
$0 install shellspec
$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!"
;; ;;
@@ -112,7 +139,7 @@ section_install()
cheat-databases) cheat-databases)
msgr run "Installing cheat databases..." msgr run "Installing cheat databases..."
for database in "$DOTFILES"/scripts/install-cheat-*; do for database in "$DOTFILES"/scripts/install-cheat-*.sh; do
bash "$database" \ bash "$database" \
&& msgr run_done "Cheat: $database run" && msgr run_done "Cheat: $database run"
done done
@@ -184,6 +211,48 @@ section_install()
&& msgr yay "NPM Packages have been installed!" && msgr yay "NPM Packages have been installed!"
;; ;;
apt-packages)
msgr run "Installing apt packages..."
bash "$DOTFILES/scripts/install-apt-packages.sh" \
&& msgr yay "apt packages installed!"
;;
dnf-packages)
msgr run "Installing dnf packages..."
bash "$DOTFILES/scripts/install-dnf-packages.sh" \
&& msgr yay "dnf packages installed!"
;;
git-crypt)
msgr run "Installing git-crypt..."
bash "$DOTFILES/scripts/install-git-crypt.sh" \
&& msgr yay "git-crypt installed!"
;;
ntfy)
msgr run "Installing ntfy..."
bash "$DOTFILES/scripts/install-ntfy.sh" \
&& msgr yay "ntfy installed!"
;;
python-packages)
msgr run "Installing Python packages..."
bash "$DOTFILES/scripts/install-python-packages.sh" \
&& msgr yay "Python packages installed!"
;;
xcode-cli-tools)
msgr run "Installing Xcode CLI tools..."
bash "$DOTFILES/scripts/install-xcode-cli-tools.sh" \
&& msgr yay "Xcode CLI tools installed!"
;;
shellspec)
msgr run "Installing shellspec..."
bash "$DOTFILES/scripts/install-shellspec.sh" \
&& msgr yay "shellspec has been installed!"
;;
z) z)
msgr run "Installing z..." msgr run "Installing z..."
bash "$DOTFILES/scripts/install-z.sh" \ bash "$DOTFILES/scripts/install-z.sh" \
@@ -194,6 +263,7 @@ section_install()
esac esac
} }
# Handle Homebrew section commands
section_brew() section_brew()
{ {
USAGE_PREFIX="$SCRIPT brew <command>" USAGE_PREFIX="$SCRIPT brew <command>"
@@ -208,89 +278,91 @@ section_brew()
"untracked:List untracked brew packages" "untracked:List untracked brew packages"
) )
x-have brew && { if ! x-have brew; then
case "$1" in msgr warn "brew not available, skipping"
install) return 0
brew bundle install --file="$BREWFILE" --force --quiet && msgr yay "Done!" fi
;;
update) case "$1" in
brew update && brew outdated && brew upgrade && brew cleanup install)
msgr yay "Done!" brew bundle install --file="$BREWFILE" --force --quiet && msgr yay "Done!"
;; ;;
updatebundle) update)
# Updates .dotfiles/homebrew/Brewfile with descriptions brew update && brew outdated && brew upgrade && brew cleanup
brew bundle dump \ msgr yay "Done!"
--force \ ;;
--file="$BREWFILE" \
--cleanup \
--tap \
--formula \
--cask \
--describe && msgr yay "Done!"
;;
leaves) updatebundle)
brew leaves --installed-on-request # Updates .dotfiles/homebrew/Brewfile with descriptions
;; brew bundle dump \
--force \
--file="$BREWFILE" \
--cleanup \
--tap \
--formula \
--cask \
--describe && msgr yay "Done!"
;;
untracked) leaves)
declare -a BREW_LIST_ALL brew leaves --installed-on-request
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)
# Remove entries that are installed as dependencies untracked)
declare -a BREW_LIST_DEPENDENCIES declare -a BREW_LIST_ALL
while IFS= read -r l; do while IFS= read -r line; do
BREW_LIST_DEPENDENCIES+=("$l") BREW_LIST_ALL+=("$line")
done < <(brew list -1 --installed-as-dependency) 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)
declare -a BREW_LIST_BUNDLED # Remove entries that are installed as dependencies
while IFS= read -r b; do declare -a BREW_LIST_DEPENDENCIES
BREW_LIST_BUNDLED+=("$b") while IFS= read -r l; do
done < <(brew bundle list --all --file="$BREWFILE") BREW_LIST_DEPENDENCIES+=("$l")
done < <(brew list -1 --installed-as-dependency)
declare -a BREW_LIST_TRACKED_WITHOUT_DEPS declare -a BREW_LIST_BUNDLED
for f in "${BREW_LIST_ALL[@]}"; do while IFS= read -r b; do
# shellcheck disable=SC2199 BREW_LIST_BUNDLED+=("$b")
if [[ " ${BREW_LIST_DEPENDENCIES[@]} " != *" ${f} "* ]]; then done < <(brew bundle list --all --file="$BREWFILE")
BREW_LIST_TRACKED_WITHOUT_DEPS+=("$f")
fi
done
array_diff BREW_LIST_UNTRACKED BREW_LIST_TRACKED_WITHOUT_DEPS BREW_LIST_BUNDLED declare -a BREW_LIST_TRACKED_WITHOUT_DEPS
for f in "${BREW_LIST_ALL[@]}"; do
# If there are no untracked packages, exit # shellcheck disable=SC2199
if [ ${#BREW_LIST_UNTRACKED[@]} -eq 0 ]; then if [[ " ${BREW_LIST_DEPENDENCIES[@]} " != *" ${f} "* ]]; then
msgr yay "No untracked packages found!" BREW_LIST_TRACKED_WITHOUT_DEPS+=("$f")
exit 0
fi fi
done
echo "Untracked:" array_diff BREW_LIST_UNTRACKED BREW_LIST_TRACKED_WITHOUT_DEPS BREW_LIST_BUNDLED
for f in "${BREW_LIST_UNTRACKED[@]}"; do
echo " $f"
done
;;
autoupdate) # If there are no untracked packages, return
brew autoupdate delete if [ ${#BREW_LIST_UNTRACKED[@]} -eq 0 ]; then
brew autoupdate start 43200 --upgrade --cleanup --immediate msgr yay "No untracked packages found!"
;; return 0
fi
clean) brew bundle cleanup --file="$BREWFILE" && msgr yay "Done!" ;; echo "Untracked:"
for f in "${BREW_LIST_UNTRACKED[@]}"; do
echo " $f"
done
;;
*) menu_builder "$USAGE_PREFIX" "${MENU[@]}" ;; autoupdate)
esac brew autoupdate delete
} brew autoupdate start 43200 --upgrade --cleanup --immediate
;;
! x-have brew && menu_builder "$USAGE_PREFIX" "brew not available on this system" clean) brew bundle cleanup --file="$BREWFILE" && msgr yay "Done!" ;;
*) menu_builder "$USAGE_PREFIX" "${MENU[@]}" ;;
esac
} }
# Handle helper utility commands
section_helpers() section_helpers()
{ {
USAGE_PREFIX="$SCRIPT helpers <command>" USAGE_PREFIX="$SCRIPT helpers <command>"
@@ -305,10 +377,10 @@ section_helpers()
"wezterm:Show wezterm keybindings" "wezterm:Show wezterm keybindings"
) )
CMD="$1" CMD="${1:-}"
shift [[ $# -gt 0 ]] && shift
SECTION="$1" SECTION="${1:-}"
shift [[ $# -gt 0 ]] && shift
case "$CMD" in case "$CMD" in
path) path)
@@ -367,6 +439,7 @@ section_helpers()
esac esac
} }
# Handle apt package manager commands
section_apt() section_apt()
{ {
USAGE_PREFIX="$SCRIPT apt <command>" USAGE_PREFIX="$SCRIPT apt <command>"
@@ -379,57 +452,63 @@ section_apt()
"clean:Clean apt cache" "clean:Clean apt cache"
) )
x-have apt && { if ! x-have apt; then
case "$1" in msgr warn "apt not available, skipping"
upkeep) return 0
sudo apt update \ fi
&& sudo apt upgrade -y \
&& sudo apt autoremove -y \
&& sudo apt clean
;;
install) case "$1" in
# if apt.txt is not found, exit upkeep)
[ ! -f "$DOTFILES/tools/apt.txt" ] && msgr err "apt.txt not found" && exit 0 sudo apt update \
&& sudo apt upgrade -y \
&& sudo apt autoremove -y \
&& sudo apt clean
;;
# Load apt.txt, remove comments (even if trailing comment) and empty lines. install)
# # if apt.txt is not found, return with error
# Ignoring "Quote this to prevent word splitting." if [ ! -f "$DOTFILES/tools/apt.txt" ]; then
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 \ sudo apt install -y $(
-y $( grep -vE '^\s*#' "$HOST_APT" \
grep -vE '^\s*#' "$DOTFILES/tools/apt.txt" \ | sed -e 's/#.*//' \
| sed -e 's/#.*//' \ | tr '\n' ' '
| tr '\n' ' ' )
) }
# If there's a apt.txt file under hosts/$hostname/apt.txt, # Try this for an alternative way to install packages
# run install on those lines too. # xargs -a <(awk '! /^ *(#|$)/' "$packagelist") -r -- sudo apt-get install -y
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' ' '
)
}
# Try this for an alternative way to install packages update) sudo apt update ;;
# xargs -a <(awk '! /^ *(#|$)/' "$packagelist") -r -- sudo apt-get install -y upgrade) sudo apt upgrade -y ;;
;; autoremove) sudo apt autoremove -y ;;
clean) sudo apt clean ;;
update) sudo apt update ;; *) menu_builder "$USAGE_PREFIX" "${MENU[@]}" ;;
upgrade) sudo apt upgrade -y ;; esac
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"
} }
# Handle documentation generation commands
section_docs() section_docs()
{ {
USAGE_PREFIX="$SCRIPT docs <command>" USAGE_PREFIX="$SCRIPT docs <command>"
@@ -454,6 +533,7 @@ section_docs()
esac esac
} }
# Handle dotfiles formatting and reset commands
section_dotfiles() section_dotfiles()
{ {
USAGE_PREFIX="$SCRIPT dotfiles <command>" USAGE_PREFIX="$SCRIPT dotfiles <command>"
@@ -521,6 +601,7 @@ section_dotfiles()
esac esac
} }
# Handle system check commands (arch, hostname)
section_check() section_check()
{ {
USAGE_PREFIX="$SCRIPT check <command>" USAGE_PREFIX="$SCRIPT check <command>"
@@ -534,50 +615,36 @@ section_check()
case "$1" in case "$1" in
a | arch) a | arch)
[[ $2 == "" ]] && echo "$X_ARCH" && exit 0 [[ $2 == "" ]] && echo "$X_ARCH" && return 0
[[ $X_ARCH == "$2" ]] && exit 0 || exit 1 [[ $X_ARCH == "$2" ]] && return 0 || return 1
;; ;;
h | host | hostname) h | host | hostname)
[[ $2 == "" ]] && echo "$X_HOSTNAME" && exit 0 [[ $2 == "" ]] && echo "$X_HOSTNAME" && return 0
[[ $X_HOSTNAME == "$2" ]] && exit 0 || exit 1 [[ $X_HOSTNAME == "$2" ]] && return 0 || return 1
;; ;;
*) menu_builder "$USAGE_PREFIX" "${MENU[@]}" ;; *) menu_builder "$USAGE_PREFIX" "${MENU[@]}" ;;
esac esac
} }
# Handle install script execution
section_scripts() 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
declare -A SCRIPT_MENU local menu_items=()
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")
SCRIPT_MENU[$name]="$desc" menu_items+=("$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[@]}"
;; ;;
*) *)
@@ -609,7 +676,7 @@ section_tests()
echo " $i" echo " $i"
done done
;; ;;
msg) msgr)
# 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"
@@ -629,11 +696,12 @@ section_tests()
esac esac
} }
# Display main usage information for all sections
usage() 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 ""
@@ -652,10 +720,11 @@ usage()
section_helpers section_helpers
} }
# Parse section argument and dispatch to handler
main() main()
{ {
SECTION="$1" SECTION="${1:-}"
shift [[ $# -gt 0 ]] && 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 "$@" ;;
@@ -667,7 +736,7 @@ main()
docs) section_docs "$@" ;; docs) section_docs "$@" ;;
scripts) section_scripts "$@" ;; scripts) section_scripts "$@" ;;
tests) section_tests "$@" ;; tests) section_tests "$@" ;;
*) usage && exit 0 ;; *) usage && return 0 ;;
esac esac
} }

View File

@@ -22,32 +22,37 @@ if [ "$DEBUG" -eq 1 ]; then
set -x set -x
fi fi
# Output functions # Print an error message in red
msg_err() msg_err()
{ {
echo -e "\e[31m$*\e[0m" >&2 echo -e "\e[31m$*\e[0m" >&2
} }
# Print a success message in green
msg_success() msg_success()
{ {
echo -e "\e[32m$*\e[0m" echo -e "\e[32m$*\e[0m"
} }
# Print a warning message in yellow
msg_warn() msg_warn()
{ {
echo -e "\e[33m$*\e[0m" >&2 echo -e "\e[33m$*\e[0m" >&2
} }
# Print an info message in blue
msg_info() msg_info()
{ {
echo -e "\e[36m$*\e[0m" echo -e "\e[36m$*\e[0m"
} }
# Print a debug message when verbose mode is on
msg_debug() msg_debug()
{ {
[[ $VERBOSE -eq 1 ]] && echo -e "\e[35m$*\e[0m" [[ $VERBOSE -eq 1 ]] && echo -e "\e[35m$*\e[0m"
} }
# Display usage information and examples
show_help() show_help()
{ {
cat << EOF cat << EOF

View File

@@ -90,13 +90,14 @@ declare -A DIR_HAS_REPOS
# Record start time # Record start time
START_TIME=$(date +%s) START_TIME=$(date +%s)
# Logging functions # Log an error message
log_error() log_error()
{ {
print_color "31" "ERROR:" >&2 print_color "31" "ERROR:" >&2
echo " $*" >&2 echo " $*" >&2
} }
# Log an informational message
log_info() log_info()
{ {
if [[ $VERBOSE -eq 1 ]]; then if [[ $VERBOSE -eq 1 ]]; then
@@ -105,6 +106,7 @@ log_info()
fi fi
} }
# Log a warning message
log_warn() log_warn()
{ {
print_color "33" "WARNING:" >&2 print_color "33" "WARNING:" >&2
@@ -911,6 +913,7 @@ process_in_parallel()
echo -e "\nProcessed $total repositories in $dur (Total runtime: $runtime)" echo -e "\nProcessed $total repositories in $dur (Total runtime: $runtime)"
} }
# Check a directory for git status with progress tracking
check_directory_with_progress() check_directory_with_progress()
{ {
local dir local dir

View File

@@ -23,21 +23,25 @@ CLR_RESET="\033[0m"
# │ Color functions │ # │ Color functions │
# ╰──────────────────────────────────────────────────────────╯ # ╰──────────────────────────────────────────────────────────╯
# Wrap text in red color
function __color_red() function __color_red()
{ {
local MSG="$1" local MSG="$1"
echo -e "${CLR_RED}${MSG}${CLR_RESET}" echo -e "${CLR_RED}${MSG}${CLR_RESET}"
} }
# Wrap text in yellow color
function __color_yellow() function __color_yellow()
{ {
local MSG="$1" local MSG="$1"
echo -e "${CLR_YELLOW}${MSG}${CLR_RESET}" echo -e "${CLR_YELLOW}${MSG}${CLR_RESET}"
} }
# Wrap text in green color
function __color_green() function __color_green()
{ {
local MSG="$1" local MSG="$1"
echo -e "${CLR_GREEN}${MSG}${CLR_RESET}" echo -e "${CLR_GREEN}${MSG}${CLR_RESET}"
} }
# Wrap text in blue color
function __color_blue() function __color_blue()
{ {
local MSG="$1" local MSG="$1"
@@ -48,36 +52,43 @@ function __color_blue()
# │ Helpers │ # │ Helpers │
# ╰──────────────────────────────────────────────────────────╯ # ╰──────────────────────────────────────────────────────────╯
# Print blue arrow marker
function __log_marker() function __log_marker()
{ {
echo -e "${CLR_BLUE}➜${CLR_RESET}" echo -e "${CLR_BLUE}➜${CLR_RESET}"
} }
# Print green checkmark marker
function __log_marker_ok() function __log_marker_ok()
{ {
echo -e "${CLR_GREEN}✔${CLR_RESET}" echo -e "${CLR_GREEN}✔${CLR_RESET}"
} }
# Print blue checkmark marker
function __log_marker_ok_blue() function __log_marker_ok_blue()
{ {
echo -e "${CLR_BLUE}✔${CLR_RESET}" echo -e "${CLR_BLUE}✔${CLR_RESET}"
} }
# Print yellow warning marker
function __log_marker_warn() function __log_marker_warn()
{ {
echo -e "${CLR_YELLOW}⁕${CLR_RESET}" echo -e "${CLR_YELLOW}⁕${CLR_RESET}"
} }
# Print yellow question marker
function __log_marker_question() function __log_marker_question()
{ {
echo -e "${CLR_YELLOW}?${CLR_RESET}" echo -e "${CLR_YELLOW}?${CLR_RESET}"
} }
# Print red error marker
function __log_marker_err() function __log_marker_err()
{ {
echo -e "${CLR_RED}⛌${CLR_RESET}" echo -e "${CLR_RED}⛌${CLR_RESET}"
} }
# Print indentation spacing
function __log_indent() function __log_indent()
{ {
echo " " echo " "
@@ -87,71 +98,85 @@ function __log_indent()
# │ Log functions │ # │ Log functions │
# ╰──────────────────────────────────────────────────────────╯ # ╰──────────────────────────────────────────────────────────╯
# Print a message with arrow marker
function msg() function msg()
{ {
echo -e "$(__log_marker) $1" echo -e "$(__log_marker) $1"
} }
# Print a celebration message
function msg_yay() function msg_yay()
{ {
echo -e "🎉 $1" echo -e "🎉 $1"
} }
# Print a celebration message with checkmark
function msg_yay_done() function msg_yay_done()
{ {
echo -e "🎉 $1 ...$(__log_marker_ok)" echo -e "🎉 $1 ...$(__log_marker_ok)"
} }
# Print a message with completion checkmark
function msg_done() function msg_done()
{ {
echo -e "$(__log_marker) $1 ...$(__log_marker_ok)" echo -e "$(__log_marker) $1 ...$(__log_marker_ok)"
} }
# Print a completion checkmark suffix
function msg_done_suffix() function msg_done_suffix()
{ {
echo -e "$(__log_marker) ...$(__log_marker_ok)" echo -e "$(__log_marker) ...$(__log_marker_ok)"
} }
# Print a prompt-style message
function msg_prompt() function msg_prompt()
{ {
echo -e "$(__log_marker_question) $1" echo -e "$(__log_marker_question) $1"
} }
# Print a prompt message with checkmark
function msg_prompt_done() function msg_prompt_done()
{ {
echo -e "$(__log_marker_question) $1 ...$(__log_marker_ok)" echo -e "$(__log_marker_question) $1 ...$(__log_marker_ok)"
} }
# Print an indented message
function msg_nested() function msg_nested()
{ {
echo -e "$(__log_indent)$(__log_marker) $1" echo -e "$(__log_indent)$(__log_marker) $1"
} }
# Print an indented message with checkmark
function msg_nested_done() function msg_nested_done()
{ {
echo -e "$(__log_indent)$(__log_marker) $1 ...$(__log_marker_ok)" echo -e "$(__log_indent)$(__log_marker) $1 ...$(__log_marker_ok)"
} }
# Print a running-task message in green
function msg_run() function msg_run()
{ {
echo -e "${CLR_GREEN}➜ $1${CLR_RESET} $2" echo -e "${CLR_GREEN}➜ $1${CLR_RESET} $2"
} }
# Print a running-task message with checkmark
function msg_run_done() function msg_run_done()
{ {
echo -e "${CLR_GREEN}➜ $1${CLR_RESET} $2 ...$(__log_marker_ok)" echo -e "${CLR_GREEN}➜ $1${CLR_RESET} $2 ...$(__log_marker_ok)"
} }
# Print an ok/success message
function msg_ok() function msg_ok()
{ {
echo -e "$(__log_marker_ok) $1" echo -e "$(__log_marker_ok) $1"
} }
# Print a warning message
function msg_warn() function msg_warn()
{ {
echo -e "$(__log_marker_warn) $1" echo -e "$(__log_marker_warn) $1"
} }
# Print an error message
function msg_err() function msg_err()
{ {
echo -e "$(__log_marker_err) $1" echo -e "$(__log_marker_err) $1"
@@ -174,6 +199,7 @@ ask()
# If this is being sourced, no need to run the next steps. # If this is being sourced, no need to run the next steps.
[ "$sourced" = 1 ] && return [ "$sourced" = 1 ] && return
# Run visual tests for all message types
function __tests() function __tests()
{ {
msg "[ msg ]" msg "[ msg ]"
@@ -192,6 +218,7 @@ function __tests()
msg_yay_done "[ yay_done ]" msg_yay_done "[ yay_done ]"
} }
# Show usage information and examples
function usage() function usage()
{ {
echo "usage: msgr [type] [message] [optional second message]" echo "usage: msgr [type] [message] [optional second message]"

View File

@@ -19,7 +19,7 @@ set -euo pipefail # Add error handling
LATEST_VERSION_FORMULA="php" # The formula name for latest PHP version LATEST_VERSION_FORMULA="php" # The formula name for latest PHP version
PHP_VERSION_FILE=".php-version" # File name to look for when auto-switching PHP_VERSION_FILE=".php-version" # File name to look for when auto-switching
# Switch brew php version # Verify that Homebrew is installed
function check_dependencies() function check_dependencies()
{ {
if ! command -v brew > /dev/null 2>&1; then if ! command -v brew > /dev/null 2>&1; then
@@ -28,6 +28,7 @@ function check_dependencies()
fi fi
} }
# Display help message and usage examples
function usage() function usage()
{ {
echo "Brew PHP Switcher - Switch between PHP versions installed via Homebrew" echo "Brew PHP Switcher - Switch between PHP versions installed via Homebrew"
@@ -53,6 +54,7 @@ function usage()
exit 0 exit 0
} }
# List all PHP versions installed via Homebrew
function list_php_versions() function list_php_versions()
{ {
# Check Homebrew's installation path for PHP versions # Check Homebrew's installation path for PHP versions
@@ -185,6 +187,7 @@ function list_php_versions()
done done
} }
# Convert a version number to a Homebrew formula name
function get_php_formula_for_version() function get_php_formula_for_version()
{ {
local version="$1" local version="$1"
@@ -199,6 +202,7 @@ function get_php_formula_for_version()
echo "php@$version" echo "php@$version"
} }
# Check if a Homebrew formula is installed
function check_formula_installed() function check_formula_installed()
{ {
local formula="$1" local formula="$1"
@@ -216,6 +220,7 @@ function check_formula_installed()
return 1 return 1
} }
# Unlink the currently active PHP version
function unlink_current_php() function unlink_current_php()
{ {
local current_formula="" local current_formula=""
@@ -241,6 +246,7 @@ function unlink_current_php()
fi fi
} }
# Link a specific PHP formula as the active version
function link_php_version() function link_php_version()
{ {
local formula="$1" local formula="$1"
@@ -265,6 +271,7 @@ function link_php_version()
fi fi
} }
# Display the currently active PHP version
function get_current_version() function get_current_version()
{ {
if ! command -v php > /dev/null 2>&1; then if ! command -v php > /dev/null 2>&1; then
@@ -300,6 +307,7 @@ function get_current_version()
fi fi
} }
# Validate PHP version format (x.y or latest)
function validate_version() function validate_version()
{ {
local version="$1" local version="$1"
@@ -312,6 +320,7 @@ function validate_version()
fi fi
} }
# Search for .php-version file in directory hierarchy
function find_php_version_file() function find_php_version_file()
{ {
local dir="$PWD" local dir="$PWD"
@@ -334,6 +343,7 @@ function find_php_version_file()
return 1 return 1
} }
# Auto-switch PHP based on .php-version file
function auto_switch_php_version() function auto_switch_php_version()
{ {
local version_file local version_file
@@ -360,6 +370,7 @@ function auto_switch_php_version()
switch_php_version "$version" switch_php_version "$version"
} }
# Switch to a specific PHP version
function switch_php_version() function switch_php_version()
{ {
local version="$1" local version="$1"
@@ -398,6 +409,7 @@ function switch_php_version()
echo "PHP executable: $(command -v php)" echo "PHP executable: $(command -v php)"
} }
# Parse arguments and dispatch to appropriate action
function main() function main()
{ {
local version="" local version=""

View File

@@ -5,6 +5,7 @@
# #
# Modified by Ismo Vuorinen <https://github.com/ivuorinen> 2023 # Modified by Ismo Vuorinen <https://github.com/ivuorinen> 2023
# Display usage information for pushover
__pushover_usage() __pushover_usage()
{ {
printf "pushover <options> <message>\n" printf "pushover <options> <message>\n"
@@ -23,6 +24,7 @@ __pushover_usage()
return 1 return 1
} }
# Format an optional curl form field
__pushover_opt_field() __pushover_opt_field()
{ {
field=$1 field=$1
@@ -33,6 +35,7 @@ __pushover_opt_field()
fi fi
} }
# Send a pushover notification via curl
__pushover_send_message() __pushover_send_message()
{ {
device="${1:-}" device="${1:-}"

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="$HOME/.config/alacritty" A_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/alacritty"
# Function to print usage information # Function to print usage information
usage() usage()

View File

@@ -7,13 +7,15 @@
# 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
msgr err "Directory $1 does not exist." echo "Error: Directory $1 does not exist." >&2
exit 1 exit 1
fi fi
else else
@@ -27,7 +29,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
msgr msg "Skipping symlink $dir" echo "Skipping symlink $dir"
return return
fi fi
@@ -35,18 +37,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
msgr run "Removing $dir/node_modules" echo "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
msgr run "Removing $dir/vendor" echo "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
remove_node_modules_vendor "$item" [ -d "$item" ] && remove_node_modules_vendor "$item"
done done
fi fi
} }

View File

@@ -10,6 +10,7 @@ VERSION="1.0.0"
LANG_MAP="c:.c,.h|cpp:.cpp,.cc,.cxx,.hpp,.hxx|csharp:.cs|go:.go|java:.java| LANG_MAP="c:.c,.h|cpp:.cpp,.cc,.cxx,.hpp,.hxx|csharp:.cs|go:.go|java:.java|
javascript:.js,.jsx,.mjs,.ts,.tsx|python:.py|ruby:.rb|swift:.swift" javascript:.js,.jsx,.mjs,.ts,.tsx|python:.py|ruby:.rb|swift:.swift"
# Display usage information and options
usage() usage()
{ {
cat << EOF cat << EOF
@@ -24,22 +25,26 @@ EOF
exit "${1:-0}" exit "${1:-0}"
} }
# Log a timestamped message to stderr
log() log()
{ {
printf '[%s] %s\n' "$(date '+%H:%M:%S')" "$*" >&2 printf '[%s] %s\n' "$(date '+%H:%M:%S')" "$*" >&2
} }
# Log an error message and exit
err() err()
{ {
log "ERROR: $*" log "ERROR: $*"
exit 1 exit 1
} }
# Verify codeql binary is available in PATH
check_codeql() check_codeql()
{ {
command -v codeql > /dev/null 2>&1 || err "codeql binary not found in PATH" command -v codeql > /dev/null 2>&1 || err "codeql binary not found in PATH"
log "Found codeql: $(codeql version --format=terse)" log "Found codeql: $(codeql version --format=terse)"
} }
# Get or create the CodeQL cache directory
get_cache_dir() get_cache_dir()
{ {
cache="${XDG_CACHE_HOME:-$HOME/.cache}/codeql" cache="${XDG_CACHE_HOME:-$HOME/.cache}/codeql"
@@ -47,6 +52,7 @@ get_cache_dir()
printf '%s' "$cache" printf '%s' "$cache"
} }
# Detect supported programming languages in source path
detect_languages() detect_languages()
{ {
src_path="$1" src_path="$1"
@@ -85,6 +91,7 @@ detect_languages()
printf '%s' "$detected" | tr ' ' '\n' | sort -u | tr '\n' ' ' | sed 's/ $//' printf '%s' "$detected" | tr ' ' '\n' | sort -u | tr '\n' ' ' | sed 's/ $//'
} }
# Create a CodeQL database for a language
create_database() create_database()
{ {
lang="$1" lang="$1"
@@ -98,6 +105,7 @@ create_database()
--overwrite --overwrite
} }
# Display analysis result statistics from SARIF file
show_results_stats() show_results_stats()
{ {
sarif_file="$1" sarif_file="$1"
@@ -126,6 +134,7 @@ show_results_stats()
return 0 return 0
} }
# Run CodeQL analysis for a single language
analyze_language() analyze_language()
{ {
lang="$1" lang="$1"
@@ -172,6 +181,7 @@ analyze_language()
rm -rf "$db_path" rm -rf "$db_path"
} }
# Parse arguments and run CodeQL analysis pipeline
main() main()
{ {
src_path="." src_path="."

View File

@@ -24,7 +24,7 @@ str_to_operator = {
def vercmp(expr): def vercmp(expr):
"""Version Comparison function.""" """Version Comparison function."""
words = expr.split() words = expr.split()
comparisons = [words[i: i + 3] for i in range(0, len(words) - 2, 2)] comparisons = [words[i : i + 3] for i in range(0, len(words) - 2, 2)]
for left, op_str, right in comparisons: for left, op_str, right in comparisons:
compare_op = str_to_operator[op_str] compare_op = str_to_operator[op_str]
if not compare_op(version.parse(left), version.parse(right)): if not compare_op(version.parse(left), version.parse(right)):
@@ -63,7 +63,7 @@ def test():
except KeyError: except KeyError:
pass pass
else: else:
assert False, "invalid operator did not raise" raise AssertionError("invalid operator did not raise")
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env 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.
@@ -190,6 +190,7 @@ get_custom_group()
return 1 return 1
} }
# Check if a key matches the skipped keys list
is_skipped() is_skipped()
{ {
local key=$1 local key=$1

Some files were not shown because too many files have changed in this diff Show More