blob: 1ac21b42806579000e227a53260af9317e579cb7 [file] [log] [blame]
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +02001#!/usr/bin/env python
2
3# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
4#
5# Use of this source code is governed by a BSD-style license
6# that can be found in the LICENSE file in the root of the source
7# tree. An additional intellectual property rights grant can be found
8# in the file PATENTS. All contributing project authors may
9# be found in the AUTHORS file in the root of the source tree.
10
11"""Script for publishing WebRTC AAR on Bintray.
12
13Set BINTRAY_USER and BINTRAY_API_KEY environment variables before running
14this script for authentication.
15"""
16
17import argparse
Sami Kalliomäki201509b2017-10-09 13:44:25 +020018import json
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +020019import logging
20import os
21import re
22import shutil
23import subprocess
24import sys
25import tempfile
26import time
27
28
29SCRIPT_DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
30CHECKOUT_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, os.pardir, os.pardir))
31
32sys.path.append(os.path.join(CHECKOUT_ROOT, 'third_party'))
33import requests
34import jinja2
35
36sys.path.append(os.path.join(CHECKOUT_ROOT, 'tools_webrtc'))
37from android.build_aar import BuildAar
38
39
40ARCHS = ['armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64']
Sami Kalliomäki201509b2017-10-09 13:44:25 +020041MAVEN_REPOSITORY = 'https://google.bintray.com/webrtc'
42API = 'https://api.bintray.com'
43PACKAGE_PATH = 'google/webrtc/google-webrtc'
44CONTENT_API = API + '/content/' + PACKAGE_PATH
45PACKAGES_API = API + '/packages/' + PACKAGE_PATH
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +020046GROUP_ID = 'org/webrtc'
47ARTIFACT_ID = 'google-webrtc'
48COMMIT_POSITION_REGEX = r'^Cr-Commit-Position: refs/heads/master@{#(\d+)}$'
Sami Kalliomäki201509b2017-10-09 13:44:25 +020049API_TIMEOUT_SECONDS = 10.0
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +020050UPLOAD_TRIES = 3
51# The sleep time is increased exponentially.
52UPLOAD_RETRY_BASE_SLEEP_SECONDS = 2.0
Sami Kalliomäki201509b2017-10-09 13:44:25 +020053GRADLEW_BIN = os.path.join(CHECKOUT_ROOT,
54 'examples/androidtests/third_party/gradle/gradlew')
Sami Kalliomäki91e26fd2018-01-22 15:08:24 +010055ADB_BIN = os.path.join(CHECKOUT_ROOT,
Oleksandr Iakovenko53de7252019-03-20 13:45:31 +010056 'third_party/android_sdk/public/platform-tools/adb')
Sami Kalliomäki201509b2017-10-09 13:44:25 +020057AAR_PROJECT_DIR = os.path.join(CHECKOUT_ROOT, 'examples/aarproject')
58AAR_PROJECT_GRADLE = os.path.join(AAR_PROJECT_DIR, 'build.gradle')
59AAR_PROJECT_APP_GRADLE = os.path.join(AAR_PROJECT_DIR, 'app', 'build.gradle')
60AAR_PROJECT_DEPENDENCY = "implementation 'org.webrtc:google-webrtc:1.0.+'"
61AAR_PROJECT_VERSION_DEPENDENCY = "implementation 'org.webrtc:google-webrtc:%s'"
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +020062
63
64def _ParseArgs():
65 parser = argparse.ArgumentParser(description='Releases WebRTC on Bintray.')
66 parser.add_argument('--use-goma', action='store_true', default=False,
67 help='Use goma.')
Sami Kalliomäki201509b2017-10-09 13:44:25 +020068 parser.add_argument('--skip-tests', action='store_true', default=False,
69 help='Skips running the tests.')
70 parser.add_argument('--publish', action='store_true', default=False,
71 help='Automatically publishes the library if the tests pass.')
Sami Kalliomäki002e7102018-03-29 11:18:13 +020072 parser.add_argument('--build-dir', default=None,
73 help='Temporary directory to store the build files. If not specified, '
74 'a new directory will be created.')
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +020075 parser.add_argument('--verbose', action='store_true', default=False,
76 help='Debug logging.')
77 return parser.parse_args()
78
79
80def _GetCommitHash():
81 commit_hash = subprocess.check_output(
82 ['git', 'rev-parse', 'HEAD'], cwd=CHECKOUT_ROOT).strip()
83 return commit_hash
84
85
86def _GetCommitPos():
87 commit_message = subprocess.check_output(
88 ['git', 'rev-list', '--format=%B', '--max-count=1', 'HEAD'],
89 cwd=CHECKOUT_ROOT)
90 commit_pos_match = re.search(
91 COMMIT_POSITION_REGEX, commit_message, re.MULTILINE)
92 if not commit_pos_match:
93 raise Exception('Commit position not found in the commit message: %s'
94 % commit_message)
95 return commit_pos_match.group(1)
96
97
98def _UploadFile(user, password, filename, version, target_file):
99# URL is of format:
100 # <repository_api>/<version>/<group_id>/<artifact_id>/<version>/<target_file>
101 # Example:
102 # https://api.bintray.com/content/google/webrtc/google-webrtc/1.0.19742/org/webrtc/google-webrtc/1.0.19742/google-webrtc-1.0.19742.aar
103
104 target_dir = version + '/' + GROUP_ID + '/' + ARTIFACT_ID + '/' + version
105 target_path = target_dir + '/' + target_file
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200106 url = CONTENT_API + '/' + target_path
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200107
108 logging.info('Uploading %s to %s', filename, url)
109 with open(filename) as fh:
110 file_data = fh.read()
111
112 for attempt in xrange(UPLOAD_TRIES):
113 try:
114 response = requests.put(url, data=file_data, auth=(user, password),
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200115 timeout=API_TIMEOUT_SECONDS)
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200116 break
117 except requests.exceptions.Timeout as e:
118 logging.warning('Timeout while uploading: %s', e)
119 time.sleep(UPLOAD_RETRY_BASE_SLEEP_SECONDS ** attempt)
120 else:
121 raise Exception('Failed to upload %s' % filename)
122
123 if not response.ok:
124 raise Exception('Failed to upload %s. Response: %s' % (filename, response))
125 logging.info('Uploaded %s: %s', filename, response)
126
127
128def _GeneratePom(target_file, version, commit):
129 env = jinja2.Environment(
130 loader=jinja2.PackageLoader('release_aar'),
131 )
132 template = env.get_template('pom.jinja')
133 pom = template.render(version=version, commit=commit)
134 with open(target_file, 'w') as fh:
135 fh.write(pom)
136
137
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200138def _TestAAR(tmp_dir, username, password, version):
139 """Runs AppRTCMobile tests using the AAR. Returns true if the tests pass."""
140 logging.info('Testing library.')
141 env = jinja2.Environment(
142 loader=jinja2.PackageLoader('release_aar'),
143 )
144
145 gradle_backup = os.path.join(tmp_dir, 'build.gradle.backup')
146 app_gradle_backup = os.path.join(tmp_dir, 'app-build.gradle.backup')
147
148 # Make backup copies of the project files before modifying them.
149 shutil.copy2(AAR_PROJECT_GRADLE, gradle_backup)
150 shutil.copy2(AAR_PROJECT_APP_GRADLE, app_gradle_backup)
151
152 try:
153 maven_repository_template = env.get_template('maven-repository.jinja')
154 maven_repository = maven_repository_template.render(
155 url=MAVEN_REPOSITORY, username=username, password=password)
156
157 # Append Maven repository to build file to download unpublished files.
158 with open(AAR_PROJECT_GRADLE, 'a') as gradle_file:
159 gradle_file.write(maven_repository)
160
161 # Read app build file.
162 with open(AAR_PROJECT_APP_GRADLE, 'r') as gradle_app_file:
163 gradle_app = gradle_app_file.read()
164
165 if AAR_PROJECT_DEPENDENCY not in gradle_app:
166 raise Exception(
167 '%s not found in the build file.' % AAR_PROJECT_DEPENDENCY)
168 # Set version to the version to be tested.
169 target_dependency = AAR_PROJECT_VERSION_DEPENDENCY % version
170 gradle_app = gradle_app.replace(AAR_PROJECT_DEPENDENCY, target_dependency)
171
172 # Write back.
173 with open(AAR_PROJECT_APP_GRADLE, 'w') as gradle_app_file:
174 gradle_app_file.write(gradle_app)
175
Sami Kalliomäki91e26fd2018-01-22 15:08:24 +0100176 # Uninstall any existing version of AppRTCMobile.
177 logging.info('Uninstalling previous AppRTCMobile versions. It is okay for '
178 'these commands to fail if AppRTCMobile is not installed.')
179 subprocess.call([ADB_BIN, 'uninstall', 'org.appspot.apprtc'])
180 subprocess.call([ADB_BIN, 'uninstall', 'org.appspot.apprtc.test'])
181
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200182 # Run tests.
183 try:
184 # First clean the project.
185 subprocess.check_call([GRADLEW_BIN, 'clean'], cwd=AAR_PROJECT_DIR)
186 # Then run the tests.
187 subprocess.check_call([GRADLEW_BIN, 'connectedDebugAndroidTest'],
188 cwd=AAR_PROJECT_DIR)
189 except subprocess.CalledProcessError:
190 logging.exception('Test failure.')
191 return False # Clean or tests failed
192
193 return True # Tests pass
194 finally:
195 # Restore backups.
196 shutil.copy2(gradle_backup, AAR_PROJECT_GRADLE)
197 shutil.copy2(app_gradle_backup, AAR_PROJECT_APP_GRADLE)
198
199
200def _PublishAAR(user, password, version, additional_args):
201 args = {
202 'publish_wait_for_secs': 0 # Publish asynchronously.
203 }
204 args.update(additional_args)
205
206 url = CONTENT_API + '/' + version + '/publish'
207 response = requests.post(url, data=json.dumps(args), auth=(user, password),
208 timeout=API_TIMEOUT_SECONDS)
209
210 if not response.ok:
211 raise Exception('Failed to publish. Response: %s' % response)
212
213
214def _DeleteUnpublishedVersion(user, password, version):
215 url = PACKAGES_API + '/versions/' + version
216 response = requests.get(url, auth=(user, password),
217 timeout=API_TIMEOUT_SECONDS)
218 if not response.ok:
219 raise Exception('Failed to get version info. Response: %s' % response)
220
221 version_info = json.loads(response.content)
222 if version_info['published']:
223 logging.info('Version has already been published, not deleting.')
224 return
225
226 logging.info('Deleting unpublished version.')
227 response = requests.delete(url, auth=(user, password),
228 timeout=API_TIMEOUT_SECONDS)
229 if not response.ok:
230 raise Exception('Failed to delete version. Response: %s' % response)
231
232
Sami Kalliomäki002e7102018-03-29 11:18:13 +0200233def ReleaseAar(use_goma, skip_tests, publish, build_dir):
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200234 version = '1.0.' + _GetCommitPos()
235 commit = _GetCommitHash()
236 logging.info('Releasing AAR version %s with hash %s', version, commit)
237
238 user = os.environ.get('BINTRAY_USER', None)
239 api_key = os.environ.get('BINTRAY_API_KEY', None)
240 if not user or not api_key:
241 raise Exception('Environment variables BINTRAY_USER and BINTRAY_API_KEY '
242 'must be defined.')
243
Sami Kalliomäki002e7102018-03-29 11:18:13 +0200244 # If build directory is not specified, create a temporary directory.
245 use_tmp_dir = not build_dir
246 if use_tmp_dir:
247 build_dir = tempfile.mkdtemp()
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200248
249 try:
250 base_name = ARTIFACT_ID + '-' + version
Sami Kalliomäki002e7102018-03-29 11:18:13 +0200251 aar_file = os.path.join(build_dir, base_name + '.aar')
252 third_party_licenses_file = os.path.join(build_dir, 'LICENSE.md')
253 pom_file = os.path.join(build_dir, base_name + '.pom')
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200254
Sami Kalliomäki002e7102018-03-29 11:18:13 +0200255 logging.info('Building at %s', build_dir)
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200256 BuildAar(ARCHS, aar_file,
257 use_goma=use_goma,
Sami Kalliomäki002e7102018-03-29 11:18:13 +0200258 ext_build_dir=os.path.join(build_dir, 'aar-build'))
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200259 _GeneratePom(pom_file, version, commit)
260
261 _UploadFile(user, api_key, aar_file, version, base_name + '.aar')
262 _UploadFile(user, api_key, third_party_licenses_file, version,
263 'THIRD_PARTY_LICENSES.md')
264 _UploadFile(user, api_key, pom_file, version, base_name + '.pom')
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200265
Sami Kalliomäki002e7102018-03-29 11:18:13 +0200266 tests_pass = skip_tests or _TestAAR(build_dir, user, api_key, version)
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200267 if not tests_pass:
268 logging.info('Discarding library.')
269 _PublishAAR(user, api_key, version, {'discard': True})
270 _DeleteUnpublishedVersion(user, api_key, version)
271 raise Exception('Test failure. Discarded library.')
272
273 if publish:
274 logging.info('Publishing library.')
275 _PublishAAR(user, api_key, version, {})
276 else:
277 logging.info('Note: The library has not not been published automatically.'
278 ' Please do so manually if desired.')
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200279 finally:
Sami Kalliomäki002e7102018-03-29 11:18:13 +0200280 if use_tmp_dir:
281 shutil.rmtree(build_dir, True)
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200282
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200283
284def main():
285 args = _ParseArgs()
286 logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)
Sami Kalliomäki002e7102018-03-29 11:18:13 +0200287 ReleaseAar(args.use_goma, args.skip_tests, args.publish, args.build_dir)
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200288
289
290if __name__ == '__main__':
291 sys.exit(main())