Merge remote-tracking branch 'origin/master' into baoerjun_branch

This commit is contained in:
wauxing 2026-06-02 09:24:33 +08:00
commit ba23bf11a4
3 changed files with 0 additions and 325 deletions

View File

@ -1,122 +0,0 @@
package pm
import (
"fmt"
"net/url"
"strconv"
"github.com/gitlink-org/gitlink-cli/shortcuts/common"
)
func Shortcuts() []*common.Shortcut {
return []*common.Shortcut{
{
Name: "boards",
Description: "List kanban boards",
Flags: []common.Flag{
{Name: "page", Short: "p", Usage: "Page number", Default: "1"},
{Name: "limit", Short: "l", Usage: "Items per page", Default: "20"},
},
Run: func(ctx *common.RuntimeContext) error {
return listPM(ctx, "/pm/dashboards")
},
},
{
Name: "sprints",
Description: "List sprint issues",
Flags: []common.Flag{
{Name: "page", Short: "p", Usage: "Page number", Default: "1"},
{Name: "limit", Short: "l", Usage: "Items per page", Default: "20"},
},
Run: func(ctx *common.RuntimeContext) error {
return listPM(ctx, "/pm/sprint_issues")
},
},
{
Name: "weekly",
Description: "List weekly reports",
Flags: []common.Flag{
{Name: "page", Short: "p", Usage: "Page number", Default: "1"},
{Name: "limit", Short: "l", Usage: "Items per page", Default: "20"},
},
Run: func(ctx *common.RuntimeContext) error {
return listPM(ctx, "/pm/weekly_issues")
},
},
{
Name: "tags",
Description: "List PM issue tags",
Flags: []common.Flag{
{Name: "page", Short: "p", Usage: "Page number", Default: "1"},
{Name: "limit", Short: "l", Usage: "Items per page", Default: "20"},
},
Run: func(ctx *common.RuntimeContext) error {
return listPM(ctx, "/pm/issue_tags")
},
},
{
Name: "pipelines",
Description: "List PM pipelines",
Flags: []common.Flag{
{Name: "page", Short: "p", Usage: "Page number", Default: "1"},
{Name: "limit", Short: "l", Usage: "Items per page", Default: "20"},
},
Run: func(ctx *common.RuntimeContext) error {
return listPM(ctx, "/pm/pipelines")
},
},
{
Name: "actions",
Description: "List action run records",
Flags: []common.Flag{
{Name: "page", Short: "p", Usage: "Page number", Default: "1"},
{Name: "limit", Short: "l", Usage: "Items per page", Default: "20"},
},
Run: func(ctx *common.RuntimeContext) error {
return listPM(ctx, "/pm/action_runs")
},
},
}
}
func listPM(ctx *common.RuntimeContext, endpoint string) error {
if err := ctx.ResolveOwnerRepo(); err != nil {
return err
}
projectID, err := fetchProjectID(ctx)
if err != nil {
return err
}
q := url.Values{}
q.Set("project_id", strconv.Itoa(projectID))
q.Set("owner", ctx.Owner)
q.Set("repo", ctx.Repo)
q.Set("page", ctx.Arg("page"))
q.Set("limit", ctx.Arg("limit"))
env, err := ctx.CallAPIRawWithQuery("GET", endpoint, q)
if err != nil {
return err
}
return ctx.Output(env)
}
func fetchProjectID(ctx *common.RuntimeContext) (int, error) {
env, err := ctx.CallAPI("GET", ctx.RepoPath(), nil)
if err != nil {
return 0, fmt.Errorf("获取项目信息失败: %w", err)
}
data, ok := env.Data.(map[string]interface{})
if !ok {
return 0, fmt.Errorf("无法解析项目信息")
}
if idFloat, ok := data["repo_id"].(float64); ok {
return int(idFloat), nil
}
if idFloat, ok := data["project_id"].(float64); ok {
return int(idFloat), nil
}
if idFloat, ok := data["id"].(float64); ok {
return int(idFloat), nil
}
return 0, fmt.Errorf("项目 ID 未找到,请确认仓库是否存在")
}

View File

