103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#!/usr/bin/env python
203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved.
303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)# found in the LICENSE file.
503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)"""Script that attempts to push to a special git repository to verify that git
703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)credentials are configured correctly. It also verifies that gclient solution is
803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)configured to use git checkout.
903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
1003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)It will be added as gclient hook shortly before Chromium switches to git and
1103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)removed after the switch.
1203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
1303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)When running as hook in *.corp.google.com network it will also report status
1403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)of the push attempt to the server (on appengine), so that chrome-infra team can
1503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)collect information about misconfigured Git accounts.
1603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)"""
1703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
1803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import contextlib
1903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import datetime
2003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import errno
2103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import getpass
2203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import json
2303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import logging
2403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import netrc
2503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import optparse
2603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import os
2703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import pprint
2803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import shutil
2903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import socket
3003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import ssl
3103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import subprocess
3203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import sys
3303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import tempfile
3403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import time
3503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import urllib2
3603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)import urlparse
3703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
3803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
3903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)# Absolute path to src/ directory.
4003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
4103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
4203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)# Absolute path to a file with gclient solutions.
4303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)GCLIENT_CONFIG = os.path.join(os.path.dirname(REPO_ROOT), '.gclient')
4403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
4503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)# Incremented whenever some changes to scrip logic are made. Change in version
4603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)# will cause the check to be rerun on next gclient runhooks invocation.
4703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)CHECKER_VERSION = 1
4803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
4903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)# Do not attempt to upload a report after this date.
5003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)UPLOAD_DISABLE_TS = datetime.datetime(2014, 10, 1)
5103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
5203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)# URL to POST json with results to.
5303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)MOTHERSHIP_URL = (
5403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'https://chromium-git-access.appspot.com/'
5503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'git_access/api/v1/reports/access_check')
5603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
5703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)# Repository to push test commits to.
5803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)TEST_REPO_URL = 'https://chromium.googlesource.com/a/playground/access_test'
5903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
6003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)# Git-compatible gclient solution.
6103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)GOOD_GCLIENT_SOLUTION = {
6203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  'name': 'src',
631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  'deps_file': 'DEPS',
6403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  'managed': False,
6503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  'url': 'https://chromium.googlesource.com/chromium/src.git',
6603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
6703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
6803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)# Possible chunks of git push response in case .netrc is misconfigured.
6903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)BAD_ACL_ERRORS = (
7003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  '(prohibited by Gerrit)',
7103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  'does not match your user account',
7203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  'Git repository not found',
7303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  'Invalid user name or password',
7403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  'Please make sure you have the correct access rights',
7503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles))
7603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
7703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)# Git executable to call.
7803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)GIT_EXE = 'git.bat' if sys.platform == 'win32' else 'git'
7903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
8003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
8103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def is_on_bot():
8203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """True when running under buildbot."""
8303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return os.environ.get('CHROME_HEADLESS') == '1'
8403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
8503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
8603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def is_in_google_corp():
8703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """True when running in google corp network."""
8803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  try:
8903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return socket.getfqdn().endswith('.corp.google.com')
9003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  except socket.error:
9103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    logging.exception('Failed to get FQDN')
9203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return False
9303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
9403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
9503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def is_using_git():
9603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """True if git checkout is used."""
9703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return os.path.exists(os.path.join(REPO_ROOT, '.git', 'objects'))
9803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
9903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
10003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def is_using_svn():
10103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """True if svn checkout is used."""
10203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return os.path.exists(os.path.join(REPO_ROOT, '.svn'))
10303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
10403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
10503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def read_git_config(prop):
10603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """Reads git config property of src.git repo.
10703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
10803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  Returns empty string in case of errors.
10903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """
11003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  try:
11103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    proc = subprocess.Popen(
11203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        [GIT_EXE, 'config', prop], stdout=subprocess.PIPE, cwd=REPO_ROOT)
11303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    out, _ = proc.communicate()
11403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return out.strip()
11503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  except OSError as exc:
11603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    if exc.errno != errno.ENOENT:
11703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      logging.exception('Unexpected error when calling git')
11803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return ''
11903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
12003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
12103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def read_netrc_user(netrc_obj, host):
12203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """Reads 'user' field of a host entry in netrc.
12303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
12403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  Returns empty string if netrc is missing, or host is not there.
12503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """
12603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if not netrc_obj:
12703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return ''
12803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  entry = netrc_obj.authenticators(host)
12903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if not entry:
13003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return ''
13103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return entry[0]
13203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
13303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
13403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def get_git_version():
13503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """Returns version of git or None if git is not available."""
13603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  try:
13703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    proc = subprocess.Popen([GIT_EXE, '--version'], stdout=subprocess.PIPE)
13803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    out, _ = proc.communicate()
13903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return out.strip() if proc.returncode == 0 else ''
14003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  except OSError as exc:
14103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    if exc.errno != errno.ENOENT:
14203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      logging.exception('Unexpected error when calling git')
14303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return ''
14403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
14503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
14603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def read_gclient_solution():
14703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """Read information about 'src' gclient solution from .gclient file.
14803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
14903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  Returns tuple:
15003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    (url, deps_file, managed)
15103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    or
15203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    (None, None, None) if no such solution.
15303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """
15403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  try:
15503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    env = {}
15603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    execfile(GCLIENT_CONFIG, env, env)
15703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    for sol in (env.get('solutions') or []):
15803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      if sol.get('name') == 'src':
15903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        return sol.get('url'), sol.get('deps_file'), sol.get('managed')
16003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return None, None, None
16103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  except Exception:
16203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    logging.exception('Failed to read .gclient solution')
16303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return None, None, None
16403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
16503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
16603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def read_git_insteadof(host):
16703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """Reads relevant insteadOf config entries."""
16803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  try:
16903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    proc = subprocess.Popen([GIT_EXE, 'config', '-l'], stdout=subprocess.PIPE)
17003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    out, _ = proc.communicate()
17103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    lines = []
17203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    for line in out.strip().split('\n'):
17303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      line = line.lower()
17403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      if 'insteadof=' in line and host in line:
17503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        lines.append(line)
17603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return '\n'.join(lines)
17703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  except OSError as exc:
17803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    if exc.errno != errno.ENOENT:
17903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      logging.exception('Unexpected error when calling git')
18003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return ''
18103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
18203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
18303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def scan_configuration():
18403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """Scans local environment for git related configuration values."""
18503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # Git checkout?
18603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  is_git = is_using_git()
18703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
18803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # On Windows HOME should be set.
18903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if 'HOME' in os.environ:
19003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    netrc_path = os.path.join(
19103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        os.environ['HOME'],
19203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        '_netrc' if sys.platform.startswith('win') else '.netrc')
19303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  else:
19403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    netrc_path = None
19503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
19603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # Netrc exists?
19703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  is_using_netrc = netrc_path and os.path.exists(netrc_path)
19803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
19903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # Read it.
20003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  netrc_obj = None
20103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if is_using_netrc:
20203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    try:
20303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      netrc_obj = netrc.netrc(netrc_path)
20403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    except Exception:
20503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      logging.exception('Failed to read netrc from %s', netrc_path)
20603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      netrc_obj = None
20703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
20803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # Read gclient 'src' solution.
20903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  gclient_url, gclient_deps, gclient_managed = read_gclient_solution()
21003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
21103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return {
21203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'checker_version': CHECKER_VERSION,
21303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'is_git': is_git,
21403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'is_home_set': 'HOME' in os.environ,
21503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'is_using_netrc': is_using_netrc,
21603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'netrc_file_mode': os.stat(netrc_path).st_mode if is_using_netrc else 0,
21703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'git_version': get_git_version(),
21803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'platform': sys.platform,
21903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'username': getpass.getuser(),
22003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'git_user_email': read_git_config('user.email') if is_git else '',
22103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'git_user_name': read_git_config('user.name') if is_git else '',
22203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'git_insteadof': read_git_insteadof('chromium.googlesource.com'),
22303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'chromium_netrc_email':
22403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        read_netrc_user(netrc_obj, 'chromium.googlesource.com'),
22503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'chrome_internal_netrc_email':
22603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        read_netrc_user(netrc_obj, 'chrome-internal.googlesource.com'),
22703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'gclient_deps': gclient_deps,
22803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'gclient_managed': gclient_managed,
22903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'gclient_url': gclient_url,
23003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  }
23103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
23203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
23303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def last_configuration_path():
23403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """Path to store last checked configuration."""
23503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if is_using_git():
23603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return os.path.join(REPO_ROOT, '.git', 'check_git_push_access_conf.json')
23703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  elif is_using_svn():
23803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return os.path.join(REPO_ROOT, '.svn', 'check_git_push_access_conf.json')
23903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  else:
24003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return os.path.join(REPO_ROOT, '.check_git_push_access_conf.json')
24103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
24203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
24303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def read_last_configuration():
24403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """Reads last checked configuration if it exists."""
24503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  try:
24603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    with open(last_configuration_path(), 'r') as f:
24703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      return json.load(f)
24803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  except (IOError, ValueError):
24903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return None
25003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
25103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
25203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def write_last_configuration(conf):
25303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """Writes last checked configuration to a file."""
25403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  try:
25503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    with open(last_configuration_path(), 'w') as f:
25603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      json.dump(conf, f, indent=2, sort_keys=True)
25703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  except IOError:
25803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    logging.exception('Failed to write JSON to %s', path)
25903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
26003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
26103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)@contextlib.contextmanager
26203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def temp_directory():
26303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """Creates a temp directory, then nukes it."""
26403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  tmp = tempfile.mkdtemp()
26503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  try:
26603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    yield tmp
26703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  finally:
26803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    try:
26903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      shutil.rmtree(tmp)
27003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    except (OSError, IOError):
27103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      logging.exception('Failed to remove temp directory %s', tmp)
27203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
27303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
27403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)class Runner(object):
27503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """Runs a bunch of commands in some directory, collects logs from them."""
27603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
27703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  def __init__(self, cwd, verbose):
27803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    self.cwd = cwd
27903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    self.verbose = verbose
28003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    self.log = []
28103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
28203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  def run(self, cmd):
28303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    self.append_to_log('> ' + ' '.join(cmd))
28403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    retcode = -1
28503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    try:
28603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      proc = subprocess.Popen(
28703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          cmd,
28803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          stdout=subprocess.PIPE,
28903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          stderr=subprocess.STDOUT,
29003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          cwd=self.cwd)
29103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      out, _ = proc.communicate()
29203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      out = out.strip()
29303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      retcode = proc.returncode
29403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    except OSError as exc:
29503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      out = str(exc)
29603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    if retcode:
29703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      out += '\n(exit code: %d)' % retcode
29803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    self.append_to_log(out)
29903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return retcode
30003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
30103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  def append_to_log(self, text):
30203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    if text:
30303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      self.log.append(text)
30403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      if self.verbose:
30503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        logging.warning(text)
30603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
30703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
30803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def check_git_config(conf, report_url, verbose):
30903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """Attempts to push to a git repository, reports results to a server.
31003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
31103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  Returns True if the check finished without incidents (push itself may
31203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  have failed) and should NOT be retried on next invocation of the hook.
31303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """
31403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # Don't even try to push if netrc is not configured.
31503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if not conf['chromium_netrc_email']:
31603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return upload_report(
31703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        conf,
31803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        report_url,
31903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        verbose,
32003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        push_works=False,
32103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        push_log='',
32203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        push_duration_ms=0)
32303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
32403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # Ref to push to, each user has its own ref.
32503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  ref = 'refs/push-test/%s' % conf['chromium_netrc_email']
32603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
32703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  push_works = False
32803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  flake = False
32903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  started = time.time()
33003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  try:
33103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    logging.warning('Checking push access to the git repository...')
33203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    with temp_directory() as tmp:
33303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      # Prepare a simple commit on a new timeline.
33403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      runner = Runner(tmp, verbose)
33503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      runner.run([GIT_EXE, 'init', '.'])
33603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      if conf['git_user_name']:
33703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        runner.run([GIT_EXE, 'config', 'user.name', conf['git_user_name']])
33803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      if conf['git_user_email']:
33903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        runner.run([GIT_EXE, 'config', 'user.email', conf['git_user_email']])
34003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      with open(os.path.join(tmp, 'timestamp'), 'w') as f:
34103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        f.write(str(int(time.time() * 1000)))
34203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      runner.run([GIT_EXE, 'add', 'timestamp'])
34303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      runner.run([GIT_EXE, 'commit', '-m', 'Push test.'])
34403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      # Try to push multiple times if it fails due to issues other than ACLs.
34503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      attempt = 0
34603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      while attempt < 5:
34703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        attempt += 1
34803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        logging.info('Pushing to %s %s', TEST_REPO_URL, ref)
34903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        ret = runner.run(
35003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)            [GIT_EXE, 'push', TEST_REPO_URL, 'HEAD:%s' % ref, '-f'])
35103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        if not ret:
35203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          push_works = True
35303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          break
35403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        if any(x in runner.log[-1] for x in BAD_ACL_ERRORS):
35503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          push_works = False
35603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          break
35703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  except Exception:
35803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    logging.exception('Unexpected exception when pushing')
35903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    flake = True
36003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
36103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if push_works:
36203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    logging.warning('Git push works!')
36303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  else:
36403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    logging.warning(
36503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        'Git push doesn\'t work, which is fine if you are not a committer.')
36603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
36703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  uploaded = upload_report(
36803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      conf,
36903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      report_url,
37003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      verbose,
37103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      push_works=push_works,
37203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      push_log='\n'.join(runner.log),
37303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      push_duration_ms=int((time.time() - started) * 1000))
37403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return uploaded and not flake
37503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
37603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
37703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def check_gclient_config(conf):
37803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """Shows warning if gclient solution is not properly configured for git."""
37903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # Ignore configs that do not have 'src' solution at all.
38003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if not conf['gclient_url']:
38103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return
38203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  current = {
38303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'name': 'src',
3841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    'deps_file': conf['gclient_deps'] or 'DEPS',
38503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'managed': conf['gclient_managed'] or False,
38603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    'url': conf['gclient_url'],
38703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  }
3881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  # After depot_tools r291592 both DEPS and .DEPS.git are valid.
3891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  good = GOOD_GCLIENT_SOLUTION.copy()
3901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  good['deps_file'] = current['deps_file']
39103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if current == good:
39203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return
39303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # Show big warning if url or deps_file is wrong.
39403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if current['url'] != good['url'] or current['deps_file'] != good['deps_file']:
39503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print '-' * 80
39603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print 'Your gclient solution is not set to use supported git workflow!'
39703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print
39803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print 'Your \'src\' solution (in %s):' % GCLIENT_CONFIG
39903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print pprint.pformat(current, indent=2)
40003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print
40103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print 'Correct \'src\' solution to use git:'
40203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print pprint.pformat(good, indent=2)
40303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print
40403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print 'Please update your .gclient file ASAP.'
40503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print '-' * 80
40603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # Show smaller (additional) warning about managed workflow.
40703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if current['managed']:
40803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print '-' * 80
40903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print (
41003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        'You are using managed gclient mode with git, which was deprecated '
41103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        'on 8/22/13:')
41203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print (
41303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        'https://groups.google.com/a/chromium.org/'
41403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        'forum/#!topic/chromium-dev/n9N5N3JL2_U')
41503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print
41603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print (
41703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        'It is strongly advised to switch to unmanaged mode. For more '
41803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        'information about managed mode and reasons for its deprecation see:')
41903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print 'http://www.chromium.org/developers/how-tos/get-the-code#Managed_mode'
42003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print
42103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print (
42203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        'There\'s also a large suite of tools to assist managing git '
42303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        'checkouts.\nSee \'man depot_tools\' (or read '
42403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        'depot_tools/man/html/depot_tools.html).')
42503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print '-' * 80
42603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
42703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
42803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def upload_report(
42903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    conf, report_url, verbose, push_works, push_log, push_duration_ms):
43003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """Posts report to the server, returns True if server accepted it.
43103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
43203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  Uploads the report only if script is running in Google corp network. Otherwise
43303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  just prints the report.
43403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  """
43503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  report = conf.copy()
43603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  report.update(
43703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      push_works=push_works,
43803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      push_log=push_log,
43903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      push_duration_ms=push_duration_ms)
44003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
44103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  as_bytes = json.dumps({'access_check': report}, indent=2, sort_keys=True)
44203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if verbose:
44303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print 'Status of git push attempt:'
44403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    print as_bytes
44503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
44603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # Do not upload it outside of corp or if server side is already disabled.
44703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if not is_in_google_corp() or datetime.datetime.now() > UPLOAD_DISABLE_TS:
44803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    if verbose:
44903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      print (
45003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          'You can send the above report to chrome-git-migration@google.com '
45103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          'if you need help to set up you committer git account.')
45203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return True
45303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
45403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  req = urllib2.Request(
45503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      url=report_url,
45603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      data=as_bytes,
45703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      headers={'Content-Type': 'application/json; charset=utf-8'})
45803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
45903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  attempt = 0
46003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  success = False
46103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  while not success and attempt < 10:
46203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    attempt += 1
46303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    try:
46403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      logging.warning(
46503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          'Attempting to upload the report to %s...',
46603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          urlparse.urlparse(report_url).netloc)
46703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      resp = urllib2.urlopen(req, timeout=5)
46803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      report_id = None
46903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      try:
47003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        report_id = json.load(resp)['report_id']
47103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      except (ValueError, TypeError, KeyError):
47203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        pass
47303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      logging.warning('Report uploaded: %s', report_id)
47403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      success = True
47503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    except (urllib2.URLError, socket.error, ssl.SSLError) as exc:
47603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      logging.warning('Failed to upload the report: %s', exc)
47703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return success
47803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
47903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
48003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)def main(args):
48103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  parser = optparse.OptionParser(description=sys.modules[__name__].__doc__)
48203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  parser.add_option(
48303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      '--running-as-hook',
48403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      action='store_true',
48503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      help='Set when invoked from gclient hook')
48603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  parser.add_option(
48703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      '--report-url',
48803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      default=MOTHERSHIP_URL,
48903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      help='URL to submit the report to')
49003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  parser.add_option(
49103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      '--verbose',
49203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      action='store_true',
49303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      help='More logging')
49403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  options, args = parser.parse_args()
49503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if args:
49603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    parser.error('Unknown argument %s' % args)
49703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  logging.basicConfig(
49803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      format='%(message)s',
49903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      level=logging.INFO if options.verbose else logging.WARN)
50003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
50103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # When invoked not as a hook, always run the check.
50203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if not options.running_as_hook:
50303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    config = scan_configuration()
50403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    check_gclient_config(config)
50503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    check_git_config(config, options.report_url, True)
50603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return 0
50703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
50803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # Always do nothing on bots.
50903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if is_on_bot():
51003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return 0
51103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
51203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # Read current config, verify gclient solution looks correct.
51303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  config = scan_configuration()
51403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  check_gclient_config(config)
51503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
51603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # Do not attempt to push from non-google owned machines.
51703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if not is_in_google_corp():
51803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    logging.info('Skipping git push check: non *.corp.google.com machine.')
51903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return 0
52003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
52103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # Skip git push check if current configuration was already checked.
52203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if config == read_last_configuration():
52303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    logging.info('Check already performed, skipping.')
52403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return 0
52503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
52603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # Run the check. Mark configuration as checked only on success. Ignore any
52703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  # exceptions or errors. This check must not break gclient runhooks.
52803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  try:
52903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    ok = check_git_config(config, options.report_url, False)
53003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    if ok:
53103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      write_last_configuration(config)
53203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    else:
53303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      logging.warning('Check failed and will be retried on the next run')
53403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  except Exception:
53503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    logging.exception('Unexpected exception when performing git access check')
53603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return 0
53703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
53803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
53903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)if __name__ == '__main__':
54003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  sys.exit(main(sys.argv[1:]))
541