Add dev_env role: zsh/tmux/nvim for workstation-class hosts
A new role (separate from base) that gives workstation-class hosts (ubongo now, mamba later) a clean interactive environment: zsh + oh-my-zsh + oh-my-posh, tmux + TPM plugins, and neovim. Dotfiles are real files deployed via GNU stow (not templated); pinned nvim v0.12.2 + oh-my-posh 29.0.1. Configs re-derived (ADR-013) from AnsibleBaobabV4 + the operator's fisi setup on boma's terms: no Nerd Font (headless host), no system LSP suite (nvim uses mason), versions pinned (V4 tracks latest). Applied via playbooks/workstation.yml to the control group for users sjat + claude. Lint + Molecule (idempotent) green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b9daf2a0ad
commit
f3f382ae69
36 changed files with 1807 additions and 0 deletions
58
docs/superpowers/plans/2026-06-11-dev-env-role.md
Normal file
58
docs/superpowers/plans/2026-06-11-dev-env-role.md
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
# `dev_env` Role — Implementation Plan (iteration 1)
|
||||||
|
|
||||||
|
> Built in the same 2026-06-11 session as the `ubongo` bring-up. A developer
|
||||||
|
> interactive environment (zsh/tmux/nvim) for **workstation-class** hosts.
|
||||||
|
|
||||||
|
**Goal:** Give `ubongo` (and future `mamba`) a clean interactive shell/editor setup,
|
||||||
|
reproducibly, as a boma-native Ansible role — so the operator (and the `claude` agent
|
||||||
|
user) can work comfortably over SSH.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
- **Separate role, never part of `base`.** `base` is the security/infra baseline for
|
||||||
|
*every* host; a dev environment is only for human workstation-class hosts. Servers and
|
||||||
|
service VMs must never get it.
|
||||||
|
- **Stow, not templating.** Dotfiles are **real files** under `files/dotfiles/{zsh,tmux,nvim}/`
|
||||||
|
(re-derived `$HOME`-relative from `fisi`'s live configs), symlinked into `~` with GNU
|
||||||
|
stow. No Jinja-templated dotfiles (they rot; you'd edit templates not configs).
|
||||||
|
- **Users:** `dev_env__users` (default `[]`). Set to `[sjat, claude]` for `ubongo` in
|
||||||
|
`group_vars/control`.
|
||||||
|
- **V4 (ADR-013):** configs/package-lists/install-mechanism *consulted* from V4 and
|
||||||
|
**re-derived on boma's terms** — not its structure. V4 identifiers stripped from the
|
||||||
|
dotfiles.
|
||||||
|
|
||||||
|
## Re-derivations vs V4
|
||||||
|
|
||||||
|
- **No Nerd Font** on `ubongo` — it's headless; fonts are a client-side concern.
|
||||||
|
- **No system-wide LSP suite** — the operator's nvim uses **mason**, which self-installs
|
||||||
|
LSPs/formatters inside nvim (needs only nvim + git + a C compiler + node).
|
||||||
|
- **Pinned versions** (ADR-014): nvim `v0.12.2`, oh-my-posh `29.0.1` (V4 tracks "latest").
|
||||||
|
- **Plugins self-bootstrap**: lazy.nvim installs nvim plugins on first launch; the role
|
||||||
|
only lays down config + pre-clones omz/tmux plugins.
|
||||||
|
|
||||||
|
## Tasks (role: `roles/dev_env/`)
|
||||||
|
|
||||||
|
- `tasks/main.yml` — apt packages (`packages` tag) → include `neovim.yml`, `oh_my_posh.yml`
|
||||||
|
→ loop `per_user.yml` over `dev_env__users`.
|
||||||
|
- `tasks/neovim.yml` — install pinned nvim release to `/opt`, symlink, version sentinel.
|
||||||
|
- `tasks/oh_my_posh.yml` — install pinned oh-my-posh binary + deploy `zen.toml` to `/etc`.
|
||||||
|
- `tasks/per_user.yml` — set login shell to zsh (`users`); clone oh-my-zsh + custom
|
||||||
|
plugins + tmux/TPM plugins; copy dotfiles to `~/.dotfiles`; `stow` into `~` (`config`).
|
||||||
|
- `defaults/main.yml`, `meta/main.yml`, `README.md`, `requirements.yml`.
|
||||||
|
- `molecule/default/{converge,verify}.yml` — create a `tester` user, apply, assert
|
||||||
|
packages + nvim/omp/zen present + shell=zsh + dotfiles stowed (symlinks).
|
||||||
|
- `playbooks/workstation.yml` — apply `dev_env` to the `control` group (ubongo).
|
||||||
|
- `inventories/production/group_vars/control/vars.yml` — `dev_env__users: [sjat, claude]`.
|
||||||
|
|
||||||
|
## Verify / apply
|
||||||
|
|
||||||
|
- `make lint`; `make test ROLE=dev_env` (Molecule, Debian 13) must pass.
|
||||||
|
- Apply to `ubongo`: `make check`/`deploy PLAYBOOK=workstation` from a host that can SSH
|
||||||
|
to `ubongo` as `sjat` with `--ask-become-pass` (the Ansible-manages-ubongo connection
|
||||||
|
isn't bootstrapped yet — handle at apply time).
|
||||||
|
|
||||||
|
## Deferred (iteration 2+)
|
||||||
|
|
||||||
|
- A proper `workstations` inventory group (when `mamba` joins) instead of reusing `control`.
|
||||||
|
- lazygit, extra CLI tooling, any system LSP/formatters mason can't cover.
|
||||||
|
- Pinning tmux plugins to commits (currently `master` except catppuccin `v1.0.3`).
|
||||||
7
inventories/production/group_vars/control/vars.yml
Normal file
7
inventories/production/group_vars/control/vars.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
# Workstation-class control node (ubongo, ADR-015) — developer-environment users.
|
||||||
|
# The operator and the dedicated AI-worker user both get the dev_env role (dotfiles,
|
||||||
|
# zsh/tmux/nvim), so `sudo -iu claude` lands in the same clean shell.
|
||||||
|
dev_env__users:
|
||||||
|
- sjat
|
||||||
|
- claude
|
||||||
10
playbooks/workstation.yml
Normal file
10
playbooks/workstation.yml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
# workstation.yml — developer environment for workstation-class hosts.
|
||||||
|
# Targets the `control` group (ubongo) for now; generalizes to a dedicated
|
||||||
|
# `workstations` group when mamba joins. Run: make deploy PLAYBOOK=workstation
|
||||||
|
- name: Apply developer environment to workstation-class hosts
|
||||||
|
hosts: control
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- role: dev_env
|
||||||
|
tags: [dev_env]
|
||||||
50
roles/dev_env/README.md
Normal file
50
roles/dev_env/README.md
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
# dev_env
|
||||||
|
|
||||||
|
Interactive developer environment for **workstation-class** boma hosts (`ubongo`, and
|
||||||
|
later `mamba`). Gives the operator — and the `claude` agent user — a clean shell/editor
|
||||||
|
setup over SSH: zsh + oh-my-zsh + oh-my-posh, tmux, and neovim.
|
||||||
|
|
||||||
|
**This is not part of `base`.** `base` is the security/infra baseline every host gets;
|
||||||
|
`dev_env` is only for human workstation-class hosts and must never be applied to servers
|
||||||
|
or service VMs.
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
- Installs packages: `zsh, tmux, git, stow, build-essential, curl, ca-certificates,
|
||||||
|
fzf, ripgrep, direnv, nodejs, npm` (`dev_env__packages`).
|
||||||
|
- Installs **pinned** neovim (`dev_env__nvim_version`) and oh-my-posh
|
||||||
|
(`dev_env__omp_version`) from GitHub releases, and the system-wide oh-my-posh theme
|
||||||
|
`/etc/oh-my-posh/zen.toml`.
|
||||||
|
- For each user in `dev_env__users`: sets the login shell to zsh, clones oh-my-zsh +
|
||||||
|
custom plugins and the tmux/TPM plugins, and **stows** the dotfiles into `~`.
|
||||||
|
|
||||||
|
## Dotfiles
|
||||||
|
|
||||||
|
Real files under `files/dotfiles/{zsh,tmux,nvim}/`, deployed to `~/.dotfiles/` and
|
||||||
|
symlinked into the home directory with GNU **stow** (not Jinja templates — so they stay
|
||||||
|
editable as live configs). nvim plugins self-bootstrap via lazy.nvim on first launch;
|
||||||
|
LSPs/formatters self-install via mason (no system LSP packages needed).
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
| Variable | Default | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `dev_env__users` | `[]` | Users to configure. Set per group, e.g. `group_vars/control → [sjat, claude]`. Empty = no per-user work. |
|
||||||
|
| `dev_env__nvim_version` | `v0.12.2` | Pinned neovim release. |
|
||||||
|
| `dev_env__omp_version` | `29.0.1` | Pinned oh-my-posh release. |
|
||||||
|
| `dev_env__packages` | see defaults | APT packages. |
|
||||||
|
| `dev_env__omz_custom_plugins` | autosuggestions, syntax-highlighting | Cloned into `~/.oh-my-zsh/custom/plugins`. |
|
||||||
|
| `dev_env__tmux_plugins` | tpm, tmux-sensible, vim-tmux-navigator, catppuccin@v1.0.3 | Cloned into `~/.tmux/plugins`. |
|
||||||
|
|
||||||
|
## Apply
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make test ROLE=dev_env # Molecule (Debian 13)
|
||||||
|
make deploy PLAYBOOK=workstation # applies to the control group (ubongo)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Provenance
|
||||||
|
|
||||||
|
Configs re-derived (ADR-013) from the heritage `AnsibleBaobabV4` repo and the operator's
|
||||||
|
live `fisi` setup, on boma's terms — V4's structure was not imported. No Nerd Font is
|
||||||
|
installed (headless host; fonts are a client-side concern).
|
||||||
51
roles/dev_env/defaults/main.yml
Normal file
51
roles/dev_env/defaults/main.yml
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
---
|
||||||
|
# dev_env — interactive developer environment (zsh/tmux/nvim) for workstation-class
|
||||||
|
# hosts. Its own role, never part of `base`. See the role README and ADR-015.
|
||||||
|
|
||||||
|
# Users who receive the environment. Empty = no-op. Set in group_vars for the
|
||||||
|
# workstation-class group (e.g. group_vars/control → [sjat, claude]).
|
||||||
|
dev_env__users: []
|
||||||
|
|
||||||
|
# APT packages. nvim + oh-my-posh are installed separately from pinned releases.
|
||||||
|
# (nvim uses mason internally for LSPs, so no system LSP packages are needed; node is
|
||||||
|
# present so mason's node-based servers work. direnv is referenced by the .zshrc.)
|
||||||
|
dev_env__packages:
|
||||||
|
- zsh
|
||||||
|
- tmux
|
||||||
|
- git
|
||||||
|
- stow
|
||||||
|
- build-essential
|
||||||
|
- curl
|
||||||
|
- ca-certificates
|
||||||
|
- fzf
|
||||||
|
- ripgrep
|
||||||
|
- direnv
|
||||||
|
- nodejs
|
||||||
|
- npm
|
||||||
|
|
||||||
|
# Pinned tool versions (ADR-014 — pin, don't track "latest").
|
||||||
|
dev_env__nvim_version: "v0.12.2"
|
||||||
|
dev_env__omp_version: "29.0.1"
|
||||||
|
|
||||||
|
# oh-my-zsh custom plugins (cloned per user into ~/.oh-my-zsh/custom/plugins).
|
||||||
|
dev_env__omz_custom_plugins:
|
||||||
|
- name: zsh-autosuggestions
|
||||||
|
repo: "https://github.com/zsh-users/zsh-autosuggestions.git"
|
||||||
|
- name: zsh-syntax-highlighting
|
||||||
|
repo: "https://github.com/zsh-users/zsh-syntax-highlighting.git"
|
||||||
|
|
||||||
|
# tmux plugins (pre-cloned per user into ~/.tmux/plugins; TPM loads them on tmux start).
|
||||||
|
# catppuccin is pinned to the v1 API the .tmux.conf targets.
|
||||||
|
dev_env__tmux_plugins:
|
||||||
|
- name: tpm
|
||||||
|
repo: "https://github.com/tmux-plugins/tpm.git"
|
||||||
|
version: master
|
||||||
|
- name: tmux-sensible
|
||||||
|
repo: "https://github.com/tmux-plugins/tmux-sensible.git"
|
||||||
|
version: master
|
||||||
|
- name: vim-tmux-navigator
|
||||||
|
repo: "https://github.com/christoomey/vim-tmux-navigator.git"
|
||||||
|
version: master
|
||||||
|
- name: tmux
|
||||||
|
repo: "https://github.com/catppuccin/tmux.git"
|
||||||
|
version: v1.0.3
|
||||||
26
roles/dev_env/files/dotfiles/nvim/.config/nvim/init.lua
Normal file
26
roles/dev_env/files/dotfiles/nvim/.config/nvim/init.lua
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
-- Neovim configuration — managed by boma dev_env role (stow)
|
||||||
|
-- Bootstrap lazy.nvim plugin manager
|
||||||
|
|
||||||
|
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
|
||||||
|
if not vim.loop.fs_stat(lazypath) then
|
||||||
|
vim.fn.system({
|
||||||
|
"git",
|
||||||
|
"clone",
|
||||||
|
"--filter=blob:none",
|
||||||
|
"https://github.com/folke/lazy.nvim.git",
|
||||||
|
"--branch=stable",
|
||||||
|
lazypath,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
vim.opt.rtp:prepend(lazypath)
|
||||||
|
|
||||||
|
-- Set leader key before loading plugins
|
||||||
|
vim.g.mapleader = " "
|
||||||
|
vim.g.maplocalleader = " "
|
||||||
|
|
||||||
|
-- Load core configuration
|
||||||
|
require("options")
|
||||||
|
require("keymaps")
|
||||||
|
|
||||||
|
-- Load lazy.nvim and plugin configuration
|
||||||
|
require("lazy-setup")
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"Comment.nvim": { "branch": "master", "commit": "e30b7f2008e52442154b66f7c519bfd2f1e32acb" },
|
||||||
|
"LuaSnip": { "branch": "master", "commit": "0abc8f390b278c3b4aabc4c004ac8a088b65cf24" },
|
||||||
|
"catppuccin": { "branch": "main", "commit": "8edd468af4d63212b84d69b2ddb5ffc9023ef5eb" },
|
||||||
|
"cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" },
|
||||||
|
"cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" },
|
||||||
|
"cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" },
|
||||||
|
"cmp_luasnip": { "branch": "master", "commit": "98d9cb5c2c38532bd9bdb481067b20fea8f32e90" },
|
||||||
|
"conform.nvim": { "branch": "master", "commit": "619363c30309d29ffa631e67c8183f2a72caa373" },
|
||||||
|
"gitsigns.nvim": { "branch": "main", "commit": "dd3f588bacbeb041be6facf1742e42097f62165d" },
|
||||||
|
"indent-blankline.nvim": { "branch": "master", "commit": "d28a3f70721c79e3c5f6693057ae929f3d9c0a03" },
|
||||||
|
"lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
|
||||||
|
"lualine.nvim": { "branch": "master", "commit": "131a558e13f9f28b15cd235557150ccb23f89286" },
|
||||||
|
"neo-tree.nvim": { "branch": "v3.x", "commit": "ebd66767191714e008ce73b769518a763ff31bdc" },
|
||||||
|
"nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" },
|
||||||
|
"nvim-autopairs": { "branch": "master", "commit": "7b9923abad60b903ece7c52940e1321d39eccc79" },
|
||||||
|
"nvim-cmp": { "branch": "main", "commit": "a1d504892f2bc56c2e79b65c6faded2fd21f3eca" },
|
||||||
|
"nvim-lspconfig": { "branch": "master", "commit": "a4ed4e761c400849e8c9f8bda33e5083f890268c" },
|
||||||
|
"nvim-surround": { "branch": "main", "commit": "2e93e154de9ff326def6480a4358bfc149d5da2c" },
|
||||||
|
"nvim-tmux-navigation": { "branch": "main", "commit": "4898c98702954439233fdaf764c39636681e2861" },
|
||||||
|
"nvim-treesitter": { "branch": "master", "commit": "cf12346a3414fa1b06af75c79faebe7f76df080a" },
|
||||||
|
"nvim-web-devicons": { "branch": "master", "commit": "0d7d35fa946837b8738b17c18d1faa1ac351e7f9" },
|
||||||
|
"plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" },
|
||||||
|
"telescope.nvim": { "branch": "0.1.x", "commit": "a0bbec21143c7bc5f8bb02e0005fa0b982edc026" },
|
||||||
|
"toggleterm.nvim": { "branch": "main", "commit": "50ea089fc548917cc3cc16b46a8211833b9e3c7c" },
|
||||||
|
"which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" },
|
||||||
|
"zen-mode.nvim": { "branch": "main", "commit": "8564ce6d29ec7554eb9df578efa882d33b3c23a7" }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
-- Neovim keymaps configuration
|
||||||
|
-- Ported from vimrc.j2 and enhanced
|
||||||
|
|
||||||
|
local keymap = vim.keymap.set
|
||||||
|
local opts = { noremap = true, silent = true }
|
||||||
|
|
||||||
|
-- Set leader key
|
||||||
|
vim.g.mapleader = " "
|
||||||
|
vim.g.maplocalleader = " "
|
||||||
|
|
||||||
|
-- Quick save (Ctrl+s in normal and insert mode)
|
||||||
|
keymap('n', '<C-s>', ':w<CR>', opts)
|
||||||
|
keymap('i', '<C-s>', '<Esc>:w<CR>a', opts)
|
||||||
|
|
||||||
|
-- Leader shortcuts
|
||||||
|
keymap('n', '<leader>w', ':w<CR>', opts) -- Save
|
||||||
|
keymap('n', '<leader>q', ':q<CR>', opts) -- Quit
|
||||||
|
keymap('n', '<leader>e', ':Ex<CR>', opts) -- File explorer (netrw)
|
||||||
|
|
||||||
|
-- Clear search highlighting
|
||||||
|
keymap('n', '<Esc>', ':nohlsearch<CR>', opts)
|
||||||
|
|
||||||
|
-- Better indenting (keep selection in visual mode)
|
||||||
|
keymap('v', '<', '<gv', opts)
|
||||||
|
keymap('v', '>', '>gv', opts)
|
||||||
|
|
||||||
|
-- Move text up and down in visual mode
|
||||||
|
keymap('v', 'J', ":m '>+1<CR>gv=gv", opts)
|
||||||
|
keymap('v', 'K', ":m '<-2<CR>gv=gv", opts)
|
||||||
|
|
||||||
|
-- Keep cursor centered when scrolling
|
||||||
|
keymap('n', '<C-d>', '<C-d>zz', opts)
|
||||||
|
keymap('n', '<C-u>', '<C-u>zz', opts)
|
||||||
|
keymap('n', 'n', 'nzzzv', opts)
|
||||||
|
keymap('n', 'N', 'Nzzzv', opts)
|
||||||
|
|
||||||
|
-- Buffer cycling (inner layer — mirrors tmux window cycling)
|
||||||
|
keymap('n', '<C-Tab>', ':bnext<CR>', opts)
|
||||||
|
keymap('n', '<C-PageDown>', ':bnext<CR>', opts)
|
||||||
|
keymap('n', '<C-PageUp>', ':bprevious<CR>', opts)
|
||||||
|
|
||||||
|
-- Better paste (don't yank replaced text in visual mode)
|
||||||
|
keymap('x', '<leader>p', '"_dP', opts)
|
||||||
|
|
||||||
|
-- Explicit yank-to-system-clipboard. Default y/Y already mirror to + via
|
||||||
|
-- the TextYankPost autocmd in options.lua, so these are redundant but kept
|
||||||
|
-- as muscle-memory aliases. Paste counterparts removed — `p` already pastes
|
||||||
|
-- correctly and `"+p` would route through OSC52 paste, which most terminals
|
||||||
|
-- refuse (silent E353).
|
||||||
|
keymap('n', '<leader>y', '"+y', { noremap = true, silent = true, desc = 'Yank to system clipboard' })
|
||||||
|
keymap('v', '<leader>y', '"+y', { noremap = true, silent = true, desc = 'Yank to system clipboard' })
|
||||||
|
keymap('n', '<leader>Y', '"+Y', { noremap = true, silent = true, desc = 'Yank line to system clipboard' })
|
||||||
|
|
||||||
|
-- Delete to black hole register
|
||||||
|
keymap('n', '<leader>d', '"_d', opts)
|
||||||
|
keymap('v', '<leader>d', '"_d', opts)
|
||||||
|
|
||||||
|
-- Window management — arrow direction = where new pane opens (mirrors kitty and tmux)
|
||||||
|
keymap('n', '<leader><Right>', ':vsplit<CR>', opts)
|
||||||
|
keymap('n', '<leader><Down>', ':split<CR>', opts)
|
||||||
|
keymap('n', '<leader>sx', ':close<CR>', opts)
|
||||||
|
keymap('n', '<leader>sv', ':vsplit<CR>', opts)
|
||||||
|
keymap('n', '<leader>sh', ':split<CR>', opts)
|
||||||
|
|
||||||
|
-- Buffer navigation
|
||||||
|
keymap('n', '<leader>bn', ':bnext<CR>', opts) -- Next buffer
|
||||||
|
keymap('n', '<leader>bp', ':bprevious<CR>', opts) -- Previous buffer
|
||||||
|
keymap('n', '<leader>bd', ':bdelete<CR>', opts) -- Delete buffer
|
||||||
|
|
||||||
|
-- Quick fix list navigation
|
||||||
|
keymap('n', '<leader>j', ':cnext<CR>', opts)
|
||||||
|
keymap('n', '<leader>k', ':cprev<CR>', opts)
|
||||||
|
|
||||||
|
-- Diagnostic keymaps (will be overridden by LSP if active)
|
||||||
|
keymap('n', '<A-k>', vim.diagnostic.goto_prev, opts) -- Alt+k = up = prev diagnostic
|
||||||
|
keymap('n', '<A-j>', vim.diagnostic.goto_next, opts) -- Alt+j = down = next diagnostic
|
||||||
|
keymap('n', '<leader>dl', vim.diagnostic.setloclist, opts)
|
||||||
|
keymap('n', '<leader>df', vim.diagnostic.open_float, opts)
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
-- Lazy.nvim setup
|
||||||
|
-- Loads all plugins from lua/plugins/ directory
|
||||||
|
|
||||||
|
require("lazy").setup("plugins", {
|
||||||
|
-- Use lockfile for deterministic installations
|
||||||
|
lockfile = vim.fn.stdpath("config") .. "/lazy-lock.json",
|
||||||
|
|
||||||
|
-- Disable automatic checking for plugin updates (managed by Ansible)
|
||||||
|
checker = {
|
||||||
|
enabled = false, -- Updates only via Ansible
|
||||||
|
notify = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Disable change detection (we control updates via Ansible)
|
||||||
|
change_detection = {
|
||||||
|
enabled = false,
|
||||||
|
notify = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Performance optimizations
|
||||||
|
performance = {
|
||||||
|
cache = {
|
||||||
|
enabled = true,
|
||||||
|
},
|
||||||
|
rtp = {
|
||||||
|
-- Debian puts bundled treesitter parsers (vimdoc etc.) under /usr/lib/nvim
|
||||||
|
-- which lazy's rtp reset would otherwise drop.
|
||||||
|
paths = vim.fn.isdirectory("/usr/lib/nvim") == 1 and { "/usr/lib/nvim" } or {},
|
||||||
|
-- Disable unused built-in plugins
|
||||||
|
disabled_plugins = {
|
||||||
|
"gzip",
|
||||||
|
"matchit",
|
||||||
|
"matchparen",
|
||||||
|
"netrwPlugin",
|
||||||
|
"tarPlugin",
|
||||||
|
"tohtml",
|
||||||
|
"tutor",
|
||||||
|
"zipPlugin",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
-- UI configuration
|
||||||
|
ui = {
|
||||||
|
border = "rounded",
|
||||||
|
icons = {
|
||||||
|
cmd = "⌘",
|
||||||
|
config = "🛠",
|
||||||
|
event = "📅",
|
||||||
|
ft = "📂",
|
||||||
|
init = "⚙",
|
||||||
|
keys = "🗝",
|
||||||
|
plugin = "🔌",
|
||||||
|
runtime = "💻",
|
||||||
|
source = "📄",
|
||||||
|
start = "🚀",
|
||||||
|
task = "📌",
|
||||||
|
lazy = "💤 ",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
119
roles/dev_env/files/dotfiles/nvim/.config/nvim/lua/options.lua
Normal file
119
roles/dev_env/files/dotfiles/nvim/.config/nvim/lua/options.lua
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
-- Neovim options configuration
|
||||||
|
-- Ported from vimrc.j2 and enhanced with Neovim best practices
|
||||||
|
|
||||||
|
local opt = vim.opt
|
||||||
|
|
||||||
|
-- Line numbers
|
||||||
|
opt.number = true -- Show line numbers
|
||||||
|
opt.relativenumber = true -- Show relative line numbers
|
||||||
|
opt.cursorline = true -- Highlight the current line
|
||||||
|
|
||||||
|
-- Tabs and indentation
|
||||||
|
opt.expandtab = true -- Use spaces instead of tabs
|
||||||
|
opt.tabstop = 4 -- Number of spaces for a tab
|
||||||
|
opt.shiftwidth = 4 -- Number of spaces for indentation
|
||||||
|
opt.softtabstop = 4 -- Number of spaces for soft tab
|
||||||
|
opt.autoindent = true -- Enable auto-indentation
|
||||||
|
opt.smartindent = true -- Smart indentation
|
||||||
|
|
||||||
|
-- Search
|
||||||
|
opt.hlsearch = true -- Highlight search results
|
||||||
|
opt.incsearch = true -- Incremental search
|
||||||
|
opt.ignorecase = true -- Case-insensitive search
|
||||||
|
opt.smartcase = true -- Override ignorecase if search contains uppercase
|
||||||
|
|
||||||
|
-- UI
|
||||||
|
opt.showmatch = true -- Show matching brackets
|
||||||
|
opt.mouse = 'a' -- Enable mouse support
|
||||||
|
opt.termguicolors = true -- Enable 24-bit RGB colors
|
||||||
|
opt.laststatus = 2 -- Always show status line
|
||||||
|
opt.ruler = true -- Show cursor position
|
||||||
|
opt.wildmenu = true -- Enhanced command-line completion
|
||||||
|
opt.linebreak = true -- Wrap lines at word boundaries
|
||||||
|
|
||||||
|
-- Clipboard — OSC52 tunnels clipboard through SSH/tmux to local kitty.
|
||||||
|
-- We only wire the *copy* half: OSC52 paste requires the terminal to read
|
||||||
|
-- back its own clipboard, which most terminals refuse for security. Pairing
|
||||||
|
-- it with unnamedplus made `p` route through OSC52 paste and silently fail
|
||||||
|
-- (E353). Instead, leave the unnamed register alone so `p` uses vim's
|
||||||
|
-- internal buffer, and mirror every yank into `+` via TextYankPost so the
|
||||||
|
-- system clipboard still gets updated.
|
||||||
|
vim.g.clipboard = {
|
||||||
|
name = 'OSC 52',
|
||||||
|
copy = {
|
||||||
|
['+'] = require('vim.ui.clipboard.osc52').copy('+'),
|
||||||
|
['*'] = require('vim.ui.clipboard.osc52').copy('*'),
|
||||||
|
},
|
||||||
|
paste = {
|
||||||
|
['+'] = require('vim.ui.clipboard.osc52').paste('+'),
|
||||||
|
['*'] = require('vim.ui.clipboard.osc52').paste('*'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
vim.api.nvim_create_autocmd('TextYankPost', {
|
||||||
|
pattern = '*',
|
||||||
|
callback = function()
|
||||||
|
if vim.v.event.operator == 'y' then
|
||||||
|
vim.fn.setreg('+', vim.v.event.regcontents, vim.v.event.regtype)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Spell-check for markdown buffers (English + Danish). Nvim auto-downloads
|
||||||
|
-- missing spellfiles on first use from neovim's spell repo.
|
||||||
|
vim.api.nvim_create_autocmd('FileType', {
|
||||||
|
pattern = 'markdown',
|
||||||
|
callback = function()
|
||||||
|
vim.opt_local.spell = true
|
||||||
|
vim.opt_local.spelllang = { 'en', 'da' }
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Files and backups
|
||||||
|
opt.backup = false -- Disable backup files
|
||||||
|
opt.swapfile = false -- Disable swap files
|
||||||
|
opt.undofile = true -- Enable persistent undo
|
||||||
|
opt.undodir = vim.fn.expand('~/.local/state/nvim/undo') -- Neovim undo directory
|
||||||
|
|
||||||
|
-- Auto-reload buffers when files change on disk (e.g. external edits by Claude Code).
|
||||||
|
-- Nvim only checks on certain events; the autocmd below pokes it more aggressively.
|
||||||
|
opt.autoread = true
|
||||||
|
vim.api.nvim_create_autocmd({ 'FocusGained', 'BufEnter', 'CursorHold', 'CursorHoldI' }, {
|
||||||
|
pattern = '*',
|
||||||
|
command = 'checktime',
|
||||||
|
})
|
||||||
|
vim.api.nvim_create_autocmd('FileChangedShellPost', {
|
||||||
|
pattern = '*',
|
||||||
|
callback = function()
|
||||||
|
vim.notify('File changed on disk — buffer reloaded', vim.log.levels.WARN)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Performance
|
||||||
|
opt.updatetime = 300 -- Faster completion and updates
|
||||||
|
|
||||||
|
-- Encoding
|
||||||
|
opt.encoding = 'utf-8' -- Set encoding to UTF-8
|
||||||
|
opt.fileformat = 'unix' -- Use Unix file format
|
||||||
|
|
||||||
|
-- Folding
|
||||||
|
opt.foldmethod = 'indent' -- Fold based on indentation
|
||||||
|
opt.foldlevel = 99 -- Start with all folds open
|
||||||
|
|
||||||
|
-- Split behavior
|
||||||
|
opt.splitbelow = true -- Horizontal splits below
|
||||||
|
opt.splitright = true -- Vertical splits to the right
|
||||||
|
|
||||||
|
-- Sign column (for LSP diagnostics and git signs)
|
||||||
|
opt.signcolumn = 'yes' -- Always show sign column
|
||||||
|
|
||||||
|
-- Completion menu
|
||||||
|
opt.completeopt = { 'menu', 'menuone', 'noselect' } -- Better completion experience
|
||||||
|
|
||||||
|
-- Scroll offset
|
||||||
|
opt.scrolloff = 8 -- Keep 8 lines visible above/below cursor
|
||||||
|
opt.sidescrolloff = 8 -- Keep 8 columns visible left/right of cursor
|
||||||
|
|
||||||
|
-- Command line
|
||||||
|
opt.cmdheight = 1 -- Command line height
|
||||||
|
|
||||||
|
-- Color scheme is set by catppuccin plugin in lua/plugins/ui.lua
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
-- Autocompletion (nvim-cmp)
|
||||||
|
return {
|
||||||
|
"hrsh7th/nvim-cmp",
|
||||||
|
event = "InsertEnter",
|
||||||
|
dependencies = {
|
||||||
|
"hrsh7th/cmp-nvim-lsp", -- LSP source
|
||||||
|
"hrsh7th/cmp-buffer", -- Buffer words source
|
||||||
|
"hrsh7th/cmp-path", -- Filesystem path source
|
||||||
|
"L3MON4D3/LuaSnip", -- Snippet engine
|
||||||
|
"saadparwaiz1/cmp_luasnip", -- Snippet source
|
||||||
|
},
|
||||||
|
config = function()
|
||||||
|
local cmp = require("cmp")
|
||||||
|
local luasnip = require("luasnip")
|
||||||
|
|
||||||
|
cmp.setup({
|
||||||
|
snippet = {
|
||||||
|
expand = function(args)
|
||||||
|
luasnip.lsp_expand(args.body)
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
mapping = cmp.mapping.preset.insert({
|
||||||
|
["<C-j>"] = cmp.mapping.select_next_item(),
|
||||||
|
["<C-k>"] = cmp.mapping.select_prev_item(),
|
||||||
|
["<C-b>"] = cmp.mapping.scroll_docs(-4),
|
||||||
|
["<C-f>"] = cmp.mapping.scroll_docs(4),
|
||||||
|
["<C-e>"] = cmp.mapping.abort(),
|
||||||
|
["<CR>"] = cmp.mapping.confirm({ select = false }),
|
||||||
|
["<Tab>"] = cmp.mapping(function(fallback)
|
||||||
|
if cmp.visible() then
|
||||||
|
cmp.select_next_item()
|
||||||
|
elseif luasnip.expand_or_jumpable() then
|
||||||
|
luasnip.expand_or_jump()
|
||||||
|
else
|
||||||
|
fallback()
|
||||||
|
end
|
||||||
|
end, { "i", "s" }),
|
||||||
|
["<S-Tab>"] = cmp.mapping(function(fallback)
|
||||||
|
if cmp.visible() then
|
||||||
|
cmp.select_prev_item()
|
||||||
|
elseif luasnip.jumpable(-1) then
|
||||||
|
luasnip.jump(-1)
|
||||||
|
else
|
||||||
|
fallback()
|
||||||
|
end
|
||||||
|
end, { "i", "s" }),
|
||||||
|
}),
|
||||||
|
sources = cmp.config.sources({
|
||||||
|
{ name = "nvim_lsp", priority = 1000 },
|
||||||
|
{ name = "luasnip", priority = 750 },
|
||||||
|
{ name = "buffer", priority = 500 },
|
||||||
|
{ name = "path", priority = 250 },
|
||||||
|
}),
|
||||||
|
formatting = {
|
||||||
|
format = function(entry, item)
|
||||||
|
local source_labels = {
|
||||||
|
nvim_lsp = "[LSP]",
|
||||||
|
luasnip = "[Snip]",
|
||||||
|
buffer = "[Buf]",
|
||||||
|
path = "[Path]",
|
||||||
|
}
|
||||||
|
item.menu = source_labels[entry.source.name] or ""
|
||||||
|
return item
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
window = {
|
||||||
|
completion = cmp.config.window.bordered(),
|
||||||
|
documentation = cmp.config.window.bordered(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
-- File browser (neo-tree.nvim)
|
||||||
|
return {
|
||||||
|
"nvim-neo-tree/neo-tree.nvim",
|
||||||
|
branch = "v3.x",
|
||||||
|
dependencies = {
|
||||||
|
"nvim-lua/plenary.nvim",
|
||||||
|
"nvim-tree/nvim-web-devicons",
|
||||||
|
"MunifTanjim/nui.nvim",
|
||||||
|
},
|
||||||
|
cmd = "Neotree",
|
||||||
|
keys = {
|
||||||
|
{ "<leader>ee", "<cmd>Neotree toggle<cr>", desc = "Toggle file browser" },
|
||||||
|
{ "<leader>ef", "<cmd>Neotree focus<cr>", desc = "Focus file browser" },
|
||||||
|
{ "<leader>eg", "<cmd>Neotree git_status<cr>", desc = "Git status tree" },
|
||||||
|
},
|
||||||
|
config = function()
|
||||||
|
require("neo-tree").setup({
|
||||||
|
close_if_last_window = true,
|
||||||
|
window = {
|
||||||
|
width = 30,
|
||||||
|
mappings = {
|
||||||
|
["<space>"] = "none", -- don't steal leader
|
||||||
|
},
|
||||||
|
},
|
||||||
|
filesystem = {
|
||||||
|
filtered_items = {
|
||||||
|
hide_dotfiles = false,
|
||||||
|
hide_gitignored = true,
|
||||||
|
},
|
||||||
|
follow_current_file = {
|
||||||
|
enabled = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
git_status = {
|
||||||
|
symbols = {
|
||||||
|
added = "✚",
|
||||||
|
modified = "",
|
||||||
|
deleted = "✖",
|
||||||
|
renamed = "",
|
||||||
|
untracked = "",
|
||||||
|
ignored = "",
|
||||||
|
unstaged = "",
|
||||||
|
staged = "",
|
||||||
|
conflict = "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
-- Formatting configuration (conform.nvim)
|
||||||
|
return {
|
||||||
|
"stevearc/conform.nvim",
|
||||||
|
event = { "BufWritePre" },
|
||||||
|
cmd = { "ConformInfo" },
|
||||||
|
config = function()
|
||||||
|
local conform = require("conform")
|
||||||
|
|
||||||
|
conform.setup({
|
||||||
|
formatters_by_ft = {
|
||||||
|
python = function(bufnr)
|
||||||
|
-- Prefer ruff if available, fallback to black
|
||||||
|
if vim.fn.executable("ruff") == 1 then
|
||||||
|
return { "ruff_format", "ruff_fix" }
|
||||||
|
elseif vim.fn.executable("black") == 1 then
|
||||||
|
return { "black" }
|
||||||
|
end
|
||||||
|
return {}
|
||||||
|
end,
|
||||||
|
javascript = { "prettier" },
|
||||||
|
typescript = { "prettier" },
|
||||||
|
javascriptreact = { "prettier" },
|
||||||
|
typescriptreact = { "prettier" },
|
||||||
|
html = { "prettier" },
|
||||||
|
css = { "prettier" },
|
||||||
|
scss = { "prettier" },
|
||||||
|
json = { "prettier" },
|
||||||
|
markdown = { "prettier" },
|
||||||
|
php = { "php_cs_fixer" },
|
||||||
|
yaml = { "yamlfmt" },
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Format on save configuration
|
||||||
|
format_on_save = function(bufnr)
|
||||||
|
-- Graceful fallback: disable if no formatter available
|
||||||
|
local formatters = conform.list_formatters(bufnr)
|
||||||
|
if #formatters == 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
timeout_ms = 500,
|
||||||
|
lsp_fallback = true,
|
||||||
|
async = false,
|
||||||
|
quiet = false,
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- Custom formatters
|
||||||
|
formatters = {
|
||||||
|
ruff_format = {
|
||||||
|
command = "ruff",
|
||||||
|
args = { "format", "--stdin-filename", "$FILENAME", "-" },
|
||||||
|
stdin = true,
|
||||||
|
},
|
||||||
|
ruff_fix = {
|
||||||
|
command = "ruff",
|
||||||
|
args = { "check", "--fix", "--stdin-filename", "$FILENAME", "-" },
|
||||||
|
stdin = true,
|
||||||
|
},
|
||||||
|
php_cs_fixer = {
|
||||||
|
command = "php-cs-fixer",
|
||||||
|
args = { "fix", "$FILENAME" },
|
||||||
|
stdin = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Keymap for manual formatting
|
||||||
|
vim.keymap.set({ "n", "v" }, "<leader>fm", function()
|
||||||
|
conform.format({
|
||||||
|
lsp_fallback = true,
|
||||||
|
async = false,
|
||||||
|
timeout_ms = 500,
|
||||||
|
})
|
||||||
|
end, { desc = "Format file or range (in visual mode)" })
|
||||||
|
|
||||||
|
-- Command to show formatter info
|
||||||
|
vim.api.nvim_create_user_command("FormatInfo", function()
|
||||||
|
local formatters = conform.list_formatters(0)
|
||||||
|
if #formatters == 0 then
|
||||||
|
print("No formatters available for this filetype")
|
||||||
|
else
|
||||||
|
print("Available formatters:")
|
||||||
|
for _, formatter in ipairs(formatters) do
|
||||||
|
print(" - " .. formatter.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end, {})
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
-- Git integration (gitsigns.nvim)
|
||||||
|
return {
|
||||||
|
"lewis6991/gitsigns.nvim",
|
||||||
|
event = { "BufReadPre", "BufNewFile" },
|
||||||
|
config = function()
|
||||||
|
require("gitsigns").setup({
|
||||||
|
signs = {
|
||||||
|
add = { text = '│' },
|
||||||
|
change = { text = '│' },
|
||||||
|
delete = { text = '_' },
|
||||||
|
topdelete = { text = '‾' },
|
||||||
|
changedelete = { text = '~' },
|
||||||
|
untracked = { text = '┆' },
|
||||||
|
},
|
||||||
|
signcolumn = true, -- Toggle with `:Gitsigns toggle_signs`
|
||||||
|
numhl = false, -- Toggle with `:Gitsigns toggle_numhl`
|
||||||
|
linehl = false, -- Toggle with `:Gitsigns toggle_linehl`
|
||||||
|
word_diff = false, -- Toggle with `:Gitsigns toggle_word_diff`
|
||||||
|
watch_gitdir = {
|
||||||
|
interval = 1000,
|
||||||
|
follow_files = true,
|
||||||
|
},
|
||||||
|
attach_to_untracked = true,
|
||||||
|
current_line_blame = false, -- Toggle with `:Gitsigns toggle_current_line_blame`
|
||||||
|
current_line_blame_opts = {
|
||||||
|
virt_text = true,
|
||||||
|
virt_text_pos = 'eol', -- 'eol' | 'overlay' | 'right_align'
|
||||||
|
delay = 1000,
|
||||||
|
ignore_whitespace = false,
|
||||||
|
},
|
||||||
|
sign_priority = 6,
|
||||||
|
update_debounce = 100,
|
||||||
|
status_formatter = nil, -- Use default
|
||||||
|
max_file_length = 40000,
|
||||||
|
preview_config = {
|
||||||
|
-- Options passed to nvim_open_win
|
||||||
|
border = 'rounded',
|
||||||
|
style = 'minimal',
|
||||||
|
relative = 'cursor',
|
||||||
|
row = 0,
|
||||||
|
col = 1,
|
||||||
|
},
|
||||||
|
on_attach = function(bufnr)
|
||||||
|
local gs = package.loaded.gitsigns
|
||||||
|
|
||||||
|
local function map(mode, l, r, opts)
|
||||||
|
opts = opts or {}
|
||||||
|
opts.buffer = bufnr
|
||||||
|
vim.keymap.set(mode, l, r, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Navigation (j = down = next, k = up = prev)
|
||||||
|
map('n', '<leader>hj', function()
|
||||||
|
if vim.wo.diff then return ']c' end
|
||||||
|
vim.schedule(function() gs.next_hunk() end)
|
||||||
|
return '<Ignore>'
|
||||||
|
end, {expr=true, desc="Next git hunk"})
|
||||||
|
|
||||||
|
map('n', '<leader>hk', function()
|
||||||
|
if vim.wo.diff then return '[c' end
|
||||||
|
vim.schedule(function() gs.prev_hunk() end)
|
||||||
|
return '<Ignore>'
|
||||||
|
end, {expr=true, desc="Previous git hunk"})
|
||||||
|
|
||||||
|
-- Actions
|
||||||
|
map('n', '<leader>hs', gs.stage_hunk, {desc="Stage hunk"})
|
||||||
|
map('n', '<leader>hr', gs.reset_hunk, {desc="Reset hunk"})
|
||||||
|
map('v', '<leader>hs', function() gs.stage_hunk {vim.fn.line('.'), vim.fn.line('v')} end, {desc="Stage hunk"})
|
||||||
|
map('v', '<leader>hr', function() gs.reset_hunk {vim.fn.line('.'), vim.fn.line('v')} end, {desc="Reset hunk"})
|
||||||
|
map('n', '<leader>hS', gs.stage_buffer, {desc="Stage buffer"})
|
||||||
|
map('n', '<leader>hu', gs.undo_stage_hunk, {desc="Undo stage hunk"})
|
||||||
|
map('n', '<leader>hR', gs.reset_buffer, {desc="Reset buffer"})
|
||||||
|
map('n', '<leader>hp', gs.preview_hunk, {desc="Preview hunk"})
|
||||||
|
map('n', '<leader>hb', function() gs.blame_line{full=true} end, {desc="Blame line"})
|
||||||
|
map('n', '<leader>tb', gs.toggle_current_line_blame, {desc="Toggle line blame"})
|
||||||
|
map('n', '<leader>hd', gs.diffthis, {desc="Diff this"})
|
||||||
|
map('n', '<leader>hD', function() gs.diffthis('~') end, {desc="Diff this ~"})
|
||||||
|
map('n', '<leader>td', gs.toggle_deleted, {desc="Toggle deleted"})
|
||||||
|
|
||||||
|
-- Text object
|
||||||
|
map({'o', 'x'}, 'ih', ':<C-U>Gitsigns select_hunk<CR>', {desc="Select hunk"})
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
-- Visual indent guides (indent-blankline.nvim)
|
||||||
|
return {
|
||||||
|
"lukas-reineke/indent-blankline.nvim",
|
||||||
|
main = "ibl",
|
||||||
|
event = { "BufReadPost", "BufNewFile" },
|
||||||
|
config = function()
|
||||||
|
require("ibl").setup({
|
||||||
|
indent = {
|
||||||
|
char = "│",
|
||||||
|
},
|
||||||
|
scope = {
|
||||||
|
enabled = true,
|
||||||
|
show_start = false,
|
||||||
|
show_end = false,
|
||||||
|
},
|
||||||
|
exclude = {
|
||||||
|
filetypes = {
|
||||||
|
"help",
|
||||||
|
"neo-tree",
|
||||||
|
"lazy",
|
||||||
|
"mason",
|
||||||
|
"TelescopePrompt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
-- LSP configuration (nvim 0.11+ native API)
|
||||||
|
return {
|
||||||
|
{
|
||||||
|
"neovim/nvim-lspconfig",
|
||||||
|
config = function()
|
||||||
|
|
||||||
|
-- Diagnostic configuration
|
||||||
|
vim.diagnostic.config({
|
||||||
|
virtual_text = true,
|
||||||
|
signs = {
|
||||||
|
text = {
|
||||||
|
[vim.diagnostic.severity.ERROR] = "✘",
|
||||||
|
[vim.diagnostic.severity.WARN] = "▲",
|
||||||
|
[vim.diagnostic.severity.HINT] = "⚑",
|
||||||
|
[vim.diagnostic.severity.INFO] = "»",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
underline = true,
|
||||||
|
update_in_insert = false,
|
||||||
|
severity_sort = true,
|
||||||
|
float = {
|
||||||
|
border = "rounded",
|
||||||
|
source = true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Global LSP keybindings applied when any server attaches
|
||||||
|
vim.lsp.config('*', {
|
||||||
|
on_attach = function(client, bufnr)
|
||||||
|
local opts = { buffer = bufnr, noremap = true, silent = true }
|
||||||
|
vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts)
|
||||||
|
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
|
||||||
|
vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
|
||||||
|
vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, opts)
|
||||||
|
vim.keymap.set('n', '<leader>K', vim.lsp.buf.signature_help, opts)
|
||||||
|
vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename, opts)
|
||||||
|
vim.keymap.set('n', '<leader>ca', vim.lsp.buf.code_action, opts)
|
||||||
|
vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
|
||||||
|
vim.keymap.set('n', '<leader>f', function() vim.lsp.buf.format({ async = true }) end, opts)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Python: pyright (prefer) or pylsp (fallback)
|
||||||
|
if vim.fn.executable("pyright-langserver") == 1 then
|
||||||
|
vim.lsp.config('pyright', {
|
||||||
|
settings = {
|
||||||
|
python = {
|
||||||
|
analysis = {
|
||||||
|
typeCheckingMode = "basic",
|
||||||
|
autoSearchPaths = true,
|
||||||
|
useLibraryCodeForTypes = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
vim.lsp.enable('pyright')
|
||||||
|
elseif vim.fn.executable("pylsp") == 1 then
|
||||||
|
vim.lsp.enable('pylsp')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- PHP: intelephense
|
||||||
|
if vim.fn.executable("intelephense") == 1 then
|
||||||
|
vim.lsp.enable('intelephense')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- JavaScript/TypeScript: typescript-language-server
|
||||||
|
if vim.fn.executable("typescript-language-server") == 1 then
|
||||||
|
vim.lsp.enable('ts_ls')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- HTML
|
||||||
|
if vim.fn.executable("vscode-html-language-server") == 1 then
|
||||||
|
vim.lsp.enable('html')
|
||||||
|
end
|
||||||
|
-- CSS
|
||||||
|
if vim.fn.executable("vscode-css-language-server") == 1 then
|
||||||
|
vim.lsp.enable('cssls')
|
||||||
|
end
|
||||||
|
-- JSON
|
||||||
|
if vim.fn.executable("vscode-json-language-server") == 1 then
|
||||||
|
vim.lsp.enable('jsonls')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- YAML
|
||||||
|
if vim.fn.executable("yaml-language-server") == 1 then
|
||||||
|
vim.lsp.config('yamlls', {
|
||||||
|
settings = {
|
||||||
|
yaml = {
|
||||||
|
schemas = {
|
||||||
|
["https://json.schemastore.org/github-workflow.json"] = "/.github/workflows/*",
|
||||||
|
["https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/ansible.json#/$defs/playbook"] = "/*play*.{yml,yaml}",
|
||||||
|
["https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/ansible.json#/$defs/tasks"] = "/tasks/*.{yml,yaml}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
vim.lsp.enable('yamlls')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Markdown: marksman
|
||||||
|
if vim.fn.executable("marksman") == 1 then
|
||||||
|
vim.lsp.enable('marksman')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Bash: bash-language-server
|
||||||
|
if vim.fn.executable("bash-language-server") == 1 then
|
||||||
|
vim.lsp.enable('bashls')
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
-- tadmccorkle/markdown.nvim — buffer-local helpers for markdown editing.
|
||||||
|
-- Defaults already wire emphasis (gs/gss/ds/cs), links (gl/gx) and heading
|
||||||
|
-- nav (]c/]p/]]/[[). List-item/checkbox features are user commands only,
|
||||||
|
-- so on_attach adds <localleader> bindings for them.
|
||||||
|
return {
|
||||||
|
"tadmccorkle/markdown.nvim",
|
||||||
|
ft = "markdown",
|
||||||
|
opts = {
|
||||||
|
on_attach = function(bufnr)
|
||||||
|
local map = function(mode, lhs, rhs, desc)
|
||||||
|
vim.keymap.set(mode, lhs, rhs, { buffer = bufnr, silent = true, desc = desc })
|
||||||
|
end
|
||||||
|
map("n", "<localleader>o", "<Cmd>MDListItemBelow<CR>", "Insert list item below")
|
||||||
|
map("n", "<localleader>O", "<Cmd>MDListItemAbove<CR>", "Insert list item above")
|
||||||
|
map("n", "<localleader>ch", "<Cmd>MDTaskToggle<CR>", "Toggle checkbox")
|
||||||
|
-- <localleader>n (not rn): the LSP's on_attach binds <leader>rn to
|
||||||
|
-- vim.lsp.buf.rename buffer-local and overrides ours.
|
||||||
|
map("n", "<localleader>n", "<Cmd>%MDResetListNumbering<CR>", "Renumber ordered list")
|
||||||
|
map("x", "<localleader>ch", ":MDTaskToggle<CR>", "Toggle checkbox (range)")
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
-- echasnovski/mini.move — move lines/selections with Alt+arrows in any
|
||||||
|
-- filetype. Uses arrow keys to avoid clashing with existing <A-j>/<A-k>
|
||||||
|
-- diagnostic mappings in keymaps.lua.
|
||||||
|
return {
|
||||||
|
"echasnovski/mini.move",
|
||||||
|
event = "VeryLazy",
|
||||||
|
opts = {
|
||||||
|
mappings = {
|
||||||
|
-- visual selection
|
||||||
|
left = "<M-Left>",
|
||||||
|
right = "<M-Right>",
|
||||||
|
down = "<M-Down>",
|
||||||
|
up = "<M-Up>",
|
||||||
|
-- current line in normal mode
|
||||||
|
line_left = "<M-Left>",
|
||||||
|
line_right = "<M-Right>",
|
||||||
|
line_down = "<M-Down>",
|
||||||
|
line_up = "<M-Up>",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
return {
|
||||||
|
"alexghergh/nvim-tmux-navigation",
|
||||||
|
event = "VeryLazy",
|
||||||
|
config = function()
|
||||||
|
local nav = require("nvim-tmux-navigation")
|
||||||
|
nav.setup({ disable_when_zoomed = true })
|
||||||
|
vim.keymap.set("n", "<C-h>", nav.NvimTmuxNavigateLeft, { noremap = true, silent = true, desc = "Navigate left" })
|
||||||
|
vim.keymap.set("n", "<C-j>", nav.NvimTmuxNavigateDown, { noremap = true, silent = true, desc = "Navigate down" })
|
||||||
|
vim.keymap.set("n", "<C-k>", nav.NvimTmuxNavigateUp, { noremap = true, silent = true, desc = "Navigate up" })
|
||||||
|
vim.keymap.set("n", "<C-l>", nav.NvimTmuxNavigateRight, { noremap = true, silent = true, desc = "Navigate right" })
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
-- Telescope fuzzy finder configuration
|
||||||
|
return {
|
||||||
|
"nvim-telescope/telescope.nvim",
|
||||||
|
branch = "0.1.x",
|
||||||
|
dependencies = {
|
||||||
|
"nvim-lua/plenary.nvim",
|
||||||
|
},
|
||||||
|
cmd = "Telescope",
|
||||||
|
keys = {
|
||||||
|
{ "<leader>ff", "<cmd>Telescope find_files<cr>", desc = "Find files" },
|
||||||
|
{ "<leader>fg", "<cmd>Telescope live_grep<cr>", desc = "Live grep" },
|
||||||
|
{ "<leader>fb", "<cmd>Telescope buffers<cr>", desc = "Find buffers" },
|
||||||
|
{ "<leader>fh", "<cmd>Telescope help_tags<cr>", desc = "Help tags" },
|
||||||
|
{ "<leader>fr", "<cmd>Telescope oldfiles<cr>", desc = "Recent files" },
|
||||||
|
{ "<leader>fc", "<cmd>Telescope commands<cr>", desc = "Commands" },
|
||||||
|
{ "<leader>fk", "<cmd>Telescope keymaps<cr>", desc = "Keymaps" },
|
||||||
|
},
|
||||||
|
config = function()
|
||||||
|
local telescope = require("telescope")
|
||||||
|
local actions = require("telescope.actions")
|
||||||
|
|
||||||
|
telescope.setup({
|
||||||
|
defaults = {
|
||||||
|
prompt_prefix = "🔍 ",
|
||||||
|
selection_caret = "➤ ",
|
||||||
|
path_display = { "truncate" },
|
||||||
|
sorting_strategy = "ascending",
|
||||||
|
layout_config = {
|
||||||
|
horizontal = {
|
||||||
|
prompt_position = "top",
|
||||||
|
preview_width = 0.55,
|
||||||
|
results_width = 0.8,
|
||||||
|
},
|
||||||
|
vertical = {
|
||||||
|
mirror = false,
|
||||||
|
},
|
||||||
|
width = 0.87,
|
||||||
|
height = 0.80,
|
||||||
|
preview_cutoff = 120,
|
||||||
|
},
|
||||||
|
mappings = {
|
||||||
|
i = {
|
||||||
|
["<C-k>"] = actions.move_selection_previous,
|
||||||
|
["<C-j>"] = actions.move_selection_next,
|
||||||
|
["<C-q>"] = actions.send_selected_to_qflist + actions.open_qflist,
|
||||||
|
["<Esc>"] = actions.close,
|
||||||
|
},
|
||||||
|
n = {
|
||||||
|
["q"] = actions.close,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pickers = {
|
||||||
|
find_files = {
|
||||||
|
hidden = false,
|
||||||
|
find_command = { "rg", "--files", "--hidden", "--glob", "!.git/*" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
-- Terminal management (toggleterm.nvim)
|
||||||
|
-- <leader>tt floating shell
|
||||||
|
-- <leader>gg lazygit
|
||||||
|
return {
|
||||||
|
"akinsho/toggleterm.nvim",
|
||||||
|
version = "*",
|
||||||
|
keys = {
|
||||||
|
{ "<leader>tt", desc = "Toggle floating terminal" },
|
||||||
|
{ "<leader>gg", desc = "Open lazygit" },
|
||||||
|
},
|
||||||
|
config = function()
|
||||||
|
require("toggleterm").setup({
|
||||||
|
direction = "float",
|
||||||
|
float_opts = {
|
||||||
|
border = "curved",
|
||||||
|
width = function() return math.floor(vim.o.columns * 0.9) end,
|
||||||
|
height = function() return math.floor(vim.o.lines * 0.9) end,
|
||||||
|
},
|
||||||
|
close_on_exit = true,
|
||||||
|
})
|
||||||
|
|
||||||
|
local Terminal = require("toggleterm.terminal").Terminal
|
||||||
|
|
||||||
|
local shell_term = Terminal:new({ direction = "float" })
|
||||||
|
vim.keymap.set("n", "<leader>tt", function()
|
||||||
|
shell_term:toggle()
|
||||||
|
end, { desc = "Toggle floating terminal" })
|
||||||
|
|
||||||
|
local lazygit = Terminal:new({
|
||||||
|
cmd = "lazygit",
|
||||||
|
direction = "float",
|
||||||
|
close_on_exit = true,
|
||||||
|
on_open = function(_)
|
||||||
|
vim.cmd("startinsert!")
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
vim.keymap.set("n", "<leader>gg", function()
|
||||||
|
lazygit:toggle()
|
||||||
|
end, { desc = "Open lazygit" })
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
-- Treesitter configuration
|
||||||
|
-- Pin to legacy `master` branch: upstream made `main` the default, but `main`
|
||||||
|
-- is a rewrite that removed the `nvim-treesitter.configs` module this config
|
||||||
|
-- depends on. `master` is still maintained and exposes the classic API.
|
||||||
|
return {
|
||||||
|
"nvim-treesitter/nvim-treesitter",
|
||||||
|
branch = "master",
|
||||||
|
build = ":TSUpdate",
|
||||||
|
config = function()
|
||||||
|
-- Check if treesitter is available
|
||||||
|
local status_ok, treesitter_configs = pcall(require, "nvim-treesitter.configs")
|
||||||
|
if not status_ok then
|
||||||
|
vim.notify("nvim-treesitter not loaded", vim.log.levels.WARN)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
treesitter_configs.setup({
|
||||||
|
-- Install parsers for these languages.
|
||||||
|
-- Omit parsers bundled with nvim 0.10 (c, lua, markdown, markdown_inline,
|
||||||
|
-- vim, vimdoc) — they live in /usr/lib/nvim/parser/ and do not need building.
|
||||||
|
ensure_installed = {
|
||||||
|
"bash",
|
||||||
|
"css",
|
||||||
|
"html",
|
||||||
|
"javascript",
|
||||||
|
"json",
|
||||||
|
"php",
|
||||||
|
"python",
|
||||||
|
"typescript",
|
||||||
|
"yaml",
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Install parsers synchronously (only applied to `ensure_installed`)
|
||||||
|
sync_install = false,
|
||||||
|
|
||||||
|
-- Automatically install missing parsers when entering buffer
|
||||||
|
auto_install = true,
|
||||||
|
|
||||||
|
highlight = {
|
||||||
|
enable = true,
|
||||||
|
additional_vim_regex_highlighting = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
indent = {
|
||||||
|
enable = true,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Incremental selection
|
||||||
|
incremental_selection = {
|
||||||
|
enable = true,
|
||||||
|
keymaps = {
|
||||||
|
init_selection = "<C-space>",
|
||||||
|
node_incremental = "<C-space>",
|
||||||
|
scope_incremental = false,
|
||||||
|
node_decremental = "<C-backspace>",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,140 @@
|
||||||
|
-- UI and editing quality-of-life plugins
|
||||||
|
return {
|
||||||
|
-- ---- Catppuccin colorscheme (priority 1000 — loads before other UI) ----
|
||||||
|
{
|
||||||
|
"catppuccin/nvim",
|
||||||
|
name = "catppuccin",
|
||||||
|
priority = 1000,
|
||||||
|
config = function()
|
||||||
|
require("catppuccin").setup({
|
||||||
|
flavour = "mocha",
|
||||||
|
transparent_background = false,
|
||||||
|
term_colors = true,
|
||||||
|
integrations = {
|
||||||
|
treesitter = true,
|
||||||
|
telescope = { enabled = true },
|
||||||
|
gitsigns = true,
|
||||||
|
which_key = true,
|
||||||
|
cmp = true,
|
||||||
|
native_lsp = { enabled = true },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
vim.cmd.colorscheme("catppuccin-mocha")
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- ---- Lualine status line ----
|
||||||
|
{
|
||||||
|
"nvim-lualine/lualine.nvim",
|
||||||
|
dependencies = { "nvim-tree/nvim-web-devicons" },
|
||||||
|
event = "VeryLazy",
|
||||||
|
config = function()
|
||||||
|
require("lualine").setup({
|
||||||
|
options = {
|
||||||
|
theme = "catppuccin-mocha",
|
||||||
|
globalstatus = true,
|
||||||
|
component_separators = { left = "", right = "" },
|
||||||
|
section_separators = { left = "", right = "" },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- ---- Zen mode ----
|
||||||
|
{
|
||||||
|
"folke/zen-mode.nvim",
|
||||||
|
cmd = "ZenMode",
|
||||||
|
keys = { { "<leader>z", "<cmd>ZenMode<cr>", desc = "Zen Mode" } },
|
||||||
|
config = function()
|
||||||
|
require("zen-mode").setup({
|
||||||
|
window = {
|
||||||
|
width = 90,
|
||||||
|
height = 1,
|
||||||
|
options = { signcolumn = "no", number = false, relativenumber = false },
|
||||||
|
},
|
||||||
|
plugins = { tmux = { enabled = true } },
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- ---- Which-key (v3 API) ----
|
||||||
|
{
|
||||||
|
"folke/which-key.nvim",
|
||||||
|
event = "VeryLazy",
|
||||||
|
config = function()
|
||||||
|
local wk = require("which-key")
|
||||||
|
wk.setup({ preset = "modern", delay = 500 })
|
||||||
|
wk.add({
|
||||||
|
{ "<leader>f", group = "Find (Telescope)" },
|
||||||
|
{ "<leader>h", group = "Git Hunks" },
|
||||||
|
{ "<leader>z", desc = "Zen Mode" },
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Auto-close brackets, quotes, etc.
|
||||||
|
{
|
||||||
|
"windwp/nvim-autopairs",
|
||||||
|
event = "InsertEnter",
|
||||||
|
config = function()
|
||||||
|
require("nvim-autopairs").setup({
|
||||||
|
check_ts = true, -- Enable treesitter integration
|
||||||
|
ts_config = {
|
||||||
|
lua = { "string" }, -- Don't add pairs in lua string treesitter nodes
|
||||||
|
javascript = { "template_string" },
|
||||||
|
java = false, -- Don't check treesitter on java
|
||||||
|
},
|
||||||
|
disable_filetype = { "TelescopePrompt", "vim" },
|
||||||
|
fast_wrap = {
|
||||||
|
map = "<M-e>",
|
||||||
|
chars = { "{", "[", "(", '"', "'" },
|
||||||
|
pattern = [=[[%'%"%>%]%)%}%,]]=],
|
||||||
|
end_key = "$",
|
||||||
|
keys = "qwertyuiopzxcvbnmasdfghjkl",
|
||||||
|
check_comma = true,
|
||||||
|
highlight = "Search",
|
||||||
|
highlight_grey = "Comment",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Surround text objects
|
||||||
|
{
|
||||||
|
"kylechui/nvim-surround",
|
||||||
|
event = "VeryLazy",
|
||||||
|
config = function()
|
||||||
|
require("nvim-surround").setup()
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Smart commenting
|
||||||
|
{
|
||||||
|
"numToStr/Comment.nvim",
|
||||||
|
event = "VeryLazy",
|
||||||
|
config = function()
|
||||||
|
require("Comment").setup({
|
||||||
|
padding = true,
|
||||||
|
sticky = true,
|
||||||
|
ignore = "^$", -- Ignore empty lines
|
||||||
|
toggler = {
|
||||||
|
line = "gcc",
|
||||||
|
block = "gbc",
|
||||||
|
},
|
||||||
|
opleader = {
|
||||||
|
line = "gc",
|
||||||
|
block = "gb",
|
||||||
|
},
|
||||||
|
extra = {
|
||||||
|
above = "gcO",
|
||||||
|
below = "gco",
|
||||||
|
eol = "gcA",
|
||||||
|
},
|
||||||
|
mappings = {
|
||||||
|
basic = true,
|
||||||
|
extra = true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
88
roles/dev_env/files/dotfiles/tmux/.tmux.conf
Normal file
88
roles/dev_env/files/dotfiles/tmux/.tmux.conf
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
# tmux configuration — managed by boma dev_env role (stow)
|
||||||
|
|
||||||
|
# Prefix
|
||||||
|
unbind C-b
|
||||||
|
set-option -g prefix C-a
|
||||||
|
bind-key C-a send-prefix
|
||||||
|
|
||||||
|
# General
|
||||||
|
set -g default-terminal "screen-256color"
|
||||||
|
set -ag terminal-overrides ",xterm-256color:RGB"
|
||||||
|
set -g mouse on
|
||||||
|
set -g history-limit 50000
|
||||||
|
set -g display-time 4000
|
||||||
|
set -g status-interval 5
|
||||||
|
set -g focus-events on
|
||||||
|
set -s extended-keys off
|
||||||
|
set -s set-clipboard on
|
||||||
|
set -g allow-passthrough on
|
||||||
|
set -as terminal-features ',xterm-256color:clipboard'
|
||||||
|
|
||||||
|
# Base index
|
||||||
|
set -g base-index 1
|
||||||
|
set -g pane-base-index 1
|
||||||
|
set-window-option -g pane-base-index 1
|
||||||
|
set-option -g renumber-windows on
|
||||||
|
|
||||||
|
# Vi mode in copy mode
|
||||||
|
set-window-option -g mode-keys vi
|
||||||
|
bind-key -T copy-mode-vi v send-keys -X begin-selection
|
||||||
|
bind-key -T copy-mode-vi C-v send-keys -X rectangle-toggle
|
||||||
|
bind-key -T copy-mode-vi y send-keys -X copy-selection-and-cancel
|
||||||
|
|
||||||
|
# Splits — arrow direction = where new pane opens (mirrors kitty and nvim)
|
||||||
|
bind Right split-window -h -c "#{pane_current_path}"
|
||||||
|
bind Down split-window -v -c "#{pane_current_path}"
|
||||||
|
unbind '"'
|
||||||
|
unbind %
|
||||||
|
unbind v
|
||||||
|
unbind -
|
||||||
|
|
||||||
|
# Pane resize (repeatable)
|
||||||
|
bind -r H resize-pane -L 5
|
||||||
|
bind -r J resize-pane -D 5
|
||||||
|
bind -r K resize-pane -U 5
|
||||||
|
bind -r L resize-pane -R 5
|
||||||
|
|
||||||
|
# Window cycling — no prefix needed; mirrors nvim buffer cycling (Ctrl = inner layer)
|
||||||
|
bind -n C-Tab next-window
|
||||||
|
bind -n C-NPage next-window
|
||||||
|
bind -n C-PPage previous-window
|
||||||
|
|
||||||
|
# New window — t = tab (mirrors kitty Ctrl+Shift+T); keep c as alias
|
||||||
|
bind t new-window -c "#{pane_current_path}"
|
||||||
|
bind c new-window -c "#{pane_current_path}"
|
||||||
|
|
||||||
|
# Close pane without confirmation prompt
|
||||||
|
bind x kill-pane
|
||||||
|
|
||||||
|
# Reload config
|
||||||
|
bind r source-file ~/.tmux.conf \; display "Config reloaded!"
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# Plugins (TPM)
|
||||||
|
# Pin catppuccin to v1 API to avoid v2 breaking changes
|
||||||
|
# =============================================================
|
||||||
|
set -g @plugin 'tmux-plugins/tpm'
|
||||||
|
set -g @plugin 'tmux-plugins/tmux-sensible'
|
||||||
|
set -g @plugin 'christoomey/vim-tmux-navigator'
|
||||||
|
set -g @plugin 'catppuccin/tmux#v1.0.3'
|
||||||
|
|
||||||
|
# Catppuccin theme (v1 API)
|
||||||
|
set -g @catppuccin_flavour 'mocha'
|
||||||
|
set -g @catppuccin_window_left_separator ""
|
||||||
|
set -g @catppuccin_window_right_separator " "
|
||||||
|
set -g @catppuccin_window_middle_separator " █"
|
||||||
|
set -g @catppuccin_window_number_position "right"
|
||||||
|
set -g @catppuccin_window_default_fill "number"
|
||||||
|
set -g @catppuccin_window_current_fill "number"
|
||||||
|
set -g @catppuccin_window_current_text "#{pane_current_path}"
|
||||||
|
set -g @catppuccin_status_modules_right "directory session"
|
||||||
|
set -g @catppuccin_status_left_separator " "
|
||||||
|
set -g @catppuccin_status_right_separator ""
|
||||||
|
set -g @catppuccin_status_fill "icon"
|
||||||
|
set -g @catppuccin_status_connect_separator "no"
|
||||||
|
set -g @catppuccin_directory_text "#{pane_current_path}"
|
||||||
|
|
||||||
|
# Initialize TPM (keep at very bottom)
|
||||||
|
run '~/.tmux/plugins/tpm/tpm'
|
||||||
55
roles/dev_env/files/dotfiles/zsh/.zshrc
Normal file
55
roles/dev_env/files/dotfiles/zsh/.zshrc
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
# ---- managed by boma dev_env role (stow) ----
|
||||||
|
# If Oh My Zsh is installed, point ZSH and load it; otherwise pure zsh.
|
||||||
|
export ZSH="$HOME/.oh-my-zsh"
|
||||||
|
|
||||||
|
ZSH_THEME="robbyrussell"
|
||||||
|
plugins=(git history sudo zsh-autosuggestions zsh-syntax-highlighting)
|
||||||
|
|
||||||
|
if [ -d "$ZSH" ]; then
|
||||||
|
source "$ZSH/oh-my-zsh.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Oh My Posh prompt (preferred)
|
||||||
|
if command -v oh-my-posh >/dev/null 2>&1; then
|
||||||
|
eval "$(oh-my-posh init zsh --config /etc/oh-my-posh/zen.toml)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Terminal title
|
||||||
|
function set_terminal_title {
|
||||||
|
echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD}\007"
|
||||||
|
}
|
||||||
|
precmd() { set_terminal_title }
|
||||||
|
|
||||||
|
# Aliases and basics
|
||||||
|
alias ll="ls -lh"
|
||||||
|
alias la="ls -lha"
|
||||||
|
alias ..="cd .."
|
||||||
|
alias update="sudo apt update && sudo apt upgrade -y"
|
||||||
|
alias rclone="/usr/bin/rclone"
|
||||||
|
|
||||||
|
# Use neovim for vim/vi commands
|
||||||
|
alias vim='nvim'
|
||||||
|
alias vi='nvim'
|
||||||
|
|
||||||
|
# History
|
||||||
|
HISTFILE="$HOME/.zsh_history"
|
||||||
|
HISTSIZE=10000
|
||||||
|
SAVEHIST=10000
|
||||||
|
setopt inc_append_history share_history
|
||||||
|
|
||||||
|
# Completion/globbing
|
||||||
|
autoload -Uz compinit
|
||||||
|
compinit
|
||||||
|
setopt autocd correct globdots no_beep
|
||||||
|
|
||||||
|
# Editor & PATH
|
||||||
|
export EDITOR="/usr/local/bin/nvim"
|
||||||
|
export VISUAL="/usr/local/bin/nvim"
|
||||||
|
export PATH="$HOME/.local/bin:$HOME/bin:$PATH"
|
||||||
|
|
||||||
|
# Ensure USER is set (edge cases)
|
||||||
|
export USER=$(whoami)
|
||||||
|
|
||||||
|
# Extras from inventory
|
||||||
|
# Enable direnv for automatic virtualenv activation
|
||||||
|
eval "$(direnv hook zsh)"
|
||||||
78
roles/dev_env/files/oh-my-posh/zen.toml
Normal file
78
roles/dev_env/files/oh-my-posh/zen.toml
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
#:schema https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json
|
||||||
|
version = 3
|
||||||
|
final_space = true
|
||||||
|
console_title_template = '{{ .Shell }} in {{ .Folder }}'
|
||||||
|
|
||||||
|
[[blocks]]
|
||||||
|
type = 'prompt'
|
||||||
|
alignment = 'left'
|
||||||
|
newline = true
|
||||||
|
|
||||||
|
[[blocks.segments]]
|
||||||
|
type = 'text'
|
||||||
|
style = 'plain'
|
||||||
|
foreground = 'cyan'
|
||||||
|
background = 'transparent'
|
||||||
|
template = '{{ .UserName }}@{{ .HostName }}:'
|
||||||
|
|
||||||
|
[[blocks.segments]]
|
||||||
|
type = 'path'
|
||||||
|
style = 'plain'
|
||||||
|
foreground = 'blue'
|
||||||
|
background = 'transparent'
|
||||||
|
template = '{{ .Path }}'
|
||||||
|
[blocks.segments.properties]
|
||||||
|
style = 'full'
|
||||||
|
|
||||||
|
[[blocks.segments]]
|
||||||
|
type = 'text'
|
||||||
|
style = 'plain'
|
||||||
|
foreground = 'magenta'
|
||||||
|
background = 'transparent'
|
||||||
|
template = '>'
|
||||||
|
|
||||||
|
[[blocks.segments]]
|
||||||
|
type = 'git'
|
||||||
|
style = 'plain'
|
||||||
|
foreground = 'p:grey'
|
||||||
|
background = 'transparent'
|
||||||
|
template = ' {{ .HEAD }}{{ if or (.Working.Changed) (.Staging.Changed) }}*{{ end }} <cyan>{{ if gt .Behind 0 }}⇣{{ end }}{{ if gt .Ahead 0 }}⇡{{ end }}</>'
|
||||||
|
|
||||||
|
[[blocks]]
|
||||||
|
type = 'rprompt'
|
||||||
|
overflow = 'hidden'
|
||||||
|
[[blocks.segments]]
|
||||||
|
type = 'executiontime'
|
||||||
|
style = 'plain'
|
||||||
|
foreground = 'yellow'
|
||||||
|
background = 'transparent'
|
||||||
|
template = '{{ .FormattedMs }}'
|
||||||
|
[blocks.segments.properties]
|
||||||
|
threshold = 5000
|
||||||
|
|
||||||
|
[[blocks]]
|
||||||
|
type = 'prompt'
|
||||||
|
alignment = 'left'
|
||||||
|
newline = true
|
||||||
|
[[blocks.segments]]
|
||||||
|
type = 'text'
|
||||||
|
style = 'plain'
|
||||||
|
foreground_templates = [
|
||||||
|
"{{if gt .Code 0}}red{{end}}",
|
||||||
|
"{{if eq .Code 0}}magenta{{end}}",
|
||||||
|
]
|
||||||
|
background = 'transparent'
|
||||||
|
template = '❯'
|
||||||
|
|
||||||
|
[transient_prompt]
|
||||||
|
foreground_templates = [
|
||||||
|
"{{if gt .Code 0}}red{{end}}",
|
||||||
|
"{{if eq .Code 0}}magenta{{end}}",
|
||||||
|
]
|
||||||
|
background = 'transparent'
|
||||||
|
template = '❯ '
|
||||||
|
|
||||||
|
[secondary_prompt]
|
||||||
|
foreground = 'magenta'
|
||||||
|
background = 'transparent'
|
||||||
|
template = '❯❯ '
|
||||||
1
roles/dev_env/handlers/main.yml
Normal file
1
roles/dev_env/handlers/main.yml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
---
|
||||||
13
roles/dev_env/meta/main.yml
Normal file
13
roles/dev_env/meta/main.yml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
galaxy_info:
|
||||||
|
author: sjat
|
||||||
|
description: >-
|
||||||
|
Interactive developer environment (zsh/oh-my-zsh/oh-my-posh, tmux, neovim) for
|
||||||
|
workstation-class boma hosts (Debian 13). Dotfiles deployed via GNU stow.
|
||||||
|
license: MIT
|
||||||
|
min_ansible_version: "2.17"
|
||||||
|
platforms:
|
||||||
|
- name: Debian
|
||||||
|
versions:
|
||||||
|
- trixie
|
||||||
|
dependencies: []
|
||||||
15
roles/dev_env/molecule/default/converge.yml
Normal file
15
roles/dev_env/molecule/default/converge.yml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
- name: Converge
|
||||||
|
hosts: all
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
vars:
|
||||||
|
dev_env__users:
|
||||||
|
- tester
|
||||||
|
pre_tasks:
|
||||||
|
- name: Create a test user to receive the environment
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: tester
|
||||||
|
create_home: true
|
||||||
|
roles:
|
||||||
|
- role: dev_env
|
||||||
31
roles/dev_env/molecule/default/molecule.yml
Normal file
31
roles/dev_env/molecule/default/molecule.yml
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
---
|
||||||
|
dependency:
|
||||||
|
name: galaxy
|
||||||
|
options:
|
||||||
|
requirements-file: ../../requirements.yml
|
||||||
|
|
||||||
|
driver:
|
||||||
|
name: docker
|
||||||
|
|
||||||
|
platforms:
|
||||||
|
- name: instance
|
||||||
|
# Project-owned image built from .docker/molecule-debian13/Dockerfile
|
||||||
|
# and hosted in the Forgejo container registry.
|
||||||
|
# Build/push with: make molecule-image / make molecule-image-push
|
||||||
|
image: forgejo.nyumbani.baobab.band/sjat/molecule-debian13:latest
|
||||||
|
pre_build_image: true
|
||||||
|
privileged: true # required for systemd
|
||||||
|
cgroupns_mode: host
|
||||||
|
volumes:
|
||||||
|
- /sys/fs/cgroup:/sys/fs/cgroup:rw
|
||||||
|
command: /lib/systemd/systemd
|
||||||
|
|
||||||
|
provisioner:
|
||||||
|
name: ansible
|
||||||
|
inventory:
|
||||||
|
host_vars:
|
||||||
|
instance:
|
||||||
|
ansible_user: root
|
||||||
|
|
||||||
|
verifier:
|
||||||
|
name: ansible
|
||||||
73
roles/dev_env/molecule/default/verify.yml
Normal file
73
roles/dev_env/molecule/default/verify.yml
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
---
|
||||||
|
- name: Verify
|
||||||
|
hosts: all
|
||||||
|
become: true
|
||||||
|
gather_facts: false
|
||||||
|
tasks:
|
||||||
|
- name: Gather installed-package facts
|
||||||
|
ansible.builtin.package_facts:
|
||||||
|
manager: apt
|
||||||
|
|
||||||
|
- name: Assert core packages are present
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- "'zsh' in ansible_facts.packages"
|
||||||
|
- "'tmux' in ansible_facts.packages"
|
||||||
|
- "'stow' in ansible_facts.packages"
|
||||||
|
- "'direnv' in ansible_facts.packages"
|
||||||
|
fail_msg: core dev_env packages missing
|
||||||
|
|
||||||
|
- name: Stat system binaries and theme
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ item }}"
|
||||||
|
loop:
|
||||||
|
- /usr/local/bin/nvim
|
||||||
|
- /usr/local/bin/oh-my-posh
|
||||||
|
- /etc/oh-my-posh/zen.toml
|
||||||
|
register: dev_env__sys
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item }}"
|
||||||
|
|
||||||
|
- name: Assert system tools are installed
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- dev_env__sys.results[0].stat.exists
|
||||||
|
- dev_env__sys.results[1].stat.exists
|
||||||
|
- dev_env__sys.results[2].stat.exists
|
||||||
|
fail_msg: nvim/oh-my-posh/zen.toml missing
|
||||||
|
|
||||||
|
- name: Look up the test user
|
||||||
|
ansible.builtin.getent:
|
||||||
|
database: passwd
|
||||||
|
key: tester
|
||||||
|
|
||||||
|
- name: Assert tester login shell is zsh
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- "getent_passwd['tester'][5] == '/usr/bin/zsh'"
|
||||||
|
fail_msg: tester login shell was not set to zsh
|
||||||
|
|
||||||
|
- name: Stat tester dotfiles and frameworks
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ item }}"
|
||||||
|
loop:
|
||||||
|
- /home/tester/.zshrc
|
||||||
|
- /home/tester/.tmux.conf
|
||||||
|
- /home/tester/.config/nvim/init.lua
|
||||||
|
- /home/tester/.oh-my-zsh
|
||||||
|
- /home/tester/.tmux/plugins/tpm
|
||||||
|
register: dev_env__dots
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item }}"
|
||||||
|
|
||||||
|
- name: Assert dotfiles are stowed (symlinks) and frameworks cloned
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- dev_env__dots.results[0].stat.exists
|
||||||
|
- dev_env__dots.results[0].stat.islnk
|
||||||
|
- dev_env__dots.results[1].stat.exists
|
||||||
|
- dev_env__dots.results[1].stat.islnk
|
||||||
|
- dev_env__dots.results[2].stat.exists
|
||||||
|
- dev_env__dots.results[3].stat.exists
|
||||||
|
- dev_env__dots.results[4].stat.exists
|
||||||
|
fail_msg: dotfiles not stowed or omz/tpm not cloned
|
||||||
4
roles/dev_env/requirements.yml
Normal file
4
roles/dev_env/requirements.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
# No extra Galaxy collections beyond the repo-level requirements.yml — dev_env uses
|
||||||
|
# only ansible.builtin modules. Present so the Molecule galaxy dependency step resolves.
|
||||||
|
collections: []
|
||||||
23
roles/dev_env/tasks/main.yml
Normal file
23
roles/dev_env/tasks/main.yml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
- name: Install developer-environment packages
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: "{{ dev_env__packages }}"
|
||||||
|
state: present
|
||||||
|
update_cache: true
|
||||||
|
cache_valid_time: 3600
|
||||||
|
tags: [packages]
|
||||||
|
|
||||||
|
- name: Install Neovim (pinned release)
|
||||||
|
ansible.builtin.include_tasks: neovim.yml
|
||||||
|
tags: [packages]
|
||||||
|
|
||||||
|
- name: Install oh-my-posh prompt (pinned release)
|
||||||
|
ansible.builtin.include_tasks: oh_my_posh.yml
|
||||||
|
tags: [packages]
|
||||||
|
|
||||||
|
- name: Configure each developer user
|
||||||
|
ansible.builtin.include_tasks: per_user.yml
|
||||||
|
loop: "{{ dev_env__users }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: dev_env__user
|
||||||
|
label: "{{ dev_env__user }}"
|
||||||
52
roles/dev_env/tasks/neovim.yml
Normal file
52
roles/dev_env/tasks/neovim.yml
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
---
|
||||||
|
- name: Neovim | Read installed-version sentinel
|
||||||
|
ansible.builtin.slurp:
|
||||||
|
src: /etc/nvim_installed_version
|
||||||
|
register: dev_env__nvim_sentinel
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Neovim | Determine installed version
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
dev_env__nvim_installed: >-
|
||||||
|
{{ (dev_env__nvim_sentinel.content | b64decode | trim)
|
||||||
|
if dev_env__nvim_sentinel.content is defined else '' }}
|
||||||
|
|
||||||
|
- name: Neovim | Install pinned release
|
||||||
|
when: dev_env__nvim_installed != dev_env__nvim_version
|
||||||
|
block:
|
||||||
|
- name: Neovim | Download release tarball
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: "https://github.com/neovim/neovim/releases/download/{{ dev_env__nvim_version }}/nvim-linux-x86_64.tar.gz"
|
||||||
|
dest: "/tmp/nvim-{{ dev_env__nvim_version }}.tar.gz"
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Neovim | Create versioned install directory
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "/opt/nvim-{{ dev_env__nvim_version }}"
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Neovim | Extract tarball
|
||||||
|
ansible.builtin.unarchive:
|
||||||
|
src: "/tmp/nvim-{{ dev_env__nvim_version }}.tar.gz"
|
||||||
|
dest: "/opt/nvim-{{ dev_env__nvim_version }}"
|
||||||
|
remote_src: true
|
||||||
|
extra_opts: ["--strip-components=1"]
|
||||||
|
|
||||||
|
- name: Neovim | Symlink into PATH
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: "/opt/nvim-{{ dev_env__nvim_version }}/bin/nvim"
|
||||||
|
dest: /usr/local/bin/nvim
|
||||||
|
state: link
|
||||||
|
force: true
|
||||||
|
|
||||||
|
- name: Neovim | Write version sentinel
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: "{{ dev_env__nvim_version }}"
|
||||||
|
dest: /etc/nvim_installed_version
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Neovim | Remove downloaded tarball
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "/tmp/nvim-{{ dev_env__nvim_version }}.tar.gz"
|
||||||
|
state: absent
|
||||||
25
roles/dev_env/tasks/oh_my_posh.yml
Normal file
25
roles/dev_env/tasks/oh_my_posh.yml
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
- name: Oh-my-posh | Check installed version
|
||||||
|
ansible.builtin.command: oh-my-posh --version
|
||||||
|
register: dev_env__omp_check
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Oh-my-posh | Install pinned binary
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: "https://github.com/JanDeDobbeleer/oh-my-posh/releases/download/v{{ dev_env__omp_version }}/posh-linux-amd64"
|
||||||
|
dest: /usr/local/bin/oh-my-posh
|
||||||
|
mode: "0755"
|
||||||
|
when: (dev_env__omp_check.stdout | default('') | trim) != dev_env__omp_version
|
||||||
|
|
||||||
|
- name: Oh-my-posh | Ensure theme directory
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /etc/oh-my-posh
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Oh-my-posh | Deploy zen.toml theme (system-wide)
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: oh-my-posh/zen.toml
|
||||||
|
dest: /etc/oh-my-posh/zen.toml
|
||||||
|
mode: "0644"
|
||||||
69
roles/dev_env/tasks/per_user.yml
Normal file
69
roles/dev_env/tasks/per_user.yml
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
---
|
||||||
|
- name: Look up account for {{ dev_env__user }}
|
||||||
|
ansible.builtin.getent:
|
||||||
|
database: passwd
|
||||||
|
key: "{{ dev_env__user }}"
|
||||||
|
|
||||||
|
- name: Resolve home directory for {{ dev_env__user }}
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
dev_env__home: "{{ getent_passwd[dev_env__user][4] }}"
|
||||||
|
|
||||||
|
- name: Set login shell to zsh for {{ dev_env__user }}
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: "{{ dev_env__user }}"
|
||||||
|
shell: /usr/bin/zsh
|
||||||
|
tags: [users]
|
||||||
|
|
||||||
|
- name: Clone oh-my-zsh for {{ dev_env__user }}
|
||||||
|
become: true
|
||||||
|
become_user: "{{ dev_env__user }}"
|
||||||
|
ansible.builtin.git:
|
||||||
|
repo: https://github.com/ohmyzsh/ohmyzsh.git
|
||||||
|
dest: "{{ dev_env__home }}/.oh-my-zsh"
|
||||||
|
version: master
|
||||||
|
depth: 1
|
||||||
|
update: false
|
||||||
|
|
||||||
|
- name: Clone oh-my-zsh custom plugins for {{ dev_env__user }}
|
||||||
|
become: true
|
||||||
|
become_user: "{{ dev_env__user }}"
|
||||||
|
ansible.builtin.git:
|
||||||
|
repo: "{{ item.repo }}"
|
||||||
|
dest: "{{ dev_env__home }}/.oh-my-zsh/custom/plugins/{{ item.name }}"
|
||||||
|
version: master
|
||||||
|
depth: 1
|
||||||
|
update: false
|
||||||
|
loop: "{{ dev_env__omz_custom_plugins }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.name }}"
|
||||||
|
|
||||||
|
- name: Clone tmux plugins (incl. TPM) for {{ dev_env__user }}
|
||||||
|
become: true
|
||||||
|
become_user: "{{ dev_env__user }}"
|
||||||
|
ansible.builtin.git:
|
||||||
|
repo: "{{ item.repo }}"
|
||||||
|
dest: "{{ dev_env__home }}/.tmux/plugins/{{ item.name }}"
|
||||||
|
version: "{{ item.version }}"
|
||||||
|
depth: 1
|
||||||
|
update: false
|
||||||
|
loop: "{{ dev_env__tmux_plugins }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.name }}"
|
||||||
|
|
||||||
|
- name: Install dotfiles into ~/.dotfiles for {{ dev_env__user }}
|
||||||
|
become: true
|
||||||
|
become_user: "{{ dev_env__user }}"
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: dotfiles/
|
||||||
|
dest: "{{ dev_env__home }}/.dotfiles/"
|
||||||
|
mode: preserve
|
||||||
|
tags: [config]
|
||||||
|
|
||||||
|
- name: Stow dotfiles into home for {{ dev_env__user }}
|
||||||
|
become: true
|
||||||
|
become_user: "{{ dev_env__user }}"
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "stow --no-folding -v -d {{ dev_env__home }}/.dotfiles -t {{ dev_env__home }} zsh tmux nvim"
|
||||||
|
register: dev_env__stow
|
||||||
|
changed_when: "'LINK:' in dev_env__stow.stderr or 'LINK:' in dev_env__stow.stdout"
|
||||||
|
tags: [config]
|
||||||
Loading…
Add table
Reference in a new issue