From f43afa75a62a037e2ba573712fb2fc480eca9ac8 Mon Sep 17 00:00:00 2001 From: farmyobutu5233 Date: Sun, 5 Jul 2026 12:06:21 +0000 Subject: [PATCH] fix(milestone): make +create --description/--due-date optional to match platform API Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../milestone-create-optional-fields.md | 20 +++++++++++++++ shortcuts/milestone/milestone.go | 14 +++++------ shortcuts/milestone/milestone_test.go | 25 +++++++++++++++++++ 3 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 doc/changes/milestone-create-optional-fields.md diff --git a/doc/changes/milestone-create-optional-fields.md b/doc/changes/milestone-create-optional-fields.md new file mode 100644 index 0000000..bc9ca2a --- /dev/null +++ b/doc/changes/milestone-create-optional-fields.md @@ -0,0 +1,20 @@ +# milestone +create 放宽必填参数 + +## 背景 + +`milestone +create` 之前把 `--description` 与 `--due-date` 标记为必填, +但平台 v1 API 创建里程碑仅要求 `name`(已在生产环境 +`POST /api/v1/:owner/:repo/milestones` 用仅含 `{"name": ...}` 的请求验证成功)。 +CLI 侧的额外限制迫使用户为可选字段编造内容。 + +## 变更 + +- `--description` 与 `--due-date` 改为可选;未提供时请求体中省略对应字段。 +- `milestonePayload` 的 `requireAll` 参数收敛为 `requireName`,仅校验 `--name`。 + +## 验证 + +- `go test ./shortcuts/milestone/`(新增 `TestMilestoneCreateNameOnly` + 断言未提供的可选字段不出现在请求体中) +- `go vet ./...` +- 生产 gitlink.org.cn 实测:`milestone +create -n ` 仅带名称创建成功。 diff --git a/shortcuts/milestone/milestone.go b/shortcuts/milestone/milestone.go index 1ec2b30..5a4213f 100644 --- a/shortcuts/milestone/milestone.go +++ b/shortcuts/milestone/milestone.go @@ -46,8 +46,8 @@ func Shortcuts() []*common.Shortcut { Description: "Create a milestone", Flags: []common.Flag{ {Name: "name", Short: "n", Usage: "Milestone name", Required: true}, - {Name: "description", Short: "d", Usage: "Milestone description", Required: true}, - {Name: "due-date", Usage: "Due date in YYYY-MM-DD format", Required: true}, + {Name: "description", Short: "d", Usage: "Milestone description"}, + {Name: "due-date", Usage: "Due date in YYYY-MM-DD format"}, }, Run: func(ctx *common.RuntimeContext) error { if err := ctx.ResolveOwnerRepo(); err != nil { @@ -168,7 +168,7 @@ func milestoneStatusPath(ctx *common.RuntimeContext, id string) string { return fmt.Sprintf("%s/milestones/%s/update_status", ctx.RepoPath(), url.PathEscape(id)) } -func milestonePayload(ctx *common.RuntimeContext, requireAll bool) (map[string]interface{}, error) { +func milestonePayload(ctx *common.RuntimeContext, requireName bool) (map[string]interface{}, error) { payload := map[string]interface{}{} if name := ctx.Arg("name"); name != "" { payload["name"] = name @@ -180,11 +180,9 @@ func milestonePayload(ctx *common.RuntimeContext, requireAll bool) (map[string]i payload["effective_date"] = dueDate } - if requireAll { - for _, name := range []string{"name", "description", "due-date"} { - if _, err := ctx.RequireArg(name); err != nil { - return nil, err - } + if requireName { + if _, err := ctx.RequireArg("name"); err != nil { + return nil, err } return payload, nil } diff --git a/shortcuts/milestone/milestone_test.go b/shortcuts/milestone/milestone_test.go index bf76193..caffd02 100644 --- a/shortcuts/milestone/milestone_test.go +++ b/shortcuts/milestone/milestone_test.go @@ -55,6 +55,31 @@ func TestMilestoneCreatePayload(t *testing.T) { assertEqual(t, payload["effective_date"], "2026-07-01") } +func TestMilestoneCreateNameOnly(t *testing.T) { + var payload map[string]interface{} + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assertRequest(t, r, "POST", "/v1/owner/repo/milestones.json") + payload = decodeJSON(t, r) + writeJSON(t, w, map[string]interface{}{"status": 0, "message": "success"}) + })) + defer server.Close() + + err := runMilestoneShortcut(t, server, "create", map[string]string{ + "name": "v1.1", + }) + if err != nil { + t.Fatalf("create shortcut failed: %v", err) + } + + assertEqual(t, payload["name"], "v1.1") + if _, ok := payload["description"]; ok { + t.Fatalf("description should be omitted when not provided") + } + if _, ok := payload["effective_date"]; ok { + t.Fatalf("effective_date should be omitted when not provided") + } +} + func TestMilestoneViewWithIssueFilters(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assertRequest(t, r, "GET", "/v1/owner/repo/milestones/7.json")