| package repo_manager |
| |
| import ( |
| "context" |
| "errors" |
| "fmt" |
| "net/http" |
| |
| "go.skia.org/infra/autoroll/go/strategy" |
| "go.skia.org/infra/go/gerrit" |
| "go.skia.org/infra/go/gitiles" |
| ) |
| |
| /* |
| Repo manager which does not use a local checkout. |
| |
| Use this repo manager as a helper but not directly. |
| */ |
| |
| // NoCheckoutRepoManagerConfig provides configuration for the noCheckoutRepoManager. |
| type NoCheckoutRepoManagerConfig struct { |
| CommonRepoManagerConfig |
| |
| // Gerrit project for the parent repo. |
| GerritProject string `json:"gerritProject"` |
| // URL of the parent repo. |
| ParentRepo string `json:"parentRepo"` |
| } |
| |
| func (c *NoCheckoutRepoManagerConfig) Validate() error { |
| if err := c.CommonRepoManagerConfig.Validate(); err != nil { |
| return err |
| } |
| if c.GerritProject == "" { |
| return errors.New("GerritProject is required.") |
| } |
| if c.ParentRepo == "" { |
| return errors.New("ParentRepo is required.") |
| } |
| if len(c.PreUploadSteps) > 0 { |
| return errors.New("Checkout-less rollers don't support pre-upload steps") |
| } |
| return nil |
| } |
| |
| // noCheckoutRepoManager is a RepoManager which rolls Android AFDO profile version |
| // numbers into Chromium. Unlike other rollers, there is no child repo to sync; |
| // the version number is obtained from Google Cloud Storage. |
| type noCheckoutRepoManager struct { |
| *commonRepoManager |
| baseCommit string |
| buildCommitMessage noCheckoutBuildCommitMessageFunc |
| gerritProject string |
| nextRollChanges map[string]string |
| parentRepo *gitiles.Repo |
| updateHelper noCheckoutUpdateHelperFunc |
| } |
| |
| // noCheckoutUpdateHelperFunc is a function called by noCheckoutRepoManager.Update() |
| // which returns the last roll revision, next roll revision, number of not-yet-rolled |
| // revisions, and a map of file names to contents indicating what should be changed |
| // in the next roll. The parameters are the parent repo and its base commit. |
| type noCheckoutUpdateHelperFunc func(context.Context, strategy.NextRollStrategy, *gitiles.Repo, string) (string, string, int, map[string]string, error) |
| |
| // noCheckoutBuildCommitMessageFunc is a function called by |
| // noCheckoutRepoManager.CreateNewRoll() which returns the commit message for |
| // a given roll given the previous roll revision, next roll revision, URL of the |
| // server, extra trybots for the CQ, and TBR emails. |
| type noCheckoutBuildCommitMessageFunc func(string, string, string, string, []string) (string, error) |
| |
| // Return a noCheckoutRepoManager instance. |
| func newNoCheckoutRepoManager(ctx context.Context, c NoCheckoutRepoManagerConfig, workdir string, g gerrit.GerritInterface, serverURL, gitcookiesPath string, client *http.Client, buildCommitMessage noCheckoutBuildCommitMessageFunc, updateHelper noCheckoutUpdateHelperFunc) (*noCheckoutRepoManager, error) { |
| if err := c.Validate(); err != nil { |
| return nil, err |
| } |
| crm, err := newCommonRepoManager(c.CommonRepoManagerConfig, workdir, serverURL, g) |
| if err != nil { |
| return nil, err |
| } |
| rv := &noCheckoutRepoManager{ |
| commonRepoManager: crm, |
| buildCommitMessage: buildCommitMessage, |
| gerritProject: c.GerritProject, |
| parentRepo: gitiles.NewRepo(c.ParentRepo, gitcookiesPath, client), |
| updateHelper: updateHelper, |
| } |
| return rv, nil |
| } |
| |
| // See documentation for RepoManager interface. |
| func (rm *noCheckoutRepoManager) CreateNewRoll(ctx context.Context, from, to string, emails []string, cqExtraTrybots string, dryRun bool) (int64, error) { |
| // Build the commit message. |
| commitMsg, err := rm.buildCommitMessage(from, to, rm.serverURL, cqExtraTrybots, emails) |
| if err != nil { |
| return 0, err |
| } |
| |
| rm.infoMtx.Lock() |
| defer rm.infoMtx.Unlock() |
| |
| // Create the change. |
| ci, err := gerrit.CreateAndEditChange(rm.g, rm.gerritProject, rm.parentBranch, commitMsg, rm.baseCommit, func(g gerrit.GerritInterface, ci *gerrit.ChangeInfo) error { |
| for file, contents := range rm.nextRollChanges { |
| if err := g.EditFile(ci, file, contents); err != nil { |
| return fmt.Errorf("Failed to edit %s file: %s", file, err) |
| } |
| } |
| return nil |
| }) |
| if err != nil { |
| if ci != nil { |
| if err2 := rm.g.Abandon(ci, "Failed to create roll CL"); err != nil { |
| return 0, fmt.Errorf("Failed to create roll with: %s\nAnd failed to abandon the change with: %s", err, err2) |
| } |
| } |
| return 0, err |
| } |
| |
| // Set the CQ bit as appropriate. |
| cq := gerrit.COMMITQUEUE_LABEL_SUBMIT |
| if dryRun { |
| cq = gerrit.COMMITQUEUE_LABEL_DRY_RUN |
| } |
| if err = rm.g.SetReview(ci, "", map[string]interface{}{ |
| gerrit.CODEREVIEW_LABEL: gerrit.CODEREVIEW_LABEL_APPROVE, |
| gerrit.COMMITQUEUE_LABEL: cq, |
| }, emails); err != nil { |
| // TODO(borenet): Should we try to abandon the CL? |
| return 0, err |
| } |
| |
| return ci.Issue, nil |
| } |
| |
| // See documentation for RepoManager interface. |
| func (rm *noCheckoutRepoManager) Update(ctx context.Context) error { |
| // Find HEAD of the desired parent branch. We make sure to provide the |
| // base commit of our change, to avoid clobbering other changes to the |
| // DEPS file. |
| baseCommit, err := rm.parentRepo.GetCommit(rm.parentBranch) |
| if err != nil { |
| return err |
| } |
| |
| // Get the next roll rev, and the list of versions in between the last |
| // and next rolls. |
| rm.strategyMtx.RLock() |
| defer rm.strategyMtx.RUnlock() |
| lastRollRev, nextRollRev, commitsNotRolled, nextRollChanges, err := rm.updateHelper(ctx, rm.strategy, rm.parentRepo, baseCommit.Hash) |
| if err != nil { |
| return err |
| } |
| |
| rm.infoMtx.Lock() |
| defer rm.infoMtx.Unlock() |
| rm.baseCommit = baseCommit.Hash |
| rm.lastRollRev = lastRollRev |
| rm.nextRollRev = nextRollRev |
| rm.commitsNotRolled = commitsNotRolled |
| rm.nextRollChanges = nextRollChanges |
| return nil |
| } |
| |
| // See documentation for RepoManager interface. |
| func (rm *noCheckoutRepoManager) FullChildHash(ctx context.Context, ver string) (string, error) { |
| return "", fmt.Errorf("NOT IMPLEMENTED") |
| } |
| |
| // See documentation for RepoManager interface. |
| func (rm *noCheckoutRepoManager) RolledPast(ctx context.Context, ver string) (bool, error) { |
| return false, fmt.Errorf("NOT IMPLEMENTED") |
| } |
| |
| // See documentation for RepoManager interface. |
| func (r *noCheckoutRepoManager) CreateNextRollStrategy(ctx context.Context, s string) (strategy.NextRollStrategy, error) { |
| return nil, fmt.Errorf("NOT IMPLEMENTED") |
| } |
| |
| // See documentation for RepoManager interface. |
| func (r *noCheckoutRepoManager) DefaultStrategy() string { |
| return "NOT IMPLEMENTED" |
| } |
| |
| // See documentation for RepoManager interface. |
| func (r *noCheckoutRepoManager) ValidStrategies() []string { |
| return []string{} // NOT IMPLEMENTED |
| } |