@@ -225,6 +225,7 @@ var tasks = []struct {
225
225
{"check cherry picks" , (* gopherbot ).checkCherryPicks },
226
226
{"update needs" , (* gopherbot ).updateNeeds },
227
227
{"congratulate new contributors" , (* gopherbot ).congratulateNewContributors },
228
+ {"un-wait CLs" , (* gopherbot ).unwaitCLs },
228
229
}
229
230
230
231
func (b * gopherbot ) initCorpus () {
@@ -851,6 +852,84 @@ func (b *gopherbot) congratulateNewContributors(ctx context.Context) error {
851
852
return nil
852
853
}
853
854
855
+ // unwaitCLs removes wait-* hashtags from CLs.
856
+ func (b * gopherbot ) unwaitCLs (ctx context.Context ) error {
857
+ return b .corpus .Gerrit ().ForeachProjectUnsorted (func (gp * maintner.GerritProject ) error {
858
+ if gp .Server () != "go.googlesource.com" {
859
+ return nil
860
+ }
861
+ return gp .ForeachOpenCL (func (cl * maintner.GerritCL ) error {
862
+ tags := cl .Meta .Hashtags ()
863
+ if tags .Len () == 0 {
864
+ return nil
865
+ }
866
+ // If the CL is tagged "wait-author", remove
867
+ // that tag if the author has replied since
868
+ // the last time the "wait-author" tag was
869
+ // added.
870
+ if tags .Contains ("wait-author" ) {
871
+ // Figure out othe last index at which "wait-author" was added.
872
+ waitAuthorIndex := - 1
873
+ for i := len (cl .Metas ) - 1 ; i >= 0 ; i -- {
874
+ if cl .Metas [i ].HashtagsAdded ().Contains ("wait-author" ) {
875
+ waitAuthorIndex = i
876
+ break
877
+ }
878
+ }
879
+
880
+ // Find the author has replied since
881
+ author := cl .Metas [0 ].Commit .Author .Str
882
+ hasReplied := false
883
+ for _ , m := range cl .Metas [waitAuthorIndex + 1 :] {
884
+ if m .Commit .Author .Str == author {
885
+ hasReplied = true
886
+ break
887
+ }
888
+ }
889
+ if hasReplied {
890
+ log .Printf ("https://golang.org/cl/%d -- remove wait-author; reply from %s" , cl .Number , author )
891
+ err := b .onLatestCL (ctx , cl , func () error {
892
+ if * dryRun {
893
+ log .Printf ("[dry run] would remove hashtag 'wait-author' from CL %d" , cl .Number )
894
+ return nil
895
+ }
896
+ _ , err := b .gerrit .RemoveHashtags (ctx , fmt .Sprint (cl .Number ), "wait-author" )
897
+ if err != nil {
898
+ log .Printf ("https://golang.org/cl/%d: error removing wait-author: %v" , cl .Number , err )
899
+ return err
900
+ }
901
+ log .Printf ("https://golang.org/cl/%d: removed wait-author" , cl .Number )
902
+ return nil
903
+ })
904
+ if err != nil {
905
+ return err
906
+ }
907
+ }
908
+ }
909
+ return nil
910
+ })
911
+ })
912
+ }
913
+
914
+ // onLatestCL checks whether cl's metadata is in sync with Gerrit's
915
+ // upstream data and, if so, returns f(). If it's out of sync, it does
916
+ // nothing more and returns nil.
917
+ func (b * gopherbot ) onLatestCL (ctx context.Context , cl * maintner.GerritCL , f func () error ) error {
918
+ ci , err := b .gerrit .GetChangeDetail (ctx , fmt .Sprint (cl .Number ), gerrit.QueryChangesOpt {Fields : []string {"MESSAGES" }})
919
+ if err != nil {
920
+ return err
921
+ }
922
+ if len (ci .Messages ) == 0 {
923
+ log .Printf ("onLatestCL: CL %v has no messages. Odd. Ignoring." )
924
+ return nil
925
+ }
926
+ if ci .Messages [len (ci .Messages )- 1 ].ID == cl .Meta .Commit .Hash .String () {
927
+ return f ()
928
+ }
929
+ log .Printf ("onLatestCL: maintner metadata for CL %d is behind; skipping action for now." , cl .Number )
930
+ return nil
931
+ }
932
+
854
933
// errStopIteration is used to stop iteration over issues or comments.
855
934
// It has no special meaning.
856
935
var errStopIteration = errors .New ("stop iteration" )
0 commit comments