cherry/testtiming: update dependencies

Update the dependencies to the latest versions.

Use the latest LUCI gRPC API.

Apparently unauthenticated Gitiles API may fail with 503. Fall
back to query Gerrit.

Change-Id: If6dc6c0ced937567b27c15d44326f49ef93eae5a
Reviewed-on: https://go-review.googlesource.com/c/scratch/+/851925
Reviewed-by: Cherry Mui <cherryyz@google.com>
TryBot-Bypass: Cherry Mui <cherryyz@google.com>
Commit-Queue: Cherry Mui <cherryyz@google.com>
diff --git a/cherry/testtiming/go.mod b/cherry/testtiming/go.mod
index 19d33eb..2179c86 100644
--- a/cherry/testtiming/go.mod
+++ b/cherry/testtiming/go.mod
@@ -1,23 +1,23 @@
 module golang.org/x/scratch/cherry/testtiming
 
-go 1.22
+go 1.26.10
 
 require (
-	go.chromium.org/luci v0.0.0-20240716011143-b5eb7a221b66
-	golang.org/x/sync v0.7.0
-	google.golang.org/protobuf v1.34.2
+	go.chromium.org/luci v0.0.0-20261020175336-14cab3291a1b
+	golang.org/x/sync v0.24.0
+	google.golang.org/protobuf v1.36.12
 )
 
 require (
-	github.com/golang/mock v1.6.0 // indirect
+	github.com/golang/mock v1.7.0-rc.1 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/julienschmidt/httprouter v1.3.0 // indirect
-	github.com/klauspost/compress v1.17.8 // indirect
-	golang.org/x/net v0.23.0 // indirect
-	golang.org/x/sys v0.18.0 // indirect
-	golang.org/x/text v0.14.0 // indirect
-	google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 // indirect
-	google.golang.org/grpc v1.61.0 // indirect
+	github.com/klauspost/compress v1.20.3 // indirect
+	golang.org/x/net v0.61.0 // indirect
+	golang.org/x/sys v0.49.0 // indirect
+	golang.org/x/text v0.43.0 // indirect
+	google.golang.org/genproto v0.0.0-20261019180522-ce908d29299b // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20261019180522-ce908d29299b // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20261019180522-ce908d29299b // indirect
+	google.golang.org/grpc v1.84.0 // indirect
 )
diff --git a/cherry/testtiming/go.sum b/cherry/testtiming/go.sum
index 3f62d76..c6bc512 100644
--- a/cherry/testtiming/go.sum
+++ b/cherry/testtiming/go.sum
@@ -1,9 +1,12 @@
 github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
 github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
+github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
+github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
 github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
 github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
 github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
 github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
 github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
@@ -12,50 +15,81 @@
 github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
 github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
 github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
+github.com/klauspost/compress v1.20.3 h1:zWNoFqq/lSuYQRim3dUXlXeA0IqwuZGicdG5ZTfY5j4=
+github.com/klauspost/compress v1.20.3/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
 github.com/smarty/assertions v1.15.1 h1:812oFiXI+G55vxsFf+8bIZ1ux30qtkdqzKbEFwyX3Tk=
 github.com/smarty/assertions v1.15.1/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
+github.com/smarty/assertions v1.16.0 h1:EvHNkdRA4QHMrn75NZSoUQ/mAUXAYWfatfB01yTCzfY=
 github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
 github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 go.chromium.org/luci v0.0.0-20240716011143-b5eb7a221b66 h1:7M08VAaHGjcRE9gonYdmvUL8PKKek9NUZ7mFSjZyJA4=
 go.chromium.org/luci v0.0.0-20240716011143-b5eb7a221b66/go.mod h1:VKWpjBb/iM+b62Tkkvb8Fs6bKxixITrPUpuImWvecvY=
+go.chromium.org/luci v0.0.0-20261020175336-14cab3291a1b h1:WoGAeCJ/pxgA3wJWP85GO4Tz+v54GgrcqCZspgny6tI=
+go.chromium.org/luci v0.0.0-20261020175336-14cab3291a1b/go.mod h1:b4r5vud5fWGkuHUdKB1Z+XIdCU/e9OiCVLrGxWuHzqM=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
 golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.61.0 h1:RR+6j/BTBCrggOLugxa+66R8FvoN6MHlzDlZIbN9YFw=
+golang.org/x/net v0.61.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
 golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
+golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA=
+golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc=
+golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
 google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
