Example #1
0
// MungePullRequest is the workhorse the will actually make updates to the PR
func (s *SizeMunger) MungePullRequest(config *config.MungeConfig, pr *github.PullRequest, issue *github.Issue, commits []github.RepositoryCommit, events []github.IssueEvent) {
	s.getGeneratedFiles(config)
	genFiles := *s.genFiles
	genPrefixes := *s.genPrefixes

	if pr.Additions == nil {
		glog.Warningf("PR %d has nil Additions", *pr.Number)
		return
	}
	adds := *pr.Additions
	if pr.Deletions == nil {
		glog.Warningf("PR %d has nil Deletions", *pr.Number)
		return
	}
	dels := *pr.Deletions

	for _, c := range commits {
		for _, f := range c.Files {
			for _, p := range genPrefixes {
				if strings.HasPrefix(*f.Filename, p) {
					adds = adds - *f.Additions
					dels = dels - *f.Deletions
					continue
				}
			}
			if genFiles.Has(*f.Filename) {
				adds = adds - *f.Additions
				dels = dels - *f.Deletions
				continue
			}
		}
	}

	newSize := calculateSize(adds, dels)
	newLabel := labelSizePrefix + newSize

	existing := github_util.GetLabelsWithPrefix(issue.Labels, labelSizePrefix)
	needsUpdate := true
	for _, l := range existing {
		if l == newLabel {
			needsUpdate = false
			continue
		}
		config.RemoveLabel(*pr.Number, l)
	}
	if needsUpdate {
		config.AddLabels(*pr.Number, []string{newLabel})

		body := fmt.Sprintf("Labelling this PR as %s", newLabel)
		config.WriteComment(*pr.Number, body)
	}
}
Example #2
0
func (NeedsRebaseMunger) MungePullRequest(config *config.MungeConfig, pr *github.PullRequest, issue *github.Issue, commits []github.RepositoryCommit, events []github.IssueEvent) {
	mergeable, err := config.IsPRMergeable(pr)
	if err != nil {
		glog.V(2).Infof("Skipping %d - problem determining mergeable", *pr.Number)
		return
	}
	if mergeable && github_util.HasLabel(issue.Labels, needsRebase) {
		config.RemoveLabel(*pr.Number, needsRebase)
	}
	if !mergeable && !github_util.HasLabel(issue.Labels, needsRebase) {
		config.AddLabels(*pr.Number, []string{needsRebase})
	}
}
Example #3
0
func doMungers(config *config.MungeConfig) error {
	if len(config.IssueMungers) == 0 && len(config.PRMungersList) == 0 {
		glog.Fatalf("must include at least one --issue-mungers or --pr-mungers")
	}
	for {
		nextRunStartTime := time.Now().Add(config.Period)
		if len(config.IssueMungers) > 0 {
			glog.Infof("Running issue mungers")
			if err := issues.MungeIssues(config); err != nil {
				glog.Errorf("Error munging issues: %v", err)
			}
		}
		if len(config.PRMungersList) > 0 {
			glog.Infof("Running PR mungers")
			if err := pulls.MungePullRequests(config); err != nil {
				glog.Errorf("Error munging PRs: %v", err)
			}
		}
		config.ResetAPICount()
		if config.Once {
			break
		}
		if nextRunStartTime.After(time.Now()) {
			sleepDuration := nextRunStartTime.Sub(time.Now())
			glog.Infof("Sleeping for %v\n", sleepDuration)
			time.Sleep(sleepDuration)
		} else {
			glog.Infof("Not sleeping as we took more than %v to complete one loop\n", config.Period)
		}
	}
	return nil
}
Example #4
0
func (b *BlunderbussMunger) MungePullRequest(config *config.MungeConfig, pr *github.PullRequest, issue *github.Issue, commits []github.RepositoryCommit, events []github.IssueEvent) {
	if b.config == nil {
		b.loadConfig()
	}
	if !b.blunderbussReassign && issue.Assignee != nil {
		glog.V(6).Infof("skipping %v: reassign: %v assignee: %v", *pr.Number, b.blunderbussReassign, describeUser(issue.Assignee))
		return
	}
	potentialOwners := weightMap{}
	weightSum := int64(0)
	for _, commit := range commits {
		if commit.Author == nil || commit.Author.Login == nil || commit.SHA == nil {
			glog.Warningf("Skipping invalid commit for %d: %#v", *pr.Number, commit)
			continue
		}
		for _, file := range commit.Files {
			fileWeight := int64(1)
			if file.Changes != nil && *file.Changes != 0 {
				fileWeight = int64(*file.Changes)
			}
			// Judge file size on a log scale-- effectively this
			// makes three buckets, we shouldn't have many 10k+
			// line changes.
			fileWeight = int64(math.Log10(float64(fileWeight))) + 1
			fileOwners := b.config.FindOwners(*file.Filename)
			if len(fileOwners) == 0 {
				glog.Warningf("Couldn't find an owner for: %s", *file.Filename)
			}
			for owner, ownerWeight := range fileOwners {
				if owner == *pr.User.Login {
					continue
				}
				potentialOwners[owner] = potentialOwners[owner] + fileWeight*ownerWeight
				weightSum += fileWeight * ownerWeight
			}
		}
	}
	if len(potentialOwners) == 0 {
		glog.Errorf("No owners found for PR %d", *pr.Number)
		return
	}
	glog.V(4).Infof("Weights: %#v\nSum: %v", potentialOwners, weightSum)

	if issue.Assignee != nil {
		cur := *issue.Assignee.Login
		glog.Infof("Current assignee %v has a %02.2f%% chance of having been chosen", cur, 100.0*float64(potentialOwners[cur])/float64(weightSum))
	}
	selection := rand.Int63n(weightSum)
	owner := ""
	for o, w := range potentialOwners {
		owner = o
		selection -= w
		if selection <= 0 {
			break
		}
	}
	glog.Infof("Assigning %v to %v (previously assigned to %v)", *pr.Number, owner, describeUser(issue.Assignee))
	config.AssignPR(*pr.Number, owner)
}
Example #5
0
// getGeneratedFiles returns a list of all automatically generated files in the repo. These include
// docs, deep_copy, and conversions
//
// It would be 'better' to call this for every commit but that takes
// a whole lot of time for almost always the same information, and if
// our results are slightly wrong, who cares? Instead look for the
// generated files once and if someone changed what files are generated
// we'll size slightly wrong. No biggie.
func (s *SizeMunger) getGeneratedFiles(config *config.MungeConfig) {
	if s.genFiles != nil {
		return
	}
	files := sets.NewString()
	prefixes := []string{}
	s.genFiles = &files
	s.genPrefixes = &prefixes

	file := s.generatedFilesFile
	if len(file) == 0 {
		glog.Infof("No --generated-files-config= supplied, applying no labels")
		return
	}
	fp, err := os.Open(file)
	if err != nil {
		glog.Errorf("Unable to open %q: %v", file, err)
		return
	}

	defer fp.Close()
	scanner := bufio.NewScanner(fp)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.HasPrefix(line, "#") || line == "" {
			continue
		}
		fields := strings.Fields(line)
		if len(fields) != 2 {
			glog.Errorf("Invalid line in generated docs config %s: %q", file, line)
			continue
		}
		eType := fields[0]
		file := fields[1]
		if eType == "prefix" {
			prefixes = append(prefixes, file)
		} else if eType == "path" {
			files.Insert(file)
		} else if eType == "paths-from-repo" {
			docs, err := config.GetFileContents(file, "")
			if err != nil {
				continue
			}
			docSlice := strings.Split(docs, "\n")
			files.Insert(docSlice...)
		} else {
			glog.Errorf("Invalid line in generated docs config, unknown type: %s, %q", eType, line)
			continue
		}
	}
	if scanner.Err() != nil {
		glog.Errorf("Error scanning %s: %v", file, err)
		return
	}
	s.genFiles = &files
	s.genPrefixes = &prefixes

	return
}
Example #6
0
func (OkToTestMunger) MungePullRequest(config *config.MungeConfig, pr *github.PullRequest, issue *github.Issue, commits []github.RepositoryCommit, events []github.IssueEvent) {
	if !github_util.HasLabel(issue.Labels, "lgtm") {
		return
	}
	status, err := config.GetStatus(*pr.Number, []string{"Jenkins GCE e2e"})
	if err != nil {
		glog.Errorf("unexpected error getting status: %v", err)
		return
	}
	if status == "incomplete" {
		glog.V(2).Infof("status is incomplete, adding ok to test")
		msg := `@k8s-bot ok to test

	pr builder appears to be missing, activating due to 'lgtm' label.`
		config.WriteComment(*pr.Number, msg)
	}
}
Example #7
0
func MungeIssues(config *config.MungeConfig) error {
	mfunc := func(pr *github_api.PullRequest, issue *github_api.Issue) error {
		return mungeIssue(config, pr, issue)
	}
	if err := config.ForEachIssueDo([]string{}, mfunc); err != nil {
		return err
	}
	return nil
}
Example #8
0
// MungePullRequest is the workhorse the will actually make updates to the PR
func (LGTMAfterCommitMunger) MungePullRequest(config *config.MungeConfig, pr *github.PullRequest, issue *github.Issue, commits []github.RepositoryCommit, events []github.IssueEvent) {
	lastModified := lastModifiedTime(commits)
	lgtmTime := lgtmTime(events)

	if lastModified == nil || lgtmTime == nil {
		return
	}

	if !github_util.HasLabel(issue.Labels, "lgtm") {
		return
	}

	if lastModified.After(*lgtmTime) {
		lgtmRemovedBody := "PR changed after LGTM, removing LGTM."
		if err := config.WriteComment(*pr.Number, lgtmRemovedBody); err != nil {
			return
		}
		config.RemoveLabel(*pr.Number, "lgtm")
	}
}
Example #9
0
func (PingCIMunger) MungePullRequest(config *config.MungeConfig, pr *github.PullRequest, issue *github.Issue, commits []github.RepositoryCommit, events []github.IssueEvent) {
	if !github_util.HasLabel(issue.Labels, "lgtm") {
		return
	}
	if mergeable, err := config.IsPRMergeable(pr); err != nil {
		glog.V(2).Infof("Skipping %d - problem determining mergeability", *pr.Number)
	} else if !mergeable {
		glog.V(2).Infof("Skipping %d - not mergeable", *pr.Number)
	}
	status, err := config.GetStatus(pr, []string{"Shippable", "continuous-integration/travis-ci/pr"})
	if err != nil {
		glog.Errorf("unexpected error getting status: %v", err)
		return
	}
	if status == "incomplete" {
		glog.V(2).Infof("status is incomplete, closing and re-opening")
		msg := "Continuous integration appears to have missed, closing and re-opening to trigger it"
		config.WriteComment(*pr.Number, msg)

		config.ClosePR(pr)
		time.Sleep(5 * time.Second)
		config.OpenPR(pr, 10)
	}
}
Example #10
0
func mungePR(config *config.MungeConfig, pr *github_api.PullRequest, issue *github_api.Issue) error {
	if pr == nil {
		fmt.Printf("found nil pr\n")
	}
	mungers, err := getMungers(config.PRMungersList)
	if err != nil {
		return err
	}

	commits, err := config.GetFilledCommits(*pr.Number)
	if err != nil {
		return err
	}

	events, err := config.GetAllEventsForPR(*pr.Number)
	if err != nil {
		return err
	}

	for _, munger := range mungers {
		munger.MungePullRequest(config, pr, issue, commits, events)
	}
	return nil
}
Example #11
0
// getGeneratedFiles returns a list of all automatically generated files in the repo. These include
// docs, deep_copy, and conversions
func getGeneratedFiles(config *config.MungeConfig, c github.RepositoryCommit) []string {
	genFiles := []string{
		"pkg/api/v1/deep_copy_generated.go",
		"pkg/api/deep_copy_generated.go",
		"pkg/expapi/v1/deep_copy_generated.go",
		"pkg/expapi/deep_copy_generated.go",
		"pkg/api/v1/conversion_generated.go",
		"pkg/expapi/v1/conversion_generated.go",
		"api/swagger-spec/resourceListing.json",
		"api/swagger-spec/version.json",
		"api/swagger-spec/api.json",
		"api/swagger-spec/v1.json",
	}
	docs, err := config.GetFileContents(".generated_docs", *c.SHA)
	if err != nil {
		docs = ""
	}
	docSlice := strings.Split(docs, "\n")
	genFiles = append(genFiles, docSlice...)

	return genFiles
}
Example #12
0
// MungePullRequest is the workhorse the will actually make updates to the PR
func (p *PathLabelMunger) MungePullRequest(config *config.MungeConfig, pr *github.PullRequest, issue *github.Issue, commits []github.RepositoryCommit, events []github.IssueEvent) {
	if p.labelMap == nil {
		if err := p.loadPathMap(); err != nil {
			return
		}
	}
	labelMap := *p.labelMap

	needsLabels := sets.NewString()
	for _, c := range commits {
		for _, f := range c.Files {
			for prefix, label := range labelMap {
				if strings.HasPrefix(*f.Filename, prefix) && !github_util.HasLabel(issue.Labels, label) {
					needsLabels.Insert(label)
				}
			}
		}
	}

	if needsLabels.Len() != 0 {
		config.AddLabels(*pr.Number, needsLabels.List())
	}
}
Example #13
0
func (PRSizeMunger) MungePullRequest(config *config.MungeConfig, pr *github.PullRequest, issue *github.Issue, commits []github.RepositoryCommit, events []github.IssueEvent) {
	if pr.Additions == nil {
		glog.Warningf("PR %d has nil Additions", *pr.Number)
		return
	}
	if pr.Deletions == nil {
		glog.Warningf("PR %d has nil Deletions", *pr.Number)
		return
	}

	adds := *pr.Additions
	dels := *pr.Deletions

	// It would be 'better' to call this for every commit but that takes
	// a whole lot of time for almost always the same information, and if
	// our results are slightly wrong, who cares? Instead look for the
	// generated files once per PR and if someone changed both what files
	// are generated and then undid that change in an intermediate commit
	// we might call this PR bigger than we "should."
	genFiles := getGeneratedFiles(config, commits[len(commits)-1])

	for _, c := range commits {
		for _, f := range c.Files {
			if strings.HasPrefix(*f.Filename, "Godeps/") {
				adds = adds - *f.Additions
				dels = dels - *f.Deletions
				continue
			}
			found := false
			for _, genFile := range genFiles {
				if *f.Filename == genFile {
					adds = adds - *f.Additions
					dels = dels - *f.Deletions
					found = true
					break
				}
			}
			if found {
				continue
			}
		}
	}

	newSize := calculateSize(adds, dels)
	newLabel := labelSizePrefix + newSize

	existing := github_util.GetLabelsWithPrefix(issue.Labels, labelSizePrefix)
	needsUpdate := true
	for _, l := range existing {
		if l == newLabel {
			needsUpdate = false
			continue
		}
		config.RemoveLabel(*pr.Number, l)
	}
	if needsUpdate {
		config.AddLabels(*pr.Number, []string{newLabel})

		body := fmt.Sprintf("Labelling this PR as %s", newLabel)
		config.WriteComment(*pr.Number, body)
	}
}