-- SOPS integration for automatic encryption/decryption of secrets files -- This module sets up autocmds to handle .secrets.yaml files transparently local sops_group = vim.api.nvim_create_augroup("SopsEncryption", { clear = true }) -- Pattern matching for secrets files local secrets_patterns = { "*/secrets.yaml", "*secrets*.yaml", } -- Helper function to check if file matches secrets pattern local function is_secrets_file(filepath) for _, pattern in ipairs(secrets_patterns) do if vim.fn.match(filepath, vim.fn.glob2regpat(pattern)) ~= -1 then return true end end return false end -- Decrypt file after reading vim.api.nvim_create_autocmd("BufReadPost", { group = sops_group, pattern = secrets_patterns, callback = function(args) local filepath = vim.fn.expand("%:p") -- Only decrypt if file exists and has content if vim.fn.filereadable(filepath) == 1 and vim.fn.getfsize(filepath) > 0 then -- Save cursor position local cursor_pos = vim.api.nvim_win_get_cursor(0) -- Decrypt file content local result = vim.fn.system("sops --decrypt " .. vim.fn.shellescape(filepath)) if vim.v.shell_error == 0 then -- Replace buffer content with decrypted content vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(result, "\n")) -- Mark buffer as not modified (since we just loaded it) vim.bo.modified = false -- Restore cursor position pcall(vim.api.nvim_win_set_cursor, 0, cursor_pos) -- Disable swap, backup, and undo files for security vim.bo.swapfile = false vim.bo.backup = false vim.bo.writebackup = false vim.bo.undofile = false -- Set filetype to yaml if not already set if vim.bo.filetype == "" then vim.bo.filetype = "yaml" end vim.notify("SOPS: File decrypted successfully", vim.log.levels.INFO) else vim.notify("SOPS: Failed to decrypt file: " .. result, vim.log.levels.ERROR) end end end, }) -- Encrypt file before writing vim.api.nvim_create_autocmd("BufWritePre", { group = sops_group, pattern = secrets_patterns, callback = function(args) local filepath = vim.fn.expand("%:p") if is_secrets_file(filepath) then -- Get current buffer content local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) local content = table.concat(lines, "\n") -- Encrypt content using SOPS local encrypted = vim.fn.system("sops --encrypt /dev/stdin", content) if vim.v.shell_error == 0 then -- Write encrypted content directly to file local file = io.open(filepath, "w") if file then file:write(encrypted) file:close() -- Mark buffer as saved (prevent Vim from writing again) vim.bo.modified = false vim.notify("SOPS: File encrypted and saved successfully", vim.log.levels.INFO) else vim.notify("SOPS: Failed to write encrypted file", vim.log.levels.ERROR) end else vim.notify("SOPS: Failed to encrypt file: " .. encrypted, vim.log.levels.ERROR) -- Prevent write on encryption failure return true end -- Prevent default write behavior since we handled it return true end end, }) -- Re-decrypt after writing to show plaintext in buffer vim.api.nvim_create_autocmd("BufWritePost", { group = sops_group, pattern = secrets_patterns, callback = function(args) local filepath = vim.fn.expand("%:p") if is_secrets_file(filepath) and vim.fn.filereadable(filepath) == 1 then -- Decrypt and reload buffer content local result = vim.fn.system("sops --decrypt " .. vim.fn.shellescape(filepath)) if vim.v.shell_error == 0 then -- Save cursor position local cursor_pos = vim.api.nvim_win_get_cursor(0) -- Replace buffer with decrypted content vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(result, "\n")) -- Mark as not modified vim.bo.modified = false -- Restore cursor position pcall(vim.api.nvim_win_set_cursor, 0, cursor_pos) end end end, }) -- Warn when leaving a secrets buffer with unsaved changes vim.api.nvim_create_autocmd("BufLeave", { group = sops_group, pattern = secrets_patterns, callback = function(args) if vim.bo.modified then vim.notify("Warning: Unsaved changes in secrets file!", vim.log.levels.WARN) end end, })