blob: af3919faf39077ce735114d5758f6c1f0f85e9f5 [file] [log] [blame]
package parent
/*
Parent implementations which use a local checkout to create changes.
*/
import (
"context"
"os"
"path/filepath"
"strings"
"go.skia.org/infra/autoroll/go/codereview"
"go.skia.org/infra/autoroll/go/config"
"go.skia.org/infra/autoroll/go/config_vars"
"go.skia.org/infra/autoroll/go/repo_manager/common/git_common"
"go.skia.org/infra/autoroll/go/repo_manager/common/version_file_common"
"go.skia.org/infra/autoroll/go/revision"
"go.skia.org/infra/go/git"
"go.skia.org/infra/go/skerr"
"go.skia.org/infra/go/sklog"
)
const gitHeadRef = "HEAD"
// GitCheckoutParent is a base for implementations of Parent which use a local
// Git checkout.
type GitCheckoutParent struct {
*git_common.Checkout
childID string
createRoll git_common.CreateRollFunc
uploadRoll git_common.UploadRollFunc
}
// NewGitCheckout returns a base for implementations of Parent which use
// a local checkout to create changes.
func NewGitCheckout(ctx context.Context, c *config.GitCheckoutParentConfig, reg *config_vars.Registry, workdir string, cr codereview.CodeReview, co *git.Checkout, createRoll git_common.CreateRollFunc, uploadRoll git_common.UploadRollFunc) (*GitCheckoutParent, error) {
if err := c.Validate(); err != nil {
return nil, skerr.Wrap(err)
}
// Create the local checkout.
// TODO(borenet): Don't modify the passed-in config!
deps := make([]*config.VersionFileConfig, 0, len(c.Dep.Transitive)+1)
deps = append(deps, c.Dep.Primary)
for _, td := range c.Dep.Transitive {
deps = append(deps, td.Parent)
}
c.GitCheckout.Dependencies = deps
checkout, err := git_common.NewCheckout(ctx, c.GitCheckout, reg, workdir, cr.UserName(), cr.UserEmail(), co)
if err != nil {
return nil, skerr.Wrap(err)
}
return &GitCheckoutParent{
Checkout: checkout,
childID: c.Dep.Primary.Id,
createRoll: createRoll,
uploadRoll: uploadRoll,
}, nil
}
// Update implements Parent.
func (p *GitCheckoutParent) Update(ctx context.Context) (string, error) {
rev, _, err := p.Checkout.Update(ctx)
if err != nil {
return "", skerr.Wrap(err)
}
lastRollRev, ok := rev.Dependencies[p.childID]
if !ok {
return "", skerr.Fmt("Unable to find dependency %q in %#v", p.childID, rev)
}
return lastRollRev, nil
}
// CreateNewRoll implements Parent.
func (p *GitCheckoutParent) CreateNewRoll(ctx context.Context, from, to *revision.Revision, rolling []*revision.Revision, emails []string, dryRun bool, commitMsg string) (int64, error) {
return p.Checkout.CreateNewRoll(ctx, from, to, rolling, emails, dryRun, commitMsg, p.createRoll, p.uploadRoll)
}
// gitCheckoutFileCreateRollFunc returns a GitCheckoutCreateRollFunc which uses
// a local Git checkout and pins dependencies using a file checked into the
// repo.
func gitCheckoutFileCreateRollFunc(dep *config.DependencyConfig) git_common.CreateRollFunc {
return func(ctx context.Context, co *git.Checkout, from *revision.Revision, to *revision.Revision, rolling []*revision.Revision, commitMsg string) (string, error) {
// Determine what changes need to be made.
getFileWrapped := func(ctx context.Context, path string) (string, error) {
return getFile(ctx, co, path)
}
changes, err := version_file_common.UpdateDep(ctx, dep, to, getFileWrapped)
if err != nil {
return "", skerr.Wrap(err)
}
// Perform the changes.
for path, contents := range changes {
err := writeFile(ctx, co, path, contents)
if err != nil {
return "", skerr.Wrap(err)
}
}
// Commit.
if _, err := co.Git(ctx, "commit", "-m", commitMsg); err != nil {
return "", skerr.Wrap(err)
}
out, err := co.RevParse(ctx, gitHeadRef)
if err != nil {
return "", skerr.Wrap(err)
}
return strings.TrimSpace(out), nil
}
}
func getFile(ctx context.Context, co *git.Checkout, path string) (string, error) {
sklog.Infof("Getting file \"%s\".", path)
if ok, err := co.IsSubmodule(ctx, path, gitHeadRef); ok && err == nil {
sklog.Infof("File \"%s\" is submodule.", path)
return co.ReadSubmodule(ctx, path, gitHeadRef)
}
return co.GetFile(ctx, path, gitHeadRef)
}
func writeFile(ctx context.Context, co *git.Checkout, path, contents string) error {
if ok, _ := co.IsSubmodule(ctx, path, gitHeadRef); ok {
sklog.Infof("Writing to submodule \"%s\".", path)
return co.UpdateSubmodule(ctx, path, contents)
}
sklog.Infof("Writing to file \"%s\".", path)
fullPath := filepath.Join(co.Dir(), path)
if err := os.WriteFile(fullPath, []byte(contents), os.ModePerm); err != nil {
return skerr.Wrap(err)
}
if _, err := co.Git(ctx, "add", path); err != nil {
return skerr.Wrap(err)
}
return nil
}
var _ Parent = &GitCheckoutParent{}