go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/luci_notify/notify/gitiles.go (about)

     1  // Copyright 2017 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 notify
    16  
    17  import (
    18  	"context"
    19  	"net/http"
    20  	"time"
    21  
    22  	"go.chromium.org/luci/common/api/gitiles"
    23  	"go.chromium.org/luci/common/errors"
    24  	"go.chromium.org/luci/common/logging"
    25  	gitpb "go.chromium.org/luci/common/proto/git"
    26  	gitilespb "go.chromium.org/luci/common/proto/gitiles"
    27  	"go.chromium.org/luci/grpc/grpcutil"
    28  	"go.chromium.org/luci/server/auth"
    29  )
    30  
    31  // HistoryFunc is a function that gets a list of commits from Gitiles for a
    32  // specific repository, between oldRevision and newRevision, inclusive.
    33  //
    34  // If oldRevision is not reachable from newRevision, returns an empty slice
    35  // and nil error.
    36  type HistoryFunc func(ctx context.Context, luciProject, gerritHost, gerritProject, oldRevision, newRevision string) ([]*gitpb.Commit, error)
    37  
    38  // gitilesHistory is an implementation of a HistoryFunc intended to be used
    39  // in production (not for testing).
    40  func gitilesHistory(ctx context.Context, luciProject, gerritHost, gerritProject, oldRevision, newRevision string) ([]*gitpb.Commit, error) {
    41  	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
    42  	defer cancel()
    43  
    44  	opts := []auth.RPCOption{
    45  		auth.WithProject(luciProject),
    46  		auth.WithScopes(gitiles.OAuthScope),
    47  	}
    48  	transport, err := auth.GetRPCTransport(ctx, auth.AsProject, opts...)
    49  	if err != nil {
    50  		return nil, errors.Annotate(err, "getting RPC Transport").Err()
    51  	}
    52  	client, err := gitiles.NewRESTClient(&http.Client{Transport: transport}, gerritHost, true)
    53  	if err != nil {
    54  		return nil, errors.Annotate(err, "creating Gitiles client").Err()
    55  	}
    56  
    57  	req := &gitilespb.LogRequest{
    58  		Project: gerritProject,
    59  		// With ~1, if newCommit.Revision == oldRevision,
    60  		// we'll get one commit,
    61  		// otherwise we will get 0 commits which is indistinguishable from
    62  		// oldRevision not being reachable from newRevision.
    63  		ExcludeAncestorsOf: oldRevision + "~1",
    64  		Committish:         newRevision,
    65  	}
    66  	logging.Infof(ctx, "Gitiles request to host %q: %q", gerritHost, req)
    67  	res, err := gitiles.PagingLog(ctx, client, req, 0)
    68  	switch {
    69  	case err != nil:
    70  		return nil, grpcutil.WrapIfTransient(errors.Annotate(err, "fetching commit from Gitiles").Err())
    71  	case len(res) > 0 && res[0].Id != newRevision: // Sanity check.
    72  		return nil, errors.Reason("gitiles returned inconsistent results").Err()
    73  	default:
    74  		return res, nil
    75  	}
    76  }