Esempio n. 1
0
// updateRepo syncs the given repo and returns a set of BuildCandidates for it.
func (q *BuildQueue) updateRepo(repoUrl string, repo *gitinfo.GitInfo, now time.Time) (map[string][]*BuildCandidate, error) {
	from := now.Add(-q.period)
	if q.period == PERIOD_FOREVER {
		from = time.Unix(0, 0)
	}
	recentCommits := repo.From(from)
	commitDetails := map[string]*gitinfo.LongCommit{}
	for _, c := range recentCommits {
		details, err := repo.Details(c)
		if err != nil {
			return nil, err
		}
		commitDetails[c] = details
	}

	// Get all builds associated with the recent commits.
	buildsByCommit, err := buildbot.GetBuildsForCommits(recentCommits, map[int]bool{})
	if err != nil {
		return nil, err
	}

	// Find the sets of all bots and masters, organize builds by
	// commit/builder and builder/number.
	masters := map[string]string{}
	builds := map[string]map[string]*buildbot.Build{}
	buildsByBuilderAndNum := map[string]map[int]*buildbot.Build{}
	for commit, buildsForCommit := range buildsByCommit {
		builds[commit] = map[string]*buildbot.Build{}
		for _, build := range buildsForCommit {
			if !util.In(build.Builder, q.botWhitelist) {
				continue
			}
			masters[build.Builder] = build.Master
			builds[commit][build.Builder] = build
			if _, ok := buildsByBuilderAndNum[build.Builder]; !ok {
				buildsByBuilderAndNum[build.Builder] = map[int]*buildbot.Build{}
			}
			buildsByBuilderAndNum[build.Builder][build.Number] = build
		}
	}
	allBots := make([]string, 0, len(masters))
	for builder, _ := range masters {
		allBots = append(allBots, builder)
	}

	// Find the current scores for each commit/builder pair.
	currentScores := map[string]map[string]float64{}
	for _, commit := range recentCommits {
		myBuilds, ok := builds[commit]
		if !ok {
			myBuilds = map[string]*buildbot.Build{}
		}
		currentScores[commit] = map[string]float64{}
		for _, builder := range allBots {
			currentScores[commit][builder] = scoreBuild(commitDetails[commit], myBuilds[builder], now, q.timeLambda)
		}
	}

	// For each commit/builder pair, determine the score increase obtained
	// by running a build at that commit.
	scoreIncrease := map[string]map[string]float64{}
	for _, commit := range recentCommits {
		scoreIncrease[commit] = map[string]float64{}
		for _, builder := range allBots {
			// Shortcut: Don't bisect builds with a huge number
			// of commits.  This saves lots of time and only affects
			// the first successful build for a bot.
			if _, ok := builds[commit][builder]; ok {
				if len(builds[commit][builder].Commits) > NO_BISECT_COMMIT_LIMIT {
					glog.Warningf("Skipping %s on %s; previous build has too many commits.", commit[0:7], builder)
					scoreIncrease[commit][builder] = 0.0
					continue
				}
			}

			newScores := map[string]float64{}
			// Pretend to create a new Build at the given commit.
			newBuild := buildbot.Build{
				Builder:     builder,
				Master:      masters[builder],
				Number:      math.MaxInt32,
				GotRevision: commit,
				Repository:  repoUrl,
			}
			commits, stealFrom, stolen, err := buildbot.FindCommitsForBuild(&newBuild, q.repos)
			if err != nil {
				return nil, err
			}
			// Re-score all commits in the new build.
			newBuild.Commits = commits
			for _, c := range commits {
				if _, ok := currentScores[c]; !ok {
					// If this build has commits which are outside of our window,
					// insert them into currentScores to account for them.
					score := scoreBuild(commitDetails[commit], builds[commit][builder], now, q.timeLambda)
					currentScores[c] = map[string]float64{
						builder: score,
					}
				}
				if _, ok := commitDetails[c]; !ok {
					d, err := repo.Details(c)
					if err != nil {
						return nil, err
					}
					commitDetails[c] = d
				}
				newScores[c] = scoreBuild(commitDetails[c], &newBuild, now, q.timeLambda)
			}
			// If the new build includes commits previously included in
			// another build, update scores for commits in the build we stole
			// them from.
			if stealFrom != -1 {
				stoleFromOrig, ok := buildsByBuilderAndNum[builder][stealFrom]
				if !ok {
					// The build may not be cached. Fall back on getting it from the DB.
					stoleFromOrig, err = buildbot.GetBuildFromDB(builder, masters[builder], stealFrom)
					if err != nil {
						return nil, err
					}
					buildsByBuilderAndNum[builder][stealFrom] = stoleFromOrig
				}
				// "copy" the build so that we can assign new commits to it
				// without modifying the cached build.
				stoleFromBuild := *stoleFromOrig
				newCommits := []string{}
				for _, c := range stoleFromBuild.Commits {
					if !util.In(c, stolen) {
						newCommits = append(newCommits, c)
					}
				}
				stoleFromBuild.Commits = newCommits
				for _, c := range stoleFromBuild.Commits {
					newScores[c] = scoreBuild(commitDetails[c], &stoleFromBuild, now, q.timeLambda)
				}
			}
			// Sum the old and new scores.
			// First, sort the old and new scores to help with numerical stability.
			oldScoresList := make([]float64, 0, len(newScores))
			newScoresList := make([]float64, 0, len(newScores))
			for c, score := range newScores {
				oldScoresList = append(oldScoresList, currentScores[c][builder])
				newScoresList = append(newScoresList, score)
			}
			sort.Sort(sort.Float64Slice(oldScoresList))
			sort.Sort(sort.Float64Slice(newScoresList))
			oldTotal := 0.0
			newTotal := 0.0
			for i, _ := range oldScoresList {
				oldTotal += oldScoresList[i]
				newTotal += newScoresList[i]
			}
			scoreIncrease[commit][builder] = newTotal - oldTotal
		}
	}

	// Arrange the score increases by builder.
	candidates := map[string][]*BuildCandidate{}
	for commit, builders := range scoreIncrease {
		for builder, scoreIncrease := range builders {
			if _, ok := candidates[builder]; !ok {
				candidates[builder] = []*BuildCandidate{}
			}
			// Don't schedule builds below the given threshold.
			if scoreIncrease > q.scoreThreshold {
				candidates[builder] = append(candidates[builder], &BuildCandidate{
					Author:  commitDetails[commit].Author,
					Builder: builder,
					Commit:  commit,
					Repo:    repoUrl,
					Score:   scoreIncrease,
				})
			}
		}
	}

	return candidates, nil
}