@@ -42,6 +42,7 @@ import (
42
42
"sync"
43
43
"sync/atomic"
44
44
"time"
45
+ "unicode"
45
46
46
47
"go4.org/syncutil"
47
48
grpc "grpc.go4.org"
@@ -1054,7 +1055,8 @@ func (k *tryKey) ChangeTriple() string {
1054
1055
type trySet struct {
1055
1056
// immutable
1056
1057
tryKey
1057
- tryID string // "T" + 9 random hex
1058
+ tryID string // "T" + 9 random hex
1059
+ slowBots []* dashboard.BuildConfig // any opt-in slower builders to run in a trybot run
1058
1060
1059
1061
// wantedAsOf is guarded by statusMu and is used by
1060
1062
// findTryWork. It records the last time this tryKey was still
@@ -1105,14 +1107,19 @@ var testingKnobSkipBuilds bool
1105
1107
func newTrySet (work * apipb.GerritTryWorkItem ) * trySet {
1106
1108
key := tryWorkItemKey (work )
1107
1109
goBranch := key .Branch // assume same as repo's branch for now
1110
+
1108
1111
builders := dashboard .TryBuildersForProject (key .Project , key .Branch , goBranch )
1112
+ slowBots := slowBotsFromComments (work , builders )
1113
+ builders = append (builders , slowBots ... )
1114
+
1109
1115
log .Printf ("Starting new trybot set for %v" , key )
1110
1116
ts := & trySet {
1111
1117
tryKey : key ,
1112
1118
tryID : "T" + randHex (9 ),
1113
1119
trySetState : trySetState {
1114
1120
builds : make ([]* buildStatus , 0 , len (builders )),
1115
1121
},
1122
+ slowBots : slowBots ,
1116
1123
}
1117
1124
1118
1125
// Defensive check that the input is well-formed.
@@ -1237,7 +1244,11 @@ func (ts *trySet) state() trySetState {
1237
1244
// notifyStarting runs in its own goroutine and posts to Gerrit that
1238
1245
// the trybots have started on the user's CL with a link of where to watch.
1239
1246
func (ts * trySet ) notifyStarting () {
1240
- msg := "TryBots beginning. Status page: https://farmer.golang.org/try?commit=" + ts .Commit [:8 ]
1247
+ name := "TryBots"
1248
+ if len (ts .slowBots ) > 0 {
1249
+ name = "SlowBots"
1250
+ }
1251
+ msg := name + " beginning. Status page: https://farmer.golang.org/try?commit=" + ts .Commit [:8 ]
1241
1252
1242
1253
ctx := context .Background ()
1243
1254
if ci , err := gerritClient .GetChangeDetail (ctx , ts .ChangeTriple ()); err == nil {
@@ -1382,20 +1393,38 @@ func (ts *trySet) noteBuildComplete(bs *buildStatus) {
1382
1393
}
1383
1394
1384
1395
if remain == 0 {
1385
- score , msg := 1 , "TryBots are happy."
1386
- if numFail > 0 {
1396
+ name := "TryBots"
1397
+ if len (ts .slowBots ) > 0 {
1398
+ name = "SlowBots"
1399
+ }
1400
+
1401
+ var buf bytes.Buffer
1402
+ var score int
1403
+ if numFail == 0 {
1404
+ score = 1
1405
+ fmt .Fprintf (& buf , "%s are happy.\n " , name )
1406
+ } else if numFail > 0 {
1407
+ score = - 1
1387
1408
ts .mu .Lock ()
1388
1409
errMsg := ts .errMsg .String ()
1389
1410
ts .mu .Unlock ()
1390
- score , msg = - 1 , fmt .Sprintf ("%d of %d TryBots failed:\n %s\n " + failureFooter ,
1391
- numFail , len (ts .builds ), errMsg )
1411
+ fmt .Fprintf (& buf , "%d of %d %s failed:\n %s\n " + failureFooter ,
1412
+ numFail , len (ts .builds ), name , errMsg )
1413
+ }
1414
+ if len (ts .slowBots ) > 0 {
1415
+ fmt .Fprintf (& buf , "Extra slowbot builds that ran:\n " )
1416
+ for _ , c := range ts .slowBots {
1417
+ fmt .Fprintf (& buf , "* %s\n " , c .Name )
1418
+ }
1392
1419
}
1420
+ // TODO: provide a link in the final report that links to a permanent summary page
1421
+ // of which builds ran, and for how long.
1393
1422
if len (benchResults ) > 0 {
1394
1423
// TODO: restore this functionality
1395
1424
// msg += fmt.Sprintf("\nBenchmark results are available at:\nhttps://perf.golang.org/search?q=cl:%d+try:%s", ts.ci.ChangeNumber, ts.tryID)
1396
1425
}
1397
1426
if err := gerritClient .SetReview (context .Background (), ts .ChangeTriple (), ts .Commit , gerrit.ReviewInput {
1398
- Message : msg ,
1427
+ Message : buf . String () ,
1399
1428
Labels : map [string ]int {
1400
1429
"TryBot-Result" : score ,
1401
1430
},
@@ -1857,12 +1886,25 @@ func (st *buildStatus) build() error {
1857
1886
1858
1887
func (st * buildStatus ) isTry () bool { return st .trySet != nil }
1859
1888
1889
+ func (st * buildStatus ) isSlowBot () bool {
1890
+ if st .trySet == nil {
1891
+ return false
1892
+ }
1893
+ for _ , conf := range st .trySet .slowBots {
1894
+ if st .conf == conf {
1895
+ return true
1896
+ }
1897
+ }
1898
+ return false
1899
+ }
1900
+
1860
1901
func (st * buildStatus ) buildRecord () * types.BuildRecord {
1861
1902
rec := & types.BuildRecord {
1862
1903
ID : st .buildID ,
1863
1904
ProcessID : processID ,
1864
1905
StartTime : st .startTime ,
1865
1906
IsTry : st .isTry (),
1907
+ IsExtra : st .isSlowBot (),
1866
1908
GoRev : st .Rev ,
1867
1909
Rev : st .SubRevOrGoRev (),
1868
1910
Repo : st .RepoOrGo (),
@@ -3634,3 +3676,59 @@ func importPathOfRepo(repo string) string {
3634
3676
}
3635
3677
return "golang.org/x/" + repo
3636
3678
}
3679
+
3680
+ // slowBotsFromComments looks at the TRY= comments from Gerrit (in
3681
+ // work) and returns any addition slow trybot build configuration that
3682
+ // should be tested in addition to the ones provided in the existing
3683
+ // slice.
3684
+ func slowBotsFromComments (work * apipb.GerritTryWorkItem , existing []* dashboard.BuildConfig ) (extraBuilders []* dashboard.BuildConfig ) {
3685
+ tryMsg := latestTryMessage (work ) // "aix, darwin, linux-386-387, arm64"
3686
+ if tryMsg == "" {
3687
+ return nil
3688
+ }
3689
+ if len (tryMsg ) > 1 << 10 { // arbitrary sanity
3690
+ return nil
3691
+ }
3692
+
3693
+ have := map [string ]bool {}
3694
+ for _ , bc := range existing {
3695
+ have [bc .Name ] = true
3696
+ }
3697
+
3698
+ tryTerms := strings .FieldsFunc (tryMsg , func (c rune ) bool {
3699
+ return ! unicode .IsLetter (c ) && ! unicode .IsNumber (c ) && c != '-' && c != '_'
3700
+ })
3701
+ for _ , bc := range dashboard .Builders {
3702
+ if have [bc .Name ] {
3703
+ continue
3704
+ }
3705
+ for _ , term := range tryTerms {
3706
+ if bc .MatchesSlowBotTerm (term ) {
3707
+ extraBuilders = append (extraBuilders , bc )
3708
+ break
3709
+ }
3710
+ }
3711
+ }
3712
+ sort .Slice (extraBuilders , func (i , j int ) bool {
3713
+ return extraBuilders [i ].Name < extraBuilders [j ].Name
3714
+ })
3715
+ return
3716
+ }
3717
+
3718
+ func latestTryMessage (work * apipb.GerritTryWorkItem ) string {
3719
+ // Prioritize exact version matches first
3720
+ for i := len (work .TryMessage ) - 1 ; i >= 0 ; i -- {
3721
+ m := work .TryMessage [i ]
3722
+ if m .Version == work .Version {
3723
+ return m .Message
3724
+ }
3725
+ }
3726
+ // Otherwise the latest message at all
3727
+ for i := len (work .TryMessage ) - 1 ; i >= 0 ; i -- {
3728
+ m := work .TryMessage [i ]
3729
+ if m .Message != "" {
3730
+ return m .Message
3731
+ }
3732
+ }
3733
+ return ""
3734
+ }
0 commit comments