blob: 9f5a3e00a512b56995c0d668d4516889fcd91afc [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
18import argparse
19import json
20import urlparse
21import os
22import sys
Sergiy Belozorov64a001e2018-12-03 23:19:51 +000023import time
smut@google.com5f0788b2015-06-09 00:04:51 +000024
25from third_party import httplib2
26
27import auth
28
29
30BUILDBUCKET_URL = 'https://cr-buildbucket.appspot.com'
smut@google.com8e6e1e62015-08-07 21:52:18 +000031BUILDBUCKET_API_URL = urlparse.urljoin(
smut@google.com5f0788b2015-06-09 00:04:51 +000032 BUILDBUCKET_URL,
33 '_ah/api/buildbucket/v1/builds',
34)
35
36
37def main(argv):
38 parser = argparse.ArgumentParser()
39 parser.add_argument(
40 '-v',
41 '--verbose',
42 action='store_true',
43 )
44 subparsers = parser.add_subparsers(dest='command')
smut@google.com8e6e1e62015-08-07 21:52:18 +000045 get_parser = subparsers.add_parser('get')
46 get_parser.add_argument(
47 '--id',
48 help='The ID of the build to get the status of.',
49 required=True,
50 )
smut@google.com5f0788b2015-06-09 00:04:51 +000051 put_parser = subparsers.add_parser('put')
52 put_parser.add_argument(
smut@google.com1da6b032015-06-09 00:16:03 +000053 '-b',
smut@google.com5f0788b2015-06-09 00:04:51 +000054 '--bucket',
55 help=(
56 'The bucket to schedule the build on. Typically the master name, e.g.'
57 ' master.tryserver.chromium.linux.'
58 ),
59 required=True,
60 )
61 put_parser.add_argument(
smut@google.comc1ae89e2015-06-22 23:06:44 +000062 '-c',
63 '--changes',
64 help='A flie to load a JSON list of changes dicts from.',
65 )
66 put_parser.add_argument(
smut@google.com5f0788b2015-06-09 00:04:51 +000067 '-n',
68 '--builder-name',
69 help='The builder to schedule the build on.',
70 required=True,
71 )
72 put_parser.add_argument(
73 '-p',
74 '--properties',
Sergiy Byelozyorovc3975e52018-07-09 22:30:09 +000075 help=(
76 'A file to load a JSON dict of properties from. Use "-" to pipe JSON '
77 'from another command.'
78 ),
smut@google.com5f0788b2015-06-09 00:04:51 +000079 )
Sergiy Belozorov64a001e2018-12-03 23:19:51 +000080 retry_parser = subparsers.add_parser('retry')
81 retry_parser.add_argument(
82 '--id',
83 help='The ID of the build to retry.',
84 required=True,
85 )
86
smut@google.com5f0788b2015-06-09 00:04:51 +000087 args = parser.parse_args()
smut@google.com5f0788b2015-06-09 00:04:51 +000088
smut@google.com8e6e1e62015-08-07 21:52:18 +000089 body = None
smut@google.comc1ae89e2015-06-22 23:06:44 +000090
smut@google.com8e6e1e62015-08-07 21:52:18 +000091 if args.command == 'get':
92 method = 'GET'
93 url = '%s/%s' % (BUILDBUCKET_API_URL, args.id)
94 elif args.command == 'put':
95 changes = []
96 if args.changes:
97 try:
98 with open(args.changes) as fp:
99 changes.extend(json.load(fp))
100 except (TypeError, ValueError):
101 sys.stderr.write('%s contained invalid JSON list.\n' % args.changes)
102 raise
103
104 properties = {}
105 if args.properties:
106 try:
Sergiy Byelozyorovc3975e52018-07-09 22:30:09 +0000107 # Allow using pipes to stream properties from another command, e.g.
108 # echo '{"foo": "bar", "baz": 42}' | buildbucket.py -p -
109 if args.properties == '-':
110 properties.update(json.load(sys.stdin))
111 else:
112 with open(args.properties) as fp:
113 properties.update(json.load(fp))
smut@google.com8e6e1e62015-08-07 21:52:18 +0000114 except (TypeError, ValueError):
115 sys.stderr.write('%s contained invalid JSON dict.\n' % args.properties)
116 raise
117
118 body = json.dumps({
119 'bucket': args.bucket,
120 'parameters_json': json.dumps({
121 'builder_name': args.builder_name,
122 'changes': changes,
123 'properties': properties,
124 }),
125 })
126 method = 'PUT'
127 url = BUILDBUCKET_API_URL
Sergiy Belozorov64a001e2018-12-03 23:19:51 +0000128 elif args.command == 'retry':
129 method = 'PUT'
130 url = '%s/%s/retry' % (BUILDBUCKET_API_URL, args.id)
smut@google.com5f0788b2015-06-09 00:04:51 +0000131
132 authenticator = auth.get_authenticator_for_host(
133 BUILDBUCKET_URL,
134 auth.make_auth_config(use_oauth2=True),
135 )
136 http = authenticator.authorize(httplib2.Http())
137 http.force_exception_to_status_code = True
Sergiy Belozorov64a001e2018-12-03 23:19:51 +0000138
139 if args.verbose:
140 print 'Request URL:', url
141 print 'Request method:', method
142 print 'Request body:', body
143
smut@google.com5f0788b2015-06-09 00:04:51 +0000144 response, content = http.request(
smut@google.com8e6e1e62015-08-07 21:52:18 +0000145 url,
146 method,
147 body=body,
smut@google.com5f0788b2015-06-09 00:04:51 +0000148 headers={'Content-Type': 'application/json'},
149 )
150
151 if args.verbose:
Sergiy Belozorov64a001e2018-12-03 23:19:51 +0000152 print 'Response:', response
153 print 'Content:', content
smut@google.com5f0788b2015-06-09 00:04:51 +0000154
Sergiy Belozorov64a001e2018-12-03 23:19:51 +0000155 try:
156 build_url = json.loads(content)['build']['url']
157 except (ValueError, TypeError, KeyError):
158 pass
159 else:
Michael Achenbach0639cbc2018-09-18 11:33:14 +0000160 print 'Build triggered on: %s' % build_url
161
smut@google.com5f0788b2015-06-09 00:04:51 +0000162 return response.status != 200
163
164
165if __name__ == '__main__':
166 sys.exit(main(sys.argv))