From a36e9c92c3a2db097439055813441308b0c3ada5 Mon Sep 17 00:00:00 2001 From: Taoyouce Date: Fri, 10 Jul 2026 11:39:12 +0000 Subject: [PATCH] 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). --- README.md | 3 +++ README.zh-CN.md | 3 +++ internal/i18n/locales/en-US.json | 2 ++ internal/i18n/locales/zh-CN.json | 2 ++ shortcuts/issue/issue.go | 24 ++++++++++++++++++++++++ shortcuts/issue/issue_test.go | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 66 insertions(+) diff --git a/README.md b/README.md index e5e4318..04ec107 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/README.zh-CN.md b/README.zh-CN.md index 6a8879d..45a2707 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -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 diff --git a/internal/i18n/locales/en-US.json b/internal/i18n/locales/en-US.json index 0739395..44a215e 100644 --- a/internal/i18n/locales/en-US.json +++ b/internal/i18n/locales/en-US.json @@ -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", diff --git a/internal/i18n/locales/zh-CN.json b/internal/i18n/locales/zh-CN.json index 2e6fc4d..deec23a 100644 --- a/internal/i18n/locales/zh-CN.json +++ b/internal/i18n/locales/zh-CN.json @@ -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", diff --git a/shortcuts/issue/issue.go b/shortcuts/issue/issue.go index b19027e..b0a3503 100644 --- a/shortcuts/issue/issue.go +++ b/shortcuts/issue/issue.go @@ -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"), diff --git a/shortcuts/issue/issue_test.go b/shortcuts/issue/issue_test.go index 48be057..0112c0d 100644 --- a/shortcuts/issue/issue_test.go +++ b/shortcuts/issue/issue_test.go @@ -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) {