Merge PR #319: feat(pr): add merge readiness and branch helper shortcuts
# Conflicts: # internal/i18n/locales/en-US.json # internal/i18n/locales/zh-CN.json
This commit is contained in:
commit
1567d96276
|
|
@ -180,6 +180,11 @@ gitlink-cli pr +create --owner Gitlink --repo forgeplus -t "feat: Search feature
|
|||
# Create a PR (from a fork)
|
||||
gitlink-cli pr +create --owner Gitlink --repo forgeplus -t "feat: New feature" --head your_username/forgeplus:feature/my-feature --base master
|
||||
|
||||
# List PR branch candidates and check merge readiness
|
||||
gitlink-cli pr +branches --owner Gitlink --repo forgeplus
|
||||
gitlink-cli pr +check-can-merge --owner Gitlink --repo forgeplus --head feature/search --base master --dry-run
|
||||
gitlink-cli pr +check-can-merge --owner Gitlink --repo forgeplus --head feature/search --base master --yes
|
||||
|
||||
# View a PR
|
||||
gitlink-cli pr +view --owner Gitlink --repo forgeplus -i 42
|
||||
|
||||
|
|
|
|||
|
|
@ -192,6 +192,11 @@ gitlink-cli pr +create --owner Gitlink --repo forgeplus -t "feat: 搜索功能"
|
|||
# 创建 PR(从 Fork 仓库)
|
||||
gitlink-cli pr +create --owner Gitlink --repo forgeplus -t "feat: 新功能" --head your_username/forgeplus:feature/my-feature --base master
|
||||
|
||||
# 列出 PR 可用分支并检查合并准备状态
|
||||
gitlink-cli pr +branches --owner Gitlink --repo forgeplus
|
||||
gitlink-cli pr +check-can-merge --owner Gitlink --repo forgeplus --head feature/search --base master --dry-run
|
||||
gitlink-cli pr +check-can-merge --owner Gitlink --repo forgeplus --head feature/search --base master --yes
|
||||
|
||||
# 查看 PR
|
||||
gitlink-cli pr +view --owner Gitlink --repo forgeplus -i 42
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
# PR readiness shortcuts
|
||||
|
||||
## Background
|
||||
|
||||
Pull request workflows already support list, create, view, merge, review,
|
||||
comments, changed files, and patchset/version inspection. Two helper endpoints
|
||||
were still documented as Raw API calls:
|
||||
|
||||
- `GET /:owner/:repo/pulls/get_branches`
|
||||
- `POST /:owner/:repo/pulls/check_can_merge`
|
||||
|
||||
This change adds first-class shortcuts for those PR preparation workflows.
|
||||
|
||||
## New shortcuts
|
||||
|
||||
- `pr +branches` lists PR source/target branch candidates.
|
||||
- `pr +check-can-merge` checks whether a source branch can merge into a target
|
||||
branch.
|
||||
|
||||
## Safety model
|
||||
|
||||
`pr +branches` is read-only and can run directly:
|
||||
|
||||
```bash
|
||||
gitlink-cli pr +branches --owner Gitlink --repo forgeplus
|
||||
```
|
||||
|
||||
`pr +check-can-merge` uses a remote POST endpoint, so it supports dry-run and
|
||||
requires explicit confirmation for real execution:
|
||||
|
||||
```bash
|
||||
gitlink-cli pr +check-can-merge --owner Gitlink --repo forgeplus \
|
||||
--head feature/search --base master --dry-run
|
||||
gitlink-cli pr +check-can-merge --owner Gitlink --repo forgeplus \
|
||||
--head feature/search --base master --yes
|
||||
```
|
||||
|
||||
## Documentation updates
|
||||
|
||||
- README and README.zh-CN include branch helper and merge readiness examples.
|
||||
- `skills/gitlink-pr` now prefers `pr +branches` and `pr +check-can-merge`
|
||||
over Raw API calls.
|
||||
- `skills/gitlink-gatekeeper` includes merge readiness as a PR preflight signal.
|
||||
|
||||
## Tests
|
||||
|
||||
Unit tests cover:
|
||||
|
||||
- endpoint method/path mapping for `pr +branches`;
|
||||
- `pr +check-can-merge` dry-run behavior;
|
||||
- `--yes` confirmation guard;
|
||||
- request payload and default `--base master`;
|
||||
- HTTP error propagation for both shortcuts.
|
||||
|
||||
Suggested verification:
|
||||
|
||||
```bash
|
||||
go test ./shortcuts/pr ./shortcuts
|
||||
```
|
||||
|
||||
Full project verification:
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
|
|
@ -47,17 +47,13 @@
|
|||
"cmd.issue.short": "Issue operations",
|
||||
"cmd.issue.update.short": "Update an issue",
|
||||
"cmd.issue.view.short": "View issue details",
|
||||
"cmd.notification.delete.short": "Delete messages",
|
||||
"cmd.notification.list.short": "List user messages",
|
||||
"cmd.notification.read.short": "Mark messages as read",
|
||||
"cmd.notification.short": "User message operations",
|
||||
"cmd.org.create.short": "Create an organization",
|
||||
"cmd.org.info.short": "Show organization details",
|
||||
"cmd.org.list.short": "List organizations",
|
||||
"cmd.org.members.short": "List organization members",
|
||||
"cmd.org.short": "Organization operations",
|
||||
"cmd.pr.checks.long": "Resolve a pull request's head branch and head commit, then report the CI build(s) that match. A matching commit SHA is authoritative; the head branch is used as a fallback. If builds do not expose branch or commit fields, all recent builds are shown with a note.",
|
||||
"cmd.pr.checks.short": "Show CI build status for a pull request",
|
||||
"cmd.pr.branches.short": "List pull request source and target branches",
|
||||
"cmd.pr.check_can_merge.short": "Check whether pull request branches can merge",
|
||||
"cmd.pr.close.short": "Close a pull request",
|
||||
"cmd.pr.comment.short": "Add a comment to a pull request",
|
||||
"cmd.pr.create.short": "Create a pull request",
|
||||
|
|
@ -99,11 +95,6 @@
|
|||
"cmd.search.repos.short": "Search repositories",
|
||||
"cmd.search.short": "Search operations",
|
||||
"cmd.search.users.short": "Search users",
|
||||
"cmd.tag.delete.short": "Delete a repository tag",
|
||||
"cmd.tag.list.short": "List repository tags with commit details",
|
||||
"cmd.tag.names.short": "List tag names without pagination",
|
||||
"cmd.tag.short": "Git tag operations",
|
||||
"cmd.tag.view.short": "View repository tag details",
|
||||
"cmd.user.info.short": "Show user profile",
|
||||
"cmd.user.me.short": "Show current authenticated user",
|
||||
"cmd.user.short": "User operations",
|
||||
|
|
@ -184,12 +175,6 @@
|
|||
"flag.issue.title": "Issue title",
|
||||
"flag.lang": "Display language",
|
||||
"flag.limit": "Items per page",
|
||||
"flag.notification.ids": "Comma-separated message IDs",
|
||||
"flag.notification.ids_read": "Comma-separated message IDs, or -1 for all unread messages",
|
||||
"flag.notification.status": "Read status: unread, read, or all",
|
||||
"flag.notification.type": "Message type: notification or atme",
|
||||
"flag.notification.type_all": "Message type: notification, atme, or all",
|
||||
"flag.notification.user": "User login. Defaults to current authenticated user.",
|
||||
"flag.org.id": "Organization ID",
|
||||
"flag.org.id_or_login": "Organization ID or login",
|
||||
"flag.org.name": "Organization name",
|
||||
|
|
@ -198,6 +183,7 @@
|
|||
"flag.pr.assignee_id": "Assignee user ID",
|
||||
"flag.pr.base": "Target branch",
|
||||
"flag.pr.body": "PR description",
|
||||
"flag.pr.check_yes": "Confirm the remote merge readiness check",
|
||||
"flag.pr.file": "Filter diff by file path",
|
||||
"flag.pr.head": "Source branch",
|
||||
"flag.pr.id": "PR number",
|
||||
|
|
@ -234,8 +220,6 @@
|
|||
"flag.search.keyword": "Search keyword",
|
||||
"flag.sort_by": "Sort field",
|
||||
"flag.sort_direction": "Sort direction: asc, desc",
|
||||
"flag.tag.keyword": "Search keyword for tag names",
|
||||
"flag.tag.name": "Tag name",
|
||||
"flag.user": "User login (default: current user)",
|
||||
"flag.user.login": "User login name",
|
||||
"flag.webhook.active": "Whether the webhook is active: true or false",
|
||||
|
|
@ -274,8 +258,6 @@
|
|||
"output.doctor.suggestion.check_token": "Check whether the stored token is valid, or run gitlink-cli auth login again.",
|
||||
"output.doctor.suggestion.fix_config_yaml": "Fix the YAML syntax in the gitlink-cli config file.",
|
||||
"output.doctor.suggestion.pass_owner_repo": "Run the command with --owner and --repo when not inside a GitLink repository.",
|
||||
"output.pr.checks.no_match": "No CI build was found for this pull request's head branch or commit.",
|
||||
"output.pr.checks.unlinkable": "CI builds do not expose a branch or commit field, so they could not be linked to this pull request; showing all recent builds. Correlate manually using the head commit above.",
|
||||
"output.version": "gitlink-cli {version}",
|
||||
"prompt.auth.password": "Password: ",
|
||||
"prompt.auth.token": "Paste your access token: ",
|
||||
|
|
|
|||
|
|
@ -47,17 +47,13 @@
|
|||
"cmd.issue.short": "议题操作",
|
||||
"cmd.issue.update.short": "更新议题",
|
||||
"cmd.issue.view.short": "查看议题详情",
|
||||
"cmd.notification.delete.short": "删除消息",
|
||||
"cmd.notification.list.short": "列出用户消息",
|
||||
"cmd.notification.read.short": "标记消息为已读",
|
||||
"cmd.notification.short": "用户消息操作",
|
||||
"cmd.org.create.short": "创建组织",
|
||||
"cmd.org.info.short": "显示组织详情",
|
||||
"cmd.org.list.short": "列出组织",
|
||||
"cmd.org.members.short": "列出组织成员",
|
||||
"cmd.org.short": "组织操作",
|
||||
"cmd.pr.checks.long": "解析拉取请求的源分支与源提交,再显示与之匹配的 CI 构建。提交 SHA 匹配为准,源分支作为回退。若构建未暴露分支或提交字段,则显示全部最近构建并附带说明。",
|
||||
"cmd.pr.checks.short": "查看拉取请求的 CI 构建状态",
|
||||
"cmd.pr.branches.short": "列出拉取请求可用源分支和目标分支",
|
||||
"cmd.pr.check_can_merge.short": "检查拉取请求分支是否可合并",
|
||||
"cmd.pr.close.short": "关闭拉取请求",
|
||||
"cmd.pr.comment.short": "给拉取请求添加评论",
|
||||
"cmd.pr.create.short": "创建拉取请求",
|
||||
|
|
@ -99,11 +95,6 @@
|
|||
"cmd.search.repos.short": "搜索仓库",
|
||||
"cmd.search.short": "搜索操作",
|
||||
"cmd.search.users.short": "搜索用户",
|
||||
"cmd.tag.delete.short": "删除仓库标签",
|
||||
"cmd.tag.list.short": "列出仓库标签及提交详情",
|
||||
"cmd.tag.names.short": "列出标签名称",
|
||||
"cmd.tag.short": "Git 标签操作",
|
||||
"cmd.tag.view.short": "查看仓库标签详情",
|
||||
"cmd.user.info.short": "显示用户资料",
|
||||
"cmd.user.me.short": "显示当前认证用户",
|
||||
"cmd.user.short": "用户操作",
|
||||
|
|
@ -184,12 +175,6 @@
|
|||
"flag.issue.title": "议题标题",
|
||||
"flag.lang": "显示语言",
|
||||
"flag.limit": "每页条目数",
|
||||
"flag.notification.ids": "逗号分隔的消息 ID",
|
||||
"flag.notification.ids_read": "逗号分隔的消息 ID,或用 -1 表示全部未读消息",
|
||||
"flag.notification.status": "阅读状态:unread、read 或 all",
|
||||
"flag.notification.type": "消息类型:notification 或 atme",
|
||||
"flag.notification.type_all": "消息类型:notification、atme 或 all",
|
||||
"flag.notification.user": "用户登录名,默认使用当前认证用户。",
|
||||
"flag.org.id": "组织 ID",
|
||||
"flag.org.id_or_login": "组织 ID 或登录名",
|
||||
"flag.org.name": "组织名称",
|
||||
|
|
@ -198,6 +183,7 @@
|
|||
"flag.pr.assignee_id": "指派人用户 ID",
|
||||
"flag.pr.base": "目标分支",
|
||||
"flag.pr.body": "PR 描述",
|
||||
"flag.pr.check_yes": "确认执行远端可合并检查",
|
||||
"flag.pr.file": "按文件路径筛选 diff",
|
||||
"flag.pr.head": "源分支",
|
||||
"flag.pr.id": "PR 编号",
|
||||
|
|
@ -234,8 +220,6 @@
|
|||
"flag.search.keyword": "搜索关键词",
|
||||
"flag.sort_by": "排序字段",
|
||||
"flag.sort_direction": "排序方向:asc、desc",
|
||||
"flag.tag.keyword": "标签名称搜索关键词",
|
||||
"flag.tag.name": "标签名称",
|
||||
"flag.user": "用户登录名(默认:当前用户)",
|
||||
"flag.user.login": "用户登录名",
|
||||
"flag.webhook.active": "Webhook 是否启用:true 或 false",
|
||||
|
|
@ -274,8 +258,6 @@
|
|||
"output.doctor.suggestion.check_token": "检查已保存的 Token 是否有效,或重新运行 gitlink-cli auth login。",
|
||||
"output.doctor.suggestion.fix_config_yaml": "修复 gitlink-cli 配置文件中的 YAML 语法。",
|
||||
"output.doctor.suggestion.pass_owner_repo": "不在 GitLink 仓库目录内时,请通过 --owner 和 --repo 指定仓库。",
|
||||
"output.pr.checks.no_match": "未找到与该拉取请求源分支或源提交对应的 CI 构建。",
|
||||
"output.pr.checks.unlinkable": "CI 构建未暴露分支或提交字段,无法与该拉取请求关联;已显示全部最近构建。请依据上方的源提交手动核对。",
|
||||
"output.version": "gitlink-cli {version}",
|
||||
"prompt.auth.password": "密码:",
|
||||
"prompt.auth.token": "粘贴你的访问 Token:",
|
||||
|
|
|
|||
|
|
@ -123,6 +123,31 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
|
|||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "branches",
|
||||
Description: tr.T("cmd.pr.branches.short"),
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
env, err := ctx.CallAPI("GET", ctx.RepoPath()+"/pulls/get_branches", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "check-can-merge",
|
||||
Description: tr.T("cmd.pr.check_can_merge.short"),
|
||||
Flags: []common.Flag{
|
||||
{Name: "head", Usage: tr.T("flag.pr.head"), Required: true},
|
||||
{Name: "base", Usage: tr.T("flag.pr.base"), Default: "master"},
|
||||
{Name: "dry-run", Usage: tr.T("flag.dry_run"), Bool: true, Default: "false"},
|
||||
{Name: "yes", Usage: tr.T("flag.pr.check_yes"), Bool: true, Default: "false"},
|
||||
},
|
||||
Run: runCheckCanMerge,
|
||||
},
|
||||
{
|
||||
Name: "view",
|
||||
Description: tr.T("cmd.pr.view.short"),
|
||||
|
|
@ -469,6 +494,43 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
|
|||
}
|
||||
}
|
||||
|
||||
func runCheckCanMerge(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
head, err := ctx.RequireArg("head")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
base := ctx.Arg("base")
|
||||
if base == "" {
|
||||
base = "master"
|
||||
}
|
||||
payload := map[string]interface{}{
|
||||
"head": head,
|
||||
"base": base,
|
||||
}
|
||||
path := ctx.RepoPath() + "/pulls/check_can_merge"
|
||||
if ctx.Arg("dry-run") == "true" {
|
||||
return ctx.OutputData(map[string]interface{}{
|
||||
"repository": fmt.Sprintf("%s/%s", ctx.Owner, ctx.Repo),
|
||||
"dry_run": true,
|
||||
"action": "check_can_merge",
|
||||
"method": "POST",
|
||||
"path": path,
|
||||
"payload": payload,
|
||||
})
|
||||
}
|
||||
if ctx.Arg("yes") != "true" {
|
||||
return fmt.Errorf("check-can-merge calls a remote POST endpoint; run with --dry-run first, then pass --yes to execute")
|
||||
}
|
||||
env, err := ctx.CallAPI("POST", path, payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
}
|
||||
|
||||
func shortcutTranslator(translators ...*i18n.Translator) *i18n.Translator {
|
||||
if len(translators) > 0 && translators[0] != nil {
|
||||
return translators[0]
|
||||
|
|
|
|||
|
|
@ -517,6 +517,103 @@ func TestPRCreateNoBody(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// --- branches / check-can-merge ---
|
||||
|
||||
func TestPRBranches(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "GET" {
|
||||
t.Fatalf("expected GET, got %s", r.Method)
|
||||
}
|
||||
if r.URL.Path != "/owner/repo/pulls/get_branches.json" {
|
||||
t.Fatalf("unexpected path: %s", r.URL.Path)
|
||||
}
|
||||
writeJSON(t, w, map[string]interface{}{
|
||||
"branches": []interface{}{"master", "feature/search"},
|
||||
})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runPRShortcut(t, server, "branches", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("branches failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPRCheckCanMergeDryRunDoesNotCallAPI(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatalf("check-can-merge dry-run should not call API, got: %s %s", r.Method, r.URL.Path)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runPRShortcut(t, server, "check-can-merge", map[string]string{
|
||||
"head": "feature/search",
|
||||
"base": "master",
|
||||
"dry-run": "true",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("check-can-merge dry-run failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPRCheckCanMergeRequiresYes(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatalf("check-can-merge without --yes should not call API, got: %s %s", r.Method, r.URL.Path)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runPRShortcut(t, server, "check-can-merge", map[string]string{
|
||||
"head": "feature/search",
|
||||
"base": "master",
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected check-can-merge to require --yes")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPRCheckCanMergeWithYesCallsEndpoint(t *testing.T) {
|
||||
var payload map[string]interface{}
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
t.Fatalf("expected POST, got %s", r.Method)
|
||||
}
|
||||
if r.URL.Path != "/owner/repo/pulls/check_can_merge.json" {
|
||||
t.Fatalf("unexpected path: %s", r.URL.Path)
|
||||
}
|
||||
payload = decodeJSON(t, r)
|
||||
writeJSON(t, w, map[string]interface{}{"can_merge": true})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runPRShortcut(t, server, "check-can-merge", map[string]string{
|
||||
"head": "feature/search",
|
||||
"base": "master",
|
||||
"yes": "true",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("check-can-merge failed: %v", err)
|
||||
}
|
||||
assertEqual(t, payload["head"], "feature/search")
|
||||
assertEqual(t, payload["base"], "master")
|
||||
}
|
||||
|
||||
func TestPRCheckCanMergeDefaultsBaseToMaster(t *testing.T) {
|
||||
var payload map[string]interface{}
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
payload = decodeJSON(t, r)
|
||||
writeJSON(t, w, map[string]interface{}{"can_merge": true})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runPRShortcut(t, server, "check-can-merge", map[string]string{
|
||||
"head": "feature/search",
|
||||
"yes": "true",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("check-can-merge failed: %v", err)
|
||||
}
|
||||
assertEqual(t, payload["base"], "master")
|
||||
}
|
||||
|
||||
// --- view ---
|
||||
|
||||
func TestPRView(t *testing.T) {
|
||||
|
|
@ -902,6 +999,36 @@ func TestPRCreateHTTPError(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPRBranchesHTTPError(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("server error"))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runPRShortcut(t, server, "branches", nil)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for HTTP 500")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPRCheckCanMergeHTTPError(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("server error"))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runPRShortcut(t, server, "check-can-merge", map[string]string{
|
||||
"head": "feature/search",
|
||||
"base": "master",
|
||||
"yes": "true",
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for HTTP 500")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPRViewHTTPError(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ gitlink-gatekeeper 是一个**可复现的 PR 合并门禁**:团队把合并
|
|||
| 阶段 | 操作 | AI Agent 角色 |
|
||||
|------|------|--------------|
|
||||
| ① 加载策略 | 读 `gatekeeper.yaml`,找不到则回退内置默认策略 | 解析 / 校验 / 回退 |
|
||||
| ② 采集上下文 | 拉 PR 元信息、变更文件、diff、commits、CI 状态 | 执行 CLI 命令采集数据 |
|
||||
| ② 采集上下文 | 拉 PR 元信息、分支可合并性、变更文件、diff、commits、CI 状态 | 执行 CLI 命令采集数据 |
|
||||
| ③ 产出发现 | 逐文件审查,按 severity 分级标记问题 | AI 分析,输出发现列表 |
|
||||
| ④ 逐维评分 | 五维各算 `0..weight` 得分,相加得 `total` | 确定性计算(非主观) |
|
||||
| ⑤ 硬门禁 | 逐项判定 `hard_gates`,命中即拦截 | 布尔判定 |
|
||||
|
|
@ -98,6 +98,7 @@ behavior:
|
|||
|------|------|------|
|
||||
| 聚合上下文 | 仓库、PR、文件、Review、Issue、标签 | `gitlink-cli workflow +review-context --number <id> --format json` |
|
||||
| PR 元信息 | 标题/描述/作者/关联 issue | `gitlink-cli pr +view -i <id> --format json` |
|
||||
| 合并准备状态 | 源/目标分支是否可合并 | `gitlink-cli pr +check-can-merge --head <head> --base <base> --dry-run`,确认后 `--yes` |
|
||||
| 变更文件 | 文件路径列表 | `gitlink-cli pr +files -i <id> --format json` |
|
||||
| Diff | 变更内容供 AI 审查 | `gitlink-cli pr +diff -i <id> --format json` |
|
||||
| CI 状态 | 构建结果 | `gitlink-cli ci +builds --format json` |
|
||||
|
|
@ -108,6 +109,7 @@ behavior:
|
|||
PR=42
|
||||
gitlink-cli workflow +review-context --number "$PR" --format json
|
||||
gitlink-cli pr +view -i "$PR" --format json # title / body / 关联 issue
|
||||
gitlink-cli pr +check-can-merge --head feature/pr --base master --dry-run
|
||||
gitlink-cli pr +files -i "$PR" --format json # changed files
|
||||
gitlink-cli pr +diff -i "$PR" --format json # diff(供 AI 审查)
|
||||
gitlink-cli ci +builds --format json
|
||||
|
|
@ -293,6 +295,7 @@ gitlink-cli api POST /:owner/:repo/issues/$ISSUE_ID --body '{
|
|||
# 在目标仓库目录下,对 PR #42 跑门禁,仅预览评分卡
|
||||
PR=42
|
||||
gitlink-cli pr +view -i "$PR" --format json
|
||||
gitlink-cli pr +check-can-merge --head feature/pr --base master --dry-run
|
||||
gitlink-cli pr +files -i "$PR" --format json
|
||||
gitlink-cli pr +diff -i "$PR" --format json
|
||||
gitlink-cli workflow +review-context --number "$PR" --format json
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: gitlink-pr
|
||||
version: 1.0.0
|
||||
description: "Pull Request 管理:创建、查看、合并、关闭 PR,查看变更文件、Diff 和 patchset/version。当用户需要操作 GitLink PR 时触发。"
|
||||
version: 2.0.0
|
||||
description: "Pull Request 管理:创建、查看、合并、关闭 PR,查看变更文件、Diff、patchset/version、可用分支和合并准备状态。当用户需要操作 GitLink PR 时触发。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["gitlink-cli"]
|
||||
|
|
@ -21,6 +21,8 @@ metadata:
|
|||
|----------|------|----------|
|
||||
| `pr +list` | PR 列表 | 否(公开项目) |
|
||||
| `pr +create` | 创建 PR | 是 |
|
||||
| `pr +branches` | 获取 PR 可用源分支和目标分支 | 否 |
|
||||
| `pr +check-can-merge` | 检查源/目标分支是否可合并,先 dry-run | 是 |
|
||||
| `pr +view` | PR 详情 | 否(公开项目) |
|
||||
| `pr +merge` | 合并 PR | 是 |
|
||||
| `pr +refuse` | 拒绝并关闭 PR | 是 |
|
||||
|
|
@ -45,6 +47,13 @@ gitlink-cli pr +list --owner Gitlink --repo forgeplus --state merged --keyword r
|
|||
# 创建 PR(源分支必须有实际代码变更)
|
||||
gitlink-cli pr +create --title "feat: 新增搜索功能" --head feature/search --base master --body "实现了全文搜索"
|
||||
|
||||
# 获取可用分支
|
||||
gitlink-cli pr +branches --owner Gitlink --repo forgeplus
|
||||
|
||||
# 检查源分支是否可合并到目标分支,先 dry-run 再执行
|
||||
gitlink-cli pr +check-can-merge --owner Gitlink --repo forgeplus --head feature/search --base master --dry-run
|
||||
gitlink-cli pr +check-can-merge --owner Gitlink --repo forgeplus --head feature/search --base master --yes
|
||||
|
||||
# 查看 PR 详情(使用 pull_request_number,即网页 URL 中的序号)
|
||||
gitlink-cli pr +view --id 3
|
||||
|
||||
|
|
@ -138,9 +147,6 @@ gitlink-cli api GET /:owner/:repo/sub_entries --query 'filepath=file.md&ref=dev'
|
|||
# 从 entries.sha 获取 SHA,然后:
|
||||
gitlink-cli api PUT /:owner/:repo/update_file --body '{"filepath":"file.md","content":"<base64>","sha":"<sha>","branch":"dev","message":"update file"}'
|
||||
|
||||
# 检查是否可合并
|
||||
gitlink-cli api POST /:owner/:repo/pulls/check_can_merge --body '{"head":"dev","base":"main"}'
|
||||
|
||||
# 创建 Review
|
||||
gitlink-cli api POST /v1/:owner/:repo/pulls/:id/reviews --body '{"content":"LGTM","status":"approved"}'
|
||||
|
||||
|
|
@ -148,9 +154,6 @@ gitlink-cli api POST /v1/:owner/:repo/pulls/:id/reviews --body '{"content":"LGTM
|
|||
gitlink-cli api GET /v1/:owner/:repo/pulls/:id/reviews
|
||||
gitlink-cli api GET /v1/:owner/:repo/pulls/:id/reviews?status=approved
|
||||
|
||||
# 获取可用分支
|
||||
gitlink-cli api GET /:owner/:repo/pulls/get_branches
|
||||
|
||||
# 查看 PR patchset/version 列表(v1 API)
|
||||
gitlink-cli api GET /v1/:owner/:repo/pulls/:id/versions
|
||||
|
||||
|
|
@ -162,6 +165,8 @@ gitlink-cli api GET /v1/:owner/:repo/pulls/:id/versions/:version_id/diff
|
|||
|
||||
- ⛔ **GitLink 的 PR 操作必须用 `gitlink-cli pr`,不能用 `gh pr`。** `gh` 是 GitHub CLI,无法操作 GitLink 平台。详见 [`../gitlink-shared/SKILL.md`](../gitlink-shared/SKILL.md) 的「工具使用边界」章节。
|
||||
- GitLink 的默认分支通常是 `master`(非 `main`),创建 PR 时注意 `--base` 参数
|
||||
- 创建或检查 PR 前可用 `pr +branches` 获取可选源/目标分支
|
||||
- `pr +check-can-merge` 使用远端 POST 检查接口,Agent 应先执行 `--dry-run`,确认后再加 `--yes`
|
||||
- 合并 PR 前建议先用 `pr +view` 确认状态
|
||||
- **PR 创建要求源分支与目标分支有实际代码差异**,否则返回"分支内容相同,无需创建合并请求"
|
||||
- PR 查看/合并/关闭/重开需要使用 `pull_request_number`(即网页 URL `/pulls/N` 中的序号,从 `pr +list` 返回)
|
||||
|
|
|
|||
Loading…
Reference in New Issue