Support for unlink

This commit is contained in:
benebsiny 2024-01-19 23:35:18 +08:00
parent bb1ab90a9a
commit e209d14516
4 changed files with 149 additions and 10 deletions

View file

@ -138,6 +138,6 @@ func printResults(config linkConfig, repo queries.Repository) error {
return nil
}
_, err := fmt.Fprintf(config.io.Out, "%s\n", repo.Project.URL)
_, err := fmt.Fprintf(config.io.Out, "%s\n", repo.URL)
return err
}

View file

@ -19,6 +19,7 @@ import (
cmdLink "github.com/cli/cli/v2/pkg/cmd/project/link"
cmdList "github.com/cli/cli/v2/pkg/cmd/project/list"
cmdTemplate "github.com/cli/cli/v2/pkg/cmd/project/mark-template"
cmdUnlink "github.com/cli/cli/v2/pkg/cmd/project/unlink"
cmdView "github.com/cli/cli/v2/pkg/cmd/project/view"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/spf13/cobra"
@ -47,6 +48,7 @@ func NewCmdProject(f *cmdutil.Factory) *cobra.Command {
cmd.AddCommand(cmdLink.NewCmdLink(f, nil))
cmd.AddCommand(cmdView.NewCmdView(f, nil))
cmd.AddCommand(cmdTemplate.NewCmdMarkTemplate(f, nil))
cmd.AddCommand(cmdUnlink.NewCmdUnlink(f, nil))
// items
cmd.AddCommand(cmdItemList.NewCmdList(f, nil))

View file

@ -104,15 +104,9 @@ type PageInfo struct {
// Repository is a Repository GraphQL object https://docs.github.com/en/graphql/reference/objects#repository
type Repository struct {
Name string
ID string
URL string
Project struct {
Title string
ID string
Number int32
URL string
} `graphql:"projectV2(number: $projectNumber)"`
Name string
ID string
URL string
}
// Project is a ProjectV2 GraphQL object https://docs.github.com/en/graphql/reference/objects#projectv2.

View file

@ -0,0 +1,143 @@
package unlink
import (
"fmt"
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/pkg/cmd/project/shared/client"
"github.com/cli/cli/v2/pkg/cmd/project/shared/queries"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/shurcooL/githubv4"
"github.com/spf13/cobra"
"net/http"
"strconv"
)
type unlinkOpts struct {
number int32
owner string
repo string
projectID string
repoID string
format string
exporter cmdutil.Exporter
}
type unlinkConfig struct {
httpClient func() (*http.Client, error)
client *queries.Client
opts unlinkOpts
io *iostreams.IOStreams
}
type unlinkProjectFromRepoMutation struct {
UnlinkProjectV2FromRepository struct {
Repository queries.Repository `graphql:"repository"`
} `graphql:"unlinkProjectV2FromRepository(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",
Use: "unlink [<number>] [flag]",
Example: heredoc.Doc(`
# unlink monalisa's project "1" from her repository "my_repo"
gh project unlink 1 --repo my_repo
`),
RunE: func(cmd *cobra.Command, args []string) error {
client, err := client.New(f)
if err != nil {
return err
}
if len(args) == 1 {
num, err := strconv.ParseInt(args[0], 10, 32)
if err != nil {
return cmdutil.FlagErrorf("invalid number: %v", args[0])
}
opts.number = int32(num)
}
config := unlinkConfig{
httpClient: f.HttpClient,
client: client,
opts: opts,
io: f.IOStreams,
}
if config.opts.repo == "" {
return fmt.Errorf("required flag: repo")
}
// allow testing of the command without actually running it
if runF != nil {
return runF(config)
}
return runUnlink(config)
},
}
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")
cmdutil.AddFormatFlags(linkCmd, &opts.exporter)
return linkCmd
}
func runUnlink(config unlinkConfig) error {
canPrompt := config.io.CanPrompt()
owner, err := config.client.NewOwner(canPrompt, config.opts.owner)
if err != nil {
return err
}
project, err := config.client.NewProject(canPrompt, owner, config.opts.number, false)
if err != nil {
return err
}
config.opts.projectID = project.ID
httpClient, err := config.httpClient()
if err != nil {
return err
}
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
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)
}
return printResults(config, query.UnlinkProjectV2FromRepository.Repository)
}
func unlinkRepoArgs(config unlinkConfig) (*unlinkProjectFromRepoMutation, map[string]interface{}) {
return &unlinkProjectFromRepoMutation{}, map[string]interface{}{
"input": githubv4.UnlinkProjectV2FromRepositoryInput{
ProjectID: githubv4.ID(config.opts.projectID),
RepositoryID: githubv4.ID(config.opts.repoID),
},
}
}
func printResults(config unlinkConfig, repo queries.Repository) error {
if !config.io.IsStdoutTTY() {
return nil
}
_, err := fmt.Fprintf(config.io.Out, "%s\n", repo.URL)
return err
}