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