diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4543068..958c6c1 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -71,8 +71,8 @@ jobs: run: | GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build \ -ldflags="-s -w" \ - -ldflags="-X 'github.com/open-sauced/pizza-cli/pkg/utils.writeOnlyPublicPosthogKey=${{ secrets.POSTHOG_WRITE_PUBLIC_KEY }}'" \ - -ldflags="-X 'github.com/open-sauced/pizza-cli/pkg/utils.Version={{ needs.release.outputs.release-tag }}'" \ + -ldflags="-X 'github.com/open-sauced/pizza-cli/pkg/utils.writeOnlyPublicPosthogKey=${{ vars.POSTHOG_WRITE_PUBLIC_KEY }}'" \ + -ldflags="-X 'github.com/open-sauced/pizza-cli/pkg/utils.Version=${{ needs.release.outputs.release-tag }}'" \ -ldflags="-X 'github.com/open-sauced/pizza-cli/pkg/utils.Sha=$(git rev-parse HEAD)'" \ -o build/pizza-${{ matrix.goos }}-${{ matrix.goarch }} gh release upload ${{ needs.release.outputs.release-tag }} build/pizza-${{ matrix.goos }}-${{ matrix.goarch }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 23538ca..c5c6c92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,34 @@ > All notable changes to this project will be documented in this file +## [1.1.0-beta.3](https://github.com/open-sauced/pizza-cli/compare/v1.1.0-beta.2...v1.1.0-beta.3) (2023-10-26) + + +### πŸ• Features + +* Add filtering for usernames on user-contributions ([d10c1ef](https://github.com/open-sauced/pizza-cli/commit/d10c1ef0bc69d3acd2e9f3ebdab7eee813debe19)) + +## [1.1.0-beta.2](https://github.com/open-sauced/pizza-cli/compare/v1.1.0-beta.1...v1.1.0-beta.2) (2023-10-26) + + +### πŸ• Features + +* Add "pizza insights user-contributions" command ([248f90a](https://github.com/open-sauced/pizza-cli/commit/248f90a270540c7dd992b9d69dff7da1eb2ceddd)) + +## [1.1.0-beta.1](https://github.com/open-sauced/pizza-cli/compare/v1.0.1-beta.1...v1.1.0-beta.1) (2023-10-18) + + +### πŸ• Features + +* add csv support for contributors insights ([51c1897](https://github.com/open-sauced/pizza-cli/commit/51c18978cb9012fa72642fcb77c9f33bf0a88076)) + ## [1.0.1](https://github.com/open-sauced/pizza-cli/compare/v1.0.0...v1.0.1) (2023-10-11) ### πŸ› Bug Fixes +* Hotfix for broken release workflow file ([e115fe9](https://github.com/open-sauced/pizza-cli/commit/e115fe91ded711ad075e2847b2c73f3f065b50d1)) +* Release pipeline missing some env vars ([21e43e3](https://github.com/open-sauced/pizza-cli/commit/21e43e365d490eb0a9182895aaa5e45a2c00a025)) * Roll forward fix for semantic release ([#51](https://github.com/open-sauced/pizza-cli/issues/51)) ([3a6bb27](https://github.com/open-sauced/pizza-cli/commit/3a6bb27da209caccc4ab092e202516442b1cc621)) ## [1.0.1-beta.1](https://github.com/open-sauced/pizza-cli/compare/v1.0.0...v1.0.1-beta.1) (2023-10-11) diff --git a/cmd/insights/contributors.go b/cmd/insights/contributors.go index 1420f6a..4757dc1 100644 --- a/cmd/insights/contributors.go +++ b/cmd/insights/contributors.go @@ -1,10 +1,11 @@ package insights import ( + "bytes" "context" + "encoding/csv" "errors" "fmt" - "net/http" "strconv" "strings" "sync" @@ -134,11 +135,39 @@ func (cis contributorsInsightsSlice) BuildOutput(format string) (string, error) return utils.OutputJSON(cis) case constants.OutputYAML: return utils.OutputYAML(cis) + case constants.OuputCSV: + return cis.OutputCSV() default: return "", fmt.Errorf("unknown output format %s", format) } } +func (cis contributorsInsightsSlice) OutputCSV() (string, error) { + if len(cis) == 0 { + return "", fmt.Errorf("repository is either non-existent or has not been indexed yet") + } + b := new(bytes.Buffer) + writer := csv.NewWriter(b) + + // write headers + err := writer.WriteAll([][]string{{"Repository URL", "New Contributors", "Recent Contributors", "Alumni Contributors", "Repeat Contributors"}}) + if err != nil { + return "", err + } + + // write records + for _, ci := range cis { + err := writer.WriteAll([][]string{{ci.RepoURL, strconv.Itoa(len(ci.New)), strconv.Itoa(len(ci.Recent)), + strconv.Itoa(len(ci.Alumni)), strconv.Itoa(len(ci.Repeat))}}) + + if err != nil { + return "", err + } + } + + return b.String(), nil +} + func (cis contributorsInsightsSlice) OutputTable() (string, error) { tables := make([]string, 0, len(cis)) for i := range cis { @@ -176,23 +205,6 @@ func (cis contributorsInsightsSlice) OutputTable() (string, error) { return strings.Join(tables, separator), nil } -func findRepositoryByOwnerAndRepoName(ctx context.Context, apiClient *client.APIClient, repoURL string) (*client.DbRepo, error) { - owner, repoName, err := utils.GetOwnerAndRepoFromURL(repoURL) - if err != nil { - return nil, fmt.Errorf("could not extract owner and repo from url: %w", err) - } - repo, response, err := apiClient.RepositoryServiceAPI.FindOneByOwnerAndRepo(ctx, owner, repoName).Execute() - if err != nil { - if response != nil && response.StatusCode == http.StatusNotFound { - message := fmt.Sprintf("repository %s is either non-existent or has not been indexed yet", repoURL) - fmt.Println("ignoring repository issue:", message) - return nil, nil - } - return nil, fmt.Errorf("error while calling 'RepositoryServiceAPI.FindOneByOwnerAndRepo' with owner %q and repo %q: %w", owner, repoName, err) - } - return repo, nil -} - func findAllContributorsInsights(ctx context.Context, opts *contributorsOptions, repoURL string) (*contributorsInsights, error) { repo, err := findRepositoryByOwnerAndRepoName(ctx, opts.APIClient, repoURL) if err != nil { diff --git a/cmd/insights/insights.go b/cmd/insights/insights.go index e09539d..b2ff7ee 100644 --- a/cmd/insights/insights.go +++ b/cmd/insights/insights.go @@ -16,5 +16,6 @@ func NewInsightsCommand() *cobra.Command { } cmd.AddCommand(NewContributorsCommand()) cmd.AddCommand(NewRepositoriesCommand()) + cmd.AddCommand(NewUserContributionsCommand()) return cmd } diff --git a/cmd/insights/repositories.go b/cmd/insights/repositories.go index 51d3b64..b93568f 100644 --- a/cmd/insights/repositories.go +++ b/cmd/insights/repositories.go @@ -39,9 +39,10 @@ type repositoriesOptions struct { func NewRepositoriesCommand() *cobra.Command { opts := &repositoriesOptions{} cmd := &cobra.Command{ - Use: "repositories url... [flags]", - Short: "Gather insights about indexed git repositories", - Long: "Gather insights about indexed git repositories. This command will show info about contributors, pull requests, etc.", + Use: "repositories url... [flags]", + Aliases: []string{"repos"}, + Short: "Gather insights about indexed git repositories", + Long: "Gather insights about indexed git repositories. This command will show info about contributors, pull requests, etc.", Args: func(cmd *cobra.Command, args []string) error { fileFlag := cmd.Flags().Lookup(constants.FlagNameFile) if !fileFlag.Changed && len(args) == 0 { diff --git a/cmd/insights/user-contributions.go b/cmd/insights/user-contributions.go new file mode 100644 index 0000000..4fb2171 --- /dev/null +++ b/cmd/insights/user-contributions.go @@ -0,0 +1,285 @@ +package insights + +import ( + "bytes" + "context" + "encoding/csv" + "errors" + "fmt" + "strconv" + "sync" + + bubblesTable "github.com/charmbracelet/bubbles/table" + "github.com/open-sauced/go-api/client" + "github.com/open-sauced/pizza-cli/pkg/api" + "github.com/open-sauced/pizza-cli/pkg/constants" + "github.com/open-sauced/pizza-cli/pkg/utils" + "github.com/spf13/cobra" +) + +type userContributionsOptions struct { + // APIClient is the http client for making calls to the open-sauced api + APIClient *client.APIClient + + // Repos is the array of git repository urls + Repos []string + + // Users is the list of usernames to filter for + Users []string + + // usersMap is a fast access set of usernames built from the Users string slice + usersMap map[string]struct{} + + // FilePath is the path to yaml file containing an array of git repository urls + FilePath string + + // Period is the number of days, used for query filtering + Period int32 + + // Output is the formatting style for command output + Output string +} + +// NewUserContributionsCommand returns a new user-contributions command +func NewUserContributionsCommand() *cobra.Command { + opts := &userContributionsOptions{} + + cmd := &cobra.Command{ + Use: "user-contributions url... [flags]", + Short: "Gather insights on individual contributors for given repo URLs", + Long: "Gather insights on individual contributors given a list of repository URLs", + Args: func(cmd *cobra.Command, args []string) error { + fileFlag := cmd.Flags().Lookup(constants.FlagNameFile) + if !fileFlag.Changed && len(args) == 0 { + return fmt.Errorf("must specify git repository url argument(s) or provide %s flag", fileFlag.Name) + } + opts.Repos = append(opts.Repos, args...) + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + endpointURL, _ := cmd.Flags().GetString(constants.FlagNameEndpoint) + opts.APIClient = api.NewGoClient(endpointURL) + output, _ := cmd.Flags().GetString(constants.FlagNameOutput) + opts.Output = output + return opts.run(context.TODO()) + }, + } + + cmd.Flags().StringVarP(&opts.FilePath, constants.FlagNameFile, "f", "", "Path to yaml file containing an array of git repository urls") + cmd.Flags().Int32VarP(&opts.Period, constants.FlagNamePeriod, "p", 30, "Number of days, used for query filtering") + cmd.Flags().StringSliceVarP(&opts.Users, "users", "u", []string{}, "Inclusive comma separated list of GitHub usernames to filter for") + + return cmd +} + +func (opts *userContributionsOptions) run(ctx context.Context) error { + repositories, err := utils.HandleRepositoryValues(opts.Repos, opts.FilePath) + if err != nil { + return err + } + + opts.usersMap = make(map[string]struct{}) + for _, username := range opts.Users { + // For fast access to list of users to filter out, uses an empty struct + opts.usersMap[username] = struct{}{} + } + + var ( + waitGroup = new(sync.WaitGroup) + errorChan = make(chan error, len(repositories)) + insightsChan = make(chan *userContributionsInsightGroup, len(repositories)) + doneChan = make(chan struct{}) + insights = make([]*userContributionsInsightGroup, 0, len(repositories)) + allErrors error + ) + + go func() { + for url := range repositories { + repoURL := url + waitGroup.Add(1) + + go func() { + defer waitGroup.Done() + + data, err := findAllUserContributionsInsights(ctx, opts, repoURL) + if err != nil { + errorChan <- err + return + } + + if data == nil { + return + } + + insightsChan <- data + }() + } + + waitGroup.Wait() + close(doneChan) + }() + + for { + select { + case err = <-errorChan: + allErrors = errors.Join(allErrors, err) + case data := <-insightsChan: + insights = append(insights, data) + case <-doneChan: + if allErrors != nil { + return allErrors + } + + for _, insight := range insights { + output, err := insight.BuildOutput(opts.Output) + if err != nil { + return err + } + + fmt.Println(output) + } + + return nil + } + } +} + +type userContributionsInsights struct { + Login string `json:"login" yaml:"login"` + Commits int `json:"commits" yaml:"commits"` + PrsCreated int `json:"prs_created" yaml:"prs_created"` + TotalContributions int `json:"total_contributions" yaml:"total_contributions"` +} + +type userContributionsInsightGroup struct { + RepoURL string `json:"repo_url" yaml:"repo_url"` + Insights []userContributionsInsights +} + +func (ucig userContributionsInsightGroup) BuildOutput(format string) (string, error) { + switch format { + case constants.OutputTable: + return ucig.OutputTable() + case constants.OutputJSON: + return utils.OutputJSON(ucig) + case constants.OutputYAML: + return utils.OutputYAML(ucig) + case constants.OuputCSV: + return ucig.OutputCSV() + default: + return "", fmt.Errorf("unknown output format %s", format) + } +} + +func (ucig userContributionsInsightGroup) OutputCSV() (string, error) { + if len(ucig.Insights) == 0 { + return "", fmt.Errorf("repository is either non-existent or has not been indexed yet") + } + + b := new(bytes.Buffer) + writer := csv.NewWriter(b) + + // write headers + err := writer.WriteAll([][]string{ + { + ucig.RepoURL, + }, + { + "User", + "Total", + "Commits", + "PRs Created", + }, + }) + if err != nil { + return "", err + } + + // write records + for _, uci := range ucig.Insights { + err := writer.WriteAll([][]string{ + { + uci.Login, + strconv.Itoa(uci.Commits + uci.PrsCreated), + strconv.Itoa(uci.Commits), + strconv.Itoa(uci.PrsCreated), + }, + }) + + if err != nil { + return "", err + } + } + + return b.String(), nil +} + +func (ucig userContributionsInsightGroup) OutputTable() (string, error) { + rows := []bubblesTable.Row{} + + for _, uci := range ucig.Insights { + rows = append(rows, bubblesTable.Row{ + uci.Login, + strconv.Itoa(uci.TotalContributions), + strconv.Itoa(uci.Commits), + strconv.Itoa(uci.PrsCreated), + }) + } + + columns := []bubblesTable.Column{ + { + Title: "User", + Width: utils.GetMaxTableRowWidth(rows), + }, + { + Title: "Total", + Width: 10, + }, + { + Title: "Commits", + Width: 10, + }, + { + Title: "PRs Created", + Width: 15, + }, + } + + return fmt.Sprintf("%s\n%s\n", ucig.RepoURL, utils.OutputTable(rows, columns)), nil +} + +func findAllUserContributionsInsights(ctx context.Context, opts *userContributionsOptions, repoURL string) (*userContributionsInsightGroup, error) { + owner, name, err := utils.GetOwnerAndRepoFromURL(repoURL) + if err != nil { + return nil, err + } + + repoUserContributionsInsightGroup := &userContributionsInsightGroup{ + RepoURL: repoURL, + } + + dataPoints, _, err := opts. + APIClient. + RepositoryServiceAPI. + FindAllContributorsByRepoId(ctx, owner, name). + Range_(opts.Period). + Execute() + + if err != nil { + return nil, fmt.Errorf("error while calling 'RepositoryServiceAPI.FindAllContributorsByRepoId' with repository %s/%s': %w", owner, name, err) + } + + for _, data := range dataPoints { + _, ok := opts.usersMap[*data.Login] + if len(opts.usersMap) == 0 || ok { + repoUserContributionsInsightGroup.Insights = append(repoUserContributionsInsightGroup.Insights, userContributionsInsights{ + Login: *data.Login, + Commits: int(data.Commits), + PrsCreated: int(data.PrsCreated), + TotalContributions: int(data.Commits) + int(data.PrsCreated), + }) + } + } + + return repoUserContributionsInsightGroup, nil +} diff --git a/cmd/insights/utils.go b/cmd/insights/utils.go new file mode 100644 index 0000000..4978efd --- /dev/null +++ b/cmd/insights/utils.go @@ -0,0 +1,29 @@ +package insights + +import ( + "context" + "fmt" + "net/http" + + "github.com/open-sauced/go-api/client" + "github.com/open-sauced/pizza-cli/pkg/utils" +) + +// findRepositoryByOwnerAndRepoName returns an API client Db Repo +// based on the given repository URL +func findRepositoryByOwnerAndRepoName(ctx context.Context, apiClient *client.APIClient, repoURL string) (*client.DbRepo, error) { + owner, repoName, err := utils.GetOwnerAndRepoFromURL(repoURL) + if err != nil { + return nil, fmt.Errorf("could not extract owner and repo from url: %w", err) + } + + repo, response, err := apiClient.RepositoryServiceAPI.FindOneByOwnerAndRepo(ctx, owner, repoName).Execute() + if err != nil { + if response != nil && response.StatusCode == http.StatusNotFound { + return nil, fmt.Errorf("repository %s is either non-existent, private, or has not been indexed yet", repoURL) + } + return nil, fmt.Errorf("error while calling 'RepositoryServiceAPI.FindOneByOwnerAndRepo' with owner %q and repo %q: %w", owner, repoName, err) + } + + return repo, nil +} diff --git a/cmd/repo-query/repo-query.go b/cmd/repo-query/repo-query.go index ff27edd..ed47db2 100644 --- a/cmd/repo-query/repo-query.go +++ b/cmd/repo-query/repo-query.go @@ -103,7 +103,7 @@ func run(opts *Options) error { return err } - fmt.Printf("Checking if %s/%s is indexed by us...⏳\n", owner, repo) + fmt.Printf("Checking if %s/%s is indexed by us ... ⏳\n", owner, repo) resp, err := agent.client.HTTPClient.Get(fmt.Sprintf("%s/collection?owner=%s&name=%s&branch=%s", agent.client.Endpoint, owner, repo, opts.branch)) if err != nil { return err @@ -112,8 +112,8 @@ func run(opts *Options) error { switch resp.StatusCode { case http.StatusNotFound: // repo is not indexed - fmt.Println("Repo not found❗") - fmt.Println("Indexing repo...⏳") + fmt.Println("Repo not found ❗") + fmt.Println("Indexing repo ... ⏳") err := agent.indexRepo(owner, repo, opts.branch) if err != nil { return err @@ -152,7 +152,7 @@ func (rq *repoQueryAgent) startQnALoop(owner string, repo string, branch string) go func() { <-c - fmt.Println("\nπŸ•Exiting...") + fmt.Println("\nπŸ• Exiting ...") os.Exit(0) }() @@ -163,7 +163,7 @@ func (rq *repoQueryAgent) startQnALoop(owner string, repo string, branch string) if scanner.Scan() { input := scanner.Text() if input == "exit" { - fmt.Println("πŸ•Exiting...") + fmt.Println("πŸ• Exiting ...") os.Exit(0) } err := rq.askQuestion(input, owner, repo, branch) @@ -338,11 +338,11 @@ func (rq *repoQueryAgent) processIndexChunk(chunk string) error { switch event { case "FETCH_REPO": - fmt.Println("Fetching Repository from GitHub...") + fmt.Println("Fetching Repository from GitHub ...") case "EMBED_REPO": - fmt.Println("Embedding Repository...") + fmt.Println("Embedding Repository ...") case "SAVE_EMBEDDINGS": - fmt.Println("Saving the embeddings to our database...") + fmt.Println("Saving the embeddings to our database ...") case "ERROR": fmt.Println("There was an error while indexing this repository.") return errors.New("error while indexing repository") @@ -378,13 +378,13 @@ func (rq *repoQueryAgent) processChatChunk(chunk string) error { switch event { case "SEARCH_CODEBASE": - fmt.Println("Searching the codebase for your query...πŸ”") + fmt.Println("Searching the codebase for your query ... πŸ”") case "SEARCH_FILE": - fmt.Printf("Searching %s for your query...πŸ”\n", data.(map[string]interface{})["path"]) + fmt.Printf("Searching %s for your query ... πŸ”\n", data.(map[string]interface{})["path"]) case "SEARCH_PATH": - fmt.Printf("Looking for %s in the codebase...πŸ”\n", data.(map[string]interface{})["path"]) + fmt.Printf("Looking for %s in the codebase ... πŸ”\n", data.(map[string]interface{})["path"]) case "GENERATE_RESPONSE": - fmt.Println("Generating a response...🧠") + fmt.Println("Generating a response ... 🧠") case "DONE": fmt.Println() // the server sends the data as a string, with \n, "", and \t escaped. diff --git a/cmd/root/root.go b/cmd/root/root.go index cf21f0e..faf1ba5 100644 --- a/cmd/root/root.go +++ b/cmd/root/root.go @@ -36,7 +36,7 @@ func NewRootCommand() (*cobra.Command, error) { cmd.PersistentFlags().StringP(constants.FlagNameEndpoint, "e", constants.EndpointProd, "The API endpoint to send requests to") cmd.PersistentFlags().Bool(constants.FlagNameBeta, false, fmt.Sprintf("Shorthand for using the beta OpenSauced API endpoint (\"%s\"). Supersedes the '--%s' flag", constants.EndpointBeta, constants.FlagNameEndpoint)) cmd.PersistentFlags().Bool(constants.FlagNameTelemetry, false, "Disable sending telemetry data to OpenSauced") - cmd.PersistentFlags().StringP(constants.FlagNameOutput, "o", constants.OutputTable, "The formatting style for command output") + cmd.PersistentFlags().StringP(constants.FlagNameOutput, "o", constants.OutputTable, "The formatting for command output. One of: (table, yaml, csv, json)") cmd.AddCommand(bake.NewBakeCommand()) cmd.AddCommand(repoquery.NewRepoQueryCommand()) diff --git a/go.mod b/go.mod index e09d154..12817ed 100644 --- a/go.mod +++ b/go.mod @@ -5,33 +5,33 @@ go 1.21 require ( github.com/charmbracelet/bubbles v0.16.1 github.com/charmbracelet/bubbletea v0.24.2 - github.com/charmbracelet/lipgloss v0.8.0 - github.com/cli/browser v1.2.0 - github.com/open-sauced/go-api/client v0.0.0-20230925192938-8a8b1fa31f60 + github.com/charmbracelet/lipgloss v0.9.1 + github.com/cli/browser v1.3.0 + github.com/open-sauced/go-api/client v0.0.0-20231025234817-a8f01f3b26d8 github.com/posthog/posthog-go v0.0.0-20230801140217-d607812dee69 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 - golang.org/x/term v0.12.0 + golang.org/x/term v0.13.0 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/containerd/console v1.0.4-0.20230706203907-8f6c4e4faef5 // indirect + github.com/google/uuid v1.3.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.2 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/rivo/uniseg v0.4.4 // indirect github.com/sahilm/fuzzy v0.1.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.12.0 // indirect - golang.org/x/text v0.3.8 // indirect + golang.org/x/sync v0.4.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect ) diff --git a/go.sum b/go.sum index fa4ea40..9e024aa 100644 --- a/go.sum +++ b/go.sum @@ -7,45 +7,53 @@ github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5 github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc= github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY= github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= -github.com/charmbracelet/lipgloss v0.8.0 h1:IS00fk4XAHcf8uZKc3eHeMUTCxUH6NkaTrdyCQk84RU= -github.com/charmbracelet/lipgloss v0.8.0/go.mod h1:p4eYUZZJ/0oXTuCQKFF8mqyKCz0ja6y+7DniDDw5KKU= -github.com/cli/browser v1.2.0 h1:yvU7e9qf97kZqGFX6n2zJPHsmSObY9ske+iCvKelvXg= -github.com/cli/browser v1.2.0/go.mod h1:xFFnXLVcAyW9ni0cuo6NnrbCP75JxJ0RO7VtCBiH/oI= +github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= +github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= +github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo= +github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/containerd/console v1.0.4-0.20230706203907-8f6c4e4faef5 h1:Ig+OPkE3XQrrl+SKsOqAjlkrBN/zrr+Qpw7rCuDjRCE= +github.com/containerd/console v1.0.4-0.20230706203907-8f6c4e4faef5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= -github.com/open-sauced/go-api/client v0.0.0-20230925192938-8a8b1fa31f60 h1:JqzkafXPIqfn88GkVuVkN3HSWRvBzYLpn7LyVZq+fSg= -github.com/open-sauced/go-api/client v0.0.0-20230925192938-8a8b1fa31f60/go.mod h1:W/TRuLUqYpMvkmElDUQvQ07xlxhK8TOfpwRh8SCAuNA= +github.com/open-sauced/go-api/client v0.0.0-20231023202852-271ed316d5fd h1:xWarN75bBSNVdXJ9DkTRwWnmDG/uAooasLp1IOakIPI= +github.com/open-sauced/go-api/client v0.0.0-20231023202852-271ed316d5fd/go.mod h1:W/TRuLUqYpMvkmElDUQvQ07xlxhK8TOfpwRh8SCAuNA= +github.com/open-sauced/go-api/client v0.0.0-20231024233005-61e58f577005 h1:qrsKqDy8Duw/MNPoaAyD3R/kZScSYosWKBI5jIR6Iwk= +github.com/open-sauced/go-api/client v0.0.0-20231024233005-61e58f577005/go.mod h1:W/TRuLUqYpMvkmElDUQvQ07xlxhK8TOfpwRh8SCAuNA= +github.com/open-sauced/go-api/client v0.0.0-20231025234817-a8f01f3b26d8 h1:qzSaxN4BdovOr2DjXcYJz4LH7RSXEdhw98zAdnJVqJU= +github.com/open-sauced/go-api/client v0.0.0-20231025234817-a8f01f3b26d8/go.mod h1:W/TRuLUqYpMvkmElDUQvQ07xlxhK8TOfpwRh8SCAuNA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posthog/posthog-go v0.0.0-20230801140217-d607812dee69 h1:01dHVodha5BzrMtVmcpPeA4VYbZEsTXQ6m4123zQXJk= github.com/posthog/posthog-go v0.0.0-20230801140217-d607812dee69/go.mod h1:migYMxlAqcnQy+3eN8mcL0b2tpKy6R+8Zc0lxwk4dKM= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= @@ -56,17 +64,16 @@ github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRM github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/npm/package.json b/npm/package.json index 19ceb0c..32682b5 100644 --- a/npm/package.json +++ b/npm/package.json @@ -1,6 +1,6 @@ { "name": "pizza", - "version": "1.0.1", + "version": "1.1.0-beta.3", "description": "A command line utility for insights, metrics, and all things OpenSauced", "repository": "https://github.com/open-sauced/pizza-cli", "license": "MIT", diff --git a/pkg/constants/output.go b/pkg/constants/output.go index 3cdb360..652f9d2 100644 --- a/pkg/constants/output.go +++ b/pkg/constants/output.go @@ -4,4 +4,5 @@ const ( OutputJSON = "json" OutputTable = "table" OutputYAML = "yaml" + OuputCSV = "csv" ) diff --git a/pkg/constants/templates.go b/pkg/constants/templates.go index 6aa9fc4..d3c179f 100644 --- a/pkg/constants/templates.go +++ b/pkg/constants/templates.go @@ -3,12 +3,12 @@ package constants const ( HelpTemplate = ` {{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}} - {{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}` // UsageTemplate is identical to the default cobra usage template, // but utilizes wrappedFlagUsages to ensure flag usages don't wrap around - UsageTemplate = `Usage:{{if .Runnable}} + UsageTemplate = ` +Usage:{{if .Runnable}} {{.UseLine}}{{end}}{{if gt (len .Aliases) 0}} Aliases: