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  }