diff --git a/cmd/github/github.go b/cmd/github/github.go index 6151d6a..fceb645 100644 --- a/cmd/github/github.go +++ b/cmd/github/github.go @@ -40,7 +40,7 @@ import ( logger "github.com/LF-Engineering/insights-datasource-shared/ingestjob" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" - "github.com/google/go-github/v43/github" + "github.com/google/go-github/v50/github" jsoniter "github.com/json-iterator/go" "golang.org/x/oauth2" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" @@ -131,13 +131,14 @@ const ( // GitHubPullrequest ... GitHubPullrequest = "pullrequest" // GitHubIssue ... - GitHubIssue = "issue" - contentHashField = "contentHash" + GitHubIssue = "issue" + // GitHubActions ... + GitHubActions = "actions" ) var ( // GitHubCategories - categories defined for GitHub - GitHubCategories = map[string]struct{}{"issue": {}, "pull_request": {}, "repository": {}} + GitHubCategories = map[string]struct{}{"issue": {}, "pull_request": {}, "repository": {}, "actions": {}} // GitHubIssueRoles - roles to fetch affiliation data for github issue GitHubIssueRoles = []string{"user_data", "assignee_data", "closed_by_data"} // GitHubIssueCommentRoles - roles to fetch affiliation data for github issue comment @@ -180,6 +181,9 @@ var ( reviewersCacheFile = "reviewers-cache" createdPulls = make(map[string]bool) createdIssues = make(map[string]bool) + createdActions = make(map[string]bool) + cachedActions = make(map[string]ItemCache) + usersCache = make(map[string]*github.User) ) // Publisher - for streaming data to Kinesis @@ -3003,6 +3007,476 @@ func (j *DSGitHub) ProcessPull(ctx *shared.Ctx, inPull map[string]interface{}) ( return } +// FetchItemsActions - implement raw actions data for GitHub datasource +func (j *DSGitHub) FetchItemsActions(ctx *shared.Ctx) error { + j.getCachedActions() + from := *ctx.DateFrom + remoteLastSync := *ctx.DateFrom + epocDate := time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC) + if epocDate != from { + from = from.Add(time.Hour * -24) + } + opt := &github.ListWorkflowRunsOptions{} + opt.PerPage = ItemsPerPage + opt.Created = fmt.Sprintf("updated>%s", from.Format(time.RFC3339)) + + eventsCreate := make([]interface{}, 0) + eventsUpdate := make([]interface{}, 0) + + count := 0 + totalFetched := 0 + c := j.Clients[j.Hint] + var updateAt, latestUpdateAt time.Time + retry := false + for { + if count == 1000 { + j.log.WithFields(logrus.Fields{"operation": "FetchItemsActions"}).Infof("fetched %d runs", totalFetched) + opt.Created = fmt.Sprintf("updated<%s", updateAt.Format(time.RFC3339)) + opt.Page = 1 + count = 0 + } + runs, response, err := c.Actions.ListRepositoryWorkflowRuns(j.Context, j.Org, j.Repo, opt) + if err != nil && !retry { + j.log.WithFields(logrus.Fields{"operation": "getModelDataWorkflowRun"}).Warningf("Unable to get workflow jobs: response: %+v, because: %+v, retrying rate", response, err) + j.log.WithFields(logrus.Fields{"operation": "getModelDataWorkflowRun"}).Info("ListWorkflowJobs: handle rate") + abuse, rateLimit := j.isAbuse(err) + if abuse { + sleepFor := AbuseWaitSeconds + rand.Intn(AbuseWaitSeconds) + j.log.WithFields(logrus.Fields{"operation": "githubUserOrgs"}).Infof("GitHub detected abuse (get workflow jobs), waiting for %ds", sleepFor) + time.Sleep(time.Duration(sleepFor) * time.Second) + } + if rateLimit { + j.log.WithFields(logrus.Fields{"operation": "githubUserOrgs"}).Info("Rate limit reached on a token (get workflow jobs) waiting 1s before token switch") + time.Sleep(time.Duration(1) * time.Second) + } + + j.Hint, _, err = j.handleRate(ctx) + if err != nil { + return err + } + c = j.Clients[j.Hint] + if !abuse && !rateLimit { + retry = true + } + continue + } + if err != nil { + return err + } + updateAt = runs.WorkflowRuns[len(runs.WorkflowRuns)-1].UpdatedAt.Time + if totalFetched == 0 { + latestUpdateAt = runs.WorkflowRuns[0].UpdatedAt.Time + } + count += len(runs.WorkflowRuns) + totalFetched += len(runs.WorkflowRuns) + createdEvents, updatedEvents, err := j.getModelDataWorkflowRun(ctx, runs, c) + if err != nil { + return err + } + if len(createdEvents) > 0 { + eventsCreate = append(eventsCreate, createdEvents...) + } + if len(updatedEvents) > 0 { + eventsUpdate = append(eventsUpdate, updatedEvents...) + } + + if response.NextPage == 0 { + break + } + opt.Page = response.NextPage + if ctx.Debug > 0 { + j.log.WithFields(logrus.Fields{"operation": "FetchItemsActions"}).Debugf("processing next action run: %d", opt.Page) + } + } + j.log.WithFields(logrus.Fields{"operation": "FetchItemsActions"}).Infof("total count of fetched runs: %d", totalFetched) + + endpoint := fmt.Sprintf("%s-%s", j.Org, j.Repo) + + if len(eventsCreate) == 0 && len(eventsUpdate) == 0 { + err := j.cacheProvider.SetLastSync(fmt.Sprintf("%s/%s/%s", j.Org, j.Repo, GitHubActions), latestUpdateAt) + if err != nil { + j.log.WithFields(logrus.Fields{"operation": "FetchItemsActions"}).Errorf("unable to set last sync date to cache.error: %v", err) + return err + } + } + + for len(eventsCreate) > 0 { + createdEvents := make([]interface{}, 0) + if len(eventsCreate) > 1000 { + createdEvents = eventsCreate[len(eventsCreate)-1000:] + eventsCreate = eventsCreate[:len(eventsCreate)-1000] + } else { + createdEvents = eventsCreate + eventsCreate = eventsCreate[:0] + } + + // push create events + path, err := j.Publisher.PushEvents(createdEvents[0].(igh.ActionCreatedEvent).Event(), "insights", GitHubDataSource, GitHubActions, os.Getenv("STAGE"), createdEvents, endpoint) + if err != nil { + j.log.WithFields(logrus.Fields{"operation": "FetchItemsActions"}).Errorf("push actions created eventsCreate error: %+v", err) + return err + } + j.log.WithFields(logrus.Fields{"operation": "FetchItemsActions"}).Infof("pushed %d create run events", len(createdEvents)) + + // create and push cache + if err = j.cacheCreatedActions(createdEvents, path); err != nil { + j.log.WithFields(logrus.Fields{"operation": "FetchItemsActions"}).Errorf("unable to cache created eventsCreate data to cache.error: %v", err) + return err + } + + if err = j.updateRemoteCache("actions-cache.csv", GitHubActions); err != nil { + j.log.WithFields(logrus.Fields{"operation": "FetchItemsActions"}).Errorf("unable to update remote cache.error: %v", err) + return err + } + + // update last sync + lastSync := createdEvents[0].(igh.ActionCreatedEvent).Payload.UpdatedAt + if remoteLastSync.Before(lastSync) { + err = j.cacheProvider.SetLastSync(fmt.Sprintf("%s/%s/%s", j.Org, j.Repo, GitHubActions), lastSync) + if err != nil { + j.log.WithFields(logrus.Fields{"operation": "FetchItemsActions"}).Errorf("unable to set last sync date to cache.error: %v", err) + return err + } + remoteLastSync = lastSync + } + + } + + for len(eventsUpdate) > 0 { + ev := make([]interface{}, 0) + if len(eventsUpdate) > 1000 { + ev = eventsUpdate[len(eventsUpdate)-1000:] + eventsUpdate = eventsUpdate[:len(eventsUpdate)-1000] + } else { + ev = eventsUpdate + eventsUpdate = eventsUpdate[:0] + } + + // push events Update + path, err := j.Publisher.PushEvents(ev[0].(igh.ActionUpdatedEvent).Event(), "insights", GitHubDataSource, GitHubActions, os.Getenv("STAGE"), ev, endpoint) + if err != nil { + j.log.WithFields(logrus.Fields{"operation": "FetchItemsActions"}).Errorf("push actions update error: %+v", err) + return err + } + j.log.WithFields(logrus.Fields{"operation": "FetchItemsActions"}).Infof("pushed %d update run events", len(ev)) + + // create and push cache + if err = j.cacheUpdatedActions(ev, path); err != nil { + j.log.WithFields(logrus.Fields{"operation": "FetchItemsActions"}).Errorf("unable to cache updated events data to cache.error: %v", err) + return err + } + + if err = j.updateRemoteCache("actions-cache.csv", GitHubActions); err != nil { + j.log.WithFields(logrus.Fields{"operation": "FetchItemsActions"}).Errorf("unable to update remote cache.error: %v", err) + return err + } + + // update last sync + lastSync := ev[0].(igh.ActionUpdatedEvent).Payload.UpdatedAt + if remoteLastSync.Before(lastSync) { + err = j.cacheProvider.SetLastSync(fmt.Sprintf("%s/%s/%s", j.Org, j.Repo, GitHubActions), lastSync) + if err != nil { + j.log.WithFields(logrus.Fields{"operation": "FetchItemsActions"}).Errorf("unable to set last sync date to cache.error: %v", err) + return err + } + remoteLastSync = lastSync + } + + } + + return nil +} + +func (j *DSGitHub) getModelDataWorkflowSteps(job *github.WorkflowJob, jobID string) ([]igh.Step, error) { + stepsSchema := make([]igh.Step, 0) + for _, step := range job.Steps { + stepID, err := igh.GenerateGithubActionStepID(jobID, *step.Name) + if err != nil { + return []igh.Step{}, err + } + conclusion := "" + if step.Conclusion != nil { + conclusion = *step.Conclusion + } + completedAt, startedAt := time.Time{}, time.Time{} + if step.CompletedAt != nil { + completedAt = step.CompletedAt.Time + } + if step.StartedAt != nil { + startedAt = step.StartedAt.Time + } + stepsSchema = append(stepsSchema, igh.Step{ + ID: stepID, + Name: *step.Name, + Status: *step.Status, + Number: int(*step.Number), + Conclusion: conclusion, + StartedAt: startedAt, + CompletedAt: completedAt, + }) + } + return stepsSchema, nil +} + +func (j *DSGitHub) getModelDataWorkflowJobs(ctx *shared.Ctx, client *github.Client, runID int64, repoID string, runSourceID string) ([]igh.Job, error) { + jobsSchema := make([]igh.Job, 0) + opt := &github.ListWorkflowJobsOptions{} + opt.PerPage = ItemsPerPage + retry := false + for { + jobs, response, err := client.Actions.ListWorkflowJobs(j.Context, j.Org, j.Repo, runID, opt) + if err != nil && !retry { + j.log.WithFields(logrus.Fields{"operation": "getModelDataWorkflowJobs"}).Warningf("Unable to get workflow jobs: response: %+v, because: %+v, retrying rate", response, err) + j.log.WithFields(logrus.Fields{"operation": "getModelDataWorkflowJobs"}).Info("ListWorkflowJobs: handle rate") + abuse, rateLimit := j.isAbuse(err) + if abuse { + sleepFor := AbuseWaitSeconds + rand.Intn(AbuseWaitSeconds) + j.log.WithFields(logrus.Fields{"operation": "getModelDataWorkflowJobs"}).Infof("GitHub detected abuse (get workflow jobs), waiting for %ds", sleepFor) + time.Sleep(time.Duration(sleepFor) * time.Second) + } + if rateLimit { + j.log.WithFields(logrus.Fields{"operation": "getModelDataWorkflowJobs"}).Info("Rate limit reached on a token (get workflow jobs) waiting 1s before token switch") + time.Sleep(time.Duration(1) * time.Second) + } + + j.Hint, _, err = j.handleRate(ctx) + if err != nil { + return []igh.Job{}, err + } + client = j.Clients[j.Hint] + if !abuse && !rateLimit { + retry = true + } + continue + } + if err != nil { + return []igh.Job{}, err + } + for _, job := range jobs.Jobs { + jobSourceID := strconv.FormatInt(*job.ID, 10) + jobID, err := igh.GenerateGithubActionJobID(repoID, runSourceID) + if err != nil { + return []igh.Job{}, err + } + stepsSchema, err := j.getModelDataWorkflowSteps(job, jobID) + if err != nil { + return []igh.Job{}, err + } + + conclusion := "" + if job.Conclusion != nil { + conclusion = *job.Conclusion + } + completedAt, startedAt := time.Time{}, time.Time{} + if job.CompletedAt != nil { + completedAt = job.CompletedAt.Time + } + if job.StartedAt != nil { + startedAt = job.StartedAt.Time + } + jobsSchema = append(jobsSchema, igh.Job{ + ID: jobID, + JobID: jobSourceID, + Name: *job.Name, + Status: *job.Status, + Conclusion: conclusion, + StartedAt: startedAt, + CompletedAt: completedAt, + Steps: stepsSchema, + }) + } + + if response.NextPage == 0 { + break + } + opt.Page = response.NextPage + if ctx.Debug > 0 { + j.log.WithFields(logrus.Fields{"operation": "getModelDataWorkflowJobs"}).Debugf("processing next action run: %d", opt.Page) + } + retry = false + } + + return jobsSchema, nil +} + +func (j *DSGitHub) getModelDataWorkflowRun(ctx *shared.Ctx, workflowRuns *github.WorkflowRuns, client *github.Client) ([]interface{}, []interface{}, error) { + source := GitHubDataSource + repoID, err := repository.GenerateRepositoryID(j.SourceID, j.URL, source) + if err != nil { + j.log.WithFields(logrus.Fields{"operation": "getModelDataWorkflowRun"}).Errorf("GenerateRepositoryID(%s,%s,%s): %+v ", j.SourceID, j.URL, source, err) + return []interface{}{}, []interface{}{}, err + } + actionBaseEvent := igh.ActionBaseEvent{ + Connector: insights.GithubConnector, + ConnectorVersion: GitHubBackendVersion, + Source: insights.GithubSource, + } + actionCreatedEvents := make([]interface{}, 0) + actionUpdatedEvents := make([]interface{}, 0) + for _, workflowRun := range workflowRuns.WorkflowRuns { + runSourceID := strconv.FormatInt(*workflowRun.WorkflowID, 10) + runID, err := igh.GenerateGithubActionRunID(repoID, runSourceID) + if err != nil { + return []interface{}{}, []interface{}{}, err + } + jobsSchema, err := j.getModelDataWorkflowJobs(ctx, client, *workflowRun.ID, repoID, runSourceID) + if err != nil { + return []interface{}{}, []interface{}{}, err + } + workflowSourceID := strconv.FormatInt(*workflowRun.ID, 10) + usr, err := j.getUser(ctx, *workflowRun.Actor.Login) + if err != nil { + return []interface{}{}, []interface{}{}, err + } + email, name := "", "" + if usr != nil && usr.Email != nil { + email = *usr.Email + } + if usr != nil && usr.Name != nil { + name = *usr.Name + } + contributors := make([]insights.Contributor, 0) + userID, err := user.GenerateIdentity(&source, &email, &name, workflowRun.Actor.Login) + if err != nil { + j.log.WithFields(logrus.Fields{"operation": "GetModelDataPullRequest"}).Errorf("GenerateIdentity(%s,%s,%s,%s): %+v for %+v", source, email, name, *workflowRun.Actor.Login, err, *workflowRun.Actor) + return []interface{}{}, []interface{}{}, err + } + + contributors = append(contributors, insights.Contributor{ + Identity: user.UserIdentityObjectBase{ + ID: userID, + Avatar: *workflowRun.Actor.AvatarURL, + Email: email, + IsVerified: false, + Name: name, + Username: *workflowRun.Actor.Login, + Source: source, + }, + Role: insights.ActorRole, + Weight: 1, + }) + + runPayload := igh.Run{ + ID: runID, + RepositoryID: repoID, + RepositoryURL: *workflowRun.URL, + RunID: workflowSourceID, + RunNumber: *workflowRun.RunNumber, + Name: *workflowRun.Name, + Status: *workflowRun.Status, + Conclusion: *workflowRun.Conclusion, + RunAttempt: *workflowRun.RunAttempt, + Event: *workflowRun.Event, + StartedAt: workflowRun.RunStartedAt.Time, + UpdatedAt: workflowRun.UpdatedAt.Time, + Contributors: contributors, + Jobs: jobsSchema, + } + runB, err := jsoniter.Marshal(runPayload) + if err != nil { + return []interface{}{}, []interface{}{}, err + } + contentHash := fmt.Sprintf("%x", sha256.Sum256(runB)) + _, updateExist := cachedActions[contentHash] + _, isCreated := createdActions[runID] + if isCreated && !updateExist { + actionUpdatedEvent := igh.ActionUpdatedEvent{ + ActionBaseEvent: actionBaseEvent, + BaseEvent: service.BaseEvent{ + Type: service.EventType(igh.ActionUpdatedEvent{}.Event()), + CRUDInfo: service.CRUDInfo{ + CreatedBy: GitHubConnector, + UpdatedBy: GitHubConnector, + CreatedAt: time.Now().Unix(), + UpdatedAt: time.Now().Unix(), + }, + }, + Payload: runPayload, + } + actionUpdatedEvents = append(actionUpdatedEvents, actionUpdatedEvent) + } + if !isCreated { + actionCreatedEvent := igh.ActionCreatedEvent{ + ActionBaseEvent: actionBaseEvent, + BaseEvent: service.BaseEvent{ + Type: service.EventType(igh.ActionCreatedEvent{}.Event()), + CRUDInfo: service.CRUDInfo{ + CreatedBy: GitHubConnector, + UpdatedBy: GitHubConnector, + CreatedAt: time.Now().Unix(), + UpdatedAt: time.Now().Unix(), + }, + }, + Payload: runPayload, + } + actionCreatedEvents = append(actionCreatedEvents, actionCreatedEvent) + } + + } + + return actionCreatedEvents, actionUpdatedEvents, nil +} + +func (j *DSGitHub) getUser(ctx *shared.Ctx, login string) (*github.User, error) { + usr, ok := usersCache[login] + if ok { + return usr, nil + } + + // Try GitHub API 3rd + var c *github.Client + if j.GitHubMtx != nil { + j.GitHubMtx.RLock() + } + c = j.Clients[j.Hint] + if j.GitHubMtx != nil { + j.GitHubMtx.RUnlock() + } + + retry := false + usr, response, err := c.Users.Get(j.Context, login) + if ctx.Debug > 2 { + j.log.WithFields(logrus.Fields{"operation": "getUser"}).Debugf("GET %s -> {%+v, %+v, %+v}\n", login, usr, response, err) + } + if err != nil && strings.Contains(err.Error(), "404 Not Found") { + return usr, err + } + if err != nil && !retry { + j.log.WithFields(logrus.Fields{"operation": "githubUser"}).Warningf("Unable to get %s user: response: %+v, because: %+v, retrying rate", login, response, err) + j.log.WithFields(logrus.Fields{"operation": "githubUser"}).Info("handle rate") + abuse, rateLimit := j.isAbuse(err) + if abuse { + sleepFor := AbuseWaitSeconds + rand.Intn(AbuseWaitSeconds) + j.log.WithFields(logrus.Fields{"operation": "githubUser"}).Infof("GitHub detected abuse (get user %s), waiting for %ds", login, sleepFor) + time.Sleep(time.Duration(sleepFor) * time.Second) + } + if rateLimit { + j.log.WithFields(logrus.Fields{"operation": "githubUser"}).Infof("Rate limit reached on a token (get user %s) waiting 1s before token switch", login) + time.Sleep(time.Duration(1) * time.Second) + } + if j.GitHubMtx != nil { + j.GitHubMtx.Lock() + } + j.Hint, _, err = j.handleRate(ctx) + if err != nil { + return usr, err + } + c = j.Clients[j.Hint] + if j.GitHubMtx != nil { + j.GitHubMtx.Unlock() + } + if !abuse && !rateLimit { + retry = true + } + } + if err != nil { + return usr, err + } + + usersCache[login] = usr + return usr, nil +} + // FetchItemsRepository - implement raw repository data for GitHub datasource func (j *DSGitHub) FetchItemsRepository(ctx *shared.Ctx) (err error) { items := []interface{}{} @@ -6092,6 +6566,8 @@ func (j *DSGitHub) SyncCurrentCategory(ctx *shared.Ctx) (err error) { return j.FetchItemsIssue(ctx) case "pull_request": return j.FetchItemsPullRequest(ctx) + case "actions": + return j.FetchItemsActions(ctx) } return } @@ -8896,6 +9372,48 @@ func (j *DSGitHub) AddCacheProvider() { j.cacheProvider = *cacheProvider } +func (j *DSGitHub) cacheCreatedActions(v []interface{}, path string) error { + for _, val := range v { + runAction := val.(igh.ActionCreatedEvent).Payload + b, err := json.Marshal(runAction) + if err != nil { + return err + } + contentHash := fmt.Sprintf("%x", sha256.Sum256(b)) + cachedActions[contentHash] = ItemCache{ + Timestamp: fmt.Sprintf("%v", runAction.UpdatedAt.Unix()), + EntityID: runAction.ID, + SourceEntityID: runAction.RunID, + FileLocation: path, + Hash: contentHash, + Orphaned: false, + } + createdActions[runAction.ID] = true + } + return nil +} + +func (j *DSGitHub) cacheUpdatedActions(v []interface{}, path string) error { + for _, val := range v { + runAction := val.(igh.ActionUpdatedEvent).Payload + b, err := json.Marshal(runAction) + if err != nil { + return err + } + contentHash := fmt.Sprintf("%x", sha256.Sum256(b)) + cachedActions[contentHash] = ItemCache{ + Timestamp: fmt.Sprintf("%v", runAction.UpdatedAt.Unix()), + EntityID: runAction.ID, + SourceEntityID: runAction.RunID, + FileLocation: path, + Hash: contentHash, + Orphaned: false, + } + createdActions[runAction.ID] = true + } + return nil +} + func (j *DSGitHub) cacheCreatedPullrequest(v []interface{}, path string) error { for _, val := range v { pr := val.(igh.PullRequestCreatedEvent).Payload @@ -9203,6 +9721,37 @@ func (j *DSGitHub) getCachedIssues() { } } +func (j *DSGitHub) getCachedActions() { + actionsB, err := j.cacheProvider.GetFileByKey(fmt.Sprintf("%s/%s/%s", j.Org, j.Repo, GitHubActions), "actions-cache.csv") + if err != nil { + return + } + reader := csv.NewReader(bytes.NewBuffer(actionsB)) + records, err := reader.ReadAll() + if err != nil { + return + } + for i, record := range records { + if i == 0 { + continue + } + orphaned, err := strconv.ParseBool(record[5]) + if err != nil { + orphaned = false + } + cachedActions[record[4]] = ItemCache{ + Timestamp: record[0], + EntityID: record[1], + SourceEntityID: record[2], + FileLocation: record[3], + Hash: record[4], + Orphaned: orphaned, + } + createdActions[record[1]] = true + + } +} + func isParentKeyCreated(element map[string]bool, id string) bool { _, ok := element[id] if ok { @@ -9235,6 +9784,11 @@ func (j *DSGitHub) updateRemoteCache(cacheFile string, cacheType string) error { for _, c := range cachedIssues { records = append(records, []string{c.Timestamp, c.EntityID, c.SourceEntityID, c.FileLocation, c.Hash, strconv.FormatBool(c.Orphaned)}) } + case GitHubActions: + category = GitHubActions + for _, c := range cachedActions { + records = append(records, []string{c.Timestamp, c.EntityID, c.SourceEntityID, c.FileLocation, c.Hash, strconv.FormatBool(c.Orphaned)}) + } default: return fmt.Errorf("cache must be one of issue or pullrequest") } diff --git a/go.mod b/go.mod index 0eba1c6..dd6d713 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,10 @@ go 1.17 require ( github.com/LF-Engineering/dev-analytics-libraries v1.1.28 github.com/LF-Engineering/insights-datasource-shared v1.5.18-0.20221031165121-cb720cbf98a9 - github.com/LF-Engineering/lfx-event-schema v0.1.37 + github.com/LF-Engineering/lfx-event-schema v0.1.39-0.20230217151211-c9422c4a36bc github.com/aws/aws-lambda-go v1.28.0 github.com/aws/aws-sdk-go v1.43.22 - github.com/google/go-github/v43 v43.0.0 + github.com/google/go-github/v50 v50.0.0 github.com/json-iterator/go v1.1.12 github.com/sirupsen/logrus v1.8.1 golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a @@ -51,10 +51,12 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/tinylib/msgp v1.1.2 // indirect golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect - golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect + golang.org/x/net v0.6.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect + golang.org/x/tools v0.6.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.0 // indirect diff --git a/go.sum b/go.sum index 14a79e8..45c1908 100644 --- a/go.sum +++ b/go.sum @@ -46,8 +46,8 @@ github.com/LF-Engineering/dev-analytics-libraries v1.1.28/go.mod h1:O+9mOX1nf6qG github.com/LF-Engineering/insights-datasource-shared v1.5.18-0.20221031165121-cb720cbf98a9 h1:FBZZ1+Znl6t5JZoO9UMUQhF9VsGBI4YIs67U8DfLDEU= github.com/LF-Engineering/insights-datasource-shared v1.5.18-0.20221031165121-cb720cbf98a9/go.mod h1:9DmFQbC8nnm1C7k+/tDo3Rmqzzx7AzmhPBlFouXaBZ8= github.com/LF-Engineering/lfx-event-schema v0.1.14/go.mod h1:CfFIZ4mwzo88umf5+KxDQEzqlVkPG7Vx8eLK2oDfWIs= -github.com/LF-Engineering/lfx-event-schema v0.1.37 h1:ny46D2NdCXokvJZ01GJcw2RfQM64ousJjaYsrRj5zzg= -github.com/LF-Engineering/lfx-event-schema v0.1.37/go.mod h1:CfFIZ4mwzo88umf5+KxDQEzqlVkPG7Vx8eLK2oDfWIs= +github.com/LF-Engineering/lfx-event-schema v0.1.39-0.20230217151211-c9422c4a36bc h1:4DHTsIPX4RrDlfBJad3EVDGohpZwI2kccmBbDX5yizM= +github.com/LF-Engineering/lfx-event-schema v0.1.39-0.20230217151211-c9422c4a36bc/go.mod h1:CfFIZ4mwzo88umf5+KxDQEzqlVkPG7Vx8eLK2oDfWIs= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= @@ -180,11 +180,11 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-github/v33 v33.0.0/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg= -github.com/google/go-github/v43 v43.0.0 h1:y+GL7LIsAIF2NZlJ46ZoC/D1W1ivZasT0lnWHMYPZ+U= -github.com/google/go-github/v43 v43.0.0/go.mod h1:ZkTvvmCXBvsfPpTHXnH/d2hP9Y0cTbvN9kr5xqyXOIc= +github.com/google/go-github/v50 v50.0.0 h1:gdO1AeuSZZK4iYWwVbjni7zg8PIQhp7QfmPunr016Jk= +github.com/google/go-github/v50 v50.0.0/go.mod h1:Ev4Tre8QoKiolvbpOSG3FIi4Mlon3S2Nt9W5JYqKiwA= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= @@ -322,6 +322,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -363,6 +365,8 @@ golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -416,6 +420,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -428,6 +434,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -473,6 +481,8 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=