blob: bc7f471ae364fa1a8b71691f88ecca3515b1004c [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.
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +020010"""Script for publishing WebRTC AAR on Bintray.
11
12Set BINTRAY_USER and BINTRAY_API_KEY environment variables before running
13this script for authentication.
14"""
15
16import argparse
Sami Kalliomäki201509b2017-10-09 13:44:25 +020017import json
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +020018import logging
19import os
20import re
21import shutil
22import subprocess
23import sys
24import tempfile
25import time
26
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +020027SCRIPT_DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
28CHECKOUT_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, os.pardir, os.pardir))
29
30sys.path.append(os.path.join(CHECKOUT_ROOT, 'third_party'))
31import requests
32import jinja2
33
34sys.path.append(os.path.join(CHECKOUT_ROOT, 'tools_webrtc'))
35from android.build_aar import BuildAar
36
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +020037ARCHS = ['armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64']
Sami Kalliomäki201509b2017-10-09 13:44:25 +020038MAVEN_REPOSITORY = 'https://google.bintray.com/webrtc'
39API = 'https://api.bintray.com'
40PACKAGE_PATH = 'google/webrtc/google-webrtc'
41CONTENT_API = API + '/content/' + PACKAGE_PATH
42PACKAGES_API = API + '/packages/' + PACKAGE_PATH
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +020043GROUP_ID = 'org/webrtc'
44ARTIFACT_ID = 'google-webrtc'
45COMMIT_POSITION_REGEX = r'^Cr-Commit-Position: refs/heads/master@{#(\d+)}$'
Sami Kalliomäki201509b2017-10-09 13:44:25 +020046API_TIMEOUT_SECONDS = 10.0
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +020047UPLOAD_TRIES = 3
48# The sleep time is increased exponentially.
49UPLOAD_RETRY_BASE_SLEEP_SECONDS = 2.0
Sami Kalliomäki201509b2017-10-09 13:44:25 +020050GRADLEW_BIN = os.path.join(CHECKOUT_ROOT,
51 'examples/androidtests/third_party/gradle/gradlew')
Sami Kalliomäki91e26fd2018-01-22 15:08:24 +010052ADB_BIN = os.path.join(CHECKOUT_ROOT,
Oleksandr Iakovenko53de7252019-03-20 13:45:31 +010053 'third_party/android_sdk/public/platform-tools/adb')
Sami Kalliomäki201509b2017-10-09 13:44:25 +020054AAR_PROJECT_DIR = os.path.join(CHECKOUT_ROOT, 'examples/aarproject')
55AAR_PROJECT_GRADLE = os.path.join(AAR_PROJECT_DIR, 'build.gradle')
56AAR_PROJECT_APP_GRADLE = os.path.join(AAR_PROJECT_DIR, 'app', 'build.gradle')
57AAR_PROJECT_DEPENDENCY = "implementation 'org.webrtc:google-webrtc:1.0.+'"
58AAR_PROJECT_VERSION_DEPENDENCY = "implementation 'org.webrtc:google-webrtc:%s'"
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +020059
60
61def _ParseArgs():
Mirko Bonadei8cc66952020-10-30 10:13:45 +010062 parser = argparse.ArgumentParser(description='Releases WebRTC on Bintray.')
63 parser.add_argument('--use-goma',
64 action='store_true',
65 default=False,
66 help='Use goma.')
67 parser.add_argument('--skip-tests',
68 action='store_true',
69 default=False,
70 help='Skips running the tests.')
71 parser.add_argument(
72 '--publish',
73 action='store_true',
74 default=False,
75 help='Automatically publishes the library if the tests pass.')
76 parser.add_argument(
77 '--build-dir',
78 default=None,
79 help='Temporary directory to store the build files. If not specified, '
80 'a new directory will be created.')
81 parser.add_argument('--verbose',
82 action='store_true',
83 default=False,
84 help='Debug logging.')
85 return parser.parse_args()
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +020086
87
88def _GetCommitHash():
Mirko Bonadei8cc66952020-10-30 10:13:45 +010089 commit_hash = subprocess.check_output(['git', 'rev-parse', 'HEAD'],
90 cwd=CHECKOUT_ROOT).strip()
91 return commit_hash
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +020092
93
94def _GetCommitPos():
Mirko Bonadei8cc66952020-10-30 10:13:45 +010095 commit_message = subprocess.check_output(
96 ['git', 'rev-list', '--format=%B', '--max-count=1', 'HEAD'],
97 cwd=CHECKOUT_ROOT)
98 commit_pos_match = re.search(COMMIT_POSITION_REGEX, commit_message,
99 re.MULTILINE)
100 if not commit_pos_match:
101 raise Exception('Commit position not found in the commit message: %s' %
102 commit_message)
103 return commit_pos_match.group(1)
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200104
105
106def _UploadFile(user, password, filename, version, target_file):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100107 # URL is of format:
108 # <repository_api>/<version>/<group_id>/<artifact_id>/<version>/<target_file>
109 # Example:
110 # 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
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200111
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100112 target_dir = version + '/' + GROUP_ID + '/' + ARTIFACT_ID + '/' + version
113 target_path = target_dir + '/' + target_file
114 url = CONTENT_API + '/' + target_path
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200115
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100116 logging.info('Uploading %s to %s', filename, url)
117 with open(filename) as fh:
118 file_data = fh.read()
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200119
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100120 for attempt in xrange(UPLOAD_TRIES):
121 try:
122 response = requests.put(url,
123 data=file_data,
124 auth=(user, password),
125 timeout=API_TIMEOUT_SECONDS)
126 break
127 except requests.exceptions.Timeout as e:
128 logging.warning('Timeout while uploading: %s', e)
129 time.sleep(UPLOAD_RETRY_BASE_SLEEP_SECONDS**attempt)
130 else:
131 raise Exception('Failed to upload %s' % filename)
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200132
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100133 if not response.ok:
134 raise Exception('Failed to upload %s. Response: %s' %
135 (filename, response))
136 logging.info('Uploaded %s: %s', filename, response)
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200137
138
139def _GeneratePom(target_file, version, commit):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100140 env = jinja2.Environment(loader=jinja2.PackageLoader('release_aar'), )
141 template = env.get_template('pom.jinja')
142 pom = template.render(version=version, commit=commit)
143 with open(target_file, 'w') as fh:
144 fh.write(pom)
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200145
146
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200147def _TestAAR(tmp_dir, username, password, version):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100148 """Runs AppRTCMobile tests using the AAR. Returns true if the tests pass."""
149 logging.info('Testing library.')
150 env = jinja2.Environment(loader=jinja2.PackageLoader('release_aar'), )
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200151
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100152 gradle_backup = os.path.join(tmp_dir, 'build.gradle.backup')
153 app_gradle_backup = os.path.join(tmp_dir, 'app-build.gradle.backup')
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200154
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100155 # Make backup copies of the project files before modifying them.
156 shutil.copy2(AAR_PROJECT_GRADLE, gradle_backup)
157 shutil.copy2(AAR_PROJECT_APP_GRADLE, app_gradle_backup)
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200158
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200159 try:
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100160 maven_repository_template = env.get_template('maven-repository.jinja')
161 maven_repository = maven_repository_template.render(
162 url=MAVEN_REPOSITORY, username=username, password=password)
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200163
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100164 # Append Maven repository to build file to download unpublished files.
165 with open(AAR_PROJECT_GRADLE, 'a') as gradle_file:
166 gradle_file.write(maven_repository)
167
168 # Read app build file.
169 with open(AAR_PROJECT_APP_GRADLE, 'r') as gradle_app_file:
170 gradle_app = gradle_app_file.read()
171
172 if AAR_PROJECT_DEPENDENCY not in gradle_app:
173 raise Exception('%s not found in the build file.' %
174 AAR_PROJECT_DEPENDENCY)
175 # Set version to the version to be tested.
176 target_dependency = AAR_PROJECT_VERSION_DEPENDENCY % version
177 gradle_app = gradle_app.replace(AAR_PROJECT_DEPENDENCY,
178 target_dependency)
179
180 # Write back.
181 with open(AAR_PROJECT_APP_GRADLE, 'w') as gradle_app_file:
182 gradle_app_file.write(gradle_app)
183
184 # Uninstall any existing version of AppRTCMobile.
185 logging.info(
186 'Uninstalling previous AppRTCMobile versions. It is okay for '
187 'these commands to fail if AppRTCMobile is not installed.')
188 subprocess.call([ADB_BIN, 'uninstall', 'org.appspot.apprtc'])
189 subprocess.call([ADB_BIN, 'uninstall', 'org.appspot.apprtc.test'])
190
191 # Run tests.
192 try:
193 # First clean the project.
194 subprocess.check_call([GRADLEW_BIN, 'clean'], cwd=AAR_PROJECT_DIR)
195 # Then run the tests.
196 subprocess.check_call([GRADLEW_BIN, 'connectedDebugAndroidTest'],
197 cwd=AAR_PROJECT_DIR)
198 except subprocess.CalledProcessError:
199 logging.exception('Test failure.')
200 return False # Clean or tests failed
201
202 return True # Tests pass
203 finally:
204 # Restore backups.
205 shutil.copy2(gradle_backup, AAR_PROJECT_GRADLE)
206 shutil.copy2(app_gradle_backup, AAR_PROJECT_APP_GRADLE)
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200207
208
209def _PublishAAR(user, password, version, additional_args):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100210 args = {
211 'publish_wait_for_secs': 0 # Publish asynchronously.
212 }
213 args.update(additional_args)
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200214
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100215 url = CONTENT_API + '/' + version + '/publish'
216 response = requests.post(url,
217 data=json.dumps(args),
218 auth=(user, password),
219 timeout=API_TIMEOUT_SECONDS)
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200220
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100221 if not response.ok:
222 raise Exception('Failed to publish. Response: %s' % response)
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200223
224
225def _DeleteUnpublishedVersion(user, password, version):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100226 url = PACKAGES_API + '/versions/' + version
227 response = requests.get(url,
228 auth=(user, password),
229 timeout=API_TIMEOUT_SECONDS)
230 if not response.ok:
231 raise Exception('Failed to get version info. Response: %s' % response)
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200232
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100233 version_info = json.loads(response.content)
234 if version_info['published']:
235 logging.info('Version has already been published, not deleting.')
236 return
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200237
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100238 logging.info('Deleting unpublished version.')
239 response = requests.delete(url,
240 auth=(user, password),
241 timeout=API_TIMEOUT_SECONDS)
242 if not response.ok:
243 raise Exception('Failed to delete version. Response: %s' % response)
Sami Kalliomäki201509b2017-10-09 13:44:25 +0200244
245
Sami Kalliomäki002e7102018-03-29 11:18:13 +0200246def ReleaseAar(use_goma, skip_tests, publish, build_dir):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100247 version = '1.0.' + _GetCommitPos()
248 commit = _GetCommitHash()
249 logging.info('Releasing AAR version %s with hash %s', version, commit)
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200250
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100251 user = os.environ.get('BINTRAY_USER', None)
252 api_key = os.environ.get('BINTRAY_API_KEY', None)
253 if not user or not api_key:
254 raise Exception(
255 'Environment variables BINTRAY_USER and BINTRAY_API_KEY '
256 'must be defined.')
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200257
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100258 # If build directory is not specified, create a temporary directory.
259 use_tmp_dir = not build_dir
Sami Kalliomäki002e7102018-03-29 11:18:13 +0200260 if use_tmp_dir:
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100261 build_dir = tempfile.mkdtemp()
262
263 try:
264 base_name = ARTIFACT_ID + '-' + version
265 aar_file = os.path.join(build_dir, base_name + '.aar')
266 third_party_licenses_file = os.path.join(build_dir, 'LICENSE.md')
267 pom_file = os.path.join(build_dir, base_name + '.pom')
268
269 logging.info('Building at %s', build_dir)
270 BuildAar(ARCHS,
271 aar_file,
272 use_goma=use_goma,
273 ext_build_dir=os.path.join(build_dir, 'aar-build'))
274 _GeneratePom(pom_file, version, commit)
275
276 _UploadFile(user, api_key, aar_file, version, base_name + '.aar')
277 _UploadFile(user, api_key, third_party_licenses_file, version,
278 'THIRD_PARTY_LICENSES.md')
279 _UploadFile(user, api_key, pom_file, version, base_name + '.pom')
280
281 tests_pass = skip_tests or _TestAAR(build_dir, user, api_key, version)
282 if not tests_pass:
283 logging.info('Discarding library.')
284 _PublishAAR(user, api_key, version, {'discard': True})
285 _DeleteUnpublishedVersion(user, api_key, version)
286 raise Exception('Test failure. Discarded library.')
287
288 if publish:
289 logging.info('Publishing library.')
290 _PublishAAR(user, api_key, version, {})
291 else:
292 logging.info(
293 'Note: The library has not not been published automatically.'
294 ' Please do so manually if desired.')
295 finally:
296 if use_tmp_dir:
297 shutil.rmtree(build_dir, True)
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200298
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200299
300def main():
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100301 args = _ParseArgs()
302 logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)
303 ReleaseAar(args.use_goma, args.skip_tests, args.publish, args.build_dir)
Sami Kalliomäkidbb15a72017-10-05 16:15:02 +0200304
305
306if __name__ == '__main__':
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100307 sys.exit(main())