From 84fbb30d82356db6353e741bc1e006a93feb6d7d Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 7 Jun 2026 16:50:33 +0000 Subject: [PATCH] chore: drop macOS/Windows support and offline nvim tooling - Remove macOS/Windows clipboard branches, homebrew fzf path from .vimrc - Remove is_macos/is_windows logic from wezterm.lua - Drop scripts/nvim-offline-* and NVIM_OFFLINE plumbing from nvim config - Clean macOS section from .gitignore; simplify .gitattributes to LF-only - Trim README of offline-bundle section --- .config/.vimrc | 9 +- .config/nvim/lua/brian/core/options.lua | 4 - .config/nvim/lua/brian/lazy.lua | 7 +- .config/nvim/lua/brian/plugins/dap.lua | 9 +- .config/nvim/lua/brian/plugins/mason.lua | 43 +-- .config/nvim/lua/brian/plugins/treesitter.lua | 8 +- .config/wezterm/wezterm.lua | 93 +----- .gitattributes | 15 +- .gitignore | 47 +-- README.md | 42 --- scripts/nvim-offline-install.sh | 191 ------------ scripts/nvim-offline-prepare.sh | 280 ------------------ 12 files changed, 20 insertions(+), 728 deletions(-) delete mode 100755 scripts/nvim-offline-install.sh delete mode 100755 scripts/nvim-offline-prepare.sh diff --git a/.config/.vimrc b/.config/.vimrc index 35b533c..b4a9ccb 100644 --- a/.config/.vimrc +++ b/.config/.vimrc @@ -166,13 +166,7 @@ set background=dark " hi VertSplit guibg=NONE guifg=NONE ctermbg=NONE ctermfg=NONE " Sync clipboard with OS -if has('win32') || has('win64') - set clipboard=unnamedplus " Windows -elseif has('macunix') - set clipboard=unnamed " macOS -else - set clipboard=unnamedplus " Linux/WSL -endif +set clipboard=unnamedplus " True colors if !has('gui_running') && &term =~ '\%(screen\|tmux\)' @@ -198,4 +192,3 @@ augroup netrw_setup | au! au FileType netrw nmap l augroup END -set rtp+=/opt/homebrew/opt/fzf diff --git a/.config/nvim/lua/brian/core/options.lua b/.config/nvim/lua/brian/core/options.lua index 947acb7..a9bf456 100644 --- a/.config/nvim/lua/brian/core/options.lua +++ b/.config/nvim/lua/brian/core/options.lua @@ -42,10 +42,6 @@ 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 --- Offline mode: set NVIM_OFFLINE=1 to disable all network operations --- (plugin update checks, Mason auto-install, Treesitter auto-install) -vim.g.offline_mode = vim.env.NVIM_OFFLINE == '1' - -- 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 diff --git a/.config/nvim/lua/brian/lazy.lua b/.config/nvim/lua/brian/lazy.lua index 74442de..f7f1e32 100644 --- a/.config/nvim/lua/brian/lazy.lua +++ b/.config/nvim/lua/brian/lazy.lua @@ -22,19 +22,14 @@ if not (vim.uv or vim.loop).fs_stat(lazypath) then end vim.opt.rtp:prepend(lazypath) -local offline = vim.g.offline_mode - require('lazy').setup { spec = { { import = 'brian.plugins' }, }, install = { colorscheme = { 'kanagawa' } }, - checker = { enabled = not offline, notify = false }, + checker = { enabled = true, notify = false }, change_detection = { notify = false, }, - git = { - timeout = offline and 1 or 120, - }, } vim.keymap.set('n', 'l', ':Lazy', { desc = 'Plugin Manager' }) diff --git a/.config/nvim/lua/brian/plugins/dap.lua b/.config/nvim/lua/brian/plugins/dap.lua index 0914bcd..18f0286 100644 --- a/.config/nvim/lua/brian/plugins/dap.lua +++ b/.config/nvim/lua/brian/plugins/dap.lua @@ -1,6 +1,3 @@ -local preparing = vim.env.NVIM_OFFLINE_PREPARE == '1' -local dap_auto_install = not vim.g.offline_mode and not preparing - return { 'mfussenegger/nvim-dap', dependencies = { @@ -52,13 +49,13 @@ return { dependencies = 'mason.nvim', cmd = { 'DapInstall', 'DapUninstall' }, opts = { - automatic_installation = dap_auto_install, + automatic_installation = true, handlers = {}, - ensure_installed = dap_auto_install and { + ensure_installed = { 'python', 'js', 'codelldb', - } or {}, + }, }, }, }, diff --git a/.config/nvim/lua/brian/plugins/mason.lua b/.config/nvim/lua/brian/plugins/mason.lua index ae1ad82..e905c4c 100644 --- a/.config/nvim/lua/brian/plugins/mason.lua +++ b/.config/nvim/lua/brian/plugins/mason.lua @@ -1,19 +1,3 @@ -local offline = vim.g.offline_mode -local preparing = vim.env.NVIM_OFFLINE_PREPARE == '1' -local online = not offline -local auto_install = online and not preparing -local skip_prepare_packages = {} - -do - local raw = vim.env.PREPARE_SKIP_MASON_PACKAGES or '' - for name in string.gmatch(raw, '([^,]+)') do - local trimmed = vim.trim(name) - if trimmed ~= '' then - skip_prepare_packages[trimmed] = true - end - end -end - local lsp_servers = { 'angularls', 'bashls', @@ -68,32 +52,13 @@ local function dedupe(list) return out end -local function filter_skips(list) - if not preparing or vim.tbl_count(skip_prepare_packages) == 0 then - return list - end - local out = {} - for _, item in ipairs(list) do - if not skip_prepare_packages[item] then - table.insert(out, item) - end - end - return out -end - -local configured_mason_packages = dedupe( - vim.list_extend(vim.deepcopy(tools), lsp_servers) -) -local prepare_tools = filter_skips(vim.deepcopy(configured_mason_packages)) -local tool_installer_list = preparing and prepare_tools or configured_mason_packages +local tool_installer_list = dedupe(vim.list_extend(vim.deepcopy(tools), lsp_servers)) return { { 'mason-org/mason-lspconfig.nvim', opts = { - -- skip auto-install when offline; packages are pre-installed - -- during offline prepare we install LSP packages via MasonToolsInstallSync - ensure_installed = auto_install and lsp_servers or {}, + ensure_installed = lsp_servers, -- Only auto-enable the LSP servers we explicitly manage in this config. -- This avoids accidental multi-server attaches (e.g. ts_ls + vtsls), -- which can produce duplicate Telescope definition results. @@ -121,14 +86,12 @@ return { 'mason-org/mason.nvim', }, opts = { - -- skip auto-install when offline; packages are pre-installed - -- during offline prepare we install tools + lsp packages in one sync pass ensure_installed = tool_installer_list, integrations = { -- keep mapping enabled so lspconfig names (e.g. ts_ls) resolve to Mason packages ['mason-lspconfig'] = true, }, - run_on_start = auto_install, + run_on_start = true, start_delay = 0, }, }, diff --git a/.config/nvim/lua/brian/plugins/treesitter.lua b/.config/nvim/lua/brian/plugins/treesitter.lua index de10aaa..b5fd27b 100644 --- a/.config/nvim/lua/brian/plugins/treesitter.lua +++ b/.config/nvim/lua/brian/plugins/treesitter.lua @@ -1,12 +1,8 @@ -- Highlight, edit, and navigate code -local preparing = vim.env.NVIM_OFFLINE_PREPARE == '1' - return { 'nvim-treesitter/nvim-treesitter', branch = 'master', - -- During offline prepare we run TSUpdateSync explicitly from the script. - -- Skipping lazy build here prevents duplicate parser builds in later steps. - build = preparing and nil or ':TSUpdate', + build = ':TSUpdate', main = 'nvim-treesitter.configs', dependencies = { 'nvim-treesitter/nvim-treesitter-textobjects', @@ -41,7 +37,7 @@ return { 'html', 'rust', }, - auto_install = not vim.g.offline_mode and not preparing, + auto_install = true, highlight = { enable = true }, indent = { enable = true }, incremental_selection = { diff --git a/.config/wezterm/wezterm.lua b/.config/wezterm/wezterm.lua index eafb859..ddb7c77 100644 --- a/.config/wezterm/wezterm.lua +++ b/.config/wezterm/wezterm.lua @@ -4,10 +4,6 @@ if wezterm.config_builder then config = wezterm.config_builder() end -local target_triple = wezterm.target_triple or "" -local is_macos = target_triple:find("apple%-darwin") ~= nil -local is_windows = target_triple:find("windows") ~= nil - local function env_number(name, fallback) local raw = os.getenv(name) local parsed = raw and tonumber(raw) or nil @@ -26,16 +22,10 @@ config.use_fancy_tab_bar = false config.tab_bar_at_bottom = false config.enable_tab_bar = false -- Font configuration -local default_font_size = is_windows and 13 or 16 -config.font_size = env_number("WEZTERM_FONT_SIZE", default_font_size) +config.font_size = env_number("WEZTERM_FONT_SIZE", 16) config.font = wezterm.font("JetBrains Mono", { weight = "Regular" }) config.bold_brightens_ansi_colors = true config.force_reverse_video_cursor = false -if is_windows then - -- Windows GPUs/drivers can show transient render artifacts with the default - -- backend. OpenGL is generally more stable here. - config.front_end = "OpenGL" -end local function resolve_theme_name() local value = (os.getenv("WEZTERM_THEME") or "dragon"):lower() local aliases = { @@ -128,20 +118,11 @@ config.window_padding = { top = 8, bottom = 8, } --- Window transparency and blur -local default_opacity = is_windows and 1.0 or 0.80 -config.window_background_opacity = env_number("WEZTERM_WINDOW_OPACITY", default_opacity) -if is_macos then - config.macos_window_background_blur = 60 -end +-- Window transparency +config.window_background_opacity = env_number("WEZTERM_WINDOW_OPACITY", 0.80) -- Keep text cells opaque by default; transparent text backgrounds can show -- grid/dot artifacts on some GPU/compositor combinations. config.text_background_opacity = env_number("WEZTERM_TEXT_OPACITY", 1.0) -if is_windows then - -- Disable transparency on Windows to avoid compositor repaint glitches. - config.window_background_opacity = 1.0 - config.text_background_opacity = 1.0 -end -- Performance config.scrollback_lines = 10000 config.enable_scroll_bar = false @@ -151,10 +132,10 @@ config.cursor_blink_rate = 500 config.cursor_blink_ease_in = "Constant" config.cursor_blink_ease_out = "Constant" -- Key bindings -local keys = { +config.keys = { { key = "Enter", mods = "CTRL", action = wezterm.action({ SendString = "\x1b[13;5u" }) }, { key = "Enter", mods = "SHIFT", action = wezterm.action({ SendString = "\x1b[13;2u" }) }, - -- Navigate splits (via physical Win+Shift) + -- Navigate splits { key = "h", mods = "CTRL|SHIFT", action = wezterm.action.ActivatePaneDirection("Left") }, { key = "l", mods = "CTRL|SHIFT", action = wezterm.action.ActivatePaneDirection("Right") }, { key = "k", mods = "CTRL|SHIFT", action = wezterm.action.ActivatePaneDirection("Up") }, @@ -168,70 +149,6 @@ local keys = { { key = "z", mods = "CTRL|SHIFT", action = wezterm.action.TogglePaneZoomState }, { key = "f", mods = "SHIFT|CTRL", action = wezterm.action.ToggleFullScreen }, } - -if is_windows then - -- Keep CapsLock->Escape only on Windows; macOS is handled by Karabiner. - table.insert(keys, { - key = "CapsLock", - mods = "NONE", - action = wezterm.action.SendKey({ key = "Escape" }), - }) -end - -if is_macos then - local mac_keys = { - -- ===================================================== - -- NuPhy V3 fix: Physical Ctrl sends CMD to macOS. - -- Map CMD+key -> Ctrl+key so terminal apps work naturally. - -- ===================================================== - - -- Tmux prefix: physical Ctrl+Space - { key = "Space", mods = "CMD", action = wezterm.action.SendKey({ key = "Space", mods = "CTRL" }) }, - - -- Common terminal control characters - { key = "a", mods = "CMD", action = wezterm.action.SendKey({ key = "a", mods = "CTRL" }) }, - { key = "b", mods = "CMD", action = wezterm.action.SendKey({ key = "b", mods = "CTRL" }) }, - { key = "c", mods = "CMD", action = wezterm.action.SendKey({ key = "c", mods = "CTRL" }) }, - { key = "d", mods = "CMD", action = wezterm.action.SendKey({ key = "d", mods = "CTRL" }) }, - { key = "e", mods = "CMD", action = wezterm.action.SendKey({ key = "e", mods = "CTRL" }) }, - { key = "f", mods = "CMD", action = wezterm.action.SendKey({ key = "f", mods = "CTRL" }) }, - { key = "g", mods = "CMD", action = wezterm.action.SendKey({ key = "g", mods = "CTRL" }) }, - { key = "h", mods = "CMD", action = wezterm.action.SendKey({ key = "h", mods = "CTRL" }) }, - { key = "j", mods = "CMD", action = wezterm.action.SendKey({ key = "j", mods = "CTRL" }) }, - { key = "k", mods = "CMD", action = wezterm.action.SendKey({ key = "k", mods = "CTRL" }) }, - { key = "l", mods = "CMD", action = wezterm.action.SendKey({ key = "l", mods = "CTRL" }) }, - { key = "n", mods = "CMD", action = wezterm.action.SendKey({ key = "n", mods = "CTRL" }) }, - { key = "o", mods = "CMD", action = wezterm.action.SendKey({ key = "o", mods = "CTRL" }) }, - { key = "p", mods = "CMD", action = wezterm.action.SendKey({ key = "p", mods = "CTRL" }) }, - { key = "r", mods = "CMD", action = wezterm.action.SendKey({ key = "r", mods = "CTRL" }) }, - { key = "t", mods = "CMD", action = wezterm.action.SendKey({ key = "t", mods = "CTRL" }) }, - { key = "u", mods = "CMD", action = wezterm.action.SendKey({ key = "u", mods = "CTRL" }) }, - { key = "v", mods = "CMD", action = wezterm.action.SendKey({ key = "v", mods = "CTRL" }) }, - { key = "w", mods = "CMD", action = wezterm.action.SendKey({ key = "w", mods = "CTRL" }) }, - { key = "x", mods = "CMD", action = wezterm.action.SendKey({ key = "x", mods = "CTRL" }) }, - { key = "z", mods = "CMD", action = wezterm.action.SendKey({ key = "z", mods = "CTRL" }) }, - { key = "[", mods = "CMD", action = wezterm.action.SendKey({ key = "[", mods = "CTRL" }) }, - { key = "]", mods = "CMD", action = wezterm.action.SendKey({ key = "]", mods = "CTRL" }) }, - { key = "\\", mods = "CMD", action = wezterm.action.SendKey({ key = "\\", mods = "CTRL" }) }, - - -- Physical Ctrl+Shift combos (CMD+SHIFT -> WezTerm actions) - { key = "f", mods = "CMD|SHIFT", action = wezterm.action.ToggleFullScreen }, - { key = "h", mods = "CMD|SHIFT", action = wezterm.action.ActivatePaneDirection("Left") }, - { key = "l", mods = "CMD|SHIFT", action = wezterm.action.ActivatePaneDirection("Right") }, - { key = "k", mods = "CMD|SHIFT", action = wezterm.action.ActivatePaneDirection("Up") }, - { key = "j", mods = "CMD|SHIFT", action = wezterm.action.ActivatePaneDirection("Down") }, - { key = "|", mods = "CMD|SHIFT", action = wezterm.action.SplitHorizontal({ domain = "CurrentPaneDomain" }) }, - { key = "_", mods = "CMD|SHIFT", action = wezterm.action.SplitVertical({ domain = "CurrentPaneDomain" }) }, - { key = "w", mods = "CMD|SHIFT", action = wezterm.action.CloseCurrentPane({ confirm = false }) }, - { key = "z", mods = "CMD|SHIFT", action = wezterm.action.TogglePaneZoomState }, - } - - for _, key in ipairs(mac_keys) do - table.insert(keys, key) - end -end - -config.keys = keys -- Hyperlink rules config.hyperlink_rules = { { regex = "\\((\\w+://\\S+)\\)", format = "$1", highlight = 1 }, diff --git a/.gitattributes b/.gitattributes index be87e51..6313b56 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,14 +1 @@ -* text=auto - -# Keep Unix configs LF so copies into WSL don't break parsers. -*.sh text eol=lf -*.zsh text eol=lf -*.conf text eol=lf -*.lua text eol=lf -*.toml text eol=lf -*.vim text eol=lf -*.vimrc text eol=lf -*.md text eol=lf - -# Keep PowerShell script native on Windows. -*.ps1 text eol=crlf +* text=auto eol=lf diff --git a/.gitignore b/.gitignore index ab38be0..b0af8d9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,38 +1,5 @@ -# Created by https://www.toptal.com/developers/gitignore/api/vim,macos -# Edit at https://www.toptal.com/developers/gitignore?templates=vim,macos - -### macOS ### -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### macOS Patch ### -# iCloud generated files -*.icloud +# Created by https://www.toptal.com/developers/gitignore/api/vim +# Edit at https://www.toptal.com/developers/gitignore?templates=vim ### Vim ### # Swap @@ -64,8 +31,6 @@ tags !.config/zed/ !.config/zed/settings.json !.config/zed/keymap.json -!.config/karabiner/ -!.config/karabiner/karabiner.json !.config/tmux/ !.config/tmux/nord-theme.conf !.config/tmux/dragon-theme.conf @@ -75,9 +40,7 @@ tags !.config/.vimrc !.config/.ideavimrc -# Ignore subfolders in karabiner and tmux -.config/karabiner/** -!.config/karabiner/karabiner.json +# Ignore subfolders in tmux .config/tmux/** !.config/tmux/nord-theme.conf !.config/tmux/dragon-theme.conf @@ -87,7 +50,5 @@ tags !.config/.vimrc !.config/.ideavimrc -scripts/nvim-offline-bundle-output - .nvimlog -# End of https://www.toptal.com/developers/gitignore/api/vim,macos +# End of https://www.toptal.com/developers/gitignore/api/vim diff --git a/README.md b/README.md index 44b9822..c238497 100644 --- a/README.md +++ b/README.md @@ -49,45 +49,3 @@ Set in shell: ```bash export KANAGAWA_THEME="wave" # or "dragon" ``` - -## Neovim Offline Setup (Corporate Proxy) - -If you're behind a corporate proxy where Mason, Treesitter, or plugin installs fail, you can pre-download everything on a machine with internet access and deploy offline. - -### Prerequisites - -On the machine with internet access, make sure you have: - -- Docker (Linux containers enabled) - -### Step 1: Prepare the bundle (with internet) - -```bash -./scripts/nvim-offline-prepare.sh -``` - -Optionally pin Neovim to a specific tag: - -```bash -NVIM_RELEASE_TAG=v0.11.6 ./scripts/nvim-offline-prepare.sh -``` - -Output: `scripts/nvim-offline-bundle-output/nvim-offline-bundle.tar.gz` - -### Step 2: Install the bundle (on the proxy machine) - -```bash -./scripts/nvim-offline-install.sh ./scripts/nvim-offline-bundle-output/nvim-offline-bundle.tar.gz -``` - -### Step 3: Enable offline mode - -```bash -export NVIM_OFFLINE=1 -``` - -### Re-enabling network access - -```bash -unset NVIM_OFFLINE -``` diff --git a/scripts/nvim-offline-install.sh b/scripts/nvim-offline-install.sh deleted file mode 100755 index df22b0f..0000000 --- a/scripts/nvim-offline-install.sh +++ /dev/null @@ -1,191 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# nvim-offline-install.sh -# ======================= -# Deploys pre-downloaded Neovim dependencies on an offline / corporate proxy machine. -# Compatible with the Docker-generated offline bundle. - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -DOTFILES_DIR="$(dirname "$SCRIPT_DIR")" -NVIM_CONFIG="$DOTFILES_DIR/.config/nvim" - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' - -info() { echo -e "${GREEN}==>${NC} $*"; } -warn() { echo -e "${YELLOW}WARNING:${NC} $*"; } -error() { echo -e "${RED}ERROR:${NC} $*" >&2; } - -# ── Archive hygiene (defensive) ────────────────────────────────────────────── -export COPYFILE_DISABLE=1 - -TARBALL="${1:-}" -if [ -z "$TARBALL" ]; then - error "Usage: $0 " - exit 1 -fi - -if [ ! -f "$TARBALL" ]; then - error "File not found: $TARBALL" - exit 1 -fi - -copy_tree() { - local src="$1" - local dst="$2" - rsync -a --delete "$src/" "$dst/" -} - -rewrite_mason_paths() { - local mason_root="$1" - local rewritten=0 - - if [ ! -d "$mason_root" ]; then - return 0 - fi - - while IFS= read -r -d '' file; do - # Only rewrite text files. - if ! LC_ALL=C grep -Iq . "$file"; then - continue - fi - if ! grep -q '/mason/packages/' "$file"; then - continue - fi - - local tmp - tmp="$(mktemp)" - local escaped_root="${mason_root//&/\\&}" - sed -E "s|/[^\"'[:space:]]*/mason/packages/|$escaped_root/packages/|g" "$file" >"$tmp" - - if ! cmp -s "$file" "$tmp"; then - cat "$tmp" >"$file" - chmod +x "$file" || true - rewritten=$((rewritten + 1)) - fi - rm -f "$tmp" - done < <(find "$mason_root" -type f -print0) - - info "Rewrote $rewritten Mason file(s) in $mason_root" -} - -if ! command -v rsync >/dev/null 2>&1; then - warn "rsync not found; using cp -a fallback." - copy_tree() { - local src="$1" - local dst="$2" - rm -rf "$dst" - mkdir -p "$dst" - cp -a "$src/." "$dst/" - } -fi - -XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}" -NVIM_DATA="$XDG_DATA_HOME/nvim" - -info "Installing offline Neovim bundle..." -echo " Source : $TARBALL" -echo " Target : $NVIM_DATA" -echo "" - -# ── Extract to temp directory ─────────────────────────────────────────────── -TMPDIR="$(mktemp -d)" -trap 'rm -rf "$TMPDIR"' EXIT - -info "Extracting bundle..." -# Note: Docker tarball creates a flat structure (share/ at root) -tar \ - --no-xattrs \ - --no-acls \ - --no-selinux \ - -xzf "$TARBALL" \ - -C "$TMPDIR" - -# [CRITICAL CHANGE] -# The Docker script places 'share' directly in the root of the tarball. -# We no longer look for a 'nvim-offline-bundle' subdirectory. -BUNDLE_DIR="$TMPDIR" - -if [ ! -d "$BUNDLE_DIR/share/nvim" ]; then - error "Invalid bundle structure: 'share/nvim' directory not found." - echo " Debug: Extracted contents of $TMPDIR:" - ls -F "$TMPDIR" - exit 1 -fi - -# ── Sanity check: NO AppleDouble files ────────────────────────────────────── -if find "$BUNDLE_DIR" -name '._*' | grep -q .; then - error "AppleDouble (._*) files detected in bundle. Refusing to install." - exit 1 -fi - -# ── Install lazy.nvim plugins ────────────────────────────────────────────── -# Path in bundle: share/nvim/lazy -SOURCE_LAZY="$BUNDLE_DIR/share/nvim/lazy" - -if [ -d "$SOURCE_LAZY" ]; then - PLUGIN_COUNT=$(ls -1 "$SOURCE_LAZY" | wc -l) - info "Installing $PLUGIN_COUNT plugins..." - - if [ -d "$NVIM_DATA/lazy" ]; then - warn "Existing lazy/ directory found — backing up to lazy.bak/" - rm -rf "$NVIM_DATA/lazy.bak" - mv "$NVIM_DATA/lazy" "$NVIM_DATA/lazy.bak" - fi - - mkdir -p "$NVIM_DATA" - copy_tree "$SOURCE_LAZY" "$NVIM_DATA/lazy" -fi - -# ── Install Mason packages ───────────────────────────────────────────────── -# Path in bundle: share/nvim/mason -SOURCE_MASON="$BUNDLE_DIR/share/nvim/mason" - -if [ -d "$SOURCE_MASON" ]; then - MASON_COUNT=$(ls -1 "$SOURCE_MASON/packages" 2>/dev/null | wc -l) - info "Installing $MASON_COUNT Mason packages..." - - if [ -d "$NVIM_DATA/mason" ]; then - warn "Existing mason/ directory found — backing up to mason.bak/" - rm -rf "$NVIM_DATA/mason.bak" - mv "$NVIM_DATA/mason" "$NVIM_DATA/mason.bak" - fi - - copy_tree "$SOURCE_MASON" "$NVIM_DATA/mason" - rewrite_mason_paths "$NVIM_DATA/mason" - - if rg -n '/tmp/runtime/share/nvim/mason/packages/' "$NVIM_DATA/mason" -S >/dev/null 2>&1; then - error "Stale Mason package paths detected after rewrite. Aborting." - exit 1 - fi -fi - -# ── Install lazy-lock.json ───────────────────────────────────────────────── -if [ -f "$BUNDLE_DIR/lazy-lock.json" ] && [ -d "$NVIM_CONFIG" ]; then - info "Installing lazy-lock.json..." - cp "$BUNDLE_DIR/lazy-lock.json" "$NVIM_CONFIG/lazy-lock.json" -fi - -# ── Final verification ───────────────────────────────────────────────────── -if find "$NVIM_DATA" -name '._*' | grep -q .; then - error "Post-install AppleDouble files detected. Installation aborted." - exit 1 -fi - -# ── Summary ──────────────────────────────────────────────────────────────── -echo "" -info "Installation complete!" -echo "" -echo " Plugins : $NVIM_DATA/lazy/" -echo " Mason : $NVIM_DATA/mason/" -echo "" -echo " ┌─────────────────────────────────────────────────────────┐" -echo " │ Add the following to your ~/.zshrc or ~/.bashrc: │" -echo " │ │" -echo " │ export NVIM_OFFLINE=1 │" -echo " │ │" -echo " │ Then restart your shell or run: source ~/.zshrc │" -echo " └─────────────────────────────────────────────────────────┘" diff --git a/scripts/nvim-offline-prepare.sh b/scripts/nvim-offline-prepare.sh deleted file mode 100755 index 2fa8fe9..0000000 --- a/scripts/nvim-offline-prepare.sh +++ /dev/null @@ -1,280 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# nvim-offline-prepare.sh -# ======================= -# Build an offline Neovim bundle in a temporary Linux x86_64 container. -# The container downloads and compiles all dependencies, then writes only the -# final tarball to the host output directory. - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -DOTFILES_DIR="$(dirname "$SCRIPT_DIR")" -NVIM_CONFIG="$DOTFILES_DIR/.config/nvim" -OUTPUT_DIR="$SCRIPT_DIR/nvim-offline-bundle-output" -TARBALL_NAME="nvim-offline-bundle.tar.gz" -NVIM_RELEASE_TAG="${NVIM_RELEASE_TAG:-}" -NVIM_PREPARE_TIMEOUT="${NVIM_PREPARE_TIMEOUT:-75m}" -BUILDER_IMAGE="${BUILDER_IMAGE:-ubuntu:24.04}" -ALLOW_EMPTY_MASON="${ALLOW_EMPTY_MASON:-0}" -PREPARE_SKIP_MASON_PACKAGES="${PREPARE_SKIP_MASON_PACKAGES:-}" - -# ── Colors & Helpers ──────────────────────────────────────────────────────── -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' - -info() { echo -e "${GREEN}==>${NC} $*"; } -warn() { echo -e "${YELLOW}WARNING:${NC} $*"; } -error() { echo -e "${RED}ERROR:${NC} $*" >&2; } -format_duration() { - local total_seconds="$1" - local hours=$((total_seconds / 3600)) - local minutes=$(((total_seconds % 3600) / 60)) - local seconds=$((total_seconds % 60)) - printf "%02dh:%02dm:%02ds" "$hours" "$minutes" "$seconds" -} - -# ── Checks ────────────────────────────────────────────────────────────────── -command -v docker >/dev/null 2>&1 || { - error "Docker is required but not installed." - exit 1 -} -if ! docker info >/dev/null 2>&1; then - error "Docker is not running." - exit 1 -fi - -if [ ! -d "$NVIM_CONFIG" ]; then - error "Neovim config directory not found: $NVIM_CONFIG" - exit 1 -fi - -# Create output directory -mkdir -p "$OUTPUT_DIR" -# Clear previous run artifacts -rm -f "$OUTPUT_DIR/$TARBALL_NAME" - -info "Starting Linux container (x86_64) to build offline bundle..." -info " Source Config : $NVIM_CONFIG" -info " Output Dir : $OUTPUT_DIR" -info " Docker Image : $BUILDER_IMAGE" -if [ -n "$NVIM_RELEASE_TAG" ]; then - info " Neovim Tag : $NVIM_RELEASE_TAG" -else - info " Neovim Tag : latest stable" -fi -info " Nvim Timeout : $NVIM_PREPARE_TIMEOUT" -if [ "$ALLOW_EMPTY_MASON" = "1" ]; then - warn "ALLOW_EMPTY_MASON=1 (will not fail if Mason installs 0 packages)" -fi -if [ -n "$PREPARE_SKIP_MASON_PACKAGES" ]; then - warn "PREPARE_SKIP_MASON_PACKAGES=$PREPARE_SKIP_MASON_PACKAGES" -fi - -# ── Run Builder Container ─────────────────────────────────────────────────── -# We use --platform linux/amd64 to simulate a standard corporate Linux server. -# This ensures binaries (blink.cmp) and parsers (treesitter) are compiled for x86_64. - -PREPARE_STARTED_AT="$(date +%s)" -if ! docker run --rm \ - --platform linux/amd64 \ - -v "$NVIM_CONFIG:/input/nvim:ro" \ - -v "$OUTPUT_DIR:/output" \ - -e HOST_UID="$(id -u)" \ - -e HOST_GID="$(id -g)" \ - -e TARBALL_NAME="$TARBALL_NAME" \ - -e NVIM_RELEASE_TAG="$NVIM_RELEASE_TAG" \ - -e NVIM_PREPARE_TIMEOUT="$NVIM_PREPARE_TIMEOUT" \ - -e ALLOW_EMPTY_MASON="$ALLOW_EMPTY_MASON" \ - -e PREPARE_SKIP_MASON_PACKAGES="$PREPARE_SKIP_MASON_PACKAGES" \ - "$BUILDER_IMAGE" /bin/bash -c ' - - set -euo pipefail - NVIM_PREPARE_TIMEOUT="${NVIM_PREPARE_TIMEOUT:-75m}" - ALLOW_EMPTY_MASON="${ALLOW_EMPTY_MASON:-0}" - - echo ">> [Container] Installing dependencies..." - export DEBIAN_FRONTEND=noninteractive - # Build/runtime tools for Lazy, Treesitter, Mason, and common package installers. - apt-get update -qq && apt-get install -y -qq \ - ca-certificates \ - git \ - curl \ - wget \ - unzip \ - tar \ - gzip \ - jq \ - build-essential \ - python3 \ - python3-venv \ - python3-pip \ - lua5.1 \ - luarocks \ - nodejs \ - npm \ - >/dev/null - update-ca-certificates >/dev/null 2>&1 || true - - echo ">> [Container] Preparing writable config copy..." - mkdir -p /tmp/config - cp -a /input/nvim /tmp/config/nvim - chmod -R u+rwX /tmp/config/nvim - - echo ">> [Container] Fetching Neovim release..." - cd /tmp - - if [ -n "${NVIM_RELEASE_TAG:-}" ]; then - TAG="$NVIM_RELEASE_TAG" - else - TAG="$(curl -fsSL https://api.github.com/repos/neovim/neovim/releases/latest | jq -r .tag_name)" - fi - - if [ -z "$TAG" ] || [ "$TAG" = "null" ]; then - echo "Error: Could not determine a Neovim release tag." - exit 1 - fi - - echo ">> [Container] Downloading $TAG for x86_64..." - ASSET="" - for CANDIDATE in nvim-linux-x86_64.tar.gz nvim-linux64.tar.gz; do - URL="https://github.com/neovim/neovim/releases/download/${TAG}/${CANDIDATE}" - if curl -fL --retry 3 --retry-delay 2 -o nvim-linux.tar.gz "$URL"; then - ASSET="$CANDIDATE" - break - fi - done - if [ -z "$ASSET" ]; then - echo "Error: Failed to download Neovim archive for tag $TAG." - exit 1 - fi - - # Verify download integrity ( > 1MB ) - FILESIZE=$(stat -c%s nvim-linux.tar.gz) - if [ "$FILESIZE" -lt 1000000 ]; then - echo "Error: Download failed (file too small)." - cat nvim-linux.tar.gz - exit 1 - fi - - tar -xf nvim-linux.tar.gz - NVIM_DIR="${ASSET%.tar.gz}" - if [ ! -x "/tmp/$NVIM_DIR/bin/nvim" ]; then - NVIM_DIR="$(find /tmp -maxdepth 1 -type d -name "nvim-linux*" | head -n1 | xargs -r basename)" - fi - if [ -z "$NVIM_DIR" ] || [ ! -x "/tmp/$NVIM_DIR/bin/nvim" ]; then - echo "Error: Neovim binary directory not found after extraction." - exit 1 - fi - export PATH="/tmp/$NVIM_DIR/bin:$PATH" - - # Define isolated runtime paths so host files are never added to the bundle. - export XDG_CONFIG_HOME="/tmp/config" - export RUNTIME_ROOT="/tmp/runtime" - export XDG_DATA_HOME="$RUNTIME_ROOT/share" - export XDG_STATE_HOME="$RUNTIME_ROOT/state" - export XDG_CACHE_HOME="$RUNTIME_ROOT/cache" - export NVIM_OFFLINE_PREPARE=1 - - mkdir -p "$XDG_DATA_HOME" "$XDG_STATE_HOME" "$XDG_CACHE_HOME" - - echo ">> [Container] 1/5 Bootstrapping lazy.nvim..." - # We bootstrap lazy.nvim manually first - git clone --filter=blob:none https://github.com/folke/lazy.nvim.git \ - "$XDG_DATA_HOME/nvim/lazy/lazy.nvim" || true - - echo ">> [Container] 2/5 Restoring Lazy plugins..." - if ! timeout "$NVIM_PREPARE_TIMEOUT" nvim --headless \ - -c "set nomore" \ - -c "Lazy! restore" \ - -c "qa!"; then - echo "Error: Lazy restore failed or timed out after $NVIM_PREPARE_TIMEOUT." - exit 1 - fi - - echo ">> [Container] 3/5 Building Treesitter parsers..." - if ! timeout "$NVIM_PREPARE_TIMEOUT" nvim --headless \ - -c "set nomore" \ - -c "TSUpdateSync" \ - -c "qa!"; then - echo "Error: TSUpdateSync failed or timed out after $NVIM_PREPARE_TIMEOUT." - exit 1 - fi - - echo ">> [Container] 4/5 Installing Mason tools..." - if ! timeout "$NVIM_PREPARE_TIMEOUT" nvim --headless \ - -c "set nomore" \ - -c "MasonToolsInstallSync" \ - -c "qa!"; then - echo "Error: MasonToolsInstallSync failed or timed out after $NVIM_PREPARE_TIMEOUT." - echo "Tip: retry with NVIM_PREPARE_TIMEOUT=120m if needed." - exit 1 - fi - - # Verification (safe with pipefail even when directories are missing) - PLUGIN_COUNT=0 - if [ -d "$XDG_DATA_HOME/nvim/lazy" ]; then - PLUGIN_COUNT=$(find "$XDG_DATA_HOME/nvim/lazy" -mindepth 1 -maxdepth 1 -type d | wc -l) - fi - - MASON_COUNT=0 - if [ -d "$XDG_DATA_HOME/nvim/mason/packages" ]; then - MASON_COUNT=$(find "$XDG_DATA_HOME/nvim/mason/packages" -mindepth 1 -maxdepth 1 -type d | wc -l) - fi - - echo ">> [Container] Installed $PLUGIN_COUNT plugins and $MASON_COUNT Mason packages." - if [ "$MASON_COUNT" -eq 0 ]; then - if [ "$ALLOW_EMPTY_MASON" = "1" ]; then - echo ">> [Container] WARNING: No Mason packages detected under $XDG_DATA_HOME/nvim/mason/packages." - else - echo "Error: No Mason packages were installed. Aborting to avoid an incomplete offline bundle." - echo "Tip: set ALLOW_EMPTY_MASON=1 only if you intentionally want a plugins-only bundle." - exit 1 - fi - fi - - # Cleanup junk - echo ">> [Container] 5/5 Packaging & Cleaning..." - BUNDLE_ROOT="/tmp/bundle" - mkdir -p "$BUNDLE_ROOT" - cp -a "$XDG_DATA_HOME" "$BUNDLE_ROOT/share" - find "$BUNDLE_ROOT" -name ".git" -type d -exec rm -rf {} + - find "$BUNDLE_ROOT" -name "._*" -type f -delete - - # Copy lockfile - if [ -f "$XDG_CONFIG_HOME/nvim/lazy-lock.json" ]; then - cp "$XDG_CONFIG_HOME/nvim/lazy-lock.json" "$BUNDLE_ROOT/" - fi - - # Create the Tarball - tar --exclude="._*" -czf "/output/$TARBALL_NAME" -C "$BUNDLE_ROOT" . - - # Fix ownership - chown "$HOST_UID:$HOST_GID" "/output/$TARBALL_NAME" - - echo ">> [Container] Done." -'; then - PREPARE_ENDED_AT="$(date +%s)" - PREPARE_ELAPSED="$((PREPARE_ENDED_AT - PREPARE_STARTED_AT))" - error "Offline prepare container failed after $(format_duration "$PREPARE_ELAPSED"). Review the container logs above." - exit 1 -fi -PREPARE_ENDED_AT="$(date +%s)" -PREPARE_ELAPSED="$((PREPARE_ENDED_AT - PREPARE_STARTED_AT))" - -# ── Summary ───────────────────────────────────────────────────────────────── -TARBALL="$OUTPUT_DIR/$TARBALL_NAME" - -if [ -f "$TARBALL" ]; then - SIZE=$(du -h "$TARBALL" | cut -f1) - echo "" - info "Success! Bundle created at:" - echo " $TARBALL ($SIZE)" - echo " Elapsed: $(format_duration "$PREPARE_ELAPSED")" - echo "" - echo " Target Platform: Linux (x86_64)" -else - error "Docker container finished but tarball was not found." - exit 1 -fi