blob: 66282cc7f30ee6cc0cdaa79b93ce82876082de26 [file] [log] [blame]
#!/usr/bin/python2
"""Syncs to a given Cobalt build id.
Syncs current gclient instance to a given build id, as
generated by "build_id.py" and stored on carbon-airlock-95823.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import argparse
import json
import os
import subprocess
import sys
import requests
_BUILD_ID_QUERY_URL = (
"https://carbon-airlock-95823.appspot.com/build_version/search")
_BUILD_ID_QUERY_PARAMETER_NAME = "build_number"
def _GetGClientRoot(repo_path):
"""Finds the root of this gclient repo, or returns repo_path if failed."""
current_path = os.path.normpath(repo_path)
while not os.access(os.path.join(current_path, ".gclient"), os.R_OK):
next_path = os.path.dirname(current_path)
if next_path == current_path:
return repo_path
current_path = next_path
return current_path
class SubprocessFailedException(Exception):
"""Exception for non-zero subprocess exits."""
def __init__(self, command):
super(SubprocessFailedException, self).__init__()
self.command = command
def __str__(self):
return "Subprocess failed '{0}'".format(self.command)
def _RunGitCommand(gitargs, **kwargs):
"""Runs a git command with "gitargs", returning the output splitlines().
Args:
gitargs: Commandline args that follow 'git'.
**kwargs: Keyword args for Popen.
Returns:
All of stdout, as an array of lines.
Raises:
SubprocessFailedException: if the exit code is nonzero.
"""
result_tuple = _RunGitCommandReturnExitCode(gitargs, **kwargs)
if result_tuple[0] != 0:
raise SubprocessFailedException(" ".join(["git"] + gitargs))
return result_tuple[1]
def _RunGitCommandReturnExitCode(gitargs, **kwargs):
"""Runs a git command with "gitargs", returning the exit code and output.
Args:
gitargs: Commandline args that follow 'git'.
**kwargs: Keyword args for Popen.
Returns:
Tuple of (exit code, all of stdout as an array of lines).
"""
popen_args = ["git"] + gitargs
p = subprocess.Popen(popen_args, stdout=subprocess.PIPE, **kwargs)
output = p.stdout.read().splitlines()
return p.wait(), output
def main():
dev_null = open("/dev/null", "w")
arg_parser = argparse.ArgumentParser(
description="Syncs to a given Cobalt build id")
arg_parser.add_argument("buildid", nargs=1)
args = arg_parser.parse_args()
r = requests.get(
_BUILD_ID_QUERY_URL,
params={
_BUILD_ID_QUERY_PARAMETER_NAME: args.buildid[0]
})
if not r.ok:
print(
"HTTP request failed\n{0} {1}\n{2}".format(r.status_code, r.reason,
r.text),
file=sys.stderr)
return 1
# The response starts with a security-related close expression line
outer_json = json.loads(r.text.splitlines()[1])
hashes = json.loads(outer_json["deps"])
gclient_root = _GetGClientRoot(os.getcwd())
for relpath, rep_hash in hashes.iteritems():
path = os.path.join(gclient_root, relpath)
if not os.path.exists(path):
# No warning in this case, we will attempt to clone the repository in
# the next pass through the repos.
continue
is_dirty = (
bool(
_RunGitCommandReturnExitCode(
["diff", "--no-ext-diff", "--quiet"], cwd=path,
stderr=dev_null)[0]) or bool(
_RunGitCommandReturnExitCode(
["diff", "--no-ext-diff", "--quiet", "--cached"],
cwd=path,
stderr=dev_null)[0]))
if is_dirty:
print("{0} is dirty, please resolve".format(relpath))
return 1
for relpath, rep_hash in hashes.iteritems():
path = os.path.join(gclient_root, relpath)
# repo_hash has a repo path prefix like this:
# 'https://chromium.googlesource.com/chromium/llvm-project/libcxx.git
# @48198f9110397fff47fe7c37cbfa296be7d44d3d'
(requested_repo, requested_hash) = rep_hash.split("@")
if not os.path.exists(path):
print("Missing path {0}, cloning from {1}.".format(path, requested_repo))
try:
# The clone command will create all missing directories leading to the
# path. If the clone is successful, we continue on as usual and let
# the subsequent logic here checkout the appropriate git hash.
_RunGitCommand(["clone", "-q", requested_repo, path])
except SubprocessFailedException:
print("There was an error cloning the repository.")
continue
current_hash = _RunGitCommand(["rev-parse", "HEAD"], cwd=path)[0]
if requested_hash == current_hash:
continue
symbolic_ref = None
try:
symbolic_ref = _RunGitCommand(
["symbolic-ref", "--short", "-q", "HEAD"], cwd=path,
stderr=dev_null)[0]
except SubprocessFailedException:
pass
user_visible_commit = symbolic_ref if symbolic_ref else current_hash[0:7]
print("{0} was at {1} now {2}".format(path, user_visible_commit,
requested_hash[0:7]))
_RunGitCommand(["checkout", "-q", "--detach", requested_hash], cwd=path)
return 0
if __name__ == "__main__":
try:
sys.exit(main())
except SubprocessFailedException as ex:
print(str(ex), file=sys.stderr)
sys.exit(1)