[autoroll] Refactor roll strategy into interface; add LKGR support
Bug: skia:
Change-Id: I5368251c79410d1ea63a26ab472b15f9344e60d1
Reviewed-on: https://skia-review.googlesource.com/25260
Reviewed-by: Ravi Mistry <rmistry@google.com>
Commit-Queue: Eric Boren <borenet@google.com>
diff --git a/autoroll/go/autoroll/main.go b/autoroll/go/autoroll/main.go
index c9bb7bd..b941f91 100644
--- a/autoroll/go/autoroll/main.go
+++ b/autoroll/go/autoroll/main.go
@@ -270,12 +270,16 @@
}
// Start the autoroller.
+ strat, err := repo_manager.GetNextRollStrategy(*strategy, *childBranch, "")
+ if err != nil {
+ sklog.Fatal(err)
+ }
if *rollIntoAndroid {
- arb, err = autorollerv2.NewAndroidAutoRoller(*workdir, *parentBranch, *childPath, *childBranch, cqExtraTrybots, emails, g, *strategy, *preUploadSteps)
+ arb, err = autorollerv2.NewAndroidAutoRoller(*workdir, *parentBranch, *childPath, *childBranch, cqExtraTrybots, emails, g, repo_manager.StrategyRemoteHead(*childBranch), *preUploadSteps)
} else if *useManifest {
- arb, err = autorollerv2.NewManifestAutoRoller(*workdir, *parentRepo, *parentBranch, *childPath, *childBranch, cqExtraTrybots, emails, g, depotTools, *strategy, *preUploadSteps)
+ arb, err = autorollerv2.NewManifestAutoRoller(*workdir, *parentRepo, *parentBranch, *childPath, *childBranch, cqExtraTrybots, emails, g, depotTools, strat, *preUploadSteps)
} else {
- arb, err = autorollerv2.NewDEPSAutoRoller(*workdir, *parentRepo, *parentBranch, *childPath, *childBranch, cqExtraTrybots, emails, g, depotTools, *strategy, *preUploadSteps)
+ arb, err = autorollerv2.NewDEPSAutoRoller(*workdir, *parentRepo, *parentBranch, *childPath, *childBranch, cqExtraTrybots, emails, g, depotTools, strat, *preUploadSteps)
}
if err != nil {
sklog.Fatal(err)
diff --git a/autoroll/go/autoroller/roller.go b/autoroll/go/autoroller/roller.go
index f999308..79fcacd 100644
--- a/autoroll/go/autoroller/roller.go
+++ b/autoroll/go/autoroller/roller.go
@@ -50,7 +50,7 @@
}
// NewAutoRoller creates and returns a new AutoRoller which runs at the given frequency.
-func NewAutoRoller(workdir, parentRepo, parentBranch, childPath, childBranch, cqExtraTrybots string, emails []string, gerrit *gerrit.Gerrit, depot_tools string, rollIntoAndroid, useManifest bool, strategy string) (*AutoRoller, error) {
+func NewAutoRoller(workdir, parentRepo, parentBranch, childPath, childBranch, cqExtraTrybots string, emails []string, gerrit *gerrit.Gerrit, depot_tools string, rollIntoAndroid, useManifest bool, strategy repo_manager.NextRollStrategy) (*AutoRoller, error) {
var err error
var rm repo_manager.RepoManager
if rollIntoAndroid {
diff --git a/autoroll/go/autoroller/roller_test.go b/autoroll/go/autoroller/roller_test.go
index 025415f..39b1cdc 100644
--- a/autoroll/go/autoroller/roller_test.go
+++ b/autoroll/go/autoroller/roller_test.go
@@ -76,6 +76,13 @@
return r.updateCount
}
+// ChildRevList returns a list of hashes.
+func (r *mockRepoManager) ChildRevList(args ...string) ([]string, error) {
+ r.mtx.RLock()
+ defer r.mtx.RUnlock()
+ return nil, fmt.Errorf("Not implemented")
+}
+
// FullChildHash returns the full hash of the given short hash or ref in the
// mocked child repo.
func (r *mockRepoManager) FullChildHash(shortHash string) (string, error) {
@@ -408,7 +415,7 @@
// setup initializes a fake AutoRoller for testing. It returns the working
// directory, AutoRoller instance, URLMock for faking HTTP requests, and an
// gerrit.ChangeInfo representing the first CL that was uploaded by the AutoRoller.
-func setup(t *testing.T, strategy string) (string, *AutoRoller, *mockRepoManager, *mockCodereview, *gerrit.ChangeInfo) {
+func setup(t *testing.T, strategy repo_manager.NextRollStrategy) (string, *AutoRoller, *mockRepoManager, *mockCodereview, *gerrit.ChangeInfo) {
testutils.SkipIfShort(t)
// Setup mocks.
@@ -429,7 +436,7 @@
}
rm := &mockRepoManager{t: t}
- repo_manager.NewDEPSRepoManager = func(workdir, parentRepo, parentBranch, childPath, childBranch string, depot_tools string, g *gerrit.Gerrit, strategy string, preUploadSteps []string) (repo_manager.RepoManager, error) {
+ repo_manager.NewDEPSRepoManager = func(workdir, parentRepo, parentBranch, childPath, childBranch string, depot_tools string, g *gerrit.Gerrit, strategy repo_manager.NextRollStrategy, preUploadSteps []string) (repo_manager.RepoManager, error) {
return rm, nil
}
@@ -456,7 +463,9 @@
func TestAutoRollBasic(t *testing.T) {
testutils.LargeTest(t)
// setup will initialize the roller and upload a CL.
- workdir, roller, rm, rv, roll1 := setup(t, repo_manager.ROLL_STRATEGY_BATCH)
+ s, err := repo_manager.GetNextRollStrategy(repo_manager.ROLL_STRATEGY_BATCH, "master", "")
+ assert.NoError(t, err)
+ workdir, roller, rm, rv, roll1 := setup(t, s)
defer func() {
assert.NoError(t, roller.Close())
assert.NoError(t, os.RemoveAll(workdir))
@@ -491,7 +500,9 @@
func TestAutoRollStop(t *testing.T) {
testutils.MediumTest(t)
// setup will initialize the roller and upload a CL.
- workdir, roller, rm, rv, roll1 := setup(t, repo_manager.ROLL_STRATEGY_BATCH)
+ s, err := repo_manager.GetNextRollStrategy(repo_manager.ROLL_STRATEGY_BATCH, "master", "")
+ assert.NoError(t, err)
+ workdir, roller, rm, rv, roll1 := setup(t, s)
defer func() {
assert.NoError(t, roller.Close())
assert.NoError(t, os.RemoveAll(workdir))
@@ -539,7 +550,9 @@
// TestAutoRollDryRun ensures that the Dry Run functionalify works as expected.
func TestAutoRollDryRun(t *testing.T) {
testutils.MediumTest(t)
- workdir, roller, rm, rv, roll1 := setup(t, repo_manager.ROLL_STRATEGY_BATCH)
+ s, err := repo_manager.GetNextRollStrategy(repo_manager.ROLL_STRATEGY_BATCH, "master", "")
+ assert.NoError(t, err)
+ workdir, roller, rm, rv, roll1 := setup(t, s)
defer func() {
assert.NoError(t, roller.Close())
assert.NoError(t, os.RemoveAll(workdir))
@@ -640,7 +653,9 @@
// sync the code, waiting for the commit to show up.
func TestAutoRollCommitLandRace(t *testing.T) {
testutils.LargeTest(t)
- workdir, roller, rm, rv, roll1 := setup(t, repo_manager.ROLL_STRATEGY_BATCH)
+ s, err := repo_manager.GetNextRollStrategy(repo_manager.ROLL_STRATEGY_BATCH, "master", "")
+ assert.NoError(t, err)
+ workdir, roller, rm, rv, roll1 := setup(t, s)
defer func() {
assert.NoError(t, roller.Close())
assert.NoError(t, os.RemoveAll(workdir))
@@ -693,7 +708,9 @@
// doesn't upload new CLs over and over.
func TestAutoRollThrottle(t *testing.T) {
testutils.MediumTest(t)
- workdir, roller, rm, rv, roll1 := setup(t, repo_manager.ROLL_STRATEGY_BATCH)
+ s, err := repo_manager.GetNextRollStrategy(repo_manager.ROLL_STRATEGY_BATCH, "master", "")
+ assert.NoError(t, err)
+ workdir, roller, rm, rv, roll1 := setup(t, s)
defer func() {
assert.NoError(t, roller.Close())
assert.NoError(t, os.RemoveAll(workdir))
@@ -732,7 +749,9 @@
func TestAutoRollSingle(t *testing.T) {
testutils.MediumTest(t)
// setup will initialize the roller and upload a CL.
- workdir, roller, rm, rv, roll1 := setup(t, repo_manager.ROLL_STRATEGY_SINGLE)
+ s, err := repo_manager.GetNextRollStrategy(repo_manager.ROLL_STRATEGY_BATCH, "master", "")
+ assert.NoError(t, err)
+ workdir, roller, rm, rv, roll1 := setup(t, s)
defer func() {
assert.NoError(t, roller.Close())
assert.NoError(t, os.RemoveAll(workdir))
diff --git a/autoroll/go/autorollerv2/autoroller.go b/autoroll/go/autorollerv2/autoroller.go
index 9054caf..f898004 100644
--- a/autoroll/go/autorollerv2/autoroller.go
+++ b/autoroll/go/autorollerv2/autoroller.go
@@ -77,7 +77,7 @@
}
// NewAndroidAutoRoller returns an AutoRoller instance which rolls into Android.
-func NewAndroidAutoRoller(workdir, parentBranch, childPath, childBranch, cqExtraTrybots string, emails []string, gerrit *gerrit.Gerrit, strategy string, preUploadSteps []string) (*AutoRoller, error) {
+func NewAndroidAutoRoller(workdir, parentBranch, childPath, childBranch, cqExtraTrybots string, emails []string, gerrit *gerrit.Gerrit, strategy repo_manager.NextRollStrategy, preUploadSteps []string) (*AutoRoller, error) {
rm, err := repo_manager.NewAndroidRepoManager(workdir, parentBranch, childPath, childBranch, gerrit, strategy, preUploadSteps)
if err != nil {
return nil, err
@@ -89,7 +89,7 @@
}
// NewDEPSAutoRoller returns an AutoRoller instance which rolls using DEPS.
-func NewDEPSAutoRoller(workdir, parentRepo, parentBranch, childPath, childBranch, cqExtraTrybots string, emails []string, gerrit *gerrit.Gerrit, depot_tools string, strategy string, preUploadSteps []string) (*AutoRoller, error) {
+func NewDEPSAutoRoller(workdir, parentRepo, parentBranch, childPath, childBranch, cqExtraTrybots string, emails []string, gerrit *gerrit.Gerrit, depot_tools string, strategy repo_manager.NextRollStrategy, preUploadSteps []string) (*AutoRoller, error) {
rm, err := repo_manager.NewDEPSRepoManager(workdir, parentRepo, parentBranch, childPath, childBranch, depot_tools, gerrit, strategy, preUploadSteps)
if err != nil {
return nil, err
@@ -101,7 +101,7 @@
}
// NewManifestAutoRoller returns an AutoRoller instance which rolls using DEPS.
-func NewManifestAutoRoller(workdir, parentRepo, parentBranch, childPath, childBranch, cqExtraTrybots string, emails []string, gerrit *gerrit.Gerrit, depot_tools string, strategy string, preUploadSteps []string) (*AutoRoller, error) {
+func NewManifestAutoRoller(workdir, parentRepo, parentBranch, childPath, childBranch, cqExtraTrybots string, emails []string, gerrit *gerrit.Gerrit, depot_tools string, strategy repo_manager.NextRollStrategy, preUploadSteps []string) (*AutoRoller, error) {
rm, err := repo_manager.NewManifestRepoManager(workdir, parentRepo, parentBranch, childPath, childBranch, depot_tools, gerrit, strategy, preUploadSteps)
if err != nil {
return nil, err
diff --git a/autoroll/go/repo_manager/android_repo_manager.go b/autoroll/go/repo_manager/android_repo_manager.go
index 5789a34..73a8bef 100644
--- a/autoroll/go/repo_manager/android_repo_manager.go
+++ b/autoroll/go/repo_manager/android_repo_manager.go
@@ -26,7 +26,7 @@
var (
// Use this function to instantiate a NewAndroidRepoManager. This is able to be
// overridden for testing.
- NewAndroidRepoManager func(string, string, string, string, gerrit.GerritInterface, string, []string) (RepoManager, error) = newAndroidRepoManager
+ NewAndroidRepoManager func(string, string, string, string, gerrit.GerritInterface, NextRollStrategy, []string) (RepoManager, error) = newAndroidRepoManager
IGNORE_MERGE_CONFLICT_FILES = []string{"include/config/SkUserConfig.h"}
@@ -44,7 +44,7 @@
authDaemonRunning bool
}
-func newAndroidRepoManager(workdir, parentBranch, childPath, childBranch string, g gerrit.GerritInterface, strategy string, preUploadStepNames []string) (RepoManager, error) {
+func newAndroidRepoManager(workdir, parentBranch, childPath, childBranch string, g gerrit.GerritInterface, strategy NextRollStrategy, preUploadStepNames []string) (RepoManager, error) {
user, err := user.Current()
if err != nil {
return nil, err
@@ -52,6 +52,8 @@
repoToolPath := path.Join(user.HomeDir, "bin", "repo")
gitCookieAuthDaemonPath := path.Join(user.HomeDir, "gcompute-tools", "git-cookie-authdaemon")
wd := path.Join(workdir, "android_repo")
+ childDir := path.Join(wd, childPath)
+ childRepo := &git.Checkout{GitDir: git.GitDir(childDir)}
preUploadSteps, err := GetPreUploadSteps(preUploadStepNames)
if err != nil {
@@ -61,9 +63,9 @@
r := &androidRepoManager{
commonRepoManager: &commonRepoManager{
parentBranch: parentBranch,
- childDir: path.Join(wd, childPath),
+ childDir: childDir,
childPath: childPath,
- childRepo: nil, // This will be filled in on the first update.
+ childRepo: childRepo,
childBranch: childBranch,
preUploadSteps: preUploadSteps,
strategy: strategy,
@@ -115,11 +117,6 @@
return err
}
- // Create the child GitInfo if needed.
- if r.childRepo == nil {
- r.childRepo = &git.Checkout{GitDir: git.GitDir(r.childDir)}
- }
-
// Fix the review config to a URL which will work outside prod.
if _, err := exec.RunCwd(r.childRepo.Dir(), "git", "config", "remote.goog.review", fmt.Sprintf("%s/", r.repoUrl)); err != nil {
return err
@@ -143,24 +140,11 @@
}
// Get the next roll revision.
- childHead, err := r.getChildRepoHead()
+ nextRollRev, err := r.strategy.GetNextRollRev(r.childRepo, lastRollRev)
if err != nil {
return err
}
- var nextRollRev string
- if r.strategy == ROLL_STRATEGY_SINGLE {
- commits, err := r.childRepo.RevList(fmt.Sprintf("%s..%s", lastRollRev, childHead))
- if err != nil {
- return fmt.Errorf("Failed to list revisions: %s", err)
- }
- if len(commits) == 0 {
- nextRollRev = lastRollRev
- } else {
- nextRollRev = commits[len(commits)-1]
- }
- } else {
- nextRollRev = childHead
- }
+
r.infoMtx.Lock()
defer r.infoMtx.Unlock()
r.lastRollRev = lastRollRev
@@ -169,16 +153,6 @@
return nil
}
-// getChildRepoHead returns the commit hash of the latest commit in the child repo.
-func (r *androidRepoManager) getChildRepoHead() (string, error) {
- output, err := exec.RunCwd(r.childRepo.Dir(), "git", "ls-remote", UPSTREAM_REMOTE_NAME, fmt.Sprintf("refs/heads/%s", r.childBranch), "-1")
- if err != nil {
- return "", err
- }
- tokens := strings.Split(output, "\t")
- return tokens[0], nil
-}
-
// getLastRollRev returns the commit hash of the last-completed DEPS roll.
func (r *androidRepoManager) getLastRollRev() (string, error) {
output, err := exec.RunCwd(r.childRepo.Dir(), "git", "merge-base", fmt.Sprintf("refs/remotes/remote/%s", r.childBranch), fmt.Sprintf("refs/remotes/goog/%s", r.parentBranch))
diff --git a/autoroll/go/repo_manager/android_repo_manager_test.go b/autoroll/go/repo_manager/android_repo_manager_test.go
index baf3ae5..637c28c 100644
--- a/autoroll/go/repo_manager/android_repo_manager_test.go
+++ b/autoroll/go/repo_manager/android_repo_manager_test.go
@@ -65,7 +65,7 @@
defer cleanup()
g, err := gerrit.NewGerrit(mockAndroidServer, "", nil)
assert.NoError(t, err)
- rm, err := NewAndroidRepoManager(wd, "master", childPath, "master", g, ROLL_STRATEGY_BATCH, nil)
+ rm, err := NewAndroidRepoManager(wd, "master", childPath, "master", g, StrategyRemoteHead("master"), nil)
assert.NoError(t, err)
assert.Equal(t, fmt.Sprintf("%s/android_repo/%s", wd, childPath), rm.(*androidRepoManager).childDir)
@@ -82,7 +82,7 @@
defer cleanup()
g := &gerrit.MockedGerrit{IssueID: androidIssueNum}
- rm, err := NewAndroidRepoManager(wd, "master", childPath, "master", g, ROLL_STRATEGY_BATCH, nil)
+ rm, err := NewAndroidRepoManager(wd, "master", childPath, "master", g, StrategyRemoteHead("master"), nil)
assert.NoError(t, err)
issue, err := rm.CreateNewRoll(rm.LastRollRev(), rm.NextRollRev(), androidEmails, "", false)
@@ -148,7 +148,7 @@
defer cleanup()
g := &gerrit.MockedGerrit{IssueID: androidIssueNum}
- rm, err := NewAndroidRepoManager(wd, "master", childPath, "master", g, ROLL_STRATEGY_BATCH, nil)
+ rm, err := NewAndroidRepoManager(wd, "master", childPath, "master", g, StrategyRemoteHead("master"), nil)
assert.NoError(t, err)
ran := false
diff --git a/autoroll/go/repo_manager/deps_repo_manager.go b/autoroll/go/repo_manager/deps_repo_manager.go
index 9fb9e98..9d50ed1 100644
--- a/autoroll/go/repo_manager/deps_repo_manager.go
+++ b/autoroll/go/repo_manager/deps_repo_manager.go
@@ -28,7 +28,7 @@
var (
// Use this function to instantiate a RepoManager. This is able to be
// overridden for testing.
- NewDEPSRepoManager func(string, string, string, string, string, string, *gerrit.Gerrit, string, []string) (RepoManager, error) = newDEPSRepoManager
+ NewDEPSRepoManager func(string, string, string, string, string, string, *gerrit.Gerrit, NextRollStrategy, []string) (RepoManager, error) = newDEPSRepoManager
DEPOT_TOOLS_AUTH_USER_REGEX = regexp.MustCompile(fmt.Sprintf("Logged in to %s as ([\\w-]+).", autoroll.RIETVELD_URL))
)
@@ -47,7 +47,7 @@
// newDEPSRepoManager returns a RepoManager instance which operates in the given
// working directory and updates at the given frequency.
-func newDEPSRepoManager(workdir, parentRepo, parentBranch, childPath, childBranch string, depot_tools string, g *gerrit.Gerrit, strategy string, preUploadStepNames []string) (RepoManager, error) {
+func newDEPSRepoManager(workdir, parentRepo, parentBranch, childPath, childBranch string, depot_tools string, g *gerrit.Gerrit, strategy NextRollStrategy, preUploadStepNames []string) (RepoManager, error) {
gclient := GCLIENT
rollDep := ROLL_DEP
if depot_tools != "" {
@@ -58,6 +58,8 @@
wd := path.Join(workdir, "repo_manager")
parentBase := strings.TrimSuffix(path.Base(parentRepo), ".git")
parentDir := path.Join(wd, parentBase)
+ childDir := path.Join(wd, childPath)
+ childRepo := &git.Checkout{GitDir: git.GitDir(childDir)}
user, err := g.GetUserEmail()
if err != nil {
@@ -74,15 +76,15 @@
depotToolsRepoManager: &depotToolsRepoManager{
commonRepoManager: &commonRepoManager{
parentBranch: parentBranch,
- childDir: path.Join(wd, childPath),
+ childDir: childDir,
childPath: childPath,
- childRepo: nil, // This will be filled in on the first update.
+ childRepo: childRepo,
childBranch: childBranch,
+ g: g,
preUploadSteps: preUploadSteps,
strategy: strategy,
user: user,
workdir: wd,
- g: g,
},
depot_tools: depot_tools,
gclient: gclient,
@@ -91,6 +93,7 @@
},
rollDep: rollDep,
}
+
// TODO(borenet): This update can be extremely expensive. Consider
// moving it out of the startup critical path.
return dr, dr.Update()
@@ -106,11 +109,6 @@
return fmt.Errorf("Could not create and sync parent repo: %s", err)
}
- // Create the child GitInfo if needed.
- if dr.childRepo == nil {
- dr.childRepo = &git.Checkout{GitDir: git.GitDir(dr.childDir)}
- }
-
// Get the last roll revision.
lastRollRev, err := dr.getLastRollRev()
if err != nil {
@@ -118,24 +116,10 @@
}
// Get the next roll revision.
- childHead, err := dr.childRepo.FullHash(fmt.Sprintf("origin/%s", dr.childBranch))
+ nextRollRev, err := dr.strategy.GetNextRollRev(dr.childRepo, lastRollRev)
if err != nil {
return err
}
- var nextRollRev string
- if dr.strategy == ROLL_STRATEGY_SINGLE {
- commits, err := dr.childRepo.RevList(fmt.Sprintf("%s..%s", lastRollRev, childHead))
- if err != nil {
- return fmt.Errorf("Failed to list revisions: %s", err)
- }
- if len(commits) == 0 {
- nextRollRev = lastRollRev
- } else {
- nextRollRev = commits[len(commits)-1]
- }
- } else {
- nextRollRev = childHead
- }
dr.infoMtx.Lock()
defer dr.infoMtx.Unlock()
dr.lastRollRev = lastRollRev
diff --git a/autoroll/go/repo_manager/deps_repo_manager_test.go b/autoroll/go/repo_manager/deps_repo_manager_test.go
index cc92b8f..b6c5a93 100644
--- a/autoroll/go/repo_manager/deps_repo_manager_test.go
+++ b/autoroll/go/repo_manager/deps_repo_manager_test.go
@@ -107,7 +107,9 @@
defer cleanup()
g := setupFakeGerrit(t, wd)
- rm, err := NewDEPSRepoManager(wd, parent.RepoUrl(), "master", childPath, "master", depotTools, g, ROLL_STRATEGY_BATCH, nil)
+ s, err := GetNextRollStrategy(ROLL_STRATEGY_BATCH, "master", "")
+ assert.NoError(t, err)
+ rm, err := NewDEPSRepoManager(wd, parent.RepoUrl(), "master", childPath, "master", depotTools, g, s, nil)
assert.NoError(t, err)
assert.Equal(t, childCommits[0], rm.LastRollRev())
assert.Equal(t, childCommits[len(childCommits)-1], rm.NextRollRev())
@@ -143,8 +145,10 @@
wd, child, childCommits, parent, cleanup := setup(t)
defer cleanup()
+ s, err := GetNextRollStrategy(strategy, "master", "")
+ assert.NoError(t, err)
g := setupFakeGerrit(t, wd)
- rm, err := NewDEPSRepoManager(wd, parent.RepoUrl(), "master", childPath, "master", depotTools, g, strategy, nil)
+ rm, err := NewDEPSRepoManager(wd, parent.RepoUrl(), "master", childPath, "master", depotTools, g, s, nil)
assert.NoError(t, err)
// Create a roll, assert that it's at tip of tree.
@@ -178,8 +182,10 @@
wd, _, _, parent, cleanup := setup(t)
defer cleanup()
+ s, err := GetNextRollStrategy(ROLL_STRATEGY_BATCH, "master", "")
+ assert.NoError(t, err)
g := setupFakeGerrit(t, wd)
- rm, err := NewDEPSRepoManager(wd, parent.RepoUrl(), "master", childPath, "master", depotTools, g, ROLL_STRATEGY_BATCH, nil)
+ rm, err := NewDEPSRepoManager(wd, parent.RepoUrl(), "master", childPath, "master", depotTools, g, s, nil)
assert.NoError(t, err)
ran := false
diff --git a/autoroll/go/repo_manager/manifest_repo_manager.go b/autoroll/go/repo_manager/manifest_repo_manager.go
index a039d28..8a58c73 100644
--- a/autoroll/go/repo_manager/manifest_repo_manager.go
+++ b/autoroll/go/repo_manager/manifest_repo_manager.go
@@ -25,7 +25,7 @@
var (
// Use this function to instantiate a RepoManager. This is able to be
// overridden for testing.
- NewManifestRepoManager func(string, string, string, string, string, string, *gerrit.Gerrit, string, []string) (RepoManager, error) = newManifestRepoManager
+ NewManifestRepoManager func(string, string, string, string, string, string, *gerrit.Gerrit, NextRollStrategy, []string) (RepoManager, error) = newManifestRepoManager
)
// manifestRepoManager is a struct used by Manifest AutoRoller for managing checkouts.
@@ -35,10 +35,15 @@
// newManifestRepoManager returns a RepoManager instance which operates in the
// given working directory and updates at the given frequency.
-func newManifestRepoManager(workdir, parentRepo, parentBranch, childPath, childBranch string, depot_tools string, g *gerrit.Gerrit, strategy string, preUploadStepNames []string) (RepoManager, error) {
+func newManifestRepoManager(workdir, parentRepo, parentBranch, childPath, childBranch string, depot_tools string, g *gerrit.Gerrit, strategy NextRollStrategy, preUploadStepNames []string) (RepoManager, error) {
wd := path.Join(workdir, "repo_manager")
parentBase := strings.TrimSuffix(path.Base(parentRepo), ".git")
parentDir := path.Join(wd, parentBase)
+ childDir := path.Join(wd, childPath)
+ childRepo, err := git.NewCheckout(common.REPO_SKIA, wd)
+ if err != nil {
+ return nil, err
+ }
user, err := g.GetUserEmail()
if err != nil {
@@ -55,9 +60,9 @@
depotToolsRepoManager: &depotToolsRepoManager{
commonRepoManager: &commonRepoManager{
parentBranch: parentBranch,
- childDir: path.Join(wd, childPath),
+ childDir: childDir,
childPath: childPath,
- childRepo: nil, // This will be filled in on the first update.
+ childRepo: childRepo,
childBranch: childBranch,
preUploadSteps: preUploadSteps,
strategy: strategy,
@@ -71,6 +76,7 @@
parentRepo: parentRepo,
},
}
+
// TODO(borenet): This update can be extremely expensive. Consider
// moving it out of the startup critical path.
return mr, mr.Update()
@@ -86,14 +92,6 @@
return fmt.Errorf("Could not create and sync parent repo: %s", err)
}
- // Create the child GitInfo if needed.
- var err error
- if mr.childRepo == nil {
- mr.childRepo, err = git.NewCheckout(common.REPO_SKIA, mr.workdir)
- if err != nil {
- return err
- }
- }
if err := mr.childRepo.Update(); err != nil {
return err
}
@@ -105,24 +103,11 @@
}
// Get the next roll revision.
- childHead, err := mr.childRepo.FullHash(fmt.Sprintf("origin/%s", mr.childBranch))
+ nextRollRev, err := mr.strategy.GetNextRollRev(mr.childRepo, lastRollRev)
if err != nil {
return err
}
- var nextRollRev string
- if mr.strategy == ROLL_STRATEGY_SINGLE {
- commits, err := mr.childRepo.RevList(fmt.Sprintf("%s..%s", lastRollRev, childHead))
- if err != nil {
- return fmt.Errorf("Failed to list revisions: %s", err)
- }
- if len(commits) == 0 {
- nextRollRev = lastRollRev
- } else {
- nextRollRev = commits[len(commits)-1]
- }
- } else {
- nextRollRev = childHead
- }
+
mr.infoMtx.Lock()
defer mr.infoMtx.Unlock()
mr.lastRollRev = lastRollRev
diff --git a/autoroll/go/repo_manager/manifest_repo_manager_test.go b/autoroll/go/repo_manager/manifest_repo_manager_test.go
index 7fe9b8a..62fb11d 100644
--- a/autoroll/go/repo_manager/manifest_repo_manager_test.go
+++ b/autoroll/go/repo_manager/manifest_repo_manager_test.go
@@ -131,8 +131,10 @@
wd, child, childCommits, parent, cleanup := setupManifest(t)
defer cleanup()
+ s, err := GetNextRollStrategy(ROLL_STRATEGY_BATCH, "master", "")
+ assert.NoError(t, err)
g := setupManifestFakeGerrit(t, wd)
- rm, err := NewManifestRepoManager(wd, parent.RepoUrl(), "master", childPath, "master", depotTools, g, ROLL_STRATEGY_BATCH, nil)
+ rm, err := NewManifestRepoManager(wd, parent.RepoUrl(), "master", childPath, "master", depotTools, g, s, nil)
assert.NoError(t, err)
assert.Equal(t, childCommits[0], rm.LastRollRev())
assert.Equal(t, childCommits[len(childCommits)-1], rm.NextRollRev())
@@ -154,8 +156,10 @@
wd, _, _, parent, cleanup := setupManifest(t)
defer cleanup()
+ s, err := GetNextRollStrategy(ROLL_STRATEGY_BATCH, "master", "")
+ assert.NoError(t, err)
g := setupManifestFakeGerrit(t, wd)
- rm, err := NewManifestRepoManager(wd, parent.RepoUrl(), "master", childPath, "master", depotTools, g, ROLL_STRATEGY_BATCH, nil)
+ rm, err := NewManifestRepoManager(wd, parent.RepoUrl(), "master", childPath, "master", depotTools, g, s, nil)
assert.NoError(t, err)
// Create a roll, assert that it's at tip of tree.
@@ -173,8 +177,10 @@
wd, _, _, parent, cleanup := setupManifest(t)
defer cleanup()
+ s, err := GetNextRollStrategy(ROLL_STRATEGY_BATCH, "master", "")
+ assert.NoError(t, err)
g := setupManifestFakeGerrit(t, wd)
- rm, err := NewManifestRepoManager(wd, parent.RepoUrl(), "master", childPath, "master", depotTools, g, ROLL_STRATEGY_BATCH, nil)
+ rm, err := NewManifestRepoManager(wd, parent.RepoUrl(), "master", childPath, "master", depotTools, g, s, nil)
assert.NoError(t, err)
ran := false
rm.(*manifestRepoManager).preUploadSteps = []PreUploadStep{
diff --git a/autoroll/go/repo_manager/repo_manager.go b/autoroll/go/repo_manager/repo_manager.go
index 1ded0b4..e33206c 100644
--- a/autoroll/go/repo_manager/repo_manager.go
+++ b/autoroll/go/repo_manager/repo_manager.go
@@ -18,9 +18,7 @@
)
const (
- ROLL_STRATEGY_BATCH = "batch"
- ROLL_STRATEGY_SINGLE = "single"
- ROLL_BRANCH = "roll_branch"
+ ROLL_BRANCH = "roll_branch"
)
// RepoManager is the interface used by different Autoroller implementations
@@ -28,6 +26,7 @@
type RepoManager interface {
Update() error
FullChildHash(string) (string, error)
+ ChildRevList(...string) ([]string, error)
LastRollRev() string
NextRollRev() string
RolledPast(string) (bool, error)
@@ -51,12 +50,19 @@
childRepo *git.Checkout
childBranch string
preUploadSteps []PreUploadStep
- strategy string
+ strategy NextRollStrategy
user string
workdir string
g gerrit.GerritInterface
}
+// ChildRevList returns a slice of commit hashes from the child repo.
+func (r *commonRepoManager) ChildRevList(args ...string) ([]string, error) {
+ r.repoMtx.RLock()
+ defer r.repoMtx.RUnlock()
+ return r.childRepo.RevList(args...)
+}
+
// FullChildHash returns the full hash of the given short hash or ref in the
// child repo.
func (r *commonRepoManager) FullChildHash(shortHash string) (string, error) {
diff --git a/autoroll/go/repo_manager/strategy.go b/autoroll/go/repo_manager/strategy.go
new file mode 100644
index 0000000..66d274e
--- /dev/null
+++ b/autoroll/go/repo_manager/strategy.go
@@ -0,0 +1,150 @@
+package repo_manager
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "strings"
+
+ "go.skia.org/infra/go/git"
+ "go.skia.org/infra/go/util"
+)
+
+const (
+ ROLL_STRATEGY_BATCH = "batch"
+ ROLL_STRATEGY_LKGR = "lkgr"
+ ROLL_STRATEGY_REMOTE_BATCH = "remote batch"
+ ROLL_STRATEGY_SINGLE = "single"
+)
+
+// NextRollStrategy is an interface for modules which determine what the next roll
+// revision should be.
+type NextRollStrategy interface {
+ // Return the next roll revision, or an error. Parameters are the child
+ // git checkout and the last roll revision.
+ GetNextRollRev(*git.Checkout, string) (string, error)
+}
+
+// Return the NextRollStrategy indicated by the given string.
+func GetNextRollStrategy(strategy string, branch, lkgr string) (NextRollStrategy, error) {
+ switch strategy {
+ case ROLL_STRATEGY_REMOTE_BATCH:
+ return StrategyRemoteHead(branch), nil
+ case ROLL_STRATEGY_BATCH:
+ return StrategyHead(branch), nil
+ case ROLL_STRATEGY_SINGLE:
+ return StrategySingle(branch), nil
+ case ROLL_STRATEGY_LKGR:
+ return StrategyLKGR(lkgr), nil
+ default:
+ return nil, fmt.Errorf("Unknown roll strategy %q", strategy)
+ }
+}
+
+// headStrategy is a NextRollStrategy which always rolls to HEAD of a given branch.
+type headStrategy struct {
+ branch string
+}
+
+// See documentation for NextRollStrategy interface.
+func (s *headStrategy) GetNextRollRev(repo *git.Checkout, _ string) (string, error) {
+ return repo.FullHash(fmt.Sprintf("origin/%s", s.branch))
+}
+
+// StrategyHead returns a NextRollStrategy which always rolls to HEAD of a given branch.
+func StrategyHead(branch string) NextRollStrategy {
+ return &headStrategy{
+ branch: branch,
+ }
+}
+
+// remoteHeadStrategy is a NextRollStrategy which always rolls to HEAD of a
+// given branch, as defined by "git ls-remote".
+type remoteHeadStrategy struct {
+ branch string
+}
+
+// See documentation for NextRollStrategy interface.
+func (s *remoteHeadStrategy) GetNextRollRev(repo *git.Checkout, _ string) (string, error) {
+ output, err := repo.Git("ls-remote", UPSTREAM_REMOTE_NAME, fmt.Sprintf("refs/heads/%s", s.branch), "-1")
+ if err != nil {
+ return "", err
+ }
+ tokens := strings.Split(output, "\t")
+ return tokens[0], nil
+}
+
+// StrategyRemoteHead returns a NextRollStrategy which always rolls to HEAD of a
+// given branch, as defined by "git ls-remote".
+func StrategyRemoteHead(branch string) NextRollStrategy {
+ return &remoteHeadStrategy{
+ branch: branch,
+ }
+}
+
+// singleStrategy is a NextRollStrategy which rolls toward HEAD of a given branch, one
+// commit at a time.
+type singleStrategy struct {
+ *headStrategy
+}
+
+// See documentation for NextRollStrategy interface.
+func (s *singleStrategy) GetNextRollRev(repo *git.Checkout, lastRollRev string) (string, error) {
+ head, err := s.headStrategy.GetNextRollRev(repo, lastRollRev)
+ if err != nil {
+ return "", err
+ }
+ commits, err := repo.RevList(fmt.Sprintf("%s..%s", lastRollRev, head))
+ if err != nil {
+ return "", fmt.Errorf("Failed to list revisions: %s", err)
+ }
+ if len(commits) == 0 {
+ return lastRollRev, nil
+ } else {
+ return commits[len(commits)-1], nil
+ }
+}
+
+// StrategySingle returns a NextRollStrategy which rolls toward HEAD of a given branch,
+// one commit at a time.
+func StrategySingle(branch string) NextRollStrategy {
+ return &singleStrategy{StrategyHead(branch).(*headStrategy)}
+}
+
+// urlStrategy is a NextRollStrategy which rolls to a revision specified by a web
+// server.
+type urlStrategy struct {
+ parse func(string) (string, error)
+ url string
+}
+
+// See documentation for NextRollStrategy interface.
+func (s *urlStrategy) GetNextRollRev(_ *git.Checkout, _ string) (string, error) {
+ resp, err := http.Get(s.url)
+ if err != nil {
+ return "", err
+ }
+ defer util.Close(resp.Body)
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return "", err
+ }
+ return s.parse(string(body))
+}
+
+// StrategyURL returns a NextRollStrategy which rolls to a revision specified by a web
+// server.
+func StrategyURL(url string, parseFn func(string) (string, error)) NextRollStrategy {
+ return &urlStrategy{
+ parse: parseFn,
+ url: url,
+ }
+}
+
+// StrategyLKGR returns a NextRollStrategy which rolls to a Last Known Good Revision,
+// which is obtainable from a web server.
+func StrategyLKGR(url string) NextRollStrategy {
+ return StrategyURL(url, func(body string) (string, error) {
+ return strings.TrimSpace(body), nil
+ })
+}