ci: format

This commit is contained in:
Barrett Ruth 2026-03-27 17:19:09 -04:00
parent c4da2cda2a
commit 5ee2cc567a
No known key found for this signature in database
GPG key ID: A6C96C9349D2FC81
6 changed files with 2173 additions and 2394 deletions

View file

@ -2,240 +2,234 @@ local forge = require('forge')
---@type forge.Forge ---@type forge.Forge
local M = { local M = {
name = 'codeberg', name = 'codeberg',
cli = 'tea', cli = 'tea',
kinds = { issue = 'issues', pr = 'pulls' }, kinds = { issue = 'issues', pr = 'pulls' },
labels = { labels = {
issue = 'Issues', issue = 'Issues',
pr = 'PRs', pr = 'PRs',
pr_one = 'PR', pr_one = 'PR',
pr_full = 'Pull Requests', pr_full = 'Pull Requests',
ci = 'CI/CD', ci = 'CI/CD',
}, },
} }
---@param kind string ---@param kind string
---@param state string ---@param state string
---@return string ---@return string
function M:list_cmd(kind, state) function M:list_cmd(kind, state)
return ('tea %s list --state %s'):format(kind, state) return ('tea %s list --state %s'):format(kind, state)
end end
---@param state string ---@param state string
---@return string[] ---@return string[]
function M:list_pr_json_cmd(state) function M:list_pr_json_cmd(state)
return { return {
'tea', 'tea',
'pulls', 'pulls',
'list', 'list',
'--state', '--state',
state, state,
'--output', '--output',
'json', 'json',
'--fields', '--fields',
'index,title,head,state,poster,created_at', 'index,title,head,state,poster,created_at',
} }
end end
---@param state string ---@param state string
---@return string[] ---@return string[]
function M:list_issue_json_cmd(state) function M:list_issue_json_cmd(state)
return { return {
'tea', 'tea',
'issues', 'issues',
'list', 'list',
'--state', '--state',
state, state,
'--output', '--output',
'json', 'json',
'--fields', '--fields',
'index,title,state,poster,created_at', 'index,title,state,poster,created_at',
} }
end end
function M:pr_json_fields() function M:pr_json_fields()
return { return {
number = 'index', number = 'index',
title = 'title', title = 'title',
branch = 'head', branch = 'head',
state = 'state', state = 'state',
author = 'poster', author = 'poster',
created_at = 'created_at', created_at = 'created_at',
} }
end end
function M:issue_json_fields() function M:issue_json_fields()
return { return {
number = 'index', number = 'index',
title = 'title', title = 'title',
state = 'state', state = 'state',
author = 'poster', author = 'poster',
created_at = 'created_at', created_at = 'created_at',
} }
end end
---@param kind string ---@param kind string
---@param num string ---@param num string
function M:view_web(kind, num) function M:view_web(kind, num)
local base = forge.remote_web_url() local base = forge.remote_web_url()
vim.ui.open(('%s/%s/%s'):format(base, kind, num)) vim.ui.open(('%s/%s/%s'):format(base, kind, num))
end end
---@param loc string ---@param loc string
---@param branch string ---@param branch string
function M:browse(loc, branch) function M:browse(loc, branch)
local base = forge.remote_web_url() local base = forge.remote_web_url()
local file, lines = loc:match('^(.+):(.+)$') local file, lines = loc:match('^(.+):(.+)$')
vim.ui.open(('%s/src/branch/%s/%s#L%s'):format(base, branch, file, lines)) vim.ui.open(('%s/src/branch/%s/%s#L%s'):format(base, branch, file, lines))
end end
function M:browse_root() function M:browse_root()
vim.ui.open(forge.remote_web_url()) vim.ui.open(forge.remote_web_url())
end end
function M:browse_branch(branch) function M:browse_branch(branch)
local base = forge.remote_web_url() local base = forge.remote_web_url()
vim.ui.open(base .. '/src/branch/' .. branch) vim.ui.open(base .. '/src/branch/' .. branch)
end end
function M:browse_commit(sha) function M:browse_commit(sha)
local base = forge.remote_web_url() local base = forge.remote_web_url()
vim.ui.open(base .. '/commit/' .. sha) vim.ui.open(base .. '/commit/' .. sha)
end end
function M:checkout_cmd(num) function M:checkout_cmd(num)
return { 'tea', 'pr', 'checkout', num } return { 'tea', 'pr', 'checkout', num }
end end
---@param loc string ---@param loc string
function M:yank_branch(loc) function M:yank_branch(loc)
local branch = vim.trim(vim.fn.system('git branch --show-current')) local branch = vim.trim(vim.fn.system('git branch --show-current'))
local base = forge.remote_web_url() local base = forge.remote_web_url()
local file, lines = loc:match('^(.+):(.+)$') local file, lines = loc:match('^(.+):(.+)$')
vim.fn.setreg( vim.fn.setreg('+', ('%s/src/branch/%s/%s#L%s'):format(base, branch, file, lines))
'+',
('%s/src/branch/%s/%s#L%s'):format(base, branch, file, lines)
)
end end
---@param loc string ---@param loc string
function M:yank_commit(loc) function M:yank_commit(loc)
local commit = vim.trim(vim.fn.system('git rev-parse HEAD')) local commit = vim.trim(vim.fn.system('git rev-parse HEAD'))
local base = forge.remote_web_url() local base = forge.remote_web_url()
local file, lines = loc:match('^(.+):(.+)$') local file, lines = loc:match('^(.+):(.+)$')
vim.fn.setreg( vim.fn.setreg('+', ('%s/src/commit/%s/%s#L%s'):format(base, commit, file, lines))
'+',
('%s/src/commit/%s/%s#L%s'):format(base, commit, file, lines)
)
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:fetch_pr(num) function M:fetch_pr(num)
return { 'git', 'fetch', 'origin', ('pull/%s/head:pr-%s'):format(num, num) } return { 'git', 'fetch', 'origin', ('pull/%s/head:pr-%s'):format(num, num) }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:pr_base_cmd(num) function M:pr_base_cmd(num)
return { 'tea', 'pr', num, '--fields', 'base', '--output', 'simple' } return { 'tea', 'pr', num, '--fields', 'base', '--output', 'simple' }
end end
---@param branch string ---@param branch string
---@return string[] ---@return string[]
function M:pr_for_branch_cmd(_branch) function M:pr_for_branch_cmd(_branch)
return { return {
'tea', 'tea',
'pr', 'pr',
'list', 'list',
'--fields', '--fields',
'index,head', 'index,head',
'--output', '--output',
'simple', 'simple',
'--state', '--state',
'open', 'open',
} }
end end
---@param num string ---@param num string
---@return string ---@return string
function M:checks_cmd(num) function M:checks_cmd(num)
local _ = num local _ = num
return 'tea actions runs list' return 'tea actions runs list'
end end
---@param run_id string ---@param run_id string
---@param failed_only boolean ---@param failed_only boolean
---@return string[] ---@return string[]
function M:check_log_cmd(run_id, failed_only) function M:check_log_cmd(run_id, failed_only)
local _ = failed_only local _ = failed_only
local lines = forge.config().ci.lines local lines = forge.config().ci.lines
return { return {
'sh', 'sh',
'-c', '-c',
('tea actions runs logs %s | tail -n %d'):format(run_id, lines), ('tea actions runs logs %s | tail -n %d'):format(run_id, lines),
} }
end end
---@param run_id string ---@param run_id string
---@return string[] ---@return string[]
function M:check_tail_cmd(run_id) function M:check_tail_cmd(run_id)
return { 'tea', 'actions', 'runs', 'logs', run_id, '--follow' } return { 'tea', 'actions', 'runs', 'logs', run_id, '--follow' }
end end
function M:list_runs_cmd(_branch) function M:list_runs_cmd(_branch)
return 'tea actions runs list' return 'tea actions runs list'
end end
function M:run_log_cmd(id, failed_only) function M:run_log_cmd(id, failed_only)
local _ = failed_only local _ = failed_only
local lines = forge.config().ci.lines local lines = forge.config().ci.lines
return { return {
'sh', 'sh',
'-c', '-c',
('tea actions runs logs %s | tail -n %d'):format(id, lines), ('tea actions runs logs %s | tail -n %d'):format(id, lines),
} }
end end
function M:run_tail_cmd(id) function M:run_tail_cmd(id)
return { 'tea', 'actions', 'runs', 'logs', id, '--follow' } return { 'tea', 'actions', 'runs', 'logs', id, '--follow' }
end end
---@param num string ---@param num string
---@param method string ---@param method string
---@return string[] ---@return string[]
function M:merge_cmd(num, method) function M:merge_cmd(num, method)
return { 'tea', 'pr', 'merge', num, '--style', method } return { 'tea', 'pr', 'merge', num, '--style', method }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:approve_cmd(num) function M:approve_cmd(num)
return { 'tea', 'pr', 'approve', num } return { 'tea', 'pr', 'approve', num }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:close_cmd(num) function M:close_cmd(num)
return { 'tea', 'pulls', 'close', num } return { 'tea', 'pulls', 'close', num }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:reopen_cmd(num) function M:reopen_cmd(num)
return { 'tea', 'pulls', 'reopen', num } return { 'tea', 'pulls', 'reopen', num }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:close_issue_cmd(num) function M:close_issue_cmd(num)
return { 'tea', 'issues', 'close', num } return { 'tea', 'issues', 'close', num }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:reopen_issue_cmd(num) function M:reopen_issue_cmd(num)
return { 'tea', 'issues', 'reopen', num } return { 'tea', 'issues', 'reopen', num }
end end
---@param title string ---@param title string
@ -245,72 +239,74 @@ end
---@param _reviewers string[]? ---@param _reviewers string[]?
---@return string[] ---@return string[]
function M:create_pr_cmd(title, body, base, _draft, _reviewers) function M:create_pr_cmd(title, body, base, _draft, _reviewers)
return { 'tea', 'pr', 'create', '--title', title, '--description', body, '--base', base } return { 'tea', 'pr', 'create', '--title', title, '--description', body, '--base', base }
end end
---@return string[]? ---@return string[]?
function M:create_pr_web_cmd() function M:create_pr_web_cmd()
local branch = vim.trim(vim.fn.system('git branch --show-current')) local branch = vim.trim(vim.fn.system('git branch --show-current'))
local base_url = forge.remote_web_url() local base_url = forge.remote_web_url()
local default = vim.trim(vim.fn.system( local default = vim.trim(
"git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||'" vim.fn.system(
)) "git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||'"
if default == '' then )
default = 'main' )
end if default == '' then
vim.ui.open(('%s/compare/%s...%s'):format(base_url, default, branch)) default = 'main'
return nil end
vim.ui.open(('%s/compare/%s...%s'):format(base_url, default, branch))
return nil
end end
---@return string[] ---@return string[]
function M:default_branch_cmd() function M:default_branch_cmd()
return { return {
'sh', '-c', 'sh',
"git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||'", '-c',
} "git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||'",
}
end end
---@return string[] ---@return string[]
function M:template_paths() function M:template_paths()
return { return {
'.gitea/pull_request_template.md', '.gitea/pull_request_template.md',
'.github/pull_request_template.md', '.github/pull_request_template.md',
'.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE.md',
} }
end end
---@param _num string ---@param _num string
---@param _is_draft boolean ---@param _is_draft boolean
---@return string[]? ---@return string[]?
function M:draft_toggle_cmd(_num, _is_draft) function M:draft_toggle_cmd(_num, _is_draft)
return nil return nil
end end
---@return forge.RepoInfo ---@return forge.RepoInfo
function M:repo_info() function M:repo_info()
return { return {
permission = 'ADMIN', permission = 'ADMIN',
merge_methods = { 'squash', 'rebase', 'merge' }, merge_methods = { 'squash', 'rebase', 'merge' },
} }
end end
---@param num string ---@param num string
---@return forge.PRState ---@return forge.PRState
function M:pr_state(num) function M:pr_state(num)
local result = vim.system( local result = vim
{ 'tea', 'pr', num, '--fields', 'state,mergeable', '--output', 'json' }, .system({ 'tea', 'pr', num, '--fields', 'state,mergeable', '--output', 'json' }, { text = true })
{ text = true } :wait()
):wait() local ok, data = pcall(vim.json.decode, result.stdout or '{}')
local ok, data = pcall(vim.json.decode, result.stdout or '{}') if not ok or type(data) ~= 'table' then
if not ok or type(data) ~= 'table' then data = {}
data = {} end
end return {
return { state = (data.state or 'unknown'):upper(),
state = (data.state or 'unknown'):upper(), mergeable = data.mergeable and 'MERGEABLE' or 'UNKNOWN',
mergeable = data.mergeable and 'MERGEABLE' or 'UNKNOWN', review_decision = '',
review_decision = '', is_draft = false,
is_draft = false, }
}
end end
return M return M

