2020-12-01 21:56:04 -07:00
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package doctor
import (
2022-01-19 16:26:57 -07:00
"context"
2020-12-01 21:56:04 -07:00
"fmt"
"strings"
2021-09-19 05:49:59 -06:00
"code.gitea.io/gitea/models/db"
2022-06-13 03:37:59 -06:00
issues_model "code.gitea.io/gitea/models/issues"
2021-12-09 18:27:50 -07:00
repo_model "code.gitea.io/gitea/models/repo"
2020-12-01 21:56:04 -07:00
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
2021-09-19 05:49:59 -06:00
2020-12-01 21:56:04 -07:00
"xorm.io/builder"
)
2022-06-13 03:37:59 -06:00
func iteratePRs ( ctx context . Context , repo * repo_model . Repository , each func ( * repo_model . Repository , * issues_model . PullRequest ) error ) error {
2021-09-19 05:49:59 -06:00
return db . Iterate (
2022-03-22 09:22:54 -06:00
ctx ,
2022-06-13 03:37:59 -06:00
new ( issues_model . PullRequest ) ,
2020-12-01 21:56:04 -07:00
builder . Eq { "base_repo_id" : repo . ID } ,
func ( idx int , bean interface { } ) error {
2022-06-13 03:37:59 -06:00
return each ( repo , bean . ( * issues_model . PullRequest ) )
2020-12-01 21:56:04 -07:00
} ,
)
}
2022-01-19 16:26:57 -07:00
func checkPRMergeBase ( ctx context . Context , logger log . Logger , autofix bool ) error {
2020-12-01 21:56:04 -07:00
numRepos := 0
numPRs := 0
numPRsUpdated := 0
2022-03-22 09:22:54 -06:00
err := iterateRepositories ( ctx , func ( repo * repo_model . Repository ) error {
2020-12-01 21:56:04 -07:00
numRepos ++
2022-06-13 03:37:59 -06:00
return iteratePRs ( ctx , repo , func ( repo * repo_model . Repository , pr * issues_model . PullRequest ) error {
2020-12-01 21:56:04 -07:00
numPRs ++
pr . BaseRepo = repo
repoPath := repo . RepoPath ( )
oldMergeBase := pr . MergeBase
if ! pr . HasMerged {
var err error
2022-10-23 08:44:45 -06:00
pr . MergeBase , _ , err = git . NewCommand ( ctx , "merge-base" ) . AddDashesAndList ( pr . BaseBranch , pr . GetGitRefName ( ) ) . RunStdString ( & git . RunOpts { Dir : repoPath } )
2020-12-01 21:56:04 -07:00
if err != nil {
var err2 error
2022-10-23 08:44:45 -06:00
pr . MergeBase , _ , err2 = git . NewCommand ( ctx , "rev-parse" ) . AddDynamicArguments ( git . BranchPrefix + pr . BaseBranch ) . RunStdString ( & git . RunOpts { Dir : repoPath } )
2020-12-01 21:56:04 -07:00
if err2 != nil {
logger . Warn ( "Unable to get merge base for PR ID %d, #%d onto %s in %s/%s. Error: %v & %v" , pr . ID , pr . Index , pr . BaseBranch , pr . BaseRepo . OwnerName , pr . BaseRepo . Name , err , err2 )
return nil
}
}
} else {
2022-10-23 08:44:45 -06:00
parentsString , _ , err := git . NewCommand ( ctx , "rev-list" , "--parents" , "-n" , "1" ) . AddDynamicArguments ( pr . MergedCommitID ) . RunStdString ( & git . RunOpts { Dir : repoPath } )
2020-12-01 21:56:04 -07:00
if err != nil {
logger . Warn ( "Unable to get parents for merged PR ID %d, #%d onto %s in %s/%s. Error: %v" , pr . ID , pr . Index , pr . BaseBranch , pr . BaseRepo . OwnerName , pr . BaseRepo . Name , err )
return nil
}
parents := strings . Split ( strings . TrimSpace ( parentsString ) , " " )
if len ( parents ) < 2 {
return nil
}
2022-10-23 08:44:45 -06:00
refs := append ( [ ] string { } , parents [ 1 : ] ... )
refs = append ( refs , pr . GetGitRefName ( ) )
cmd := git . NewCommand ( ctx , "merge-base" ) . AddDashesAndList ( refs ... )
pr . MergeBase , _ , err = cmd . RunStdString ( & git . RunOpts { Dir : repoPath } )
2020-12-01 21:56:04 -07:00
if err != nil {
logger . Warn ( "Unable to get merge base for merged PR ID %d, #%d onto %s in %s/%s. Error: %v" , pr . ID , pr . Index , pr . BaseBranch , pr . BaseRepo . OwnerName , pr . BaseRepo . Name , err )
return nil
}
}
pr . MergeBase = strings . TrimSpace ( pr . MergeBase )
if pr . MergeBase != oldMergeBase {
if autofix {
if err := pr . UpdateCols ( "merge_base" ) ; err != nil {
logger . Critical ( "Failed to update merge_base. ERROR: %v" , err )
2022-10-24 13:29:17 -06:00
return fmt . Errorf ( "Failed to update merge_base. ERROR: %w" , err )
2020-12-01 21:56:04 -07:00
}
} else {
logger . Info ( "#%d onto %s in %s/%s: MergeBase should be %s but is %s" , pr . Index , pr . BaseBranch , pr . BaseRepo . OwnerName , pr . BaseRepo . Name , oldMergeBase , pr . MergeBase )
}
numPRsUpdated ++
}
return nil
} )
} )
if autofix {
logger . Info ( "%d PR mergebases updated of %d PRs total in %d repos" , numPRsUpdated , numPRs , numRepos )
} else {
2022-05-31 12:49:40 -06:00
if numPRsUpdated == 0 {
logger . Info ( "All %d PRs in %d repos have a correct mergebase" , numPRs , numRepos )
} else if err == nil {
2020-12-01 21:56:04 -07:00
logger . Critical ( "%d PRs with incorrect mergebases of %d PRs total in %d repos" , numPRsUpdated , numPRs , numRepos )
return fmt . Errorf ( "%d PRs with incorrect mergebases of %d PRs total in %d repos" , numPRsUpdated , numPRs , numRepos )
2022-05-31 12:49:40 -06:00
} else {
logger . Warn ( "%d PRs with incorrect mergebases of %d PRs total in %d repos" , numPRsUpdated , numPRs , numRepos )
2020-12-01 21:56:04 -07:00
}
}
return err
}
func init ( ) {
Register ( & Check {
Title : "Recalculate merge bases" ,
Name : "recalculate-merge-bases" ,
IsDefault : false ,
Run : checkPRMergeBase ,
Priority : 7 ,
} )
}