From 281107e6b7071d7dd7e7347f6104053dc785bd95 Mon Sep 17 00:00:00 2001 From: Shawn Yu Date: Mon, 13 May 2024 14:58:09 -0400 Subject: [PATCH] feat: dispatch command --- golang/cmd/dispatch/dispatch.go | 88 +++++++++++++++++++++++++++++++++ golang/cmd/push/push.go | 2 - golang/cmd/root.go | 6 +-- golang/gh/gh.go | 42 +++++++++++++--- golang/git/git.go | 9 ++++ golang/main.go | 1 + golang/utils/utils.go | 4 +- 7 files changed, 138 insertions(+), 14 deletions(-) create mode 100644 golang/cmd/dispatch/dispatch.go diff --git a/golang/cmd/dispatch/dispatch.go b/golang/cmd/dispatch/dispatch.go new file mode 100644 index 0000000..7ba5997 --- /dev/null +++ b/golang/cmd/dispatch/dispatch.go @@ -0,0 +1,88 @@ +package dispatch + +import ( + "github.com/charmbracelet/log" + "github.com/shawnyu5/gh-ac/cmd" + "github.com/shawnyu5/gh-ac/gh" + "github.com/shawnyu5/gh-ac/git" + "github.com/shawnyu5/gh-ac/utils" + + "github.com/spf13/cobra" +) + +type cmdFlags struct { + // Name of workflowName + workflowName string + // Branch or commit to reference. Defaults to current branch + githubRef string + // Input to pass to workflow, in the form KEY=VALUE + body string +} + +var flags cmdFlags + +// dispatchCmd represents the dispatch command +var dispatchCmd = &cobra.Command{ + Use: "dispatch", + Short: "Create a workflow dispatch event, and open the workflow in the browser", + Run: func(cmd *cobra.Command, args []string) { + var workflowName string + _ = workflowName + + if flags.workflowName != "" { + workflowName = flags.workflowName + } else { + name, err := utils.SelectRepoWorkflowName() + if err != nil { + log.Fatalf("Failed to select target workflow: %w", err) + } + workflowName = *name + } + + var githubRef string + if flags.githubRef != "" { + githubRef = flags.githubRef + } else { + var err error + githubRef, err = git.CurrentBranchName() + if err != nil { + log.Fatalf("Failed to get current branch name: %s", err) + } + } + + s := utils.RandomSpinner("Looking for new workflow run\n") + + newWorkflow, err := utils.TrackNewWorkflowRun(workflowName, func() { + _, err := gh.New[any](). + Arg("workflow"). + Arg("run"). + Arg(workflowName). + Arg("--ref"). + Arg(githubRef). + ParseOutputJson(false). + Exec() + if err != nil { + log.Fatal(err) + } + + s.Start() + }) + if err != nil { + log.Fatalf("Failed to track new workflow: %w", err) + } + + err = utils.OpenInBrowser([]string{newWorkflow.GetHTMLURL()}) + if err != nil { + log.Fatalf("Failed to open workflow in browser: %w", err) + } + s.Stop() + + }, +} + +func init() { + cmd.RootCmd.AddCommand(dispatchCmd) + dispatchCmd.Flags().StringVarP(&flags.workflowName, "workflow", "w", "", "case insensitive name for the workflow name track") + dispatchCmd.Flags().StringVar(&flags.githubRef, "ref", "", "the branch or tag name which contains the version of the workflow file you'd like to run") + dispatchCmd.Flags().StringVarP(&flags.body, "form", "f", "", "Input to pass to workflow, in the form KEY=VALUE") +} diff --git a/golang/cmd/push/push.go b/golang/cmd/push/push.go index 6dd3812..b0e3b81 100644 --- a/golang/cmd/push/push.go +++ b/golang/cmd/push/push.go @@ -11,8 +11,6 @@ import ( type cmdFlags struct { // Name of workflowName workflowName string - // Toggle print the URL to workflowName instead of opening it in browser. Defaults to false - printUrl bool } var flags cmdFlags diff --git a/golang/cmd/root.go b/golang/cmd/root.go index e231be2..1437534 100644 --- a/golang/cmd/root.go +++ b/golang/cmd/root.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "github.com/charmbracelet/log" + "github.com/shawnyu5/gh-ac/git" "github.com/spf13/cobra" "os" ) @@ -18,13 +19,12 @@ var RootCmd = &cobra.Command{ Use: "ac", Short: "Fire off Github action workflow runs, and open it in the browser", PersistentPreRun: func(cmd *cobra.Command, args []string) { + fmt.Println(git.CurrentBranchName()) if flags.debug { log.SetLevel(log.DebugLevel) + } }, - //Run: func(cmd *cobra.Command, args []string) { - // fmt.Println("HEYYY") - //}, } func init() { diff --git a/golang/gh/gh.go b/golang/gh/gh.go index d069433..4a88e6e 100644 --- a/golang/gh/gh.go +++ b/golang/gh/gh.go @@ -1,3 +1,4 @@ +// Package gh A wrapper around `gh`, that takes into account configured `hostname` package gh import ( @@ -11,11 +12,19 @@ import ( type Cmd[T any] struct { // Arguments passed to the command args []string + // Append `--hostname` flag the gh cli command. + // + // Some commands such as `gh workflow run` does not support the `--hostname` flag. Default: false + appendHostName bool + // Parse the output of the gh command as json. Default: true + parseOutputJson bool } // New create a new instance of Cmd func New[T any]() *Cmd[T] { - return &Cmd[T]{} + return &Cmd[T]{ + parseOutputJson: true, + } } // Arg appends an argument to the command @@ -24,25 +33,44 @@ func (c *Cmd[T]) Arg(a string) *Cmd[T] { return c } +// AppendHostName appends the host name to the gh cli command. +// +// Default: false +func (c *Cmd[T]) AppendHostName() *Cmd[T] { + c.appendHostName = true + return c +} + +// ParseOutputJson toggles parsing the output as json. Defaults: true +func (c *Cmd[T]) ParseOutputJson(a bool) *Cmd[T] { + c.parseOutputJson = a + return c +} + // Exec executes the gh command with the args, appending the hostname flag if configured // // Returns output of the command parsed into `T` func (c *Cmd[T]) Exec() (output *T, err error) { - con, err := config.Load() + cfg, err := config.Load() if err != nil { return nil, err } - if con.HostName != "" { - c.args = append(c.args, "--hostname", con.HostName) + if c.appendHostName && cfg.HostName != "" { + c.args = append(c.args, "--hostname", cfg.HostName) } - // If we dont need to fetch all pages, then execute and return the result - var jsonResult T log.Debugf("Executing command `gh` with arguments %s", c.args) - stdout, _, err := gh.Exec(c.args...) + stdout, stderr, err := gh.Exec(c.args...) if err != nil { + log.Error(stderr.String()) return nil, err } + + if !c.parseOutputJson { + return nil, nil + } + + var jsonResult T err = json.Unmarshal(stdout.Bytes(), &jsonResult) if err != nil { return nil, err diff --git a/golang/git/git.go b/golang/git/git.go index 471243d..a051395 100644 --- a/golang/git/git.go +++ b/golang/git/git.go @@ -1,6 +1,8 @@ +// Package git a wrapper around `git` package git import ( + "bytes" "fmt" "os/exec" ) @@ -33,3 +35,10 @@ func Commit(args []string) error { fmt.Println(string(output)) return err } + +// CurrentBranchName get the current branch name +func CurrentBranchName() (string, error) { + args := []string{"rev-parse", "--abbrev-ref", "HEAD"} + output, err := exec.Command("git", args...).CombinedOutput() + return string(bytes.TrimSpace(output)), err +} diff --git a/golang/main.go b/golang/main.go index 2cc4240..1692e53 100644 --- a/golang/main.go +++ b/golang/main.go @@ -3,6 +3,7 @@ package main import ( "github.com/shawnyu5/gh-ac/cmd" _ "github.com/shawnyu5/gh-ac/cmd/config" + _ "github.com/shawnyu5/gh-ac/cmd/dispatch" _ "github.com/shawnyu5/gh-ac/cmd/force" _ "github.com/shawnyu5/gh-ac/cmd/push" ) diff --git a/golang/utils/utils.go b/golang/utils/utils.go index e0cffb1..ca00d12 100644 --- a/golang/utils/utils.go +++ b/golang/utils/utils.go @@ -19,7 +19,7 @@ import ( // // Will return an error if no workflow with `name` is found func GetWorkflowRunByName(name string) (*github.WorkflowRun, error) { - workflowRuns, err := gh.New[github.WorkflowRuns]().Arg("api").Arg("/repos/{owner}/{repo}/actions/runs").Exec() + workflowRuns, err := gh.New[github.WorkflowRuns]().Arg("api").Arg("/repos/{owner}/{repo}/actions/runs").AppendHostName().Exec() if err != nil { return nil, err } @@ -86,7 +86,7 @@ func SelectRepoWorkflowName() (workflowName *string, err error) { var repoWorkflowDefinitions []*github.Workflow page := 1 for { - workflows, err := gh.New[github.Workflows]().Arg("api").Arg(fmt.Sprintf("/repos/{owner}/{repo}/actions/workflows?per_page=100&page=%d", page)).Exec() + workflows, err := gh.New[github.Workflows]().Arg("api").Arg(fmt.Sprintf("/repos/{owner}/{repo}/actions/workflows?per_page=100&page=%d", page)).AppendHostName().Exec() if err != nil { return nil, err }