View file

@ -2,283 +2,278 @@ local forge = require('forge')
---@type forge.Forge ---@type forge.Forge
local M = { local M = {
name = 'github', name = 'github',
cli = 'gh', cli = 'gh',
kinds = { issue = 'issue', pr = 'pr' }, kinds = { issue = 'issue', pr = 'pr' },
labels = { labels = {
issue = 'Issues', issue = 'Issues',
pr = 'PRs', pr = 'PRs',
pr_one = 'PR', pr_one = 'PR',
pr_full = 'Pull Requests', pr_full = 'Pull Requests',
ci = 'CI/CD', ci = 'CI/CD',
}, },
} }
local function nwo() local function nwo()
local url = forge.remote_web_url() local url = forge.remote_web_url()
return url:match('github%.com/(.+)$') or '' return url:match('github%.com/(.+)$') or ''
end end
---@param kind string ---@param kind string
---@param state string ---@param state string
---@return string ---@return string
function M:list_cmd(kind, state) function M:list_cmd(kind, state)
return ('gh %s list --limit 100 --state %s'):format(kind, state) return ('gh %s list --limit 100 --state %s'):format(kind, state)
end end
---@param state string ---@param state string
---@return string[] ---@return string[]
function M:list_pr_json_cmd(state) function M:list_pr_json_cmd(state)
return { return {
'gh', 'gh',
'pr', 'pr',
'list', 'list',
'--limit', '--limit',
'100', '100',
'--state', '--state',
state, state,
'--json', '--json',
'number,title,headRefName,state,author,createdAt', 'number,title,headRefName,state,author,createdAt',
} }
end end
---@param state string ---@param state string
---@return string[] ---@return string[]
function M:list_issue_json_cmd(state) function M:list_issue_json_cmd(state)
return { return {
'gh', 'gh',
'issue', 'issue',
'list', 'list',
'--limit', '--limit',
'100', '100',
'--state', '--state',
state, state,
'--json', '--json',
'number,title,state,author,createdAt', 'number,title,state,author,createdAt',
} }
end end
function M:pr_json_fields() function M:pr_json_fields()
return { return {
number = 'number', number = 'number',
title = 'title', title = 'title',
branch = 'headRefName', branch = 'headRefName',
state = 'state', state = 'state',
author = 'author', author = 'author',
created_at = 'createdAt', created_at = 'createdAt',
} }
end end
function M:issue_json_fields() function M:issue_json_fields()
return { return {
number = 'number', number = 'number',
title = 'title', title = 'title',
state = 'state', state = 'state',
author = 'author', author = 'author',
created_at = 'createdAt', created_at = 'createdAt',
} }
end end
---@param kind string ---@param kind string
---@param num string ---@param num string
function M:view_web(kind, num) function M:view_web(kind, num)
vim.system({ 'gh', kind, 'view', num, '--web' }) vim.system({ 'gh', kind, 'view', num, '--web' })
end end
---@param loc string ---@param loc string
---@param branch string ---@param branch string
function M:browse(loc, branch) function M:browse(loc, branch)
vim.system({ 'gh', 'browse', loc, '--branch', branch }) vim.system({ 'gh', 'browse', loc, '--branch', branch })
end end
function M:browse_root() function M:browse_root()
vim.system({ 'gh', 'browse' }) vim.system({ 'gh', 'browse' })
end end
function M:browse_branch(branch) function M:browse_branch(branch)
vim.system({ 'gh', 'browse', '--branch', branch }) vim.system({ 'gh', 'browse', '--branch', branch })
end end
function M:browse_commit(sha) function M:browse_commit(sha)
vim.system({ 'gh', 'browse', sha }) vim.system({ 'gh', 'browse', sha })
end end
function M:checkout_cmd(num) function M:checkout_cmd(num)
return { 'gh', 'pr', 'checkout', num } return { 'gh', 'pr', 'checkout', num }
end end
---@param loc string ---@param loc string
function M:yank_branch(loc) function M:yank_branch(loc)
forge.yank_url({ 'gh', 'browse', loc, '-n' }) forge.yank_url({ 'gh', 'browse', loc, '-n' })
end end
---@param loc string ---@param loc string
function M:yank_commit(loc) function M:yank_commit(loc)
forge.yank_url({ 'gh', 'browse', loc, '--commit=last', '-n' }) forge.yank_url({ 'gh', 'browse', loc, '--commit=last', '-n' })
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:fetch_pr(num) function M:fetch_pr(num)
return { 'git', 'fetch', 'origin', ('pull/%s/head:pr-%s'):format(num, num) } return { 'git', 'fetch', 'origin', ('pull/%s/head:pr-%s'):format(num, num) }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:pr_base_cmd(num) function M:pr_base_cmd(num)
return { return {
'gh', 'gh',
'pr', 'pr',
'view', 'view',
num, num,
'--json', '--json',
'baseRefName', 'baseRefName',
'--jq', '--jq',
'.baseRefName', '.baseRefName',
} }
end end
---@param branch string ---@param branch string
---@return string[] ---@return string[]
function M:pr_for_branch_cmd(branch) function M:pr_for_branch_cmd(branch)
return { return {
'gh', 'gh',
'pr', 'pr',
'list', 'list',
'--head', '--head',
branch, branch,
'--json', '--json',
'number', 'number',
'--jq', '--jq',
'.[0].number', '.[0].number',
} }
end end
---@param num string ---@param num string
---@return string ---@return string
function M:checks_cmd(num) function M:checks_cmd(num)
return ('gh pr checks %s'):format(num) return ('gh pr checks %s'):format(num)
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:checks_json_cmd(num) function M:checks_json_cmd(num)
return { return {
'gh', 'gh',
'pr', 'pr',
'checks', 'checks',
num, num,
'--json', '--json',
'name,bucket,link,state,startedAt,completedAt', 'name,bucket,link,state,startedAt,completedAt',
} }
end end
---@param run_id string ---@param run_id string
---@param failed_only boolean ---@param failed_only boolean
---@return string[] ---@return string[]
function M:check_log_cmd(run_id, failed_only) function M:check_log_cmd(run_id, failed_only)
local lines = forge.config().ci.lines local lines = forge.config().ci.lines
local flag = failed_only and '--log-failed' or '--log' local flag = failed_only and '--log-failed' or '--log'
return { return {
'sh', 'sh',
'-c', '-c',
('gh run view %s -R %s %s | tail -n %d'):format( ('gh run view %s -R %s %s | tail -n %d'):format(run_id, nwo(), flag, lines),
run_id, }
nwo(),
flag,
lines
),
}
end end
---@param run_id string ---@param run_id string
---@return string[] ---@return string[]
function M:check_tail_cmd(run_id) function M:check_tail_cmd(run_id)
return { 'gh', 'run', 'watch', run_id, '-R', nwo() } return { 'gh', 'run', 'watch', run_id, '-R', nwo() }
end end
function M:list_runs_json_cmd(branch) function M:list_runs_json_cmd(branch)
local cmd = { local cmd = {
'gh', 'gh',
'run', 'run',
'list', 'list',
'--json', '--json',
'databaseId,name,headBranch,status,conclusion,event,url,createdAt', 'databaseId,name,headBranch,status,conclusion,event,url,createdAt',
'--limit', '--limit',
'30', '30',
} }
if branch then if branch then
table.insert(cmd, '--branch') table.insert(cmd, '--branch')
table.insert(cmd, branch) table.insert(cmd, branch)
end end
return cmd return cmd
end end
function M:normalize_run(entry) function M:normalize_run(entry)
local status = entry.status or '' local status = entry.status or ''
if status == 'completed' then if status == 'completed' then
status = entry.conclusion or 'unknown' status = entry.conclusion or 'unknown'
end end
return { return {
id = tostring(entry.databaseId or ''), id = tostring(entry.databaseId or ''),
name = entry.name or '', name = entry.name or '',
branch = entry.headBranch or '', branch = entry.headBranch or '',
status = status, status = status,
event = entry.event or '', event = entry.event or '',
url = entry.url or '', url = entry.url or '',
created_at = entry.createdAt or '', created_at = entry.createdAt or '',
} }
end end
function M:run_log_cmd(id, failed_only) function M:run_log_cmd(id, failed_only)
local lines = forge.config().ci.lines local lines = forge.config().ci.lines
local flag = failed_only and '--log-failed' or '--log' local flag = failed_only and '--log-failed' or '--log'
return { return {
'sh', 'sh',
'-c', '-c',
('gh run view %s -R %s %s | tail -n %d'):format(id, nwo(), flag, lines), ('gh run view %s -R %s %s | tail -n %d'):format(id, nwo(), flag, lines),
} }
end end
function M:run_tail_cmd(id) function M:run_tail_cmd(id)
return { 'gh', 'run', 'watch', id, '-R', nwo() } return { 'gh', 'run', 'watch', id, '-R', nwo() }
end end
---@param num string ---@param num string
---@param method string ---@param method string
---@return string[] ---@return string[]
function M:merge_cmd(num, method) function M:merge_cmd(num, method)
return { 'gh', 'pr', 'merge', num, '--' .. method } return { 'gh', 'pr', 'merge', num, '--' .. method }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:approve_cmd(num) function M:approve_cmd(num)
return { 'gh', 'pr', 'review', num, '--approve' } return { 'gh', 'pr', 'review', num, '--approve' }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:close_cmd(num) function M:close_cmd(num)
return { 'gh', 'pr', 'close', num } return { 'gh', 'pr', 'close', num }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:reopen_cmd(num) function M:reopen_cmd(num)
return { 'gh', 'pr', 'reopen', num } return { 'gh', 'pr', 'reopen', num }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:close_issue_cmd(num) function M:close_issue_cmd(num)
return { 'gh', 'issue', 'close', num } return { 'gh', 'issue', 'close', num }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:reopen_issue_cmd(num) function M:reopen_issue_cmd(num)
return { 'gh', 'issue', 'reopen', num } return { 'gh', 'issue', 'reopen', num }
end end
---@param title string ---@param title string
@ -288,100 +283,104 @@ end
---@param reviewers string[]? ---@param reviewers string[]?
---@return string[] ---@return string[]
function M:create_pr_cmd(title, body, base, draft, reviewers) function M:create_pr_cmd(title, body, base, draft, reviewers)
local cmd = { 'gh', 'pr', 'create', '--title', title, '--body', body, '--base', base } local cmd = { 'gh', 'pr', 'create', '--title', title, '--body', body, '--base', base }
if draft then if draft then
table.insert(cmd, '--draft') table.insert(cmd, '--draft')
end end
for _, r in ipairs(reviewers or {}) do for _, r in ipairs(reviewers or {}) do
table.insert(cmd, '--reviewer') table.insert(cmd, '--reviewer')
table.insert(cmd, r) table.insert(cmd, r)
end end
return cmd return cmd
end end
---@return string[] ---@return string[]
function M:create_pr_web_cmd() function M:create_pr_web_cmd()
return { 'gh', 'pr', 'create', '--web' } return { 'gh', 'pr', 'create', '--web' }
end end
---@return string[] ---@return string[]
function M:default_branch_cmd() function M:default_branch_cmd()
return { 'gh', 'repo', 'view', '--json', 'defaultBranchRef', '--jq', '.defaultBranchRef.name' } return { 'gh', 'repo', 'view', '--json', 'defaultBranchRef', '--jq', '.defaultBranchRef.name' }
end end
---@return string[] ---@return string[]
function M:template_paths() function M:template_paths()
return { return {
'.github/pull_request_template.md', '.github/pull_request_template.md',
'.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE.md',
'.github/PULL_REQUEST_TEMPLATE/', '.github/PULL_REQUEST_TEMPLATE/',
} }
end end
---@param num string ---@param num string
---@param is_draft boolean ---@param is_draft boolean
---@return string[]? ---@return string[]?
function M:draft_toggle_cmd(num, is_draft) function M:draft_toggle_cmd(num, is_draft)
if is_draft then if is_draft then
return { 'gh', 'pr', 'ready', num } return { 'gh', 'pr', 'ready', num }
end end
return { 'gh', 'pr', 'ready', num, '--undo' } return { 'gh', 'pr', 'ready', num, '--undo' }
end end
---@return forge.RepoInfo ---@return forge.RepoInfo
function M:repo_info() function M:repo_info()
local result = vim.system({ local result = vim
'gh', .system({
'repo', 'gh',
'view', 'repo',
nwo(), 'view',
'--json', nwo(),
'viewerPermission,squashMergeAllowed,rebaseMergeAllowed,mergeCommitAllowed', '--json',
}, { text = true }):wait() 'viewerPermission,squashMergeAllowed,rebaseMergeAllowed,mergeCommitAllowed',
}, { text = true })
:wait()
local ok, data = pcall(vim.json.decode, result.stdout or '{}') local ok, data = pcall(vim.json.decode, result.stdout or '{}')
if not ok or type(data) ~= 'table' then if not ok or type(data) ~= 'table' then
data = {} data = {}
end end
local methods = {} local methods = {}
if data.squashMergeAllowed then if data.squashMergeAllowed then
table.insert(methods, 'squash') table.insert(methods, 'squash')
end end
if data.rebaseMergeAllowed then if data.rebaseMergeAllowed then
table.insert(methods, 'rebase') table.insert(methods, 'rebase')
end end
if data.mergeCommitAllowed then if data.mergeCommitAllowed then
table.insert(methods, 'merge') table.insert(methods, 'merge')
end end
return { return {
permission = (data.viewerPermission or 'READ'):upper(), permission = (data.viewerPermission or 'READ'):upper(),
merge_methods = methods, merge_methods = methods,
} }
end end
---@param num string ---@param num string
---@return forge.PRState ---@return forge.PRState
function M:pr_state(num) function M:pr_state(num)
local result = vim.system({ local result = vim
'gh', .system({
'pr', 'gh',
'view', 'pr',
num, 'view',
'--json', num,
'state,mergeable,reviewDecision,isDraft', '--json',
}, { text = true }):wait() 'state,mergeable,reviewDecision,isDraft',
}, { text = true })
:wait()
local ok, data = pcall(vim.json.decode, result.stdout or '{}') local ok, data = pcall(vim.json.decode, result.stdout or '{}')
if not ok or type(data) ~= 'table' then if not ok or type(data) ~= 'table' then
data = {} data = {}
end end
return { return {
state = data.state or 'UNKNOWN', state = data.state or 'UNKNOWN',
mergeable = data.mergeable or 'UNKNOWN', mergeable = data.mergeable or 'UNKNOWN',
review_decision = data.reviewDecision or '', review_decision = data.reviewDecision or '',
is_draft = data.isDraft == true, is_draft = data.isDraft == true,
} }
end end
return M return M