@ -1,200 +0,0 @@
package pm
import (
"net/http"
"testing"
"github.com/gitlink-org/gitlink-cli/shortcuts/common"
)
func TestFetchProjectID(t *testing.T) {
cases := []struct {
name string
response map[string]interface{}
wantID int
}{
{"repo_id", map[string]interface{}{"repo_id": float64(100)}, 100},
{"project_id", map[string]interface{}{"project_id": float64(200)}, 200},
{"id", map[string]interface{}{"id": float64(300)}, 300},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
resp := tc.response
server := common.NewTestServer(t, func(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" && r.URL.Path == "/owner/repo.json" {
common.WriteJSON(t, w, resp)
return
}
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
})
defer server.Close()
ctx := common.NewTestContext(t, server, "owner", "repo", map[string]string{})
id, err := fetchProjectID(ctx)
if err != nil {
t.Fatalf("fetchProjectID failed: %v", err)
}
if id != tc.wantID {
t.Fatalf("got %d, want %d", id, tc.wantID)
}
})
}
}
func TestFetchProjectIDNotFound(t *testing.T) {
server := common.NewTestServer(t, func(w http.ResponseWriter, r *http.Request) {
common.WriteJSON(t, w, map[string]interface{}{"name": "repo"})
})
defer server.Close()
ctx := common.NewTestContext(t, server, "owner", "repo", map[string]string{})
_, err := fetchProjectID(ctx)
if err == nil {
t.Fatal("expected error for missing project ID")
}
}
func TestPMBoards(t *testing.T) {
server := common.NewTestServer(t, func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "GET" && r.URL.Path == "/owner/repo.json":
common.WriteJSON(t, w, map[string]interface{}{"id": float64(123)})
case r.Method == "GET" && r.URL.Path == "/pm/dashboards":
common.WriteJSON(t, w, map[string]interface{}{
"boards": []interface{}{
map[string]interface{}{"id": 1, "name": "Sprint 1"},
map[string]interface{}{"id": 2, "name": "Sprint 2"},
},
})
default:
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
}
})
defer server.Close()
ctx := common.NewTestContext(t, server, "owner", "repo", map[string]string{})
err := common.RunShortcut(t, Shortcuts(), "boards", ctx)
if err != nil {
t.Fatalf("boards failed: %v", err)
}
}
func TestPMSprints(t *testing.T) {
server := common.NewTestServer(t, func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "GET" && r.URL.Path == "/owner/repo.json":
common.WriteJSON(t, w, map[string]interface{}{"id": float64(123)})
case r.Method == "GET" && r.URL.Path == "/pm/sprint_issues":
common.WriteJSON(t, w, map[string]interface{}{
"issues": []interface{}{
map[string]interface{}{"id": 10, "subject": "Task A"},
},
})
default:
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
}
})
defer server.Close()
ctx := common.NewTestContext(t, server, "owner", "repo", map[string]string{})
err := common.RunShortcut(t, Shortcuts(), "sprints", ctx)
if err != nil {
t.Fatalf("sprints failed: %v", err)
}
}
func TestPMWeekly(t *testing.T) {
server := common.NewTestServer(t, func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "GET" && r.URL.Path == "/owner/repo.json":
common.WriteJSON(t, w, map[string]interface{}{"id": float64(123)})
case r.Method == "GET" && r.URL.Path == "/pm/weekly_issues":
common.WriteJSON(t, w, map[string]interface{}{
"reports": []interface{}{
map[string]interface{}{"id": 1, "title": "Week 21"},
},
})
default:
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
}
})
defer server.Close()
ctx := common.NewTestContext(t, server, "owner", "repo", map[string]string{})
err := common.RunShortcut(t, Shortcuts(), "weekly", ctx)
if err != nil {
t.Fatalf("weekly failed: %v", err)
}
}
func TestPMTags(t *testing.T) {
server := common.NewTestServer(t, func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "GET" && r.URL.Path == "/owner/repo.json":
common.WriteJSON(t, w, map[string]interface{}{"id": float64(123)})
case r.Method == "GET" && r.URL.Path == "/pm/issue_tags":
common.WriteJSON(t, w, map[string]interface{}{
"tags": []interface{}{
map[string]interface{}{"id": 1, "name": "bug"},
},
})
default:
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
}
})
defer server.Close()
ctx := common.NewTestContext(t, server, "owner", "repo", map[string]string{})
err := common.RunShortcut(t, Shortcuts(), "tags", ctx)
if err != nil {
t.Fatalf("tags failed: %v", err)
}
}
func TestPMPipelines(t *testing.T) {
server := common.NewTestServer(t, func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "GET" && r.URL.Path == "/owner/repo.json":
common.WriteJSON(t, w, map[string]interface{}{"id": float64(123)})
case r.Method == "GET" && r.URL.Path == "/pm/pipelines":
common.WriteJSON(t, w, map[string]interface{}{
"pipelines": []interface{}{
map[string]interface{}{"id": 1, "name": "CI"},
},
})
default:
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
}
})
defer server.Close()
ctx := common.NewTestContext(t, server, "owner", "repo", map[string]string{})
err := common.RunShortcut(t, Shortcuts(), "pipelines", ctx)
if err != nil {
t.Fatalf("pipelines failed: %v", err)
}
}
func TestPMActions(t *testing.T) {
server := common.NewTestServer(t, func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "GET" && r.URL.Path == "/owner/repo.json":
common.WriteJSON(t, w, map[string]interface{}{"id": float64(123)})
case r.Method == "GET" && r.URL.Path == "/pm/action_runs":
common.WriteJSON(t, w, map[string]interface{}{
"runs": []interface{}{
map[string]interface{}{"id": 1, "status": "success"},
},
})
default:
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
}
})
defer server.Close()
ctx := common.NewTestContext(t, server, "owner", "repo", map[string]string{})
err := common.RunShortcut(t, Shortcuts(), "actions", ctx)
if err != nil {
t.Fatalf("actions failed: %v", err)
}
}

View File

@ -12,7 +12,6 @@ import (
"github.com/gitlink-org/gitlink-cli/shortcuts/member"
"github.com/gitlink-org/gitlink-cli/shortcuts/milestone"
"github.com/gitlink-org/gitlink-cli/shortcuts/org"
"github.com/gitlink-org/gitlink-cli/shortcuts/pm"
"github.com/gitlink-org/gitlink-cli/shortcuts/pr"
"github.com/gitlink-org/gitlink-cli/shortcuts/release"
"github.com/gitlink-org/gitlink-cli/shortcuts/repo"
@ -41,7 +40,6 @@ func RegisterAll(root *cobra.Command) {
"webhook": webhook.Shortcuts(),
"member": member.Shortcuts(),
"snippet": snippet.Shortcuts(),
"pm": pm.Shortcuts(),
"wiki": wiki.Shortcuts(),
}
@ -61,7 +59,6 @@ func RegisterAll(root *cobra.Command) {
"webhook": "Webhook operations",
"member": "Project member operations",
"snippet": "Local code snippet management",
"pm": "Project management (kanban, sprints, weekly reports)",
"wiki": "Wiki operations",
}