Support for team

This commit is contained in:
benebsiny 2024-01-20 22:46:22 +08:00
parent e209d14516
commit f98acb2d99
3 changed files with 149 additions and 40 deletions

View file

@ -19,8 +19,10 @@ type linkOpts struct {
number int32
owner string
repo string
team string
projectID string
repoID string
teamID string
format string
exporter cmdutil.Exporter
}
@ -38,14 +40,23 @@ type linkProjectToRepoMutation struct {
} `graphql:"linkProjectV2ToRepository(input:$input)"`
}
type linkProjectToTeamMutation struct {
LinkProjectV2ToTeam struct {
Team queries.Team `graphql:"team"`
} `graphql:"linkProjectV2ToTeam(input:$input)"`
}
func NewCmdLink(f *cmdutil.Factory, runF func(config linkConfig) error) *cobra.Command {
opts := linkOpts{}
linkCmd := &cobra.Command{
Short: "Link a project to a repository",
Short: "Link a project to a repository or a team",
Use: "link [<number>] [flag]",
Example: heredoc.Doc(`
# link monalisa's project "1" to her repository "my_repo"
gh project link 1 --repo my_repo
# link monalisa's project 1 to her repository "my_repo"
gh project link 1 --owner monalisa --repo my_repo
# link monalisa's project 1 to her team "my_team"
gh project link 1 --owner my_organization --team my_team
`),
RunE: func(cmd *cobra.Command, args []string) error {
client, err := client.New(f)
@ -68,8 +79,10 @@ func NewCmdLink(f *cmdutil.Factory, runF func(config linkConfig) error) *cobra.C
io: f.IOStreams,
}
if config.opts.repo == "" {
return fmt.Errorf("required flag: repo")
if config.opts.repo != "" && config.opts.team != "" {
return fmt.Errorf("specify only one repo or team")
} else if config.opts.repo == "" && config.opts.team == "" {
return fmt.Errorf("specify at least one repo or team")
}
// allow testing of the command without actually running it
@ -82,6 +95,7 @@ func NewCmdLink(f *cmdutil.Factory, runF func(config linkConfig) error) *cobra.C
linkCmd.Flags().StringVar(&opts.owner, "owner", "", "Login of the owner. Use \"@me\" for the current user.")
linkCmd.Flags().StringVarP(&opts.repo, "repo", "R", "", "The repository to be linked to this project")
linkCmd.Flags().StringVarP(&opts.team, "team", "T", "", "The team to be linked to this project")
cmdutil.AddFormatFlags(linkCmd, &opts.exporter)
return linkCmd
@ -106,22 +120,51 @@ func runLink(config linkConfig) error {
}
c := api.NewClientFromHTTP(httpClient)
repo, err := api.GitHubRepo(c, ghrepo.New(owner.Login, config.opts.repo))
if err != nil {
return err
}
config.opts.repoID = repo.ID
if config.opts.repo != "" {
repo, err := api.GitHubRepo(c, ghrepo.New(owner.Login, config.opts.repo))
if err != nil {
return err
}
config.opts.repoID = repo.ID
query, variable := linkRepoArgs(config)
err = config.client.Mutate("LinkProjectV2ToRepository", query, variable)
if err != nil {
return err
}
query, variable := linkRepoArgs(config)
err = config.client.Mutate("LinkProjectV2ToRepository", query, variable)
if err != nil {
return err
}
if config.opts.exporter != nil {
return config.opts.exporter.Write(config.io, query.LinkProjectV2ToRepository.Repository)
if config.opts.exporter != nil {
return config.opts.exporter.Write(config.io, query.LinkProjectV2ToRepository.Repository)
}
return printResults(config, query.LinkProjectV2ToRepository.Repository.URL)
} else if config.opts.team != "" {
teams, err := api.OrganizationTeams(c, ghrepo.New(owner.Login, ""))
if err != nil {
return err
}
for _, team := range teams {
if team.Slug == config.opts.team {
config.opts.teamID = team.ID
break
}
}
if config.opts.teamID == "" {
return fmt.Errorf("can't find team %s", config.opts.team)
}
query, variable := linkTeamArgs(config)
err = config.client.Mutate("LinkProjectV2ToTeam", query, variable)
if err != nil {
return err
}
if config.opts.exporter != nil {
return config.opts.exporter.Write(config.io, query.LinkProjectV2ToTeam.Team)
}
return printResults(config, query.LinkProjectV2ToTeam.Team.URL)
}
return printResults(config, query.LinkProjectV2ToRepository.Repository)
return fmt.Errorf("specify at least one repo or team")
}
func linkRepoArgs(config linkConfig) (*linkProjectToRepoMutation, map[string]interface{}) {
@ -133,11 +176,20 @@ func linkRepoArgs(config linkConfig) (*linkProjectToRepoMutation, map[string]int
}
}
func printResults(config linkConfig, repo queries.Repository) error {
func linkTeamArgs(config linkConfig) (*linkProjectToTeamMutation, map[string]interface{}) {
return &linkProjectToTeamMutation{}, map[string]interface{}{
"input": githubv4.LinkProjectV2ToTeamInput{
ProjectID: githubv4.ID(config.opts.projectID),
TeamID: githubv4.ID(config.opts.teamID),
},
}
}
func printResults(config linkConfig, url string) error {
if !config.io.IsStdoutTTY() {
return nil
}
_, err := fmt.Fprintf(config.io.Out, "%s\n", repo.URL)
_, err := fmt.Fprintf(config.io.Out, "%s\n", url)
return err
}

View file

@ -109,6 +109,11 @@ type Repository struct {
URL string
}
type Team struct {
ID string
URL string
}
// Project is a ProjectV2 GraphQL object https://docs.github.com/en/graphql/reference/objects#projectv2.
type Project struct {
Number int32

View file

@ -19,8 +19,10 @@ type unlinkOpts struct {
number int32
owner string
repo string
team string
projectID string
repoID string
teamID string
format string
exporter cmdutil.Exporter
}
@ -38,14 +40,23 @@ type unlinkProjectFromRepoMutation struct {
} `graphql:"unlinkProjectV2FromRepository(input:$input)"`
}
type unlinkProjectFromTeamMutation struct {
UnlinkProjectV2FromTeam struct {
Team queries.Team `graphql:"team"`
} `graphql:"unlinkProjectV2FromTeam(input:$input)"`
}
func NewCmdUnlink(f *cmdutil.Factory, runF func(config unlinkConfig) error) *cobra.Command {
opts := unlinkOpts{}
linkCmd := &cobra.Command{
Short: "Unlink a project from a repository",
Short: "Unlink a project from a repository or a team",
Use: "unlink [<number>] [flag]",
Example: heredoc.Doc(`
# unlink monalisa's project "1" from her repository "my_repo"
gh project unlink 1 --repo my_repo
# unlink monalisa's project 1 from her repository "my_repo"
gh project unlink 1 --owner monalisa --repo my_repo
# unlink monalisa's project 1 from her team "my_team"
gh project unlink 1 --owner my_organization --team my_team
`),
RunE: func(cmd *cobra.Command, args []string) error {
client, err := client.New(f)
@ -68,8 +79,10 @@ func NewCmdUnlink(f *cmdutil.Factory, runF func(config unlinkConfig) error) *cob
io: f.IOStreams,
}
if config.opts.repo == "" {
return fmt.Errorf("required flag: repo")
if config.opts.repo != "" && config.opts.team != "" {
return fmt.Errorf("specify only one repo or team")
} else if config.opts.repo == "" && config.opts.team == "" {
return fmt.Errorf("specify at least one repo or team")
}
// allow testing of the command without actually running it
@ -82,6 +95,7 @@ func NewCmdUnlink(f *cmdutil.Factory, runF func(config unlinkConfig) error) *cob
linkCmd.Flags().StringVar(&opts.owner, "owner", "", "Login of the owner. Use \"@me\" for the current user.")
linkCmd.Flags().StringVarP(&opts.repo, "repo", "R", "", "The repository to be unlinked from this project")
linkCmd.Flags().StringVarP(&opts.team, "team", "T", "", "The team to be unlinked from this project")
cmdutil.AddFormatFlags(linkCmd, &opts.exporter)
return linkCmd
@ -106,22 +120,51 @@ func runUnlink(config unlinkConfig) error {
}
c := api.NewClientFromHTTP(httpClient)
repo, err := api.GitHubRepo(c, ghrepo.New(owner.Login, config.opts.repo))
if err != nil {
return err
}
config.opts.repoID = repo.ID
if config.opts.repo != "" {
repo, err := api.GitHubRepo(c, ghrepo.New(owner.Login, config.opts.repo))
if err != nil {
return err
}
config.opts.repoID = repo.ID
query, variable := unlinkRepoArgs(config)
err = config.client.Mutate("UnlinkProjectV2FromRepository", query, variable)
if err != nil {
return err
}
query, variable := unlinkRepoArgs(config)
err = config.client.Mutate("UnlinkProjectV2FromRepository", query, variable)
if err != nil {
return err
}
if config.opts.exporter != nil {
return config.opts.exporter.Write(config.io, query.UnlinkProjectV2FromRepository.Repository)
if config.opts.exporter != nil {
return config.opts.exporter.Write(config.io, query.UnlinkProjectV2FromRepository.Repository)
}
return printResults(config, query.UnlinkProjectV2FromRepository.Repository.URL)
} else if config.opts.team != "" {
teams, err := api.OrganizationTeams(c, ghrepo.New(owner.Login, ""))
if err != nil {
return err
}
for _, team := range teams {
if team.Slug == config.opts.team {
config.opts.teamID = team.ID
break
}
}
if config.opts.teamID == "" {
return fmt.Errorf("can't find team %s", config.opts.team)
}
query, variable := unlinkTeamArgs(config)
err = config.client.Mutate("UnlinkProjectV2FromTeam", query, variable)
if err != nil {
return err
}
if config.opts.exporter != nil {
return config.opts.exporter.Write(config.io, query.UnlinkProjectV2FromTeam.Team)
}
return printResults(config, query.UnlinkProjectV2FromTeam.Team.URL)
}
return printResults(config, query.UnlinkProjectV2FromRepository.Repository)
return fmt.Errorf("specify at least one repo or team")
}
func unlinkRepoArgs(config unlinkConfig) (*unlinkProjectFromRepoMutation, map[string]interface{}) {
@ -133,11 +176,20 @@ func unlinkRepoArgs(config unlinkConfig) (*unlinkProjectFromRepoMutation, map[st
}
}
func printResults(config unlinkConfig, repo queries.Repository) error {
func unlinkTeamArgs(config unlinkConfig) (*unlinkProjectFromTeamMutation, map[string]interface{}) {
return &unlinkProjectFromTeamMutation{}, map[string]interface{}{
"input": githubv4.UnlinkProjectV2FromTeamInput{
ProjectID: githubv4.ID(config.opts.projectID),
TeamID: githubv4.ID(config.opts.teamID),
},
}
}
func printResults(config unlinkConfig, url string) error {
if !config.io.IsStdoutTTY() {
return nil
}
_, err := fmt.Fprintf(config.io.Out, "%s\n", repo.URL)
_, err := fmt.Fprintf(config.io.Out, "%s\n", url)
return err
}