/ Taskfile.yml
Taskfile.yml
  1  # yaml-language-server: $schema=https://taskfile.dev/schema.json
  2  version: "3"
  3  dotenv: [".env"]
  4  vars:
  5    SHELL: /usr/bin/env bash -o pipefail
  6    DOTFILES_HOME: "{{.USER_WORKING_DIR}}"
  7    DOTFILES_CONFIG: "{{.DOTFILES_HOME}}/etc/config"
  8    DOTFILES_PROFILE: "{{.DOTFILES_HOME}}/etc/profile"
  9    DOTFILES_RC: "{{.DOTFILES_HOME}}/etc/rc"
 10    DOTFILES_SCRIPTS: "{{.DOTFILES_HOME}}/scripts"
 11  includes:
 12    common:install:core:
 13      taskfile: ./tasks/common/install/core/Taskfile.yml
 14    linux:install:apt:
 15      taskfile: ./tasks/linux/install/apt/Taskfile.yml
 16    macos:install:brew:
 17      taskfile: ./tasks/macos/install/brew/Taskfile.yml
 18    macos:install:core:
 19      taskfile: ./tasks/macos/install/core/Taskfile.yml
 20    macos:install:docker:
 21      taskfile: ./tasks/macos/install/docker/Taskfile.yml
 22    macos:install:go:
 23      taskfile: ./tasks/macos/install/go/Taskfile.yml
 24    macos:install:java:
 25      taskfile: ./tasks/macos/install/java/Taskfile.yml
 26    macos:install:k8s:
 27      taskfile: ./tasks/macos/install/k8s/Taskfile.yml
 28    macos:install:hammerspoon:
 29      taskfile: ./tasks/macos/install/hammerspoon/Taskfile.yml
 30    macos:install:rust:
 31      taskfile: ./tasks/macos/install/rust/Taskfile.yml
 32  tasks:
 33    default:
 34      cmds:
 35        - task: list
 36      silent: true
 37    list:
 38      desc: Lists available commands
 39      cmds:
 40        - task -l
 41    hooks:
 42      desc: Setup git hooks locally
 43      cmds:
 44        - cp scripts/hooks/* .git/hooks/
 45      silent: true
 46    test:lua:
 47      desc: "Run Lua Gherkin features (byfeature-style output with colors) and Busted specs"
 48      dir: "{{.DOTFILES_HOME}}"
 49      cmds:
 50        - cmd: "LUA_PATH='{{.DOTFILES_HOME}}/lua_modules/share/lua/5.4/?.lua;{{.DOTFILES_HOME}}/lua_modules/share/lua/5.4/?/init.lua;;' LUA_CPATH='{{.DOTFILES_HOME}}/lua_modules/lib/lua/5.4/?.so;;' lua tests/run_features.lua"
 51        - cmd: "LUA_PATH='{{.DOTFILES_HOME}}/lua_modules/share/lua/5.4/?.lua;{{.DOTFILES_HOME}}/lua_modules/share/lua/5.4/?/init.lua;;' LUA_CPATH='{{.DOTFILES_HOME}}/lua_modules/lib/lua/5.4/?.so;;' lua {{.DOTFILES_HOME}}/lua_modules/lib/luarocks/rocks-5.4/busted/2.3.0-1/bin/busted tests/spec --no-coverage"
 52    precommit:
 53      desc: Verifies and fix requirements for new commits
 54      cmds:
 55        - scripts/hooks/pre-commit
 56    markdownlint:
 57      desc: Lint Markdown with markdownlint-cli (.markdownlint.yaml)
 58      dir: "{{.TASKFILE_DIR}}"
 59      cmds:
 60        - cmd: "markdownlint -c .markdownlint.yaml '**/*.md' -i CHANGELOG.md"
 61    yamlfmt:
 62      desc: "Format YAML files with yamlfmt (install with: go install github.com/google/yamlfmt/cmd/yamlfmt@latest)"
 63      dir: "{{.TASKFILE_DIR}}"
 64      cmds:
 65        - |
 66          find . \( -name "*.yaml" -o -name "*.yml" \) \
 67            -not -path "./.git/*" \
 68            -not -path "./node_modules/*" \
 69            -not -path "./.devbox/*" \
 70            -exec yamlfmt -w {} + 2>/dev/null || true
 71    jsonfmt:
 72      desc: Format JSON with jq (sort keys, validate)
 73      dir: "{{.TASKFILE_DIR}}"
 74      cmds:
 75        - |
 76          for f in $(git ls-files '*.json' 2>/dev/null); do
 77            [ -f "$f" ] && jq -e . "$f" >/dev/null 2>&1 && jq -S . "$f" > "$f.tmp" && mv "$f.tmp" "$f" || true
 78          done
 79    version:next:
 80      desc: "Calculate next semver from conventional commits since last v* tag (feat→minor, fix→patch, breaking→major)"
 81      dir: "{{.TASKFILE_DIR}}"
 82      cmds:
 83        - scripts/calc-next-version.sh
 84    changelog:
 85      silent: true
 86      desc: Generates CHANGELOG
 87      cmds:
 88        - echo "Updating Changelog"
 89        - cmd: |-
 90            export RUST_LOG=error
 91            git cliff --context \
 92            | jq '.[].commits.[] |= (. + {extra: {date: ( .committer.timestamp | strftime("%Y-%m-%d") )}} ) | sort_by(.commits.[].commit.committer.timestamp)' \
 93            | jq -S 'walk(if type == "array" then sort_by(.timestamp) | reverse else . end)' \
 94            | git cliff --from-context - > CHANGELOG.md
 95        - cmd: git add {{.USER_WORKING_DIR}}/CHANGELOG.md
 96    changelog:rewrite-history:
 97      desc: "Rewrite all commit messages to conventional format (DESTRUCTIVE; backup first, then force-push)"
 98      dir: "{{.TASKFILE_DIR}}"
 99      cmds:
