go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/util/changelogutil/changelogutil.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 changelogutil contains utility functions for changelogs. 16 package changelogutil 17 18 import ( 19 "context" 20 "errors" 21 "fmt" 22 23 "go.chromium.org/luci/bisection/internal/gitiles" 24 "go.chromium.org/luci/bisection/model" 25 pb "go.chromium.org/luci/bisection/proto/v1" 26 bbpb "go.chromium.org/luci/buildbucket/proto" 27 "go.chromium.org/luci/common/logging" 28 ) 29 30 // GetChangeLogs queries Gitiles for changelogs in the regression range. 31 // If shouldIncludeLastPass is true, the result should also include the last pass revision. 32 // The result will be in descending order of recency (i.e. first failed revision at index 0). 33 func GetChangeLogs(c context.Context, rr *pb.RegressionRange, shouldIncludeLastPass bool) ([]*model.ChangeLog, error) { 34 if rr.LastPassed.Host != rr.FirstFailed.Host || rr.LastPassed.Project != rr.FirstFailed.Project { 35 return nil, fmt.Errorf("RepoURL for last pass and first failed commits must be same, but aren't: %v and %v", rr.LastPassed, rr.FirstFailed) 36 } 37 repoURL := gitiles.GetRepoUrl(c, rr.FirstFailed) 38 lastPassID := rr.LastPassed.Id 39 if shouldIncludeLastPass { 40 lastPassID = fmt.Sprintf("%s^1", lastPassID) 41 } 42 return gitiles.GetChangeLogs(c, repoURL, lastPassID, rr.FirstFailed.Id) 43 } 44 45 func ChangeLogsToBlamelist(ctx context.Context, changeLogs []*model.ChangeLog) *pb.BlameList { 46 if len(changeLogs) == 0 { 47 return &pb.BlameList{} 48 } 49 commits := []*pb.BlameListSingleCommit{} 50 for i := 0; i < len(changeLogs)-1; i++ { 51 cl := changeLogs[i] 52 commits = append(commits, changelogToCommit(ctx, cl)) 53 } 54 return &pb.BlameList{ 55 Commits: commits, 56 LastPassCommit: changelogToCommit(ctx, changeLogs[len(changeLogs)-1]), 57 } 58 } 59 60 func changelogToCommit(ctx context.Context, cl *model.ChangeLog) *pb.BlameListSingleCommit { 61 reviewURL, err := cl.GetReviewUrl() 62 if err != nil { 63 // Just log, this is not important for nth-section analysis 64 logging.Errorf(ctx, "Error getting review URL: %s", err) 65 } 66 67 reviewTitle, err := cl.GetReviewTitle() 68 if err != nil { 69 // Just log, this is not important for nth-section analysis 70 logging.Errorf(ctx, "Error getting review title: %s", err) 71 } 72 73 commitTime, err := cl.GetCommitTime() 74 if err != nil { 75 // Just log, this is informational. 76 logging.Errorf(ctx, "Error getting commit time: %s", err) 77 } 78 79 return &pb.BlameListSingleCommit{ 80 Commit: cl.Commit, 81 ReviewUrl: reviewURL, 82 ReviewTitle: reviewTitle, 83 CommitTime: commitTime, 84 } 85 } 86 87 // SetCommitPositionInBlamelist sets the position field in BlameListSingleCommits of this blamelist. 88 // Commits in the blamelist are ordered by commit position in descending order. 89 // Index 0 refers to the highest-position commit in the regression range, with has the commit position same as regression end position. 90 // Index n-1 refers to the lowest-position commit in the regression range. 91 // We can find the commit position of all commits in between. 92 func SetCommitPositionInBlamelist(blamelist *pb.BlameList, regressionStartPosition, regressionEndPosition int64) error { 93 if int(regressionEndPosition-regressionStartPosition) != len(blamelist.Commits) { 94 msg := fmt.Sprintf("Number of changelog in the regression range (%d) doesn't match the regression commit position (%d)-(%d)", 95 len(blamelist.Commits), regressionStartPosition, regressionEndPosition) 96 return errors.New(msg) 97 } 98 for i, c := range blamelist.Commits { 99 c.Position = regressionEndPosition - int64(i) 100 } 101 blamelist.LastPassCommit.Position = regressionStartPosition 102 return nil 103 } 104 105 // FindCommitIndexInBlameList find the index of the gitiles commit in blamelist. 106 // It returns -1 if it couldn't find. 107 func FindCommitIndexInBlameList(gitilesCommit *bbpb.GitilesCommit, blamelist *pb.BlameList) int { 108 for i, commit := range blamelist.Commits { 109 if commit.Commit == gitilesCommit.Id { 110 return i 111 } 112 } 113 return -1 114 }