github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/gerrit/adapter/trigger.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package adapter
    18  
    19  import (
    20  	"strings"
    21  	"time"
    22  
    23  	"github.com/andygrunwald/go-gerrit"
    24  	"github.com/sirupsen/logrus"
    25  	"k8s.io/apimachinery/pkg/util/sets"
    26  
    27  	"sigs.k8s.io/prow/pkg/config"
    28  	"sigs.k8s.io/prow/pkg/gerrit/client"
    29  	"sigs.k8s.io/prow/pkg/pjutil"
    30  )
    31  
    32  // presubmitContexts returns the set of failing and all job names contained in the reports.
    33  func presubmitContexts(failed sets.Set[string], presubmits []config.Presubmit, logger logrus.FieldLogger) (sets.Set[string], sets.Set[string]) {
    34  	allContexts := sets.Set[string]{}
    35  	for _, presubmit := range presubmits {
    36  		allContexts.Insert(presubmit.Name) // TODO(fejta): context, not name
    37  	}
    38  	failedContexts := allContexts.Intersection(failed)
    39  	return failedContexts, allContexts
    40  }
    41  
    42  // currentMessages returns messages on the current revision after the specified time.
    43  func currentMessages(change gerrit.ChangeInfo, since time.Time) []gerrit.ChangeMessageInfo {
    44  	var messages []gerrit.ChangeMessageInfo
    45  	want := change.Revisions[change.CurrentRevision].Number
    46  	for _, have := range change.Messages {
    47  		if have.RevisionNumber != want {
    48  			continue
    49  		}
    50  		if !have.Date.Time.After(since) {
    51  			continue
    52  		}
    53  		messages = append(messages, have)
    54  	}
    55  	return messages
    56  }
    57  
    58  func indicatesChangeFromDraftToActiveState(s string) bool {
    59  	return strings.HasSuffix(s, client.ReadyForReviewMessageFixed) ||
    60  		strings.HasSuffix(s, client.ReadyForReviewMessageCustomizable)
    61  }
    62  
    63  // messageFilter returns filter that matches all /test all, /test foo, /retest comments since lastUpdate.
    64  //
    65  // The behavior of each message matches the behavior of pjutil.PresubmitFilter.
    66  func messageFilter(messages []gerrit.ChangeMessageInfo, failingContexts, allContexts sets.Set[string], triggerTimes map[string]time.Time, logger logrus.FieldLogger) pjutil.Filter {
    67  	var filters []pjutil.Filter
    68  	contextGetter := func() (sets.Set[string], sets.Set[string], error) {
    69  		return failingContexts, allContexts, nil
    70  	}
    71  	for _, message := range messages {
    72  		// Use the PresubmitFilter before possibly adding the /test-all filter to ensure explicitly requested presubmits are forced to run.
    73  		filter, err := pjutil.PresubmitFilter(false, contextGetter, message.Message, logger)
    74  		if err != nil {
    75  			logger.WithError(err).WithField("message", message).Warn("failed to create presubmit filter")
    76  			continue
    77  		}
    78  		filters = append(filters, &timeAnnotationFilter{
    79  			Filter:       filter,
    80  			eventTime:    message.Date.Time,
    81  			triggerTimes: triggerTimes,
    82  		})
    83  		// If the Gerrit Change changed from draft to active state, trigger all
    84  		// presubmit Prow jobs.
    85  		if indicatesChangeFromDraftToActiveState(message.Message) {
    86  			filters = append(filters, &timeAnnotationFilter{
    87  				Filter:       pjutil.NewTestAllFilter(),
    88  				eventTime:    message.Date.Time,
    89  				triggerTimes: triggerTimes,
    90  			})
    91  			continue
    92  		}
    93  	}
    94  
    95  	return pjutil.NewAggregateFilter(filters)
    96  }
    97  
    98  // timeAnnotationFilter is a wrapper around a pjutil.Filter that records the eventTime in
    99  // the triggerTimes map when the Filter returns a true 'shouldRun' value.
   100  type timeAnnotationFilter struct {
   101  	pjutil.Filter                      // Delegate filter. Only override/wrap ShouldRun() for time annotation, use Name() directly.
   102  	eventTime     time.Time            // The time of the event. If the filter matches, use this time for annotation.
   103  	triggerTimes  map[string]time.Time // This map is referenced by all timeAnnotationFilters for a single processing iteration.
   104  }
   105  
   106  func (taf *timeAnnotationFilter) ShouldRun(p config.Presubmit) (shouldRun bool, forcedToRun bool, defaultBehavior bool) {
   107  	shouldRun, forced, def := taf.Filter.ShouldRun(p)
   108  	if shouldRun {
   109  		taf.triggerTimes[p.Name] = taf.eventTime
   110  	}
   111  	return shouldRun, forced, def
   112  }