github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/run/cancel/cancel.go (about) 1 package cancel 2 3 import ( 4 "errors" 5 "fmt" 6 "net/http" 7 8 "github.com/ungtb10d/cli/v2/api" 9 "github.com/ungtb10d/cli/v2/internal/ghrepo" 10 "github.com/ungtb10d/cli/v2/pkg/cmd/run/shared" 11 "github.com/ungtb10d/cli/v2/pkg/cmdutil" 12 "github.com/ungtb10d/cli/v2/pkg/iostreams" 13 "github.com/spf13/cobra" 14 ) 15 16 type CancelOptions struct { 17 HttpClient func() (*http.Client, error) 18 IO *iostreams.IOStreams 19 BaseRepo func() (ghrepo.Interface, error) 20 21 Prompt bool 22 23 RunID string 24 } 25 26 func NewCmdCancel(f *cmdutil.Factory, runF func(*CancelOptions) error) *cobra.Command { 27 opts := &CancelOptions{ 28 IO: f.IOStreams, 29 HttpClient: f.HttpClient, 30 } 31 32 cmd := &cobra.Command{ 33 Use: "cancel [<run-id>]", 34 Short: "Cancel a workflow run", 35 Args: cobra.MaximumNArgs(1), 36 RunE: func(cmd *cobra.Command, args []string) error { 37 // support `-R, --repo` override 38 opts.BaseRepo = f.BaseRepo 39 40 if len(args) > 0 { 41 opts.RunID = args[0] 42 } else if !opts.IO.CanPrompt() { 43 return cmdutil.FlagErrorf("run ID required when not running interactively") 44 } else { 45 opts.Prompt = true 46 } 47 48 if runF != nil { 49 return runF(opts) 50 } 51 52 return runCancel(opts) 53 }, 54 } 55 56 return cmd 57 } 58 59 func runCancel(opts *CancelOptions) error { 60 httpClient, err := opts.HttpClient() 61 if err != nil { 62 return fmt.Errorf("failed to create http client: %w", err) 63 } 64 client := api.NewClientFromHTTP(httpClient) 65 66 cs := opts.IO.ColorScheme() 67 68 repo, err := opts.BaseRepo() 69 if err != nil { 70 return fmt.Errorf("failed to determine base repo: %w", err) 71 } 72 73 runID := opts.RunID 74 var run *shared.Run 75 76 if opts.Prompt { 77 runs, err := shared.GetRunsWithFilter(client, repo, nil, 10, func(run shared.Run) bool { 78 return run.Status != shared.Completed 79 }) 80 if err != nil { 81 return fmt.Errorf("failed to get runs: %w", err) 82 } 83 if len(runs) == 0 { 84 return fmt.Errorf("found no in progress runs to cancel") 85 } 86 runID, err = shared.PromptForRun(cs, runs) 87 if err != nil { 88 return err 89 } 90 // TODO silly stopgap until dust settles and PromptForRun can just return a run 91 for _, r := range runs { 92 if fmt.Sprintf("%d", r.ID) == runID { 93 run = &r 94 break 95 } 96 } 97 } else { 98 run, err = shared.GetRun(client, repo, runID) 99 if err != nil { 100 var httpErr api.HTTPError 101 if errors.As(err, &httpErr) { 102 if httpErr.StatusCode == http.StatusNotFound { 103 err = fmt.Errorf("Could not find any workflow run with ID %s", opts.RunID) 104 } 105 } 106 return err 107 } 108 } 109 110 err = cancelWorkflowRun(client, repo, fmt.Sprintf("%d", run.ID)) 111 if err != nil { 112 var httpErr api.HTTPError 113 if errors.As(err, &httpErr) { 114 if httpErr.StatusCode == http.StatusConflict { 115 err = fmt.Errorf("Cannot cancel a workflow run that is completed") 116 } 117 } 118 119 return err 120 } 121 122 fmt.Fprintf(opts.IO.Out, "%s Request to cancel workflow submitted.\n", cs.SuccessIcon()) 123 124 return nil 125 } 126 127 func cancelWorkflowRun(client *api.Client, repo ghrepo.Interface, runID string) error { 128 path := fmt.Sprintf("repos/%s/actions/runs/%s/cancel", ghrepo.FullName(repo), runID) 129 130 err := client.REST(repo.RepoHost(), "POST", path, nil, nil) 131 if err != nil { 132 return err 133 } 134 135 return nil 136 }