mirror of
https://github.com/harivansh-afk/forge.nvim.git
synced 2026-04-15 01:00:30 +00:00
feat: initial commit
This commit is contained in:
commit
c4da2cda2a
23 changed files with 3743 additions and 0 deletions
9
.editorconfig
Normal file
9
.editorconfig
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
|
||||
[*.lua]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
17
.github/DISCUSSION_TEMPLATE/q-a.yaml
vendored
Normal file
17
.github/DISCUSSION_TEMPLATE/q-a.yaml
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
title: 'Q&A'
|
||||
labels: []
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Use this space for questions, ideas, and general discussion about forge.nvim.
|
||||
For bug reports, please [open an issue](https://github.com/barrettruth/forge.nvim/issues/new/choose) instead.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Question or topic
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Context
|
||||
description: Any relevant details (Neovim version, config, screenshots)
|
||||
60
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
60
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
name: Bug Report
|
||||
description: Report a bug
|
||||
title: 'bug: '
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Prerequisites
|
||||
options:
|
||||
- label:
|
||||
I have searched [existing
|
||||
issues](https://github.com/barrettruth/forge.nvim/issues)
|
||||
required: true
|
||||
- label: I have updated to the latest version
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 'Neovim version'
|
||||
description: 'Output of `nvim --version`'
|
||||
render: text
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: 'Operating system'
|
||||
placeholder: 'e.g. Arch Linux, macOS 15, Ubuntu 24.04'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: What happened? What did you expect?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Minimal steps to trigger the bug
|
||||
value: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 'Health check'
|
||||
description: 'Output of `:checkhealth forge`'
|
||||
render: text
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 'Forge CLI version'
|
||||
description: 'Output of `gh --version`, `glab --version`, or `tea --version`'
|
||||
render: text
|
||||
5
.github/ISSUE_TEMPLATE/config.yaml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yaml
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Questions
|
||||
url: https://github.com/barrettruth/forge.nvim/discussions
|
||||
about: Ask questions and discuss ideas
|
||||
30
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
30
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
name: Feature Request
|
||||
description: Suggest a feature
|
||||
title: 'feat: '
|
||||
labels: [enhancement]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Prerequisites
|
||||
options:
|
||||
- label:
|
||||
I have searched [existing
|
||||
issues](https://github.com/barrettruth/forge.nvim/issues)
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Problem
|
||||
description: What problem does this solve?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Proposed solution
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Alternatives considered
|
||||
21
.github/workflows/luarocks.yaml
vendored
Normal file
21
.github/workflows/luarocks.yaml
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
name: luarocks
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
quality:
|
||||
uses: ./.github/workflows/quality.yaml
|
||||
|
||||
publish:
|
||||
needs: quality
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: nvim-neorocks/luarocks-tag-release@v7
|
||||
env:
|
||||
LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }}
|
||||
87
.github/workflows/quality.yaml
vendored
Normal file
87
.github/workflows/quality.yaml
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
name: quality
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
lua: ${{ steps.changes.outputs.lua }}
|
||||
markdown: ${{ steps.changes.outputs.markdown }}
|
||||
vimdoc: ${{ steps.changes.outputs.vimdoc }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: changes
|
||||
with:
|
||||
filters: |
|
||||
lua:
|
||||
- 'lua/**'
|
||||
- 'plugin/**'
|
||||
- '*.lua'
|
||||
- '.luarc.json'
|
||||
- '*.toml'
|
||||
- 'vim.yaml'
|
||||
markdown:
|
||||
- '*.md'
|
||||
vimdoc:
|
||||
- 'doc/**'
|
||||
|
||||
lua-format:
|
||||
name: Lua Format Check
|
||||
runs-on: ubuntu-latest
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.lua == 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v31
|
||||
- run: nix develop .#ci --command stylua --check .
|
||||
|
||||
lua-lint:
|
||||
name: Lua Lint Check
|
||||
runs-on: ubuntu-latest
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.lua == 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v31
|
||||
- run: nix develop .#ci --command selene --display-style quiet .
|
||||
|
||||
lua-typecheck:
|
||||
name: Lua Type Check
|
||||
runs-on: ubuntu-latest
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.lua == 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run Lua LS Type Check
|
||||
uses: mrcjkb/lua-typecheck-action@v0
|
||||
with:
|
||||
checklevel: Warning
|
||||
directories: lua
|
||||
configpath: .luarc.json
|
||||
|
||||
vimdoc-check:
|
||||
name: Vimdoc Check
|
||||
runs-on: ubuntu-latest
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.vimdoc == 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v31
|
||||
- run: nix develop .#ci --command vimdoc-language-server check doc/
|
||||
|
||||
markdown-format:
|
||||
name: Markdown Format Check
|
||||
runs-on: ubuntu-latest
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.markdown == 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v31
|
||||
- run: nix develop .#ci --command prettier --check .
|
||||
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
doc/tags
|
||||
*.log
|
||||
|
||||
.*cache*
|
||||
CLAUDE.md
|
||||
.claude/
|
||||
|
||||
node_modules/
|
||||
|
||||
result
|
||||
result-*
|
||||
.direnv/
|
||||
.envrc
|
||||
15
.luarc.json
Normal file
15
.luarc.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"runtime.version": "LuaJIT",
|
||||
"runtime.path": ["lua/?.lua", "lua/?/init.lua"],
|
||||
"diagnostics.globals": ["vim", "jit"],
|
||||
"workspace.library": [
|
||||
"$VIMRUNTIME/lua",
|
||||
"${3rd}/luv/library",
|
||||
"${3rd}/busted/library",
|
||||
"${3rd}/luassert/library"
|
||||
],
|
||||
"workspace.checkThirdParty": false,
|
||||
"diagnostics.libraryFiles": "Disable",
|
||||
"workspace.ignoreDir": [".direnv"],
|
||||
"completion.callSnippet": "Replace"
|
||||
}
|
||||
17
.pre-commit-config.yaml
Normal file
17
.pre-commit-config.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
minimum_pre_commit_version: '3.5.0'
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/JohnnyMorganz/StyLua
|
||||
rev: v2.3.1
|
||||
hooks:
|
||||
- id: stylua-github
|
||||
name: stylua (Lua formatter)
|
||||
files: \.lua$
|
||||
pass_filenames: true
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v4.0.0-alpha.8
|
||||
hooks:
|
||||
- id: prettier
|
||||
name: prettier
|
||||
files: \.(md|toml|yaml|yml|sh)$
|
||||
1
.prettierignore
Normal file
1
.prettierignore
Normal file
|
|
@ -0,0 +1 @@
|
|||
node_modules/
|
||||
9
.prettierrc
Normal file
9
.prettierrc
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"proseWrap": "always",
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"trailingComma": "none",
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
}
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2026 Raphael
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
21
forge.nvim-scm-1.rockspec
Normal file
21
forge.nvim-scm-1.rockspec
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
rockspec_format = '3.0'
|
||||
package = 'forge.nvim'
|
||||
version = 'scm-1'
|
||||
|
||||
source = {
|
||||
url = 'git+https://github.com/barrettruth/forge.nvim.git',
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = 'Forge-agnostic git workflow for Neovim',
|
||||
homepage = 'https://github.com/barrettruth/forge.nvim',
|
||||
license = 'MIT',
|
||||
}
|
||||
|
||||
dependencies = {
|
||||
'lua >= 5.1',
|
||||
}
|
||||
|
||||
build = {
|
||||
type = 'builtin',
|
||||
}
|
||||
316
lua/forge/codeberg.lua
Normal file
316
lua/forge/codeberg.lua
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
local forge = require('forge')
|
||||
|
||||
---@type forge.Forge
|
||||
local M = {
|
||||
name = 'codeberg',
|
||||
cli = 'tea',
|
||||
kinds = { issue = 'issues', pr = 'pulls' },
|
||||
labels = {
|
||||
issue = 'Issues',
|
||||
pr = 'PRs',
|
||||
pr_one = 'PR',
|
||||
pr_full = 'Pull Requests',
|
||||
ci = 'CI/CD',
|
||||
},
|
||||
}
|
||||
|
||||
---@param kind string
|
||||
---@param state string
|
||||
---@return string
|
||||
function M:list_cmd(kind, state)
|
||||
return ('tea %s list --state %s'):format(kind, state)
|
||||
end
|
||||
|
||||
---@param state string
|
||||
---@return string[]
|
||||
function M:list_pr_json_cmd(state)
|
||||
return {
|
||||
'tea',
|
||||
'pulls',
|
||||
'list',
|
||||
'--state',
|
||||
state,
|
||||
'--output',
|
||||
'json',
|
||||
'--fields',
|
||||
'index,title,head,state,poster,created_at',
|
||||
}
|
||||
end
|
||||
|
||||
---@param state string
|
||||
---@return string[]
|
||||
function M:list_issue_json_cmd(state)
|
||||
return {
|
||||
'tea',
|
||||
'issues',
|
||||
'list',
|
||||
'--state',
|
||||
state,
|
||||
'--output',
|
||||
'json',
|
||||
'--fields',
|
||||
'index,title,state,poster,created_at',
|
||||
}
|
||||
end
|
||||
|
||||
function M:pr_json_fields()
|
||||
return {
|
||||
number = 'index',
|
||||
title = 'title',
|
||||
branch = 'head',
|
||||
state = 'state',
|
||||
author = 'poster',
|
||||
created_at = 'created_at',
|
||||
}
|
||||
end
|
||||
|
||||
function M:issue_json_fields()
|
||||
return {
|
||||
number = 'index',
|
||||
title = 'title',
|
||||
state = 'state',
|
||||
author = 'poster',
|
||||
created_at = 'created_at',
|
||||
}
|
||||
end
|
||||
|
||||
---@param kind string
|
||||
---@param num string
|
||||
function M:view_web(kind, num)
|
||||
local base = forge.remote_web_url()
|
||||
vim.ui.open(('%s/%s/%s'):format(base, kind, num))
|
||||
end
|
||||
|
||||
---@param loc string
|
||||
---@param branch string
|
||||
function M:browse(loc, branch)
|
||||
local base = forge.remote_web_url()
|
||||
local file, lines = loc:match('^(.+):(.+)$')
|
||||
vim.ui.open(('%s/src/branch/%s/%s#L%s'):format(base, branch, file, lines))
|
||||
end
|
||||
|
||||
function M:browse_root()
|
||||
vim.ui.open(forge.remote_web_url())
|
||||
end
|
||||
|
||||
function M:browse_branch(branch)
|
||||
local base = forge.remote_web_url()
|
||||
vim.ui.open(base .. '/src/branch/' .. branch)
|
||||
end
|
||||
|
||||
function M:browse_commit(sha)
|
||||
local base = forge.remote_web_url()
|
||||
vim.ui.open(base .. '/commit/' .. sha)
|
||||
end
|
||||
|
||||
function M:checkout_cmd(num)
|
||||
return { 'tea', 'pr', 'checkout', num }
|
||||
end
|
||||
|
||||
---@param loc string
|
||||
function M:yank_branch(loc)
|
||||
local branch = vim.trim(vim.fn.system('git branch --show-current'))
|
||||
local base = forge.remote_web_url()
|
||||
local file, lines = loc:match('^(.+):(.+)$')
|
||||
vim.fn.setreg(
|
||||
'+',
|
||||
('%s/src/branch/%s/%s#L%s'):format(base, branch, file, lines)
|
||||
)
|
||||
end
|
||||
|
||||
---@param loc string
|
||||
function M:yank_commit(loc)
|
||||
local commit = vim.trim(vim.fn.system('git rev-parse HEAD'))
|
||||
local base = forge.remote_web_url()
|
||||
local file, lines = loc:match('^(.+):(.+)$')
|
||||
vim.fn.setreg(
|
||||
'+',
|
||||
('%s/src/commit/%s/%s#L%s'):format(base, commit, file, lines)
|
||||
)
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:fetch_pr(num)
|
||||
return { 'git', 'fetch', 'origin', ('pull/%s/head:pr-%s'):format(num, num) }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:pr_base_cmd(num)
|
||||
return { 'tea', 'pr', num, '--fields', 'base', '--output', 'simple' }
|
||||
end
|
||||
|
||||
---@param branch string
|
||||
---@return string[]
|
||||
function M:pr_for_branch_cmd(_branch)
|
||||
return {
|
||||
'tea',
|
||||
'pr',
|
||||
'list',
|
||||
'--fields',
|
||||
'index,head',
|
||||
'--output',
|
||||
'simple',
|
||||
'--state',
|
||||
'open',
|
||||
}
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string
|
||||
function M:checks_cmd(num)
|
||||
local _ = num
|
||||
return 'tea actions runs list'
|
||||
end
|
||||
|
||||
---@param run_id string
|
||||
---@param failed_only boolean
|
||||
---@return string[]
|
||||
function M:check_log_cmd(run_id, failed_only)
|
||||
local _ = failed_only
|
||||
local lines = forge.config().ci.lines
|
||||
return {
|
||||
'sh',
|
||||
'-c',
|
||||
('tea actions runs logs %s | tail -n %d'):format(run_id, lines),
|
||||
}
|
||||
end
|
||||
|
||||
---@param run_id string
|
||||
---@return string[]
|
||||
function M:check_tail_cmd(run_id)
|
||||
return { 'tea', 'actions', 'runs', 'logs', run_id, '--follow' }
|
||||
end
|
||||
|
||||
function M:list_runs_cmd(_branch)
|
||||
return 'tea actions runs list'
|
||||
end
|
||||
|
||||
function M:run_log_cmd(id, failed_only)
|
||||
local _ = failed_only
|
||||
local lines = forge.config().ci.lines
|
||||
return {
|
||||
'sh',
|
||||
'-c',
|
||||
('tea actions runs logs %s | tail -n %d'):format(id, lines),
|
||||
}
|
||||
end
|
||||
|
||||
function M:run_tail_cmd(id)
|
||||
return { 'tea', 'actions', 'runs', 'logs', id, '--follow' }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@param method string
|
||||
---@return string[]
|
||||
function M:merge_cmd(num, method)
|
||||
return { 'tea', 'pr', 'merge', num, '--style', method }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:approve_cmd(num)
|
||||
return { 'tea', 'pr', 'approve', num }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:close_cmd(num)
|
||||
return { 'tea', 'pulls', 'close', num }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:reopen_cmd(num)
|
||||
return { 'tea', 'pulls', 'reopen', num }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:close_issue_cmd(num)
|
||||
return { 'tea', 'issues', 'close', num }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:reopen_issue_cmd(num)
|
||||
return { 'tea', 'issues', 'reopen', num }
|
||||
end
|
||||
|
||||
---@param title string
|
||||
---@param body string
|
||||
---@param base string
|
||||
---@param _draft boolean
|
||||
---@param _reviewers string[]?
|
||||
---@return string[]
|
||||
function M:create_pr_cmd(title, body, base, _draft, _reviewers)
|
||||
return { 'tea', 'pr', 'create', '--title', title, '--description', body, '--base', base }
|
||||
end
|
||||
|
||||
---@return string[]?
|
||||
function M:create_pr_web_cmd()
|
||||
local branch = vim.trim(vim.fn.system('git branch --show-current'))
|
||||
local base_url = forge.remote_web_url()
|
||||
local default = vim.trim(vim.fn.system(
|
||||
"git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||'"
|
||||
))
|
||||
if default == '' then
|
||||
default = 'main'
|
||||
end
|
||||
vim.ui.open(('%s/compare/%s...%s'):format(base_url, default, branch))
|
||||
return nil
|
||||
end
|
||||
|
||||
---@return string[]
|
||||
function M:default_branch_cmd()
|
||||
return {
|
||||
'sh', '-c',
|
||||
"git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||'",
|
||||
}
|
||||
end
|
||||
|
||||
---@return string[]
|
||||
function M:template_paths()
|
||||
return {
|
||||
'.gitea/pull_request_template.md',
|
||||
'.github/pull_request_template.md',
|
||||
'.github/PULL_REQUEST_TEMPLATE.md',
|
||||
}
|
||||
end
|
||||
|
||||
---@param _num string
|
||||
---@param _is_draft boolean
|
||||
---@return string[]?
|
||||
function M:draft_toggle_cmd(_num, _is_draft)
|
||||
return nil
|
||||
end
|
||||
|
||||
---@return forge.RepoInfo
|
||||
function M:repo_info()
|
||||
return {
|
||||
permission = 'ADMIN',
|
||||
merge_methods = { 'squash', 'rebase', 'merge' },
|
||||
}
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return forge.PRState
|
||||
function M:pr_state(num)
|
||||
local result = vim.system(
|
||||
{ 'tea', 'pr', num, '--fields', 'state,mergeable', '--output', 'json' },
|
||||
{ text = true }
|
||||
):wait()
|
||||
local ok, data = pcall(vim.json.decode, result.stdout or '{}')
|
||||
if not ok or type(data) ~= 'table' then
|
||||
data = {}
|
||||
end
|
||||
return {
|
||||
state = (data.state or 'unknown'):upper(),
|
||||
mergeable = data.mergeable and 'MERGEABLE' or 'UNKNOWN',
|
||||
review_decision = '',
|
||||
is_draft = false,
|
||||
}
|
||||
end
|
||||
|
||||
return M
|
||||
387
lua/forge/github.lua
Normal file
387
lua/forge/github.lua
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
local forge = require('forge')
|
||||
|
||||
---@type forge.Forge
|
||||
local M = {
|
||||
name = 'github',
|
||||
cli = 'gh',
|
||||
kinds = { issue = 'issue', pr = 'pr' },
|
||||
labels = {
|
||||
issue = 'Issues',
|
||||
pr = 'PRs',
|
||||
pr_one = 'PR',
|
||||
pr_full = 'Pull Requests',
|
||||
ci = 'CI/CD',
|
||||
},
|
||||
}
|
||||
|
||||
local function nwo()
|
||||
local url = forge.remote_web_url()
|
||||
return url:match('github%.com/(.+)$') or ''
|
||||
end
|
||||
|
||||
---@param kind string
|
||||
---@param state string
|
||||
---@return string
|
||||
function M:list_cmd(kind, state)
|
||||
return ('gh %s list --limit 100 --state %s'):format(kind, state)
|
||||
end
|
||||
|
||||
---@param state string
|
||||
---@return string[]
|
||||
function M:list_pr_json_cmd(state)
|
||||
return {
|
||||
'gh',
|
||||
'pr',
|
||||
'list',
|
||||
'--limit',
|
||||
'100',
|
||||
'--state',
|
||||
state,
|
||||
'--json',
|
||||
'number,title,headRefName,state,author,createdAt',
|
||||
}
|
||||
end
|
||||
|
||||
---@param state string
|
||||
---@return string[]
|
||||
function M:list_issue_json_cmd(state)
|
||||
return {
|
||||
'gh',
|
||||
'issue',
|
||||
'list',
|
||||
'--limit',
|
||||
'100',
|
||||
'--state',
|
||||
state,
|
||||
'--json',
|
||||
'number,title,state,author,createdAt',
|
||||
}
|
||||
end
|
||||
|
||||
function M:pr_json_fields()
|
||||
return {
|
||||
number = 'number',
|
||||
title = 'title',
|
||||
branch = 'headRefName',
|
||||
state = 'state',
|
||||
author = 'author',
|
||||
created_at = 'createdAt',
|
||||
}
|
||||
end
|
||||
|
||||
function M:issue_json_fields()
|
||||
return {
|
||||
number = 'number',
|
||||
title = 'title',
|
||||
state = 'state',
|
||||
author = 'author',
|
||||
created_at = 'createdAt',
|
||||
}
|
||||
end
|
||||
|
||||
---@param kind string
|
||||
---@param num string
|
||||
function M:view_web(kind, num)
|
||||
vim.system({ 'gh', kind, 'view', num, '--web' })
|
||||
end
|
||||
|
||||
---@param loc string
|
||||
---@param branch string
|
||||
function M:browse(loc, branch)
|
||||
vim.system({ 'gh', 'browse', loc, '--branch', branch })
|
||||
end
|
||||
|
||||
function M:browse_root()
|
||||
vim.system({ 'gh', 'browse' })
|
||||
end
|
||||
|
||||
function M:browse_branch(branch)
|
||||
vim.system({ 'gh', 'browse', '--branch', branch })
|
||||
end
|
||||
|
||||
function M:browse_commit(sha)
|
||||
vim.system({ 'gh', 'browse', sha })
|
||||
end
|
||||
|
||||
function M:checkout_cmd(num)
|
||||
return { 'gh', 'pr', 'checkout', num }
|
||||
end
|
||||
|
||||
---@param loc string
|
||||
function M:yank_branch(loc)
|
||||
forge.yank_url({ 'gh', 'browse', loc, '-n' })
|
||||
end
|
||||
|
||||
---@param loc string
|
||||
function M:yank_commit(loc)
|
||||
forge.yank_url({ 'gh', 'browse', loc, '--commit=last', '-n' })
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:fetch_pr(num)
|
||||
return { 'git', 'fetch', 'origin', ('pull/%s/head:pr-%s'):format(num, num) }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:pr_base_cmd(num)
|
||||
return {
|
||||
'gh',
|
||||
'pr',
|
||||
'view',
|
||||
num,
|
||||
'--json',
|
||||
'baseRefName',
|
||||
'--jq',
|
||||
'.baseRefName',
|
||||
}
|
||||
end
|
||||
|
||||
---@param branch string
|
||||
---@return string[]
|
||||
function M:pr_for_branch_cmd(branch)
|
||||
return {
|
||||
'gh',
|
||||
'pr',
|
||||
'list',
|
||||
'--head',
|
||||
branch,
|
||||
'--json',
|
||||
'number',
|
||||
'--jq',
|
||||
'.[0].number',
|
||||
}
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string
|
||||
function M:checks_cmd(num)
|
||||
return ('gh pr checks %s'):format(num)
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:checks_json_cmd(num)
|
||||
return {
|
||||
'gh',
|
||||
'pr',
|
||||
'checks',
|
||||
num,
|
||||
'--json',
|
||||
'name,bucket,link,state,startedAt,completedAt',
|
||||
}
|
||||
end
|
||||
|
||||
---@param run_id string
|
||||
---@param failed_only boolean
|
||||
---@return string[]
|
||||
function M:check_log_cmd(run_id, failed_only)
|
||||
local lines = forge.config().ci.lines
|
||||
local flag = failed_only and '--log-failed' or '--log'
|
||||
return {
|
||||
'sh',
|
||||
'-c',
|
||||
('gh run view %s -R %s %s | tail -n %d'):format(
|
||||
run_id,
|
||||
nwo(),
|
||||
flag,
|
||||
lines
|
||||
),
|
||||
}
|
||||
end
|
||||
|
||||
---@param run_id string
|
||||
---@return string[]
|
||||
function M:check_tail_cmd(run_id)
|
||||
return { 'gh', 'run', 'watch', run_id, '-R', nwo() }
|
||||
end
|
||||
|
||||
function M:list_runs_json_cmd(branch)
|
||||
local cmd = {
|
||||
'gh',
|
||||
'run',
|
||||
'list',
|
||||
'--json',
|
||||
'databaseId,name,headBranch,status,conclusion,event,url,createdAt',
|
||||
'--limit',
|
||||
'30',
|
||||
}
|
||||
if branch then
|
||||
table.insert(cmd, '--branch')
|
||||
table.insert(cmd, branch)
|
||||
end
|
||||
return cmd
|
||||
end
|
||||
|
||||
function M:normalize_run(entry)
|
||||
local status = entry.status or ''
|
||||
if status == 'completed' then
|
||||
status = entry.conclusion or 'unknown'
|
||||
end
|
||||
return {
|
||||
id = tostring(entry.databaseId or ''),
|
||||
name = entry.name or '',
|
||||
branch = entry.headBranch or '',
|
||||
status = status,
|
||||
event = entry.event or '',
|
||||
url = entry.url or '',
|
||||
created_at = entry.createdAt or '',
|
||||
}
|
||||
end
|
||||
|
||||
function M:run_log_cmd(id, failed_only)
|
||||
local lines = forge.config().ci.lines
|
||||
local flag = failed_only and '--log-failed' or '--log'
|
||||
return {
|
||||
'sh',
|
||||
'-c',
|
||||
('gh run view %s -R %s %s | tail -n %d'):format(id, nwo(), flag, lines),
|
||||
}
|
||||
end
|
||||
|
||||
function M:run_tail_cmd(id)
|
||||
return { 'gh', 'run', 'watch', id, '-R', nwo() }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@param method string
|
||||
---@return string[]
|
||||
function M:merge_cmd(num, method)
|
||||
return { 'gh', 'pr', 'merge', num, '--' .. method }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:approve_cmd(num)
|
||||
return { 'gh', 'pr', 'review', num, '--approve' }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:close_cmd(num)
|
||||
return { 'gh', 'pr', 'close', num }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:reopen_cmd(num)
|
||||
return { 'gh', 'pr', 'reopen', num }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:close_issue_cmd(num)
|
||||
return { 'gh', 'issue', 'close', num }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:reopen_issue_cmd(num)
|
||||
return { 'gh', 'issue', 'reopen', num }
|
||||
end
|
||||
|
||||
---@param title string
|
||||
---@param body string
|
||||
---@param base string
|
||||
---@param draft boolean
|
||||
---@param reviewers string[]?
|
||||
---@return string[]
|
||||
function M:create_pr_cmd(title, body, base, draft, reviewers)
|
||||
local cmd = { 'gh', 'pr', 'create', '--title', title, '--body', body, '--base', base }
|
||||
if draft then
|
||||
table.insert(cmd, '--draft')
|
||||
end
|
||||
for _, r in ipairs(reviewers or {}) do
|
||||
table.insert(cmd, '--reviewer')
|
||||
table.insert(cmd, r)
|
||||
end
|
||||
return cmd
|
||||
end
|
||||
|
||||
---@return string[]
|
||||
function M:create_pr_web_cmd()
|
||||
return { 'gh', 'pr', 'create', '--web' }
|
||||
end
|
||||
|
||||
---@return string[]
|
||||
function M:default_branch_cmd()
|
||||
return { 'gh', 'repo', 'view', '--json', 'defaultBranchRef', '--jq', '.defaultBranchRef.name' }
|
||||
end
|
||||
|
||||
---@return string[]
|
||||
function M:template_paths()
|
||||
return {
|
||||
'.github/pull_request_template.md',
|
||||
'.github/PULL_REQUEST_TEMPLATE.md',
|
||||
'.github/PULL_REQUEST_TEMPLATE/',
|
||||
}
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@param is_draft boolean
|
||||
---@return string[]?
|
||||
function M:draft_toggle_cmd(num, is_draft)
|
||||
if is_draft then
|
||||
return { 'gh', 'pr', 'ready', num }
|
||||
end
|
||||
return { 'gh', 'pr', 'ready', num, '--undo' }
|
||||
end
|
||||
|
||||
---@return forge.RepoInfo
|
||||
function M:repo_info()
|
||||
local result = vim.system({
|
||||
'gh',
|
||||
'repo',
|
||||
'view',
|
||||
nwo(),
|
||||
'--json',
|
||||
'viewerPermission,squashMergeAllowed,rebaseMergeAllowed,mergeCommitAllowed',
|
||||
}, { text = true }):wait()
|
||||
|
||||
local ok, data = pcall(vim.json.decode, result.stdout or '{}')
|
||||
if not ok or type(data) ~= 'table' then
|
||||
data = {}
|
||||
end
|
||||
local methods = {}
|
||||
if data.squashMergeAllowed then
|
||||
table.insert(methods, 'squash')
|
||||
end
|
||||
if data.rebaseMergeAllowed then
|
||||
table.insert(methods, 'rebase')
|
||||
end
|
||||
if data.mergeCommitAllowed then
|
||||
table.insert(methods, 'merge')
|
||||
end
|
||||
|
||||
return {
|
||||
permission = (data.viewerPermission or 'READ'):upper(),
|
||||
merge_methods = methods,
|
||||
}
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return forge.PRState
|
||||
function M:pr_state(num)
|
||||
local result = vim.system({
|
||||
'gh',
|
||||
'pr',
|
||||
'view',
|
||||
num,
|
||||
'--json',
|
||||
'state,mergeable,reviewDecision,isDraft',
|
||||
}, { text = true }):wait()
|
||||
|
||||
local ok, data = pcall(vim.json.decode, result.stdout or '{}')
|
||||
if not ok or type(data) ~= 'table' then
|
||||
data = {}
|
||||
end
|
||||
return {
|
||||
state = data.state or 'UNKNOWN',
|
||||
mergeable = data.mergeable or 'UNKNOWN',
|
||||
review_decision = data.reviewDecision or '',
|
||||
is_draft = data.isDraft == true,
|
||||
}
|
||||
end
|
||||
|
||||
return M
|
||||
421
lua/forge/gitlab.lua
Normal file
421
lua/forge/gitlab.lua
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
local forge = require('forge')
|
||||
|
||||
---@type forge.Forge
|
||||
local M = {
|
||||
name = 'gitlab',
|
||||
cli = 'glab',
|
||||
kinds = { issue = 'issue', pr = 'mr' },
|
||||
labels = {
|
||||
issue = 'Issues',
|
||||
pr = 'MRs',
|
||||
pr_one = 'MR',
|
||||
pr_full = 'Merge Requests',
|
||||
ci = 'CI/CD',
|
||||
},
|
||||
}
|
||||
|
||||
---@param kind string
|
||||
---@param state string
|
||||
---@return string
|
||||
function M:list_cmd(kind, state)
|
||||
local cmd = ('glab %s list --per-page 100'):format(kind)
|
||||
if state == 'closed' then
|
||||
cmd = cmd .. ' --closed'
|
||||
elseif state == 'all' then
|
||||
cmd = cmd .. ' --all'
|
||||
end
|
||||
return cmd
|
||||
end
|
||||
|
||||
---@param state string
|
||||
---@return string[]
|
||||
function M:list_pr_json_cmd(state)
|
||||
local cmd = {
|
||||
'glab',
|
||||
'mr',
|
||||
'list',
|
||||
'--per-page',
|
||||
'100',
|
||||
'--output',
|
||||
'json',
|
||||
}
|
||||
if state == 'closed' then
|
||||
table.insert(cmd, '--closed')
|
||||
elseif state == 'all' then
|
||||
table.insert(cmd, '--all')
|
||||
end
|
||||
return cmd
|
||||
end
|
||||
|
||||
---@param state string
|
||||
---@return string[]
|
||||
function M:list_issue_json_cmd(state)
|
||||
local cmd = {
|
||||
'glab',
|
||||
'issue',
|
||||
'list',
|
||||
'--per-page',
|
||||
'100',
|
||||
'--output',
|
||||
'json',
|
||||
}
|
||||
if state == 'closed' then
|
||||
table.insert(cmd, '--closed')
|
||||
elseif state == 'all' then
|
||||
table.insert(cmd, '--all')
|
||||
end
|
||||
return cmd
|
||||
end
|
||||
|
||||
function M:pr_json_fields()
|
||||
return {
|
||||
number = 'iid',
|
||||
title = 'title',
|
||||
branch = 'source_branch',
|
||||
state = 'state',
|
||||
author = 'author',
|
||||
created_at = 'created_at',
|
||||
}
|
||||
end
|
||||
|
||||
function M:issue_json_fields()
|
||||
return {
|
||||
number = 'iid',
|
||||
title = 'title',
|
||||
state = 'state',
|
||||
author = 'author',
|
||||
created_at = 'created_at',
|
||||
}
|
||||
end
|
||||
|
||||
---@param kind string
|
||||
---@param num string
|
||||
function M:view_web(kind, num)
|
||||
vim.system({ 'glab', kind, 'view', num, '--web' })
|
||||
end
|
||||
|
||||
---@param loc string
|
||||
---@param branch string
|
||||
function M:browse(loc, branch)
|
||||
local base = forge.remote_web_url()
|
||||
local file, lines = loc:match('^(.+):(.+)$')
|
||||
vim.ui.open(('%s/-/blob/%s/%s#L%s'):format(base, branch, file, lines))
|
||||
end
|
||||
|
||||
function M:browse_root()
|
||||
vim.system({ 'glab', 'repo', 'view', '--web' })
|
||||
end
|
||||
|
||||
function M:browse_branch(branch)
|
||||
local base = forge.remote_web_url()
|
||||
vim.ui.open(base .. '/-/tree/' .. branch)
|
||||
end
|
||||
|
||||
function M:browse_commit(sha)
|
||||
local base = forge.remote_web_url()
|
||||
vim.ui.open(base .. '/-/commit/' .. sha)
|
||||
end
|
||||
|
||||
function M:checkout_cmd(num)
|
||||
return { 'glab', 'mr', 'checkout', num }
|
||||
end
|
||||
|
||||
---@param loc string
|
||||
function M:yank_branch(loc)
|
||||
local branch = vim.trim(vim.fn.system('git branch --show-current'))
|
||||
local base = forge.remote_web_url()
|
||||
local file, lines = loc:match('^(.+):(.+)$')
|
||||
vim.fn.setreg(
|
||||
'+',
|
||||
('%s/-/blob/%s/%s#L%s'):format(base, branch, file, lines)
|
||||
)
|
||||
end
|
||||
|
||||
---@param loc string
|
||||
function M:yank_commit(loc)
|
||||
local commit = vim.trim(vim.fn.system('git rev-parse HEAD'))
|
||||
local base = forge.remote_web_url()
|
||||
local file, lines = loc:match('^(.+):(.+)$')
|
||||
vim.fn.setreg(
|
||||
'+',
|
||||
('%s/-/blob/%s/%s#L%s'):format(base, commit, file, lines)
|
||||
)
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:fetch_pr(num)
|
||||
return {
|
||||
'git',
|
||||
'fetch',
|
||||
'origin',
|
||||
('merge-requests/%s/head:mr-%s'):format(num, num),
|
||||
}
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:pr_base_cmd(num)
|
||||
return {
|
||||
'sh',
|
||||
'-c',
|
||||
('glab mr view %s -F json | jq -r .target_branch'):format(num),
|
||||
}
|
||||
end
|
||||
|
||||
---@param branch string
|
||||
---@return string[]
|
||||
function M:pr_for_branch_cmd(branch)
|
||||
return {
|
||||
'sh',
|
||||
'-c',
|
||||
("glab mr list --source-branch '%s' -F json | jq -r '.[0].iid // empty'"):format(
|
||||
branch
|
||||
),
|
||||
}
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string
|
||||
function M:checks_cmd(num)
|
||||
local _ = num
|
||||
return 'glab ci list'
|
||||
end
|
||||
|
||||
---@param run_id string
|
||||
---@param failed_only boolean
|
||||
---@return string[]
|
||||
function M:check_log_cmd(run_id, failed_only)
|
||||
local _ = failed_only
|
||||
local lines = forge.config().ci.lines
|
||||
return {
|
||||
'sh',
|
||||
'-c',
|
||||
('glab ci trace %s | tail -n %d'):format(run_id, lines),
|
||||
}
|
||||
end
|
||||
|
||||
---@param run_id string
|
||||
---@return string[]
|
||||
function M:check_tail_cmd(run_id)
|
||||
return { 'glab', 'ci', 'trace', run_id }
|
||||
end
|
||||
|
||||
function M:list_runs_json_cmd(branch)
|
||||
local cmd = {
|
||||
'glab',
|
||||
'ci',
|
||||
'list',
|
||||
'--output',
|
||||
'json',
|
||||
'--per-page',
|
||||
'30',
|
||||
}
|
||||
if branch then
|
||||
table.insert(cmd, '--ref')
|
||||
table.insert(cmd, branch)
|
||||
end
|
||||
return cmd
|
||||
end
|
||||
|
||||
function M:normalize_run(entry)
|
||||
local ref = entry.ref or ''
|
||||
local mr_num = ref:match('^refs/merge%-requests/(%d+)/head$')
|
||||
return {
|
||||
id = tostring(entry.id or ''),
|
||||
name = mr_num and ('!%s'):format(mr_num) or ref,
|
||||
branch = '',
|
||||
status = entry.status or '',
|
||||
event = entry.source or '',
|
||||
url = entry.web_url or '',
|
||||
created_at = entry.created_at or '',
|
||||
}
|
||||
end
|
||||
|
||||
function M:run_log_cmd(id, failed_only)
|
||||
local lines = forge.config().ci.lines
|
||||
local jq_filter = failed_only
|
||||
and '[.[] | select(.status=="failed")][0].id // .[0].id'
|
||||
or '.[0].id'
|
||||
return {
|
||||
'sh',
|
||||
'-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(
|
||||
id,
|
||||
jq_filter,
|
||||
lines
|
||||
),
|
||||
}
|
||||
end
|
||||
|
||||
function M:run_tail_cmd(id)
|
||||
local jq_filter =
|
||||
'[.[] | select(.status=="running" or .status=="pending")][0].id // .[0].id'
|
||||
return {
|
||||
'sh',
|
||||
'-c',
|
||||
('JOB=$(glab api \'projects/:id/pipelines/%s/jobs?per_page=100\' | jq -r \'%s\') && [ "$JOB" != "null" ] && glab ci trace "$JOB"'):format(
|
||||
id,
|
||||
jq_filter
|
||||
),
|
||||
}
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@param method string
|
||||
---@return string[]
|
||||
function M:merge_cmd(num, method)
|
||||
local cmd = { 'glab', 'mr', 'merge', num }
|
||||
if method == 'squash' then
|
||||
table.insert(cmd, '--squash')
|
||||
elseif method == 'rebase' then
|
||||
table.insert(cmd, '--rebase')
|
||||
end
|
||||
return cmd
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:approve_cmd(num)
|
||||
return { 'glab', 'mr', 'approve', num }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:close_cmd(num)
|
||||
return { 'glab', 'mr', 'close', num }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:reopen_cmd(num)
|
||||
return { 'glab', 'mr', 'reopen', num }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:close_issue_cmd(num)
|
||||
return { 'glab', 'issue', 'close', num }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return string[]
|
||||
function M:reopen_issue_cmd(num)
|
||||
return { 'glab', 'issue', 'reopen', num }
|
||||
end
|
||||
|
||||
---@param title string
|
||||
---@param body string
|
||||
---@param base string
|
||||
---@param draft boolean
|
||||
---@param reviewers string[]?
|
||||
---@return string[]
|
||||
function M:create_pr_cmd(title, body, base, draft, reviewers)
|
||||
local cmd = {
|
||||
'glab', 'mr', 'create',
|
||||
'--title', title,
|
||||
'--description', body,
|
||||
'--target-branch', base,
|
||||
'--yes',
|
||||
}
|
||||
if draft then
|
||||
table.insert(cmd, '--draft')
|
||||
end
|
||||
for _, r in ipairs(reviewers or {}) do
|
||||
table.insert(cmd, '--reviewer')
|
||||
table.insert(cmd, r)
|
||||
end
|
||||
return cmd
|
||||
end
|
||||
|
||||
---@return string[]
|
||||
function M:create_pr_web_cmd()
|
||||
return { 'glab', 'mr', 'create', '--web' }
|
||||
end
|
||||
|
||||
---@return string[]
|
||||
function M:default_branch_cmd()
|
||||
return {
|
||||
'sh', '-c',
|
||||
"glab repo view -F json | jq -r '.default_branch'",
|
||||
}
|
||||
end
|
||||
|
||||
---@return string[]
|
||||
function M:template_paths()
|
||||
return { '.gitlab/merge_request_templates/' }
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@param is_draft boolean
|
||||
---@return string[]?
|
||||
function M:draft_toggle_cmd(num, is_draft)
|
||||
if is_draft then
|
||||
return { 'glab', 'mr', 'update', num, '--ready' }
|
||||
end
|
||||
return { 'glab', 'mr', 'update', num, '--draft' }
|
||||
end
|
||||
|
||||
---@return forge.RepoInfo
|
||||
function M:repo_info()
|
||||
local result = vim.system(
|
||||
{ 'glab', 'api', 'projects/:id' },
|
||||
{ text = true }
|
||||
)
|
||||
:wait()
|
||||
local ok, data = pcall(vim.json.decode, result.stdout or '{}')
|
||||
if not ok or type(data) ~= 'table' then
|
||||
data = {}
|
||||
end
|
||||
local perms = type(data.permissions) == 'table' and data.permissions or {}
|
||||
local pa = type(perms.project_access) == 'table' and perms.project_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'
|
||||
if level >= 40 then
|
||||
permission = 'ADMIN'
|
||||
elseif level >= 30 then
|
||||
permission = 'WRITE'
|
||||
end
|
||||
|
||||
local methods = {}
|
||||
local merge_method = data.merge_method or 'merge'
|
||||
if merge_method == 'ff' or merge_method == 'rebase_merge' then
|
||||
table.insert(methods, 'rebase')
|
||||
else
|
||||
table.insert(methods, 'merge')
|
||||
end
|
||||
if data.squash_option ~= 'never' then
|
||||
table.insert(methods, 'squash')
|
||||
end
|
||||
|
||||
return {
|
||||
permission = permission,
|
||||
merge_methods = methods,
|
||||
}
|
||||
end
|
||||
|
||||
---@param num string
|
||||
---@return forge.PRState
|
||||
function M:pr_state(num)
|
||||
local result = vim.system(
|
||||
{ 'glab', 'mr', 'view', num, '--output', 'json' },
|
||||
{ text = true }
|
||||
):wait()
|
||||
local ok, data = pcall(vim.json.decode, result.stdout or '{}')
|
||||
if not ok or type(data) ~= 'table' then
|
||||
data = {}
|
||||
end
|
||||
return {
|
||||
state = (data.state or 'unknown'):upper(),
|
||||
mergeable = data.merge_status or 'unknown',
|
||||
review_decision = '',
|
||||
is_draft = data.draft == true,
|
||||
}
|
||||
end
|
||||
|
||||
return M
|
||||
47
lua/forge/health.lua
Normal file
47
lua/forge/health.lua
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
local M = {}
|
||||
|
||||
function M.check()
|
||||
vim.health.start('forge.nvim')
|
||||
|
||||
if vim.fn.executable('git') == 1 then
|
||||
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
|
||||
vim.health.info(cli[1] .. ' not found (' .. cli[2] .. ' support disabled)')
|
||||
end
|
||||
end
|
||||
|
||||
local has_fzf = pcall(require, 'fzf-lua')
|
||||
if has_fzf then
|
||||
vim.health.ok('fzf-lua found')
|
||||
else
|
||||
vim.health.error('fzf-lua not found (required)')
|
||||
end
|
||||
|
||||
local has_diffs = pcall(require, 'diffs')
|
||||
if has_diffs then
|
||||
vim.health.ok('diffs.nvim found (review mode available)')
|
||||
else
|
||||
vim.health.info('diffs.nvim not found (review mode disabled)')
|
||||
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
|
||||
|
||||
return M
|
||||
1064
lua/forge/init.lua
Normal file
1064
lua/forge/init.lua
Normal file
File diff suppressed because it is too large
Load diff
1144
plugin/forge.lua
Normal file
1144
plugin/forge.lua
Normal file
File diff suppressed because it is too large
Load diff
4
selene.toml
Normal file
4
selene.toml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
std = 'vim'
|
||||
|
||||
[lints]
|
||||
bad_string_escape = 'allow'
|
||||
8
stylua.toml
Normal file
8
stylua.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
column_width = 100
|
||||
line_endings = "Unix"
|
||||
indent_type = "Spaces"
|
||||
indent_width = 2
|
||||
quote_style = "AutoPreferSingle"
|
||||
call_parentheses = "Always"
|
||||
[sort_requires]
|
||||
enabled = true
|
||||
26
vim.yaml
Normal file
26
vim.yaml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
base: lua51
|
||||
name: vim
|
||||
lua_versions:
|
||||
- luajit
|
||||
globals:
|
||||
vim:
|
||||
any: true
|
||||
jit:
|
||||
any: true
|
||||
assert:
|
||||
any: true
|
||||
describe:
|
||||
any: true
|
||||
it:
|
||||
any: true
|
||||
before_each:
|
||||
any: true
|
||||
after_each:
|
||||
any: true
|
||||
spy:
|
||||
any: true
|
||||
stub:
|
||||
any: true
|
||||
bit:
|
||||
any: true
|
||||
Loading…
Add table
Add a link
Reference in a new issue