blob: dba0c1692cc6a7498d092198cab0ea7d81953c16 [file] [log] [blame]
Ravi Mistrye1e47b72017-09-08 13:54:41 -04001#!/usr/bin/env python
2# Copyright (c) 2017 The Chromium 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.
5
6"""Script that uploads the specified Skia Gerrit change to Android.
7
8This script does the following:
9* Downloads the repo tool.
10* Inits and checks out the bare-minimum required Android checkout.
11* Sets the required git config options in external/skia.
12* Cherry-picks the specified Skia patch.
13* Modifies the change subject to append a "Test:" line required for presubmits.
14* Uploads the Skia change to Android's Gerrit instance.
15
16After the change is uploaded to Android, developers can trigger TH and download
17binaries (if required) after runs complete.
18
19The script re-uses the workdir when it is run again. To start from a clean slate
20delete the workdir.
21
22Timings:
23* ~1m15s when using an empty/non-existent workdir for the first time.
24* ~15s when using a workdir previously populated by the script.
25
26Example usage:
27 $ python upload_to_android.py -w /repos/testing -c 44200
28"""
29
30import argparse
Mike Klein11cd4642017-09-08 17:55:31 -040031import getpass
Ravi Mistrye1e47b72017-09-08 13:54:41 -040032import json
33import os
34import subprocess
35import stat
36import urllib2
37
38
39REPO_TOOL_URL = 'https://storage.googleapis.com/git-repo-downloads/repo'
40SKIA_PATH_IN_ANDROID = os.path.join('external', 'skia')
41ANDROID_REPO_URL = 'https://googleplex-android.googlesource.com'
42REPO_BRANCH_NAME = 'experiment'
43SKIA_GERRIT_INSTANCE = 'https://skia-review.googlesource.com'
Ravi Mistryff72a082017-09-12 09:40:46 -040044SK_USER_CONFIG_PATH = os.path.join('include', 'config', 'SkUserConfig.h')
Ravi Mistrye1e47b72017-09-08 13:54:41 -040045
46
47def get_change_details(change_num):
48 response = urllib2.urlopen('%s/changes/%s/detail?o=ALL_REVISIONS' % (
49 SKIA_GERRIT_INSTANCE, change_num), timeout=5)
50 content = response.read()
51 # Remove the first line which contains ")]}'\n".
52 return json.loads(content[5:])
53
54
Yuqian Li33e2fad2017-10-24 14:42:57 -040055def init_work_dir(work_dir):
Ravi Mistrye1e47b72017-09-08 13:54:41 -040056 if not os.path.isdir(work_dir):
57 print 'Creating %s' % work_dir
58 os.makedirs(work_dir)
59
60 # Ensure the repo tool exists in the work_dir.
61 repo_dir = os.path.join(work_dir, 'bin')
62 repo_binary = os.path.join(repo_dir, 'repo')
63 if not os.path.isdir(repo_dir):
64 print 'Creating %s' % repo_dir
65 os.makedirs(repo_dir)
66 if not os.path.exists(repo_binary):
67 print 'Downloading %s from %s' % (repo_binary, REPO_TOOL_URL)
68 response = urllib2.urlopen(REPO_TOOL_URL, timeout=5)
69 content = response.read()
70 with open(repo_binary, 'w') as f:
71 f.write(content)
72 # Set executable bit.
73 st = os.stat(repo_binary)
74 os.chmod(repo_binary, st.st_mode | stat.S_IEXEC)
75
76 # Create android-repo directory in the work_dir.
77 android_dir = os.path.join(work_dir, 'android-repo')
78 if not os.path.isdir(android_dir):
79 print 'Creating %s' % android_dir
80 os.makedirs(android_dir)
81
82 print """
83
84About to run repo init. If it hangs asking you to run glogin then please:
85* Exit the script (ctrl-c).
86* Run 'glogin'.
87* Re-run the script.
88
89"""
90 os.chdir(android_dir)
91 subprocess.check_call(
Ravi Mistryb8163222017-09-11 10:47:34 -040092 '%s init -u %s/a/platform/manifest -g "all,-notdefault,-darwin" '
93 '-b master --depth=1'
Ravi Mistrye1e47b72017-09-08 13:54:41 -040094 % (repo_binary, ANDROID_REPO_URL), shell=True)
95
96 print 'Syncing the Android checkout at %s' % android_dir
Ravi Mistryb8163222017-09-11 10:47:34 -040097 subprocess.check_call('%s sync %s tools/repohooks -j 32 -c' % (
Ravi Mistrye1e47b72017-09-08 13:54:41 -040098 repo_binary, SKIA_PATH_IN_ANDROID), shell=True)
99
100 # Set the necessary git config options.
101 os.chdir(SKIA_PATH_IN_ANDROID)
102 subprocess.check_call(
103 'git config remote.goog.review %s/' % ANDROID_REPO_URL, shell=True)
104 subprocess.check_call(
105 'git config review.%s/.autoupload true' % ANDROID_REPO_URL, shell=True)
Mike Klein11cd4642017-09-08 17:55:31 -0400106 subprocess.check_call(
107 'git config user.email %s@google.com' % getpass.getuser(), shell=True)
Ravi Mistrye1e47b72017-09-08 13:54:41 -0400108
Yuqian Li33e2fad2017-10-24 14:42:57 -0400109 return repo_binary
110
Yuqian Li33e2fad2017-10-24 14:42:57 -0400111
Yuqian Liee6784d2017-10-31 10:14:25 -0400112class Modifier:
113 def modify(self):
114 raise NotImplementedError
115 def get_user_msg(self):
116 raise NotImplementedError
Ravi Mistrye1e47b72017-09-08 13:54:41 -0400117
Yuqian Liee6784d2017-10-31 10:14:25 -0400118
119class FetchModifier(Modifier):
120 def __init__(self, change_num, debug):
121 self.change_num = change_num
122 self.debug = debug
123
124 def modify(self):
Ravi Mistrye1e47b72017-09-08 13:54:41 -0400125 # Download and cherry-pick the patch.
Yuqian Liee6784d2017-10-31 10:14:25 -0400126 change_details = get_change_details(self.change_num)
Ravi Mistrye1e47b72017-09-08 13:54:41 -0400127 latest_patchset = len(change_details['revisions'])
Yuqian Liee6784d2017-10-31 10:14:25 -0400128 mod = int(self.change_num) % 100
Ravi Mistrye1e47b72017-09-08 13:54:41 -0400129 download_ref = 'refs/changes/%s/%s/%s' % (
Yuqian Liee6784d2017-10-31 10:14:25 -0400130 str(mod).zfill(2), self.change_num, latest_patchset)
Ravi Mistrye1e47b72017-09-08 13:54:41 -0400131 subprocess.check_call(
132 'git fetch https://skia.googlesource.com/skia %s' % download_ref,
133 shell=True)
134 subprocess.check_call('git cherry-pick FETCH_HEAD', shell=True)
135
Yuqian Liee6784d2017-10-31 10:14:25 -0400136 if self.debug:
Ravi Mistryff72a082017-09-12 09:40:46 -0400137 # Add SK_DEBUG to SkUserConfig.h.
138 with open(SK_USER_CONFIG_PATH, 'a') as f:
139 f.write('#ifndef SK_DEBUG\n')
140 f.write('#define SK_DEBUG\n')
141 f.write('#endif//SK_DEBUG\n')
142 subprocess.check_call('git add %s' % SK_USER_CONFIG_PATH, shell=True)
143
Ravi Mistrya6b64692017-09-12 10:56:42 -0400144 # Amend the commit message to add a prefix that makes it clear that the
145 # change should not be submitted and a "Test:" line which is required by
146 # Android presubmit checks.
Ravi Mistrye1e47b72017-09-08 13:54:41 -0400147 original_commit_message = change_details['subject']
148 new_commit_message = (
Ravi Mistrya6b64692017-09-12 10:56:42 -0400149 # Intentionally breaking up the below string because some presubmits
150 # complain about it.
151 '[DO ' + 'NOT ' + 'SUBMIT] %s\n\n'
Ravi Mistrya2bdf002017-09-11 11:00:34 -0400152 'Test: Presubmit checks will test this change.' % (
Ravi Mistrye1e47b72017-09-08 13:54:41 -0400153 original_commit_message))
Yuqian Liee6784d2017-10-31 10:14:25 -0400154
Ravi Mistrye1e47b72017-09-08 13:54:41 -0400155 subprocess.check_call('git commit --amend -m "%s"' % new_commit_message,
156 shell=True)
157
Yuqian Liee6784d2017-10-31 10:14:25 -0400158 def get_user_msg(self):
159 return """
Ravi Mistrye1e47b72017-09-08 13:54:41 -0400160
161Open the above URL and trigger TH by checking 'Presubmit-Ready'.
162You can download binaries (if required) from the TH link after it completes.
163"""
Yuqian Liee6784d2017-10-31 10:14:25 -0400164
165
166# Add a legacy flag if it doesn't exist, or remove it if it exists.
167class AndroidLegacyFlagModifier(Modifier):
168 def __init__(self, flag):
169 self.flag = flag
170 self.verb = "Unknown"
171
172 def modify(self):
173 flag_line = " #define %s\n" % self.flag
174
175 config_file = os.path.join('include', 'config', 'SkUserConfigManual.h')
176
177 with open(config_file) as f:
178 lines = f.readlines()
179
180 if flag_line not in lines:
181 lines.insert(
182 lines.index("#endif // SkUserConfigManual_DEFINED\n"), flag_line)
183 verb = "Add"
184 else:
185 lines.remove(flag_line)
186 verb = "Remove"
187
188 with open(config_file, 'w') as f:
189 for line in lines:
190 f.write(line)
191
192 subprocess.check_call('git add %s' % config_file, shell=True)
193 message = '%s %s\n\nTest: Presubmit checks will test this change.' % (
194 verb, self.flag)
195
196 subprocess.check_call('git commit -m "%s"' % message, shell=True)
197
198 def get_user_msg(self):
199 return """
200
201 Please open the above URL to review and land the change.
202"""
203
204
205def upload_to_android(work_dir, modifier):
206 repo_binary = init_work_dir(work_dir)
207
208 # Create repo branch.
209 subprocess.check_call('%s start %s .' % (repo_binary, REPO_BRANCH_NAME),
210 shell=True)
211 try:
212 modifier.modify()
213
214 # Upload to Android Gerrit.
215 subprocess.check_call('%s upload --verify' % repo_binary, shell=True)
216
217 print modifier.get_user_msg()
Ravi Mistrye1e47b72017-09-08 13:54:41 -0400218 finally:
219 # Abandon repo branch.
220 subprocess.call('%s abandon %s' % (repo_binary, REPO_BRANCH_NAME),
221 shell=True)
222
223
224def main():
225 parser = argparse.ArgumentParser()
226 parser.add_argument(
227 '--work-dir', '-w', required=True,
228 help='Directory where an Android checkout will be created (if it does '
229 'not already exist). Note: ~1GB space will be used.')
230 parser.add_argument(
231 '--change-num', '-c', required=True,
232 help='The skia-rev Gerrit change number that should be patched into '
233 'Android.')
Ravi Mistryff72a082017-09-12 09:40:46 -0400234 parser.add_argument(
235 '--debug', '-d', action='store_true', default=False,
236 help='Adds SK_DEBUG to SkUserConfig.h.')
Ravi Mistrye1e47b72017-09-08 13:54:41 -0400237 args = parser.parse_args()
Yuqian Liee6784d2017-10-31 10:14:25 -0400238 upload_to_android(args.work_dir, FetchModifier(args.change_num, args.debug))
Ravi Mistrye1e47b72017-09-08 13:54:41 -0400239
240
241if __name__ == '__main__':
242 main()