Skip to content

Commit

Permalink
feat: improve git remote handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Willem Jan Noort committed Jun 22, 2024
1 parent 5eb04bc commit c631083
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 31 deletions.
3 changes: 2 additions & 1 deletion lua/adopure/config/internal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
---@class adopure.InternalConfig
---@field pat_token string|nil
---@field hl_groups adopure.InternalHighlights
---@field preferred_remotes string[]

local InternalConfig = {}
function InternalConfig:new()
---@type adopure.InternalConfig
local default_config = {
pat_token = os.getenv("AZURE_DEVOPS_EXT_PAT"),
hl_groups = {
Expand All @@ -19,6 +19,7 @@ function InternalConfig:new()
inactive = "DiagnosticUnderlineOk",
inactive_sign = "@comment.note",
},
preferred_remotes = {},
}
local user_config = type(vim.g.adopure) == "function" and vim.g.adopure() or vim.g.adopure or {}
local config = vim.tbl_deep_extend("force", default_config, user_config or {})
Expand Down
1 change: 1 addition & 0 deletions lua/adopure/config/meta.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
---@class adopure.Config
---@field pat_token? string
---@field hl_groups? adopure.Highlights
---@field preferred_remotes? string[]

local config = {}

Expand Down
72 changes: 48 additions & 24 deletions lua/adopure/git.lua
Original file line number Diff line number Diff line change
@@ -1,30 +1,47 @@
local M = {}

---@param remote_url string
---@param remote_stdout string[]
---@return string remote
local function elect_remote(remote_stdout)
local preferred_remotes = require("adopure.config.internal").preferred_remotes
for _, remote_line in ipairs(remote_stdout) do
local name_and_details = vim.split(remote_line, "\t")
local remote_name = name_and_details[1]
if vim.tbl_contains(preferred_remotes, remote_name) then
return remote_line
end
end
for _, remote_line in ipairs(remote_stdout) do
if remote_line:find("azure.com") or remote_line:find("visualstudio.com") then
return remote_line
end
end
vim.notify("adopure unable to elect azure devops remote url; taking the first", 3)
return remote_stdout[1]
end

---@param remote_stdout string
---@return string organization_url
---@return string project_name
---@return string repository_name
local function extract_git_details(remote_url)
local organization_url, project_name, repository_name
if remote_url:find("@ssh.dev.azure.com") then
local _, _, base_url, org_name, project_name_extracted, repo_name_extracted =
remote_url:find(".-@(ssh.dev.azure.com):v3/(.-)/(.-)/(.+)%s*%(fetch%)")
organization_url = "https://" .. base_url:gsub("ssh.", "") .. "/" .. org_name
project_name = project_name_extracted
repository_name = repo_name_extracted
elseif remote_url:find("https") then
local https_pattern = "(https://)[^@]*@([^/]+)/([^/]+)/([^/]+)/_git/([^%s]+)"
local _, _, protocol, domain, org_name, project_name_extracted, repo_name_extracted =
remote_url:find(https_pattern)
organization_url = protocol .. domain .. "/" .. org_name
project_name = project_name_extracted
repository_name = repo_name_extracted
local function extract_git_details(remote_stdout)
local host, project_name, repository_name, organization_name
local url_with_type = vim.split(remote_stdout, "\t")[2]
local url = vim.split(url_with_type, " ")[1]
if url:find("@ssh") then
local ssh_base
ssh_base, organization_name, project_name, repository_name = unpack(vim.split(url, "/"))
host = vim.split(vim.split(ssh_base, ":")[1], "@ssh.")[2]
end

