[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 1 | #!/usr/bin/python |
[email protected] | 6f943c2 | 2009-09-01 00:41:14 | [diff] [blame] | 2 | # Copyright (c) 2009 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. |
[email protected] | 565db0e | 2009-06-05 18:08:54 | [diff] [blame] | 5 | |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 6 | import getpass |
| 7 | import optparse |
| 8 | import os |
| 9 | import subprocess |
| 10 | import tempfile |
| 11 | import traceback |
| 12 | import urllib |
| 13 | import sys |
[email protected] | b0e621f | 2009-08-25 21:01:51 | [diff] [blame] | 14 | import re |
| 15 | import trychange |
[email protected] | 565db0e | 2009-06-05 18:08:54 | [diff] [blame] | 16 | |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 17 | |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 18 | def Backquote(cmd, cwd=None): |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 19 | """Like running `cmd` in a shell script.""" |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 20 | return subprocess.Popen(cmd, |
| 21 | cwd=cwd, |
| 22 | stdout=subprocess.PIPE).communicate()[0].strip() |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 23 | |
| 24 | |
| 25 | def GetTryServerConfig(): |
| 26 | """Returns the dictionary of try server options or None if they |
| 27 | cannot be found.""" |
| 28 | script_path = 'tools/tryserver/tryserver.py' |
| 29 | root_dir = Backquote(['git', 'rev-parse', '--show-cdup']) |
| 30 | try: |
| 31 | script_file = open(os.path.join(root_dir, script_path)) |
| 32 | except IOError: |
| 33 | return None |
| 34 | locals = {} |
| 35 | try: |
| 36 | exec(script_file, locals) |
| 37 | except Exception, e: |
| 38 | return None |
| 39 | return locals |
| 40 | |
| 41 | |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 42 | def GetBranchName(working_dir=None): |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 43 | """Return name of current git branch.""" |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 44 | branch = Backquote(['git', 'symbolic-ref', 'HEAD'], working_dir) |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 45 | if not branch.startswith('refs/heads/'): |
| 46 | raise "Couldn't figure out branch name" |
| 47 | branch = branch[len('refs/heads/'):] |
| 48 | return branch |
| 49 | |
| 50 | |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 51 | def GetPatchName(working_dir=None): |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 52 | """Construct a name for this patch.""" |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 53 | short_sha = Backquote(['git', 'rev-parse', '--short=4', 'HEAD'], working_dir) |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 54 | return GetBranchName() + '-' + short_sha |
| 55 | |
| 56 | |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 57 | def GetRietveldIssueNumber(): |
| 58 | return Backquote(['git', 'config', |
| 59 | 'branch.%s.rietveldissue' % GetBranchName()]) |
| 60 | |
| 61 | |
| 62 | def GetRietveldPatchsetNumber(): |
| 63 | return Backquote(['git', 'config', |
| 64 | 'branch.%s.rietveldpatchset' % GetBranchName()]) |
| 65 | |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 66 | def GetSubRepWorkingDir(sub_rep_path): |
| 67 | """Computes the path to the sub repository""" |
| 68 | if sub_rep_path: |
| 69 | root_dir = os.path.abspath(Backquote(['git', 'rev-parse', '--show-cdup'])) |
| 70 | return os.path.join(root_dir, sub_rep_path) |
| 71 | return None |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 72 | |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 73 | def GetMungedDiff(branch, prefix, sub_rep_path): |
| 74 | """Get the diff we'll send to the try server. We munge paths to match svn. |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 75 | We add the prefix that the try bot is expecting. If sub_rep_path is |
| 76 | provided, diff will be calculated in the sub repository. |
| 77 | We also return the list of files in this diff, without munged paths.""" |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 78 | # Make the following changes: |
[email protected] | 832179b | 2009-09-17 17:13:24 | [diff] [blame] | 79 | # - Prepend "src/" (or some other prefix) to paths as svn is expecting |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 80 | # - In the case of added files, replace /dev/null with the path to the file |
| 81 | # being added. |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 82 | |
| 83 | cwd = GetSubRepWorkingDir(sub_rep_path) |
| 84 | |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 85 | output = [] |
| 86 | if not branch: |
| 87 | # Try to guess the upstream branch. |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 88 | branch = Backquote(['git', 'cl', 'upstream'], cwd) |
| 89 | command = ['git', 'diff-tree', '-p'] |
| 90 | |
| 91 | new_cwd = None |
| 92 | if not sub_rep_path: |
| 93 | command.extend(['--no-prefix']) |
| 94 | else: |
| 95 | # Append / |
| 96 | sub_rep_path = os.path.join(sub_rep_path, '') |
| 97 | # Add the right prefix |
| 98 | command.extend(['--src-prefix=' + sub_rep_path]) |
| 99 | command.extend(['--dst-prefix=' + sub_rep_path]) |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 100 | |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 101 | command.extend([branch, 'HEAD']) |
| 102 | |
| 103 | # Run diff tree |
| 104 | diff = subprocess.Popen(command, |
| 105 | stdout=subprocess.PIPE, |
| 106 | cwd=cwd).stdout.readlines() |
| 107 | # Replace --- /dev/null with --- <new file name> |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 108 | for i in range(len(diff)): |
| 109 | line = diff[i] |
| 110 | if line.startswith('--- /dev/null'): |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 111 | line = '--- %s' % diff[i+1][4:] |
| 112 | output.append(line) |
| 113 | diff = output |
| 114 | |
| 115 | # Add root prefix |
| 116 | output = [] |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 117 | file_set = set() |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 118 | for line in diff: |
| 119 | if line.startswith('--- ') or line.startswith('+++ '): |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 120 | filename = line[4:] |
| 121 | line = line[0:4] + os.path.join(prefix, filename) |
| 122 | file_set.add(filename.rstrip('\r\n')) |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 123 | output.append(line) |
| 124 | |
| 125 | munged_diff = ''.join(output) |
| 126 | if len(munged_diff.strip()) == 0: |
| 127 | raise Exception("Patch was empty, did you give the right remote branch?") |
| 128 | |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 129 | return (munged_diff, list(file_set)) |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 130 | |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 131 | def OneRepositoryDiff(diff_file, patch_names, branch, prefix, sub_rep_path): |
| 132 | """Computes a diff for one git repository at a given path against a given |
| 133 | branch. Writes the diff into diff_file and appends a name to the |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 134 | patch_names list. |
| 135 | |
| 136 | Returns the list of files in the diff.""" |
| 137 | |
| 138 | (diff, file_list) = GetMungedDiff(branch, prefix, sub_rep_path) |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 139 | |
| 140 | # Write the diff out |
| 141 | diff_file.write(diff) |
| 142 | |
| 143 | # Add patch name to list of patches |
| 144 | patch_name = GetPatchName(GetSubRepWorkingDir(sub_rep_path)) |
| 145 | patch_names.extend([patch_name]) |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 146 | return file_list |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 147 | |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 148 | |
[email protected] | b0e621f | 2009-08-25 21:01:51 | [diff] [blame] | 149 | def ValidEmail(email): |
| 150 | return re.match(r"^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email) |
| 151 | |
| 152 | |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 153 | def GetEmail(): |
[email protected] | b0e621f | 2009-08-25 21:01:51 | [diff] [blame] | 154 | email = Backquote(['git', 'config', 'user.email']) |
| 155 | runmsg = "Try: git config user.email <EMAIL>" |
| 156 | assert ValidEmail(email), "Email '%s' is not valid. %s" % (email, runmsg) |
| 157 | return email |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 158 | |
| 159 | |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 160 | def TryChange(args, file_list): |
[email protected] | b0e621f | 2009-08-25 21:01:51 | [diff] [blame] | 161 | """Put a patch on the try server.""" |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 162 | trychange.TryChange(args, file_list, False) |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 163 | |
| 164 | |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 165 | if __name__ == '__main__': |
| 166 | parser = optparse.OptionParser( |
| 167 | usage='git try [options] [branch]', |
| 168 | description='Upload the current diff of branch...HEAD to the try server.') |
[email protected] | 6f3c2c6 | 2009-10-15 20:36:26 | [diff] [blame] | 169 | parser.add_option("-b", "--bot", action="append", |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 170 | help="Force the use of a specific build slave (eg mac, " |
| 171 | "win, or linux)") |
| 172 | parser.add_option("-c", "--clobber", action="store_true", |
| 173 | help="Make the try run use be a clobber build") |
| 174 | parser.add_option("-r", "--revision", |
| 175 | help="Specify the SVN base revision to use") |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 176 | parser.add_option("--root", default="src", metavar="PATH", |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 177 | help="Specify the root prefix that is prepended to paths " |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 178 | "in the patch") |
| 179 | parser.add_option("--dry_run", action="store_true", |
| 180 | help="Print the diff but don't send it to the try bots") |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 181 | parser.add_option("--sub_rep", nargs=2, action="append", default=[], |
| 182 | metavar="PATH BRANCH", |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 183 | help="Specify a path to a git sub-repository and a branch " |
| 184 | "to diff with in order to simultanously try changes " |
| 185 | "in multiple git repositories. Option may be " |
| 186 | "specified multiple times.") |
| 187 | parser.add_option("--webkit", metavar="BRANCH", |
| 188 | help="Specify webkit branch. Syntactic sugar for " |
| 189 | "--sub_rep third_party/WebKit/ <branch>") |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 190 | |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 191 | (options, args) = parser.parse_args(sys.argv) |
| 192 | |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 193 | if options.webkit: |
| 194 | options.sub_rep.extend([('third_party/WebKit/', options.webkit)]) |
| 195 | |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 196 | branch = None |
| 197 | if len(args) > 1: |
| 198 | branch = args[1] |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 199 | patch_names = [] |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 200 | |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 201 | # Dump all diffs into one diff file. |
[email protected] | b0e621f | 2009-08-25 21:01:51 | [diff] [blame] | 202 | diff_file = tempfile.NamedTemporaryFile() |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 203 | |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 204 | # Calculate diff for main git repository. |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 205 | file_list = OneRepositoryDiff(diff_file, patch_names, branch, options.root, |
| 206 | None) |
| 207 | |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 208 | # Calculate diff for each extra git repository. |
| 209 | for path_and_branch in options.sub_rep: |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 210 | file_list.extend(OneRepositoryDiff(diff_file, |
| 211 | patch_names, |
| 212 | path_and_branch[1], |
| 213 | options.root, |
| 214 | path_and_branch[0])) |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 215 | # Make diff file ready for reading. |
[email protected] | b0e621f | 2009-08-25 21:01:51 | [diff] [blame] | 216 | diff_file.flush() |
| 217 | |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 218 | # Concatenate patch names |
| 219 | # Prepare args for TryChange |
[email protected] | b0e621f | 2009-08-25 21:01:51 | [diff] [blame] | 220 | email = GetEmail() |
| 221 | user = email.partition('@')[0] |
| 222 | args = [ |
| 223 | '-u', user, |
| 224 | '-e', email, |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 225 | '-n', '-'.join(patch_names), |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 226 | '--diff', diff_file.name, |
[email protected] | b0e621f | 2009-08-25 21:01:51 | [diff] [blame] | 227 | ] |
| 228 | |
| 229 | # Send to try server via HTTP if we can parse the config, otherwise |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 230 | # upload via SVN. |
| 231 | config = GetTryServerConfig() |
| 232 | if config is not None: |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 233 | sendmsg = "Sending %s using HTTP..." % '-'.join(patch_names) |
[email protected] | b0e621f | 2009-08-25 21:01:51 | [diff] [blame] | 234 | args.extend(['--use_http']) |
| 235 | if config['try_server_http_host'] is not None: |
| 236 | args.extend(['--host', config['try_server_http_host']]) |
| 237 | if config['try_server_http_port'] is not None: |
| 238 | args.extend(['--port', config['try_server_http_port']]) |
| 239 | |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 240 | else: |
| 241 | print "Could not get server config -- if you're within Google, " |
| 242 | print "do you have have src-internal checked out?" |
[email protected] | 8871f45 | 2009-09-23 15:48:36 | [diff] [blame] | 243 | sendmsg = "Sending %s using SVN..." % '-'.join(patch_names) |
[email protected] | b0e621f | 2009-08-25 21:01:51 | [diff] [blame] | 244 | args.extend([ |
| 245 | '--use_svn', '--svn_repo', |
| 246 | 'svn://svn.chromium.org/chrome-try/try', |
| 247 | ]) |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 248 | |
[email protected] | 5ab676c | 2009-10-16 00:15:46 | [diff] [blame] | 249 | if options.bot: |
| 250 | for bot in options.bot: |
| 251 | args.extend(['--bot', bot]) |
[email protected] | b0e621f | 2009-08-25 21:01:51 | [diff] [blame] | 252 | if options.clobber: |
| 253 | args.append('--clobber') |
| 254 | if options.revision: |
| 255 | args.extend(['-r', options.revision]) |
[email protected] | b0e621f | 2009-08-25 21:01:51 | [diff] [blame] | 256 | if GetRietveldPatchsetNumber(): |
| 257 | args.extend([ |
| 258 | '--issue', GetRietveldIssueNumber(), |
| 259 | '--patchset', GetRietveldPatchsetNumber(), |
| 260 | ]) |
[email protected] | cabf2ad | 2009-08-22 01:26:31 | [diff] [blame] | 261 | |
[email protected] | 742eb52 | 2009-09-22 16:18:42 | [diff] [blame] | 262 | if options.dry_run: |
| 263 | print open(diff_file.name, 'r').read() |
| 264 | exit(0) |
| 265 | |
[email protected] | b0e621f | 2009-08-25 21:01:51 | [diff] [blame] | 266 | print sendmsg |
[email protected] | de24345 | 2009-10-06 21:02:56 | [diff] [blame] | 267 | TryChange(args, file_list) |