From ae7921d095c7ebad9ba4c4196a972762d9c72469 Mon Sep 17 00:00:00 2001 From: "Alex Ellis (OpenFaaS Ltd)" Date: Mon, 19 Aug 2024 15:27:17 +0100 Subject: [PATCH] Make jobs command far less verbose Add way to see URLs for each jobs Signed-off-by: Alex Ellis (OpenFaaS Ltd) --- cmd/jobs.go | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++- go.mod | 2 + go.sum | 4 ++ 3 files changed, 107 insertions(+), 2 deletions(-) diff --git a/cmd/jobs.go b/cmd/jobs.go index 769a35a..6d9fcd5 100644 --- a/cmd/jobs.go +++ b/cmd/jobs.go @@ -4,9 +4,14 @@ import ( "bytes" "encoding/json" "fmt" + "io" "net/http" "os" "strings" + "text/tabwriter" + "time" + + "github.com/docker/go-units" "github.com/self-actuated/actuated-cli/pkg" "github.com/spf13/cobra" @@ -29,6 +34,8 @@ func makeJobs() *cobra.Command { cmd.RunE = runJobsE + cmd.Flags().BoolP("verbose", "v", false, "Show additional columns in the output") + cmd.Flags().BoolP("json", "j", false, "Request output in JSON format") return cmd @@ -56,13 +63,20 @@ func runJobsE(cmd *cobra.Command, args []string) error { return err } + verbose, err := cmd.Flags().GetBool("verbose") + if err != nil { + return err + } + if len(pat) == 0 { return fmt.Errorf("pat is required") } c := pkg.NewClient(http.DefaultClient, os.Getenv("ACTUATED_URL")) - res, status, err := c.ListJobs(pat, owner, staff, requestJson) + acceptJSON := true + + res, status, err := c.ListJobs(pat, owner, staff, acceptJSON) if err != nil { return err @@ -80,9 +94,94 @@ func runJobsE(cmd *cobra.Command, args []string) error { return err } res = prettyJSON.String() + fmt.Println(res) + } else { + + var statuses []JobStatus + + if err := json.Unmarshal([]byte(res), &statuses); err != nil { + return err + } + printEvents(os.Stdout, statuses, verbose) } - fmt.Println(res) return nil } + +func printEvents(w io.Writer, statuses []JobStatus, verbose bool) { + tabwriter := tabwriter.NewWriter(w, 0, 0, 1, ' ', tabwriter.TabIndent) + if verbose { + fmt.Fprintf(tabwriter, "JOB ID\tOWNER\tREPO\tJOB\tRUNNER\tSERVER\tSTATUS\tSTARTED\tAGE\tLABELS\tURL\n") + } else { + fmt.Fprintf(tabwriter, "OWNER\tREPO\tJOB\tSTATUS\tAGE\tURL\n") + } + + for _, status := range statuses { + duration := "" + + if status.StartedAt != nil && !status.StartedAt.IsZero() { + duration = humanDuration(time.Since(*status.StartedAt)) + } + + if verbose { + fmt.Fprintf(tabwriter, "%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", + status.JobID, + status.Owner, + status.Repo, + status.JobName, + status.RunnerName, + status.AgentName, + status.Status, + status.StartedAt.Format(time.RFC3339), + duration, + strings.Join(status.Labels, ","), + fmt.Sprintf("https://github.com/%s/%s/runs/%d", status.Owner, status.Repo, status.JobID), + ) + } else { + fmt.Fprintf(tabwriter, "%s\t%s\t%s\t%s\t%s\t%s\n", + status.Owner, + status.Repo, + status.JobName, + status.Status, + duration, + fmt.Sprintf("https://github.com/%s/%s/runs/%d", status.Owner, status.Repo, status.JobID)) + + } + + } + tabwriter.Flush() +} + +type JobStatus struct { + JobID int64 `json:"job_id"` + Owner string `json:"owner"` + Repo string `json:"repo"` + WorkflowName string `json:"workflow_name"` + JobName string `json:"job_name"` + Actor string `json:"actor,omitempty"` + + RunnerName string `json:"runner_name,omitempty"` + Status string `json:"status"` + Conclusion string `json:"conclusion,omitempty"` + Labels []string `json:"labels,omitempty"` + + UpdatedAt *time.Time `json:"updated_at"` + StartedAt *time.Time `json:"startedAt,omitempty"` + CompletedAt *time.Time `json:"completedAt,omitempty"` + + AgentName string `json:"agent_name,omitempty"` +} + +// types.HumanDuration fixes a long string for a value < 1s +func humanDuration(duration time.Duration) string { + v := strings.ToLower(units.HumanDuration(duration)) + + if v == "less than a second" { + return fmt.Sprintf("%d ms", duration.Milliseconds()) + } else if v == "about a minute" { + return fmt.Sprintf("%d seconds", int(duration.Seconds())) + } + + return v +} diff --git a/go.mod b/go.mod index a39157f..d9be967 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,11 @@ module github.com/self-actuated/actuated-cli go 1.22 require ( + github.com/docker/go-units v0.5.0 github.com/google/go-github/v52 v52.0.0 github.com/morikuni/aec v1.0.0 github.com/olekukonko/tablewriter v0.0.5 + github.com/openfaasltd/actuated/types v0.0.0-20240729175505-d862db002103 github.com/spf13/cobra v1.8.0 golang.org/x/oauth2 v0.21.0 ) diff --git a/go.sum b/go.sum index fb21814..c2b130b 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUK github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI= github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -21,6 +23,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/openfaasltd/actuated/types v0.0.0-20240729175505-d862db002103 h1:/Yut1iB0gjNwSrzWnwR/EUiirResccGwEoKVPYPZ4Gc= +github.com/openfaasltd/actuated/types v0.0.0-20240729175505-d862db002103/go.mod h1:B2zlss5C6piwLY00cfk7UZBgxbEE3K6Gv0NK3ktxsjU= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=