in cmd/locktrigger/main.go [65:172]
func main() {
flag.Usage = usage
flag.Parse()
log.SetPrefix("locktrigger: ")
log.SetFlags(0)
if *project == "" || *build == "" || *repo == "" {
usage()
}
ctx := context.Background()
c, err := cloudbuild.NewClient(ctx)
if err != nil {
log.Fatal(err)
}
defer c.Close()
// Find commit hash of local Git
myHash := run("git", "rev-parse", "HEAD")
log.Printf("my hash: %v", myHash)
// Find build object for current build, check that it matches.
self := getBuild(c, ctx, *build)
if hash := self.Substitutions["COMMIT_SHA"]; hash != myHash {
log.Fatalf("build COMMIT_SHA does not match local hash: %v != %v", hash, myHash)
}
log.Printf("my build: %v", self.Id)
if self.BuildTriggerId == "" {
log.Fatalf("build has no trigger ID")
}
log.Printf("my trigger: %v", self.BuildTriggerId)
// List all builds for our trigger that are still running.
req := &cloudbuildpb.ListBuildsRequest{
ProjectId: *project,
// Note: Really want "status=WORKING buildTriggerId="+self.BuildTriggerId,
// but that fails with an InvalidArgument error for unknown reasons.
// status=WORKING will narrow the list down to something reasonable,
// and we filter the unrelated triggers below.
Filter: "status=WORKING",
}
it := c.ListBuilds(ctx, req)
foundSelf := false
shallow := false
if _, err := os.Stat(run("git", "rev-parse", "--git-dir") + "/shallow"); err == nil {
shallow = true
}
for {
b, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("reading builds: %v (%q)", err, req.Filter)
}
if b.BuildTriggerId != self.BuildTriggerId {
continue
}
// Check whether this build is an older or newer commit.
// If this build is older, cancel it.
// If this build is newer, cancel ourselves.
if b.Id == self.Id {
foundSelf = true
continue
}
hash := b.Substitutions["COMMIT_SHA"]
if hash == "" {
log.Fatalf("cannot find COMMIT_SHA for build %v", b.Id)
}
if hash == myHash {
log.Fatalf("found another build %v at same commit %v", b.Id, hash)
}
// Fetch the full Git repo so we can answer the history questions.
// This is delayed until now to avoid the expense of fetching the full repo
// if we are the only build that is running.
if shallow {
log.Printf("git fetch --unshallow")
run("git", "fetch", "--unshallow", *repo)
shallow = false
}
// Contention.
// Find the common ancestor between us and that build,
// to tell whether we're older, it's older, or we're unrelated.
log.Printf("checking %v", hash)
switch run("git", "merge-base", myHash, hash) {
default:
log.Fatalf("unexpected build for unrelated commit %v", hash)
case myHash:
// myHash is older than b's hash. Cancel self.
log.Printf("canceling self, for build %v commit %v", b.Id, hash)
cancel(c, ctx, self.Id)
case hash:
// b's hash is older than myHash. Cancel b.
log.Printf("canceling build %v commit %v", b.Id, hash)
cancel(c, ctx, b.Id)
}
}
// If we listed all the in-progress builds, we should have seen ourselves.
if !foundSelf {
log.Fatalf("reading builds: didn't find self")
}
}