View file

@ -2,306 +2,296 @@ local forge = require('forge')
---@type forge.Forge ---@type forge.Forge
local M = { local M = {
name = 'gitlab', name = 'gitlab',
cli = 'glab', cli = 'glab',
kinds = { issue = 'issue', pr = 'mr' }, kinds = { issue = 'issue', pr = 'mr' },
labels = { labels = {
issue = 'Issues', issue = 'Issues',
pr = 'MRs', pr = 'MRs',
pr_one = 'MR', pr_one = 'MR',
pr_full = 'Merge Requests', pr_full = 'Merge Requests',
ci = 'CI/CD', ci = 'CI/CD',
}, },
} }
---@param kind string ---@param kind string
---@param state string ---@param state string
---@return string ---@return string
function M:list_cmd(kind, state) function M:list_cmd(kind, state)
local cmd = ('glab %s list --per-page 100'):format(kind) local cmd = ('glab %s list --per-page 100'):format(kind)
if state == 'closed' then if state == 'closed' then
cmd = cmd .. ' --closed' cmd = cmd .. ' --closed'
elseif state == 'all' then elseif state == 'all' then
cmd = cmd .. ' --all' cmd = cmd .. ' --all'
end end
return cmd return cmd
end end
---@param state string ---@param state string
---@return string[] ---@return string[]
function M:list_pr_json_cmd(state) function M:list_pr_json_cmd(state)
local cmd = { local cmd = {
'glab', 'glab',
'mr', 'mr',
'list', 'list',
'--per-page', '--per-page',
'100', '100',
'--output', '--output',
'json', 'json',
} }
if state == 'closed' then if state == 'closed' then
table.insert(cmd, '--closed') table.insert(cmd, '--closed')
elseif state == 'all' then elseif state == 'all' then
table.insert(cmd, '--all') table.insert(cmd, '--all')
end end
return cmd return cmd
end end
---@param state string ---@param state string
---@return string[] ---@return string[]
function M:list_issue_json_cmd(state) function M:list_issue_json_cmd(state)
local cmd = { local cmd = {
'glab', 'glab',
'issue', 'issue',
'list', 'list',
'--per-page', '--per-page',
'100', '100',
'--output', '--output',
'json', 'json',
} }
if state == 'closed' then if state == 'closed' then
table.insert(cmd, '--closed') table.insert(cmd, '--closed')
elseif state == 'all' then elseif state == 'all' then
table.insert(cmd, '--all') table.insert(cmd, '--all')
end end
return cmd return cmd
end end
function M:pr_json_fields() function M:pr_json_fields()
return { return {
number = 'iid', number = 'iid',
title = 'title', title = 'title',
branch = 'source_branch', branch = 'source_branch',
state = 'state', state = 'state',
author = 'author', author = 'author',
created_at = 'created_at', created_at = 'created_at',
} }
end end
function M:issue_json_fields() function M:issue_json_fields()
return { return {
number = 'iid', number = 'iid',
title = 'title', title = 'title',
state = 'state', state = 'state',
author = 'author', author = 'author',
created_at = 'created_at', created_at = 'created_at',
} }
end end
---@param kind string ---@param kind string
---@param num string ---@param num string
function M:view_web(kind, num) function M:view_web(kind, num)
vim.system({ 'glab', kind, 'view', num, '--web' }) vim.system({ 'glab', kind, 'view', num, '--web' })
end end
---@param loc string ---@param loc string
---@param branch string ---@param branch string
function M:browse(loc, branch) function M:browse(loc, branch)
local base = forge.remote_web_url() local base = forge.remote_web_url()
local file, lines = loc:match('^(.+):(.+)$') local file, lines = loc:match('^(.+):(.+)$')
vim.ui.open(('%s/-/blob/%s/%s#L%s'):format(base, branch, file, lines)) vim.ui.open(('%s/-/blob/%s/%s#L%s'):format(base, branch, file, lines))
end end
function M:browse_root() function M:browse_root()
vim.system({ 'glab', 'repo', 'view', '--web' }) vim.system({ 'glab', 'repo', 'view', '--web' })
end end
function M:browse_branch(branch) function M:browse_branch(branch)
local base = forge.remote_web_url() local base = forge.remote_web_url()
vim.ui.open(base .. '/-/tree/' .. branch) vim.ui.open(base .. '/-/tree/' .. branch)
end end
function M:browse_commit(sha) function M:browse_commit(sha)
local base = forge.remote_web_url() local base = forge.remote_web_url()
vim.ui.open(base .. '/-/commit/' .. sha) vim.ui.open(base .. '/-/commit/' .. sha)
end end
function M:checkout_cmd(num) function M:checkout_cmd(num)
return { 'glab', 'mr', 'checkout', num } return { 'glab', 'mr', 'checkout', num }
end end
---@param loc string ---@param loc string
function M:yank_branch(loc) function M:yank_branch(loc)
local branch = vim.trim(vim.fn.system('git branch --show-current')) local branch = vim.trim(vim.fn.system('git branch --show-current'))
local base = forge.remote_web_url() local base = forge.remote_web_url()
local file, lines = loc:match('^(.+):(.+)$') local file, lines = loc:match('^(.+):(.+)$')
vim.fn.setreg( vim.fn.setreg('+', ('%s/-/blob/%s/%s#L%s'):format(base, branch, file, lines))
'+',
('%s/-/blob/%s/%s#L%s'):format(base, branch, file, lines)
)
end end
---@param loc string ---@param loc string
function M:yank_commit(loc) function M:yank_commit(loc)
local commit = vim.trim(vim.fn.system('git rev-parse HEAD')) local commit = vim.trim(vim.fn.system('git rev-parse HEAD'))
local base = forge.remote_web_url() local base = forge.remote_web_url()
local file, lines = loc:match('^(.+):(.+)$') local file, lines = loc:match('^(.+):(.+)$')
vim.fn.setreg( vim.fn.setreg('+', ('%s/-/blob/%s/%s#L%s'):format(base, commit, file, lines))
'+',
('%s/-/blob/%s/%s#L%s'):format(base, commit, file, lines)
)
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:fetch_pr(num) function M:fetch_pr(num)
return { return {
'git', 'git',
'fetch', 'fetch',
'origin', 'origin',
('merge-requests/%s/head:mr-%s'):format(num, num), ('merge-requests/%s/head:mr-%s'):format(num, num),
} }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:pr_base_cmd(num) function M:pr_base_cmd(num)
return { return {
'sh', 'sh',
'-c', '-c',
('glab mr view %s -F json | jq -r .target_branch'):format(num), ('glab mr view %s -F json | jq -r .target_branch'):format(num),
} }
end end
---@param branch string ---@param branch string
---@return string[] ---@return string[]
function M:pr_for_branch_cmd(branch) function M:pr_for_branch_cmd(branch)
return { return {
'sh', 'sh',
'-c', '-c',
("glab mr list --source-branch '%s' -F json | jq -r '.[0].iid // empty'"):format( ("glab mr list --source-branch '%s' -F json | jq -r '.[0].iid // empty'"):format(branch),
branch }
),
}
end end
---@param num string ---@param num string
---@return string ---@return string
function M:checks_cmd(num) function M:checks_cmd(num)
local _ = num local _ = num
return 'glab ci list' return 'glab ci list'
end end
---@param run_id string ---@param run_id string
---@param failed_only boolean ---@param failed_only boolean
---@return string[] ---@return string[]
function M:check_log_cmd(run_id, failed_only) function M:check_log_cmd(run_id, failed_only)
local _ = failed_only local _ = failed_only
local lines = forge.config().ci.lines local lines = forge.config().ci.lines
return { return {
'sh', 'sh',
'-c', '-c',
('glab ci trace %s | tail -n %d'):format(run_id, lines), ('glab ci trace %s | tail -n %d'):format(run_id, lines),
} }
end end
---@param run_id string ---@param run_id string
---@return string[] ---@return string[]
function M:check_tail_cmd(run_id) function M:check_tail_cmd(run_id)
return { 'glab', 'ci', 'trace', run_id } return { 'glab', 'ci', 'trace', run_id }
end end
function M:list_runs_json_cmd(branch) function M:list_runs_json_cmd(branch)
local cmd = { local cmd = {
'glab', 'glab',
'ci', 'ci',
'list', 'list',
'--output', '--output',
'json', 'json',
'--per-page', '--per-page',
'30', '30',
} }
if branch then if branch then
table.insert(cmd, '--ref') table.insert(cmd, '--ref')
table.insert(cmd, branch) table.insert(cmd, branch)
end end
return cmd return cmd
end end
function M:normalize_run(entry) function M:normalize_run(entry)
local ref = entry.ref or '' local ref = entry.ref or ''
local mr_num = ref:match('^refs/merge%-requests/(%d+)/head$') local mr_num = ref:match('^refs/merge%-requests/(%d+)/head$')
return { return {
id = tostring(entry.id or ''), id = tostring(entry.id or ''),
name = mr_num and ('!%s'):format(mr_num) or ref, name = mr_num and ('!%s'):format(mr_num) or ref,
branch = '', branch = '',
status = entry.status or '', status = entry.status or '',
event = entry.source or '', event = entry.source or '',
url = entry.web_url or '', url = entry.web_url or '',
created_at = entry.created_at or '', created_at = entry.created_at or '',
} }
end end
function M:run_log_cmd(id, failed_only) function M:run_log_cmd(id, failed_only)
local lines = forge.config().ci.lines local lines = forge.config().ci.lines
local jq_filter = failed_only local jq_filter = failed_only and '[.[] | select(.status=="failed")][0].id // .[0].id'
and '[.[] | select(.status=="failed")][0].id // .[0].id' or '.[0].id'
or '.[0].id' return {
return { 'sh',
'sh', '-c',
'-c', ('JOB=$(glab api \'projects/:id/pipelines/%s/jobs?per_page=100\' | jq -r \'%s\') && [ "$JOB" != "null" ] && glab ci trace "$JOB" | tail -n %d'):format(
('JOB=$(glab api \'projects/:id/pipelines/%s/jobs?per_page=100\' | jq -r \'%s\') && [ "$JOB" != "null" ] && glab ci trace "$JOB" | tail -n %d'):format( id,
id, jq_filter,
jq_filter, lines
lines ),
), }
}
end end
function M:run_tail_cmd(id) function M:run_tail_cmd(id)
local jq_filter = local jq_filter = '[.[] | select(.status=="running" or .status=="pending")][0].id // .[0].id'
'[.[] | select(.status=="running" or .status=="pending")][0].id // .[0].id' return {
return { 'sh',
'sh', '-c',
'-c', ('JOB=$(glab api \'projects/:id/pipelines/%s/jobs?per_page=100\' | jq -r \'%s\') && [ "$JOB" != "null" ] && glab ci trace "$JOB"'):format(
('JOB=$(glab api \'projects/:id/pipelines/%s/jobs?per_page=100\' | jq -r \'%s\') && [ "$JOB" != "null" ] && glab ci trace "$JOB"'):format( id,
id, jq_filter
jq_filter ),
), }
}
end end
---@param num string ---@param num string
---@param method string ---@param method string
---@return string[] ---@return string[]
function M:merge_cmd(num, method) function M:merge_cmd(num, method)
local cmd = { 'glab', 'mr', 'merge', num } local cmd = { 'glab', 'mr', 'merge', num }
if method == 'squash' then if method == 'squash' then
table.insert(cmd, '--squash') table.insert(cmd, '--squash')
elseif method == 'rebase' then elseif method == 'rebase' then
table.insert(cmd, '--rebase') table.insert(cmd, '--rebase')
end end
return cmd return cmd
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:approve_cmd(num) function M:approve_cmd(num)
return { 'glab', 'mr', 'approve', num } return { 'glab', 'mr', 'approve', num }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:close_cmd(num) function M:close_cmd(num)
return { 'glab', 'mr', 'close', num } return { 'glab', 'mr', 'close', num }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:reopen_cmd(num) function M:reopen_cmd(num)
return { 'glab', 'mr', 'reopen', num } return { 'glab', 'mr', 'reopen', num }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:close_issue_cmd(num) function M:close_issue_cmd(num)
return { 'glab', 'issue', 'close', num } return { 'glab', 'issue', 'close', num }
end end
---@param num string ---@param num string
---@return string[] ---@return string[]
function M:reopen_issue_cmd(num) function M:reopen_issue_cmd(num)
return { 'glab', 'issue', 'reopen', num } return { 'glab', 'issue', 'reopen', num }
end end
---@param title string ---@param title string
@ -311,111 +301,111 @@ end
---@param reviewers string[]? ---@param reviewers string[]?
---@return string[] ---@return string[]
function M:create_pr_cmd(title, body, base, draft, reviewers) function M:create_pr_cmd(title, body, base, draft, reviewers)
local cmd = { local cmd = {
'glab', 'mr', 'create', 'glab',
'--title', title, 'mr',
'--description', body, 'create',
'--target-branch', base, '--title',
'--yes', title,
} '--description',
if draft then body,
table.insert(cmd, '--draft') '--target-branch',
end base,
for _, r in ipairs(reviewers or {}) do '--yes',
table.insert(cmd, '--reviewer') }
table.insert(cmd, r) if draft then
end table.insert(cmd, '--draft')
return cmd end
for _, r in ipairs(reviewers or {}) do
table.insert(cmd, '--reviewer')
table.insert(cmd, r)
end
return cmd
end end
---@return string[] ---@return string[]
function M:create_pr_web_cmd() function M:create_pr_web_cmd()
return { 'glab', 'mr', 'create', '--web' } return { 'glab', 'mr', 'create', '--web' }
end end
---@return string[] ---@return string[]
function M:default_branch_cmd() function M:default_branch_cmd()
return { return {
'sh', '-c', 'sh',
"glab repo view -F json | jq -r '.default_branch'", '-c',
} "glab repo view -F json | jq -r '.default_branch'",
}
end end
---@return string[] ---@return string[]
function M:template_paths() function M:template_paths()
return { '.gitlab/merge_request_templates/' } return { '.gitlab/merge_request_templates/' }
end end
---@param num string ---@param num string
---@param is_draft boolean ---@param is_draft boolean
---@return string[]? ---@return string[]?
function M:draft_toggle_cmd(num, is_draft) function M:draft_toggle_cmd(num, is_draft)
if is_draft then if is_draft then
return { 'glab', 'mr', 'update', num, '--ready' } return { 'glab', 'mr', 'update', num, '--ready' }
end end
return { 'glab', 'mr', 'update', num, '--draft' } return { 'glab', 'mr', 'update', num, '--draft' }
end end
---@return forge.RepoInfo ---@return forge.RepoInfo
function M:repo_info() function M:repo_info()
local result = vim.system( local result = vim.system({ 'glab', 'api', 'projects/:id' }, { text = true }):wait()
{ 'glab', 'api', 'projects/:id' }, local ok, data = pcall(vim.json.decode, result.stdout or '{}')
{ text = true } if not ok or type(data) ~= 'table' then
) data = {}
:wait() end
local ok, data = pcall(vim.json.decode, result.stdout or '{}') local perms = type(data.permissions) == 'table' and data.permissions or {}
if not ok or type(data) ~= 'table' then local pa = type(perms.project_access) == 'table' and perms.project_access or {}
data = {} local ga = type(perms.group_access) == 'table' and perms.group_access or {}
end local access = pa.access_level or 0
local perms = type(data.permissions) == 'table' and data.permissions or {} local group_access = ga.access_level or 0
local pa = type(perms.project_access) == 'table' and perms.project_access local level = math.max(access, group_access)
or {}
local ga = type(perms.group_access) == 'table' and perms.group_access or {}
local access = pa.access_level or 0
local group_access = ga.access_level or 0
local level = math.max(access, group_access)
local permission = 'READ' local permission = 'READ'
if level >= 40 then if level >= 40 then
permission = 'ADMIN' permission = 'ADMIN'
elseif level >= 30 then elseif level >= 30 then
permission = 'WRITE' permission = 'WRITE'
end end
local methods = {} local methods = {}
local merge_method = data.merge_method or 'merge' local merge_method = data.merge_method or 'merge'
if merge_method == 'ff' or merge_method == 'rebase_merge' then if merge_method == 'ff' or merge_method == 'rebase_merge' then
table.insert(methods, 'rebase') table.insert(methods, 'rebase')
else else
table.insert(methods, 'merge') table.insert(methods, 'merge')
end end
if data.squash_option ~= 'never' then if data.squash_option ~= 'never' then
table.insert(methods, 'squash') table.insert(methods, 'squash')
end end
return { return {
permission = permission, permission = permission,
merge_methods = methods, merge_methods = methods,
} }
end end
---@param num string ---@param num string
---@return forge.PRState ---@return forge.PRState
function M:pr_state(num) function M:pr_state(num)
local result = vim.system( local result = vim
{ 'glab', 'mr', 'view', num, '--output', 'json' }, .system({ 'glab', 'mr', 'view', num, '--output', 'json' }, { text = true })
{ text = true } :wait()
):wait() local ok, data = pcall(vim.json.decode, result.stdout or '{}')
local ok, data = pcall(vim.json.decode, result.stdout or '{}') if not ok or type(data) ~= 'table' then
if not ok or type(data) ~= 'table' then data = {}
data = {} end
end return {
return { state = (data.state or 'unknown'):upper(),
state = (data.state or 'unknown'):upper(), mergeable = data.merge_status or 'unknown',
mergeable = data.merge_status or 'unknown', review_decision = '',
review_decision = '', is_draft = data.draft == true,
is_draft = data.draft == true, }
}
end end
return M return M

