From a383b0a00b5cffc0c60ee4a939fa3471d8223ce7 Mon Sep 17 00:00:00 2001 From: maidamaliziasimnw Date: Fri, 10 Jul 2026 16:45:06 +0000 Subject: [PATCH] =?UTF-8?q?fix(api):=20=E5=8D=95=E6=AC=A1=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E6=94=AF=E6=8C=81=20:owner/:repo=20=E4=B8=8E=20{{owne?= =?UTF-8?q?r}}/{{repo}}=20=E5=8D=A0=E4=BD=8D=E7=AC=A6=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=EF=BC=88issue=20#20=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- cmd/api/api.go | 27 +++++++++++++++++++++++++ cmd/api/api_test.go | 49 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/cmd/api/api.go b/cmd/api/api.go index cae531a..1e01309 100644 --- a/cmd/api/api.go +++ b/cmd/api/api.go @@ -13,6 +13,7 @@ import ( "github.com/gitlink-org/gitlink-cli/cmd/cmdutil" "github.com/gitlink-org/gitlink-cli/internal/client" + repocontext "github.com/gitlink-org/gitlink-cli/internal/context" "github.com/gitlink-org/gitlink-cli/internal/i18n" "github.com/gitlink-org/gitlink-cli/internal/output" ) @@ -73,6 +74,11 @@ func runAPI(c *cobra.Command, args []string) error { path = "/" + path } + path, err := resolvePathPlaceholders(path) + if err != nil { + return err + } + cli, err := client.New() if err != nil { return err @@ -107,6 +113,27 @@ func runAPI(c *cobra.Command, args []string) error { return output.Print(env, resolveFormat()) } +// resolvePathPlaceholders substitutes :owner/:repo (and {{owner}}/{{repo}}) +// segments in a single-call path with the global --owner/--repo flags or the +// values auto-resolved from the current git remote, matching the help-text +// examples. Paths without placeholders are returned unchanged. +func resolvePathPlaceholders(path string) (string, error) { + hasColon := strings.Contains(path, "/:owner") || strings.Contains(path, "/:repo") + hasBrace := strings.Contains(path, "{{owner}}") || strings.Contains(path, "{{repo}}") + if !hasColon && !hasBrace { + return path, nil + } + owner, repo, err := repocontext.ResolveOwnerRepo(cmdutil.Owner, cmdutil.Repo) + if err != nil { + return "", fmt.Errorf("path contains :owner/:repo placeholders: %w", err) + } + path = strings.ReplaceAll(path, "/:owner", "/"+owner) + path = strings.ReplaceAll(path, "/:repo", "/"+repo) + path = strings.ReplaceAll(path, "{{owner}}", owner) + path = strings.ReplaceAll(path, "{{repo}}", repo) + return path, nil +} + func readJSONBody(c *cobra.Command) (interface{}, error) { bodyStr, _ := c.Flags().GetString("body") bodyFile, _ := c.Flags().GetString("body-file") diff --git a/cmd/api/api_test.go b/cmd/api/api_test.go index dad41a8..2060267 100644 --- a/cmd/api/api_test.go +++ b/cmd/api/api_test.go @@ -32,6 +32,55 @@ func TestResolveFormat(t *testing.T) { } } +func TestResolvePathPlaceholders(t *testing.T) { + origOwner, origRepo := cmdutil.Owner, cmdutil.Repo + t.Cleanup(func() { cmdutil.Owner, cmdutil.Repo = origOwner, origRepo }) + cmdutil.Owner, cmdutil.Repo = "demo-owner", "demo-repo" + + tests := []struct { + name string + path string + want string + }{ + {"colon placeholders", "/:owner/:repo/issues", "/demo-owner/demo-repo/issues"}, + {"colon with suffix", "/:owner/:repo/issues/42", "/demo-owner/demo-repo/issues/42"}, + {"brace placeholders", "/{{owner}}/{{repo}}/pulls", "/demo-owner/demo-repo/pulls"}, + {"no placeholders unchanged", "/users/me", "/users/me"}, + {"literal path unchanged", "/Gitlink/gitlink-cli/issues", "/Gitlink/gitlink-cli/issues"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := resolvePathPlaceholders(tt.path) + if err != nil { + t.Fatalf("resolvePathPlaceholders(%q): %v", tt.path, err) + } + if got != tt.want { + t.Fatalf("resolvePathPlaceholders(%q) = %q, want %q", tt.path, got, tt.want) + } + }) + } +} + +func TestResolvePathPlaceholdersUnresolvable(t *testing.T) { + origOwner, origRepo := cmdutil.Owner, cmdutil.Repo + t.Cleanup(func() { cmdutil.Owner, cmdutil.Repo = origOwner, origRepo }) + cmdutil.Owner, cmdutil.Repo = "", "" + + tmp := t.TempDir() + origWD, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _ = os.Chdir(origWD) }) + if err := os.Chdir(tmp); err != nil { + t.Fatal(err) + } + + if _, err := resolvePathPlaceholders("/:owner/:repo/issues"); err == nil { + t.Fatal("expected error when owner/repo cannot be resolved") + } +} + func TestNewAPICmd(t *testing.T) { cmd := NewAPICmd() if cmd.Use != "api ( | --batch-file )" {