33// for details. All rights reserved. Use of this source code is governed by a
44// BSD-style license that can be found in the LICENSE file.
55
6- // Find the newest commit that has a full set of results on the bots.
7-
8- // @dart = 2.9
6+ // Find the newest commit that has a full set of results on the builders.
97
108import 'dart:async' ;
119import 'dart:convert' ;
@@ -17,13 +15,13 @@ import 'package:glob/glob.dart';
1715
1816void main (List <String > args) async {
1917 final parser = new ArgParser ();
20- parser.addMultiOption ("bot " ,
18+ parser.addMultiOption ("builder " ,
2119 abbr: "b" ,
22- help: "Select the bots matching the glob pattern [option is repeatable]" ,
20+ help: "Select the builders matching the glob [option is repeatable]" ,
2321 splitCommas: false );
2422 parser.addOption ("branch" ,
2523 abbr: "B" ,
26- help: "Select the bots building this branch" ,
24+ help: "Select the builders building this branch" ,
2725 defaultsTo: "master" );
2826 parser.addOption ("count" ,
2927 abbr: "c" , help: "List this many commits" , defaultsTo: "1" );
@@ -33,7 +31,7 @@ void main(List<String> args) async {
3331 if (options["help" ]) {
3432 print ("""
3533Usage: find_base_commit.dart [OPTION]...
36- Find the newest commit that has a full set of results on the bots .
34+ Find the newest commit that has a full set of results on the builders .
3735
3836The options are as follows:
3937
@@ -43,34 +41,50 @@ ${parser.usage}""");
4341
4442 int count = int .parse (options["count" ]);
4543 final globs = new List <Glob >.from (
46- options["bot " ].map ((String pattern) => new Glob (pattern)));
44+ options["builder " ].map ((String pattern) => new Glob (pattern)));
4745
4846 // Download the most recent builds from buildbucket.
49- int maxBuilds = 1000 ;
50- final url = Uri .parse (
51- "https://cr-buildbucket.appspot.com/_ah/api/buildbucket/v1/search"
52- "?bucket=luci.dart.ci.sandbox"
53- "&max_builds=$maxBuilds "
54- "&status=COMPLETED"
55- "&fields=builds(url%2Cparameters_json)" );
47+ const maxBuilds = 1000 ;
48+ final url = Uri .parse ("https://cr-buildbucket.appspot.com"
49+ "/prpc/buildbucket.v2.Builds/SearchBuilds" );
5650 const maxRetries = 3 ;
5751 const timeout = const Duration (seconds: 30 );
58- Map <String , dynamic > object;
52+ final query = jsonEncode ({
53+ "predicate" : {
54+ "builder" : {"project" : "dart" , "bucket" : "ci.sandbox" },
55+ "status" : "ENDED_MASK"
56+ },
57+ "pageSize" : maxBuilds,
58+ "fields" : "builds.*.builder.builder,builds.*.input"
59+ });
60+ late Map <String , dynamic > searchResult;
5961 for (int i = 1 ; i <= maxRetries; i++ ) {
6062 try {
6163 final client = new HttpClient ();
62- final request = await client.getUrl (url).timeout (timeout);
64+ final request = await client.postUrl (url).timeout (timeout)
65+ ..headers.contentType = ContentType .json
66+ ..headers.add (HttpHeaders .acceptHeader, ContentType .json)
67+ ..write (query);
6368 final response = await request.close ().timeout (timeout);
64- object = await response
69+ if (response.statusCode != 200 ) {
70+ print ("Failed to search for builds: "
71+ "${response .statusCode }:${response .reasonPhrase }" );
72+ exit (1 );
73+ }
74+ const prefix = ")]}'" ;
75+ searchResult = await (response
6576 .cast <List <int >>()
6677 .transform (new Utf8Decoder ())
78+ .map ((event) =>
79+ event.startsWith (prefix) ? event.substring (prefix.length) : event)
6780 .transform (new JsonDecoder ())
81+ .cast <Map <String , dynamic >>()
6882 .first
69- .timeout (timeout);
83+ .timeout (timeout)) ;
7084 client.close ();
7185 break ;
7286 } on TimeoutException catch (e) {
73- final inSeconds = e.duration.inSeconds;
87+ final inSeconds = e.duration? .inSeconds;
7488 stderr.writeln (
7589 "Attempt $i of $maxRetries timed out after $inSeconds seconds" );
7690 if (i == maxRetries) {
@@ -81,57 +95,61 @@ ${parser.usage}""");
8195 }
8296
8397 // Locate the builds we're interested in and map them to each commit. The
84- // builds returned by the API are sorted with the newest first. Since bots
98+ // builds returned by the API are sorted with the newest first. Since builders
8599 // don't build back in time and always build the latest commit whenever they
86100 // can, the first time we see a commit, we know it's newer than all commits
87- // we haven't seen yet. The insertion order into the botsForCommits map will
88- // then sorted with the newest commit first.
89- final builds = object["builds" ];
90- final botsForCommits = < String , Set <String >> {};
101+ // we haven't seen yet. The insertion order into the buildersForCommits map
102+ // will then sorted with the newest commit first.
103+ final builds = searchResult["builds" ];
104+ if (builds == null ) {
105+ print ("No builds found" );
106+ exit (1 );
107+ }
108+ final buildersForCommits = < String , Set <String >> {};
91109 for (final build in builds) {
92- final parameters = jsonDecode ( build["parameters_json" ]) ;
93- final bot = parameters[ "builder_name" ];
94- if (bot .endsWith ("-beta" ) ||
95- bot .endsWith ("-dev" ) ||
96- bot .endsWith ("-stable" )) {
110+ final builder = build["builder" ] ? [ "builder" ] ;
111+ if (builder is ! String ||
112+ builder .endsWith ("-beta" ) ||
113+ builder .endsWith ("-dev" ) ||
114+ builder .endsWith ("-stable" )) {
97115 // Ignore the release builders. The -try builders aren't in the
98116 // bucket we're reading.
99117 continue ;
100118 }
101- if (globs.isNotEmpty && ! globs.any ((glob) => glob.matches (bot ))) {
102- // Filter way bots we're not interested in.
119+ if (globs.isNotEmpty && ! globs.any ((glob) => glob.matches (builder ))) {
120+ // Filter way builders we're not interested in.
103121 continue ;
104122 }
105- final properties = parameters["properties" ];
106- final branch = properties["branch" ];
107- if (branch != null && branch != "refs/heads/${options ['branch' ]}" ) {
108- // Ignore bots that are building the wrong branch.
123+ final input = build["input" ]? ["gitilesCommit" ];
124+ if (input == null ) {
125+ // Ignore builds not triggered by a commit, e.g. fuzz-linux.
109126 continue ;
110127 }
111- final commit = properties[ "revision " ];
112- if (commit == null ) {
113- // Ignore bots that aren't commit based, e.g. fuzz-linux .
128+ final ref = input[ "ref " ];
129+ if (ref != "refs/heads/${ options [ 'branch' ]}" ) {
130+ // Ignore builds on the wrong branch .
114131 continue ;
115132 }
116- final botsForCommit =
117- botsForCommits.putIfAbsent (commit, () => new Set <String >());
118- botsForCommit.add (bot);
133+ final commit = input["id" ] as String ;
134+ final buildersForCommit =
135+ buildersForCommits.putIfAbsent (commit, () => new Set <String >());
136+ buildersForCommit.add (builder);
119137 }
120138
121- if (botsForCommits .isEmpty) {
122- print ("Failed to locate any commits having run on the bots " );
139+ if (buildersForCommits .isEmpty) {
140+ print ("Failed to locate any commits having run on the builders " );
123141 exitCode = 1 ;
124142 return ;
125143 }
126144
127145 int maxBots = 0 ;
128- for (final commit in botsForCommits.keys ) {
129- maxBots = max (maxBots, botsForCommits[commit] .length);
146+ for (final builders in buildersForCommits.values ) {
147+ maxBots = max (maxBots, builders .length);
130148 }
131149
132- // List commits run on the most bots .
133- for (final commit in botsForCommits .keys
134- .where ((commit) => botsForCommits [commit].length == maxBots)
150+ // List commits run on the most builders .
151+ for (final commit in buildersForCommits .keys
152+ .where ((commit) => buildersForCommits [commit]! .length == maxBots)
135153 .take (count)) {
136154 print (commit);
137155 }
0 commit comments