local trim_pattern = "^%s*(.-)%s*$"
return organization_url:gsub(trim_pattern, "%1") .. "/",
project_name:gsub(trim_pattern, "%1"),
repository_name:gsub(trim_pattern, "%1")
if remote_stdout:find("https://") then
local https_base, user_domain
https_base, repository_name = unpack(vim.split(url, "/_git/"))
user_domain, organization_name, project_name = unpack(vim.split(https_base, "/"), 3)
local user_at_host_parts = vim.split(user_domain, "@")
host = user_at_host_parts[#user_at_host_parts]
end
local organization_url = table.concat({ "https://", host, "/", organization_name, "/" })
return organization_url, project_name, repository_name
end

---Get config from git remote
Expand All @@ -38,9 +55,16 @@ function M.get_remote_config()
cwd = ".",
})
get_remotes:start()
---@type string
local remote = require("adopure.utils").await_result(get_remotes)[1]
return extract_git_details(remote)
local result = require("adopure.utils").await_result(get_remotes)
if result.stderr[1] then
if not result.stdout[1] then
error(result.stderr[1])
end
vim.notify(result.stderr[1], 3)
end
assert(result.stdout[1], "No remote found to extract details;")
local elected_remote = elect_remote(result.stdout)
return extract_git_details(elected_remote)
end

---@param pull_request adopure.PullRequest
Expand Down
20 changes: 14 additions & 6 deletions lua/adopure/utils.lua
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
local M = {}
---@class adopure.JobResult
---@field stdout string[]
---@field stderr string[]

--- Await result of plenary job
---@diagnostic disable-next-line: undefined-doc-name
---@param job Job
---@return unknown
---@return adopure.JobResult
function M.await_result(job)
local result
local stdout, stderr
while true do
if result then
return result
if (stdout and stdout[1]) or (stderr and stderr[1]) then
return {
stdout = stdout,
stderr = stderr,
}
end
vim.wait(1000, function()
vim.wait(200, function()
---@diagnostic disable-next-line: missing-return,undefined-field
result = job:result()
stdout = job:result()
stderr = job:stderr_result()
end)
end
end
Expand Down
62 changes: 62 additions & 0 deletions spec/git_remote_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
local assert = require("luassert.assert")
local plenary_new_job = require("plenary.job").new
local remotes = {
first_fetch = "first\t[email protected]:v3/first_org/first_project.nvim/first_repo.nvim (fetch)",
first_push = "first\t[email protected]:v3/first_org/first_project.nvim/first_repo.nvim (push)",
git_fetch = "origin\t[email protected]:Willem-J-an/adopure.nvim.git (fetch)",
git_push = "origin\t[email protected]:Willem-J-an/adopure.nvim.git (push)",
second_fetch = "second\thttps://[email protected]/second_org/second_project/_git/second_repo (fetch)",
second_push = "second\thttps://[email protected]/second_org/second_project/_git/second_repo (push)",
third_fetch = "third\thttps://dev.azure.com/third_org/third_project/_git/third_repo (fetch)",
third_push = "third\thttps://dev.azure.com/third_org/third_project/_git/third_repo (push)",
}

describe("get remote config", function()
---@param remote_stdout string[]
local function mock_git_remote(remote_stdout)
require("plenary.job").new = function(_1, _2) ---@diagnostic disable-line duplicate-set-field
local _ = _1 and _2
return {
start = function(_) end,
result = function(_)
return remote_stdout
end,
stderr_result = function(_)
return {}
end,
}
end
end

it("returns preferred ssh details", function()
require("adopure.config.internal").preferred_remotes = { "first" }
mock_git_remote(vim.tbl_values(remotes))
local organization_url, project_name, repository_name = require("adopure.git").get_remote_config()
assert.are.same("https://dev.azure.com/first_org/", organization_url)
assert.are.same("first_project.nvim", project_name)
assert.are.same("first_repo.nvim", repository_name)
end)

it("returns preferred https details with user", function()
require("adopure.config.internal").preferred_remotes = { "second" }
mock_git_remote(vim.tbl_values(remotes))
local organization_url, project_name, repository_name = require("adopure.git").get_remote_config()
assert.are.same("https://dev.azure.com/second_org/", organization_url)
assert.are.same("second_project", project_name)
assert.are.same("second_repo", repository_name)
end)


it("returns preferred https details without user", function()
require("adopure.config.internal").preferred_remotes = { "third" }
mock_git_remote(vim.tbl_values(remotes))
local organization_url, project_name, repository_name = require("adopure.git").get_remote_config()
assert.are.same("https://dev.azure.com/third_org/", organization_url)
assert.are.same("third_project", project_name)
assert.are.same("third_repo", repository_name)
end)

after_each(function()
require("plenary.job").new = plenary_new_job
end)
end)

0 comments on commit c631083

Please sign in to comment.