+google.golang.org/genproto v0.0.0-20261019180522-ce908d29299b h1:+t2qGqt6R8CO622uiX1JpnRvRvSOQoVRV0DiPzUjKXg=
+google.golang.org/genproto v0.0.0-20261019180522-ce908d29299b/go.mod h1:XRIfc1yr5xsbF8xFw5+g7SEmLJX/eQ7MMazTRbnMMvI=
 google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9 h1:4++qSzdWBUy9/2x8L5KZgwZw+mjJZ2yDSCGMVM0YzRs=
 google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:PVreiBMirk8ypES6aw9d4p6iiBNSIfZEBqr3UGoAi2E=
+google.golang.org/genproto/googleapis/api v0.0.0-20261019180522-ce908d29299b h1:nB3spRIaSPtxJpxb0k41yljROvm2Jrwfs2pSNdKS5y8=
+google.golang.org/genproto/googleapis/api v0.0.0-20261019180522-ce908d29299b/go.mod h1:9N4vtjEwvgQKEdlFVQO0M44ErhecQ9DCSQfGoZ51M6w=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 h1:hZB7eLIaYlW9qXRfCq/qDaPdbeY3757uARz5Vvfv+cY=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20261019180522-ce908d29299b h1:6WNWHZk91dp4gZ25642OALkuG1UVNZbwENJD19DnB8k=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20261019180522-ce908d29299b/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc=
 google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
 google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
+google.golang.org/grpc v1.84.0 h1:soMyaPJ8pAak5PIQ0DGBUir0XRo2fRoMqhNWMLlLxO0=
+google.golang.org/grpc v1.84.0/go.mod h1:ljCht0DrxQrXBDRTZp52Qxh3Ffk8CdYm2sj4O2QN2C0=
 google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
 google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
+google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
+google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
diff --git a/cherry/testtiming/luci.go b/cherry/testtiming/luci.go
index 09da1c7..960bdab 100644
--- a/cherry/testtiming/luci.go
+++ b/cherry/testtiming/luci.go
@@ -4,6 +4,14 @@
 
 // An ad-hoc tool to query test timing data from LUCI.
 //
+// Usage:
+//
+//	go run luci.go [-repo repo] [-branch branch] [-builder builder] -test pkg.TestName
+//
+// Example:
+//
+//	go run luci.go -builder gotip-linux-amd64 -test math/big.TestFloatSqrt
+//
 // Output CSV with the following columns:
 //
 //	commit hash, commit time, [builder,] status, pass duration, fail duration
@@ -13,18 +21,22 @@
 package main
 
 import (
+	"bytes"
 	"context"
 	"encoding/json"
 	"flag"
 	"fmt"
+	"io"
 	"log"
 	"net/http"
+	"net/url"
 	"regexp"
 	"slices"
 	"strings"
 	"time"
 
 	bbpb "go.chromium.org/luci/buildbucket/proto"
+	bbgrpcpb "go.chromium.org/luci/buildbucket/proto/grpcpb"
 	"go.chromium.org/luci/common/api/gitiles"
 	gpb "go.chromium.org/luci/common/proto/gitiles"
 	"go.chromium.org/luci/grpc/prpc"
@@ -34,16 +46,20 @@
 	"google.golang.org/protobuf/types/known/timestamppb"
 )
 
