blob: d5b87e9b3d5eff4875623c9a30fa75735510c269 [file] [log] [blame]
smut@google.com5f0788b2015-06-09 00:04:51 +00001#!/usr/bin/env python
2# Copyright (c) 2015 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"""Tool for interacting with Buildbucket.
7
8Usage:
9 $ depot-tools-auth login https://cr-buildbucket.appspot.com
10 $ buildbucket.py \
11 put \
12 --bucket master.tryserver.chromium.linux \
13 --builder my-builder \
14
15 Puts a build into buildbucket for my-builder on tryserver.chromium.linux.
16"""
17
Raul Tambre80ee78e2019-05-06 22:41:05 +000018from __future__ import print_function
19
smut@google.com5f0788b2015-06-09 00:04:51 +000020import argparse
21import json
22import urlparse
23import os
24import sys
Sergiy Belozorov64a001e2018-12-03 23:19:51 +000025import time
smut@google.com5f0788b2015-06-09 00:04:51 +000026
27from third_party import httplib2
28
29import auth
30
31
32BUILDBUCKET_URL = 'https://cr-buildbucket.appspot.com'
smut@google.com8e6e1e62015-08-07 21:52:18 +000033BUILDBUCKET_API_URL = urlparse.urljoin(
smut@google.com5f0788b2015-06-09 00:04:51 +000034 BUILDBUCKET_URL,
35 '_ah/api/buildbucket/v1/builds',
36)
37
38
John Budorickec40d022018-12-10 17:59:09 +000039def add_common_arguments(parser):
40 parser.add_argument(
41 '--response-json',
42 help=(
43 'A path to which the response JSON will be written. '
44 'If no valid JSON is received, nothing will be written.'
45 )
46 )
47
48
smut@google.com5f0788b2015-06-09 00:04:51 +000049def main(argv):
50 parser = argparse.ArgumentParser()
51 parser.add_argument(
52 '-v',
53 '--verbose',
54 action='store_true',
55 )
56 subparsers = parser.add_subparsers(dest='command')
John Budorickec40d022018-12-10 17:59:09 +000057
smut@google.com8e6e1e62015-08-07 21:52:18 +000058 get_parser = subparsers.add_parser('get')
John Budorickec40d022018-12-10 17:59:09 +000059 add_common_arguments(get_parser)
smut@google.com8e6e1e62015-08-07 21:52:18 +000060 get_parser.add_argument(
61 '--id',
62 help='The ID of the build to get the status of.',
63 required=True,
64 )
John Budorickec40d022018-12-10 17:59:09 +000065
smut@google.com5f0788b2015-06-09 00:04:51 +000066 put_parser = subparsers.add_parser('put')
John Budorickec40d022018-12-10 17:59:09 +000067 add_common_arguments(put_parser)
smut@google.com5f0788b2015-06-09 00:04:51 +000068 put_parser.add_argument(
smut@google.com1da6b032015-06-09 00:16:03 +000069 '-b',
smut@google.com5f0788b2015-06-09 00:04:51 +000070 '--bucket',
71 help=(
72 'The bucket to schedule the build on. Typically the master name, e.g.'
73 ' master.tryserver.chromium.linux.'
74 ),
75 required=True,
76 )
77 put_parser.add_argument(
smut@google.comc1ae89e2015-06-22 23:06:44 +000078 '-c',
79 '--changes',
80 help='A flie to load a JSON list of changes dicts from.',
81 )
82 put_parser.add_argument(
smut@google.com5f0788b2015-06-09 00:04:51 +000083 '-n',
84 '--builder-name',
85 help='The builder to schedule the build on.',
86 required=True,
87 )
88 put_parser.add_argument(
89 '-p',
90 '--properties',
Sergiy Byelozyorovc3975e52018-07-09 22:30:09 +000091 help=(
92 'A file to load a JSON dict of properties from. Use "-" to pipe JSON '
93 'from another command.'
94 ),
smut@google.com5f0788b2015-06-09 00:04:51 +000095 )
John Budorickec40d022018-12-10 17:59:09 +000096
Sergiy Belozorov64a001e2018-12-03 23:19:51 +000097 retry_parser = subparsers.add_parser('retry')
John Budorickec40d022018-12-10 17:59:09 +000098 add_common_arguments(retry_parser)
Sergiy Belozorov64a001e2018-12-03 23:19:51 +000099 retry_parser.add_argument(
100 '--id',
101 help='The ID of the build to retry.',
102 required=True,
103 )
104
smut@google.com5f0788b2015-06-09 00:04:51 +0000105 args = parser.parse_args()
smut@google.com5f0788b2015-06-09 00:04:51 +0000106
smut@google.com8e6e1e62015-08-07 21:52:18 +0000107 body = None
smut@google.comc1ae89e2015-06-22 23:06:44 +0000108
smut@google.com8e6e1e62015-08-07 21:52:18 +0000109 if args.command == 'get':
110 method = 'GET'
111 url = '%s/%s' % (BUILDBUCKET_API_URL, args.id)
112 elif args.command == 'put':
113 changes = []
114 if args.changes:
115 try:
116 with open(args.changes) as fp:
117 changes.extend(json.load(fp))
118 except (TypeError, ValueError):
119 sys.stderr.write('%s contained invalid JSON list.\n' % args.changes)
120 raise
121
122 properties = {}
123 if args.properties:
124 try:
Sergiy Byelozyorovc3975e52018-07-09 22:30:09 +0000125 # Allow using pipes to stream properties from another command, e.g.
126 # echo '{"foo": "bar", "baz": 42}' | buildbucket.py -p -
127 if args.properties == '-':
128 properties.update(json.load(sys.stdin))
129 else:
130 with open(args.properties) as fp:
131 properties.update(json.load(fp))
smut@google.com8e6e1e62015-08-07 21:52:18 +0000132 except (TypeError, ValueError):
133 sys.stderr.write('%s contained invalid JSON dict.\n' % args.properties)
134 raise
135
136 body = json.dumps({
137 'bucket': args.bucket,
138 'parameters_json': json.dumps({
139 'builder_name': args.builder_name,
140 'changes': changes,
141 'properties': properties,
142 }),
143 })
144 method = 'PUT'
145 url = BUILDBUCKET_API_URL
Sergiy Belozorov64a001e2018-12-03 23:19:51 +0000146 elif args.command == 'retry':
147 method = 'PUT'
148 url = '%s/%s/retry' % (BUILDBUCKET_API_URL, args.id)
smut@google.com5f0788b2015-06-09 00:04:51 +0000149
150 authenticator = auth.get_authenticator_for_host(
151 BUILDBUCKET_URL,
152 auth.make_auth_config(use_oauth2=True),
153 )
154 http = authenticator.authorize(httplib2.Http())
155 http.force_exception_to_status_code = True
Sergiy Belozorov64a001e2018-12-03 23:19:51 +0000156
157 if args.verbose:
Raul Tambre80ee78e2019-05-06 22:41:05 +0000158 print('Request URL:', url)
159 print('Request method:', method)
160 print('Request body:', body)
Sergiy Belozorov64a001e2018-12-03 23:19:51 +0000161
smut@google.com5f0788b2015-06-09 00:04:51 +0000162 response, content = http.request(
smut@google.com8e6e1e62015-08-07 21:52:18 +0000163 url,
164 method,
165 body=body,
smut@google.com5f0788b2015-06-09 00:04:51 +0000166 headers={'Content-Type': 'application/json'},
167 )
168
169 if args.verbose:
Raul Tambre80ee78e2019-05-06 22:41:05 +0000170 print('Response:', response)
171 print('Content:', content)
smut@google.com5f0788b2015-06-09 00:04:51 +0000172
Sergiy Belozorov64a001e2018-12-03 23:19:51 +0000173 try:
John Budorickec40d022018-12-10 17:59:09 +0000174 content_json = json.loads(content)
175 if args.response_json:
176 with open(args.response_json, 'w') as response_json_file:
177 response_json_file.write(content)
178 build_url = content_json['build']['url']
Sergiy Belozorov64a001e2018-12-03 23:19:51 +0000179 except (ValueError, TypeError, KeyError):
180 pass
181 else:
Raul Tambre80ee78e2019-05-06 22:41:05 +0000182 print('Build: %s' % build_url)
Michael Achenbach0639cbc2018-09-18 11:33:14 +0000183
smut@google.com5f0788b2015-06-09 00:04:51 +0000184 return response.status != 200
185
186
187if __name__ == '__main__':
188 sys.exit(main(sys.argv))