[INIT]
This commit is contained in:
50
README.md
Normal file
50
README.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# merged-repl-plugin
|
||||||
|
|
||||||
|
Neovim plugin that synchronizes a REPL repository with a main repository and provides a Perl REPL workflow inside Neovim.
|
||||||
|
|
||||||
|
Features
|
||||||
|
- Automatic synchronization of REPL repo branches with main repo (polling-based)
|
||||||
|
- `:REPL` command to open the REPL session file
|
||||||
|
- `:RUN` command to execute the REPL script and show output in a readonly buffer
|
||||||
|
- Auto-commit of REPL repo changes by default
|
||||||
|
|
||||||
|
Defaults
|
||||||
|
- `auto_commit = true` (auto-commit changes in REPL repo if dirty)
|
||||||
|
- `output_target = 'buffer'` (REPL output appears in an in-editor readonly buffer)
|
||||||
|
- `auto_start = false` (sync timer not started automatically)
|
||||||
|
|
||||||
|
Installation
|
||||||
|
1. Place the `lua/` directory inside your Neovim config or as part of a plugin path.
|
||||||
|
2. In your `init.lua` add:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require('merged-repl-plugin').setup{
|
||||||
|
main_repo_path = '/path/to/main/repo',
|
||||||
|
repl_repo_path = '/path/to/repl/repo',
|
||||||
|
repl_file = '/path/to/repl/repo/repl.pl',
|
||||||
|
auto_commit = true, -- default
|
||||||
|
output_target = 'buffer', -- default
|
||||||
|
auto_start = false, -- default
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage
|
||||||
|
- `:REPL` — open the configured `repl.pl` file for editing
|
||||||
|
- `:RUN` — save and run `repl.pl`; output streams to a buffer called `REPL Output: <repo-name>`
|
||||||
|
- `require('merged-repl-plugin').start_auto_sync()` — start automatic polling for branch changes
|
||||||
|
- `require('merged-repl-plugin').stop_auto_sync()` — stop automatic polling
|
||||||
|
|
||||||
|
Testing
|
||||||
|
- A basic integration test script is provided at `tests/run_tests.sh`.
|
||||||
|
- Run it from the project root: `./tests/run_tests.sh`
|
||||||
|
|
||||||
|
Notes & Caveats
|
||||||
|
- The plugin performs Git operations (add/commit/checkout). `auto_commit` will create commits automatically using the repo's configured Git author.
|
||||||
|
- The plugin runs Git and Perl via asynchronous jobs to avoid blocking the editor, but operations may still fail due to hooks or merge conflicts — the plugin will notify and leave repositories unchanged in those cases.
|
||||||
|
- `auto_start` is `false` by default to avoid surprising behavior. Call `start_auto_sync()` when you're ready to enable automatic syncing.
|
||||||
|
|
||||||
|
Contributing
|
||||||
|
- Feel free to open PRs for improvements: add `auto_stash` behavior, enable file-based output for headless environments, or enhance logging.
|
||||||
|
|
||||||
|
License
|
||||||
|
- MIT
|
||||||
3
lua/merged-repl-plugin.lua
Normal file
3
lua/merged-repl-plugin.lua
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-- shim to new repl_plugin module
|
||||||
|
return require('repl_plugin')
|
||||||
|
|
||||||
137
lua/repl_plugin/git.lua
Normal file
137
lua/repl_plugin/git.lua
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
local fn = vim.fn
|
||||||
|
local api = vim.api
|
||||||
|
|
||||||
|
local function job_async(argv, opts)
|
||||||
|
opts = opts or {}
|
||||||
|
local on_stdout = opts.on_stdout
|
||||||
|
local on_stderr = opts.on_stderr
|
||||||
|
local on_exit = opts.on_exit
|
||||||
|
|
||||||
|
local id = fn.jobstart(argv, {
|
||||||
|
stdout_buffered = true,
|
||||||
|
stderr_buffered = true,
|
||||||
|
on_stdout = vim.schedule_wrap(function(_, data, _)
|
||||||
|
if on_stdout then on_stdout(data) end
|
||||||
|
end),
|
||||||
|
on_stderr = vim.schedule_wrap(function(_, data, _)
|
||||||
|
if on_stderr then on_stderr(data) end
|
||||||
|
end),
|
||||||
|
on_exit = vim.schedule_wrap(function(_, code, _)
|
||||||
|
if on_exit then on_exit(code) end
|
||||||
|
end),
|
||||||
|
})
|
||||||
|
return id
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get current branch name of repo_path; cb(err, branch)
|
||||||
|
function M.get_branch(repo_path, cb)
|
||||||
|
cb = cb or function() end
|
||||||
|
if not repo_path or repo_path == '' then
|
||||||
|
return cb('no_repo')
|
||||||
|
end
|
||||||
|
job_async({'git','-C',repo_path,'rev-parse','--abbrev-ref','HEAD'}, {
|
||||||
|
on_stdout = function(data)
|
||||||
|
local out = table.concat(data, '\n'):gsub('\n+$','')
|
||||||
|
cb(nil, out)
|
||||||
|
end,
|
||||||
|
on_stderr = function(data)
|
||||||
|
-- ignore stderr unless needed
|
||||||
|
end,
|
||||||
|
on_exit = function(code)
|
||||||
|
if code ~= 0 then cb('git_error') end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- status porcelain; cb(err, lines_table)
|
||||||
|
function M.status_porcelain(repo_path, cb)
|
||||||
|
cb = cb or function() end
|
||||||
|
job_async({'git','-C',repo_path,'status','--porcelain'}, {
|
||||||
|
on_stdout = function(data)
|
||||||
|
-- data is table of lines
|
||||||
|
cb(nil, data)
|
||||||
|
end,
|
||||||
|
on_exit = function(code)
|
||||||
|
if code ~= 0 then cb('git_error') end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- commit_if_dirty: if dirty and auto_commit true, run add+commit
|
||||||
|
-- opts = { auto_commit = true, message = 'msg' }
|
||||||
|
-- cb(err, result)
|
||||||
|
function M.commit_if_dirty(repo_path, opts, cb)
|
||||||
|
cb = cb or function() end
|
||||||
|
opts = opts or {}
|
||||||
|
local auto_commit = opts.auto_commit
|
||||||
|
local message = opts.message or ("Auto REPL state: " .. os.date("%Y-%m-%d %H:%M:%S"))
|
||||||
|
|
||||||
|
M.status_porcelain(repo_path, function(err, lines)
|
||||||
|
if err then return cb('status_error') end
|
||||||
|
-- lines may contain empty string elements
|
||||||
|
local dirty = false
|
||||||
|
for _, l in ipairs(lines) do
|
||||||
|
if l and l:match('%S') then dirty = true; break end
|
||||||
|
end
|
||||||
|
if not dirty then
|
||||||
|
return cb(nil, {committed = false, reason = 'no_changes'})
|
||||||
|
end
|
||||||
|
if not auto_commit then
|
||||||
|
return cb(nil, {committed = false, reason = 'auto_commit_disabled'})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- run git add -A && git commit -m message
|
||||||
|
-- We'll run as a shell command via sh -c to combine commands easily
|
||||||
|
local cmd = {'sh','-c', "git -C " .. fn.shellescape(repo_path) .. " add -A && git -C " .. fn.shellescape(repo_path) .. " commit -m " .. fn.shellescape(message)}
|
||||||
|
local stdout_acc = {}
|
||||||
|
local stderr_acc = {}
|
||||||
|
job_async(cmd, {
|
||||||
|
on_stdout = function(data) for _, ln in ipairs(data) do table.insert(stdout_acc, ln) end end,
|
||||||
|
on_stderr = function(data) for _, ln in ipairs(data) do table.insert(stderr_acc, ln) end end,
|
||||||
|
on_exit = function(code)
|
||||||
|
if code == 0 then
|
||||||
|
cb(nil, {committed = true, success = true, stdout = stdout_acc, stderr = stderr_acc})
|
||||||
|
else
|
||||||
|
cb('commit_failed', {committed = true, success = false, stdout = stdout_acc, stderr = stderr_acc, code = code})
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.branch_exists(repo_path, branch, cb)
|
||||||
|
cb = cb or function() end
|
||||||
|
job_async({'git','-C',repo_path,'rev-parse','--verify','--quiet','refs/heads/' .. branch}, {
|
||||||
|
on_exit = function(code)
|
||||||
|
if code == 0 then cb(nil, true) else cb(nil, false) end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.checkout_branch(repo_path, branch, cb)
|
||||||
|
cb = cb or function() end
|
||||||
|
job_async({'git','-C',repo_path,'checkout',branch}, {
|
||||||
|
on_stdout = function(_) end,
|
||||||
|
on_stderr = function(_) end,
|
||||||
|
on_exit = function(code)
|
||||||
|
if code == 0 then cb(nil, true) else cb('checkout_failed', false) end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- create branch from 'from' (string), prefer using checkout then checkout -b
|
||||||
|
function M.create_branch(repo_path, branch, from, cb)
|
||||||
|
cb = cb or function() end
|
||||||
|
from = from or 'HEAD'
|
||||||
|
-- do two commands in shell: git -C repo checkout <from> && git -C repo checkout -b <branch>
|
||||||
|
local cmd = {'sh','-c', "git -C " .. fn.shellescape(repo_path) .. " checkout " .. fn.shellescape(from) .. " && git -C " .. fn.shellescape(repo_path) .. " checkout -b " .. fn.shellescape(branch)}
|
||||||
|
job_async(cmd, {
|
||||||
|
on_exit = function(code)
|
||||||
|
if code == 0 then cb(nil, true) else cb('create_failed', false) end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
87
lua/repl_plugin/init.lua
Normal file
87
lua/repl_plugin/init.lua
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
local git = require('repl_plugin.git')
|
||||||
|
local runner = require('repl_plugin.runner')
|
||||||
|
local sync = require('repl_plugin.sync')
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local default_config = {
|
||||||
|
main_repo_path = vim.fn.getcwd(),
|
||||||
|
repl_repo_path = nil,
|
||||||
|
repl_file = nil,
|
||||||
|
poll_interval = 5000,
|
||||||
|
auto_commit = true,
|
||||||
|
output_target = 'buffer', -- buffer or file
|
||||||
|
auto_start = false,
|
||||||
|
log_file = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
local config = {}
|
||||||
|
|
||||||
|
local function merge_opts(opts)
|
||||||
|
opts = opts or {}
|
||||||
|
for k, v in pairs(default_config) do
|
||||||
|
if opts[k] == nil then opts[k] = v end
|
||||||
|
end
|
||||||
|
return opts
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.setup(opts)
|
||||||
|
config = merge_opts(opts)
|
||||||
|
if not config.repl_repo_path or config.repl_repo_path == '' then
|
||||||
|
config.repl_repo_path = config.main_repo_path
|
||||||
|
end
|
||||||
|
if not config.repl_file or config.repl_file == '' then
|
||||||
|
config.repl_file = config.repl_repo_path .. '/repl.pl'
|
||||||
|
end
|
||||||
|
|
||||||
|
-- pass config to sync module
|
||||||
|
sync.setup({
|
||||||
|
main_repo_path = config.main_repo_path,
|
||||||
|
repl_repo_path = config.repl_repo_path,
|
||||||
|
auto_commit = config.auto_commit,
|
||||||
|
poll_interval = config.poll_interval,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- register commands
|
||||||
|
vim.api.nvim_create_user_command('REPL', function() M.open_repl_file() end, {})
|
||||||
|
vim.api.nvim_create_user_command('RUN', function() M.run_repl() end, {})
|
||||||
|
|
||||||
|
if config.auto_start then M.start_auto_sync() end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.open_repl_file()
|
||||||
|
local file = config.repl_file
|
||||||
|
if not file or file == '' then
|
||||||
|
vim.notify('No repl_file configured', vim.log.levels.ERROR)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
vim.cmd('edit ' .. vim.fn.fnameescape(file))
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.run_repl()
|
||||||
|
local file = config.repl_file
|
||||||
|
if not file or file == '' then
|
||||||
|
vim.notify('No repl_file configured', vim.log.levels.ERROR)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
runner.run_perl(file, {
|
||||||
|
repo_base = vim.fn.fnamemodify(config.repl_repo_path, ':t'),
|
||||||
|
open_split = true,
|
||||||
|
output_target = config.output_target,
|
||||||
|
repl_repo_path = config.repl_repo_path,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.start_auto_sync()
|
||||||
|
sync.start(config.poll_interval)
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.stop_auto_sync()
|
||||||
|
sync.stop()
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.sync_once(cb)
|
||||||
|
sync.sync_once(cb)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
137
lua/repl_plugin/runner.lua
Normal file
137
lua/repl_plugin/runner.lua
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
local M = {}
|
||||||
|
local fn = vim.fn
|
||||||
|
local api = vim.api
|
||||||
|
|
||||||
|
local function get_buf_name(repo_base)
|
||||||
|
return "REPL Output: " .. (repo_base or "repl")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function find_or_create_buffer(name)
|
||||||
|
-- search for existing buffer with this name in the buffer list
|
||||||
|
for _, bufnr in ipairs(api.nvim_list_bufs()) do
|
||||||
|
if api.nvim_buf_get_name(bufnr) == name then
|
||||||
|
return bufnr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- create new buffer
|
||||||
|
local bufnr = api.nvim_create_buf(false, true) -- listed=false, scratch
|
||||||
|
api.nvim_buf_set_name(bufnr, name)
|
||||||
|
api.nvim_buf_set_option(bufnr, 'buftype', 'nofile')
|
||||||
|
api.nvim_buf_set_option(bufnr, 'bufhidden', 'hide')
|
||||||
|
api.nvim_buf_set_option(bufnr, 'swapfile', false)
|
||||||
|
return bufnr
|
||||||
|
end
|
||||||
|
|
||||||
|
local function append_lines_to_buf(bufnr, lines)
|
||||||
|
if not bufnr or not api.nvim_buf_is_valid(bufnr) then return end
|
||||||
|
api.nvim_buf_set_option(bufnr, 'modifiable', true)
|
||||||
|
local current_lines = api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||||
|
local start = #current_lines
|
||||||
|
api.nvim_buf_set_lines(bufnr, start, start, false, lines)
|
||||||
|
api.nvim_buf_set_option(bufnr, 'modifiable', false)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- run perl file asynchronously and stream to output buffer
|
||||||
|
-- opts: { repo_base = 'name', open_split = true }
|
||||||
|
function M.run_perl(repl_file, opts)
|
||||||
|
opts = opts or {}
|
||||||
|
local target = opts.output_target or 'buffer'
|
||||||
|
local repo_base = opts.repo_base or fn.fnamemodify(repl_file, ':h:t')
|
||||||
|
|
||||||
|
if target == 'file' then
|
||||||
|
-- write output to file under repl_repo_path if provided, else next to repl_file
|
||||||
|
local out_path = opts.out_file or (opts.repl_repo_path and (opts.repl_repo_path .. '/repl_output.log')) or (fn.tempname() .. '_repl_output.log')
|
||||||
|
-- clear/initialize file
|
||||||
|
local f = io.open(out_path, 'w')
|
||||||
|
if f then f:write('-- REPL Run: ' .. os.date('%Y-%m-%d %H:%M:%S') .. '\n') f:close() end
|
||||||
|
|
||||||
|
local function append_lines_to_file(path, lines)
|
||||||
|
local fh, err = io.open(path, 'a')
|
||||||
|
if not fh then return end
|
||||||
|
for _, l in ipairs(lines) do fh:write(l .. '\n') end
|
||||||
|
fh:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function on_stdout(data)
|
||||||
|
if data then append_lines_to_file(out_path, data) end
|
||||||
|
end
|
||||||
|
local function on_stderr(data)
|
||||||
|
if data then append_lines_to_file(out_path, data) end
|
||||||
|
end
|
||||||
|
local function on_exit(code)
|
||||||
|
append_lines_to_file(out_path, {'-- REPL exited with code: ' .. tostring(code)})
|
||||||
|
end
|
||||||
|
|
||||||
|
local abs_repl = fn.fnamemodify(repl_file, ':p')
|
||||||
|
local cmd = {'perl', abs_repl}
|
||||||
|
fn.jobstart(cmd, {
|
||||||
|
stdout_buffered = true,
|
||||||
|
stderr_buffered = true,
|
||||||
|
on_stdout = vim.schedule_wrap(function(_, data, _)
|
||||||
|
on_stdout(data)
|
||||||
|
end),
|
||||||
|
on_stderr = vim.schedule_wrap(function(_, data, _)
|
||||||
|
on_stderr(data)
|
||||||
|
end),
|
||||||
|
on_exit = vim.schedule_wrap(function(_, code, _)
|
||||||
|
on_exit(code)
|
||||||
|
end),
|
||||||
|
})
|
||||||
|
|
||||||
|
return out_path
|
||||||
|
end
|
||||||
|
|
||||||
|
-- default buffer target
|
||||||
|
local bufname = get_buf_name(repo_base)
|
||||||
|
local bufnr = find_or_create_buffer(bufname)
|
||||||
|
|
||||||
|
-- clear buffer
|
||||||
|
api.nvim_buf_set_option(bufnr, 'modifiable', true)
|
||||||
|
api.nvim_buf_set_lines(bufnr, 0, -1, false, {"-- REPL Run: " .. os.date('%Y-%m-%d %H:%M:%S')})
|
||||||
|
api.nvim_buf_set_option(bufnr, 'modifiable', false)
|
||||||
|
|
||||||
|
-- open split to show buffer
|
||||||
|
if opts.open_split ~= false then
|
||||||
|
api.nvim_command('belowright split ' .. fn.shellescape(bufname))
|
||||||
|
local win = api.nvim_get_current_win()
|
||||||
|
api.nvim_win_set_buf(win, bufnr)
|
||||||
|
end
|
||||||
|
|
||||||
|
local stdout_acc = {}
|
||||||
|
local stderr_acc = {}
|
||||||
|
|
||||||
|
local function on_stdout(data)
|
||||||
|
if data then
|
||||||
|
append_lines_to_buf(bufnr, data)
|
||||||
|
for _, l in ipairs(data) do table.insert(stdout_acc, l) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function on_stderr(data)
|
||||||
|
if data then
|
||||||
|
append_lines_to_buf(bufnr, data)
|
||||||
|
for _, l in ipairs(data) do table.insert(stderr_acc, l) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function on_exit(code)
|
||||||
|
append_lines_to_buf(bufnr, {"-- REPL exited with code: " .. tostring(code)})
|
||||||
|
api.nvim_buf_set_option(bufnr, 'modifiable', false)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- spawn via jobstart
|
||||||
|
local cmd = {'perl', repl_file}
|
||||||
|
fn.jobstart(cmd, {
|
||||||
|
stdout_buffered = true,
|
||||||
|
stderr_buffered = true,
|
||||||
|
on_stdout = vim.schedule_wrap(function(_, data, _)
|
||||||
|
on_stdout(data)
|
||||||
|
end),
|
||||||
|
on_stderr = vim.schedule_wrap(function(_, data, _)
|
||||||
|
on_stderr(data)
|
||||||
|
end),
|
||||||
|
on_exit = vim.schedule_wrap(function(_, code, _)
|
||||||
|
on_exit(code)
|
||||||
|
end),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
84
lua/repl_plugin/sync.lua
Normal file
84
lua/repl_plugin/sync.lua
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
local git = require('repl_plugin.git')
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local api = vim.api
|
||||||
|
local fn = vim.fn
|
||||||
|
|
||||||
|
local config = {}
|
||||||
|
local last_branch = nil
|
||||||
|
local timer_id = nil
|
||||||
|
|
||||||
|
function M.setup(cfg)
|
||||||
|
config = cfg or {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.sync_once(cb)
|
||||||
|
cb = cb or function() end
|
||||||
|
git.get_branch(config.main_repo_path, function(err, branch)
|
||||||
|
if err or not branch or branch == '' then
|
||||||
|
vim.notify('Could not determine main repo branch', vim.log.levels.WARN)
|
||||||
|
return cb('no_branch')
|
||||||
|
end
|
||||||
|
if branch == last_branch then
|
||||||
|
return cb(nil, {changed=false})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- attempt to commit repl repo if dirty
|
||||||
|
git.commit_if_dirty(config.repl_repo_path, {auto_commit = config.auto_commit}, function(cerr, cres)
|
||||||
|
if cerr then
|
||||||
|
vim.notify('Failed to commit REPL repo: ' .. tostring(cerr), vim.log.levels.ERROR)
|
||||||
|
return cb('commit_failed')
|
||||||
|
end
|
||||||
|
-- proceed to ensure branch exists and checkout
|
||||||
|
git.branch_exists(config.repl_repo_path, branch, function(_, exists)
|
||||||
|
if exists then
|
||||||
|
git.checkout_branch(config.repl_repo_path, branch, function(co_err, ok)
|
||||||
|
if co_err then
|
||||||
|
vim.notify('Failed to checkout repl branch: ' .. tostring(co_err), vim.log.levels.ERROR)
|
||||||
|
return cb('checkout_failed')
|
||||||
|
end
|
||||||
|
last_branch = branch
|
||||||
|
vim.notify('Repl switched to branch: ' .. branch, vim.log.levels.INFO)
|
||||||
|
return cb(nil, {changed=true, branch=branch})
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
-- prefer template
|
||||||
|
git.branch_exists(config.repl_repo_path, 'template', function(_, tmpl)
|
||||||
|
local from = tmpl and 'template' or 'HEAD'
|
||||||
|
git.create_branch(config.repl_repo_path, branch, from, function(cr_err, ok)
|
||||||
|
if cr_err then
|
||||||
|
vim.notify('Failed to create repl branch: ' .. tostring(cr_err), vim.log.levels.ERROR)
|
||||||
|
return cb('create_failed')
|
||||||
|
end
|
||||||
|
last_branch = branch
|
||||||
|
vim.notify('Repl branch created: ' .. branch, vim.log.levels.INFO)
|
||||||
|
return cb(nil, {changed=true, branch=branch})
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.start(interval)
|
||||||
|
if timer_id then
|
||||||
|
vim.notify('REPL sync already running', vim.log.levels.INFO)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
interval = interval or config.poll_interval or 5000
|
||||||
|
timer_id = fn.timer_start(interval, function()
|
||||||
|
vim.schedule(function() M.sync_once() end)
|
||||||
|
end, {['repeat'] = -1})
|
||||||
|
vim.notify('Started REPL sync (interval ' .. tostring(interval) .. ' ms)', vim.log.levels.INFO)
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.stop()
|
||||||
|
if timer_id then
|
||||||
|
pcall(fn.timer_stop, timer_id)
|
||||||
|
timer_id = nil
|
||||||
|
vim.notify('Stopped REPL sync', vim.log.levels.INFO)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
24
package-lock.json
generated
Normal file
24
package-lock.json
generated
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "reperl.nvim",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"dependencies": {
|
||||||
|
"opencode-box": "^1.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/opencode-box": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/opencode-box/-/opencode-box-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-jYVne0PpzOEYRLqa8LOBsFjL4sNVAIdcZ8sKtIKjQ/QPyR6M0tU+0WJZpAs3qglk5m6iji8H3doQa+T09ws7xA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"opencodebox": "bin/agentbox.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
package.json
Normal file
5
package.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"opencode-box": "^1.4.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
test_main_repo
Submodule
1
test_main_repo
Submodule
Submodule test_main_repo added at 76f0a53b6a
1
test_repl_repo
Submodule
1
test_repl_repo
Submodule
Submodule test_repl_repo added at 76f0a53b6a
35
tests/_headless_single.lua
Normal file
35
tests/_headless_single.lua
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
local M = require('merged-repl-plugin')
|
||||||
|
local main = [[/home/dai/projects/reperl.nvim/test_main_repo]]
|
||||||
|
local repl = [[/home/dai/projects/reperl.nvim/test_repl_repo]]
|
||||||
|
local rf = repl .. '/repl.pl'
|
||||||
|
M.setup{ main_repo_path = main, repl_repo_path = repl, repl_file = rf, auto_start = false, output_target = 'file' }
|
||||||
|
-- run one sync and capture result
|
||||||
|
M.sync_once(function(err, res)
|
||||||
|
local f = io.open([[/home/dai/projects/reperl.nvim/tests/sync_result.txt]], 'w')
|
||||||
|
f:write(tostring(err) .. "\n")
|
||||||
|
if res then f:write(vim.inspect(res) .. "\n") end
|
||||||
|
f:close()
|
||||||
|
end)
|
||||||
|
-- ensure repl file exists (writing fresh, since sync may change working tree)
|
||||||
|
local fh = io.open(rf, 'w')
|
||||||
|
if fh then fh:write('print "Hello from REPL\n";') fh:close() end
|
||||||
|
|
||||||
|
-- run the repl; runner will write repl_output.log in the repl repo
|
||||||
|
M.run_repl()
|
||||||
|
-- wait up to 2s for the output file to appear
|
||||||
|
local out_path = repl .. '/repl_output.log'
|
||||||
|
local ok = vim.wait(2000, function()
|
||||||
|
return vim.loop.fs_stat(out_path) ~= nil
|
||||||
|
end, 50)
|
||||||
|
if ok then
|
||||||
|
-- copy to tests location for inspection
|
||||||
|
local in_f = io.open(out_path, 'r')
|
||||||
|
if in_f then
|
||||||
|
local content = in_f:read('*a')
|
||||||
|
in_f:close()
|
||||||
|
local out_f = io.open([[/home/dai/projects/reperl.nvim/tests/repl_output_captured.log]], 'w')
|
||||||
|
if out_f then out_f:write(content); out_f:close() end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.cmd('qa!')
|
||||||
41
tests/_headless_test.lua
Normal file
41
tests/_headless_test.lua
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
-- headless test for merged-repl-plugin
|
||||||
|
local M = require('merged-repl-plugin')
|
||||||
|
|
||||||
|
local main = [[/home/dai/projects/reperl.nvim/test_main_repo]]
|
||||||
|
local repl = [[/home/dai/projects/reperl.nvim/test_repl_repo]]
|
||||||
|
local repl_file = repl .. '/repl.pl'
|
||||||
|
local sync_out = [[/home/dai/projects/reperl.nvim/tests/sync_result.txt]]
|
||||||
|
local repl_out = [[/home/dai/projects/reperl.nvim/tests/repl_output_captured.log]]
|
||||||
|
|
||||||
|
M.setup{ main_repo_path = main, repl_repo_path = repl, repl_file = repl_file, auto_start = false }
|
||||||
|
|
||||||
|
-- run sync_once and write result to file
|
||||||
|
M.sync_once(function(err, res)
|
||||||
|
local f = io.open(sync_out, 'w')
|
||||||
|
f:write(tostring(err) .. '\n')
|
||||||
|
if res then f:write(vim.inspect(res) .. '\n') end
|
||||||
|
f:close()
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- run the repl using plugin runner
|
||||||
|
M.run_repl()
|
||||||
|
|
||||||
|
-- wait for buffer to appear and capture its contents to repl_out
|
||||||
|
local bufname = 'REPL Output: ' .. vim.fn.fnamemodify(repl, ':t')
|
||||||
|
local ok = vim.wait(2000, function()
|
||||||
|
local bufnr = vim.fn.bufnr(bufname)
|
||||||
|
if bufnr <= 0 then return false end
|
||||||
|
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||||
|
return #lines > 0
|
||||||
|
end, 50)
|
||||||
|
|
||||||
|
local bufnr = vim.fn.bufnr(bufname)
|
||||||
|
if bufnr > 0 then
|
||||||
|
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||||
|
local f = io.open(repl_out, 'w')
|
||||||
|
f:write(table.concat(lines, '\n'))
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- exit
|
||||||
|
vim.cmd('qa!')
|
||||||
1
tests/repl_output_captured.log
Normal file
1
tests/repl_output_captured.log
Normal file
@@ -0,0 +1 @@
|
|||||||
|
-- REPL Run: 2026-01-22 11:16:46
|
||||||
147
tests/run_single.sh
Executable file
147
tests/run_single.sh
Executable file
@@ -0,0 +1,147 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Single-run integration test for merged-repl-plugin
|
||||||
|
# Usage: ./tests/run_single.sh
|
||||||
|
# Requirements: bash, git, nvim (headless) available in PATH
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
MAIN="$ROOT/test_main_repo"
|
||||||
|
REPL="$ROOT/test_repl_repo"
|
||||||
|
LUA_TEST="$ROOT/tests/_headless_single.lua"
|
||||||
|
SYNC_OUT="$ROOT/tests/sync_result.txt"
|
||||||
|
REPL_OUT="$ROOT/tests/repl_output_captured.log"
|
||||||
|
TMP_CLEANUP=()
|
||||||
|
|
||||||
|
fail() { echo "ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# ensure nvim not running (we'll be conservative)
|
||||||
|
if pgrep -a nvim > /dev/null; then
|
||||||
|
echo "Warning: nvim process found. Please close running nvim instances to avoid interference."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Preparing test repos in: $ROOT"
|
||||||
|
rm -rf "$MAIN" "$REPL" "$SYNC_OUT" "$REPL_OUT" "$LUA_TEST"
|
||||||
|
mkdir -p "$MAIN" "$REPL" "$ROOT/tests"
|
||||||
|
|
||||||
|
# init main
|
||||||
|
git -C "$MAIN" init >/dev/null 2>&1 || true
|
||||||
|
if [ -z "$(git -C "$MAIN" rev-parse --verify HEAD 2>/dev/null || true)" ]; then
|
||||||
|
touch "$MAIN/README"
|
||||||
|
git -C "$MAIN" add README
|
||||||
|
git -C "$MAIN" -c user.name='CI' -c user.email='ci@example.com' commit -m "init" >/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# init repl
|
||||||
|
git -C "$REPL" init >/dev/null 2>&1 || true
|
||||||
|
if [ -z "$(git -C "$REPL" rev-parse --verify HEAD 2>/dev/null || true)" ]; then
|
||||||
|
touch "$REPL/README"
|
||||||
|
git -C "$REPL" add README
|
||||||
|
git -C "$REPL" -c user.name='CI' -c user.email='ci@example.com' commit -m "init" >/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ensure template branch exists in repl
|
||||||
|
git -C "$REPL" checkout -B template >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# create a simple repl.pl and commit
|
||||||
|
cat > "$REPL/repl.pl" <<'EOF'
|
||||||
|
print "Hello from REPL\n";
|
||||||
|
EOF
|
||||||
|
git -C "$REPL" add repl.pl >/dev/null 2>&1 || true
|
||||||
|
git -C "$REPL" -c user.name='CI' -c user.email='ci@example.com' commit -m "add repl" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# headless lua script that triggers sync_once and run_repl and dumps file output to a file
|
||||||
|
cat > "$LUA_TEST" <<LUA
|
||||||
|
local M = require('merged-repl-plugin')
|
||||||
|
local main = [[${MAIN}]]
|
||||||
|
local repl = [[${REPL}]]
|
||||||
|
local rf = repl .. '/repl.pl'
|
||||||
|
M.setup{ main_repo_path = main, repl_repo_path = repl, repl_file = rf, auto_start = false, output_target = 'file' }
|
||||||
|
-- run one sync and capture result
|
||||||
|
M.sync_once(function(err, res)
|
||||||
|
local f = io.open([[${SYNC_OUT}]], 'w')
|
||||||
|
f:write(tostring(err) .. "\n")
|
||||||
|
if res then f:write(vim.inspect(res) .. "\n") end
|
||||||
|
f:close()
|
||||||
|
end)
|
||||||
|
-- ensure repl file exists (writing fresh, since sync may change working tree)
|
||||||
|
local fh = io.open(rf, 'w')
|
||||||
|
if fh then fh:write('print "Hello from REPL\\n";') fh:close() end
|
||||||
|
|
||||||
|
-- run the repl; runner will write repl_output.log in the repl repo
|
||||||
|
M.run_repl()
|
||||||
|
-- wait up to 2s for the output file to appear
|
||||||
|
local out_path = repl .. '/repl_output.log'
|
||||||
|
local ok = vim.wait(2000, function()
|
||||||
|
return vim.loop.fs_stat(out_path) ~= nil
|
||||||
|
end, 50)
|
||||||
|
if ok then
|
||||||
|
-- copy to tests location for inspection
|
||||||
|
local in_f = io.open(out_path, 'r')
|
||||||
|
if in_f then
|
||||||
|
local content = in_f:read('*a')
|
||||||
|
in_f:close()
|
||||||
|
local out_f = io.open([[${REPL_OUT}]], 'w')
|
||||||
|
if out_f then out_f:write(content); out_f:close() end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.cmd('qa!')
|
||||||
|
LUA
|
||||||
|
|
||||||
|
# run headless nvim to execute the lua test
|
||||||
|
nvim --headless -u NONE -c "lua package.path = package.path .. ';./lua/?.lua;./lua/?/init.lua'" -c "luafile $LUA_TEST"
|
||||||
|
|
||||||
|
# wait for repl_output.log to appear (up to 5s)
|
||||||
|
out_file="$REPL/repl_output.log"
|
||||||
|
max_wait=25 # 25 * 0.2s = 5s
|
||||||
|
i=0
|
||||||
|
while [ $i -lt $max_wait ]; do
|
||||||
|
if [ -f "$out_file" ]; then break; fi
|
||||||
|
sleep 0.2
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
# dump sync output and repl output
|
||||||
|
|
||||||
|
echo "=== sync result ==="
|
||||||
|
if [ -f "$SYNC_OUT" ]; then cat "$SYNC_OUT"; else echo "(no sync output)"; fi
|
||||||
|
|
||||||
|
echo "=== repl output ==="
|
||||||
|
if [ -f "$REPL_OUT" ]; then cat "$REPL_OUT"; else echo "(no repl output captured)"; fi
|
||||||
|
|
||||||
|
# show repl_output.log content for debugging if present
|
||||||
|
if [ -f "$out_file" ]; then
|
||||||
|
echo "--- $out_file contents ---"
|
||||||
|
sed -n '1,200p' "$out_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# strict checks
|
||||||
|
# ensure repl output file exists and contains expected text
|
||||||
|
if [ ! -f "$REPL/repl_output.log" ]; then
|
||||||
|
echo "ERROR: repl_output.log missing" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
if ! grep -q "Hello from REPL" "$REPL/repl_output.log"; then
|
||||||
|
echo "ERROR: expected REPL output missing" >&2
|
||||||
|
cat "$REPL/repl_output.log" || true
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
# verify branch mirroring (create branch in main and run sync_once again)
|
||||||
|
git -C "$MAIN" checkout -B feature-single || fail "failed to create test branch in main"
|
||||||
|
nvim --headless -u NONE -c "lua package.path = package.path .. ';./lua/?.lua;./lua/?/init.lua'" -c "lua require('merged-repl-plugin').setup{ main_repo_path='${MAIN}', repl_repo_path='${REPL}', repl_file='${REPL}/repl.pl', auto_start=false } require('merged-repl-plugin').sync_once()" -c "qa!"
|
||||||
|
|
||||||
|
echo "=== repl branches ==="
|
||||||
|
git -C "$REPL" branch --list
|
||||||
|
|
||||||
|
# test auto-commit: append to repl.pl and run sync_once; then show latest commit message
|
||||||
|
echo 'print "Modified for single test\\n";' >> "$REPL/repl.pl"
|
||||||
|
nvim --headless -u NONE -c "lua package.path = package.path .. ';./lua/?.lua;./lua/?/init.lua'" -c "lua require('merged-repl-plugin').setup{ main_repo_path='${MAIN}', repl_repo_path='${REPL}', repl_file='${REPL}/repl.pl', auto_start=false } require('merged-repl-plugin').sync_once()" -c "qa!"
|
||||||
|
|
||||||
|
echo "=== latest repl commit message ==="
|
||||||
|
git -C "$REPL" log -1 --pretty=%B || true
|
||||||
|
|
||||||
|
echo "Single run complete."
|
||||||
130
tests/run_tests.sh
Executable file
130
tests/run_tests.sh
Executable file
@@ -0,0 +1,130 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Run integration tests for merged-repl-plugin
|
||||||
|
# Usage: ./tests/run_tests.sh
|
||||||
|
# This script creates two test git repositories under the project directory,
|
||||||
|
# runs headless Neovim to exercise the plugin (sync and run), and verifies
|
||||||
|
# expected results (branch creation, repl output capture, auto-commit).
|
||||||
|
|
||||||
|
REPO_ROOT=$(cd "$(dirname "$0")/.." && pwd)
|
||||||
|
MAIN="$REPO_ROOT/test_main_repo"
|
||||||
|
REPL="$REPO_ROOT/test_repl_repo"
|
||||||
|
LUA_TMP="$REPO_ROOT/tests/_headless_test.lua"
|
||||||
|
SYNC_OUT="$REPO_ROOT/tests/sync_result.txt"
|
||||||
|
REPL_OUT="$REPO_ROOT/tests/repl_output_captured.log"
|
||||||
|
|
||||||
|
echo "Running tests in: $REPO_ROOT"
|
||||||
|
|
||||||
|
# Prepare test repos
|
||||||
|
rm -rf "$MAIN" "$REPL" "$REPL_OUT" "$SYNC_OUT"
|
||||||
|
mkdir -p "$MAIN" "$REPL" "$REPO_ROOT/tests"
|
||||||
|
|
||||||
|
git -C "$MAIN" init || true
|
||||||
|
# create initial commit in main
|
||||||
|
if [ -z "$(git -C "$MAIN" rev-parse --verify HEAD 2>/dev/null || true)" ]; then
|
||||||
|
touch "$MAIN/README"
|
||||||
|
git -C "$MAIN" add README
|
||||||
|
git -C "$MAIN" -c user.name='CI' -c user.email='ci@example.com' commit -m "init"
|
||||||
|
fi
|
||||||
|
|
||||||
|
git -C "$REPL" init || true
|
||||||
|
if [ -z "$(git -C "$REPL" rev-parse --verify HEAD 2>/dev/null || true)" ]; then
|
||||||
|
touch "$REPL/README"
|
||||||
|
git -C "$REPL" add README
|
||||||
|
git -C "$REPL" -c user.name='CI' -c user.email='ci@example.com' commit -m "init"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure template branch exists in repl repo
|
||||||
|
git -C "$REPL" checkout -B template || true
|
||||||
|
|
||||||
|
# Write a simple repl.pl
|
||||||
|
cat > "$REPL/repl.pl" <<'EOF'
|
||||||
|
print "Hello from REPL\n";
|
||||||
|
EOF
|
||||||
|
# commit repl.pl
|
||||||
|
git -C "$REPL" add repl.pl || true
|
||||||
|
git -C "$REPL" -c user.name='CI' -c user.email='ci@example.com' commit -m "add repl" || true
|
||||||
|
|
||||||
|
# Compose headless lua test file
|
||||||
|
cat > "$LUA_TMP" <<LUALIB
|
||||||
|
-- headless test for merged-repl-plugin
|
||||||
|
local M = require('merged-repl-plugin')
|
||||||
|
|
||||||
|
local main = [[${MAIN}]]
|
||||||
|
local repl = [[${REPL}]]
|
||||||
|
local repl_file = repl .. '/repl.pl'
|
||||||
|
local sync_out = [[${SYNC_OUT}]]
|
||||||
|
local repl_out = [[${REPL_OUT}]]
|
||||||
|
|
||||||
|
M.setup{ main_repo_path = main, repl_repo_path = repl, repl_file = repl_file, auto_start = false }
|
||||||
|
|
||||||
|
-- run sync_once and write result to file
|
||||||
|
M.sync_once(function(err, res)
|
||||||
|
local f = io.open(sync_out, 'w')
|
||||||
|
f:write(tostring(err) .. '\n')
|
||||||
|
if res then f:write(vim.inspect(res) .. '\n') end
|
||||||
|
f:close()
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- run the repl using plugin runner
|
||||||
|
M.run_repl()
|
||||||
|
|
||||||
|
-- wait for buffer to appear and capture its contents to repl_out
|
||||||
|
local bufname = 'REPL Output: ' .. vim.fn.fnamemodify(repl, ':t')
|
||||||
|
local ok = vim.wait(2000, function()
|
||||||
|
local bufnr = vim.fn.bufnr(bufname)
|
||||||
|
if bufnr <= 0 then return false end
|
||||||
|
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||||
|
return #lines > 0
|
||||||
|
end, 50)
|
||||||
|
|
||||||
|
local bufnr = vim.fn.bufnr(bufname)
|
||||||
|
if bufnr > 0 then
|
||||||
|
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||||
|
local f = io.open(repl_out, 'w')
|
||||||
|
f:write(table.concat(lines, '\n'))
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- exit
|
||||||
|
vim.cmd('qa!')
|
||||||
|
LUALIB
|
||||||
|
|
||||||
|
# Run headless Neovim with the test lua file
|
||||||
|
nvim --headless -u NONE -c "lua package.path = package.path .. ';./lua/?.lua;./lua/?/init.lua'" -c "luafile ${LUA_TMP}"
|
||||||
|
|
||||||
|
# Check results
|
||||||
|
echo "--- Sync result ---"
|
||||||
|
if [ -f "$SYNC_OUT" ]; then
|
||||||
|
cat "$SYNC_OUT"
|
||||||
|
else
|
||||||
|
echo "No sync output file found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "--- Repl output ---"
|
||||||
|
if [ -f "$REPL_OUT" ]; then
|
||||||
|
cat "$REPL_OUT"
|
||||||
|
else
|
||||||
|
echo "No repl output captured"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify repl repo got branch created if main branch changed
|
||||||
|
# create a new branch in main to test branch mirroring
|
||||||
|
git -C "$MAIN" checkout -B feature-test
|
||||||
|
|
||||||
|
# run sync_once again
|
||||||
|
nvim --headless -u NONE -c "lua package.path = package.path .. ';./lua/?.lua;./lua/?/init.lua'" -c "lua require('merged-repl-plugin').setup{ main_repo_path = '${MAIN}', repl_repo_path = '${REPL}', repl_file = '${REPL}/repl.pl', auto_start = false } require('merged-repl-plugin').sync_once()" -c "qa!"
|
||||||
|
|
||||||
|
echo "--- Branches in repl repo ---"
|
||||||
|
git -C "$REPL" branch --list
|
||||||
|
|
||||||
|
# Test auto-commit: modify repl.pl and run sync_once to ensure commit
|
||||||
|
echo 'print "Modified\n";' >> "$REPL/repl.pl"
|
||||||
|
|
||||||
|
nvim --headless -u NONE -c "lua package.path = package.path .. ';./lua/?.lua;./lua/?/init.lua'" -c "lua require('merged-repl-plugin').setup{ main_repo_path = '${MAIN}', repl_repo_path = '${REPL}', repl_file = '${REPL}/repl.pl', auto_start = false } require('merged-repl-plugin').sync_once()" -c "qa!"
|
||||||
|
|
||||||
|
echo "--- Latest commit message in repl repo ---"
|
||||||
|
git -C "$REPL" log -1 --pretty=%B
|
||||||
|
|
||||||
|
echo "Tests finished."
|
||||||
5
tests/sync_result.txt
Normal file
5
tests/sync_result.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
nil
|
||||||
|
{
|
||||||
|
branch = "master",
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user