feat(repo): add repository units shortcuts
This commit is contained in:
parent
71ca2bb683
commit
8d61e4723f
|
|
@ -250,6 +250,10 @@ gitlink-cli repo +unfollow --owner Gitlink --repo forgeplus --project-id 123
|
|||
gitlink-cli repo +like --owner Gitlink --repo forgeplus
|
||||
gitlink-cli repo +unlike --owner Gitlink --repo forgeplus --project-id 123
|
||||
|
||||
# List and update repository navigation units
|
||||
gitlink-cli repo +units --owner Gitlink --repo forgeplus
|
||||
gitlink-cli repo +set-units --owner Gitlink --repo forgeplus --units code,issues,pulls,wiki
|
||||
|
||||
# Create a repository
|
||||
gitlink-cli repo +create -n my-project -d "Project description"
|
||||
|
||||
|
|
|
|||
|
|
@ -261,6 +261,10 @@ gitlink-cli repo +unfollow --owner Gitlink --repo forgeplus --project-id 123
|
|||
gitlink-cli repo +like --owner Gitlink --repo forgeplus
|
||||
gitlink-cli repo +unlike --owner Gitlink --repo forgeplus --project-id 123
|
||||
|
||||
# 查看和更新仓库导航模块
|
||||
gitlink-cli repo +units --owner Gitlink --repo forgeplus
|
||||
gitlink-cli repo +set-units --owner Gitlink --repo forgeplus --units code,issues,pulls,wiki
|
||||
|
||||
# 创建仓库
|
||||
gitlink-cli repo +create -n my-project -d "项目描述"
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
# Repository Units Shortcut
|
||||
|
||||
## Summary
|
||||
|
||||
Adds repository navigation unit shortcuts for the GitLink project settings API.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
gitlink-cli repo +units --owner Gitlink --repo forgeplus
|
||||
gitlink-cli repo +set-units --owner Gitlink --repo forgeplus --units code,issues,pulls,wiki
|
||||
```
|
||||
|
||||
## Behavior
|
||||
|
||||
- `repo +units` calls `GET /{owner}/{repo}/project_units`.
|
||||
- `repo +set-units` calls `POST /{owner}/{repo}/project_units` with `unit_types`.
|
||||
- `--units` accepts a comma-separated list and validates values before making a request.
|
||||
- Duplicate unit names are removed while preserving order.
|
||||
|
||||
## Validation
|
||||
|
||||
Allowed units are `code`, `issues`, `pulls`, `devops`, `versions`, `wiki`, `services`, and `resources`.
|
||||
|
||||
## Tests
|
||||
|
||||
- Unit tests cover the read endpoint, update request body, duplicate handling, and invalid input rejection.
|
||||
|
|
@ -86,8 +86,10 @@
|
|||
"cmd.repo.fork.short": "Fork a repository",
|
||||
"cmd.repo.info.short": "Show repository details",
|
||||
"cmd.repo.list.short": "List repositories for a user or organization",
|
||||
"cmd.repo.set_units.short": "Set enabled repository navigation units",
|
||||
"cmd.repo.short": "Repository operations",
|
||||
"cmd.repo.tree.short": "List repository files and directories",
|
||||
"cmd.repo.units.short": "List enabled repository navigation units",
|
||||
"cmd.root.long": "Manage repositories, issues, pull requests, releases, CI and workflows on GitLink.",
|
||||
"cmd.root.short": "GitLink CLI - command-line tool for GitLink",
|
||||
"cmd.search.repos.short": "Search repositories",
|
||||
|
|
@ -214,6 +216,7 @@
|
|||
"flag.repo.private": "Make repository private (true/false)",
|
||||
"flag.repo.tree.path": "Directory path to list (default: repository root)",
|
||||
"flag.repo.tree.ref": "Branch, tag, or commit ref",
|
||||
"flag.repo.units": "Comma-separated units: code,issues,pulls,devops,versions,wiki,services,resources",
|
||||
"flag.search.keyword": "Search keyword",
|
||||
"flag.sort_by": "Sort field",
|
||||
"flag.sort_direction": "Sort direction: asc, desc",
|
||||
|
|
|
|||
|
|
@ -86,8 +86,10 @@
|
|||
"cmd.repo.fork.short": "Fork 仓库",
|
||||
"cmd.repo.info.short": "显示仓库详情",
|
||||
"cmd.repo.list.short": "列出用户或组织的仓库",
|
||||
"cmd.repo.set_units.short": "设置启用的仓库导航模块",
|
||||
"cmd.repo.short": "仓库操作",
|
||||
"cmd.repo.tree.short": "列出仓库文件和目录",
|
||||
"cmd.repo.units.short": "列出启用的仓库导航模块",
|
||||
"cmd.root.long": "用于管理 GitLink 上的仓库、议题、拉取请求、发布、CI 和工作流。",
|
||||
"cmd.root.short": "GitLink CLI - GitLink 命令行工具",
|
||||
"cmd.search.repos.short": "搜索仓库",
|
||||
|
|
@ -214,6 +216,7 @@
|
|||
"flag.repo.private": "设为私有仓库(true/false)",
|
||||
"flag.repo.tree.path": "要列出的目录路径(默认:仓库根目录)",
|
||||
"flag.repo.tree.ref": "分支、标签或提交引用",
|
||||
"flag.repo.units": "逗号分隔的模块:code,issues,pulls,devops,versions,wiki,services,resources",
|
||||
"flag.search.keyword": "搜索关键词",
|
||||
"flag.sort_by": "排序字段",
|
||||
"flag.sort_direction": "排序方向:asc、desc",
|
||||
|
|
|
|||
|
|
@ -81,6 +81,41 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
|
|||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "units",
|
||||
Description: tr.T("cmd.repo.units.short"),
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
env, err := ctx.CallAPI("GET", repoUnitsPath(ctx), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "set-units",
|
||||
Description: tr.T("cmd.repo.set_units.short"),
|
||||
Flags: []common.Flag{
|
||||
{Name: "units", Short: "u", Usage: tr.T("flag.repo.units"), Required: true},
|
||||
},
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
units, err := parseRepoUnits(ctx.Arg("units"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env, err := ctx.CallAPI("POST", repoUnitsPath(ctx), map[string]interface{}{"unit_types": units})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "tree",
|
||||
Description: tr.T("cmd.repo.tree.short"),
|
||||
|
|
@ -500,3 +535,41 @@ func parseRepoPositiveInt(value, name string) (int, error) {
|
|||
}
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func repoUnitsPath(ctx *common.RuntimeContext) string {
|
||||
return ctx.RepoPath() + "/project_units"
|
||||
}
|
||||
|
||||
func parseRepoUnits(raw string) ([]string, error) {
|
||||
allowed := map[string]bool{
|
||||
"code": true,
|
||||
"issues": true,
|
||||
"pulls": true,
|
||||
"devops": true,
|
||||
"versions": true,
|
||||
"wiki": true,
|
||||
"services": true,
|
||||
"resources": true,
|
||||
}
|
||||
|
||||
seen := map[string]bool{}
|
||||
units := []string{}
|
||||
for _, part := range strings.Split(raw, ",") {
|
||||
unit := strings.ToLower(strings.TrimSpace(part))
|
||||
if unit == "" {
|
||||
continue
|
||||
}
|
||||
if !allowed[unit] {
|
||||
return nil, fmt.Errorf("invalid repository unit %q; allowed values: code,issues,pulls,devops,versions,wiki,services,resources", unit)
|
||||
}
|
||||
if seen[unit] {
|
||||
continue
|
||||
}
|
||||
seen[unit] = true
|
||||
units = append(units, unit)
|
||||
}
|
||||
if len(units) == 0 {
|
||||
return nil, fmt.Errorf("at least one repository unit is required")
|
||||
}
|
||||
return units, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/gitlink-org/gitlink-cli/internal/client"
|
||||
|
|
@ -152,6 +153,68 @@ func TestRepoReadmeUsesRepositoryReadmeEndpoint(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRepoUnitsUsesProjectUnitsEndpoint(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assertRequest(t, r, "GET", "/owner/repo/project_units.json")
|
||||
writeJSON(t, w, []string{"code", "issues", "pulls"})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
if err := runShortcut(t, server, "units", nil); err != nil {
|
||||
t.Fatalf("units shortcut failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoSetUnitsSendsValidatedUnitTypes(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assertRequest(t, r, "POST", "/owner/repo/project_units.json")
|
||||
var body struct {
|
||||
UnitTypes []string `json:"unit_types"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
t.Fatalf("decode request: %v", err)
|
||||
}
|
||||
want := []string{"code", "issues", "pulls"}
|
||||
if !reflect.DeepEqual(body.UnitTypes, want) {
|
||||
t.Fatalf("unit_types = %#v, want %#v", body.UnitTypes, want)
|
||||
}
|
||||
writeJSON(t, w, map[string]interface{}{
|
||||
"status": 0,
|
||||
"message": "success",
|
||||
})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runShortcut(t, server, "set-units", map[string]string{
|
||||
"units": " code,issues,pulls,issues ",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("set-units shortcut failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoSetUnitsRejectsInvalidUnitBeforeRequest(t *testing.T) {
|
||||
called := false
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
called = true
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runShortcut(t, server, "set-units", map[string]string{"units": "code,invalid"})
|
||||
if err == nil {
|
||||
t.Fatalf("expected invalid unit error")
|
||||
}
|
||||
if called {
|
||||
t.Fatalf("server was called for invalid unit")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRepoUnitsRejectsEmptyInput(t *testing.T) {
|
||||
if _, err := parseRepoUnits(" , "); err == nil {
|
||||
t.Fatalf("expected empty unit list error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTreeListsRootOnDefaultRef(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assertRequest(t, r, "GET", "/owner/repo/sub_entries.json")
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ skills/
|
|||
| Skill | 说明 | 常用命令 |
|
||||
|-------|------|----------|
|
||||
| **gitlink-shared** | 认证、全局参数、API 参考、安全规则、分支约定 | `auth login`, `auth status` |
|
||||
| **gitlink-repo** | 仓库管理与洞察 | `repo +list`, `repo +info`, `repo +languages`, `repo +contributors`, `repo +code-stats`, `repo +follow`, `repo +like` |
|
||||
| **gitlink-repo** | 仓库管理与洞察 | `repo +list`, `repo +info`, `repo +languages`, `repo +contributors`, `repo +code-stats`, `repo +follow`, `repo +like`, `repo +units`, `repo +set-units` |
|
||||
| **gitlink-issue** | Issue 管理 | `issue +create`, `issue +list`, `issue +view`, `issue +close`, `issue +batch-close`, `issue +batch-update`, `issue +batch-delete` |
|
||||
| **gitlink-pr** | Pull Request | `pr +list`, `pr +create`, `pr +view`, `pr +merge`, `pr +versions`, `pr +version-diff`, `pr +reviews`, `pr +review` |
|
||||
| **gitlink-member** | 仓库成员管理 | `member +list`, `member +add`, `member +batch-add`, `member +role`, `member +invite-link` |
|
||||
|
|
|
|||
|
|
@ -82,6 +82,17 @@ gitlink-cli repo +fork --owner Gitlink --repo forgeplus
|
|||
gitlink-cli repo +delete --owner myuser --repo old-project
|
||||
```
|
||||
|
||||
## Repository Units
|
||||
|
||||
Use these shortcuts to inspect or update the repository navigation modules shown in GitLink:
|
||||
|
||||
```bash
|
||||
gitlink-cli repo +units --owner Gitlink --repo forgeplus
|
||||
gitlink-cli repo +set-units --owner Gitlink --repo forgeplus --units code,issues,pulls,wiki
|
||||
```
|
||||
|
||||
Allowed unit values: `code`, `issues`, `pulls`, `devops`, `versions`, `wiki`, `services`, `resources`.
|
||||
|
||||
## Raw API 补充
|
||||
|
||||
Shortcuts 未覆盖的仓库操作可用 Raw API:
|
||||
|
|
|
|||
Loading…
Reference in New Issue