217 lines
6.9 KiB
Lua
217 lines
6.9 KiB
Lua
-- This module contains the logic responsible for building and starting
|
|
-- the Golang server. The Go server is responsible for making API calls
|
|
-- to Gitlab and returning the data
|
|
local List = require("gitlab.utils.list")
|
|
local state = require("gitlab.state")
|
|
local u = require("gitlab.utils")
|
|
local job = require("gitlab.job")
|
|
local Job = require("plenary.job")
|
|
local M = {}
|
|
|
|
-- Builds the binary if it doesn't exist, and starts the server. If the pre-existing binary has an older
|
|
-- tag than the Lua code (exposed via the /version endpoint) then shuts down the server, rebuilds it, and
|
|
-- restarts the server again.
|
|
M.build_and_start = function(callback)
|
|
M.build(false)
|
|
M.start(function()
|
|
M.get_version(function(version)
|
|
if version.plugin_version ~= version.binary_version then
|
|
M.shutdown(function()
|
|
if M.build(true) then
|
|
M.start(callback)
|
|
end
|
|
end)
|
|
else
|
|
callback()
|
|
end
|
|
end)
|
|
end)
|
|
end
|
|
|
|
-- Starts the Go server and call the callback provided
|
|
M.start = function(callback)
|
|
local port = tonumber(state.settings.port) or 0
|
|
local parsed_port = nil
|
|
local callback_called = false
|
|
|
|
local go_server_settings = {
|
|
gitlab_url = state.settings.gitlab_url,
|
|
port = port,
|
|
auth_token = state.settings.auth_token,
|
|
debug = state.settings.debug,
|
|
log_path = state.settings.log_path,
|
|
connection_settings = state.settings.connection_settings,
|
|
chosen_mr_iid = state.chosen_mr_iid,
|
|
}
|
|
|
|
state.chosen_mr_iid = 0 -- Do not let this interfere with subsequent reviewer.open() calls
|
|
|
|
local settings = vim.json.encode(go_server_settings)
|
|
if vim.fn.has("win32") then
|
|
settings = settings:gsub('"', '\\"')
|
|
end
|
|
|
|
local command = string.format('"%s" "%s"', state.settings.bin, settings)
|
|
|
|
local job_id = vim.fn.jobstart(command, {
|
|
on_stdout = function(_, data)
|
|
-- if port was not provided then we need to parse it from output of server
|
|
if parsed_port == nil then
|
|
for _, line in ipairs(data) do
|
|
port = line:match("Server started on port:%s+(%d+)")
|
|
if port ~= nil then
|
|
parsed_port = port
|
|
state.settings.port = port
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
-- This assumes that first output of server will be parsable and port will be correctly set.
|
|
-- Make sure that this actually check if port was correctly parsed based on server output
|
|
-- because server outputs port only if it started successfully.
|
|
if parsed_port ~= nil and not callback_called then
|
|
state.go_server_running = true
|
|
callback()
|
|
callback_called = true
|
|
end
|
|
end,
|
|
on_stderr = function(_, errors)
|
|
local err_msg = List.new(errors):reduce(function(agg, err)
|
|
if err ~= "" and err ~= nil then
|
|
agg = agg .. err .. "\n"
|
|
end
|
|
return agg
|
|
end, "")
|
|
|
|
if err_msg ~= "" then
|
|
u.notify(err_msg, vim.log.levels.ERROR)
|
|
end
|
|
end,
|
|
on_exit = function(job_id, exit_code)
|
|
if exit_code ~= 0 then
|
|
u.notify(
|
|
"Golang gitlab server exited: job_id: " .. job_id .. ", exit_code: " .. exit_code,
|
|
vim.log.levels.ERROR
|
|
)
|
|
end
|
|
end,
|
|
})
|
|
if job_id <= 0 then
|
|
u.notify("Could not start gitlab.nvim binary", vim.log.levels.ERROR)
|
|
end
|
|
end
|
|
|
|
-- Builds the Go binary with the current Git tag.
|
|
M.build = function(override)
|
|
local file_path = u.current_file_path()
|
|
local parent_dir = vim.fn.fnamemodify(file_path, ":h:h:h:h")
|
|
|
|
local bin_name = u.is_windows() and "bin.exe" or "bin"
|
|
state.settings.root_path = parent_dir
|
|
state.settings.bin = parent_dir .. u.path_separator .. "cmd" .. u.path_separator .. bin_name
|
|
|
|
if not override then
|
|
local binary_exists = vim.loop.fs_stat(state.settings.bin)
|
|
if binary_exists ~= nil then
|
|
return
|
|
end
|
|
end
|
|
|
|
local version_output = vim.system({ "git", "describe", "--tags", "--always" }, { cwd = parent_dir }):wait()
|
|
local version = version_output.code == 0 and vim.trim(version_output.stdout) or "unknown"
|
|
|
|
local ldflags = string.format("-X main.Version=%s", version)
|
|
local res = vim
|
|
.system(
|
|
{ "go", "build", "-ldflags", ldflags, "-o", bin_name },
|
|
{ cwd = state.settings.root_path .. u.path_separator .. "cmd" }
|
|
)
|
|
:wait()
|
|
|
|
if res.code ~= 0 then
|
|
u.notify(string.format("Failed to install with status code %d:\n%s", res.code, res.stderr), vim.log.levels.ERROR)
|
|
return false
|
|
end
|
|
u.notify("Installed successfully!", vim.log.levels.INFO)
|
|
return true
|
|
end
|
|
|
|
-- Shuts down the Go server and clears out all old gitlab.nvim state
|
|
M.shutdown = function(cb)
|
|
if not state.go_server_running then
|
|
vim.notify("The gitlab.nvim server is not running", vim.log.levels.ERROR)
|
|
return
|
|
end
|
|
job.run_job("/shutdown", "POST", { restart = false }, function(data)
|
|
state.go_server_running = false
|
|
state.clear_data()
|
|
if cb then
|
|
cb()
|
|
else
|
|
u.notify(data.message, vim.log.levels.INFO)
|
|
end
|
|
end)
|
|
end
|
|
|
|
---Restarts the Go server and clears out all gitlab.nvim state
|
|
M.restart = function(cb)
|
|
if not state.go_server_running then
|
|
vim.notify("The gitlab.nvim server is not running", vim.log.levels.ERROR)
|
|
return
|
|
end
|
|
job.run_job("/shutdown", "POST", { restart = true }, function(data)
|
|
state.go_server_running = false
|
|
M.start(function()
|
|
state.clear_data()
|
|
if cb then
|
|
cb()
|
|
else
|
|
u.notify(data.message, vim.log.levels.INFO)
|
|
end
|
|
end)
|
|
end)
|
|
end
|
|
|
|
-- Returns the version (git tag) that was baked into the binary when it was last built
|
|
M.get_version = function(callback)
|
|
if not state.go_server_running then
|
|
u.notify("Gitlab server not running", vim.log.levels.ERROR)
|
|
return nil
|
|
end
|
|
local file_path = u.current_file_path()
|
|
local parent_dir = vim.fn.fnamemodify(file_path, ":h:h:h:h")
|
|
|
|
local version_output = vim.system({ "git", "describe", "--tags", "--always" }, { cwd = parent_dir }):wait()
|
|
local plugin_version = version_output.code == 0 and vim.trim(version_output.stdout) or "unknown"
|
|
|
|
local args = { "-s", "-X", "GET", string.format("localhost:%s/version", state.settings.port) }
|
|
|
|
-- We call the "/version" endpoint here instead of through the regular jobs pattern because earlier versions of the plugin
|
|
-- may not have it. We handle a 404 as an "unknown" version error.
|
|
Job:new({
|
|
command = "curl",
|
|
args = args,
|
|
on_stdout = function(_, output)
|
|
vim.defer_fn(function()
|
|
if output == nil then
|
|
callback({ plugin_version = plugin_version, binary_version = "unknown" })
|
|
return
|
|
end
|
|
|
|
local data_ok, data = pcall(vim.json.decode, output)
|
|
if not data_ok or data == nil or data.version == nil then
|
|
callback({ plugin_version = plugin_version, binary_version = "unknown" })
|
|
return
|
|
end
|
|
|
|
callback({ plugin_version = plugin_version, binary_version = data.version })
|
|
end, 0)
|
|
end,
|
|
on_stderr = function() end,
|
|
on_exit = function() end,
|
|
}):start()
|
|
end
|
|
|
|
return M
|