-const resultDBHost = "results.api.cr.dev"
-const crBuildBucketHost = "cr-buildbucket.appspot.com"
-const gitilesHost = "go.googlesource.com"
+const (
+	resultDBHost      = "results.api.luci.app"
+	oldResultDBHost   = "results.api.cr.dev"
+	crBuildBucketHost = "cr-buildbucket.appspot.com"
+	gitilesHost       = "go.googlesource.com"
+	gerritHost        = "go-review.googlesource.com"
+)
 
 // LUCIClient is a LUCI client.
 type LUCIClient struct {
 	HTTPClient     *http.Client
 	GitilesClient  gpb.GitilesClient
-	BuildsClient   bbpb.BuildsClient
-	BuildersClient bbpb.BuildersClient
+	BuildsClient   bbgrpcpb.BuildsClient
+	BuildersClient bbgrpcpb.BuildersClient
 	ResultDBClient rdbpb.ResultDBClient
 
 	// TraceSteps controls whether to log each step name as it's executed.
@@ -63,11 +79,11 @@
 	if err != nil {
 		log.Fatal(err)
 	}
-	buildsClient := bbpb.NewBuildsClient(&prpc.Client{
+	buildsClient := bbgrpcpb.NewBuildsClient(&prpc.Client{
 		C:    c,
 		Host: crBuildBucketHost,
 	})
-	buildersClient := bbpb.NewBuildersClient(&prpc.Client{
+	buildersClient := bbgrpcpb.NewBuildersClient(&prpc.Client{
 		C:    c,
 		Host: crBuildBucketHost,
 	})
@@ -160,7 +176,8 @@
 		PageToken:  pageToken,
 	})
 	if err != nil {
-		log.Fatal(err)
+		log.Printf("GitilesClient.Log(%q, %q) failed: %v; falling back to Gerrit", repo, branch, err)
+		return c.listCommitsFromGerrit(ctx, repo, branch, since)
 	}
 	for _, c := range resp.GetLog() {
 		commitTime := c.GetCommitter().GetTime().AsTime()
@@ -180,6 +197,61 @@
 	return commits
 }
 
+func (c *LUCIClient) listCommitsFromGerrit(ctx context.Context, repo, branch string, since time.Time) []Commit {
+	var commits []Commit
+	after := since.Add(-24 * time.Hour).UTC().Format("2006-01-02")
+	for start := 0; ; {
+		u := fmt.Sprintf("https://%s/changes/?q=project:%s+branch:%s+status:merged+mergedafter:%s&o=CURRENT_REVISION&n=500&S=%d",
+			gerritHost, url.QueryEscape(repo), url.QueryEscape(branch), after, start)
+		req, err := http.NewRequestWithContext(ctx, "GET", u, nil)
+		if err != nil {
+			log.Fatal(err)
+		}
+		resp, err := c.HTTPClient.Do(req)
+		if err != nil {
+			log.Fatal(err)
+		}
+		body, err := io.ReadAll(resp.Body)
+		resp.Body.Close()
+		if err != nil {
+			log.Fatal(err)
+		}
+		if resp.StatusCode != http.StatusOK {
+			log.Fatalf("GET %s: %s\n%s", u, resp.Status, body)
+		}
+		body = bytes.TrimPrefix(body, []byte(")]}'\n"))
+		var changes []struct {
+			CurrentRevision string `json:"current_revision"`
+			Submitted       string `json:"submitted"`
+			MoreChanges     bool   `json:"_more_changes"`
+		}
+		if err := json.Unmarshal(body, &changes); err != nil {
+			log.Fatal(err)
+		}
+		for _, ch := range changes {
+			commitTime, err := time.Parse("2006-01-02 15:04:05.000000000", ch.Submitted)
+			if err != nil {
+				log.Fatal(err)
+			}
+			if commitTime.Before(since) {
+				continue
+			}
+			commits = append(commits, Commit{
+				Hash: ch.CurrentRevision,
+				Time: commitTime,
+			})
+		}
+		if len(changes) == 0 || !changes[len(changes)-1].MoreChanges {
+			break
+		}
+		start += len(changes)
+	}
+	slices.SortFunc(commits, func(a, b Commit) int {
+		return b.Time.Compare(a.Time)
+	})
+	return commits
+}
+
 // ListBuilders fetches the list of builders, on the given repo and goBranch.
 // If repo and goBranch are empty, it fetches all builders.
 func (c *LUCIClient) ListBuilders(ctx context.Context, repo, goBranch, builder string) ([]Builder, error) {
@@ -325,7 +397,7 @@
 					}
 				}
 				rdb := b.GetInfra().GetResultdb()
-				if rdb.GetHostname() != resultDBHost {
+				if rdb.GetHostname() != resultDBHost && rdb.GetHostname() != oldResultDBHost {
 					log.Fatalf("ResultDB host mismatch: %s %s %s", rdb.GetHostname(), resultDBHost, buildURL(id))
 				}
 				if b.GetBuilder().GetBuilder() != bName { // sanity check
@@ -435,7 +507,9 @@
 	// LUCI keeps data up to 60 days, so there is no point to go back farther
 	startTime := time.Now().Add(-60 * 24 * time.Hour)
 	dash := &Dashboard{Project: Project{*repo, *branch}}
-	c.ReadBoard(ctx, dash, *builder, startTime)
+	if err := c.ReadBoard(ctx, dash, *builder, startTime); err != nil {
+		log.Fatal(err)
+	}
 
 	printBuilder := func(string) {}
 	if len(dash.Builders) > 1 {