Add BufReadPre autocmd to set filetype=yaml before buffer is loaded, ensuring syntax highlighting works immediately when opening encrypted sops files. Also updated BufReadPost to unconditionally set yaml filetype after decryption. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
153 lines
4.7 KiB
Lua
153 lines
4.7 KiB
Lua
-- 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
|
|
|
|
-- Set filetype before reading to enable syntax highlighting
|
|
vim.api.nvim_create_autocmd("BufReadPre", {
|
|
group = sops_group,
|
|
pattern = secrets_patterns,
|
|
callback = function(args)
|
|
-- Set filetype to yaml before the file is read so syntax highlighting works
|
|
vim.bo.filetype = "yaml"
|
|
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
|
|
|
|
-- Ensure filetype is set to yaml for syntax highlighting
|
|
vim.bo.filetype = "yaml"
|
|
|
|
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,
|
|
})
|