1 # -*- coding: utf-8 -*-
2 # Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """Generate and upload tarballs for default apps cache.
8 Run inside the 'files' dir containing 'external_extensions.json' file:
9 $ chromite/bin/chrome_update_extension_cache --create --upload \\
10 chromeos-default-apps-1.0.0
12 Always increment the version when you update an existing package.
13 If no new files are added, increment the third version number.
15 If you change list of default extensions, increment the second version number.
18 Also you need to regenerate the Manifest with the new tarball digest.
19 Run inside the chroot:
20 $ ebuild chromeos-default-apps-1.0.0.ebuild manifest --force
23 from __future__ import print_function
28 import xml.dom.minidom
30 from chromite.lib import commandline
31 from chromite.lib import cros_build_lib
32 from chromite.lib import gs
33 from chromite.lib import osutils
36 UPLOAD_URL_BASE = 'gs://chromeos-localmirror-private/distfiles'
39 def DownloadCrx(ext, extension, crxdir):
40 """Download .crx file from WebStore and update entry."""
41 cros_build_lib.Info('Extension "%s"(%s)...', extension['name'], ext)
43 update_url = ('%s?x=prodversion%%3D35.1.1.1%%26id%%3D%s%%26uc' %
44 (extension['external_update_url'], ext))
45 response = urllib.urlopen(update_url)
46 if response.getcode() != 200:
47 cros_build_lib.Error('Cannot get update response, URL: %s, error: %d',
48 update_url, response.getcode())
51 dom = xml.dom.minidom.parse(response)
52 status = dom.getElementsByTagName('app')[0].getAttribute('status')
54 cros_build_lib.Error('Cannot fetch extension, status: %s', status)
57 node = dom.getElementsByTagName('updatecheck')[0]
58 url = node.getAttribute('codebase')
59 version = node.getAttribute('version')
60 filename = '%s-%s.crx' % (ext, version)
61 response = urllib.urlopen(url)
62 if response.getcode() != 200:
63 cros_build_lib.Error('Cannot download extension, URL: %s, error: %d',
64 url, response.getcode())
67 osutils.WriteFile(os.path.join(crxdir, 'extensions', filename),
70 # Keep external_update_url in json file, ExternalCache will take care about
71 # replacing it with proper external_crx path and version.
73 cros_build_lib.Info('Downloaded, current version %s', version)
77 def CreateValidationFiles(validationdir, crxdir, identifier):
78 """Create validationfiles for all extensions in |crxdir|."""
82 # Discover all extensions to be validated (but not JSON files).
83 for directory, _, filenames in os.walk(os.path.join(crxdir, 'extensions')):
85 # Make directory relative to output dir by removing crxdir and /.
86 for filename in filenames:
87 verified_files.append(os.path.join(directory[len(crxdir)+1:],
90 validation_file = os.path.join(validationdir, '%s.validation' % identifier)
92 osutils.SafeMakedirs(validationdir)
93 cros_build_lib.RunCommand(['sha256sum'] + verified_files,
94 log_stdout_to_file=validation_file,
95 cwd=crxdir, print_cmd=False)
96 cros_build_lib.Info('Hashes created.')
99 def CreateCacheTarball(extensions, outputdir, identifier, tarball):
100 """Cache |extensions| in |outputdir| and pack them in |tarball|."""
102 crxdir = os.path.join(outputdir, 'crx')
103 jsondir = os.path.join(outputdir, 'json')
104 validationdir = os.path.join(outputdir, 'validation')
106 osutils.SafeMakedirs(os.path.join(crxdir, 'extensions', 'managed_users'))
107 osutils.SafeMakedirs(os.path.join(jsondir, 'extensions', 'managed_users'))
109 for ext in extensions:
110 managed_users = extensions[ext].get('managed_users', 'no')
111 cache_crx = extensions[ext].get('cache_crx', 'yes')
113 # Remove fields that shouldn't be in the output file.
114 for key in ('cache_crx', 'managed_users'):
115 extensions[ext].pop(key, None)
117 if cache_crx == 'yes':
118 if not DownloadCrx(ext, extensions[ext], crxdir):
120 elif cache_crx == 'no':
123 cros_build_lib.Die('Unknown value for "cache_crx" %s for %s',
126 if managed_users == 'yes':
127 json_file = os.path.join(jsondir,
128 'extensions/managed_users/%s.json' % ext)
129 json.dump(extensions[ext],
130 open(json_file, 'w'),
133 separators=(',', ': '))
135 if managed_users != 'only':
136 json_file = os.path.join(jsondir, 'extensions/%s.json' % ext)
137 json.dump(extensions[ext],
138 open(json_file, 'w'),
141 separators=(',', ': '))
144 cros_build_lib.Die('FAIL to download some extensions')
146 CreateValidationFiles(validationdir, crxdir, identifier)
147 cros_build_lib.CreateTarball(tarball, outputdir)
148 cros_build_lib.Info('Tarball created %s', tarball)
152 parser = commandline.ArgumentParser(
153 '%%(prog)s [options] <version>\n\n%s' % __doc__, caching=True)
154 parser.add_argument('version', nargs=1)
155 parser.add_argument('--path', default=None, type='path',
156 help='Path of files dir with external_extensions.json')
157 parser.add_argument('--create', default=False, action='store_true',
158 help='Create cache tarball with specified name')
159 parser.add_argument('--upload', default=False, action='store_true',
160 help='Upload cache tarball with specified name')
161 options = parser.parse_args(argv)
164 os.chdir(options.path)
166 if not (options.create or options.upload):
167 cros_build_lib.Die('Need at least --create or --upload args')
169 if not os.path.exists('external_extensions.json'):
170 cros_build_lib.Die('No external_extensions.json in %s. Did you forget the '
171 '--path option?', os.getcwd())
173 identifier = options.version[0]
174 tarball = '%s.tar.xz' % identifier
176 extensions = json.load(open('external_extensions.json', 'r'))
177 with osutils.TempDir() as tempdir:
178 CreateCacheTarball(extensions, tempdir, identifier,
179 os.path.abspath(tarball))
183 url = os.path.join(UPLOAD_URL_BASE, tarball)
185 cros_build_lib.Die('This version already exists on Google Storage (%s)!\n'
186 'NEVER REWRITE EXISTING FILE. IT WILL BREAK CHROME OS '
188 ctx.Copy(os.path.abspath(tarball), url, acl='project-private')
189 cros_build_lib.Info('Tarball uploaded %s', url)
190 osutils.SafeUnlink(os.path.abspath(tarball))