feat(issue): add issue +delete with --yes confirmation
Single-issue deletion via DELETE /api/v1/:owner/:repo/issues/:number. The existing +batch-delete requires repo manager permissions and global database IDs; +delete works for the issue author using the web issue number. Destructive, so it refuses to run without --yes. Includes en/zh i18n keys, README examples, and unit tests. Production-verified on gitlink.org.cn (create -> delete -> 404 view).
This commit is contained in:
parent
9749a4c832
commit
a36e9c92c3
|
|
@ -339,6 +339,9 @@ gitlink-cli issue +update --owner Gitlink --repo forgeplus --number 123 --priori
|
|||
# Close an issue
|
||||
gitlink-cli issue +close --owner Gitlink --repo forgeplus -i 123
|
||||
|
||||
# Delete an issue (destructive; requires --yes)
|
||||
gitlink-cli issue +delete --owner Gitlink --repo forgeplus --number 123 --yes
|
||||
|
||||
# Preview batch close without changing data
|
||||
gitlink-cli issue +batch-close --owner Gitlink --repo forgeplus --numbers 123,124 --dry-run
|
||||
|
||||
|
|
|
|||
|
|
@ -350,6 +350,9 @@ gitlink-cli issue +update --owner Gitlink --repo forgeplus --number 123 --priori
|
|||
# 关闭 Issue
|
||||
gitlink-cli issue +close --owner Gitlink --repo forgeplus -i 123
|
||||
|
||||
# 删除 Issue(破坏性操作,需 --yes 确认)
|
||||
gitlink-cli issue +delete --owner Gitlink --repo forgeplus --number 123 --yes
|
||||
|
||||
# 预览批量关闭,不修改数据
|
||||
gitlink-cli issue +batch-close --owner Gitlink --repo forgeplus --numbers 123,124 --dry-run
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
"cmd.issue.close.short": "Close an issue",
|
||||
"cmd.issue.comment.short": "Add a comment to an issue",
|
||||
"cmd.issue.create.short": "Create a new issue",
|
||||
"cmd.issue.delete.short": "Delete an issue",
|
||||
"cmd.issue.list.short": "List issues",
|
||||
"cmd.issue.short": "Issue operations",
|
||||
"cmd.issue.update.short": "Update an issue",
|
||||
|
|
@ -158,6 +159,7 @@
|
|||
"flag.issue.batch_list.limit": "Maximum issues to return, capped at 100",
|
||||
"flag.issue.batch_process.limit": "Maximum issues to process, capped at 100",
|
||||
"flag.issue.body": "Issue description",
|
||||
"flag.issue.delete.yes": "Confirm issue deletion",
|
||||
"flag.issue.label": "Label ID",
|
||||
"flag.issue.label_filter": "Filter by existing label",
|
||||
"flag.issue.milestone": "Milestone ID",
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
"cmd.issue.close.short": "关闭议题",
|
||||
"cmd.issue.comment.short": "给议题添加评论",
|
||||
"cmd.issue.create.short": "创建新议题",
|
||||
"cmd.issue.delete.short": "删除议题",
|
||||
"cmd.issue.list.short": "列出议题",
|
||||
"cmd.issue.short": "议题操作",
|
||||
"cmd.issue.update.short": "更新议题",
|
||||
|
|
@ -158,6 +159,7 @@
|
|||
"flag.issue.batch_list.limit": "最多返回的议题数,上限 100",
|
||||
"flag.issue.batch_process.limit": "最多处理的议题数,上限 100",
|
||||
"flag.issue.body": "议题描述",
|
||||
"flag.issue.delete.yes": "确认删除议题",
|
||||
"flag.issue.label": "标签 ID",
|
||||
"flag.issue.label_filter": "按已有标签筛选",
|
||||
"flag.issue.milestone": "里程碑 ID",
|
||||
|
|
|
|||
|
|
@ -207,6 +207,30 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
|
|||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "delete",
|
||||
Description: tr.T("cmd.issue.delete.short"),
|
||||
Flags: appendIssueNumberFlags(
|
||||
common.Flag{Name: "yes", Usage: tr.T("flag.issue.delete.yes"), Bool: true, Default: "false"},
|
||||
),
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
number, err := issueNumberArg(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !parseBool(ctx.Arg("yes")) {
|
||||
return fmt.Errorf("delete is destructive; pass --yes to confirm deleting issue #%s", number)
|
||||
}
|
||||
env, err := ctx.CallAPI("DELETE", fmt.Sprintf("%s/issues/%s", v1RepoPath(ctx), number), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "update",
|
||||
Description: tr.T("cmd.issue.update.short"),
|
||||
|
|
|
|||
|
|
@ -417,6 +417,38 @@ func TestIssueCloseFetchFails(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// --- delete ---
|
||||
|
||||
func TestIssueDelete(t *testing.T) {
|
||||
var deletedPath string
|
||||
server := newIssueTestServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "DELETE" {
|
||||
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
|
||||
}
|
||||
deletedPath = r.URL.Path
|
||||
writeJSON(t, w, map[string]interface{}{"status": float64(0), "message": "success"})
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
err := runShortcut(t, server, "delete", map[string]string{"number": "42", "yes": "true"})
|
||||
if err != nil {
|
||||
t.Fatalf("delete failed: %v", err)
|
||||
}
|
||||
assertEqual(t, deletedPath, "/v1/owner/repo/issues/42.json")
|
||||
}
|
||||
|
||||
func TestIssueDeleteRequiresConfirmation(t *testing.T) {
|
||||
server := newIssueTestServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatalf("unexpected request without --yes: %s %s", r.Method, r.URL.Path)
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
err := runShortcut(t, server, "delete", map[string]string{"number": "42"})
|
||||
if err == nil {
|
||||
t.Fatal("expected error without --yes confirmation")
|
||||
}
|
||||
}
|
||||
|
||||
// --- update ---
|
||||
|
||||
func TestIssueUpdateTitle(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue