blob: 5dd1c84dcb366368a7bdd6d8532c8412fed53ee7 [file] [log] [blame]
#!/usr/bin/python
# Update Dartium DEPS automatically.
from datetime import datetime, timedelta
import optparse
import os
import re
from subprocess import Popen, PIPE
import sys
from time import strptime
# Instructions:
#
# To run locally:
# (a) Create and change to a directory to run the updater in:
# > mkdir /usr/local/google/home/$USER/dartium_deps_updater
# > cd /usr/local/google/home/$USER/dartium_deps_updater
#
# (b) Make a 'deps' directory to store temporary files:
# > mkdir deps
#
# (c) Checkout dart/tools/dartium (with this script):
# > svn co https://dart.googlecode.com/svn/branches/bleeding_edge/dart/tools/dartium dartium_tools
#
# (d) If your home directory is remote, consider redefining it for this shell/script:
# > cp -R $HOME/.subversion /usr/local/google/home/$USER
# > export HOME=/usr/local/google/home/$USER
#
# (e) Test by running (Ctrl-C to quit):
# > ./dartium_tools/update_deps.py
# > ./dartium_tools/update_deps.py --target=multivm
# > ./dartium_tools/update_deps.py --target=clank
# > ./dartium_tools/update_deps.py --target=integration
#
# (f) Run periodical update:
# > while true; do ./dartium_tools/update_deps.py --force ; sleep 300 ; done
########################################################################
# Repositories to auto-update
########################################################################
BRANCH_CURRENT="dart/dartium"
BRANCH_NEXT="dart/dartium"
BRANCH_MULTIVM="dart/multivm"
TARGETS = {
'dartium': (
'https://dart.googlecode.com/svn/branches/bleeding_edge/deps/dartium.deps',
'dartium',
['webkit', 'chromium'],
BRANCH_CURRENT,
),
'integration': (
'https://dart.googlecode.com/svn/branches/dartium_integration/deps/dartium.deps',
'dartium',
['webkit', 'chromium'],
BRANCH_NEXT,
),
'clank': (
'https://dart.googlecode.com/svn/branches/bleeding_edge/deps/clank.deps',
'dartium',
['webkit', 'chromium'],
BRANCH_CURRENT,
),
'multivm': (
'https://dart.googlecode.com/svn/branches/bleeding_edge/deps/multivm.deps',
'multivm',
['blink'],
BRANCH_MULTIVM,
),
}
# Each element in this map represents a repository to update. Entries
# take the form:
# (repo_tag: (svn_url, view_url))
#
# The repo_tag must match the DEPS revision entry. I.e, there must be
# an entry of the form:
# 'dartium_%s_revision' % repo_tag
# to roll forward.
#
# The view_url should be parameterized by revision number. This is
# used to generated the commit message.
REPOSITORY_INFO = {
'webkit': (
'http://src.chromium.org/blink/branches/%s',
'http://src.chromium.org/viewvc/blink?view=rev&revision=%s'),
'blink': (
'http://src.chromium.org/blink/branches/%s',
'http://src.chromium.org/viewvc/blink?view=rev&revision=%s'),
'chromium': (
'http://src.chromium.org/chrome/branches/%s',
'http://src.chromium.org/viewvc/chrome?view=rev&revision=%s'),
}
REPOSITORIES = REPOSITORY_INFO.keys()
########################################################################
# Actions
########################################################################
def write_file(filename, content):
f = open(filename, "w")
f.write(content)
f.close()
def run_cmd(cmd):
print "\n[%s]\n$ %s" % (os.getcwd(), " ".join(cmd))
pipe = Popen(cmd, stdout=PIPE, stderr=PIPE)
output = pipe.communicate()
if pipe.returncode == 0:
return output[0]
else:
print output[1]
print "FAILED. RET_CODE=%d" % pipe.returncode
sys.exit(pipe.returncode)
def parse_iso_time(s):
pair = s.rsplit(' ', 1)
d = datetime.strptime(pair[0], '%Y-%m-%d %H:%M:%S')
offset = timedelta(hours=int(pair[1][0:3]))
return d - offset
def parse_git_log(output, repo):
if len(output) < 4:
return []
lst = output.split(os.linesep)
lst = [s.strip('\'') for s in lst]
lst = [s.split(',', 3) for s in lst]
lst = [{'repo': repo,
'rev': s[0],
'isotime':s[1],
'author': s[2],
'utctime': parse_iso_time(s[1]),
'info': s[3]} for s in lst]
return lst
def parse_svn_log(output, repo):
lst = output.split(os.linesep)
lst = [s.strip('\'') for s in lst]
output = '_LINESEP_'.join(lst)
lst = output.split('------------------------------------------------------------------------')
lst = [s.replace('_LINESEP_', '\n') for s in lst]
lst = [s.strip('\n') for s in lst]
lst = [s.strip(' ') for s in lst]
lst = [s for s in lst if len(s) > 0]
pattern = re.compile(' \| (\d+) line(s|)')
lst = [pattern.sub(' | ', s) for s in lst]
lst = [s.split(' | ', 3) for s in lst]
lst = [{'repo': repo,
'rev': s[0].replace('r', ''),
'author': s[1],
'isotime':s[2][0:25],
'utctime': parse_iso_time(s[2][0:25]),
'info': s[3].split('\n')[2]} for s in lst]
return lst
def commit_url(repo, rev):
numrev = rev.replace('r', '')
if repo in REPOSITORIES:
(_, view_url) = REPOSITORY_INFO[repo]
return view_url % numrev
else:
raise Exception('Unknown repo');
def find_max(revs):
max_time = None
max_position = None
for i, rev in enumerate(revs):
if rev == []:
continue
if max_time is None or rev[0]['utctime'] > max_time:
max_time = rev[0]['utctime']
max_position = i
return max_position
def merge_revs(revs):
position = find_max(revs)
if position is None:
return []
item = revs[position][0]
revs[position] = revs[position][1:]
return [item] + merge_revs(revs)
def main():
option_parser = optparse.OptionParser()
option_parser.add_option('', '--target', help="Update one of [dartium|integration|multivm|clank]", action="store", dest="target", default="dartium")
option_parser.add_option('', '--force', help="Push DEPS update to server without prompting", action="store_true", dest="force")
options, args = option_parser.parse_args()
target = options.target
if not target in TARGETS.keys():
print "Error: invalid target"
print "Choose one of " + str(TARGETS)
(deps_dir, prefix, repos, branch) = TARGETS[target]
deps_file = deps_dir + '/DEPS'
src_dir = "/usr/local/google/home/%s/dartium_deps_updater/deps/%s" % (os.environ["USER"], target)
os.putenv("GIT_PAGER", "")
if not os.path.exists(src_dir):
print run_cmd(['svn', 'co', deps_dir, src_dir])
os.chdir(src_dir)
# parse DEPS
deps = run_cmd(['svn', 'cat', deps_file])
rev_num = {}
for repo in repos:
revision = '%s_%s_revision":\s*"(.+)"' % (prefix, repo)
rev_num[repo] = re.search(revision, deps).group(1)
# update repos
all_revs = []
for repo in repos:
(svn_url, _) = REPOSITORY_INFO[repo]
output = run_cmd(["svn", "log", "-r", "HEAD:%s" % rev_num[repo], svn_url % branch])
revs = parse_svn_log(output, repo)
if revs and revs[-1]['rev'] == rev_num[repo]:
revs.pop()
all_revs.append(revs)
pending_updates = merge_revs(all_revs)
pending_updates.reverse()
print
print "Current DEPS revisions:"
for repo in repos:
print ' %s_%s_revision=%s' % (prefix, repo, rev_num[repo])
if len(pending_updates) == 0:
print "DEPS is up-to-date."
sys.exit(0)
else:
print "Pending DEPS updates:"
for s in pending_updates:
print " %s to %s (%s) %s" % (s['repo'], s['rev'], s['isotime'], s['info'])
# make the next DEPS update
os.chdir(src_dir)
run_cmd(['rm', 'DEPS'])
print run_cmd(['svn', 'update'])
s = pending_updates[0]
pattern = re.compile(prefix + '_' + s['repo'] + '_revision":\s*"(.+)"')
new_deps = pattern.sub(prefix + '_' + s['repo'] + '_revision": "' + s['rev'] + '"', deps)
write_file('DEPS', new_deps)
commit_log = 'DEPS AutoUpdate: %s to %s (%s) %s\n' % (s['repo'], s['rev'], s['isotime'], s['author'])
commit_log += s['info'] + '\n' + commit_url(s['repo'], s['rev'])
write_file('commit_log.txt', commit_log)
print run_cmd(['svn', 'diff'])
print
print "Commit log:"
print "---------------------------------------------"
print commit_log
print "---------------------------------------------"
if not options.force:
print "Ready to push; press Enter to continue or Control-C to abort..."
sys.stdin.readline()
print run_cmd(['svn', 'commit', '--file', 'commit_log.txt'])
print "Done."
if '__main__' == __name__:
main()