Skip to content

Commit

Permalink
feat: provide repository contributors insights (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
cecobask authored Sep 6, 2023
1 parent a73bf91 commit d16091f
Show file tree
Hide file tree
Showing 22 changed files with 569 additions and 205 deletions.
1 change: 1 addition & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ linters:
- unconvert
- unused
- vet
- gci

run:
timeout: 5m
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,16 @@ Available Commands:
bake Use a pizza-oven to source git commits into OpenSauced
completion Generate the autocompletion script for the specified shell
help Help about any command
insights Gather insights about git contributors, repositories, users and pull requests
login Log into the CLI application via GitHub
repo-query Ask questions about a GitHub repository
version Displays the build version of the CLI
Flags:
--beta Shorthand for using the beta OpenSauced API endpoint
("https://beta.api.opensauced.pizza/v1"). Superceds the
'--endpoint' flag
-e, --endpoint string The API endpoint to send requests to (default
"https://api.opensauced.pizza/v1")
-h, --help help for pizza
--beta Shorthand for using the beta OpenSauced API endpoint ("https://beta.api.opensauced.pizza"). Supersedes the '--endpoint' flag
--disable-telemetry Disable sending telemetry data to OpenSauced
-e, --endpoint string The API endpoint to send requests to (default "https://api.opensauced.pizza")
-h, --help help for pizza
Use "pizza [command] --help" for more information about a command.
```
Expand Down
9 changes: 6 additions & 3 deletions cmd/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@ type Options struct {
telemetry *utils.PosthogCliClient
}

const loginLongDesc string = `Log into OpenSauced.
const (
sessionFileName = "session.json"
loginLongDesc = `Log into OpenSauced.
This command initiates the GitHub auth flow to log you into the OpenSauced application by launching your browser`
)

func NewLoginCommand() *cobra.Command {
opts := &Options{}
Expand All @@ -48,7 +51,7 @@ func NewLoginCommand() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
username, err := run()

disableTelem, _ := cmd.Flags().GetBool("disable-telemetry")
disableTelem, _ := cmd.Flags().GetBool(constants.FlagNameTelemetry)

if !disableTelem {
opts.telemetry = utils.NewPosthogCliClient()
Expand Down Expand Up @@ -121,7 +124,7 @@ func run() (string, error) {
return
}

filePath := path.Join(dirName, constants.SessionFileName)
filePath := path.Join(dirName, sessionFileName)
if err := os.WriteFile(filePath, jsonData, 0o600); err != nil {
http.Error(w, "Error writing to file", http.StatusInternalServerError)
return
Expand Down
152 changes: 54 additions & 98 deletions cmd/bake/bake.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,31 @@
package bake

import (
"bytes"
"encoding/json"
"context"
"errors"
"fmt"
"io"
"net/http"
"os"
"sync"

"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"

"gopkg.in/yaml.v3"
)

// Options are the options for the pizza bake command including user
// defined configurations
type Options struct {
// The API Client for the calls to bake git repos
APIClient *api.Client
// APIClient is the http client for making calls to the open-sauced api
APIClient *client.APIClient

// URLs are the git repo URLs that will be sourced via 'pizza bake'
URLs []string
// Repos is the array of git repository urls
Repos []string

// Wait defines the client choice to wait for /bake to finish processing
Wait bool

// FilePath is the location of the file containing a batch of repos to be baked
// FilePath is the path to yaml file containing an array of git repository urls
FilePath string

// telemetry for capturing CLI events
Expand All @@ -39,127 +36,86 @@ type Options struct {

const bakeLongDesc string = `WARNING: Proof of concept feature.
The bake command accepts one or multiple URLs to a git repository and uses a pizza-oven service
The bake command accepts one or multiple Repos to a git repository and uses a pizza-oven service
to source those commits. These commits will then be used for insights on OpenSauced.`

// NewBakeCommand returns a new cobra command for 'pizza bake'
func NewBakeCommand() *cobra.Command {
opts := &Options{}

cmd := &cobra.Command{
Use: "bake url [flags]",
Use: "bake url... [flags]",
Short: "Use a pizza-oven to source git commits into OpenSauced",
Long: bakeLongDesc,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 && opts.FilePath == "" {
return errors.New("must specify the URL(s) of a git repository or provide a batch file")
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 {
disableTelem, _ := cmd.Flags().GetBool("disable-telemetry")
endpoint, _ := cmd.Flags().GetString("endpoint")
useBeta, _ := cmd.Flags().GetBool("beta")

if useBeta {
fmt.Printf("Using beta API endpoint - %s\n", api.BetaAPIEndpoint)
endpoint = api.BetaAPIEndpoint
}
opts.APIClient = api.NewClient(endpoint)

opts.URLs = append(opts.URLs, args...)
endpointURL, _ := cmd.Flags().GetString(constants.FlagNameEndpoint)
opts.APIClient = api.NewGoClient(endpointURL)
disableTelem, _ := cmd.Flags().GetBool(constants.FlagNameTelemetry)

if !disableTelem {
opts.telemetry = utils.NewPosthogCliClient()
defer opts.telemetry.Done()

opts.telemetry.CaptureBake(opts.URLs)
opts.telemetry.CaptureBake(opts.Repos)
}

return run(opts)
},
}

cmd.Flags().BoolVarP(&opts.Wait, "wait", "w", false, "Wait for bake processing to finish")
cmd.Flags().StringVarP(&opts.FilePath, "file", "f", "", "The yaml file containing a series of repos to batch to /bake")

cmd.Flags().StringVarP(&opts.FilePath, constants.FlagNameFile, "f", "", "Path to yaml file containing an array of git repository urls")
cmd.Flags().BoolVarP(&opts.Wait, constants.FlagNameWait, "w", false, "Wait for bake processing to finish")
return cmd
}

type bakePostRequest struct {
URL string `json:"url"`
Wait bool `json:"wait"`
}

type repos struct {
URLs []string `yaml:"repos"`
}

func run(opts *Options) error {
var repos repos
uniqueURLs := make(map[string]bool)

if opts.FilePath != "" {
configFile, err := os.ReadFile(opts.FilePath)
if err != nil {
return err
}

err = yaml.Unmarshal(configFile, &repos)
if err != nil {
return err
}

for _, url := range repos.URLs {
uniqueURLs[url] = true
}
repositories, err := utils.HandleRepositoryValues(opts.Repos, opts.FilePath)
if err != nil {
return err
}

// make sure there are no duplicated queries to the same URL
for _, url := range opts.URLs {
if _, ok := uniqueURLs[url]; !ok {
uniqueURLs[url] = true
continue
}
fmt.Printf("Warning: duplicated URL (%s) would not be processed again\n", url)
var (
waitGroup = new(sync.WaitGroup)
errorChan = make(chan error, len(repositories))
)
for url := range repositories {
waitGroup.Add(1)
go func(repoURL string) {
defer waitGroup.Done()
err = bakeRepository(context.TODO(), opts.APIClient, repoURL, opts.Wait)
if err != nil {
errorChan <- err
return
}
fmt.Println("successfully baked repository", repoURL)
}(url)
}

for url := range uniqueURLs {
bodyPostReq := bakePostRequest{
URL: url,
Wait: opts.Wait,
}

err := bakeRepo(bodyPostReq, opts.APIClient)
if err != nil {
fmt.Printf("Error: failed fetch of %s repository (%s)\n", url, err.Error())
}
waitGroup.Wait()
close(errorChan)
var allErrors error
for err = range errorChan {
allErrors = errors.Join(allErrors, err)
}

return nil
return allErrors
}

func bakeRepo(bodyPostReq bakePostRequest, apiClient *api.Client) error {
bodyPostJSON, err := json.Marshal(bodyPostReq)
if err != nil {
return err
func bakeRepository(ctx context.Context, apiClient *client.APIClient, repoURL string, wait bool) error {
body := client.BakeRepoDto{
Url: repoURL,
Wait: wait,
}

responseBody := bytes.NewBuffer(bodyPostJSON)
resp, err := apiClient.HTTPClient.Post(fmt.Sprintf("%s/bake", apiClient.Endpoint), "application/json", responseBody)
_, err := apiClient.PizzaOvenServiceAPI.
BakeARepositoryWithThePizzaOvenMicroservice(ctx).
BakeRepoDto(body).
Execute()
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}

fmt.Printf("Resp body: %v\n", string(body))
return fmt.Errorf("error while calling 'PizzaOvenServiceAPI.BakeARepositoryWithThePizzaOvenMicroservice' with repository %q: %w", repoURL, err)
}

return nil
}
15 changes: 7 additions & 8 deletions cmd/bake/bake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"net/http/httptest"
"testing"

"github.com/open-sauced/pizza-cli/pkg/api"
"github.com/open-sauced/go-api/client"
)

func TestSendsPost(t *testing.T) {
Expand All @@ -16,13 +16,13 @@ func TestSendsPost(t *testing.T) {
{
name: "Sends post request",
opts: &Options{
URLs: []string{"https://test.com"},
Repos: []string{"https://test.com"},
},
},
{
name: "Sends post request with multiple URLs",
name: "Sends post request with multiple Repos",
opts: &Options{
URLs: []string{"https://test.com", "https://github.com/open-sauced/pizza", "https://github.com/open-sauced/insights"},
Repos: []string{"https://test.com", "https://github.com/open-sauced/pizza", "https://github.com/open-sauced/insights"},
},
},
}
Expand All @@ -33,7 +33,6 @@ func TestSendsPost(t *testing.T) {
if r.Method != http.MethodPost {
t.Fail()
}

// Always return an ok status with a dummy body from the mock server
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte("body"))
Expand All @@ -43,9 +42,9 @@ func TestSendsPost(t *testing.T) {
}
}))
defer testServer.Close()

tt.opts.APIClient = api.NewClient(testServer.URL)

configuration := client.NewConfiguration()
configuration.Servers = client.ServerConfigurations{{URL: testServer.URL}}
tt.opts.APIClient = client.NewAPIClient(configuration)
err := run(tt.opts)
if err != nil {
t.Fail()
Expand Down
Loading

0 comments on commit d16091f

Please sign in to comment.