From 7dfaa328aaee082640e643cf36cee91b42506dd0 Mon Sep 17 00:00:00 2001 From: benebsiny Date: Thu, 15 Feb 2024 23:16:00 +0800 Subject: [PATCH] Support for `[HOST/]OWNER/REPO` format --- pkg/cmd/project/link/link.go | 72 +++++++++++++++++++++++---- pkg/cmd/project/link/link_test.go | 60 +++++++++++++++++++++- pkg/cmd/project/unlink/unlink.go | 72 +++++++++++++++++++++++---- pkg/cmd/project/unlink/unlink_test.go | 58 +++++++++++++++++++++ 4 files changed, 241 insertions(+), 21 deletions(-) diff --git a/pkg/cmd/project/link/link.go b/pkg/cmd/project/link/link.go index 41a9795e1..0003dd98e 100644 --- a/pkg/cmd/project/link/link.go +++ b/pkg/cmd/project/link/link.go @@ -13,10 +13,12 @@ import ( "github.com/spf13/cobra" "net/http" "strconv" + "strings" ) type linkOpts struct { number int32 + host string owner string repo string team string @@ -78,6 +80,10 @@ func NewCmdLink(f *cmdutil.Factory, runF func(config linkConfig) error) *cobra.C return err } + if err = validateRepoOrTeamFlag(&opts); err != nil { + return err + } + config := linkConfig{ httpClient: f.HttpClient, config: f.Config, @@ -102,6 +108,49 @@ func NewCmdLink(f *cmdutil.Factory, runF func(config linkConfig) error) *cobra.C return linkCmd } +func validateRepoOrTeamFlag(opts *linkOpts) error { + + linkedTarget := "" + if opts.repo != "" { + linkedTarget = opts.repo + } else if opts.team != "" { + linkedTarget = opts.team + } + + if strings.Contains(linkedTarget, "/") { + nameArgs := strings.Split(linkedTarget, "/") + var host, owner, name string + + if len(nameArgs) == 2 { + owner = nameArgs[0] + name = nameArgs[1] + } else if len(nameArgs) == 3 { + host = nameArgs[0] + owner = nameArgs[1] + name = nameArgs[2] + } else { + if opts.repo != "" { + return fmt.Errorf("expected the \"[HOST/]OWNER/REPO\" or \"REPO\" format, got \"%s\"", linkedTarget) + } else if opts.team != "" { + return fmt.Errorf("expected the \"[HOST/]OWNER/TEAM\" or \"TEAM\" format, got \"%s\"", linkedTarget) + } + } + + if opts.owner != "" && opts.owner != owner { + return fmt.Errorf("'%s' has different owner from '%s'", linkedTarget, opts.owner) + } + + opts.owner = owner + opts.host = host + if opts.repo != "" { + opts.repo = name + } else if opts.team != "" { + opts.team = name + } + } + return nil +} + func runLink(config linkConfig) error { canPrompt := config.io.CanPrompt() owner, err := config.client.NewOwner(canPrompt, config.opts.owner) @@ -125,22 +174,25 @@ func runLink(config linkConfig) error { } c := api.NewClientFromHTTP(httpClient) - cfg, err := config.config() - if err != nil { - return err + if config.opts.host == "" { + cfg, err := config.config() + if err != nil { + return err + } + host, _ := cfg.Authentication().DefaultHost() + config.opts.host = host } - host, _ := cfg.Authentication().DefaultHost() if config.opts.repo != "" { - return linkRepo(c, owner, host, config) + return linkRepo(c, owner, config) } else if config.opts.team != "" { - return linkTeam(c, owner, host, config) + return linkTeam(c, owner, config) } return nil } -func linkRepo(c *api.Client, owner *queries.Owner, host string, config linkConfig) error { - repo, err := api.GitHubRepo(c, ghrepo.NewWithHost(owner.Login, config.opts.repo, host)) +func linkRepo(c *api.Client, owner *queries.Owner, config linkConfig) error { + repo, err := api.GitHubRepo(c, ghrepo.NewWithHost(owner.Login, config.opts.repo, config.opts.host)) if err != nil { return err } @@ -154,8 +206,8 @@ func linkRepo(c *api.Client, owner *queries.Owner, host string, config linkConfi return printResults(config, owner, config.opts.repo) } -func linkTeam(c *api.Client, owner *queries.Owner, host string, config linkConfig) error { - team, err := api.OrganizationTeam(c, host, owner.Login, config.opts.team) +func linkTeam(c *api.Client, owner *queries.Owner, config linkConfig) error { + team, err := api.OrganizationTeam(c, config.opts.host, owner.Login, config.opts.team) if err != nil { return err } diff --git a/pkg/cmd/project/link/link_test.go b/pkg/cmd/project/link/link_test.go index fa3611353..cd9aa074e 100644 --- a/pkg/cmd/project/link/link_test.go +++ b/pkg/cmd/project/link/link_test.go @@ -38,8 +38,8 @@ func TestNewCmdLink(t *testing.T) { name: "specify-nothing", cli: "", wants: linkOpts{ - repo: "REPO", owner: "OWNER", + repo: "REPO", }, }, { @@ -49,6 +49,35 @@ func TestNewCmdLink(t *testing.T) { repo: "my-repo", }, }, + { + name: "repo-flag-contains-owner", + cli: "--repo monalisa/my-repo", + wants: linkOpts{ + owner: "monalisa", + repo: "my-repo", + }, + }, + { + name: "repo-flag-contains-owner-and-host", + cli: "--repo github.com/monalisa/my-repo", + wants: linkOpts{ + host: "github.com", + owner: "monalisa", + repo: "my-repo", + }, + }, + { + name: "repo-flag-contains-wrong-format", + cli: "--repo h/e/l/l/o", + wantsErr: true, + wantsErrMsg: "expected the \"[HOST/]OWNER/REPO\" or \"REPO\" format, got \"h/e/l/l/o\"", + }, + { + name: "repo-flag-with-owner-different-from-owner-flag", + cli: "--repo monalisa/my-repo --owner leonardo", + wantsErr: true, + wantsErrMsg: "'monalisa/my-repo' has different owner from 'leonardo'", + }, { name: "team", cli: "--team my-team", @@ -56,6 +85,35 @@ func TestNewCmdLink(t *testing.T) { team: "my-team", }, }, + { + name: "team-flag-contains-owner", + cli: "--team my-org/my-team", + wants: linkOpts{ + owner: "my-org", + team: "my-team", + }, + }, + { + name: "team-flag-contains-owner-and-host", + cli: "--team github.com/my-org/my-team", + wants: linkOpts{ + host: "github.com", + owner: "my-org", + team: "my-team", + }, + }, + { + name: "team-flag-contains-wrong-format", + cli: "--team h/e/l/l/o", + wantsErr: true, + wantsErrMsg: "expected the \"[HOST/]OWNER/TEAM\" or \"TEAM\" format, got \"h/e/l/l/o\"", + }, + { + name: "team-flag-with-owner-different-from-owner-flag", + cli: "--team my-org/my-team --owner her-org", + wantsErr: true, + wantsErrMsg: "'my-org/my-team' has different owner from 'her-org'", + }, { name: "number", cli: "123 --repo my-repo", diff --git a/pkg/cmd/project/unlink/unlink.go b/pkg/cmd/project/unlink/unlink.go index a4b8e8e02..39928f357 100644 --- a/pkg/cmd/project/unlink/unlink.go +++ b/pkg/cmd/project/unlink/unlink.go @@ -13,10 +13,12 @@ import ( "github.com/spf13/cobra" "net/http" "strconv" + "strings" ) type unlinkOpts struct { number int32 + host string owner string repo string team string @@ -78,6 +80,10 @@ func NewCmdUnlink(f *cmdutil.Factory, runF func(config unlinkConfig) error) *cob return err } + if err = validateRepoOrTeamFlag(&opts); err != nil { + return err + } + config := unlinkConfig{ httpClient: f.HttpClient, config: f.Config, @@ -102,6 +108,49 @@ func NewCmdUnlink(f *cmdutil.Factory, runF func(config unlinkConfig) error) *cob return linkCmd } +func validateRepoOrTeamFlag(opts *unlinkOpts) error { + + unlinkedTarget := "" + if opts.repo != "" { + unlinkedTarget = opts.repo + } else if opts.team != "" { + unlinkedTarget = opts.team + } + + if strings.Contains(unlinkedTarget, "/") { + nameArgs := strings.Split(unlinkedTarget, "/") + var host, owner, name string + + if len(nameArgs) == 2 { + owner = nameArgs[0] + name = nameArgs[1] + } else if len(nameArgs) == 3 { + host = nameArgs[0] + owner = nameArgs[1] + name = nameArgs[2] + } else { + if opts.repo != "" { + return fmt.Errorf("expected the \"[HOST/]OWNER/REPO\" or \"REPO\" format, got \"%s\"", unlinkedTarget) + } else if opts.team != "" { + return fmt.Errorf("expected the \"[HOST/]OWNER/TEAM\" or \"TEAM\" format, got \"%s\"", unlinkedTarget) + } + } + + if opts.owner != "" && opts.owner != owner { + return fmt.Errorf("'%s' has different owner from '%s'", unlinkedTarget, opts.owner) + } + + opts.owner = owner + opts.host = host + if opts.repo != "" { + opts.repo = name + } else if opts.team != "" { + opts.team = name + } + } + return nil +} + func runUnlink(config unlinkConfig) error { canPrompt := config.io.CanPrompt() owner, err := config.client.NewOwner(canPrompt, config.opts.owner) @@ -125,22 +174,25 @@ func runUnlink(config unlinkConfig) error { } c := api.NewClientFromHTTP(httpClient) - cfg, err := config.config() - if err != nil { - return err + if config.opts.host == "" { + cfg, err := config.config() + if err != nil { + return err + } + host, _ := cfg.Authentication().DefaultHost() + config.opts.host = host } - host, _ := cfg.Authentication().DefaultHost() if config.opts.repo != "" { - return unlinkRepo(c, owner, host, config) + return unlinkRepo(c, owner, config) } else if config.opts.team != "" { - return unlinkTeam(c, owner, host, config) + return unlinkTeam(c, owner, config) } return nil } -func unlinkRepo(c *api.Client, owner *queries.Owner, host string, config unlinkConfig) error { - repo, err := api.GitHubRepo(c, ghrepo.NewWithHost(owner.Login, config.opts.repo, host)) +func unlinkRepo(c *api.Client, owner *queries.Owner, config unlinkConfig) error { + repo, err := api.GitHubRepo(c, ghrepo.NewWithHost(owner.Login, config.opts.repo, config.opts.host)) if err != nil { return err } @@ -154,8 +206,8 @@ func unlinkRepo(c *api.Client, owner *queries.Owner, host string, config unlinkC return printResults(config, owner, config.opts.repo) } -func unlinkTeam(c *api.Client, owner *queries.Owner, host string, config unlinkConfig) error { - team, err := api.OrganizationTeam(c, host, owner.Login, config.opts.team) +func unlinkTeam(c *api.Client, owner *queries.Owner, config unlinkConfig) error { + team, err := api.OrganizationTeam(c, config.opts.host, owner.Login, config.opts.team) if err != nil { return err } diff --git a/pkg/cmd/project/unlink/unlink_test.go b/pkg/cmd/project/unlink/unlink_test.go index a1cc13aa4..7735fbe0c 100644 --- a/pkg/cmd/project/unlink/unlink_test.go +++ b/pkg/cmd/project/unlink/unlink_test.go @@ -49,6 +49,35 @@ func TestNewCmdUnlink(t *testing.T) { repo: "my-repo", }, }, + { + name: "repo-flag-contains-owner", + cli: "--repo monalisa/my-repo", + wants: unlinkOpts{ + owner: "monalisa", + repo: "my-repo", + }, + }, + { + name: "repo-flag-contains-owner-and-host", + cli: "--repo github.com/monalisa/my-repo", + wants: unlinkOpts{ + host: "github.com", + owner: "monalisa", + repo: "my-repo", + }, + }, + { + name: "repo-flag-contains-wrong-format", + cli: "--repo h/e/l/l/o", + wantsErr: true, + wantsErrMsg: "expected the \"[HOST/]OWNER/REPO\" or \"REPO\" format, got \"h/e/l/l/o\"", + }, + { + name: "repo-flag-with-owner-different-from-owner-flag", + cli: "--repo monalisa/my-repo --owner leonardo", + wantsErr: true, + wantsErrMsg: "'monalisa/my-repo' has different owner from 'leonardo'", + }, { name: "team", cli: "--team my-team", @@ -56,6 +85,35 @@ func TestNewCmdUnlink(t *testing.T) { team: "my-team", }, }, + { + name: "team-flag-contains-owner", + cli: "--team my-org/my-team", + wants: unlinkOpts{ + owner: "my-org", + team: "my-team", + }, + }, + { + name: "team-flag-contains-owner-and-host", + cli: "--team github.com/my-org/my-team", + wants: unlinkOpts{ + host: "github.com", + owner: "my-org", + team: "my-team", + }, + }, + { + name: "team-flag-contains-wrong-format", + cli: "--team h/e/l/l/o", + wantsErr: true, + wantsErrMsg: "expected the \"[HOST/]OWNER/TEAM\" or \"TEAM\" format, got \"h/e/l/l/o\"", + }, + { + name: "team-flag-with-owner-different-from-owner-flag", + cli: "--team my-org/my-team --owner her-org", + wantsErr: true, + wantsErrMsg: "'my-org/my-team' has different owner from 'her-org'", + }, { name: "number", cli: "123 --repo my-repo",