blob: 17112aac0dbc185e2446251ba28e46cfd9530913 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001#
2# Copyright (C) 2008 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import getpass
17import os
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070018import sys
19from tempfile import mkstemp
20
21from codereview.proto_client import HttpRpc, Proxy
22from codereview.review_pb2 import ReviewService_Stub
23from codereview.upload_bundle_pb2 import *
24from git_command import GitCommand
25from error import UploadError
26
27try:
28 import readline
29except ImportError:
30 pass
31
32MAX_SEGMENT_SIZE = 1020 * 1024
33
34def _GetRpcServer(email, server, save_cookies):
35 """Returns an RpcServer.
36
37 Returns:
38 A new RpcServer, on which RPC calls can be made.
39 """
40
41 def GetUserCredentials():
42 """Prompts the user for a username and password."""
43 e = email
44 if e is None:
45 e = raw_input("Email: ").strip()
46 password = getpass.getpass("Password for %s: " % e)
47 return (e, password)
48
49 # If this is the dev_appserver, use fake authentication.
50 lc_server = server.lower()
51 if lc_server == "localhost" or lc_server.startswith("localhost:"):
52 if email is None:
53 email = "test@example.com"
54 server = HttpRpc(
55 server,
56 lambda: (email, "password"),
57 extra_headers={"Cookie":
58 'dev_appserver_login="%s:False"' % email})
59 # Don't try to talk to ClientLogin.
60 server.authenticated = True
61 return server
62
63 if save_cookies:
64 cookie_file = ".gerrit_cookies"
65 else:
66 cookie_file = None
67
68 return HttpRpc(server, GetUserCredentials,
69 cookie_file=cookie_file)
70
71def UploadBundle(project,
72 server,
73 email,
74 dest_project,
75 dest_branch,
76 src_branch,
77 bases,
Joe Onorato2896a792008-11-17 16:56:36 -050078 people,
Shawn O. Pearcec99883f2008-11-11 17:12:43 -080079 replace_changes = None,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070080 save_cookies=True):
81
82 srv = _GetRpcServer(email, server, save_cookies)
83 review = Proxy(ReviewService_Stub(srv))
84 tmp_fd, tmp_bundle = mkstemp(".bundle", ".gpq")
85 os.close(tmp_fd)
86
87 srcid = project.bare_git.rev_parse(src_branch)
88 revlist = project._revlist(src_branch, *bases)
89
90 if srcid not in revlist:
91 # This can happen if src_branch is an annotated tag
92 #
93 revlist.append(srcid)
94 revlist_size = len(revlist) * 42
95
96 try:
97 cmd = ['bundle', 'create', tmp_bundle, src_branch]
98 cmd.extend(bases)
99 if GitCommand(project, cmd).Wait() != 0:
100 raise UploadError('cannot create bundle')
101 fd = open(tmp_bundle, "rb")
102
103 bundle_id = None
104 segment_id = 0
105 next_data = fd.read(MAX_SEGMENT_SIZE - revlist_size)
106
107 while True:
108 this_data = next_data
109 next_data = fd.read(MAX_SEGMENT_SIZE)
110 segment_id += 1
111
112 if bundle_id is None:
113 req = UploadBundleRequest()
114 req.dest_project = str(dest_project)
115 req.dest_branch = str(dest_branch)
Joe Onorato2896a792008-11-17 16:56:36 -0500116 for e in people[0]:
117 req.reviewers.append(e)
118 for e in people[1]:
119 req.cc.append(e)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700120 for c in revlist:
121 req.contained_object.append(c)
Shawn O. Pearce34d237f2008-11-12 18:37:18 -0800122 if replace_changes:
123 for change_id,commit_id in replace_changes.iteritems():
124 r = req.replace.add()
125 r.change_id = change_id
126 r.object_id = commit_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700127 else:
128 req = UploadBundleContinue()
129 req.bundle_id = bundle_id
130 req.segment_id = segment_id
131
132 req.bundle_data = this_data
133 if len(next_data) > 0:
134 req.partial_upload = True
135 else:
136 req.partial_upload = False
137
138 if bundle_id is None:
139 rsp = review.UploadBundle(req)
140 else:
141 rsp = review.ContinueBundle(req)
142
143 if rsp.status_code == UploadBundleResponse.CONTINUE:
144 bundle_id = rsp.bundle_id
145 elif rsp.status_code == UploadBundleResponse.RECEIVED:
146 bundle_id = rsp.bundle_id
147 return bundle_id
148 else:
149 if rsp.status_code == UploadBundleResponse.UNKNOWN_PROJECT:
150 reason = 'unknown project "%s"' % dest_project
151 elif rsp.status_code == UploadBundleResponse.UNKNOWN_BRANCH:
152 reason = 'unknown branch "%s"' % dest_branch
153 elif rsp.status_code == UploadBundleResponse.UNKNOWN_BUNDLE:
154 reason = 'unknown bundle'
155 elif rsp.status_code == UploadBundleResponse.NOT_BUNDLE_OWNER:
156 reason = 'not bundle owner'
157 elif rsp.status_code == UploadBundleResponse.BUNDLE_CLOSED:
158 reason = 'bundle closed'
159 elif rsp.status_code == UploadBundleResponse.UNAUTHORIZED_USER:
160 reason = ('Unauthorized user. Visit http://%s/hello to sign up.'
161 % server)
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800162 elif rsp.status_code == UploadBundleResponse.UNKNOWN_CHANGE:
163 reason = 'invalid change id'
164 elif rsp.status_code == UploadBundleResponse.CHANGE_CLOSED:
165 reason = 'one or more changes are closed'
Joe Onorato2896a792008-11-17 16:56:36 -0500166 elif rsp.status_code == UploadBundleResponse.UNKNOWN_EMAIL:
167 emails = [x for x in rsp.invalid_reviewers] + [
168 x for x in rsp.invalid_cc]
169 reason = 'invalid email addresses: %s' % ", ".join(emails)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700170 else:
171 reason = 'unknown error ' + str(rsp.status_code)
172 raise UploadError(reason)
173 finally:
174 os.unlink(tmp_bundle)