aboutsummaryrefslogtreecommitdiff
path: root/catapult/common/bin/update_chrome_reference_binaries
blob: e148c7477a66f382220821f957058f49759415cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#!/usr/bin/env python
#
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Updates the Chrome reference builds.

Usage:
  $ /path/to/update_reference_build.py
  $ git commit -a
  $ git cl upload
"""

import collections
import logging
import os
import shutil
import subprocess
import sys
import tempfile
import urllib2
import zipfile

sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'py_utils'))

from py_utils import cloud_storage
from dependency_manager import base_config


def BuildNotFoundError(error_string):
  raise ValueError(error_string)


_CHROME_BINARIES_CONFIG = os.path.join(
    os.path.dirname(os.path.abspath(__file__)), '..', '..', 'common',
    'py_utils', 'py_utils', 'chrome_binaries.json')

CHROME_GS_BUCKET = 'chrome-unsigned'


# Remove a platform name from this list to disable updating it.
# Add one to enable updating it. (Must also update _PLATFORM_MAP.)
_PLATFORMS_TO_UPDATE = ['mac_x86_64', 'win_x86', 'win_AMD64', 'linux_x86_64',
                        'android_k_armeabi-v7a', 'android_l_arm64-v8a',
                        'android_l_armeabi-v7a', 'android_n_armeabi-v7a',
                        'android_n_arm64-v8a']

# Remove a channel name from this list to disable updating it.
# Add one to enable updating it.
_CHANNELS_TO_UPDATE = ['stable', 'canary', 'dev']


# Omaha is Chrome's autoupdate server. It reports the current versions used
# by each platform on each channel.
_OMAHA_PLATFORMS = { 'stable':  ['mac', 'linux', 'win', 'android'],
                    'dev':  ['linux'], 'canary': ['mac', 'win']}


# All of the information we need to update each platform.
#   omaha: name omaha uses for the platforms.
#   zip_name: name of the zip file to be retrieved from cloud storage.
#   gs_build: name of the Chrome build platform used in cloud storage.
#   destination: Name of the folder to download the reference build to.
UpdateInfo = collections.namedtuple('UpdateInfo',
    'omaha, gs_folder, gs_build, zip_name')
_PLATFORM_MAP = {'mac_x86_64': UpdateInfo(omaha='mac',
                                          gs_folder='desktop-*',
                                          gs_build='mac64',
                                          zip_name='chrome-mac.zip'),
                 'win_x86': UpdateInfo(omaha='win',
                                       gs_folder='desktop-*',
                                       gs_build='win-clang',
                                       zip_name='chrome-win-clang.zip'),
                 'win_AMD64': UpdateInfo(omaha='win',
                                         gs_folder='desktop-*',
                                         gs_build='win64-clang',
                                         zip_name='chrome-win64-clang.zip'),
                 'linux_x86_64': UpdateInfo(omaha='linux',
                                            gs_folder='desktop-*',
                                            gs_build='linux64',
                                            zip_name='chrome-linux64.zip'),
                 'android_k_armeabi-v7a': UpdateInfo(omaha='android',
                                                     gs_folder='android-*',
                                                     gs_build='arm',
                                                     zip_name='Chrome.apk'),
                 'android_l_arm64-v8a': UpdateInfo(omaha='android',
                                                   gs_folder='android-*',
                                                   gs_build='arm_64',
                                                   zip_name='ChromeModern.apk'),
                 'android_l_armeabi-v7a': UpdateInfo(omaha='android',
                                                     gs_folder='android-*',
                                                     gs_build='arm',
                                                     zip_name='Chrome.apk'),
                 'android_n_armeabi-v7a': UpdateInfo(omaha='android',
                                                     gs_folder='android-*',
                                                     gs_build='arm',
                                                     zip_name='Monochrome.apk'),
                 'android_n_arm64-v8a': UpdateInfo(omaha='android',
                                                   gs_folder='android-*',
                                                   gs_build='arm_64',
                                                   zip_name='Monochrome.apk'),

}


def _ChannelVersionsMap(channel):
  rows = _OmahaReportVersionInfo(channel)
  omaha_versions_map = _OmahaVersionsMap(rows, channel)
  channel_versions_map = {}
  for platform in _PLATFORMS_TO_UPDATE:
    omaha_platform = _PLATFORM_MAP[platform].omaha
    if omaha_platform in omaha_versions_map:
      channel_versions_map[platform] = omaha_versions_map[omaha_platform]
  return channel_versions_map


def _OmahaReportVersionInfo(channel):
  url ='https://omahaproxy.appspot.com/all?channel=%s' % channel
  lines = urllib2.urlopen(url).readlines()
  return [l.split(',') for l in lines]


def _OmahaVersionsMap(rows, channel):
  platforms = _OMAHA_PLATFORMS.get(channel, [])
  if (len(rows) < 1 or
      not rows[0][0:3] == ['os', 'channel', 'current_version']):
    raise ValueError(
        'Omaha report is not in the expected form: %s.' % rows)
  versions_map = {}
  for row in rows[1:]:
    if row[1] != channel:
      raise ValueError(
          'Omaha report contains a line with the channel %s' % row[1])
    if row[0] in platforms:
      versions_map[row[0]] = row[2]
  logging.warn('versions map: %s' % versions_map)
  if not all(platform in versions_map for platform in platforms):
    raise ValueError(
        'Omaha report did not contain all desired platforms for channel %s' % channel)
  return versions_map


def _QueuePlatformUpdate(platform, version, config, channel):
  """ platform: the name of the platform for the browser to
      be downloaded & updated from cloud storage. """
  platform_info = _PLATFORM_MAP[platform]
  filename = platform_info.zip_name
  # remote_path example: desktop-*/30.0.1595.0/precise32/chrome-precise32.zip
  remote_path = '%s/%s/%s/%s' % (
      platform_info.gs_folder, version, platform_info.gs_build, filename)
  if not cloud_storage.Exists(CHROME_GS_BUCKET, remote_path):
    cloud_storage_path = 'gs://%s/%s' % (CHROME_GS_BUCKET, remote_path)
    raise BuildNotFoundError(
        'Failed to find %s build for version %s at path %s.' % (
            platform, version, cloud_storage_path))
  reference_builds_folder = os.path.join(
      os.path.dirname(os.path.abspath(__file__)), 'chrome_telemetry_build',
      'reference_builds', channel)
  if not os.path.exists(reference_builds_folder):
    os.makedirs(reference_builds_folder)
  local_dest_path = os.path.join(reference_builds_folder, filename)
  cloud_storage.Get(CHROME_GS_BUCKET, remote_path, local_dest_path)
  _ModifyBuildIfNeeded(local_dest_path, platform)
  config.AddCloudStorageDependencyUpdateJob(
      'chrome_%s' % channel, platform, local_dest_path, version=version,
      execute_job=False)


def _ModifyBuildIfNeeded(location, platform):
  """Hook to modify the build before saving it for Telemetry to use.

  This can be used to remove various utilities that cause noise in a
  test environment. Right now, it is just used to remove Keystone,
  which is a tool used to autoupdate Chrome.
  """
  if platform == 'mac_x86_64':
    _RemoveKeystoneFromBuild(location)
    return

  if 'mac' in platform:
    raise NotImplementedError(
        'Platform <%s> sounds like it is an OSX version. If so, we may need to '
        'remove Keystone from it per crbug.com/932615. Please edit this script'
        ' and teach it what needs to be done :).')


def _RemoveKeystoneFromBuild(location):
  """Removes the Keystone autoupdate binary from the chrome mac zipfile."""
  logging.info('Removing keystone from mac build at %s' % location)
  temp_folder = tempfile.mkdtemp(prefix='RemoveKeystoneFromBuild')
  try:
    subprocess.check_call(['unzip', '-q', location, '-d', temp_folder])
    keystone_folder = os.path.join(
        temp_folder, 'chrome-mac', 'Google Chrome.app', 'Contents',
        'Frameworks', 'Google Chrome Framework.framework', 'Frameworks',
        'KeystoneRegistration.framework')
    shutil.rmtree(keystone_folder)
    os.remove(location)
    subprocess.check_call(['zip', '--quiet', '--recurse-paths', '--symlinks',
                           location, 'chrome-mac'],
                           cwd=temp_folder)
  finally:
    shutil.rmtree(temp_folder)


def UpdateBuilds():
  config = base_config.BaseConfig(_CHROME_BINARIES_CONFIG, writable=True)
  for channel in _CHANNELS_TO_UPDATE:
    channel_versions_map = _ChannelVersionsMap(channel)
    for platform in channel_versions_map:
      print 'Downloading Chrome (%s channel) on %s' % (channel, platform)
      current_version = config.GetVersion('chrome_%s' % channel, platform)
      channel_version =  channel_versions_map.get(platform)
      print 'current: %s, channel: %s' % (current_version, channel_version)
      if current_version and current_version == channel_version:
        continue
      _QueuePlatformUpdate(platform, channel_version, config, channel)

  print 'Updating chrome builds with downloaded binaries'
  config.ExecuteUpdateJobs(force=True)


def main():
  logging.getLogger().setLevel(logging.DEBUG)
  UpdateBuilds()

if __name__ == '__main__':
  main()