Compare commits
14 Commits
bc65c8f90f
...
feat/etap-
| Author | SHA1 | Date | |
|---|---|---|---|
| 06dd244035 | |||
| b829a462bc | |||
| 7f1d1022aa | |||
| cf496cf340 | |||
| 2dac56a0c4 | |||
| 9be7f4d2ea | |||
| bac61ab51d | |||
| fea469c9e9 | |||
| 287188b1c5 | |||
| 887c39a676 | |||
| 5df142d47a | |||
| 758f996e6b | |||
| 415af7efcd | |||
| 5427f3ff45 |
145
ansible/files/operator-dotfiles/mc/ini
Normal file
145
ansible/files/operator-dotfiles/mc/ini
Normal file
@@ -0,0 +1,145 @@
|
||||
[Midnight-Commander]
|
||||
verbose=true
|
||||
shell_patterns=true
|
||||
auto_save_setup=true
|
||||
preallocate_space=false
|
||||
auto_menu=false
|
||||
use_internal_view=true
|
||||
use_internal_edit=false
|
||||
clear_before_exec=true
|
||||
confirm_delete=true
|
||||
confirm_overwrite=true
|
||||
confirm_execute=false
|
||||
confirm_history_cleanup=true
|
||||
confirm_exit=false
|
||||
confirm_directory_hotlist_delete=false
|
||||
confirm_view_dir=false
|
||||
safe_delete=false
|
||||
safe_overwrite=false
|
||||
use_8th_bit_as_meta=false
|
||||
mouse_move_pages_viewer=true
|
||||
mouse_close_dialog=false
|
||||
fast_refresh=false
|
||||
drop_menus=false
|
||||
wrap_mode=true
|
||||
old_esc_mode=true
|
||||
cd_symlinks=true
|
||||
show_all_if_ambiguous=false
|
||||
use_file_to_guess_type=true
|
||||
alternate_plus_minus=false
|
||||
only_leading_plus_minus=true
|
||||
show_output_starts_shell=false
|
||||
xtree_mode=false
|
||||
file_op_compute_totals=true
|
||||
classic_progressbar=true
|
||||
use_netrc=true
|
||||
ftpfs_always_use_proxy=false
|
||||
ftpfs_use_passive_connections=true
|
||||
ftpfs_use_passive_connections_over_proxy=false
|
||||
ftpfs_use_unix_list_options=true
|
||||
ftpfs_first_cd_then_ls=true
|
||||
ignore_ftp_chattr_errors=true
|
||||
editor_fill_tabs_with_spaces=false
|
||||
editor_return_does_auto_indent=false
|
||||
editor_backspace_through_tabs=false
|
||||
editor_fake_half_tabs=true
|
||||
editor_option_save_position=true
|
||||
editor_option_auto_para_formatting=false
|
||||
editor_option_typewriter_wrap=false
|
||||
editor_edit_confirm_save=true
|
||||
editor_syntax_highlighting=true
|
||||
editor_persistent_selections=true
|
||||
editor_drop_selection_on_copy=true
|
||||
editor_cursor_beyond_eol=false
|
||||
editor_cursor_after_inserted_block=false
|
||||
editor_visible_tabs=true
|
||||
editor_visible_spaces=true
|
||||
editor_line_state=false
|
||||
editor_simple_statusbar=false
|
||||
editor_check_new_line=false
|
||||
editor_show_right_margin=false
|
||||
editor_group_undo=true
|
||||
editor_state_full_filename=true
|
||||
editor_ask_filename_before_edit=false
|
||||
nice_rotating_dash=true
|
||||
shadows=true
|
||||
mcview_remember_file_position=false
|
||||
auto_fill_mkdir_name=true
|
||||
copymove_persistent_attr=true
|
||||
pause_after_run=1
|
||||
mouse_repeat_rate=100
|
||||
double_click_speed=250
|
||||
old_esc_mode_timeout=1000000
|
||||
max_dirt_limit=10
|
||||
num_history_items_recorded=60
|
||||
vfs_timeout=60
|
||||
ftpfs_directory_timeout=900
|
||||
ftpfs_retry_seconds=30
|
||||
shell_directory_timeout=900
|
||||
editor_tab_spacing=8
|
||||
editor_word_wrap_line_length=72
|
||||
editor_option_save_mode=0
|
||||
editor_backup_extension=~
|
||||
editor_filesize_threshold=64M
|
||||
editor_stop_format_chars=-+*\\,.;:&>
|
||||
mcview_eof=
|
||||
skin=default
|
||||
|
||||
[Layout]
|
||||
output_lines=0
|
||||
left_panel_size=73
|
||||
top_panel_size=0
|
||||
message_visible=true
|
||||
keybar_visible=true
|
||||
xterm_title=true
|
||||
command_prompt=true
|
||||
menubar_visible=true
|
||||
free_space=true
|
||||
horizontal_split=false
|
||||
vertical_equal=true
|
||||
horizontal_equal=true
|
||||
|
||||
[Misc]
|
||||
timeformat_recent=%-d %b, %H:%M
|
||||
timeformat_old=%-d %b %Y
|
||||
ftp_proxy_host=gate
|
||||
ftpfs_password=anonymous@
|
||||
display_codepage=UTF-8
|
||||
source_codepage=Other_8_bit
|
||||
autodetect_codeset=
|
||||
spell_language=en
|
||||
clipboard_store=
|
||||
clipboard_paste=
|
||||
|
||||
[Colors]
|
||||
base_color=
|
||||
xterm-256color=
|
||||
color_terminals=
|
||||
tmux-256color=
|
||||
|
||||
[Panels]
|
||||
show_mini_info=true
|
||||
kilobyte_si=false
|
||||
mix_all_files=false
|
||||
show_backups=true
|
||||
show_dot_files=true
|
||||
fast_reload=false
|
||||
fast_reload_msg_shown=false
|
||||
mark_moves_down=true
|
||||
reverse_files_only=true
|
||||
auto_save_setup_panels=false
|
||||
navigate_with_arrows=false
|
||||
panel_scroll_pages=true
|
||||
panel_scroll_center=false
|
||||
mouse_move_pages=true
|
||||
filetype_mode=true
|
||||
permission_mode=false
|
||||
torben_fj_mode=false
|
||||
quick_search_mode=2
|
||||
select_flags=6
|
||||
|
||||
[Panelize]
|
||||
Modyfikowane pliki git=git ls-files --modified
|
||||
Znajdź programy SUID i SGID=find . \\( \\( -perm -04000 -a -perm /011 \\) -o \\( -perm -02000 -a -perm /01 \\) \\) -print
|
||||
Znajdź odrzuty po łataniu=find . -name \\*.rej -print
|
||||
Znajdź pliki *.orig po łataniu=find . -name \\*.orig -print
|
||||
1
ansible/files/operator-dotfiles/mc/panels.ini
Normal file
1
ansible/files/operator-dotfiles/mc/panels.ini
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
44
ansible/files/operator-dotfiles/nvim/init.lua
Normal file
44
ansible/files/operator-dotfiles/nvim/init.lua
Normal file
@@ -0,0 +1,44 @@
|
||||
-- ======================================================================
|
||||
-- init.lua — Neovim configuration with modular structure
|
||||
-- ======================================================================
|
||||
|
||||
-- Set leader keys before lazy.nvim
|
||||
vim.g.mapleader = " "
|
||||
vim.g.maplocalleader = " "
|
||||
|
||||
-- Bootstrap lazy.nvim
|
||||
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)
|
||||
|
||||
-- Load configuration modules
|
||||
require("options")
|
||||
require("keymaps")
|
||||
|
||||
-- Load plugins
|
||||
require("lazy").setup("plugins", {
|
||||
change_detection = { notify = false },
|
||||
})
|
||||
|
||||
-- Load utilities and create commands
|
||||
local utils = require("utils")
|
||||
vim.api.nvim_create_user_command("ShowFileNameRight", utils.show_filename_right, {})
|
||||
|
||||
-- MCP Server Integration - Start socket if not already listening
|
||||
if vim.fn.serverstart then
|
||||
local socket_path = "/tmp/nvim"
|
||||
-- Only start server if not already listening
|
||||
if vim.v.servername == "" or vim.v.servername ~= socket_path then
|
||||
-- Try to start server, ignore if address already in use
|
||||
local ok, err = pcall(vim.fn.serverstart, socket_path)
|
||||
if not ok and not string.match(err or "", "address already in use") then
|
||||
vim.notify("Failed to start MCP server: " .. tostring(err), vim.log.levels.WARN)
|
||||
end
|
||||
end
|
||||
end
|
||||
13
ansible/files/operator-dotfiles/nvim/lazy-lock.json
Normal file
13
ansible/files/operator-dotfiles/nvim/lazy-lock.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"coc.nvim": { "branch": "release", "commit": "c89ed8d5c393c60e0baf796f097598a8da14816d" },
|
||||
"lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" },
|
||||
"nerdtree": { "branch": "master", "commit": "690d061b591525890f1471c6675bcb5bdc8cdff9" },
|
||||
"nvim-dap": { "branch": "master", "commit": "818cd8787a77a97703eb1d9090543a374f79a9ac" },
|
||||
"nvim-dap-ui": { "branch": "master", "commit": "cf91d5e2d07c72903d052f5207511bf7ecdb7122" },
|
||||
"nvim-dap-virtual-text": { "branch": "master", "commit": "fbdb48c2ed45f4a8293d0d483f7730d24467ccb6" },
|
||||
"nvim-nio": { "branch": "master", "commit": "21f5324bfac14e22ba26553caf69ec76ae8a7662" },
|
||||
"vim-commentary": { "branch": "master", "commit": "64a654ef4a20db1727938338310209b6a63f60c9" },
|
||||
"vim-fugitive": { "branch": "master", "commit": "61b51c09b7c9ce04e821f6cf76ea4f6f903e3cf4" },
|
||||
"vim-oscyank": { "branch": "main", "commit": "d67d76b2f19b868b70a1cf33a779d71dc092cb30" },
|
||||
"vim-slime": { "branch": "main", "commit": "3fb77a9d1d3dd3abfbdbd4840eb20947f39f688b" }
|
||||
}
|
||||
154
ansible/files/operator-dotfiles/nvim/lua/hazard3_dap.lua
Normal file
154
ansible/files/operator-dotfiles/nvim/lua/hazard3_dap.lua
Normal file
@@ -0,0 +1,154 @@
|
||||
-- hazard3_dap.lua — Hazard3 helpers for nvim-dap (GDB DAP)
|
||||
|
||||
local M = {}
|
||||
|
||||
local disasm_buf ---@type integer|nil
|
||||
|
||||
local function ensure_disasm_buf()
|
||||
if disasm_buf and vim.api.nvim_buf_is_valid(disasm_buf) then
|
||||
return disasm_buf
|
||||
end
|
||||
|
||||
disasm_buf = vim.api.nvim_create_buf(false, true)
|
||||
pcall(vim.api.nvim_buf_set_name, disasm_buf, "[DAP Disassembly]")
|
||||
|
||||
vim.bo[disasm_buf].buftype = "nofile"
|
||||
vim.bo[disasm_buf].bufhidden = "hide"
|
||||
vim.bo[disasm_buf].swapfile = false
|
||||
vim.bo[disasm_buf].filetype = "asm"
|
||||
|
||||
vim.bo[disasm_buf].modifiable = true
|
||||
vim.api.nvim_buf_set_lines(disasm_buf, 0, -1, false, { "(DAP) Disassembly" })
|
||||
vim.bo[disasm_buf].modifiable = false
|
||||
|
||||
return disasm_buf
|
||||
end
|
||||
|
||||
local function show_buf(buf)
|
||||
local wins = vim.fn.win_findbuf(buf)
|
||||
if wins and #wins > 0 then
|
||||
vim.api.nvim_set_current_win(wins[1])
|
||||
vim.wo.cursorline = true
|
||||
return
|
||||
end
|
||||
|
||||
vim.cmd("vsplit")
|
||||
vim.api.nvim_set_current_buf(buf)
|
||||
vim.wo.cursorline = true
|
||||
end
|
||||
|
||||
local function update_disassembly(buf, opts)
|
||||
opts = opts or {}
|
||||
local count = opts.count or 80
|
||||
local before = opts.before or 20
|
||||
|
||||
local ok, dap = pcall(require, "dap")
|
||||
if not ok then
|
||||
return
|
||||
end
|
||||
|
||||
local session = dap.session()
|
||||
if not session then
|
||||
return
|
||||
end
|
||||
|
||||
session:request("stackTrace", { threadId = 1, startFrame = 0, levels = 1 }, function(err, resp)
|
||||
if err then
|
||||
vim.notify("DAP disasm: stackTrace failed: " .. tostring(err), vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
local frames = resp and resp.stackFrames or {}
|
||||
if #frames == 0 then
|
||||
vim.notify("DAP disasm: no stack frames", vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
local frame = frames[1]
|
||||
local memref = frame.instructionPointerReference
|
||||
if memref == nil or memref == "" then
|
||||
vim.notify("DAP disasm: no instructionPointerReference", vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
session:request("disassemble", {
|
||||
memoryReference = memref,
|
||||
instructionOffset = -before,
|
||||
instructionCount = count,
|
||||
resolveSymbols = true,
|
||||
}, function(err2, resp2)
|
||||
if err2 then
|
||||
vim.notify("DAP disasm: disassemble failed: " .. tostring(err2), vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
local insts = resp2 and resp2.instructions or {}
|
||||
local lines = {}
|
||||
lines[#lines + 1] = "memref: " .. memref
|
||||
for _, i in ipairs(insts) do
|
||||
local addr = i.address or "?"
|
||||
local ins = i.instruction or ""
|
||||
local mark = (addr == memref) and "=> " or " "
|
||||
lines[#lines + 1] = mark .. addr .. " " .. ins
|
||||
end
|
||||
|
||||
if not vim.api.nvim_buf_is_valid(buf) then
|
||||
return
|
||||
end
|
||||
|
||||
vim.bo[buf].modifiable = true
|
||||
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
|
||||
vim.bo[buf].modifiable = false
|
||||
|
||||
local pc_line
|
||||
for idx, line in ipairs(lines) do
|
||||
if line:sub(1, 3) == "=> " then
|
||||
pc_line = idx
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if pc_line then
|
||||
local wins = vim.fn.win_findbuf(buf)
|
||||
if wins and #wins > 0 then
|
||||
for _, win in ipairs(wins) do
|
||||
pcall(vim.api.nvim_win_set_cursor, win, { pc_line, 0 })
|
||||
vim.wo[win].cursorline = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
function M.open_disassembly(opts)
|
||||
local buf = ensure_disasm_buf()
|
||||
local cur_win = vim.api.nvim_get_current_win()
|
||||
show_buf(buf)
|
||||
update_disassembly(buf, opts)
|
||||
|
||||
if opts and opts.keep_focus then
|
||||
pcall(vim.api.nvim_set_current_win, cur_win)
|
||||
end
|
||||
end
|
||||
|
||||
function M.setup()
|
||||
local ok, dap = pcall(require, "dap")
|
||||
if not ok then
|
||||
return
|
||||
end
|
||||
|
||||
if vim.api.nvim_create_user_command then
|
||||
pcall(vim.api.nvim_create_user_command, "DapDisassembly", function()
|
||||
M.open_disassembly()
|
||||
end, {})
|
||||
end
|
||||
|
||||
dap.listeners.after.event_stopped["hazard3_disasm"] = function()
|
||||
if disasm_buf and vim.api.nvim_buf_is_valid(disasm_buf) then
|
||||
update_disassembly(disasm_buf, { keep_focus = true })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
107
ansible/files/operator-dotfiles/nvim/lua/keymaps.lua
Normal file
107
ansible/files/operator-dotfiles/nvim/lua/keymaps.lua
Normal file
@@ -0,0 +1,107 @@
|
||||
-- ======================================================================
|
||||
-- keymaps.lua — Key mappings
|
||||
-- ======================================================================
|
||||
|
||||
local map = vim.keymap.set
|
||||
|
||||
-- NERDTree
|
||||
map("n", "<leader>n", ":NERDTreeFocus<CR>", { silent = true, desc = "NERDTree: Focus" })
|
||||
map("n", "<C-n>", ":NERDTree<CR>", { silent = true, desc = "NERDTree: Open" })
|
||||
map("n", "<C-t>", ":NERDTreeToggle<CR>", { silent = true, desc = "NERDTree: Toggle" })
|
||||
map("n", "<C-f>", ":NERDTreeFind<CR>", { silent = true, desc = "NERDTree: Find current file" })
|
||||
|
||||
-- OSC52 (vim-oscyank)
|
||||
map("n", "<leader>c", "<Plug>OSCYankOperator", { desc = "OSC Yank: Operator" })
|
||||
map("n", "<leader>cc", "<leader>c_", { desc = "OSC Yank: Current line" })
|
||||
map("v", "<leader>c", "<Plug>OSCYankVisual", { desc = "OSC Yank: Visual selection" })
|
||||
|
||||
-- vim-slime
|
||||
vim.g.slime_no_mappings = 1
|
||||
map("x", "<C-c><C-c>", "<Plug>SlimeRegionSend", { desc = "Slime: Send region" })
|
||||
map("n", "<C-c><C-c>", function()
|
||||
require("utils").send_cell("^#%%")
|
||||
end, { silent = true, desc = "Slime: Send cell" })
|
||||
map("n", "<C-c>v", "<Plug>SlimeConfig", { desc = "Slime: Configure" })
|
||||
|
||||
-- Claude Code
|
||||
map("n", "<leader>ac", ":ClaudeCode<CR>", { silent = true, desc = "ClaudeCode: Toggle" })
|
||||
map("n", "<leader>af", ":ClaudeCodeFocus<CR>", { silent = true, desc = "ClaudeCode: Focus" })
|
||||
map("v", "<leader>as", ":ClaudeCodeSend<CR>", { silent = true, desc = "ClaudeCode: Send selection" })
|
||||
map("n", "<leader>ad", ":ClaudeCodeAdd ", { desc = "ClaudeCode: Add file to context" })
|
||||
map("n", "<leader>aa", ":ClaudeCodeDiffAccept<CR>", { silent = true, desc = "ClaudeCode: Accept diff" })
|
||||
map("n", "<leader>ar", ":ClaudeCodeDiffDeny<CR>", { silent = true, desc = "ClaudeCode: Reject diff" })
|
||||
|
||||
|
||||
-- DAP (GDB)
|
||||
local function dap_attach_or(fn)
|
||||
return function(...)
|
||||
local dap = require("dap")
|
||||
if dap.session() then
|
||||
return fn(dap, ...)
|
||||
end
|
||||
if vim.fn.exists(":Hazard3Attach") == 2 then
|
||||
vim.cmd("Hazard3Attach")
|
||||
return
|
||||
end
|
||||
return fn(dap, ...)
|
||||
end
|
||||
end
|
||||
|
||||
map("n", "<F5>", dap_attach_or(function(dap)
|
||||
dap.continue()
|
||||
end), { desc = "DAP: Continue (or Hazard3Attach)" })
|
||||
|
||||
map("n", "<F10>", dap_attach_or(function(dap)
|
||||
dap.step_over()
|
||||
end), { desc = "DAP: Step over" })
|
||||
|
||||
map("n", "<F11>", dap_attach_or(function(dap)
|
||||
dap.step_into()
|
||||
end), { desc = "DAP: Step into" })
|
||||
|
||||
map("n", "<F12>", dap_attach_or(function(dap)
|
||||
dap.step_out()
|
||||
end), { desc = "DAP: Step out" })
|
||||
|
||||
map("n", "<leader>db", function()
|
||||
require("dap").toggle_breakpoint()
|
||||
end, { desc = "DAP: Toggle breakpoint" })
|
||||
|
||||
map("n", "<leader>dB", function()
|
||||
require("dap").set_breakpoint(vim.fn.input("Condition: "))
|
||||
end, { desc = "DAP: Conditional breakpoint" })
|
||||
|
||||
map("n", "<leader>dr", function()
|
||||
require("dap").repl.open()
|
||||
end, { desc = "DAP: REPL" })
|
||||
|
||||
map("n", "<leader>du", function()
|
||||
require("dapui").toggle()
|
||||
end, { desc = "DAP: Toggle UI" })
|
||||
|
||||
map("n", "<leader>dR", function()
|
||||
local dapui = require("dapui")
|
||||
dapui.close()
|
||||
dapui.open({ reset = true })
|
||||
end, { desc = "DAP UI: Reset layout" })
|
||||
|
||||
map("n", "<leader>da", function()
|
||||
require("hazard3_dap").open_disassembly()
|
||||
end, { desc = "DAP: Disassembly" })
|
||||
|
||||
|
||||
map("n", "<leader>di", dap_attach_or(function(dap)
|
||||
dap.step_into({ granularity = "instruction" })
|
||||
end), { desc = "DAP: Step instruction into" })
|
||||
|
||||
map("n", "<leader>dI", dap_attach_or(function(dap)
|
||||
dap.step_over({ granularity = "instruction" })
|
||||
end), { desc = "DAP: Step instruction over" })
|
||||
|
||||
map("n", "<leader>dx", function()
|
||||
require("dap").terminate()
|
||||
end, { desc = "DAP: Terminate" })
|
||||
|
||||
map("n", "<leader>dp", function()
|
||||
require("dap").pause()
|
||||
end, { desc = "DAP: Pause" })
|
||||
51
ansible/files/operator-dotfiles/nvim/lua/options.lua
Normal file
51
ansible/files/operator-dotfiles/nvim/lua/options.lua
Normal file
@@ -0,0 +1,51 @@
|
||||
-- ======================================================================
|
||||
-- options.lua — Vim options and settings
|
||||
-- ======================================================================
|
||||
|
||||
local opt = vim.opt
|
||||
|
||||
-- Clipboard
|
||||
opt.clipboard = "unnamedplus"
|
||||
|
||||
-- Editing behavior
|
||||
opt.backspace = { "indent", "eol", "start" }
|
||||
opt.mouse = "r"
|
||||
opt.compatible = false
|
||||
|
||||
-- UI
|
||||
opt.number = true
|
||||
opt.cursorline = true
|
||||
opt.showcmd = true
|
||||
opt.showmode = true
|
||||
opt.showmatch = true
|
||||
opt.scrolloff = 10
|
||||
opt.wrap = false
|
||||
|
||||
-- Indentation
|
||||
opt.shiftwidth = 4
|
||||
opt.tabstop = 4
|
||||
opt.expandtab = true
|
||||
|
||||
-- Search
|
||||
opt.incsearch = true
|
||||
opt.ignorecase = true
|
||||
opt.smartcase = true
|
||||
opt.hlsearch = true
|
||||
|
||||
-- Files
|
||||
opt.backup = false
|
||||
opt.history = 1000
|
||||
opt.tags = { "./tags;,tags;" }
|
||||
|
||||
-- Completion
|
||||
opt.wildmenu = true
|
||||
opt.wildmode = { "list", "longest" }
|
||||
opt.wildignore = {
|
||||
"*.docx","*.jpg","*.png","*.gif","*.pdf","*.pyc","*.exe","*.flv","*.img","*.xlsx"
|
||||
}
|
||||
|
||||
-- Filetype detection
|
||||
vim.cmd("filetype on")
|
||||
vim.cmd("filetype plugin on")
|
||||
vim.cmd("filetype indent on")
|
||||
vim.cmd("syntax on")
|
||||
191
ansible/files/operator-dotfiles/nvim/lua/plugins.lua
Normal file
191
ansible/files/operator-dotfiles/nvim/lua/plugins.lua
Normal file
@@ -0,0 +1,191 @@
|
||||
-- ======================================================================
|
||||
-- plugins.lua — Plugin specifications for lazy.nvim
|
||||
-- ======================================================================
|
||||
|
||||
return {
|
||||
-- File explorer
|
||||
{
|
||||
"preservim/nerdtree",
|
||||
cmd = { "NERDTree", "NERDTreeToggle", "NERDTreeFind", "NERDTreeFocus" },
|
||||
},
|
||||
|
||||
-- Git integration
|
||||
{
|
||||
"tpope/vim-fugitive",
|
||||
event = "VeryLazy",
|
||||
},
|
||||
|
||||
-- Commenting
|
||||
{
|
||||
"tpope/vim-commentary",
|
||||
event = "VeryLazy",
|
||||
},
|
||||
|
||||
-- LSP and completion
|
||||
{
|
||||
"neoclide/coc.nvim",
|
||||
branch = "release",
|
||||
build = "npm ci",
|
||||
event = "VeryLazy",
|
||||
},
|
||||
|
||||
-- REPL integration (tmux)
|
||||
{
|
||||
"jpalardy/vim-slime",
|
||||
init = function()
|
||||
vim.g.slime_target = "tmux"
|
||||
if vim.fn.exists("g:slime_python_ipython") == 0 then
|
||||
vim.g.slime_python_ipython = 1
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
-- OSC52 clipboard (remote SSH/tmux)
|
||||
{
|
||||
"ojroques/vim-oscyank",
|
||||
event = "VimEnter",
|
||||
init = function()
|
||||
vim.g.oscyank_term = "tmux"
|
||||
vim.g.oscyank_silent = true
|
||||
end,
|
||||
config = function()
|
||||
local grp = vim.api.nvim_create_augroup("osc52_yank", { clear = true })
|
||||
vim.api.nvim_create_autocmd("TextYankPost", {
|
||||
group = grp,
|
||||
callback = function()
|
||||
if vim.v.event.operator == "y" and vim.v.event.regname == "" then
|
||||
vim.cmd([[OSCYankRegister "]])
|
||||
end
|
||||
end,
|
||||
})
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
-- Debugging (DAP)
|
||||
{
|
||||
"mfussenegger/nvim-dap",
|
||||
config = function()
|
||||
local dap = require("dap")
|
||||
|
||||
dap.adapters.gdb = {
|
||||
type = "executable",
|
||||
command = "gdb-multiarch",
|
||||
args = { "-i", "dap" },
|
||||
}
|
||||
|
||||
local function pick_elf()
|
||||
return vim.fn.input("ELF: ", vim.fn.getcwd() .. "/", "file")
|
||||
end
|
||||
|
||||
local function normalize_target(t)
|
||||
t = t or ""
|
||||
if vim.trim then
|
||||
t = vim.trim(t)
|
||||
else
|
||||
t = t:gsub("^%s+", ""):gsub("%s+$", "")
|
||||
end
|
||||
t = t:gsub("^target%s+extended%-remote%s+", "")
|
||||
t = t:gsub("^target%s+remote%s+", "")
|
||||
t = t:gsub("^extended%-remote%s+", "")
|
||||
t = t:gsub("^remote%s+", "")
|
||||
return t
|
||||
end
|
||||
|
||||
local function pick_target()
|
||||
local default = normalize_target(vim.env.HAZARD3_GDB_TARGET)
|
||||
if default == "" then
|
||||
default = "localhost:3333"
|
||||
end
|
||||
return normalize_target(vim.fn.input("GDB target (host:port): ", default))
|
||||
end
|
||||
|
||||
local hazard3_attach = {
|
||||
name = "Hazard3 (attach gdb-remote)",
|
||||
type = "gdb",
|
||||
request = "attach",
|
||||
program = pick_elf,
|
||||
target = pick_target,
|
||||
cwd = "${workspaceFolder}",
|
||||
}
|
||||
|
||||
local function add_cfg(ft, cfg)
|
||||
dap.configurations[ft] = dap.configurations[ft] or {}
|
||||
for _, existing in ipairs(dap.configurations[ft]) do
|
||||
if existing.name == cfg.name then
|
||||
return
|
||||
end
|
||||
end
|
||||
table.insert(dap.configurations[ft], cfg)
|
||||
end
|
||||
|
||||
for _, ft in ipairs({ "c", "cpp", "asm", "" }) do
|
||||
add_cfg(ft, hazard3_attach)
|
||||
end
|
||||
|
||||
pcall(function()
|
||||
vim.api.nvim_create_user_command("Hazard3Attach", function()
|
||||
dap.run(hazard3_attach)
|
||||
end, { desc = "DAP: Hazard3 attach" })
|
||||
end)
|
||||
|
||||
vim.fn.sign_define("DapBreakpoint", { text = "●", texthl = "DiagnosticError" })
|
||||
vim.fn.sign_define("DapStopped", { text = "▶", texthl = "DiagnosticInfo", linehl = "Visual" })
|
||||
|
||||
pcall(function()
|
||||
require("hazard3_dap").setup()
|
||||
end)
|
||||
end,
|
||||
},
|
||||
|
||||
{
|
||||
"rcarriga/nvim-dap-ui",
|
||||
dependencies = {
|
||||
"mfussenegger/nvim-dap",
|
||||
"nvim-neotest/nvim-nio",
|
||||
},
|
||||
config = function()
|
||||
local dap = require("dap")
|
||||
local dapui = require("dapui")
|
||||
|
||||
dapui.setup({
|
||||
-- ASCII/unicode-friendly icons (no codicons required)
|
||||
icons = { expanded = "v", collapsed = ">", current_frame = ">" },
|
||||
controls = {
|
||||
enabled = true,
|
||||
element = "repl",
|
||||
icons = {
|
||||
pause = "||",
|
||||
play = ">",
|
||||
step_into = "↓",
|
||||
step_over = "→",
|
||||
step_out = "↑",
|
||||
step_back = "←",
|
||||
run_last = "↻",
|
||||
terminate = "X",
|
||||
disconnect = "D",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
dap.listeners.after.event_initialized["dapui"] = function()
|
||||
dapui.open()
|
||||
end
|
||||
dap.listeners.before.event_terminated["dapui"] = function()
|
||||
dapui.close()
|
||||
end
|
||||
dap.listeners.before.event_exited["dapui"] = function()
|
||||
dapui.close()
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
{
|
||||
"theHamsta/nvim-dap-virtual-text",
|
||||
dependencies = {
|
||||
"mfussenegger/nvim-dap",
|
||||
},
|
||||
opts = {},
|
||||
},
|
||||
|
||||
}
|
||||
25
ansible/files/operator-dotfiles/nvim/lua/utils.lua
Normal file
25
ansible/files/operator-dotfiles/nvim/lua/utils.lua
Normal file
@@ -0,0 +1,25 @@
|
||||
-- ======================================================================
|
||||
-- utils.lua — Helper functions
|
||||
-- ======================================================================
|
||||
|
||||
local M = {}
|
||||
|
||||
-- Show filename aligned to the right
|
||||
function M.show_filename_right()
|
||||
local filename = vim.fn.expand("%:p")
|
||||
local columns = vim.o.columns
|
||||
local space = columns - #filename - 2
|
||||
if space < 0 then space = 0 end
|
||||
vim.api.nvim_echo({{string.rep(" ", space) .. filename, "None"}}, false, {})
|
||||
end
|
||||
|
||||
-- Send cell to slime (delimited by pattern, e.g., "^#%%")
|
||||
function M.send_cell(pattern)
|
||||
local start_line = vim.fn.search(pattern, "bnW")
|
||||
if start_line ~= 0 then start_line = start_line + 1 else start_line = 1 end
|
||||
local stop_line = vim.fn.search(pattern, "nW")
|
||||
if stop_line ~= 0 then stop_line = stop_line - 1 else stop_line = vim.fn.line("$") end
|
||||
vim.fn["slime#send_range"](start_line, stop_line)
|
||||
end
|
||||
|
||||
return M
|
||||
4
ansible/files/operator-dotfiles/tmux.conf
Normal file
4
ansible/files/operator-dotfiles/tmux.conf
Normal file
@@ -0,0 +1,4 @@
|
||||
set-option -g prefix C-a
|
||||
unbind-key C-b
|
||||
bind-key C-a send-prefix
|
||||
set -g set-clipboard on
|
||||
41
ansible/group_vars/sol_rpc.yml
Normal file
41
ansible/group_vars/sol_rpc.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
solana_user: solana
|
||||
solana_group: solana
|
||||
solana_home: /var/lib/solana
|
||||
|
||||
solana_install_script_url: https://release.anza.xyz/stable/install
|
||||
solana_active_release_bin_dir: "{{ solana_home }}/.local/share/solana/install/active_release/bin"
|
||||
solana_validator_bin: /opt/solana/bin/agave-validator
|
||||
solana_keygen_bin: /opt/solana/bin/solana-keygen
|
||||
solana_rpc_service_name: solana-rpc
|
||||
|
||||
agave_repo_url: https://github.com/anza-xyz/agave.git
|
||||
agave_version_tag: v2.3.13
|
||||
agave_src_dir: "{{ solana_home }}/src/agave"
|
||||
agave_rust_toolchain: "1.86.0"
|
||||
|
||||
solana_identity_path: /var/lib/solana/identity.json
|
||||
solana_ledger_dir: /var/lib/solana/ledger
|
||||
solana_accounts_dir: /var/lib/solana/accounts
|
||||
solana_log_dir: /var/log/solana
|
||||
|
||||
# Note: agave-validator expects all sockets to be bound to the same IP.
|
||||
# For now we bind validator + RPC to 0.0.0.0 and rely on network hardening in a later etap.
|
||||
solana_bind_address: 0.0.0.0
|
||||
solana_rpc_port: 8899
|
||||
solana_rpc_pubsub_port: 8900
|
||||
solana_dynamic_port_range: "8000-8020"
|
||||
|
||||
solana_entrypoints:
|
||||
- entrypoint.mainnet-beta.solana.com:8001
|
||||
solana_known_validators: []
|
||||
|
||||
solana_geyser_enabled: false
|
||||
solana_geyser_plugin_config_path: /etc/solana/yellowstone-geyser.json
|
||||
|
||||
solana_rpc_extra_args:
|
||||
- --full-rpc-api
|
||||
- --limit-ledger-size
|
||||
|
||||
solana_rpc_manage_service: true
|
||||
solana_rpc_enable_on_boot: true
|
||||
solana_rpc_start_now: true
|
||||
@@ -1,2 +1,2 @@
|
||||
[sol_rpc]
|
||||
mevnode ansible_host=n8c1e71.mevnode.com ansible_user=root
|
||||
mevnode ansible_host=n8c1e71.mevnode.com ansible_user=root ansible_ssh_private_key_file=/home/runner/.ssh/mevnode_baremetal
|
||||
|
||||
@@ -2,9 +2,319 @@
|
||||
- name: Minimal check for doc/rpc Sol host
|
||||
hosts: sol_rpc
|
||||
gather_facts: true
|
||||
become: false
|
||||
become: true
|
||||
vars_files:
|
||||
- ../group_vars/sol_rpc.yml
|
||||
|
||||
tasks:
|
||||
- name: Install operator packages (Debian/Ubuntu)
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- tmux
|
||||
- mc
|
||||
- git
|
||||
- neovim
|
||||
state: present
|
||||
update_cache: true
|
||||
when: ansible_facts.os_family == "Debian"
|
||||
|
||||
- name: Install Solana host base packages (Debian/Ubuntu)
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- chrony
|
||||
- curl
|
||||
- jq
|
||||
- smartmontools
|
||||
- nvme-cli
|
||||
- prometheus-node-exporter
|
||||
state: present
|
||||
update_cache: true
|
||||
when: ansible_facts.os_family == "Debian"
|
||||
|
||||
- name: Ensure solana group exists
|
||||
ansible.builtin.group:
|
||||
name: "{{ solana_group }}"
|
||||
system: true
|
||||
|
||||
- name: Ensure solana user exists
|
||||
ansible.builtin.user:
|
||||
name: "{{ solana_user }}"
|
||||
group: "{{ solana_group }}"
|
||||
home: "{{ solana_home }}"
|
||||
system: true
|
||||
create_home: true
|
||||
|
||||
- name: Ensure root config directories exist
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
loop:
|
||||
- /root/.config
|
||||
- /root/.config/mc
|
||||
- /root/.config/nvim
|
||||
- /root/.config/nvim/lua
|
||||
|
||||
- name: Ensure Solana directories exist
|
||||
ansible.builtin.file:
|
||||
path: "{{ item.path }}"
|
||||
state: directory
|
||||
owner: "{{ item.owner }}"
|
||||
group: "{{ item.group }}"
|
||||
mode: "{{ item.mode }}"
|
||||
loop:
|
||||
- { path: "/etc/solana", owner: "root", group: "root", mode: "0755" }
|
||||
- { path: "{{ solana_home }}", owner: "{{ solana_user }}", group: "{{ solana_group }}", mode: "0750" }
|
||||
- { path: "{{ solana_ledger_dir }}", owner: "{{ solana_user }}", group: "{{ solana_group }}", mode: "0750" }
|
||||
- { path: "{{ solana_accounts_dir }}", owner: "{{ solana_user }}", group: "{{ solana_group }}", mode: "0750" }
|
||||
- { path: "{{ solana_log_dir }}", owner: "{{ solana_user }}", group: "{{ solana_group }}", mode: "0750" }
|
||||
- { path: "/opt/solana/bin", owner: "root", group: "root", mode: "0755" }
|
||||
|
||||
- name: Deploy Solana sysctl tuning (network buffers)
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/sysctl.d/99-solana-rpc.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
content: |
|
||||
# Required by agave-validator startup checks
|
||||
net.core.rmem_max = 134217728
|
||||
net.core.wmem_max = 134217728
|
||||
register: solana_sysctl_conf
|
||||
|
||||
- name: Apply sysctl tuning when changed
|
||||
ansible.builtin.command: sysctl --system
|
||||
changed_when: true
|
||||
when: solana_sysctl_conf.changed
|
||||
|
||||
- name: Deploy tmux config (Ctrl+a prefix)
|
||||
ansible.builtin.copy:
|
||||
src: ../files/operator-dotfiles/tmux.conf
|
||||
dest: /root/.tmux.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Deploy Midnight Commander config
|
||||
ansible.builtin.copy:
|
||||
src: "../files/operator-dotfiles/mc/{{ item.src }}"
|
||||
dest: "/root/.config/mc/{{ item.dest }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop:
|
||||
- { src: "ini", dest: "ini" }
|
||||
- { src: "panels.ini", dest: "panels.ini" }
|
||||
|
||||
- name: Deploy Neovim config files
|
||||
ansible.builtin.copy:
|
||||
src: "../files/operator-dotfiles/nvim/{{ item.src }}"
|
||||
dest: "/root/.config/nvim/{{ item.dest }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop:
|
||||
- { src: "init.lua", dest: "init.lua" }
|
||||
- { src: "lazy-lock.json", dest: "lazy-lock.json" }
|
||||
- { src: "lua/options.lua", dest: "lua/options.lua" }
|
||||
- { src: "lua/keymaps.lua", dest: "lua/keymaps.lua" }
|
||||
- { src: "lua/plugins.lua", dest: "lua/plugins.lua" }
|
||||
- { src: "lua/utils.lua", dest: "lua/utils.lua" }
|
||||
- { src: "lua/hazard3_dap.lua", dest: "lua/hazard3_dap.lua" }
|
||||
|
||||
- name: Deploy solana-rpc systemd unit (runs as solana user)
|
||||
ansible.builtin.template:
|
||||
src: ../templates/solana-rpc.service.j2
|
||||
dest: /etc/systemd/system/{{ solana_rpc_service_name }}.service
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
register: solana_rpc_unit
|
||||
|
||||
- name: Reload systemd after unit change
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
when: solana_rpc_unit.changed
|
||||
|
||||
- name: Check validator binary exists
|
||||
ansible.builtin.stat:
|
||||
path: "{{ solana_validator_bin }}"
|
||||
register: solana_validator_bin_stat
|
||||
|
||||
- name: Install Agave toolchain for solana user when validator missing
|
||||
ansible.builtin.shell: |
|
||||
set -euo pipefail
|
||||
sh -c "$(curl -sSfL {{ solana_install_script_url }})"
|
||||
become_user: "{{ solana_user }}"
|
||||
environment:
|
||||
HOME: "{{ solana_home }}"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
when: not solana_validator_bin_stat.stat.exists
|
||||
|
||||
- name: Link Agave binaries into /opt/solana/bin
|
||||
ansible.builtin.shell: |
|
||||
set -euo pipefail
|
||||
if [ ! -d "{{ solana_active_release_bin_dir }}" ]; then
|
||||
echo "Active release bin dir missing: {{ solana_active_release_bin_dir }}" >&2
|
||||
exit 1
|
||||
fi
|
||||
for bin in "{{ solana_active_release_bin_dir }}"/*; do
|
||||
name="$(basename "$bin")"
|
||||
ln -sfn "$bin" "/opt/solana/bin/$name"
|
||||
done
|
||||
args:
|
||||
executable: /bin/bash
|
||||
when: not solana_validator_bin_stat.stat.exists
|
||||
|
||||
- name: Re-check validator binary after install
|
||||
ansible.builtin.stat:
|
||||
path: "{{ solana_validator_bin }}"
|
||||
register: solana_validator_bin_stat
|
||||
|
||||
- name: Install Agave build dependencies (Debian/Ubuntu) when validator missing
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- build-essential
|
||||
- pkg-config
|
||||
- libssl-dev
|
||||
- clang
|
||||
- llvm-dev
|
||||
- libclang-dev
|
||||
- cmake
|
||||
- protobuf-compiler
|
||||
- libudev-dev
|
||||
- zlib1g-dev
|
||||
state: present
|
||||
update_cache: true
|
||||
when:
|
||||
- ansible_facts.os_family == "Debian"
|
||||
- not solana_validator_bin_stat.stat.exists
|
||||
|
||||
- name: Ensure Agave source parent directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ agave_src_dir | dirname }}"
|
||||
state: directory
|
||||
owner: "{{ solana_user }}"
|
||||
group: "{{ solana_group }}"
|
||||
mode: "0755"
|
||||
when: not solana_validator_bin_stat.stat.exists
|
||||
|
||||
- name: Install rustup (Agave build) for solana user when validator missing
|
||||
ansible.builtin.shell: |
|
||||
set -euo pipefail
|
||||
curl -sSfL https://sh.rustup.rs | sh -s -- -y --default-toolchain "{{ agave_rust_toolchain }}"
|
||||
become_user: "{{ solana_user }}"
|
||||
environment:
|
||||
HOME: "{{ solana_home }}"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
creates: "{{ solana_home }}/.cargo/bin/cargo"
|
||||
when: not solana_validator_bin_stat.stat.exists
|
||||
|
||||
- name: Ensure Rust toolchain is installed for solana user when validator missing
|
||||
ansible.builtin.shell: |
|
||||
set -euo pipefail
|
||||
export PATH="{{ solana_home }}/.cargo/bin:$PATH"
|
||||
rustup toolchain install "{{ agave_rust_toolchain }}"
|
||||
become_user: "{{ solana_user }}"
|
||||
environment:
|
||||
HOME: "{{ solana_home }}"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
when: not solana_validator_bin_stat.stat.exists
|
||||
|
||||
- name: Clone Agave sources when validator missing
|
||||
ansible.builtin.git:
|
||||
repo: "{{ agave_repo_url }}"
|
||||
dest: "{{ agave_src_dir }}"
|
||||
version: "{{ agave_version_tag }}"
|
||||
depth: 1
|
||||
update: true
|
||||
become_user: "{{ solana_user }}"
|
||||
environment:
|
||||
HOME: "{{ solana_home }}"
|
||||
when: not solana_validator_bin_stat.stat.exists
|
||||
|
||||
- name: Build agave-validator from sources when validator missing
|
||||
ansible.builtin.shell: |
|
||||
set -euo pipefail
|
||||
export PATH="{{ solana_home }}/.cargo/bin:$PATH"
|
||||
cd "{{ agave_src_dir }}"
|
||||
cargo +{{ agave_rust_toolchain }} build --release -p agave-validator
|
||||
become_user: "{{ solana_user }}"
|
||||
environment:
|
||||
HOME: "{{ solana_home }}"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
creates: "{{ agave_src_dir }}/target/release/agave-validator"
|
||||
when: not solana_validator_bin_stat.stat.exists
|
||||
|
||||
- name: Install agave-validator to /opt/solana/bin when built
|
||||
ansible.builtin.copy:
|
||||
remote_src: true
|
||||
src: "{{ agave_src_dir }}/target/release/agave-validator"
|
||||
dest: "{{ solana_validator_bin }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
when: not solana_validator_bin_stat.stat.exists
|
||||
|
||||
- name: Re-check validator binary after source build
|
||||
ansible.builtin.stat:
|
||||
path: "{{ solana_validator_bin }}"
|
||||
register: solana_validator_bin_stat
|
||||
|
||||
- name: Ensure identity key exists
|
||||
ansible.builtin.shell: |
|
||||
set -euo pipefail
|
||||
"{{ solana_keygen_bin }}" new --no-passphrase -o "{{ solana_identity_path }}"
|
||||
become_user: "{{ solana_user }}"
|
||||
environment:
|
||||
HOME: "{{ solana_home }}"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
creates: "{{ solana_identity_path }}"
|
||||
|
||||
- name: Check identity key exists
|
||||
ansible.builtin.stat:
|
||||
path: "{{ solana_identity_path }}"
|
||||
register: solana_identity_stat
|
||||
|
||||
- name: Ensure solana-rpc service state when prerequisites exist
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ solana_rpc_service_name }}"
|
||||
enabled: "{{ solana_rpc_enable_on_boot | bool }}"
|
||||
state: "{{ 'started' if (solana_rpc_start_now | bool) else 'stopped' }}"
|
||||
when:
|
||||
- solana_rpc_manage_service | bool
|
||||
- solana_validator_bin_stat.stat.exists
|
||||
- solana_identity_stat.stat.exists
|
||||
|
||||
- name: Verify solana-rpc is active
|
||||
ansible.builtin.command: systemctl is-active {{ solana_rpc_service_name }}
|
||||
register: solana_rpc_is_active
|
||||
changed_when: false
|
||||
retries: 10
|
||||
delay: 3
|
||||
until: solana_rpc_is_active.stdout.strip() == "active"
|
||||
when:
|
||||
- solana_rpc_manage_service | bool
|
||||
- solana_validator_bin_stat.stat.exists
|
||||
- solana_identity_stat.stat.exists
|
||||
|
||||
- name: Report skipped solana-rpc start due to missing prerequisites
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "solana-rpc start skipped: missing prerequisites"
|
||||
- "validator_bin={{ solana_validator_bin }} exists={{ solana_validator_bin_stat.stat.exists }}"
|
||||
- "identity={{ solana_identity_path }} exists={{ solana_identity_stat.stat.exists }}"
|
||||
when:
|
||||
- solana_rpc_manage_service | bool
|
||||
- not (solana_validator_bin_stat.stat.exists and solana_identity_stat.stat.exists)
|
||||
|
||||
- name: Validate Ansible transport
|
||||
ansible.builtin.ping:
|
||||
|
||||
|
||||
35
ansible/templates/solana-rpc.service.j2
Normal file
35
ansible/templates/solana-rpc.service.j2
Normal file
@@ -0,0 +1,35 @@
|
||||
[Unit]
|
||||
Description=Solana RPC node (Agave)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User={{ solana_user }}
|
||||
Group={{ solana_group }}
|
||||
WorkingDirectory={{ solana_home }}
|
||||
Environment=RUST_LOG=info
|
||||
LimitNOFILE=1048576
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
TimeoutStopSec=120
|
||||
ExecStart={{ solana_validator_bin }} \
|
||||
--identity {{ solana_identity_path }} \
|
||||
--ledger {{ solana_ledger_dir }} \
|
||||
--accounts {{ solana_accounts_dir }} \
|
||||
--bind-address {{ solana_bind_address }} \
|
||||
--rpc-bind-address {{ solana_bind_address }} \
|
||||
--rpc-port {{ solana_rpc_port }} \
|
||||
--dynamic-port-range {{ solana_dynamic_port_range }}{% for ep in solana_entrypoints %} \
|
||||
--entrypoint {{ ep }}{% endfor %}{% for kv in solana_known_validators %} \
|
||||
--known-validator {{ kv }}{% endfor %}{% if solana_geyser_enabled | bool %} \
|
||||
--geyser-plugin-config {{ solana_geyser_plugin_config_path }}{% endif %}{% for arg in solana_rpc_extra_args %} \
|
||||
{{ arg }}{% endfor %} \
|
||||
--log {{ solana_log_dir }}/validator.log
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
ReadWritePaths={{ solana_ledger_dir }} {{ solana_accounts_dir }} {{ solana_log_dir }} /var/lib/solana
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -4,7 +4,7 @@ Cel etapu: uruchomić pierwszy, bezpieczny playbook Ansible dla hosta RPC (`mevn
|
||||
|
||||
## Zakres
|
||||
|
||||
1. Sprawdzić i zainstalować Ansible na VPS (control node), jeśli brak.
|
||||
1. Uruchamiać Ansible na VPS jako runner w Dockerze (bez instalacji Ansible na hoście), jeśli to jest preferowany model operacyjny.
|
||||
2. Dodać minimalną strukturę Ansible w `trade-iac`:
|
||||
- inventory dla `mevnode`,
|
||||
- minimalny playbook testowy (bez zmian destrukcyjnych).
|
||||
@@ -18,6 +18,23 @@ Cel etapu: uruchomić pierwszy, bezpieczny playbook Ansible dla hosta RPC (`mevn
|
||||
|
||||
## Kryteria akceptacji
|
||||
|
||||
- `ansible-playbook --version` działa na VPS.
|
||||
- `ansible-playbook --version` działa w kontenerze na VPS.
|
||||
- Playbook kończy się statusem success dla grupy `sol_rpc`.
|
||||
- Wynik zawiera podstawowe fakty hosta i potwierdzenie łączności Ansible.
|
||||
|
||||
## Jak uruchomić na VPS (Docker)
|
||||
|
||||
Przykład (image: `quay.io/ansible/ansible-runner:latest`):
|
||||
|
||||
```bash
|
||||
cd /opt/trade-iac
|
||||
docker run --rm -t \
|
||||
-v "$PWD/ansible:/ansible" \
|
||||
-v "$HOME/.ssh:/home/runner/.ssh:ro" \
|
||||
-w /ansible \
|
||||
quay.io/ansible/ansible-runner:latest \
|
||||
ansible-playbook -i inventory/hosts.ini playbooks/doc-rpc-sol-min.yml
|
||||
```
|
||||
|
||||
Ważne: inventory w `ansible/inventory/hosts.ini` używa jawnie pliku klucza
|
||||
`/home/runner/.ssh/mevnode_baremetal` (czyli wymaga montowania `~/.ssh` do `/home/runner/.ssh` w kontenerze).
|
||||
|
||||
20
doc/etap-002-ssh-gitea.md
Normal file
20
doc/etap-002-ssh-gitea.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Etap 002: SSH flow dla `gitea.mpabi.pl`
|
||||
|
||||
Cel etapu: przejść z push przez HTTPS/token na standardowy push przez SSH do `trade/trade-iac`.
|
||||
|
||||
## Zakres
|
||||
|
||||
1. Wygenerować dedykowany klucz SSH dla Gitei (jeśli brak).
|
||||
2. Dodać konfigurację hosta `gitea.mpabi.pl` w `~/.ssh/config`.
|
||||
3. Dodać klucz publiczny do konta `u1` w Gitei.
|
||||
4. Ustawić `origin` repo `trade-iac` na URL SSH.
|
||||
5. Zweryfikować:
|
||||
- `ssh -T git@gitea.mpabi.pl`,
|
||||
- `git ls-remote origin`,
|
||||
- `git push` bez hasła/tokena HTTP.
|
||||
|
||||
## Kryteria akceptacji
|
||||
|
||||
- SSH auth do Gitei działa dla użytkownika `git`.
|
||||
- `trade-iac` używa `git@gitea.mpabi.pl:trade/trade-iac.git`.
|
||||
- Push działa przez SSH.
|
||||
26
doc/etap-003-gitea-ssh-expose-k3s.md
Normal file
26
doc/etap-003-gitea-ssh-expose-k3s.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Etap 003: Wystawienie SSH Gitei z k3s
|
||||
|
||||
Cel etapu: udostępnić SSH Gitei na publicznym porcie, żeby `git@gitea.mpabi.pl` trafiało do serwisu `gitea-ssh` w klastrze.
|
||||
|
||||
## Zakres
|
||||
|
||||
1. Dodać `HelmChartConfig` dla Traefika z entrypointem TCP `gitssh` na porcie `2222`.
|
||||
2. Dodać `IngressRouteTCP` w namespace `gitea`, który kieruje ruch z `gitssh` do `service/gitea-ssh:22`.
|
||||
3. Wdrożyć manifesty do klastra i zweryfikować:
|
||||
- Traefik ma port `2222`,
|
||||
- `IngressRouteTCP` jest aktywny,
|
||||
- `ssh -p 2222 git@gitea.mpabi.pl` odpowiada Giteą.
|
||||
|
||||
## Decyzja techniczna
|
||||
|
||||
- Nie ruszamy hostowego `:22` (zostaje dla SSH systemu).
|
||||
- Git SSH dla Gitei idzie przez `:2222`.
|
||||
- Klienci Git powinni używać:
|
||||
- `ssh://git@gitea.mpabi.pl:2222/trade/trade-iac.git`
|
||||
- albo aliasu SSH z `Port 2222`.
|
||||
|
||||
## Kryteria akceptacji
|
||||
|
||||
- `kubectl -n kube-system get svc traefik` pokazuje port `2222/TCP`.
|
||||
- `kubectl -n gitea get ingressroutetcp gitea-ssh` istnieje.
|
||||
- Push/pull repo przez SSH na porcie `2222` działa.
|
||||
30
doc/etap-004-operator-tools-dotfiles.md
Normal file
30
doc/etap-004-operator-tools-dotfiles.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Etap 004: Narzędzia operatora + dotfiles z laptopa
|
||||
|
||||
Cel etapu: doinstalować na `mevnode` podstawowe narzędzia operatorskie i wgrać konfiguracje terminalowe zgodne z tym laptopem.
|
||||
|
||||
## Zakres
|
||||
|
||||
1. Doinstalować pakiety:
|
||||
- `tmux`
|
||||
- `mc`
|
||||
- `git`
|
||||
- `neovim`
|
||||
2. Ustawić `tmux` z prefiksem `Ctrl+a`.
|
||||
3. Wgrać konfiguracje operatora (`root`):
|
||||
- `~/.tmux.conf`
|
||||
- `~/.config/mc/ini`
|
||||
- `~/.config/nvim/init.lua`
|
||||
- `~/.config/nvim/lua/*.lua`
|
||||
- `~/.config/nvim/lazy-lock.json`
|
||||
|
||||
## Założenia
|
||||
|
||||
- Playbook działa na host `mevnode` jako `root`.
|
||||
- Dotfiles są wersjonowane w repo `trade-iac` (źródło prawdy).
|
||||
- `git` config globalny nie jest kopiowany, jeśli brak odpowiednika na laptopie.
|
||||
|
||||
## Kryteria akceptacji
|
||||
|
||||
- Polecenia `tmux`, `mc`, `git`, `nvim` są dostępne na `mevnode`.
|
||||
- `~/.tmux.conf` zawiera prefix `C-a`.
|
||||
- Pliki `~/.config/mc/ini` oraz `~/.config/nvim/*` istnieją na `mevnode`.
|
||||
22
doc/etap-005-solana-rpc-user-svc.md
Normal file
22
doc/etap-005-solana-rpc-user-svc.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Etap 005: Solana RPC jako usługa użytkownika `solana`
|
||||
|
||||
Cel etapu: przygotować i wdrożyć baseline pod `solana-rpc`, uruchamiany jako dedykowany użytkownik systemowy `solana` (nie `root`).
|
||||
|
||||
## Zakres
|
||||
|
||||
1. Rozszerzyć playbook o:
|
||||
- pakiety bazowe dla hosta RPC,
|
||||
- utworzenie użytkownika i katalogów `solana`,
|
||||
- deployment unitu `systemd` `solana-rpc.service` z `User=solana`.
|
||||
2. Dodać zmienne grupowe `sol_rpc` (ścieżki, porty, opcje startu).
|
||||
3. Dodać bezpieczną logikę startu:
|
||||
- unit jest wdrażany zawsze,
|
||||
- start usługi tylko gdy istnieją wymagane artefakty (`agave-validator`, identity keypair).
|
||||
4. Wdrożyć na `mevnode` i zweryfikować stan.
|
||||
|
||||
## Kryteria akceptacji
|
||||
|
||||
- `id solana` istnieje na `mevnode`.
|
||||
- `/etc/systemd/system/solana-rpc.service` istnieje i zawiera `User=solana`.
|
||||
- Playbook kończy się bez błędów.
|
||||
- Jeśli binarka/identity nie istnieją, playbook raportuje to jawnie i nie wymusza startu.
|
||||
31
doc/etap-006-agave-install-identity-start.md
Normal file
31
doc/etap-006-agave-install-identity-start.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Etap 006: Instalacja Agave + identity + start `solana-rpc`
|
||||
|
||||
Cel etapu: domknąć bootstrap uruchomienia `solana-rpc` jako `solana` przez:
|
||||
|
||||
1. instalację binarki `agave-validator`,
|
||||
2. wygenerowanie `identity.json` (jeśli brak),
|
||||
3. start usługi `solana-rpc` i test endpointu RPC.
|
||||
|
||||
## Zakres
|
||||
|
||||
- Rozszerzyć playbook o zadania instalacyjne Agave (idempotentnie).
|
||||
- Dodać provisioning `identity` jako użytkownik `solana`.
|
||||
- Dodać minimalny tuning OS wymagany przez startup check (`sysctl`).
|
||||
- Utrzymać bezpieczny start: usługa uruchamiana tylko przy komplecie prereq.
|
||||
- Wykonać testy powdrożeniowe (`systemd`, port, JSON-RPC).
|
||||
|
||||
## Założenia
|
||||
|
||||
- W tej wersji `agave-validator` wszystkie sockety (gossip/TPU/RPC) muszą być zbindowane do tego samego IP.
|
||||
- Na czas bootstrapu bind jest na `0.0.0.0` (żeby validator przeszedł check reachability i wystartował).
|
||||
- Produkcyjny bind na WG IP i hardening sieciowy będzie osobnym etapem.
|
||||
- Release tar z `agave-install` nie zawiera `agave-validator`, więc `agave-validator` budujemy ze źródeł (tag `v2.x`) i instalujemy do `/opt/solana/bin`.
|
||||
- Build wymaga pakietów dev, w tym `libclang`/`llvm` (Ansible instaluje je w playbooku).
|
||||
|
||||
## Kryteria akceptacji
|
||||
|
||||
- `agave-validator` istnieje pod `/opt/solana/bin/agave-validator`.
|
||||
- `identity` istnieje pod `/var/lib/solana/identity.json` (owner `solana`).
|
||||
- `systemctl is-active solana-rpc` zwraca `active`.
|
||||
- Endpoint `http://127.0.0.1:8899` odpowiada na JSON-RPC.
|
||||
- Websocket (pubsub) jest na porcie `solana_rpc_port + 1` (czyli domyślnie `8900` przy `8899`).
|
||||
67
doc/workflow.md
Normal file
67
doc/workflow.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Workflow `trade-iac`
|
||||
|
||||
Cel: utrzymywać konfigurację IaC w Git i wdrażać ją kontrolowanie na `mevnode`.
|
||||
|
||||
## 1. Dodanie klucza SSH na hoście (operator)
|
||||
|
||||
Host operatora musi mieć klucz do dostępu do repo `trade/trade-iac`.
|
||||
|
||||
Przykład:
|
||||
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -C "trade-iac-host" -f ~/.ssh/trade_iac
|
||||
cat ~/.ssh/trade_iac.pub
|
||||
```
|
||||
|
||||
Publiczny klucz dodajemy w Gitei (`Settings -> SSH Keys`), a potem test:
|
||||
|
||||
```bash
|
||||
ssh -T git@gitea.mpabi.pl
|
||||
```
|
||||
|
||||
## 2. Dodanie klucza SSH na VPS
|
||||
|
||||
VPS (runner/deployer) musi mieć osobny klucz do klonowania/pull z `trade/trade-iac`.
|
||||
|
||||
Przykład:
|
||||
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -C "trade-iac-vps" -f ~/.ssh/trade_iac
|
||||
cat ~/.ssh/trade_iac.pub
|
||||
ssh -T git@gitea.mpabi.pl
|
||||
```
|
||||
|
||||
Ten publiczny klucz też dodajemy do Gitei.
|
||||
|
||||
## 3. Wdrożenie na `mevnode` (runner na VPS)
|
||||
|
||||
Po zmianach w repo:
|
||||
|
||||
1. `doc/` (opis etapu),
|
||||
2. implementacja (playbook/vars),
|
||||
3. test/syntax-check,
|
||||
4. commit + push,
|
||||
5. wdrożenie z VPS na `mevnode`,
|
||||
6. testy powdrożeniowe.
|
||||
|
||||
Minimalny przepływ:
|
||||
|
||||
```bash
|
||||
# VPS
|
||||
git -C /opt/trade-iac pull --ff-only origin main
|
||||
cd /opt/trade-iac
|
||||
|
||||
# uruchom Ansible w kontenerze (bez instalacji Ansible na hoście)
|
||||
docker run --rm -t \
|
||||
-v "$PWD/ansible:/ansible" \
|
||||
-v "$HOME/.ssh:/home/runner/.ssh:ro" \
|
||||
-w /ansible \
|
||||
quay.io/ansible/ansible-runner:latest \
|
||||
ansible-playbook -i inventory/hosts.ini playbooks/doc-rpc-sol-min.yml
|
||||
```
|
||||
|
||||
## Zasada pracy
|
||||
|
||||
- Zmiany zawsze przez PR/commit (bez ręcznych zmian na `mevnode` poza awarią).
|
||||
- `mevnode` traktujemy jako target deploymentu, nie źródło prawdy.
|
||||
- Źródłem prawdy jest repo `trade/trade-iac`.
|
||||
13
k3s/gitea-ssh/ingressroutetcp-gitea-ssh.yaml
Normal file
13
k3s/gitea-ssh/ingressroutetcp-gitea-ssh.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRouteTCP
|
||||
metadata:
|
||||
name: gitea-ssh
|
||||
namespace: gitea
|
||||
spec:
|
||||
entryPoints:
|
||||
- gitssh
|
||||
routes:
|
||||
- match: HostSNI(`*`)
|
||||
services:
|
||||
- name: gitea-ssh
|
||||
port: 22
|
||||
14
k3s/gitea-ssh/traefik-helmchartconfig-gitssh.yaml
Normal file
14
k3s/gitea-ssh/traefik-helmchartconfig-gitssh.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: helm.cattle.io/v1
|
||||
kind: HelmChartConfig
|
||||
metadata:
|
||||
name: traefik
|
||||
namespace: kube-system
|
||||
spec:
|
||||
valuesContent: |-
|
||||
ports:
|
||||
gitssh:
|
||||
port: 2222
|
||||
expose:
|
||||
default: true
|
||||
exposedPort: 2222
|
||||
protocol: TCP
|
||||
Reference in New Issue
Block a user