blob: 6399855f75c7f385257b53f9184ffcd7b1186d16 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"fmt"
"os"
"presubmit"
"presubmit/common"
"fuchsia.googlesource.com/jiri"
"fuchsia.googlesource.com/jiri/cmdline"
"fuchsia.googlesource.com/jiri/gerrit"
"fuchsia.googlesource.com/jiri/gitutil"
"fuchsia.googlesource.com/jiri/project"
"fuchsia.googlesource.com/jiri/runutil"
)
const presubmitBranchName string = "underscore-presubmit"
var (
refsToTest string
cleanOldBranches bool
)
func init() {
flag.StringVar(&refsToTest, "cl", "", "Comma-separated list of change/patchset. Example: 1153/2,1150/1")
flag.BoolVar(&cleanOldBranches, "clean", true, "Whether to remove all presubmit branches before patching")
}
// readJiriManifest reads the jiri manifest found in JIRI_ROOT.
func readJiriManifest() (project.Projects, error) {
jirix, err := jiri.NewX(cmdline.EnvFromOS())
if err != nil {
return nil, err
}
projects, _, err := project.LoadManifest(jirix)
if err != nil {
return nil, err
}
return projects, nil
}
func getSequence() runutil.Sequence {
return runutil.NewSequence(nil, os.Stdin, os.Stdout, os.Stderr, false, false)
}
// cleanAllProjects deletes any presubmit branches that already exist. We don't run
// this after patchProject (e.g. in a defer) because unlike v23's system, our `patch`
// tool is separate and standalone. We run this _before_ we do any patching.
//
// This approach also has the nice side effect of ensuring there are no lingering
// changes in unrelated projects, no matter what the result of previous tests.
func cleanAllProjects(allProjects project.Projects) error {
cwd, err := os.Getwd()
if err != nil {
return err
}
defer os.Chdir(cwd) // Unnecessary since we expect to quit on error, but whatev.
git := gitutil.New(getSequence())
for _, project := range allProjects {
err = os.Chdir(project.Path)
if err != nil {
return err
}
if git.BranchExists(presubmitBranchName) {
// Make sure we're not on the presubmit branch (else the delete will fail).
err = git.CheckoutBranch("origin/master")
if err != nil {
return err
}
// Delete the presubmit branch.
err = git.DeleteBranch(presubmitBranchName, gitutil.ForceOpt(true))
if err != nil {
return err
}
}
// Move back to original directory in case paths are defined relatively.
err = os.Chdir(cwd)
if err != nil {
return err
}
}
return nil
}
// patchProject changes directory into the project directory, checks out the given
// change, then cds back to the original directory.
func patchProject(jiriProject project.Project, cl gerrit.Change) error {
cwd, err := os.Getwd()
if err != nil {
return err
}
defer os.Chdir(cwd)
err = os.Chdir(jiriProject.Path)
if err != nil {
return err
}
git := gitutil.New(getSequence())
err = git.CreateAndCheckoutBranch(presubmitBranchName)
if err != nil {
return err
}
// If the pull fails, it's likely because of a merge conflict.
err = git.Pull(jiriProject.Remote, cl.Reference())
if err != nil {
return err
}
return nil
}
// quitOnError is a convenience function for printing an error and exiting the program.
func quitOnError(e error) {
if e != nil {
fmt.Fprintf(os.Stderr, "%v\n", e)
os.Exit(1)
}
}
func main() {
flag.Parse()
g, err := presubmit.CreateGerrit()
quitOnError(err)
// Construct the list of changes we're going to test.
cls := []gerrit.Change{}
refs, err := common.ParseRefArg(refsToTest)
quitOnError(err)
for _, ref := range refs {
cl, err := g.GetChange(ref.Changelist)
quitOnError(err)
foundCl, foundPs, err := gerrit.ParseRefString(cl.Reference())
quitOnError(err)
// Abandon the test if we were given outdated patchsets.
if foundPs != ref.Patchset {
quitOnError(fmt.Errorf("%q is outdated; there's a newer patchset (%d/%d)\n",
ref, foundCl, foundPs))
}
fmt.Printf("Found patch: %s, %s\n", cl.Project, cl.Reference())
cls = append(cls, *cl)
}
// Read the project manifest. We need this information to know which directories and remotes
// map to the project names that come back from gerrit.
projects, err := readJiriManifest()
quitOnError(err)
// Clean all projects of any previous test branches.
if cleanOldBranches {
err = cleanAllProjects(projects)
quitOnError(err)
}
// Patch the projects that changed.
for _, cl := range cls {
// TODO(lanechr): remove the implicit assumption here that gerrit repo name == jiri project name.
localProject, err := projects.FindUnique(cl.Project)
quitOnError(err)
err = patchProject(localProject, cl)
quitOnError(err)
}
}