Add option to choose random server port (#65)

Updates the plugin to automatically choose a random port when started when no port is provided. This will allow multiple Neovim instances to open at the same time in different projects.
This commit is contained in:
johnybx
2023-10-21 17:39:37 +02:00
committed by GitHub
parent 2100bf2e43
commit c8a0267ba6
5 changed files with 119 additions and 66 deletions

View File

@@ -13,7 +13,6 @@ And a lot more!
https://github.com/harrisoncramer/gitlab.nvim/assets/32515581/50f44eaf-5f99-4cb3-93e9-ed66ace0f675 https://github.com/harrisoncramer/gitlab.nvim/assets/32515581/50f44eaf-5f99-4cb3-93e9-ed66ace0f675
## Requirements ## Requirements
- <a href="https://go.dev/">Go</a> >= v1.19 - <a href="https://go.dev/">Go</a> >= v1.19
@@ -22,10 +21,10 @@ https://github.com/harrisoncramer/gitlab.nvim/assets/32515581/50f44eaf-5f99-4cb3
1. Install Go 1. Install Go
2. Install reviewer: <a href="https://github.com/dandavison/delta">delta</a> or <a href="https://github.com/sindrets/diffview.nvim">diffview</a> 2. Install reviewer: <a href="https://github.com/dandavison/delta">delta</a> or <a href="https://github.com/sindrets/diffview.nvim">diffview</a>
2. Add configuration (see Installation section) 3. Add configuration (see Installation section)
3. Checkout your feature branch: `git checkout feature-branch` 4. Checkout your feature branch: `git checkout feature-branch`
4. Open Neovim 5. Open Neovim
5. Run `:lua require("gitlab").review()` to open the reviewer pane 6. Run `:lua require("gitlab").review()` to open the reviewer pane
## Installation ## Installation
@@ -85,7 +84,7 @@ Here is the default setup function. All of these values are optional, and if you
```lua ```lua
require("gitlab").setup({ require("gitlab").setup({
port = 21036, -- The port of the Go server, which runs in the background port = nil, -- The port of the Go server, which runs in the background, if omitted or `nil` the port will be chosen automatically
log_path = vim.fn.stdpath("cache") .. "/gitlab.nvim.log", -- Log path for the Go server log_path = vim.fn.stdpath("cache") .. "/gitlab.nvim.log", -- Log path for the Go server
reviewer = "delta", -- The reviewer type ("delta" or "diffview") reviewer = "delta", -- The reviewer type ("delta" or "diffview")
attachment_dir = nil, -- The local directory for files (see the "summary" section) attachment_dir = nil, -- The local directory for files (see the "summary" section)

View File

@@ -4,10 +4,12 @@ import (
"context" "context"
"fmt" "fmt"
"log" "log"
"net"
"net/http" "net/http"
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
"time"
) )
func main() { func main() {
@@ -29,6 +31,7 @@ func main() {
} }
m := http.NewServeMux() m := http.NewServeMux()
m.Handle("/ping", http.HandlerFunc(PingHandler))
m.Handle("/mr/summary", withGitlabContext(http.HandlerFunc(SummaryHandler), c)) m.Handle("/mr/summary", withGitlabContext(http.HandlerFunc(SummaryHandler), c))
m.Handle("/mr/attachment", withGitlabContext(http.HandlerFunc(AttachmentHandler), c)) m.Handle("/mr/attachment", withGitlabContext(http.HandlerFunc(AttachmentHandler), c))
m.Handle("/mr/reviewer", withGitlabContext(http.HandlerFunc(ReviewersHandler), c)) m.Handle("/mr/reviewer", withGitlabContext(http.HandlerFunc(ReviewersHandler), c))
@@ -44,19 +47,45 @@ func main() {
m.Handle("/pipeline", withGitlabContext(http.HandlerFunc(PipelineHandler), c)) m.Handle("/pipeline", withGitlabContext(http.HandlerFunc(PipelineHandler), c))
m.Handle("/job", withGitlabContext(http.HandlerFunc(JobHandler), c)) m.Handle("/job", withGitlabContext(http.HandlerFunc(JobHandler), c))
port := fmt.Sprintf(":%s", os.Args[3]) port := os.Args[3]
server := &http.Server{ if port == "" {
Addr: port, // port was not specified
Handler: m, port = "0"
}
addr := fmt.Sprintf("localhost:%s", port)
listener, err := net.Listen("tcp", addr)
if err != nil {
log.Fatal(err)
fmt.Fprintf(os.Stderr, "Error starting server: %s\n", err)
os.Exit(1)
}
listner_port := listener.Addr().(*net.TCPAddr).Port
errCh := make(chan error)
go func() {
err := http.Serve(listener, m)
errCh <- err
}()
go func() {
for i := 0; i < 10; i++ {
resp, err := http.Get("http://localhost:" + fmt.Sprintf("%d", listner_port) + "/ping")
if resp.StatusCode == 200 && err == nil {
/* This print is detected by the Lua code and used to fetch project information */
fmt.Println("Server started on port: ", listner_port)
return
}
// Wait for healthcheck to pass - at most 1 sec.
time.Sleep(100 * time.Microsecond)
}
errCh <- err
}()
if err := <-errCh; err != nil {
fmt.Fprintf(os.Stderr, "Error starting server: %s\n", err)
os.Exit(1)
} }
done := make(chan bool)
go server.ListenAndServe()
/* This print is detected by the Lua code and used to fetch project information */
fmt.Println("Server started.")
<-done
} }
func withGitlabContext(next http.HandlerFunc, c Client) http.Handler { func withGitlabContext(next http.HandlerFunc, c Client) http.Handler {
@@ -77,3 +106,7 @@ func getCurrentBranch() (res string, e error) {
return strings.TrimSpace(string(output)), nil return strings.TrimSpace(string(output)), nil
} }
func PingHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "pong")
}

View File

@@ -2,45 +2,75 @@
-- the Golang server. The Go server is responsible for making API calls -- the Golang server. The Go server is responsible for making API calls
-- to Gitlab and returning the data -- to Gitlab and returning the data
local state = require("gitlab.state") local state = require("gitlab.state")
local u = require("gitlab.utils") local u = require("gitlab.utils")
local M = {} local M = {}
-- Starts the Go server and call the callback provided -- Starts the Go server and call the callback provided
M.start = function(callback) M.start = function(callback)
local empty_port = "''"
local port = state.settings.port or empty_port
local parsed_port = nil
local callback_called = false
local command = state.settings.bin local command = state.settings.bin
.. " " .. " "
.. state.settings.project_id .. state.settings.project_id
.. " " .. " "
.. state.settings.gitlab_url .. state.settings.gitlab_url
.. " " .. " "
.. state.settings.port .. port
.. " " .. " "
.. state.settings.auth_token .. state.settings.auth_token
.. " " .. " "
.. state.settings.log_path .. state.settings.log_path
vim.fn.jobstart(command, { local job_id = vim.fn.jobstart(command, {
on_stdout = function(job_id) on_stdout = function(_, data)
if job_id <= 0 then -- if port was not provided then we need to parse it from output of server
vim.notify("Could not start gitlab.nvim binary", vim.log.levels.ERROR) if parsed_port == nil then
else 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
callback() callback()
callback_called = true
elseif not callback_called then
vim.notify("Failed to parse server port", vim.log.levels.ERROR)
end end
end, end,
on_stderr = function(_, errors) on_stderr = function(_, errors)
local err_msg = '' local err_msg = ""
for _, err in ipairs(errors) do for _, err in ipairs(errors) do
if err ~= "" and err ~= nil then if err ~= "" and err ~= nil then
err_msg = err_msg .. err .. "\n" err_msg = err_msg .. err .. "\n"
end end
end end
if err_msg ~= '' then vim.notify(err_msg, vim.log.levels.ERROR) end if err_msg ~= "" then
end vim.notify(err_msg, vim.log.levels.ERROR)
end
end,
on_exit = function(job_id, exit_code, ...)
vim.notify(
"Golang gitlab server exited: job_id: " .. job_id .. ", exit_code: " .. exit_code,
vim.log.levels.ERROR
)
end,
}) })
if job_id <= 0 then
vim.notify("Could not start gitlab.nvim binary", vim.log.levels.ERROR)
end
end end
-- Builds the Go binary -- Builds the Go binary
M.build = function(override) M.build = function(override)
local file_path = u.current_file_path() local file_path = u.current_file_path()
@@ -50,12 +80,13 @@ M.build = function(override)
if not override then if not override then
local binary_exists = vim.loop.fs_stat(state.settings.bin) local binary_exists = vim.loop.fs_stat(state.settings.bin)
if binary_exists ~= nil then return end if binary_exists ~= nil then
return
end
end end
local cmd = u.is_windows() and local cmd = u.is_windows() and "cd %s\\cmd && go build -o bin.exe && move bin.exe ..\\"
'cd %s\\cmd && go build -o bin.exe && move bin.exe ..\\' or or "cd %s/cmd && go build -o bin && mv bin ../bin"
'cd %s/cmd && go build -o bin && mv bin ../bin'
local command = string.format(cmd, state.settings.bin_path) local command = string.format(cmd, state.settings.bin_path)
local null = u.is_windows() and " >NUL" or " > /dev/null" local null = u.is_windows() and " >NUL" or " > /dev/null"

View File

@@ -7,11 +7,11 @@ local u = require("gitlab.utils")
local M = {} local M = {}
-- These are the default settings for the plugin -- These are the default settings for the plugin
M.settings = { M.settings = {
port = 21036, port = nil, -- choose random port
log_path = (vim.fn.stdpath("cache") .. "/gitlab.nvim.log"), log_path = (vim.fn.stdpath("cache") .. "/gitlab.nvim.log"),
reviewer = "delta", reviewer = "delta",
attachment_dir = '', attachment_dir = "",
popup = { popup = {
exit = "<Esc>", exit = "<Esc>",
perform_action = "<leader>s", perform_action = "<leader>s",
@@ -29,15 +29,15 @@ M.settings = {
relative = "editor", relative = "editor",
position = "left", position = "left",
size = "20%", size = "20%",
resolved = '', resolved = "",
unresolved = '', unresolved = "",
}, },
review_pane = { review_pane = {
delta = { delta = {
added_file = "", added_file = "",
modified_file = "", modified_file = "",
removed_file = "", removed_file = "",
} },
}, },
pipeline = { pipeline = {
created = "", created = "",

View File

@@ -126,20 +126,10 @@ M.create_popup_state = function(title, width, height)
end end
M.merge = function(defaults, overrides) M.merge = function(defaults, overrides)
local result = {}
if type(defaults) == "table" and M.table_size(defaults) == 0 and type(overrides) == "table" then if type(defaults) == "table" and M.table_size(defaults) == 0 and type(overrides) == "table" then
return overrides return overrides
end end
return vim.tbl_deep_extend("force", defaults, overrides)
for key, value in pairs(defaults) do
if type(value) == "table" then
result[key] = M.merge(value, overrides[key] or {})
else
result[key] = overrides[key] or value
end
end
return result
end end
M.join = function(tbl, separator) M.join = function(tbl, separator)