// Copyright 2022 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 (
"context"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
)
func synchronizeRepoHeads ( ctx context . Context , logger log . Logger , autofix bool ) error {
numRepos := 0
numHeadsBroken := 0
numDefaultBranchesBroken := 0
numReposUpdated := 0
err := iterateRepositories ( ctx , func ( repo * repo_model . Repository ) error {
numRepos ++
_ , _ , defaultBranchErr := git . NewCommand ( ctx , "rev-parse" ) . AddDashesAndList ( repo . DefaultBranch ) . RunStdString ( & git . RunOpts { Dir : repo . RepoPath ( ) } )
head , _ , headErr := git . NewCommand ( ctx , "symbolic-ref" , "--short" , "HEAD" ) . RunStdString ( & git . RunOpts { Dir : repo . RepoPath ( ) } )
// what we expect: default branch is valid, and HEAD points to it
if headErr == nil && defaultBranchErr == nil && head == repo . DefaultBranch {
return nil
}
if headErr != nil {
numHeadsBroken ++
}
if defaultBranchErr != nil {
numDefaultBranchesBroken ++
}
// if default branch is broken, let the user fix that in the UI
if defaultBranchErr != nil {
logger . Warn ( "Default branch for %s/%s doesn't point to a valid commit" , repo . OwnerName , repo . Name )
return nil
}
// if we're not autofixing, that's all we can do
if ! autofix {
return nil
}
// otherwise, let's try fixing HEAD
err := git . NewCommand ( ctx , "symbolic-ref" ) . AddDashesAndList ( "HEAD" , git . BranchPrefix + repo . DefaultBranch ) . Run ( & git . RunOpts { Dir : repo . RepoPath ( ) } )
if err != nil {
logger . Warn ( "Failed to fix HEAD for %s/%s: %v" , repo . OwnerName , repo . Name , err )
return nil
}
numReposUpdated ++
return nil
} )
if err != nil {
logger . Critical ( "Error when fixing repo HEADs: %v" , err )
}
if autofix {
logger . Info ( "Out of %d repos, HEADs for %d are now fixed and HEADS for %d are still broken" , numRepos , numReposUpdated , numDefaultBranchesBroken + numHeadsBroken - numReposUpdated )
} else {
if numHeadsBroken == 0 && numDefaultBranchesBroken == 0 {
logger . Info ( "All %d repos have their HEADs in the correct state" , numRepos )
} else {
if numHeadsBroken == 0 && numDefaultBranchesBroken != 0 {
logger . Critical ( "Default branches are broken for %d/%d repos" , numDefaultBranchesBroken , numRepos )
} else if numHeadsBroken != 0 && numDefaultBranchesBroken == 0 {
logger . Warn ( "HEADs are broken for %d/%d repos" , numHeadsBroken , numRepos )
} else {
logger . Critical ( "Out of %d repos, HEADS are broken for %d and default branches are broken for %d" , numRepos , numHeadsBroken , numDefaultBranchesBroken )
}
}
}
return err
}
func init ( ) {
Register ( & Check {
Title : "Synchronize repo HEADs" ,
Name : "synchronize-repo-heads" ,
IsDefault : true ,
Run : synchronizeRepoHeads ,
Priority : 7 ,
} )
}