forked from Gitlink/gitlink-cli
feat(repo): add readme shortcut
This commit is contained in:
parent
42bc83295a
commit
65cd7f6199
|
|
@ -151,6 +151,9 @@ gitlink-cli repo +list
|
||||||
# View repository info
|
# View repository info
|
||||||
gitlink-cli repo +info --owner Gitlink --repo forgeplus
|
gitlink-cli repo +info --owner Gitlink --repo forgeplus
|
||||||
|
|
||||||
|
# Read repository README
|
||||||
|
gitlink-cli repo +readme --owner Gitlink --repo forgeplus --ref master
|
||||||
|
|
||||||
# Create a repository
|
# Create a repository
|
||||||
gitlink-cli repo +create -n my-project -d "Project description"
|
gitlink-cli repo +create -n my-project -d "Project description"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,9 @@ gitlink-cli repo +list
|
||||||
# 查看仓库信息
|
# 查看仓库信息
|
||||||
gitlink-cli repo +info --owner Gitlink --repo forgeplus
|
gitlink-cli repo +info --owner Gitlink --repo forgeplus
|
||||||
|
|
||||||
|
# 读取仓库 README
|
||||||
|
gitlink-cli repo +readme --owner Gitlink --repo forgeplus --ref master
|
||||||
|
|
||||||
# 创建仓库
|
# 创建仓库
|
||||||
gitlink-cli repo +create -n my-project -d "项目描述"
|
gitlink-cli repo +create -n my-project -d "项目描述"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@ func New() (*Client, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Do(method, path string, body interface{}, query url.Values) (*output.Envelope, error) {
|
func (c *Client) Do(method, path string, body interface{}, query url.Values) (*output.Envelope, error) {
|
||||||
|
path = normalizeAPIPath(c.BaseURL, path)
|
||||||
|
|
||||||
// Append .json suffix if not already present (GitLink API convention)
|
// Append .json suffix if not already present (GitLink API convention)
|
||||||
// Handle paths that may already contain query strings (e.g., /path?key=val)
|
// Handle paths that may already contain query strings (e.g., /path?key=val)
|
||||||
if idx := strings.Index(path, "?"); idx != -1 {
|
if idx := strings.Index(path, "?"); idx != -1 {
|
||||||
|
|
@ -158,6 +160,18 @@ func (c *Client) Do(method, path string, body interface{}, query url.Values) (*o
|
||||||
return output.SuccessEnvelope(raw, meta), nil
|
return output.SuccessEnvelope(raw, meta), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeAPIPath(baseURL, path string) string {
|
||||||
|
if strings.HasSuffix(strings.TrimRight(baseURL, "/"), "/api") {
|
||||||
|
switch {
|
||||||
|
case path == "/api":
|
||||||
|
return ""
|
||||||
|
case strings.HasPrefix(path, "/api/"):
|
||||||
|
return strings.TrimPrefix(path, "/api")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) Get(path string, query url.Values) (*output.Envelope, error) {
|
func (c *Client) Get(path string, query url.Values) (*output.Envelope, error) {
|
||||||
return c.Do("GET", path, nil, query)
|
return c.Do("GET", path, nil, query)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNormalizeAPIPathStripsDuplicateAPIPrefix(t *testing.T) {
|
||||||
|
got := normalizeAPIPath("https://www.gitlink.org.cn/api", "/api/v1/repos/Gitlink/gitlink-cli/contents/README.md")
|
||||||
|
want := "/v1/repos/Gitlink/gitlink-cli/contents/README.md"
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("normalizeAPIPath() = %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNormalizeAPIPathKeepsRegularPath(t *testing.T) {
|
||||||
|
got := normalizeAPIPath("https://www.gitlink.org.cn/api", "/projects")
|
||||||
|
want := "/projects"
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("normalizeAPIPath() = %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNormalizeAPIPathKeepsAPIPrefixForNonAPIBaseURL(t *testing.T) {
|
||||||
|
got := normalizeAPIPath("https://www.gitlink.org.cn", "/api/v1/repos/Gitlink/gitlink-cli")
|
||||||
|
want := "/api/v1/repos/Gitlink/gitlink-cli"
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("normalizeAPIPath() = %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -52,6 +52,31 @@ func Shortcuts() []*common.Shortcut {
|
||||||
return ctx.Output(env)
|
return ctx.Output(env)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "readme",
|
||||||
|
Description: "Show repository README content",
|
||||||
|
Flags: []common.Flag{
|
||||||
|
{Name: "ref", Usage: "Branch, tag, or commit SHA"},
|
||||||
|
{Name: "path", Usage: "README directory path"},
|
||||||
|
},
|
||||||
|
Run: func(ctx *common.RuntimeContext) error {
|
||||||
|
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
q := url.Values{}
|
||||||
|
if ref := ctx.Arg("ref"); ref != "" {
|
||||||
|
q.Set("ref", ref)
|
||||||
|
}
|
||||||
|
if path := ctx.Arg("path"); path != "" {
|
||||||
|
q.Set("filepath", path)
|
||||||
|
}
|
||||||
|
env, err := ctx.CallAPIWithQuery("GET", ctx.RepoPath()+"/readme", q)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ctx.Output(env)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "create",
|
Name: "create",
|
||||||
Description: "Create a new repository",
|
Description: "Create a new repository",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gitlink-org/gitlink-cli/internal/client"
|
||||||
|
"github.com/gitlink-org/gitlink-cli/shortcuts/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRepoReadmeUsesRepositoryReadmeEndpoint(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "GET" || r.URL.Path != "/owner/repo/readme.json" {
|
||||||
|
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
|
||||||
|
}
|
||||||
|
if got := r.URL.Query().Get("ref"); got != "main" {
|
||||||
|
t.Fatalf("ref query = %q, want main", got)
|
||||||
|
}
|
||||||
|
if got := r.URL.Query().Get("filepath"); got != "docs" {
|
||||||
|
t.Fatalf("filepath query = %q, want docs", got)
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
|
"type": "file",
|
||||||
|
"name": "README.md",
|
||||||
|
"content": "# docs\n",
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("write response: %v", err)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
err := runRepoShortcut(server, "readme", map[string]string{
|
||||||
|
"ref": "main",
|
||||||
|
"path": "docs",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("readme shortcut failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRepoShortcut(server *httptest.Server, name string, args map[string]string) error {
|
||||||
|
for _, shortcut := range Shortcuts() {
|
||||||
|
if shortcut.Name != name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ctx := &common.RuntimeContext{
|
||||||
|
Client: &client.Client{
|
||||||
|
HTTP: server.Client(),
|
||||||
|
BaseURL: server.URL,
|
||||||
|
},
|
||||||
|
Owner: "owner",
|
||||||
|
Repo: "repo",
|
||||||
|
Format: "json",
|
||||||
|
Args: args,
|
||||||
|
}
|
||||||
|
return shortcut.Run(ctx)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("shortcut %q not found", name)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue