go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/culpritaction/revertculprit/commitrevert.go (about)

     1  // Copyright 2022 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package revertculprit
    16  
    17  import (
    18  	"context"
    19  	"time"
    20  
    21  	"go.chromium.org/luci/bisection/internal/config"
    22  	"go.chromium.org/luci/bisection/internal/gerrit"
    23  	"go.chromium.org/luci/bisection/internal/rotationproxy"
    24  	"go.chromium.org/luci/bisection/model"
    25  	bisectionpb "go.chromium.org/luci/bisection/proto/v1"
    26  
    27  	"go.chromium.org/luci/common/errors"
    28  	"go.chromium.org/luci/common/logging"
    29  	gerritpb "go.chromium.org/luci/common/proto/gerrit"
    30  )
    31  
    32  // canCommit returns:
    33  //   - whether a revert for the culprit CL can be committed;
    34  //   - the reason a revert should not be committed if applicable; and
    35  //   - the error if one occurred.
    36  func canCommit(ctx context.Context, culprit *gerritpb.ChangeInfo, culpritModel *model.Suspect, project string) (bool, string, error) {
    37  	// TODO(beining@): remove this when revert CL support has been added for test failure.
    38  	if culpritModel.AnalysisType == bisectionpb.AnalysisType_TEST_FAILURE_ANALYSIS {
    39  		return false, "LUCI Bisection has not yet support auto-commit of revert CL for test failure", nil
    40  	}
    41  	// Get gerrit config.
    42  	gerritConfig, err := config.GetGerritCfgForSuspect(ctx, culpritModel, project)
    43  	if err != nil {
    44  		return false, "", errors.Annotate(err, "error get gerrit config").Err()
    45  	}
    46  
    47  	// Check if the culprit was committed recently
    48  	maxAge := time.Duration(gerritConfig.MaxRevertibleCulpritAge) * time.Second
    49  	if !gerrit.IsRecentSubmit(ctx, culprit, maxAge) {
    50  		// culprit was not submitted recently, so the revert should not be
    51  		// automatically submitted
    52  		return false, "the target of this revert was not committed recently", nil
    53  	}
    54  
    55  	// Check if LUCI Bisection's Gerrit config allows revert submission
    56  	canSubmit, reason, err := config.CanSubmitRevert(ctx, gerritConfig)
    57  	if err != nil {
    58  		return false, "", errors.Annotate(err, "error checking Submit Revert configs").Err()
    59  	}
    60  	if !canSubmit {
    61  		// cannot submit revert based on config
    62  		return false, reason, nil
    63  	}
    64  
    65  	// We can only proceed to commit if it is a confirmed culprit
    66  	// This is for the case that a we may create a revert on verification error of an
    67  	// nthsection suspect. But we definitely don't want to auto submit the revert.
    68  	if culpritModel.VerificationStatus != model.SuspectVerificationStatus_ConfirmedCulprit {
    69  		return false, "the suspect is not verified", nil
    70  	}
    71  	return true, "", nil
    72  }
    73  
    74  // commitRevert attempts to bot-commit the given revert.
    75  // Note: this should only be called according to the service-wide configuration
    76  // data for LUCI Bisection, i.e.
    77  //   - Gerrit actions are enabled
    78  //   - Submitting reverts is enabled
    79  //   - the daily limit of submitted reverts has not yet been reached
    80  //   - the culprit is not yet older than the maximum revertible culprit age
    81  func commitRevert(ctx context.Context, gerritClient *gerrit.Client,
    82  	culpritModel *model.Suspect, revert *gerritpb.ChangeInfo) error {
    83  	// CC on-call arborists
    84  	ccEmails, err := rotationproxy.GetOnCallEmails(ctx,
    85  		culpritModel.GitilesCommit.Project)
    86  	if err != nil {
    87  		// non-critical, just log the error
    88  		err = errors.Annotate(err, "failed getting accounts to CC on bot-commit").Err()
    89  		logging.Errorf(ctx, err.Error())
    90  	}
    91  
    92  	_, err = gerritClient.CommitRevert(ctx, revert,
    93  		"LUCI Bisection is automatically submitting this revert.", ccEmails)
    94  	return err
    95  }