[infra] Add chrome_release_branch script

Bug: skia:8932
Change-Id: I258fd8f5262e5f87800b85f9bcbd0c4982ef1e3d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/208503
Commit-Queue: Eric Boren <borenet@google.com>
Reviewed-by: Ben Wagner aka dogben <benjaminwagner@google.com>
diff --git a/tools/chrome_release_branch b/tools/chrome_release_branch
new file mode 100755
index 0000000..8a6cffc
--- /dev/null
+++ b/tools/chrome_release_branch
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+#
+# Copyright 2019 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+base_dir=$(dirname "$0")
+
+PYTHONDONTWRITEBYTECODE=1 exec python -u "$base_dir/chrome_release_branch.py" "$@"
diff --git a/tools/chrome_release_branch.bat b/tools/chrome_release_branch.bat
new file mode 100644
index 0000000..1f61fe2
--- /dev/null
+++ b/tools/chrome_release_branch.bat
@@ -0,0 +1,7 @@
+@echo off
+:: Copyright 2019 Google Inc.
+::
+:: Use of this source code is governed by a BSD-style license that can be
+:: found in the LICENSE file.
+setlocal
+python -u "%~dp0\chrome_release_branch.py" %*
diff --git a/tools/chrome_release_branch.py b/tools/chrome_release_branch.py
new file mode 100644
index 0000000..7fba18b
--- /dev/null
+++ b/tools/chrome_release_branch.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+#
+# Copyright 2019 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+import subprocess
+import sys
+
+from infra import git
+from infra import go
+
+
+REFS_HEADS_PREFIX = 'refs/heads/'
+CHROME_REF_PREFIX = REFS_HEADS_PREFIX + 'chrome/m'
+SUPPORTED_CHROME_BRANCHES = 2  # Per infra policy; see skbug.com/8940
+
+
+def get_chrome_branches():
+  '''Return all Chrome milestone branches as tuples of (milestone, ref).'''
+  refs = git.git('ls-remote', 'origin', 'refs/heads/*')
+  chrome_branches = []
+  for line in refs.splitlines():
+    ref = line.split()[1]
+    if ref.startswith(CHROME_REF_PREFIX):
+      m = int(ref[len(CHROME_REF_PREFIX):])
+      chrome_branches.append((m, ref))
+  chrome_branches.sort(reverse=True)
+  return chrome_branches
+
+
+def main():
+  owner = git.git('config', 'user.email').rstrip()
+  if not owner:
+    print >> sys.stderr, 'No configured git user; please run "git config user.email <your email>".'
+    sys.exit(1)
+  branches = get_chrome_branches()
+  new_branch = branches[0][1][len(REFS_HEADS_PREFIX):]
+  old_branch = branches[SUPPORTED_CHROME_BRANCHES][1][len(REFS_HEADS_PREFIX):]
+  go.get(go.INFRA_GO+'/go/supported_branches/cmd/new-branch')
+  subprocess.check_call(['new-branch',
+                         '--branch', new_branch,
+                         '--delete', old_branch,
+                         '--owner', owner])
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/infra/__init__.py b/tools/infra/__init__.py
new file mode 100644
index 0000000..d5cf199
--- /dev/null
+++ b/tools/infra/__init__.py
@@ -0,0 +1,4 @@
+# Copyright 2019 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
diff --git a/tools/infra/git.py b/tools/infra/git.py
new file mode 100644
index 0000000..cc51d42
--- /dev/null
+++ b/tools/infra/git.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+#
+# Copyright 2019 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+import subprocess
+import sys
+
+
+GIT = 'git.bat' if sys.platform == 'win32' else 'git'
+
+
+def git(*args):
+  '''Run the given Git command, return the output.'''
+  return subprocess.check_output([GIT]+list(args))
diff --git a/tools/infra/go.py b/tools/infra/go.py
new file mode 100644
index 0000000..e30540e
--- /dev/null
+++ b/tools/infra/go.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+#
+# Copyright 2019 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+import os
+import subprocess
+import sys
+
+
+INFRA_GO = 'go.skia.org/infra'
+WHICH = 'where' if sys.platform == 'win32' else 'which'
+
+
+def check():
+  '''Verify that golang is properly installed. If not, exit with an error.'''
+  def _fail(msg):
+    print >> sys.stderr, msg
+    sys.exit(1)
+
+  try:
+    go_exe = subprocess.check_output([WHICH, 'go'])
+  except (subprocess.CalledProcessError, OSError):
+    pass
+  if not go_exe:
+    _fail('Unable to find Golang installation; see '
+          'https://golang.org/doc/install')
+  if not os.environ.get('GOPATH'):
+    _fail('GOPATH environment variable is not set; is Golang properly '
+          'installed?')
+  go_bin = os.path.join(os.environ['GOPATH'], 'bin')
+  for entry in os.environ.get('PATH', '').split(os.pathsep):
+    if entry == go_bin:
+      break
+  else:
+    _fail('%s not in PATH; is Golang properly installed?' % go_bin)
+
+
+def get(url):
+  '''Clone or update the given repo URL via "go get".'''
+  check()
+  subprocess.check_call(['go', 'get', '-u', url])
+
+
+def update_infra():
+  '''Update the local checkout of the Skia infra codebase.'''
+  get(INFRA_GO + '/...')