Merge pull request 'feat(shortcut): add shortcuts/ignore' (#153) from co63oc/gitlink-cli:fix3 into master

This commit is contained in:
wbtiger 2026-06-14 18:29:41 +08:00
commit 1c84cac060
7 changed files with 192 additions and 1 deletions

View File

@ -467,6 +467,16 @@ gitlink-cli pipeline +disable --owner Gitlink --repo forgeplus --id 7 --workflow
gitlink-cli pipeline +delete --owner Gitlink --repo forgeplus --id 7 --dry-run
```
### Ignore File Templates
```bash
# List all available .gitignore templates
gitlink-cli ignore +list
# Filter templates by name
gitlink-cli ignore +list --name Go
```
### Search
```bash

View File

@ -445,6 +445,16 @@ gitlink-cli pipeline +disable --owner Gitlink --repo forgeplus --id 7 --workflow
gitlink-cli pipeline +delete --owner Gitlink --repo forgeplus --id 7 --dry-run
```
### 忽略文件模板
```bash
# 列出所有可用的 .gitignore 模板
gitlink-cli ignore +list
# 按名称筛选模板
gitlink-cli ignore +list --name Go
```
### 搜索
```bash

View File

@ -0,0 +1,8 @@
# Ignore shortcut
新增 `ignore` Shortcut 组,补齐 GitLink 忽略文件模板(`.gitignore`)查询:
- `ignore +list`
同时补充了单元测试、README 示例。

View File

@ -0,0 +1,36 @@
package ignore
import (
"net/url"
"github.com/gitlink-org/gitlink-cli/shortcuts/common"
)
// Shortcuts returns ignore-file management shortcuts.
//
// These shortcuts provide access to the GitLink ignore-file registry,
// which lists all available .gitignore templates supported by the platform.
func Shortcuts() []*common.Shortcut {
return []*common.Shortcut{
{
Name: "list",
Description: "List available ignore-file templates",
Flags: []common.Flag{
{Name: "name", Short: "n", Usage: "Filter ignore templates by name"},
},
Run: runList,
},
}
}
func runList(ctx *common.RuntimeContext) error {
q := url.Values{}
if name := ctx.Arg("name"); name != "" {
q.Set("name", name)
}
env, err := ctx.CallAPIWithQuery("GET", "/ignores", q)
if err != nil {
return err
}
return ctx.Output(env)
}

View File

@ -0,0 +1,124 @@
package ignore
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/gitlink-org/gitlink-cli/internal/client"
"github.com/gitlink-org/gitlink-cli/shortcuts/common"
)
func runShortcut(t *testing.T, server *httptest.Server, name string, args map[string]string) error {
t.Helper()
shortcut := findShortcut(t, name)
ctx := &common.RuntimeContext{
Client: &client.Client{HTTP: server.Client(), BaseURL: server.URL},
Owner: "owner",
Repo: "repo",
Format: "json",
Args: args,
}
if ctx.Args == nil {
ctx.Args = map[string]string{}
}
return shortcut.Run(ctx)
}
func findShortcut(t *testing.T, name string) *common.Shortcut {
t.Helper()
for _, s := range Shortcuts() {
if s.Name == name {
return s
}
}
t.Fatalf("shortcut %q not found", name)
return nil
}
func writeJSON(t *testing.T, w http.ResponseWriter, payload interface{}) {
t.Helper()
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(payload); err != nil {
t.Fatalf("failed to write response: %v", err)
}
}
func TestIgnoreList(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
t.Fatalf("got method %s, want GET", r.Method)
}
if r.URL.Path != "/ignores.json" {
t.Fatalf("got path %s, want /ignores.json", r.URL.Path)
}
if got := r.URL.Query().Get("name"); got != "" {
t.Fatalf("expected no name filter, got %q", got)
}
writeJSON(t, w, map[string]interface{}{
"ignores": []interface{}{
map[string]interface{}{"id": 1, "name": "Go"},
map[string]interface{}{"id": 2, "name": "Ada"},
},
})
}))
defer server.Close()
if err := runShortcut(t, server, "list", map[string]string{}); err != nil {
t.Fatalf("ignore list failed: %v", err)
}
}
func TestIgnoreListWithNameFilter(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
t.Fatalf("got method %s, want GET", r.Method)
}
if r.URL.Path != "/ignores.json" {
t.Fatalf("got path %s, want /ignores.json", r.URL.Path)
}
if got := r.URL.Query().Get("name"); got != "Ada" {
t.Fatalf("expected name=Ada, got %q", got)
}
writeJSON(t, w, map[string]interface{}{
"ignores": []interface{}{
map[string]interface{}{"id": 2, "name": "Ada"},
},
})
}))
defer server.Close()
if err := runShortcut(t, server, "list", map[string]string{"name": "Ada"}); err != nil {
t.Fatalf("ignore list with name filter failed: %v", err)
}
}
func TestIgnoreListHTTPError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("server error"))
}))
defer server.Close()
err := runShortcut(t, server, "list", map[string]string{})
if err == nil {
t.Fatal("expected error for HTTP 500")
}
}
func TestIgnoreListEmptyResult(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/ignores.json" {
t.Fatalf("got path %s, want /ignores.json", r.URL.Path)
}
writeJSON(t, w, map[string]interface{}{
"ignores": []interface{}{},
})
}))
defer server.Close()
if err := runShortcut(t, server, "list", map[string]string{"name": "NONEXISTENT"}); err != nil {
t.Fatalf("ignore list with empty result failed: %v", err)
}
}

View File

@ -9,6 +9,7 @@ import (
"github.com/gitlink-org/gitlink-cli/shortcuts/common"
"github.com/gitlink-org/gitlink-cli/shortcuts/compare"
"github.com/gitlink-org/gitlink-cli/shortcuts/health"
"github.com/gitlink-org/gitlink-cli/shortcuts/ignore"
"github.com/gitlink-org/gitlink-cli/shortcuts/issue"
"github.com/gitlink-org/gitlink-cli/shortcuts/label"
"github.com/gitlink-org/gitlink-cli/shortcuts/license"
@ -49,6 +50,7 @@ func RegisterAll(root *cobra.Command, translators ...*i18n.Translator) {
"compare": compare.Shortcuts(),
"webhook": webhook.Shortcuts(tr),
"health": health.Shortcuts(tr),
"ignore": ignore.Shortcuts(),
"workflow": workflow.Shortcuts(),
}
@ -70,6 +72,7 @@ func RegisterAll(root *cobra.Command, translators ...*i18n.Translator) {
"compare": "Compare branches, tags, or commits",
"webhook": tr.T("cmd.webhook.short"),
"health": "Project health data collection",
"ignore": tr.T("cmd.ignore.short"),
"workflow": "AI agent workflow analysis",
}

View File

@ -14,7 +14,7 @@ func TestRegisterAll(t *testing.T) {
"repo", "issue", "label", "license", "pr", "release", "branch",
"org", "user", "search", "ci", "workflow",
"compare", "member", "milestone", "pipeline", "webhook",
"health",
"health", "ignore",
}
groupSet := map[string]bool{}