100        - |
101          echo "This rewrites git history (new SHAs). Backup first: git clone --mirror . ../.dotfiles-backup.git"
102          echo "After running: git push --force-with-lease"
103          read -p "Continue? [y/N] " r; [[ "$r" != "y" && "$r" != "Y" ]] && exit 1
104          git filter-branch -f --msg-filter "{{.TASKFILE_DIR}}/scripts/rewrite-commit-messages.sh" -- --all
105    devbox:install-cli:
106      desc: Install Devbox CLI (required before devbox:install; uses official install script)
107      dir: "{{.TASKFILE_DIR}}"
108      cmds:
109        - |
110          if command -v devbox >/dev/null 2>&1; then
111            echo "devbox already installed: $(devbox version 2>/dev/null || devbox --version 2>/dev/null || true)"
112            exit 0
113          fi
114          echo "Installing Devbox via official script (https://www.jetify.com/docs/devbox/installing-devbox)..."
115          echo "Devbox may install Nix if not present. Run as non-root on Linux."
116          curl -fsSL https://get.jetify.com/devbox | bash
117    devbox:install:
118      desc: Install devbox dependencies (run once after clone or when devbox.json changes)
119      dir: "{{.TASKFILE_DIR}}"
120      preconditions:
121        - sh: command -v devbox
122          msg: "devbox not in PATH. Run 'task devbox:install-cli' first, then retry."
123        - sh: test -f "{{.TASKFILE_DIR}}/devbox.json"
124          msg: "devbox.json not found in project root."
125      cmds:
126        - devbox install
127    devbox:shell:
128      desc: Enter devbox shell with all dev tools on PATH
129      dir: "{{.TASKFILE_DIR}}"
130      preconditions:
131        - sh: command -v devbox
132          msg: "devbox not in PATH. Run 'task devbox:install-cli' first."
133        - sh: test -f "{{.TASKFILE_DIR}}/devbox.json"
134          msg: "devbox.json not found in project root."
135      cmds:
136        - devbox shell
137    devbox:add:
138      desc: "Add a package to devbox.json and pull globally. Usage: task devbox:add -- <package@version>"
139      dir: "{{.TASKFILE_DIR}}"
140      preconditions:
141        - sh: command -v devbox
142          msg: "devbox not in PATH. Run 'task devbox:install-cli' first."
143        - sh: test -f "{{.TASKFILE_DIR}}/devbox.json"
144          msg: "devbox.json not found in project root."
145        - sh: '[ -n "{{.CLI_ARGS}}" ]'
146          msg: "No package specified. Usage: task devbox:add -- <package@version>"
147      cmds:
148        - devbox add {{.CLI_ARGS}}
149        - devbox global pull {{.TASKFILE_DIR}}/devbox.json -f
150    devbox:gc:
151      desc: Run Nix garbage collection and store optimisation to reclaim disk space
152      dir: "{{.TASKFILE_DIR}}"
153      cmds:
154        - nix-collect-garbage -d
155        - nix-store --optimise
156    nvim:update:
157      desc: Download and install the latest Neovim nightly from upstream (always runs)
158      cmds:
159        - task: common:install:core:nvim:update
160    nvim:deps:
161      desc: Install and verify all Neovim external deps (tree-sitter via prebuilt binary; rest via devbox)
162      dir: "{{.TASKFILE_DIR}}"
163      cmds:
164        - |
165          # tree-sitter: nixpkgs tops out at 0.25.x; nvim-treesitter requires 0.26.1+
166          # cargo build fails on Linux due to clang/system header path issues, so use the prebuilt binary.
167          TS_VERSION="0.26.7"
168          if command -v tree-sitter >/dev/null 2>&1 && tree-sitter --version 2>/dev/null | grep -qE '0\.2[6-9]\.'; then
169            echo "  ✓ tree-sitter ($(tree-sitter --version | head -1))"
170          else
171            case "$(uname -s)-$(uname -m)" in
172              Linux-x86_64)  TS_ASSET="tree-sitter-linux-x64" ;;
173              Linux-aarch64) TS_ASSET="tree-sitter-linux-arm64" ;;
174              Darwin-x86_64) TS_ASSET="tree-sitter-macos-x64" ;;
175              Darwin-arm64)  TS_ASSET="tree-sitter-macos-arm64" ;;
176              *) echo "  ✗ unsupported platform for tree-sitter prebuilt binary" >&2; exit 1 ;;
177            esac
178            echo "  ↳ downloading tree-sitter v${TS_VERSION} (${TS_ASSET})..."
179            URL="https://github.com/tree-sitter/tree-sitter/releases/download/v${TS_VERSION}/${TS_ASSET}.gz"
180            curl -fsSL "$URL" | gunzip -c > "${HOME}/.local/bin/tree-sitter"
181            chmod +x "${HOME}/.local/bin/tree-sitter"
182            echo "  ✓ tree-sitter ($(tree-sitter --version | head -1))"
183          fi
184        - |
185          ok=1
186          for tool in stylua shfmt ruff prettierd gopls rust-analyzer gofumpt golangci-lint govulncheck dlv chafa viu; do
187            if command -v "$tool" >/dev/null 2>&1; then
188              echo "  ✓ $tool"
189            else
190              echo "  ✗ $tool - not found (run 'devbox install' or 'devbox global pull devbox.json -f')" >&2
191              ok=0
192            fi
193          done
194          [ "$ok" -eq 1 ] || exit 1
195        - echo "All deps present. For LuaRocks run 'task nvim:luarocks-build', for blink.cmp native lib run 'task nvim:blink-build'."
196    nvim:luarocks-build:
197      desc: Run LuaRocks.nvim build once (compiles local LuaRocks; then setup() installs rocks)
198      dir: "{{.TASKFILE_DIR}}"
199      cmds:
200        - |
201          PLUGIN_DIR=$(find "${XDG_DATA_HOME:-$HOME/.local/share}/nvim/site/pack" -maxdepth 4 -type d -name "luarocks.nvim" 2>/dev/null | head -1)
202          if [ -z "$PLUGIN_DIR" ]; then
203            echo "luarocks.nvim not found in pack dir. Start nvim once so plugins are installed, then run this task again." >&2
204            exit 1
205          fi
206          if [ ! -f "$PLUGIN_DIR/build.lua" ]; then
207            echo "build.lua not found in $PLUGIN_DIR" >&2
208            exit 1
209          fi
210          echo "Running LuaRocks.nvim build in $PLUGIN_DIR ..."
211          LUA51_PREFIX=$(ls -d /nix/store/*lua-5.1*[^.drv] 2>/dev/null | head -1)
212          if [ -n "$LUA51_PREFIX" ] && [ -f "$LUA51_PREFIX/bin/lua" ]; then
213            TMPLUA=$(mktemp -d)
214            mkdir -p "$TMPLUA/bin" "$TMPLUA/include"
215            ln -s "$LUA51_PREFIX/bin/lua" "$TMPLUA/bin/lua5.1"
216            ln -s "$LUA51_PREFIX/include" "$TMPLUA/include/lua5.1"
217            export PATH="$TMPLUA/bin:$PATH"
218          fi
219          cd "$PLUGIN_DIR" && nvim -l build.lua
220          [ -n "$TMPLUA" ] && rm -rf "$TMPLUA"
221    nvim:blink-build:
222      desc: Build blink.cmp native fuzzy lib from source via cargo (fixes blink_cmp_fuzzy checkhealth warning)
223      dir: "{{.TASKFILE_DIR}}"
224      cmds:
225        - |
226          PLUGIN_DIR=$(find "${XDG_DATA_HOME:-$HOME/.local/share}/nvim/site/pack" -maxdepth 4 -type d -name "blink.cmp" 2>/dev/null | head -1)
227          if [ -z "$PLUGIN_DIR" ]; then
228            echo "blink.cmp not found in pack dir. Start nvim once so plugins are installed, then run this task again." >&2
229            exit 1
230          fi
231          echo "Building blink.cmp native fuzzy lib in $PLUGIN_DIR ..."
232          cd "$PLUGIN_DIR" && cargo build --release
233          echo "Done. Restart Neovim and run :checkhealth to verify."
234    nvim:pack-lock-sync:
235      desc: Sync nvim-pack-lock.json with full SHAs from installed plugins (fixes vim.pack lockfile checkhealth errors)
236      dir: "{{.TASKFILE_DIR}}"
237      cmds:
238        - nvim -l {{.TASKFILE_DIR}}/scripts/nvim-pack-lock-sync.lua
239    nvim:reset:
240      desc: "Backup ~/.local/share/nvim, ~/.local/state/nvim, ~/.cache/nvim with timestamped suffix (e.g. nvim.bak-YYYYMMDD-HHMMSS). Leaves ~/.config/nvim untouched so your config (or symlink) stays; start nvim and pack will auto-install plugins from scratch like Lazy did."
241      dir: "{{.TASKFILE_DIR}}"
242      cmds:
243        - ./scripts/backup-nvim-dirs.sh
244    nvim:restore:
245      desc: "Rollback to the most recent nvim backup (nvim.bak-*). Stashes any current dirs as nvim.broken-TIMESTAMP before restoring, so nothing is lost."
246      dir: "{{.TASKFILE_DIR}}"
247      cmds:
248        - ./scripts/restore-nvim-dirs.sh
249    linux:install:all:
250      desc: Install base packages and core setup on Linux (apt + common core)
251      cmds:
252        - task: linux:install:apt:all
253        - task: common:install:core:all
254    macos:install:all:
255      desc: Install all MacOS required tools
256      cmds:
257        - task: macos:install:brew:all
258        - task: macos:install:core:all
259        - task: macos:install:docker:all
260        - task: macos:install:go:all
261        - task: macos:install:k8s:all
262        - task: macos:install:java:all
263        - task: macos:install:npm:all
264        - task: macos:install:python:all
265        - task: macos:install:rust:all