OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2015 The Chromium Authors. All rights reserved. | 2 # Copyright 2015 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 ''' | 6 ''' |
7 Script to help uploading and downloading the Google Play services client | 7 Script to help uploading and downloading the Google Play services client |
8 library to and from a Google Cloud storage. | 8 library to and from a Google Cloud storage. |
9 ''' | 9 ''' |
10 | 10 |
(...skipping 19 matching lines...) Expand all Loading... | |
30 import download_from_google_storage | 30 import download_from_google_storage |
31 import upload_to_google_storage | 31 import upload_to_google_storage |
32 | 32 |
33 | 33 |
34 # Directory where the SHA1 files for the zip and the license are stored | 34 # Directory where the SHA1 files for the zip and the license are stored |
35 # It should be managed by git to provided information about new versions. | 35 # It should be managed by git to provided information about new versions. |
36 SHA1_DIRECTORY = os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'android', | 36 SHA1_DIRECTORY = os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'android', |
37 'play_services') | 37 'play_services') |
38 | 38 |
39 # Default bucket used for storing the files. | 39 # Default bucket used for storing the files. |
40 GMS_CLOUD_STORAGE = 'chrome-sdk-extras' | 40 GMS_CLOUD_STORAGE = 'chromium-android-tools/play-services' |
41 | 41 |
42 # Path to the default configuration file. It exposes the currently installed | 42 # Path to the default configuration file. It exposes the currently installed |
43 # version of the library in a human readable way. | 43 # version of the library in a human readable way. |
44 CONFIG_DEFAULT_PATH = os.path.join(constants.DIR_SOURCE_ROOT, 'build', | 44 CONFIG_DEFAULT_PATH = os.path.join(constants.DIR_SOURCE_ROOT, 'build', |
45 'android', 'play_services', 'config.json') | 45 'android', 'play_services', 'config.json') |
46 | 46 |
47 LICENSE_FILE_NAME = 'LICENSE' | 47 LICENSE_FILE_NAME = 'LICENSE' |
48 LIBRARY_FILE_NAME = 'google_play_services_library.zip' | 48 LIBRARY_FILE_NAME = 'google_play_services_library.zip' |
49 GMS_PACKAGE_ID = 'extra-google-google_play_services' # used by sdk manager | 49 GMS_PACKAGE_ID = 'extra-google-google_play_services' # used by sdk manager |
50 | 50 |
51 LICENSE_PATTERN = re.compile(r'^Pkg\.License=(?P<text>.*)$', re.MULTILINE) | 51 LICENSE_PATTERN = re.compile(r'^Pkg\.License=(?P<text>.*)$', re.MULTILINE) |
52 | 52 |
53 | 53 |
54 def Main(): | 54 def Main(raw_args): |
jbudorick
2015/11/04 16:56:32
nit: missed this before, but s/Main/main/
dgn
2015/11/04 18:43:58
Done.
| |
55 parser = argparse.ArgumentParser( | 55 parser = argparse.ArgumentParser( |
56 description=__doc__ + 'Please see the subcommand help for more details.', | 56 description=__doc__ + 'Please see the subcommand help for more details.', |
57 formatter_class=utils.DefaultsRawHelpFormatter) | 57 formatter_class=utils.DefaultsRawHelpFormatter) |
58 subparsers = parser.add_subparsers(title='commands') | 58 subparsers = parser.add_subparsers(title='commands') |
59 | 59 |
60 # Download arguments | 60 # Download arguments |
61 parser_download = subparsers.add_parser( | 61 parser_download = subparsers.add_parser( |
62 'download', | 62 'download', |
63 help='download the library from the cloud storage', | 63 help='download the library from the cloud storage', |
64 description=Download.__doc__, | 64 description=Download.__doc__, |
65 formatter_class=utils.DefaultsRawHelpFormatter) | 65 formatter_class=utils.DefaultsRawHelpFormatter) |
66 parser_download.add_argument('-f', '--force', | |
67 action='store_true', | |
68 help=('run even if the local version is ' | |
69 'already up to date')) | |
70 parser_download.set_defaults(func=Download) | 66 parser_download.set_defaults(func=Download) |
71 AddCommonArguments(parser_download) | 67 AddArguments(parser_download, bucket=True, config=True, dry_run=True, |
68 force=True, sdk_root=True, verbose=True) | |
jbudorick
2015/11/04 16:56:32
nit: indentation
dgn
2015/11/04 18:43:58
Done.
| |
72 | 69 |
73 # SDK Update arguments | 70 # SDK Update arguments |
74 parser_sdk = subparsers.add_parser( | 71 parser_sdk = subparsers.add_parser( |
75 'sdk', | 72 'sdk', |
76 help='update the local sdk using the Android SDK Manager', | 73 help='update the local sdk using the Android SDK Manager', |
77 description=UpdateSdk.__doc__, | 74 description=UpdateSdk.__doc__, |
78 formatter_class=utils.DefaultsRawHelpFormatter) | 75 formatter_class=utils.DefaultsRawHelpFormatter) |
79 parser_sdk.add_argument('--sdk-root', | |
80 help=('base path to the Android SDK tools to use to ' | |
81 'update the library'), | |
82 default=constants.ANDROID_SDK_ROOT) | |
83 parser_sdk.add_argument('-v', '--verbose', | |
84 action='store_true', | |
85 help='print debug information') | |
86 parser_sdk.set_defaults(func=UpdateSdk) | 76 parser_sdk.set_defaults(func=UpdateSdk) |
77 AddArguments(parser_sdk, verbose=True, sdk_root=True) | |
87 | 78 |
88 # Upload arguments | 79 # Upload arguments |
89 parser_upload = subparsers.add_parser( | 80 parser_upload = subparsers.add_parser( |
90 'upload', | 81 'upload', |
91 help='upload the library to the cloud storage', | 82 help='upload the library to the cloud storage', |
92 description=Upload.__doc__, | 83 description=Upload.__doc__, |
93 formatter_class=utils.DefaultsRawHelpFormatter) | 84 formatter_class=utils.DefaultsRawHelpFormatter) |
94 parser_upload.add_argument('-f', '--force', | 85 |
95 action='store_true', | |
96 help=('run even if the checked in version is ' | |
97 'already up to date')) | |
98 parser_upload.add_argument('--sdk-root', | |
99 help=('base path to the Android SDK tools to use ' | |
100 'to update the library'), | |
101 default=constants.ANDROID_SDK_ROOT) | |
102 parser_upload.add_argument('--skip-git', | 86 parser_upload.add_argument('--skip-git', |
103 action='store_true', | 87 action='store_true', |
104 help="don't commit the changes at the end") | 88 help="don't commit the changes at the end") |
105 parser_upload.set_defaults(func=Upload) | 89 parser_upload.set_defaults(func=Upload) |
106 AddCommonArguments(parser_upload) | 90 AddArguments(parser_upload, bucket=True, config=True, dry_run=True, |
91 force=True, sdk_root=True, verbose=True) | |
jbudorick
2015/11/04 16:56:32
nit: indentation
dgn
2015/11/04 18:43:58
Done.
| |
107 | 92 |
108 args = parser.parse_args() | 93 args = parser.parse_args(raw_args) |
109 if args.verbose: | 94 if args.verbose: |
110 logging.basicConfig(level=logging.DEBUG) | 95 logging.basicConfig(level=logging.DEBUG) |
111 logging_utils.ColorStreamHandler.MakeDefault() | 96 logging_utils.ColorStreamHandler.MakeDefault(not _IsBotEnvironment()) |
112 return args.func(args) | 97 return args.func(args) |
113 | 98 |
114 | 99 def AddArguments(parser, bucket=False, config=False, dry_run=False, |
jbudorick
2015/11/04 16:56:32
Split this into two functions without all of these
dgn
2015/11/04 18:43:58
Done.
| |
115 def AddCommonArguments(parser): | 100 force=False, sdk_root=False, verbose=False): |
jbudorick
2015/11/04 16:56:32
nit: indentation
dgn
2015/11/04 18:43:58
Done.
| |
116 ''' | 101 ''' |
117 Defines the common arguments on subparser rather than the main one. This | 102 Defines the common arguments on subparser rather than the main one. This |
118 allows to put arguments after the command: `foo.py upload --debug --force` | 103 allows to put arguments after the command: `foo.py upload --debug --force` |
119 instead of `foo.py --debug upload --force` | 104 instead of `foo.py --debug upload --force` |
120 ''' | 105 ''' |
121 | 106 |
122 parser.add_argument('--bucket', | 107 if bucket: |
123 help='name of the bucket where the files are stored', | 108 parser.add_argument('--bucket', |
124 default=GMS_CLOUD_STORAGE) | 109 help='name of the bucket where the files are stored', |
125 parser.add_argument('--config', | 110 default=GMS_CLOUD_STORAGE) |
126 help='JSON Configuration file', | 111 |
127 default=CONFIG_DEFAULT_PATH) | 112 if config: |
128 parser.add_argument('--dry-run', | 113 parser.add_argument('--config', |
129 action='store_true', | 114 help='JSON Configuration file', |
130 help=('run the script in dry run mode. Files will be ' | 115 default=CONFIG_DEFAULT_PATH) |
131 'copied to a local directory instead of the cloud ' | 116 |
132 'storage. The bucket name will be as path to that ' | 117 if dry_run: |
133 'directory relative to the repository root.')) | 118 parser.add_argument('--dry-run', |
134 parser.add_argument('-v', '--verbose', | 119 action='store_true', |
135 action='store_true', | 120 help=('run the script in dry run mode. Files will be ' |
136 help='print debug information') | 121 'copied to a local directory instead of the ' |
122 'cloud storage. The bucket name will be as path ' | |
123 'to that directory relative to the repository ' | |
124 'root.')) | |
125 | |
126 if force: | |
127 parser.add_argument('-f', '--force', | |
128 action='store_true', | |
129 help=('run even if the library is already ' | |
130 ' up to date')) | |
131 | |
132 if sdk_root: | |
133 parser.add_argument('--sdk-root', | |
134 help=('base path to the Android SDK tools to use when' | |
135 'updating the library'), | |
136 default=constants.ANDROID_SDK_ROOT) | |
137 | |
138 if verbose: | |
139 parser.add_argument('-v', '--verbose', | |
140 action='store_true', | |
141 help='print debug information') | |
137 | 142 |
138 | 143 |
139 def Download(args): | 144 def Download(args): |
140 ''' | 145 ''' |
141 Downloads the Google Play services client library from a Google Cloud Storage | 146 Downloads the Google Play services client library from a Google Cloud Storage |
142 bucket and installs it to | 147 bucket and installs it to |
143 //third_party/android_tools/sdk/extras/google/google_play_services. | 148 //third_party/android_tools/sdk/extras/google/google_play_services. |
144 | 149 |
145 A license check will be made, and the user might have to accept the license | 150 A license check will be made, and the user might have to accept the license |
146 if that has not been done before. | 151 if that has not been done before. |
147 ''' | 152 ''' |
148 | 153 |
149 paths = _InitPaths(constants.ANDROID_SDK_ROOT) | 154 if not os.path.isdir(args.sdk_root): |
155 logging.debug('Did not find the android sdk directory at "%s".', | |
156 args.sdk_root) | |
157 if not args.force: | |
158 logging.info('Skipping, not on an android checkout.') | |
159 return 0 | |
160 | |
161 paths = _InitPaths(args.sdk_root) | |
150 | 162 |
151 new_lib_zip_sha1 = os.path.join(SHA1_DIRECTORY, LIBRARY_FILE_NAME + '.sha1') | 163 new_lib_zip_sha1 = os.path.join(SHA1_DIRECTORY, LIBRARY_FILE_NAME + '.sha1') |
152 old_lib_zip_sha1 = os.path.join(paths.package, LIBRARY_FILE_NAME + '.sha1') | 164 old_lib_zip_sha1 = os.path.join(paths.package, LIBRARY_FILE_NAME + '.sha1') |
153 | 165 |
154 logging.debug('Comparing library hashes: %s and %s', new_lib_zip_sha1, | 166 logging.debug('Comparing library hashes: %s and %s', new_lib_zip_sha1, |
155 old_lib_zip_sha1) | 167 old_lib_zip_sha1) |
156 if utils.FileEquals(new_lib_zip_sha1, old_lib_zip_sha1) and not args.force: | 168 if utils.FileEquals(new_lib_zip_sha1, old_lib_zip_sha1) and not args.force: |
157 logging.debug('The Google Play services library is up to date.') | 169 logging.info('Skipping, the Google Play services library is up to date.') |
158 return 0 | 170 return 0 |
159 | 171 |
160 config = utils.ConfigParser(args.config) | 172 config = utils.ConfigParser(args.config) |
161 bucket_path = _VerifyBucketPathFormat(args.bucket, | 173 bucket_path = _VerifyBucketPathFormat(args.bucket, |
162 config.version_number, | 174 config.version_number, |
163 args.dry_run) | 175 args.dry_run) |
164 | 176 |
165 tmp_root = tempfile.mkdtemp() | 177 tmp_root = tempfile.mkdtemp() |
166 try: | 178 try: |
167 if not os.environ.get('CHROME_HEADLESS'): | 179 # setup the destination directory |
168 if not os.path.isdir(paths.package): | 180 if not os.path.isdir(paths.package): |
169 os.makedirs(paths.package) | 181 os.makedirs(paths.package) |
170 | 182 |
171 # download license file from bucket/{version_number}/license.sha1 | 183 # download license file from bucket/{version_number}/license.sha1 |
172 new_license = os.path.join(tmp_root, LICENSE_FILE_NAME) | 184 new_license = os.path.join(tmp_root, LICENSE_FILE_NAME) |
173 old_license = os.path.join(paths.package, LICENSE_FILE_NAME) | 185 old_license = os.path.join(paths.package, LICENSE_FILE_NAME) |
174 | 186 |
175 license_sha1 = os.path.join(SHA1_DIRECTORY, LICENSE_FILE_NAME + '.sha1') | 187 license_sha1 = os.path.join(SHA1_DIRECTORY, LICENSE_FILE_NAME + '.sha1') |
176 _DownloadFromBucket(bucket_path, license_sha1, new_license, | 188 _DownloadFromBucket(bucket_path, license_sha1, new_license, |
jbudorick
2015/11/04 16:56:32
This is now downloading the license on the bot whe
dgn
2015/11/04 18:43:58
Yes. It failed at some point where the paths were
| |
177 args.verbose, args.dry_run) | 189 args.verbose, args.dry_run) |
178 if not _CheckLicenseAgreement(new_license, old_license): | 190 |
191 if (not _IsBotEnvironment() and | |
192 not _CheckLicenseAgreement(new_license, old_license)): | |
179 logging.warning('Your version of the Google Play services library is ' | 193 logging.warning('Your version of the Google Play services library is ' |
180 'not up to date. You might run into issues building ' | 194 'not up to date. You might run into issues building ' |
181 'or running the app. Please run `%s download` to ' | 195 'or running the app. Please run `%s download` to ' |
182 'retry downloading it.', __file__) | 196 'retry downloading it.', __file__) |
183 return 0 | 197 return 0 |
184 | 198 |
185 new_lib_zip = os.path.join(tmp_root, LIBRARY_FILE_NAME) | 199 new_lib_zip = os.path.join(tmp_root, LIBRARY_FILE_NAME) |
186 _DownloadFromBucket(bucket_path, new_lib_zip_sha1, new_lib_zip, | 200 _DownloadFromBucket(bucket_path, new_lib_zip_sha1, new_lib_zip, |
187 args.verbose, args.dry_run) | 201 args.verbose, args.dry_run) |
188 | 202 |
189 # We remove only the library itself. Users having a SDK manager installed | 203 # We remove the library itself. Users having a SDK manager installed |
190 # library before will keep the documentation and samples from it. | 204 # library before will keep the documentation and samples from it. |
191 shutil.rmtree(paths.lib, ignore_errors=True) | 205 shutil.rmtree(paths.lib, ignore_errors=True) |
192 os.makedirs(paths.lib) | 206 os.makedirs(paths.lib) |
193 | 207 |
208 # We also remove source.properties so that the SDK manager doesn't fail | |
209 # recognizing that it didn't install the local version. | |
210 source_prop = os.path.join(paths.package, 'source.properties') | |
211 if os.path.isfile(source_prop): | |
212 os.remove(source_prop) | |
213 | |
194 logging.debug('Extracting the library to %s', paths.lib) | 214 logging.debug('Extracting the library to %s', paths.lib) |
195 with zipfile.ZipFile(new_lib_zip, "r") as new_lib_zip_file: | 215 with zipfile.ZipFile(new_lib_zip, "r") as new_lib_zip_file: |
196 new_lib_zip_file.extractall(paths.lib) | 216 new_lib_zip_file.extractall(paths.lib) |
197 | 217 |
198 logging.debug('Copying %s to %s', new_license, old_license) | 218 logging.debug('Copying %s to %s', new_license, old_license) |
199 shutil.copy(new_license, old_license) | 219 shutil.copy(new_license, old_license) |
200 | 220 |
201 logging.debug('Copying %s to %s', new_lib_zip_sha1, old_lib_zip_sha1) | 221 logging.debug('Copying %s to %s', new_lib_zip_sha1, old_lib_zip_sha1) |
202 shutil.copy(new_lib_zip_sha1, old_lib_zip_sha1) | 222 shutil.copy(new_lib_zip_sha1, old_lib_zip_sha1) |
203 | 223 |
224 logging.info('Update complete.') | |
204 finally: | 225 finally: |
205 shutil.rmtree(tmp_root) | 226 shutil.rmtree(tmp_root) |
206 | 227 |
207 return 0 | 228 return 0 |
208 | 229 |
209 | 230 |
210 def UpdateSdk(args): | 231 def UpdateSdk(args): |
211 ''' | 232 ''' |
212 Uses the Android SDK Manager to update or download the local Google Play | 233 Uses the Android SDK Manager to update or download the local Google Play |
213 services library. Its usual installation path is | 234 services library. Its usual installation path is |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
402 print license_part | 423 print license_part |
403 sys.stdout.flush() | 424 sys.stdout.flush() |
404 | 425 |
405 # Need to put the prompt on a separate line otherwise the gclient hook buffer | 426 # Need to put the prompt on a separate line otherwise the gclient hook buffer |
406 # only prints it after we received an input. | 427 # only prints it after we received an input. |
407 print 'Do you accept the license? [y/n]: ' | 428 print 'Do you accept the license? [y/n]: ' |
408 sys.stdout.flush() | 429 sys.stdout.flush() |
409 return raw_input('> ') in ('Y', 'y') | 430 return raw_input('> ') in ('Y', 'y') |
410 | 431 |
411 | 432 |
433 def _IsBotEnvironment(): | |
434 return bool(os.environ.get('CHROME_HEADLESS')) | |
435 | |
436 | |
412 def _VerifyBucketPathFormat(bucket_name, version_number, is_dry_run): | 437 def _VerifyBucketPathFormat(bucket_name, version_number, is_dry_run): |
413 ''' | 438 ''' |
414 Formats and checks the download/upload path depending on whether we are | 439 Formats and checks the download/upload path depending on whether we are |
415 running in dry run mode or not. Returns a supposedly safe path to use with | 440 running in dry run mode or not. Returns a supposedly safe path to use with |
416 Gsutil. | 441 Gsutil. |
417 ''' | 442 ''' |
418 | 443 |
419 if is_dry_run: | 444 if is_dry_run: |
420 bucket_path = os.path.abspath(os.path.join(bucket_name, | 445 bucket_path = os.path.abspath(os.path.join(bucket_name, |
421 str(version_number))) | 446 str(version_number))) |
(...skipping 25 matching lines...) Expand all Loading... | |
447 def call(self, *args): | 472 def call(self, *args): |
448 logging.debug('Calling command "%s"', str(args)) | 473 logging.debug('Calling command "%s"', str(args)) |
449 return cmd_helper.GetCmdStatusOutputAndError(args) | 474 return cmd_helper.GetCmdStatusOutputAndError(args) |
450 | 475 |
451 def check_call(self, *args): | 476 def check_call(self, *args): |
452 logging.debug('Calling command "%s"', str(args)) | 477 logging.debug('Calling command "%s"', str(args)) |
453 return cmd_helper.GetCmdStatusOutputAndError(args) | 478 return cmd_helper.GetCmdStatusOutputAndError(args) |
454 | 479 |
455 | 480 |
456 if __name__ == '__main__': | 481 if __name__ == '__main__': |
457 sys.exit(Main()) | 482 sys.exit(Main(sys.argv[1:])) |
jbudorick
2015/11/04 16:56:32
don't pass anything to Main, and don't pass anythi
dgn
2015/11/04 18:43:58
This is to allow providing arguments in the tests.
| |
OLD | NEW |