View file

@ -1,47 +1,47 @@
local M = {} local M = {}
function M.check() function M.check()
vim.health.start('forge.nvim') vim.health.start('forge.nvim')
if vim.fn.executable('git') == 1 then if vim.fn.executable('git') == 1 then
vim.health.ok('git found') vim.health.ok('git found')
else
vim.health.error('git not found')
end
local clis = {
{ 'gh', 'GitHub' },
{ 'glab', 'GitLab' },
{ 'tea', 'Codeberg/Gitea/Forgejo' },
}
for _, cli in ipairs(clis) do
if vim.fn.executable(cli[1]) == 1 then
vim.health.ok(cli[1] .. ' found (' .. cli[2] .. ')')
else else
vim.health.error('git not found') vim.health.info(cli[1] .. ' not found (' .. cli[2] .. ' support disabled)')
end end
end
local clis = { local has_fzf = pcall(require, 'fzf-lua')
{ 'gh', 'GitHub' }, if has_fzf then
{ 'glab', 'GitLab' }, vim.health.ok('fzf-lua found')
{ 'tea', 'Codeberg/Gitea/Forgejo' }, else
} vim.health.error('fzf-lua not found (required)')
for _, cli in ipairs(clis) do end
if vim.fn.executable(cli[1]) == 1 then
vim.health.ok(cli[1] .. ' found (' .. cli[2] .. ')')
else
vim.health.info(cli[1] .. ' not found (' .. cli[2] .. ' support disabled)')
end
end
local has_fzf = pcall(require, 'fzf-lua') local has_diffs = pcall(require, 'diffs')
if has_fzf then if has_diffs then
vim.health.ok('fzf-lua found') vim.health.ok('diffs.nvim found (review mode available)')
else else
vim.health.error('fzf-lua not found (required)') vim.health.info('diffs.nvim not found (review mode disabled)')
end end
local has_diffs = pcall(require, 'diffs') local has_fugitive = vim.fn.exists(':Git') == 2
if has_diffs then if has_fugitive then
vim.health.ok('diffs.nvim found (review mode available)') vim.health.ok('vim-fugitive found (fugitive keymaps available)')
else else
vim.health.info('diffs.nvim not found (review mode disabled)') vim.health.info('vim-fugitive not found (fugitive keymaps disabled)')
end end
local has_fugitive = vim.fn.exists(':Git') == 2
if has_fugitive then
vim.health.ok('vim-fugitive found (fugitive keymaps available)')
else
vim.health.info('vim-fugitive not found (fugitive keymaps disabled)')
end
end end
return M return M

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff