code.gitea.io/gitea@v1.19.3/modules/doctor/heads.go (about)

     1  // Copyright 2022 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package doctor
     5  
     6  import (
     7  	"context"
     8  
     9  	repo_model "code.gitea.io/gitea/models/repo"
    10  	"code.gitea.io/gitea/modules/git"
    11  	"code.gitea.io/gitea/modules/log"
    12  )
    13  
    14  func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) error {
    15  	numRepos := 0
    16  	numHeadsBroken := 0
    17  	numDefaultBranchesBroken := 0
    18  	numReposUpdated := 0
    19  	err := iterateRepositories(ctx, func(repo *repo_model.Repository) error {
    20  		numRepos++
    21  		_, _, defaultBranchErr := git.NewCommand(ctx, "rev-parse").AddDashesAndList(repo.DefaultBranch).RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
    22  
    23  		head, _, headErr := git.NewCommand(ctx, "symbolic-ref", "--short", "HEAD").RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
    24  
    25  		// what we expect: default branch is valid, and HEAD points to it
    26  		if headErr == nil && defaultBranchErr == nil && head == repo.DefaultBranch {
    27  			return nil
    28  		}
    29  
    30  		if headErr != nil {
    31  			numHeadsBroken++
    32  		}
    33  		if defaultBranchErr != nil {
    34  			numDefaultBranchesBroken++
    35  		}
    36  
    37  		// if default branch is broken, let the user fix that in the UI
    38  		if defaultBranchErr != nil {
    39  			logger.Warn("Default branch for %s/%s doesn't point to a valid commit", repo.OwnerName, repo.Name)
    40  			return nil
    41  		}
    42  
    43  		// if we're not autofixing, that's all we can do
    44  		if !autofix {
    45  			return nil
    46  		}
    47  
    48  		// otherwise, let's try fixing HEAD
    49  		err := git.NewCommand(ctx, "symbolic-ref").AddDashesAndList("HEAD", git.BranchPrefix+repo.DefaultBranch).Run(&git.RunOpts{Dir: repo.RepoPath()})
    50  		if err != nil {
    51  			logger.Warn("Failed to fix HEAD for %s/%s: %v", repo.OwnerName, repo.Name, err)
    52  			return nil
    53  		}
    54  		numReposUpdated++
    55  		return nil
    56  	})
    57  	if err != nil {
    58  		logger.Critical("Error when fixing repo HEADs: %v", err)
    59  	}
    60  
    61  	if autofix {
    62  		logger.Info("Out of %d repos, HEADs for %d are now fixed and HEADS for %d are still broken", numRepos, numReposUpdated, numDefaultBranchesBroken+numHeadsBroken-numReposUpdated)
    63  	} else {
    64  		if numHeadsBroken == 0 && numDefaultBranchesBroken == 0 {
    65  			logger.Info("All %d repos have their HEADs in the correct state", numRepos)
    66  		} else {
    67  			if numHeadsBroken == 0 && numDefaultBranchesBroken != 0 {
    68  				logger.Critical("Default branches are broken for %d/%d repos", numDefaultBranchesBroken, numRepos)
    69  			} else if numHeadsBroken != 0 && numDefaultBranchesBroken == 0 {
    70  				logger.Warn("HEADs are broken for %d/%d repos", numHeadsBroken, numRepos)
    71  			} else {
    72  				logger.Critical("Out of %d repos, HEADS are broken for %d and default branches are broken for %d", numRepos, numHeadsBroken, numDefaultBranchesBroken)
    73  			}
    74  		}
    75  	}
    76  
    77  	return err
    78  }
    79  
    80  func init() {
    81  	Register(&Check{
    82  		Title:     "Synchronize repo HEADs",
    83  		Name:      "synchronize-repo-heads",
    84  		IsDefault: true,
    85  		Run:       synchronizeRepoHeads,
    86  		Priority:  7,
    87  	})
    88  }