diff --git a/.config/.vimrc b/.config/.vimrc deleted file mode 100644 index b4a9ccb..0000000 --- a/.config/.vimrc +++ /dev/null @@ -1,194 +0,0 @@ -" ======================================== -" Options -" ======================================== - -set encoding=UTF-8 -set spelllang=en_us,de_de,es_es -set nohlsearch " Disable highlight on search -set number " Enable line numbers -set mouse=a " Enable mouse mode -set breakindent " Enable break indent -set undofile " Save undo history -set ignorecase " Case-insensitive searching unless \C or capital in search -set smartcase " Enable smart case -set signcolumn=yes " Keep signcolumn on by default -set updatetime=250 " Decrease update time -set timeoutlen=300 " Time to wait for a mapped sequence to complete (in milliseconds) -set nobackup " Don't create a backup file -set nowritebackup " Don't write backup before overwriting -set completeopt=menuone,noselect " Better completion experience -set whichwrap+=<,>,[,],h,l " Allow certain keys to move to the next line -set nowrap " Display long lines as one line -set linebreak " Don't break words when wrapping -set scrolloff=8 " Keep 8 lines above/below cursor -set sidescrolloff=8 " Keep 8 columns to the left/right of cursor -set relativenumber " Use relative line numbers -set numberwidth=4 " Number column width -set shiftwidth=4 " Spaces per indentation -set tabstop=4 " Spaces per tab -set softtabstop=4 " Spaces per tab during editing ops -set expandtab " Convert tabs to spaces -set nocursorline " Don't highlight the current line -set splitbelow " Horizontal splits below current window -set splitright " Vertical splits to the right -set noswapfile " Don't use a swap file -set smartindent " Smart indentation -set showtabline=2 " Always show tab line -set backspace=indent,eol,start " Configurable backspace behavior -set pumheight=10 " Popup menu height -set conceallevel=0 " Make `` visible in markdown -set fileencoding=utf-8 " File encoding -set cmdheight=1 " Command line height -set autoindent " Auto-indent new lines -set shortmess+=c " Don't show completion menu messages -set iskeyword+=- " Treat hyphenated words as whole words -set showmatch " show the matching part of pairs [] {} and () -set laststatus=2 " Show status bar -set statusline=%f " Path to the file -set statusline+=%= " Switch to the right side -set statusline+=%l " Current line -set statusline+=/ " Separator -set statusline+=%L " Total lines - - -" ======================================== -" Keymaps -" ======================================== - -" Set leader key -let mapleader = " " -let maplocalleader = " " - -" Disable the spacebar key's default behavior in Normal and Visual modes -nnoremap -vnoremap - -" Allow moving the cursor through wrapped lines with j, k -nnoremap k v:count == 0 ? 'gk' : 'k' -nnoremap j v:count == 0 ? 'gj' : 'j' - -" clear highlights -nnoremap :noh - -" save file -nnoremap :w - -" save file without auto-formatting -nnoremap sn :noautocmd w - -" quit file -nnoremap :q - -" delete single character without copying into register -nnoremap x "_x - -" Vertical scroll and center -nnoremap zz -nnoremap zz - -" Find and center -nnoremap n nzzzv -nnoremap N Nzzzv - -" Resize with arrows -nnoremap :resize -2 -nnoremap :resize +2 -nnoremap :vertical resize -2 -nnoremap :vertical resize +2 - -" Navigate buffers -nnoremap :bnext -nnoremap :bprevious -nnoremap sb :buffers:buffer - -" increment/decrement numbers -nnoremap + -nnoremap - - -" window management -nnoremap v v -nnoremap h s -nnoremap se = -nnoremap xs :close - -" Navigate between splits -nnoremap :wincmd k -nnoremap :wincmd j -nnoremap :wincmd h -nnoremap :wincmd l - -" tabs -nnoremap to :tabnew -nnoremap tx :tabclose -nnoremap tn :tabn -nnoremap tp :tabp - -nnoremap x :bdelete -nnoremap b :enew - -" toggle line wrapping -nnoremap lw :set wrap! - -" Press jk fast to exit insert mode -inoremap jk -inoremap kj - -" Stay in indent mode -" vnoremap < >gv - -" Keep last yanked when pasting -vnoremap p "_dP - -" Explicitly yank to system clipboard (highlighted and entire row) -noremap y "+y -noremap Y "+Y - -" Open file explorer -noremap e :Lex - -" Make W w on save -noremap :W :w - -" ======================================== -" Other -" ======================================== - -" Syntax highlighting -syntax on - -" Colorscheme -" colorscheme industry -colorscheme wildcharm -set background=dark -" hi Normal ctermbg=NONE guibg=NONE -" hi NonText ctermbg=NONE guibg=NONE guifg=NONE ctermfg=NONE -" hi VertSplit guibg=NONE guifg=NONE ctermbg=NONE ctermfg=NONE - -" Sync clipboard with OS -set clipboard=unnamedplus - -" True colors -if !has('gui_running') && &term =~ '\%(screen\|tmux\)' - let &t_8f = "\[38;2;%lu;%lu;%lum" - let &t_8b = "\[48;2;%lu;%lu;%lum" -endif -if has('termguicolors') - set termguicolors -endif - -" Use a line cursor within insert mode and a block cursor everywhere else. -let &t_SI = "\e[6 q" -let &t_EI = "\e[2 q" - -" Netrw -let g:netrw_banner = 0 -let g:netrw_liststyle = 3 -let g:netrw_browse_split = 4 -let g:netrw_altv = 1 -let g:netrw_winsize = 25 -" Use 'l' instead of to open files -augroup netrw_setup | au! - au FileType netrw nmap l -augroup END - diff --git a/.config/nvim/lua/brian/core/keymaps.lua b/.config/nvim/lua/brian/core/keymaps.lua index 17be5fd..9763414 100644 --- a/.config/nvim/lua/brian/core/keymaps.lua +++ b/.config/nvim/lua/brian/core/keymaps.lua @@ -174,7 +174,9 @@ local function angular_switch(section) local is_ts = file:match '%.component%.ts$' -- If we're in a .ts file, check for inline sections before switching files - if is_ts and (section == 'template' or section == 'styles' or section == 'ts') then + if + is_ts and (section == 'template' or section == 'styles' or section == 'ts') + then local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) local pattern if section == 'template' then @@ -213,8 +215,7 @@ local function angular_switch(section) target = base .. '.component.html' elseif section == 'styles' then local scss = base .. '.component.scss' - target = vim.fn.filereadable(scss) == 1 and scss - or base .. '.component.css' + target = vim.fn.filereadable(scss) == 1 and scss or base .. '.component.css' elseif section == 'spec' then target = base .. '.component.spec.ts' end diff --git a/.config/nvim/lua/brian/core/options.lua b/.config/nvim/lua/brian/core/options.lua index a9bf456..7ddba4c 100644 --- a/.config/nvim/lua/brian/core/options.lua +++ b/.config/nvim/lua/brian/core/options.lua @@ -42,17 +42,20 @@ vim.opt.iskeyword:append '-' -- hyphenated words recognized by searches vim.opt.formatoptions:remove { 'c', 'r', 'o' } -- don't insert the current comment leader automatically for auto-wrapping comments using 'textwidth', hitting in insert mode, or hitting 'o' or 'O' in normal mode. vim.opt.runtimepath:remove '/usr/share/vim/vimfiles' -- separate vim plugins from neovim in case vim still in use --- Neovim 0.12 changed directive captures from TSNode to TSNode[] (arrays). --- nvim-treesitter predicates do match[capture_id] expecting a single TSNode, --- but now receive a table. The nil guard passes, then get_node_text calls --- node:range(true) on a plain table which has no range method and crashes. --- Unwrap the array so nvim-treesitter keeps working until it is patched upstream. -local _get_node_text = vim.treesitter.get_node_text -vim.treesitter.get_node_text = function(node, source, opts) - if not node then return '' end - if type(node) == 'table' then - node = node[1] - if not node then return '' end +-- Temporary compatibility workaround for Neovim 0.12 directive captures. +-- Keep this scoped to 0.12 so later releases use the upstream implementation. +if vim.version().minor == 12 then + local get_node_text = vim.treesitter.get_node_text + vim.treesitter.get_node_text = function(node, source, opts) + if not node then + return '' + end + if type(node) == 'table' then + node = node[1] + if not node then + return '' + end + end + return get_node_text(node, source, opts) end - return _get_node_text(node, source, opts) end diff --git a/.config/nvim/lua/brian/lsp.lua b/.config/nvim/lua/brian/lsp.lua index 2822a22..68b0ca7 100644 --- a/.config/nvim/lua/brian/lsp.lua +++ b/.config/nvim/lua/brian/lsp.lua @@ -12,7 +12,9 @@ end local function hover_if_supported() local bufnr = vim.api.nvim_get_current_buf() for _, client in ipairs(vim.lsp.get_clients { bufnr = bufnr }) do - if client.server_capabilities and client.server_capabilities.hoverProvider then + if + client.server_capabilities and client.server_capabilities.hoverProvider + then vim.lsp.buf.hover { border = 'rounded' } return end @@ -58,12 +60,7 @@ vim.api.nvim_create_autocmd('LspAttach', { keymap.set('n', 'gt', 'Telescope lsp_type_definitions', opts) -- show lsp type definitions opts.desc = 'See available code actions' - keymap.set( - { 'n', 'v' }, - 'ca', - vim.lsp.buf.code_action, - opts - ) -- see available code actions, in visual mode will apply to selection + keymap.set({ 'n', 'v' }, 'ca', vim.lsp.buf.code_action, opts) -- see available code actions, in visual mode will apply to selection opts.desc = 'Smart rename' keymap.set('n', 'rn', vim.lsp.buf.rename, opts) -- smart rename diff --git a/.config/nvim/lua/brian/plugins/dap.lua b/.config/nvim/lua/brian/plugins/dap.lua index 1172145..89c1ec2 100644 --- a/.config/nvim/lua/brian/plugins/dap.lua +++ b/.config/nvim/lua/brian/plugins/dap.lua @@ -184,15 +184,12 @@ return { 'DapLogPoint', { text = 'L', texthl = 'DiagnosticInfo', linehl = '', numhl = '' } ) - vim.fn.sign_define( - 'DapStopped', - { - text = '>', - texthl = 'DiagnosticOk', - linehl = 'DapStoppedLine', - numhl = '', - } - ) + vim.fn.sign_define('DapStopped', { + text = '>', + texthl = 'DiagnosticOk', + linehl = 'DapStoppedLine', + numhl = '', + }) vim.fn.sign_define( 'DapBreakpointRejected', { text = 'R', texthl = 'DiagnosticError', linehl = '', numhl = '' } diff --git a/.config/nvim/lua/brian/plugins/formatting.lua b/.config/nvim/lua/brian/plugins/formatting.lua index dad3466..8646957 100644 --- a/.config/nvim/lua/brian/plugins/formatting.lua +++ b/.config/nvim/lua/brian/plugins/formatting.lua @@ -28,12 +28,11 @@ return { html = { 'prettierd', 'prettier', stop_after_first = true }, htmlangular = { 'prettierd', 'prettier', stop_after_first = true }, lua = { 'stylua' }, - rust = { 'ast_grep' }, - python = { 'ruff', 'black' }, - dockerfile = { 'hadolint' }, + rust = { 'rustfmt', lsp_format = 'fallback' }, + python = { 'ruff_format' }, }, format_on_save = { - lsp_fallback = true, + lsp_format = 'fallback', async = false, timeout_ms = 1000, }, @@ -44,7 +43,7 @@ return { vim.keymap.set({ 'n', 'v' }, 'mp', function() conform.format { - lsp_fallback = true, + lsp_format = 'fallback', async = false, timeout_ms = 1000, } diff --git a/.config/nvim/lua/brian/plugins/mason.lua b/.config/nvim/lua/brian/plugins/mason.lua index f27206e..03b68ab 100644 --- a/.config/nvim/lua/brian/plugins/mason.lua +++ b/.config/nvim/lua/brian/plugins/mason.lua @@ -52,11 +52,13 @@ local function dedupe(list) return out end -local tool_installer_list = dedupe(vim.list_extend(vim.deepcopy(tools), lsp_servers)) +local tool_installer_list = + dedupe(vim.list_extend(vim.deepcopy(tools), lsp_servers)) return { { 'mason-org/mason-lspconfig.nvim', + cond = vim.env.DOTFILES_CI ~= '1', opts = { -- Only auto-enable the LSP servers we explicitly manage in this config. -- This avoids accidental multi-server attaches (e.g. ts_ls + vtsls), @@ -81,6 +83,7 @@ return { }, { 'WhoIsSethDaniel/mason-tool-installer.nvim', + cond = vim.env.DOTFILES_CI ~= '1', dependencies = { 'mason-org/mason.nvim', }, @@ -93,8 +96,8 @@ return { ['mason-null-ls'] = false, ['mason-nvim-dap'] = false, }, - run_on_start = true, - start_delay = 0, + run_on_start = vim.env.DOTFILES_CI ~= '1', + start_delay = 3000, }, }, } diff --git a/.config/nvim/lua/brian/plugins/nvim-tree.lua b/.config/nvim/lua/brian/plugins/nvim-tree.lua index 0d450e6..2c4af4e 100644 --- a/.config/nvim/lua/brian/plugins/nvim-tree.lua +++ b/.config/nvim/lua/brian/plugins/nvim-tree.lua @@ -8,17 +8,18 @@ return { dependencies = { 'nvim-tree/nvim-web-devicons', }, - config = function() - local nvimtree = require 'nvim-tree' - -- disable netrw at the very start of your init.lua + init = function() vim.g.loaded_netrw = 1 vim.g.loaded_netrwPlugin = 1 + end, + config = function() + local nvimtree = require 'nvim-tree' - -- optionally enable 24-bit colour vim.opt.termguicolors = true - -- OR setup with some options nvimtree.setup { + disable_netrw = true, + hijack_netrw = false, sort = { sorter = 'case_sensitive', }, diff --git a/.config/nvim/lua/brian/plugins/treesitter.lua b/.config/nvim/lua/brian/plugins/treesitter.lua index b5fd27b..ffafa9d 100644 --- a/.config/nvim/lua/brian/plugins/treesitter.lua +++ b/.config/nvim/lua/brian/plugins/treesitter.lua @@ -1,4 +1,35 @@ -- Highlight, edit, and navigate code +local ci = vim.env.DOTFILES_CI == '1' +local parsers = { + 'angular', + 'lua', + 'python', + 'javascript', + 'typescript', + 'vimdoc', + 'vim', + 'regex', + 'terraform', + 'sql', + 'dockerfile', + 'toml', + 'json', + 'java', + 'groovy', + 'gitignore', + 'graphql', + 'yaml', + 'make', + 'cmake', + 'markdown', + 'markdown_inline', + 'bash', + 'tsx', + 'css', + 'html', + 'rust', +} + return { 'nvim-treesitter/nvim-treesitter', branch = 'master', @@ -8,36 +39,10 @@ return { 'nvim-treesitter/nvim-treesitter-textobjects', }, opts = { - ensure_installed = { - 'angular', - 'lua', - 'python', - 'javascript', - 'typescript', - 'vimdoc', - 'vim', - 'regex', - 'terraform', - 'sql', - 'dockerfile', - 'toml', - 'json', - 'java', - 'groovy', - 'gitignore', - 'graphql', - 'yaml', - 'make', - 'cmake', - 'markdown', - 'markdown_inline', - 'bash', - 'tsx', - 'css', - 'html', - 'rust', - }, - auto_install = true, + ensure_installed = ci and {} or parsers, + auto_install = not ci, + parser_install_dir = ci and (vim.fn.stdpath 'cache' .. '/treesitter') + or nil, highlight = { enable = true }, indent = { enable = true }, incremental_selection = { diff --git a/.config/starship/starship-wave.toml b/.config/starship/starship-wave.toml index 379cb35..6724273 100644 --- a/.config/starship/starship-wave.toml +++ b/.config/starship/starship-wave.toml @@ -113,29 +113,3 @@ purple = '#a292a3' gray = '#727169' black = '#181616' white = '#c5c9c5' - -[palettes.nord] -dark_blue = '#5E81AC' -blue = '#81A1C1' -teal = '#88C0D0' -red = '#BF616A' -orange = '#D08770' -green = '#A3BE8C' -yellow = '#EBCB8B' -purple = '#B48EAD' -gray = '#434C5E' -black = '#2E3440' -white='#D8DEE9' - -[palettes.onedark] -dark_blue='#61afef' -blue='#56b6c2' -red='#e06c75' -green='#98c379' -purple='#c678dd' -cyan='#56b6c2' -orange='#be5046' -yellow='#e5c07b' -gray='#828997' -white ='#abb2bf' -black='#2c323c' diff --git a/.config/starship/starship.toml b/.config/starship/starship.toml index af0f11f..af20134 100644 --- a/.config/starship/starship.toml +++ b/.config/starship/starship.toml @@ -113,29 +113,3 @@ purple = '#a292a3' gray = '#727169' black = '#181616' white = '#c5c9c5' - -[palettes.nord] -dark_blue = '#5E81AC' -blue = '#81A1C1' -teal = '#88C0D0' -red = '#BF616A' -orange = '#D08770' -green = '#A3BE8C' -yellow = '#EBCB8B' -purple = '#B48EAD' -gray = '#434C5E' -black = '#2E3440' -white='#D8DEE9' - -[palettes.onedark] -dark_blue='#61afef' -blue='#56b6c2' -red='#e06c75' -green='#98c379' -purple='#c678dd' -cyan='#56b6c2' -orange='#be5046' -yellow='#e5c07b' -gray='#828997' -white ='#abb2bf' -black='#2c323c' diff --git a/.config/tmux/nord-theme.conf b/.config/tmux/nord-theme.conf deleted file mode 100644 index b04942d..0000000 --- a/.config/tmux/nord-theme.conf +++ /dev/null @@ -1,23 +0,0 @@ -# bg="#3B4252" -bg="default" -default_fg="#D8DEE9" -session_fg="#A3BE8C" -session_selection_fg="#3B4252" -session_selection_bg="#81A1C1" -active_window_fg="#88C0D0" -active_pane_border="#abb2bf" - -set -g status-left-length 200 # default: 10 -set -g status-right-length 200 # default: 10 -set -g status-left "#[fg=${session_fg},bold,bg=${bg}] #S #[fg=${default_fg},nobold,bg=${bg}] | " -set -g status-right " #{cpu -i 3}  #{mem} " -set -g status-justify left -set -g status-style "bg=${bg}" -set -g window-status-format "#[fg=${default_fg},bg=default] #I:#W" -set -g window-status-current-format "#[fg=${active_window_fg},bold,bg=default]  #[underscore]#I:#W" -set -g window-status-last-style "fg=${default_fg},bg=default" -set -g message-command-style "bg=default,fg=${default_fg}" -set -g message-style "bg=default,fg=${default_fg}" -set -g mode-style "bg=${session_selection_bg},fg=${session_selection_fg}" -set -g pane-active-border-style "fg=${active_pane_border},bg=default" -set -g pane-border-style "fg=brightblack,bg=default" diff --git a/.config/tmux/tmux.conf b/.config/tmux/tmux.conf index 3cbd42b..ea26177 100644 --- a/.config/tmux/tmux.conf +++ b/.config/tmux/tmux.conf @@ -68,9 +68,9 @@ set -g @plugin 'tmux-plugins/tmux-continuum' # Automatically saves sessions e set -g @plugin 'hendrikmi/tmux-cpu-mem-monitor' # CPU and memory info set -g @plugin 'tmux-plugins/tmux-yank' -# Load theme from deterministic selector file written by ~/.zshrc. -if-shell '[ -f ~/.config/tmux/current-theme.conf ]' \ - 'source-file ~/.config/tmux/current-theme.conf' \ +# Load theme from deterministic selector file generated during shell startup. +if-shell '[ -f ~/.cache/dotfiles/tmux-theme.conf ]' \ + 'source-file ~/.cache/dotfiles/tmux-theme.conf' \ 'source-file ~/.config/tmux/dragon-theme.conf' # Resurrect diff --git a/.config/wezterm/wezterm.lua b/.config/wezterm/wezterm.lua index e36c6d8..5d2a063 100644 --- a/.config/wezterm/wezterm.lua +++ b/.config/wezterm/wezterm.lua @@ -13,12 +13,12 @@ local function env_number(name, fallback) return parsed end --- User-tunable settings live in ~/.zshrc, which writes them to env.lua +-- User-tunable settings live in ~/.zshrc, which writes them to a cache file -- (GUI-launched WezTerm never sees shell exports). WezTerm watches that -- file and live-reloads, so editing .zshrc + opening a new shell applies. --- Precedence: env.lua > process env > defaults. +-- Precedence: cache file > process env > defaults. local user = {} -local env_file = wezterm.home_dir .. "/.config/wezterm/env.lua" +local env_file = wezterm.home_dir .. "/.cache/dotfiles/wezterm-env.lua" local ok, loaded = pcall(dofile, env_file) if ok and type(loaded) == "table" then user = loaded diff --git a/.config/zed/keymap.json b/.config/zed/keymap.json deleted file mode 100644 index ac90cea..0000000 --- a/.config/zed/keymap.json +++ /dev/null @@ -1,107 +0,0 @@ -// Zed keymap -// -// This carries over the Neovim shortcuts that map cleanly onto Zed. -// Leader mappings use Zed's Vim mode sequence support. -[ - { - "context": "Editor && !menu && vim_mode == normal", - "bindings": { - "ctrl-s": "workspace::SaveAll", - "ctrl-q": "pane::CloseActiveItem", - "tab": "pane::ActivateNextItem", - "shift-tab": "pane::ActivatePreviousItem", - "ctrl-h": "vim::ToggleProjectPanelFocus", - "ctrl-j": "workspace::ActivatePaneDown", - "ctrl-k": "workspace::ActivatePaneUp", - "ctrl-l": "workspace::ActivatePaneRight", - "shift-k": "editor::Hover", - "alt-j": "editor::MoveLineDown", - "alt-k": "editor::MoveLineUp", - "[ d": "editor::GoToPreviousDiagnostic", - "] d": "editor::GoToDiagnostic", - "g d": "editor::GoToDefinition", - "g i": "editor::GoToImplementation", - "g t": "editor::GoToTypeDefinition", - "g shift-d": "editor::GoToDeclaration", - "g shift-r": "editor::FindAllReferences" - } - }, - { - "context": "ProjectPanel", - "bindings": { - "ctrl-h": "project_panel::ToggleFocus", - "ctrl-l": "project_panel::ToggleFocus", - "o": "project_panel::Open" - } - }, - { - "context": "Editor && !menu && vim_mode == visual", - "bindings": { - "alt-j": "editor::MoveLineDown", - "alt-k": "editor::MoveLineUp", - "space c a": "editor::ToggleCodeActions", - "space g h": "vim::StartOfLine", - "space g l": "vim::EndOfLine", - "space s t": "editor::SortLinesCaseSensitive", - "space ~": "editor::ToggleCase" - } - }, - { - "context": "Editor && !menu && vim_mode == normal", - "bindings": { - "space space": "tab_switcher::Toggle", - "space tab": "tab_switcher::Toggle", - "space /": "buffer_search::Deploy", - "space +": "vim::Increment", - "space -": "vim::Decrement", - "space b": "workspace::NewFile", - "space c a": "editor::ToggleCodeActions", - "space c f": "editor::Format", - "space d b": "editor::ToggleBreakpoint", - "space d c": "debugger::Continue", - "space d f": "editor::Hover", - "space d i": "debugger::StepInto", - "space d o": "debugger::StepOut", - "space d p": "debugger::Pause", - "space d r": "debugger::Rerun", - "space d shift-o": "debugger::StepOver", - "space d t": "debugger::Stop", - "space d u": "debug_panel::ToggleFocus", - "space e": "project_panel::ToggleFocus", - "space g b": "git::Branch", - "space g c f": "git::FileHistory", - "space g h": "vim::StartOfLine", - "space g l": "vim::EndOfLine", - "space g s": "git::Diff", - "space h": "pane::SplitHorizontal", - "space l g": "git_panel::ToggleFocus", - "space l w": "editor::ToggleSoftWrap", - "space m p": "editor::Format", - "space q": "diagnostics::Deploy", - "space q t": "pane::CloseOtherItems", - "space r n": "editor::Rename", - "space r s": "editor::RestartLanguageServer", - "space s /": "workspace::NewSearch", - "space s a": "workspace::SaveAll", - "space s b": "tab_switcher::Toggle", - "space s d": "diagnostics::Deploy", - "space s d s": "project_symbols::Toggle", - "space s f": "file_finder::Toggle", - "space s g": "workspace::NewSearch", - "space s n": "workspace::SaveWithoutFormat", - "space shift-d": "diagnostics::DeployCurrentFile", - "space t o": "terminal_panel::ToggleFocus", - "space t r": "task::Rerun", - "space t s": "zed::OpenTasks", - "space t t": "editor::SpawnNearestTask", - "space u d": "editor::ToggleInlineDiagnostics", - "space v": "pane::SplitVertical", - "space w h": "workspace::ActivatePaneLeft", - "space w j": "workspace::ActivatePaneDown", - "space w k": "workspace::ActivatePaneUp", - "space w l": "workspace::ActivatePaneRight", - "space w p": "workspace::ActivatePreviousPane", - "space x": "pane::CloseActiveItem" - } - } -] diff --git a/.config/zed/prompts/prompts-library-db.0.mdb/data.mdb b/.config/zed/prompts/prompts-library-db.0.mdb/data.mdb deleted file mode 100644 index edb1be9..0000000 Binary files a/.config/zed/prompts/prompts-library-db.0.mdb/data.mdb and /dev/null differ diff --git a/.config/zed/prompts/prompts-library-db.0.mdb/lock.mdb b/.config/zed/prompts/prompts-library-db.0.mdb/lock.mdb deleted file mode 100644 index 07156ca..0000000 Binary files a/.config/zed/prompts/prompts-library-db.0.mdb/lock.mdb and /dev/null differ diff --git a/.config/zed/settings.json b/.config/zed/settings.json deleted file mode 100644 index 49d4ae7..0000000 --- a/.config/zed/settings.json +++ /dev/null @@ -1,160 +0,0 @@ -// Zed settings -// -// For information on how to configure Zed, see the Zed -// documentation: https://zed.dev/docs/configuring-zed -// -// This translates the main Neovim behaviors from `.config/n into -// the Zed features that have direct equivalents. -{ - "telemetry": { - "diagnostics": false, - "metrics": false, - }, - - "vim_mode": true, - "base_keymap": "JetBrains", - - "auto_install_extensions": { - "html": true, - "angular": true, - "lua": true, - "sql": true, - "kanagawa-themes": true, - }, - - "icon_theme": { - "mode": "dark", - "light": "Zed (Default)", - "dark": "Zed (Default)", - }, - "theme": { - "mode": "dark", - "light": "Kanagawa Lotus", - "dark": "New Darcula", - }, - - "ui_font_size": 16, - "buffer_font_size": 15, - - "gutter": { - "line_numbers": true, - "breakpoints": true, - "folds": true, - "min_line_number_digits": 4, - }, - "relative_line_numbers": "enabled", - "soft_wrap": "none", - "show_wrap_guides": false, - "tab_size": 4, - "hard_tabs": false, - "vertical_scroll_margin": 4, - "horizontal_scroll_margin": 8, - "use_smartcase_search": true, - "show_whitespaces": "selection", - "hover_popover_delay": 300, - "auto_signature_help": true, - "remove_trailing_whitespace_on_save": true, - "format_on_save": "on", - - "diagnostics_max_severity": null, - "diagnostics": { - "inline": { - "enabled": false, - }, - }, - "inlay_hints": { - "enabled": true, - "show_type_hints": true, - "show_parameter_hints": true, - "show_other_hints": true, - "show_background": false, - }, - - "git": { - "git_gutter": "tracked_files", - "inline_blame": { - "enabled": false, - }, - }, - - "tabs": { - "file_icons": true, - "git_status": true, - "show_close_button": "always", - "show_diagnostics": "all", - }, - "project_panel": { - "default_width": 320, - "git_status": true, - "show_diagnostics": "all", - "hide_hidden": false, - "auto_reveal_entries": true, - "indent_guides": { - "show": "always", - }, - }, - "toolbar": { - "breadcrumbs": true, - "quick_actions": true, - "selections_menu": true, - "code_actions": true, - }, - - "file_types": { - "Groovy": ["**/*.pipeline", "**/*.multibranch"], - "Terraform": ["**/*.tfvars"], - }, - - "languages": { - "JavaScript": { - "language_servers": ["typescript-language-server", "!vtsls", "..."], - }, - "TypeScript": { - "language_servers": ["typescript-language-server", "!vtsls", "..."], - }, - "TSX": { - "language_servers": ["typescript-language-server", "!vtsls", "..."], - }, - "Lua": { - "formatter": { - "external": { - "command": "stylua", - "arguments": [ - "--respect-ignores", - "--stdin-filepath", - "{buffer_path}", - "-", - ], - }, - }, - }, - "Python": { - "formatter": { - "external": { - "command": "ruff", - "arguments": [ - "format", - "--stdin-filename", - "{buffer_path}", - "-", - ], - }, - }, - }, - "Shell Script": { - "tab_size": 4, - "hard_tabs": false, - "formatter": { - "external": { - "command": "shfmt", - "arguments": [ - "--filename", - "{buffer_path}", - "--indent", - "4", - ], - }, - }, - }, - }, -} diff --git a/.gitconfig b/.gitconfig index 95b5cc7..361befc 100644 --- a/.gitconfig +++ b/.gitconfig @@ -27,13 +27,13 @@ today = !git all --since='12am' pom = push origin main pos = push origin staging pod = push origin develop - pou = push origin uat +pou = push origin uat ruo = remote update origin cma = commit --amend cmna = commit -n --amend del = branch -D cm = commit -m -pf = push -f origin +pf = push --force-with-lease origin po = push origin rv = remote -v rao = remote add origin @@ -48,7 +48,7 @@ latest-tag = tag -l --sort=-version:refname back = !f() { git reset --soft HEAD~$1; }; f [push] -default = matching +default = simple [core] editor=nvim diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..11bf146 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,30 @@ +name: Validate + +on: + push: + pull_request: + +jobs: + dotfiles: + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + + - name: Install validation tools + run: | + sudo apt-get update + sudo apt-get install --yes gnupg shellcheck shfmt zsh + source install/lib/common.sh + source install/lib/packages.sh + install_wezterm + brew install neovim starship stylua + + - name: Restore Neovim plugins + env: + DOTFILES_CI: "1" + XDG_CONFIG_HOME: ${{ github.workspace }}/.config + run: nvim --headless "+Lazy! restore" +qa + + - name: Validate + run: bin/check-dotfiles diff --git a/.gitignore b/.gitignore index d60bc0f..7c28fef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,56 +1,33 @@ -# Created by https://www.toptal.com/developers/gitignore/api/vim -# Edit at https://www.toptal.com/developers/gitignore?templates=vim - -### Vim ### -# Swap +# Vim [._]*.s[a-v][a-z] -!*.svg # comment out if you don't need vector files [._]*.sw[a-p] [._]s[a-rt-v][a-z] [._]ss[a-gi-z] [._]sw[a-p] - -# Session Session.vim Sessionx.vim - -# Temporary .netrwhist *~ -# Auto-generated tag files tags -# Persistent undo [._]*.un~ +.nvimlog +nvim.log +.config/zed/ +# Track only the managed application configurations. .config/* - !.config/nvim/ !.config/lazygit/ !.config/starship/ !.config/wezterm/ -# machine-local, generated by .zshrc .config/wezterm/env.lua -!.config/zed/ -!.config/zed/settings.json -!.config/zed/keymap.json !.config/tmux/ -!.config/tmux/nord-theme.conf -!.config/tmux/dragon-theme.conf -!.config/tmux/wave-theme.conf -!.config/tmux/tmux-cheatsheet.md -!.config/tmux/tmux.conf -!.config/.vimrc !.config/.ideavimrc -# Ignore subfolders in tmux +# Ignore generated or downloaded tmux content. .config/tmux/** -!.config/tmux/nord-theme.conf +.config/tmux/current-theme.conf !.config/tmux/dragon-theme.conf !.config/tmux/wave-theme.conf !.config/tmux/tmux-cheatsheet.md !.config/tmux/tmux.conf -!.config/.vimrc -!.config/.ideavimrc - -.nvimlog -# End of https://www.toptal.com/developers/gitignore/api/vim diff --git a/.tmux.conf b/.tmux.conf deleted file mode 100644 index 30c568b..0000000 --- a/.tmux.conf +++ /dev/null @@ -1 +0,0 @@ -source-file ~/.config/tmux/tmux.conf diff --git a/.zshrc b/.zshrc index 38068bb..f34b3e2 100644 --- a/.zshrc +++ b/.zshrc @@ -3,13 +3,13 @@ if [ -x /home/linuxbrew/.linuxbrew/bin/brew ]; then eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" fi +export PATH="$HOME/.local/bin:$PATH" # ===================== User-tunable environment ======================== -# Single place to control the look of the whole stack. Everything below -# (and the wezterm env.lua writer further down) derives from these. +# Single place to control the look of the whole stack. export KANAGAWA_THEME="wave" # wave | dragon export WEZTERM_FONT_SIZE="13" -export WEZTERM_WINDOW_OPACITY="0.80" +export WEZTERM_WINDOW_OPACITY="0.9" export WEZTERM_TEXT_OPACITY="1.0" # ======================================================================== @@ -31,32 +31,11 @@ case "${KANAGAWA_THEME}" in ;; esac -# Write a deterministic tmux theme selector file so tmux servers don't depend -# on inherited shell env vars. -export TMUX_THEME_FILE="$HOME/.config/tmux/current-theme.conf" -mkdir -p "$HOME/.config/tmux" -if [ "$TMUX_THEME" = "wave" ]; then - printf 'source-file %s\n' "$HOME/.config/tmux/wave-theme.conf" >| "$TMUX_THEME_FILE" -else - printf 'source-file %s\n' "$HOME/.config/tmux/dragon-theme.conf" >| "$TMUX_THEME_FILE" +# Generate the theme files consumed by GUI-launched WezTerm and tmux. +if command -v dotfiles-apply-theme >/dev/null 2>&1; then + dotfiles-apply-theme fi -# Write WezTerm settings file (same pattern as tmux above). GUI-launched -# WezTerm never sees shell exports, so it watches this file and live-reloads -# when the values change. -_wez_env_file="$HOME/.config/wezterm/env.lua" -_wez_env_content="return { - theme = \"$WEZTERM_THEME\", - font_size = $WEZTERM_FONT_SIZE, - window_opacity = $WEZTERM_WINDOW_OPACITY, - text_opacity = $WEZTERM_TEXT_OPACITY, -}" -mkdir -p "$HOME/.config/wezterm" -if [ ! -f "$_wez_env_file" ] || [ "$(cat "$_wez_env_file")" != "$_wez_env_content" ]; then - printf '%s\n' "$_wez_env_content" >| "$_wez_env_file" -fi -unset _wez_env_file _wez_env_content - # Starship if command -v starship >/dev/null 2>&1; then eval "$(starship init zsh)" @@ -81,6 +60,9 @@ ZSH_HIGHLIGHT_STYLES[path]=none ZSH_HIGHLIGHT_STYLES[path_prefix]=none # Autosuggestions +# The plugin default is intentionally dim and becomes unreadable with a +# transparent terminal background. +ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=#938aa9' for _p in \ "$HOMEBREW_PREFIX/share/zsh-autosuggestions/zsh-autosuggestions.zsh" \ /usr/share/zsh-autosuggestions/zsh-autosuggestions.zsh @@ -124,7 +106,12 @@ alias sudo="sudo " alias lg="lazygit" alias update="brew upgrade && sudo apt update && sudo apt upgrade -y" -if command -v tmux &>/dev/null && [ -z "$TMUX" ]; then +export DOTFILES_AUTO_TMUX="${DOTFILES_AUTO_TMUX:-0}" +if [ "$DOTFILES_AUTO_TMUX" = "1" ] \ + && command -v tmux &>/dev/null \ + && [ -z "$TMUX" ] \ + && [ -z "$SSH_CONNECTION" ] +then tmux attach -t default 2>/dev/null || tmux new -s default fi diff --git a/README.md b/README.md index 3a66efa..41a4eb2 100644 --- a/README.md +++ b/README.md @@ -5,21 +5,12 @@ Linux dotfiles oriented around **Pop!_OS 24.04** (Cosmic DE). ## Quick Setup ```bash -# Clone -git clone ~/dotfiles - -# Symlink configs -ln -sfn ~/dotfiles/.config/nvim ~/.config/nvim -ln -sfn ~/dotfiles/.config/tmux ~/.config/tmux -ln -sfn ~/dotfiles/.config/starship ~/.config/starship -ln -sfn ~/dotfiles/.config/lazygit ~/.config/lazygit -ln -sfn ~/dotfiles/.config/wezterm ~/.config/wezterm -ln -sfn ~/dotfiles/.config/zed ~/.config/zed -ln -sfn ~/dotfiles/.config/.ideavimrc ~/.ideavimrc -ln -sfn ~/dotfiles/.zshrc ~/.zshrc -ln -sfn ~/dotfiles/.gitconfig ~/.gitconfig +git clone https://github.com/brianpooe/dotfiles.git ~/dotfiles +~/dotfiles/install/install.sh --dotfiles-only ``` +The installer backs up existing targets before linking replacements. + ## Install On Pop!_OS / Ubuntu 24.04, run the bundled installer. It uses **apt** for @@ -33,7 +24,8 @@ starship, gh, zsh plugins) so those tools stay current — one ``` See [`install/README.md`](install/README.md) for flags and the full -package list. The `update` alias runs `brew upgrade && sudo apt upgrade`. +package list. The `update` alias runs +`brew upgrade && sudo apt update && sudo apt upgrade -y`. ## Theme @@ -44,3 +36,20 @@ Set in shell: ```bash export KANAGAWA_THEME="wave" # or "dragon" ``` + +Opening a new shell runs `dotfiles-apply-theme`, which writes the generated +tmux and WezTerm settings to `~/.cache/dotfiles`. + +Automatic tmux startup is opt-in: + +```bash +export DOTFILES_AUTO_TMUX=1 +``` + +## Validate + +```bash +bin/check-dotfiles +``` + +The same checks run in GitHub Actions. diff --git a/bin/check-dotfiles b/bin/check-dotfiles new file mode 100755 index 0000000..f879470 --- /dev/null +++ b/bin/check-dotfiles @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$repo_root" + +export PATH="$HOME/.local/share/nvim/mason/bin:$PATH" + +require_cmd() { + command -v "$1" >/dev/null 2>&1 || { + printf 'Required validation tool not found: %s\n' "$1" >&2 + exit 1 + } +} + +for command in bash git nvim shellcheck shfmt starship stylua wezterm zsh; do + require_cmd "$command" +done + +shell_files=(bin/dotfiles-apply-theme bin/check-dotfiles install/install.sh install/lib/*.sh) + +git diff --check +bash -n "${shell_files[@]}" +zsh -n .zshrc +git config --file .gitconfig --list >/dev/null +shellcheck --exclude=SC1091 "${shell_files[@]}" +shfmt -d -i 2 -ci "${shell_files[@]}" +stylua --check .config/nvim + +tmp="$(mktemp -d)" +trap 'rm -rf "$tmp"' EXIT +mkdir -p "$tmp/data/nvim" +ln -s "$HOME/.local/share/nvim/lazy" "$tmp/data/nvim/lazy" + +HOME="$tmp/home" \ + KANAGAWA_THEME=wave \ + WEZTERM_FONT_SIZE=13 \ + WEZTERM_WINDOW_OPACITY=.9 \ + WEZTERM_TEXT_OPACITY=1 \ + bin/dotfiles-apply-theme + +STARSHIP_CONFIG="$repo_root/.config/starship/starship.toml" starship print-config >/dev/null +STARSHIP_CONFIG="$repo_root/.config/starship/starship-wave.toml" starship print-config >/dev/null +wezterm --config-file "$repo_root/.config/wezterm/wezterm.lua" show-keys --lua >/dev/null + +nvim_output="$( + DOTFILES_CI=1 \ + XDG_CONFIG_HOME="$repo_root/.config" \ + XDG_CACHE_HOME="$tmp/cache" \ + XDG_DATA_HOME="$tmp/data" \ + XDG_STATE_HOME="$tmp/state" \ + nvim --headless '+sleep 100m' '+lua assert(vim.v.errmsg == "", vim.v.errmsg)' '+qa' 2>&1 +)" +if [[ -n $nvim_output ]]; then + printf 'Neovim startup produced unexpected output:\n%s\n' "$nvim_output" >&2 + exit 1 +fi + +printf 'All dotfile checks passed.\n' diff --git a/bin/dotfiles-apply-theme b/bin/dotfiles-apply-theme new file mode 100755 index 0000000..137e4df --- /dev/null +++ b/bin/dotfiles-apply-theme @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +set -euo pipefail + +theme="${KANAGAWA_THEME:-wave}" +font_size="${WEZTERM_FONT_SIZE:-13}" +window_opacity="${WEZTERM_WINDOW_OPACITY:-0.9}" +text_opacity="${WEZTERM_TEXT_OPACITY:-1.0}" +state_dir="$HOME/.cache/dotfiles" + +case "$theme" in + wave | kanagawa-wave) theme=wave ;; + dragon | kanagawa-dragon) theme=dragon ;; + *) theme=dragon ;; +esac + +for value in "$font_size" "$window_opacity" "$text_opacity"; do + [[ $value =~ ^([0-9]+([.][0-9]*)?|[.][0-9]+)$ ]] || { + printf 'Invalid numeric theme value: %s\n' "$value" >&2 + exit 1 + } +done + +mkdir -p "$state_dir" + +write_if_changed() { + local destination="$1" content="$2" temporary + + if [[ -f $destination ]] && [[ $(<"$destination") == "$content" ]]; then + return + fi + + temporary="$(mktemp "$state_dir/.theme.XXXXXX")" + printf '%s\n' "$content" >"$temporary" + mv "$temporary" "$destination" +} + +write_if_changed "$state_dir/tmux-theme.conf" \ + "source-file \"$HOME/.config/tmux/$theme-theme.conf\"" + +write_if_changed "$state_dir/wezterm-env.lua" "return { + theme = \"$theme\", + font_size = $font_size, + window_opacity = $window_opacity, + text_opacity = $text_opacity, +}" diff --git a/install/README.md b/install/README.md index b2b1bec..d1bd893 100644 --- a/install/README.md +++ b/install/README.md @@ -12,6 +12,10 @@ Ubuntu 24.04 / Debian-derivative) to the state these dotfiles assume. | **nvm** | Node.js runtime | Installs the latest LTS release and sets it as the default; `.zshrc` sources `~/.nvm/nvm.sh`. | | **tpm** | tmux plugin manager | Clone of `tmux-plugins/tpm` so the bundled `tmux.conf` works on first launch. | +Downloaded installer scripts, apt signing keys, and the Nerd Font archive +are pinned and verified before use. Updating their upstream versions requires +updating the corresponding SHA-256 hashes or fingerprints in `install/lib/`. + GitHub-release-binary downloads (the previous approach for nvim/lazygit/ starship) have been retired — they ran latest at install time then went stale. Brew solves that. @@ -46,7 +50,7 @@ install/install.sh --help ## Keeping current ```bash -update # alias: brew upgrade && sudo apt upgrade +update # alias: brew upgrade && sudo apt update && sudo apt upgrade -y ``` The CLI tools come from brew, so they stay on bleeding-edge stable. Brave @@ -63,6 +67,7 @@ install/ lib/packages.sh apt base + WezTerm + Brave Beta vendor repos lib/brew.sh install Homebrew + `brew bundle` lib/extras.sh nvm + tpm + lib/fonts.sh verified JetBrainsMono Nerd Font install lib/dotfiles.sh symlink dotfiles + chsh zsh ``` @@ -75,8 +80,9 @@ install/ 5. **Homebrew install** (NONINTERACTIVE) + `brew bundle` against `Brewfile`. 6. **nvm** (pinned `$NVM_VERSION`, default `v0.40.1`) + latest LTS Node.js. 7. **tpm** clone to `~/.tmux/plugins/tpm`. -8. **Symlink** `nvim tmux starship lazygit wezterm zed → ~/.config/...`; - `.config/.ideavimrc → ~/.ideavimrc`; `.zshrc`, `.gitconfig → ~`. +8. **Symlink** `nvim tmux starship lazygit wezterm → ~/.config/...`; + `.config/.ideavimrc → ~/.ideavimrc`; `.zshrc`, `.gitconfig → ~`; + `dotfiles-apply-theme → ~/.local/bin/`. 9. **chsh** login shell to zsh. Pre-existing config targets get backed up to `*.pre-install.`, diff --git a/install/install.sh b/install/install.sh index 601144a..b833f06 100755 --- a/install/install.sh +++ b/install/install.sh @@ -36,7 +36,10 @@ for arg in "$@"; do case $arg in --packages-only) MODE=packages ;; --dotfiles-only) MODE=dotfiles ;; - -h|--help) usage; exit 0 ;; + -h | --help) + usage + exit 0 + ;; *) die "Unknown argument: $arg (try --help)" ;; esac done @@ -46,6 +49,7 @@ preflight() { ((EUID != 0)) || die "Do not run as root; run as your regular user." require_cmd sudo require_cmd curl + require_cmd sha256sum } main() { @@ -76,7 +80,7 @@ main() { info "Done. Open a new shell (or log out + back in) to pick up the new login shell." info "First tmux launch: prefix + I to fetch tpm plugins." - info "Keep tools current with: brew upgrade && sudo apt upgrade" + info "Keep tools current with: brew upgrade && sudo apt update && sudo apt upgrade -y" } main diff --git a/install/lib/brew.sh b/install/lib/brew.sh index 9a4f6cf..e97b98c 100644 --- a/install/lib/brew.sh +++ b/install/lib/brew.sh @@ -3,14 +3,25 @@ # Brew owns the CLI toolchain so `brew upgrade` always gets latest versions. BREW_PREFIX="/home/linuxbrew/.linuxbrew" +HOMEBREW_INSTALL_COMMIT="93a25fd2d2fdb86422303c292b9223f84ef23eaf" +HOMEBREW_INSTALL_SHA256="17f204b128869ec71e6b650051f1e65ef5b74cf994c2ff8b919103241554dd2f" install_homebrew() { if [[ -x $BREW_PREFIX/bin/brew ]]; then info "Homebrew already installed" else info "Installing Homebrew..." - NONINTERACTIVE=1 bash -c \ - "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + local installer + installer="$(mktemp)" + download_verified \ + "https://raw.githubusercontent.com/Homebrew/install/$HOMEBREW_INSTALL_COMMIT/install.sh" \ + "$HOMEBREW_INSTALL_SHA256" \ + "$installer" + if ! NONINTERACTIVE=1 bash "$installer"; then + rm -f "$installer" + die "Homebrew installer failed" + fi + rm -f "$installer" fi eval "$("$BREW_PREFIX/bin/brew" shellenv)" } diff --git a/install/lib/common.sh b/install/lib/common.sh index 259062f..e3dfb39 100644 --- a/install/lib/common.sh +++ b/install/lib/common.sh @@ -8,12 +8,49 @@ APT_LISTS_DIR="/etc/apt/sources.list.d" info() { printf '\033[0;32m==>\033[0m %s\n' "$*"; } warn() { printf '\033[1;33mWARNING:\033[0m %s\n' "$*" >&2; } error() { printf '\033[0;31mERROR:\033[0m %s\n' "$*" >&2; } -die() { error "$*"; exit 1; } +die() { + error "$*" + exit 1 +} require_cmd() { command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1" } +download_verified() { + local url="$1" sha256="$2" destination="$3" + + if ! curl -fsSL --retry 3 "$url" -o "$destination"; then + rm -f "$destination" + die "Failed to download $url" + fi + if ! printf '%s %s\n' "$sha256" "$destination" | sha256sum --check --status -; then + rm -f "$destination" + die "SHA-256 verification failed for $url" + fi +} + +key_fingerprints_match() { + local key="$1" expected_fingerprints="$2" + local actual_fingerprints normalized_expected + + actual_fingerprints="$( + gpg --show-keys --with-colons "$key" 2>/dev/null | + awk -F: '$1 == "fpr" { print $10 }' | + sort -u | + tr '\n' ' ' + )" + normalized_expected="$( + for fingerprint in $expected_fingerprints; do + printf '%s\n' "$fingerprint" + done | + sort -u | + tr '\n' ' ' + )" + + [[ -n $actual_fingerprints && $actual_fingerprints == "$normalized_expected" ]] +} + apt_install() { sudo apt-get install -y --no-install-recommends "$@" } @@ -45,15 +82,14 @@ backup_and_link() { info "Linked $dest -> $src" } -# Add a signed apt repo. Args: name keyring_url repo_line -# Downloads the key to a temp file first and verifies it, so a failed -# fetch never leaves an empty keyring or partial list file behind. +# Add a signed apt repo. Args: name keyring_url repo_line expected_fingerprints add_apt_repo() { - local name="$1" keyring_url="$2" repo_line="$3" + local name="$1" keyring_url="$2" repo_line="$3" expected_fingerprints="$4" local keyring="$KEYRINGS_DIR/$name.gpg" local list="$APT_LISTS_DIR/$name.list" - if [[ -f $list && -s $keyring ]]; then + if [[ -f $list && -s $keyring ]] && + key_fingerprints_match "$keyring" "$expected_fingerprints"; then return 0 fi info "Adding apt repo: $name" @@ -68,7 +104,10 @@ add_apt_repo() { fi [[ -s $tmp_key ]] || die "Downloaded key for $name is empty" - if ! gpg --yes --dearmor -o "$tmp_gpg" < "$tmp_key" 2>/dev/null; then + key_fingerprints_match "$tmp_key" "$expected_fingerprints" || + die "Signing-key fingerprint verification failed for $name" + + if ! gpg --yes --dearmor -o "$tmp_gpg" <"$tmp_key" 2>/dev/null; then # Already binary (.gpg) keys can't be dearmored; use the raw bytes instead. cp "$tmp_key" "$tmp_gpg" fi @@ -76,5 +115,5 @@ add_apt_repo() { sudo install -d -m 0755 "$KEYRINGS_DIR" sudo install -m 0644 "$tmp_gpg" "$keyring" echo "$repo_line" | sudo tee "$list" >/dev/null - unset _APT_UPDATED # force re-update so the new repo is seen + unset _APT_UPDATED # force re-update so the new repo is seen } diff --git a/install/lib/dotfiles.sh b/install/lib/dotfiles.sh index 7d4d2e8..e8d245c 100644 --- a/install/lib/dotfiles.sh +++ b/install/lib/dotfiles.sh @@ -3,15 +3,15 @@ link_dotfiles() { info "Linking dotfiles from $DOTFILES_DIR..." - backup_and_link "$DOTFILES_DIR/.config/nvim" "$HOME/.config/nvim" - backup_and_link "$DOTFILES_DIR/.config/tmux" "$HOME/.config/tmux" - backup_and_link "$DOTFILES_DIR/.config/starship" "$HOME/.config/starship" - backup_and_link "$DOTFILES_DIR/.config/lazygit" "$HOME/.config/lazygit" - backup_and_link "$DOTFILES_DIR/.config/wezterm" "$HOME/.config/wezterm" - backup_and_link "$DOTFILES_DIR/.config/zed" "$HOME/.config/zed" + backup_and_link "$DOTFILES_DIR/.config/nvim" "$HOME/.config/nvim" + backup_and_link "$DOTFILES_DIR/.config/tmux" "$HOME/.config/tmux" + backup_and_link "$DOTFILES_DIR/.config/starship" "$HOME/.config/starship" + backup_and_link "$DOTFILES_DIR/.config/lazygit" "$HOME/.config/lazygit" + backup_and_link "$DOTFILES_DIR/.config/wezterm" "$HOME/.config/wezterm" backup_and_link "$DOTFILES_DIR/.config/.ideavimrc" "$HOME/.ideavimrc" - backup_and_link "$DOTFILES_DIR/.zshrc" "$HOME/.zshrc" - backup_and_link "$DOTFILES_DIR/.gitconfig" "$HOME/.gitconfig" + backup_and_link "$DOTFILES_DIR/.zshrc" "$HOME/.zshrc" + backup_and_link "$DOTFILES_DIR/.gitconfig" "$HOME/.gitconfig" + backup_and_link "$DOTFILES_DIR/bin/dotfiles-apply-theme" "$HOME/.local/bin/dotfiles-apply-theme" } set_zsh_as_shell() { diff --git a/install/lib/extras.sh b/install/lib/extras.sh index 179d4a8..64703a0 100644 --- a/install/lib/extras.sh +++ b/install/lib/extras.sh @@ -2,15 +2,22 @@ # Per-user helpers not managed by brew: nvm (official installer) + tpm. NVM_VERSION="${NVM_VERSION:-v0.40.1}" +NVM_INSTALL_SHA256="${NVM_INSTALL_SHA256:-abdb525ee9f5b48b34d8ed9fc67c6013fb0f659712e401ecd88ab989b3af8f53}" install_nvm() { if [[ -s $HOME/.nvm/nvm.sh ]]; then info "nvm already installed" else info "Installing nvm ($NVM_VERSION)..." - PROFILE=/dev/null bash -c \ - "curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/$NVM_VERSION/install.sh | bash" || + local installer + installer="$(mktemp)" + download_verified \ + "https://raw.githubusercontent.com/nvm-sh/nvm/$NVM_VERSION/install.sh" \ + "$NVM_INSTALL_SHA256" \ + "$installer" + PROFILE=/dev/null bash "$installer" || warn "nvm install failed (install manually if you need Node)" + rm -f "$installer" fi if [[ -s $HOME/.nvm/nvm.sh ]]; then diff --git a/install/lib/fonts.sh b/install/lib/fonts.sh index 53baefa..ae183c7 100644 --- a/install/lib/fonts.sh +++ b/install/lib/fonts.sh @@ -6,6 +6,8 @@ NERD_FONT_NAME="JetBrainsMono" NERD_FONT_DIR="$HOME/.local/share/fonts/${NERD_FONT_NAME}NerdFont" +NERD_FONT_VERSION="v3.4.0" +NERD_FONT_SHA256="ef552a3e638f25125c6ad4c51176a6adcdce295ab1d2ffacf0db060caf8c1582" install_nerd_font() { if fc-list 2>/dev/null | grep -qi "JetBrainsMono Nerd Font"; then @@ -14,15 +16,12 @@ install_nerd_font() { fi info "Installing ${NERD_FONT_NAME} Nerd Font..." - local url="https://github.com/ryanoasis/nerd-fonts/releases/latest/download/${NERD_FONT_NAME}.tar.xz" + local url="https://github.com/ryanoasis/nerd-fonts/releases/download/$NERD_FONT_VERSION/${NERD_FONT_NAME}.tar.xz" local tmp tmp="$(mktemp -d)" trap 'rm -rf "$tmp"' RETURN - if ! curl -fsSL --retry 3 -o "$tmp/font.tar.xz" "$url"; then - warn "Nerd Font download failed; install manually from https://www.nerdfonts.com" - return - fi + download_verified "$url" "$NERD_FONT_SHA256" "$tmp/font.tar.xz" mkdir -p "$NERD_FONT_DIR" tar -xJf "$tmp/font.tar.xz" -C "$NERD_FONT_DIR" diff --git a/install/lib/packages.sh b/install/lib/packages.sh index 9b11790..f56125f 100644 --- a/install/lib/packages.sh +++ b/install/lib/packages.sh @@ -44,7 +44,8 @@ install_wezterm() { fi add_apt_repo wezterm-fury \ https://apt.fury.io/wez/gpg.key \ - "deb [signed-by=$KEYRINGS_DIR/wezterm-fury.gpg] https://apt.fury.io/wez/ * *" + "deb [signed-by=$KEYRINGS_DIR/wezterm-fury.gpg] https://apt.fury.io/wez/ * *" \ + "0CA603116C960BAFB2BF310BD7BA31CF90C4B319 82FA179A44D70EEF9CDEB52659C703F427E1A6C9" apt_update_once apt_install wezterm } @@ -59,7 +60,8 @@ install_brave_beta() { # "beta" in the name. add_apt_repo brave-browser-beta \ https://brave-browser-apt-beta.s3.brave.com/brave-browser-beta-archive-keyring.gpg \ - "deb [signed-by=$KEYRINGS_DIR/brave-browser-beta.gpg arch=amd64] https://brave-browser-apt-beta.s3.brave.com/ stable main" + "deb [signed-by=$KEYRINGS_DIR/brave-browser-beta.gpg arch=amd64] https://brave-browser-apt-beta.s3.brave.com/ stable main" \ + "56F49901AB19BAF099A95A76C3DE1DD4F661CDCB 8C1F16AB24DF8F75C1CF56595929A141E0E87F1F B721E073B7EF8E56ACC6B23ECBC67D2399225CCF" apt_update_once apt_install brave-browser-beta }