2020-12-02 07:56:04 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2020-12-02 07:56:04 +03:00
package doctor
import (
2022-01-20 02:26:57 +03:00
"context"
2020-12-02 07:56:04 +03:00
"fmt"
2021-07-22 14:53:54 +03:00
"os"
2020-12-02 07:56:04 +03:00
"os/exec"
2021-07-22 14:53:54 +03:00
"path"
2020-12-02 07:56:04 +03:00
"strings"
"code.gitea.io/gitea/models"
2021-09-19 14:49:59 +03:00
"code.gitea.io/gitea/models/db"
2021-12-10 04:27:50 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2020-12-02 07:56:04 +03:00
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
2021-07-22 14:53:54 +03:00
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
2021-09-19 14:49:59 +03:00
2023-07-14 06:00:31 +03:00
lru "github.com/hashicorp/golang-lru/v2"
2020-12-02 07:56:04 +03:00
"xorm.io/builder"
)
2022-03-22 18:22:54 +03:00
func iterateRepositories ( ctx context . Context , each func ( * repo_model . Repository ) error ) error {
2021-09-19 14:49:59 +03:00
err := db . Iterate (
2022-03-22 18:22:54 +03:00
ctx ,
2020-12-02 07:56:04 +03:00
builder . Gt { "id" : 0 } ,
2022-10-31 18:51:14 +03:00
func ( ctx context . Context , bean * repo_model . Repository ) error {
return each ( bean )
2020-12-02 07:56:04 +03:00
} ,
)
return err
}
2022-01-20 02:26:57 +03:00
func checkScriptType ( ctx context . Context , logger log . Logger , autofix bool ) error {
2020-12-02 07:56:04 +03:00
path , err := exec . LookPath ( setting . ScriptType )
if err != nil {
logger . Critical ( "ScriptType \"%q\" is not on the current PATH. Error: %v" , setting . ScriptType , err )
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "ScriptType \"%q\" is not on the current PATH. Error: %w" , setting . ScriptType , err )
2020-12-02 07:56:04 +03:00
}
logger . Info ( "ScriptType %s is on the current PATH at %s" , setting . ScriptType , path )
return nil
}
2022-01-20 02:26:57 +03:00
func checkHooks ( ctx context . Context , logger log . Logger , autofix bool ) error {
2022-03-22 18:22:54 +03:00
if err := iterateRepositories ( ctx , func ( repo * repo_model . Repository ) error {
2020-12-02 07:56:04 +03:00
results , err := repository . CheckDelegateHooks ( repo . RepoPath ( ) )
if err != nil {
logger . Critical ( "Unable to check delegate hooks for repo %-v. ERROR: %v" , repo , err )
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "Unable to check delegate hooks for repo %-v. ERROR: %w" , repo , err )
2020-12-02 07:56:04 +03:00
}
if len ( results ) > 0 && autofix {
logger . Warn ( "Regenerated hooks for %s" , repo . FullName ( ) )
if err := repository . CreateDelegateHooks ( repo . RepoPath ( ) ) ; err != nil {
logger . Critical ( "Unable to recreate delegate hooks for %-v. ERROR: %v" , repo , err )
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "Unable to recreate delegate hooks for %-v. ERROR: %w" , repo , err )
2020-12-02 07:56:04 +03:00
}
}
for _ , result := range results {
logger . Warn ( result )
}
return nil
} ) ; err != nil {
logger . Critical ( "Errors noted whilst checking delegate hooks." )
return err
}
return nil
}
2022-01-20 02:26:57 +03:00
func checkUserStarNum ( ctx context . Context , logger log . Logger , autofix bool ) error {
2022-05-19 16:46:56 +03:00
if autofix {
2023-10-11 07:24:07 +03:00
if err := models . DoctorUserStarNum ( ctx ) ; err != nil {
2022-05-19 16:46:56 +03:00
logger . Critical ( "Unable update User Stars numbers" )
return err
}
logger . Info ( "Updated User Stars numbers." )
} else {
logger . Info ( "No check available for User Stars numbers (skipped)" )
2020-12-02 07:56:04 +03:00
}
return nil
}
2022-01-20 02:26:57 +03:00
func checkEnablePushOptions ( ctx context . Context , logger log . Logger , autofix bool ) error {
2020-12-02 07:56:04 +03:00
numRepos := 0
numNeedUpdate := 0
2021-07-22 14:53:54 +03:00
2022-03-22 18:22:54 +03:00
if err := iterateRepositories ( ctx , func ( repo * repo_model . Repository ) error {
2020-12-02 07:56:04 +03:00
numRepos ++
2022-03-29 22:13:41 +03:00
r , err := git . OpenRepository ( ctx , repo . RepoPath ( ) )
2020-12-02 07:56:04 +03:00
if err != nil {
return err
}
defer r . Close ( )
if autofix {
2022-04-01 05:55:30 +03:00
_ , _ , err := git . NewCommand ( ctx , "config" , "receive.advertisePushOptions" , "true" ) . RunStdString ( & git . RunOpts { Dir : r . Path } )
2020-12-02 07:56:04 +03:00
return err
}
2022-04-01 05:55:30 +03:00
value , _ , err := git . NewCommand ( ctx , "config" , "receive.advertisePushOptions" ) . RunStdString ( & git . RunOpts { Dir : r . Path } )
2020-12-02 07:56:04 +03:00
if err != nil {
return err
}
result , valid := git . ParseBool ( strings . TrimSpace ( value ) )
if ! result || ! valid {
numNeedUpdate ++
logger . Info ( "%s: does not have receive.advertisePushOptions set correctly: %q" , repo . FullName ( ) , value )
}
return nil
} ) ; err != nil {
logger . Critical ( "Unable to EnablePushOptions: %v" , err )
return err
}
if autofix {
logger . Info ( "Enabled push options for %d repositories." , numRepos )
} else {
logger . Info ( "Checked %d repositories, %d need updates." , numRepos , numNeedUpdate )
}
return nil
}
2022-01-20 02:26:57 +03:00
func checkDaemonExport ( ctx context . Context , logger log . Logger , autofix bool ) error {
2021-07-22 14:53:54 +03:00
numRepos := 0
numNeedUpdate := 0
2023-07-14 06:00:31 +03:00
cache , err := lru . New [ int64 , any ] ( 512 )
2021-07-22 14:53:54 +03:00
if err != nil {
logger . Critical ( "Unable to create cache: %v" , err )
return err
}
2022-03-22 18:22:54 +03:00
if err := iterateRepositories ( ctx , func ( repo * repo_model . Repository ) error {
2021-07-22 14:53:54 +03:00
numRepos ++
if owner , has := cache . Get ( repo . OwnerID ) ; has {
2021-11-24 12:49:20 +03:00
repo . Owner = owner . ( * user_model . User )
2021-07-22 14:53:54 +03:00
} else {
2023-02-18 15:11:03 +03:00
if err := repo . LoadOwner ( ctx ) ; err != nil {
2021-07-22 14:53:54 +03:00
return err
}
cache . Add ( repo . OwnerID , repo . Owner )
}
// Create/Remove git-daemon-export-ok for git-daemon...
daemonExportFile := path . Join ( repo . RepoPath ( ) , ` git-daemon-export-ok ` )
isExist , err := util . IsExist ( daemonExportFile )
if err != nil {
log . Error ( "Unable to check if %s exists. Error: %v" , daemonExportFile , err )
return err
}
isPublic := ! repo . IsPrivate && repo . Owner . Visibility == structs . VisibleTypePublic
if isPublic != isExist {
numNeedUpdate ++
if autofix {
if ! isPublic && isExist {
if err = util . Remove ( daemonExportFile ) ; err != nil {
log . Error ( "Failed to remove %s: %v" , daemonExportFile , err )
}
} else if isPublic && ! isExist {
if f , err := os . Create ( daemonExportFile ) ; err != nil {
log . Error ( "Failed to create %s: %v" , daemonExportFile , err )
} else {
f . Close ( )
}
}
}
}
return nil
} ) ; err != nil {
logger . Critical ( "Unable to checkDaemonExport: %v" , err )
return err
}
if autofix {
logger . Info ( "Updated git-daemon-export-ok files for %d of %d repositories." , numNeedUpdate , numRepos )
} else {
logger . Info ( "Checked %d repositories, %d need updates." , numRepos , numNeedUpdate )
}
return nil
}
2022-06-25 22:02:29 +03:00
func checkCommitGraph ( ctx context . Context , logger log . Logger , autofix bool ) error {
numRepos := 0
numNeedUpdate := 0
numWritten := 0
if err := iterateRepositories ( ctx , func ( repo * repo_model . Repository ) error {
numRepos ++
commitGraphExists := func ( ) ( bool , error ) {
// Check commit-graph exists
commitGraphFile := path . Join ( repo . RepoPath ( ) , ` objects/info/commit-graph ` )
isExist , err := util . IsExist ( commitGraphFile )
if err != nil {
logger . Error ( "Unable to check if %s exists. Error: %v" , commitGraphFile , err )
return false , err
}
if ! isExist {
commitGraphsDir := path . Join ( repo . RepoPath ( ) , ` objects/info/commit-graphs ` )
isExist , err = util . IsExist ( commitGraphsDir )
if err != nil {
logger . Error ( "Unable to check if %s exists. Error: %v" , commitGraphsDir , err )
return false , err
}
}
return isExist , nil
}
isExist , err := commitGraphExists ( )
if err != nil {
return err
}
if ! isExist {
numNeedUpdate ++
if autofix {
if err := git . WriteCommitGraph ( ctx , repo . RepoPath ( ) ) ; err != nil {
logger . Error ( "Unable to write commit-graph in %s. Error: %v" , repo . FullName ( ) , err )
return err
}
isExist , err := commitGraphExists ( )
if err != nil {
return err
}
if isExist {
numWritten ++
logger . Info ( "Commit-graph written: %s" , repo . FullName ( ) )
} else {
logger . Warn ( "No commit-graph written: %s" , repo . FullName ( ) )
}
}
}
return nil
} ) ; err != nil {
logger . Critical ( "Unable to checkCommitGraph: %v" , err )
return err
}
if autofix {
logger . Info ( "Wrote commit-graph files for %d of %d repositories." , numWritten , numRepos )
} else {
logger . Info ( "Checked %d repositories, %d without commit-graphs." , numRepos , numNeedUpdate )
}
return nil
}
2020-12-02 07:56:04 +03:00
func init ( ) {
Register ( & Check {
Title : "Check if SCRIPT_TYPE is available" ,
Name : "script-type" ,
IsDefault : false ,
Run : checkScriptType ,
Priority : 5 ,
} )
Register ( & Check {
Title : "Check if hook files are up-to-date and executable" ,
Name : "hooks" ,
IsDefault : false ,
Run : checkHooks ,
Priority : 6 ,
} )
Register ( & Check {
Title : "Recalculate Stars number for all user" ,
Name : "recalculate-stars-number" ,
IsDefault : false ,
Run : checkUserStarNum ,
Priority : 6 ,
} )
Register ( & Check {
2022-05-18 18:04:12 +03:00
Title : "Check that all git repositories have receive.advertisePushOptions set to true" ,
2020-12-02 07:56:04 +03:00
Name : "enable-push-options" ,
IsDefault : false ,
Run : checkEnablePushOptions ,
Priority : 7 ,
} )
2021-07-22 14:53:54 +03:00
Register ( & Check {
Title : "Check git-daemon-export-ok files" ,
Name : "check-git-daemon-export-ok" ,
IsDefault : false ,
Run : checkDaemonExport ,
Priority : 8 ,
} )
2022-06-25 22:02:29 +03:00
Register ( & Check {
Title : "Check commit-graphs" ,
Name : "check-commit-graphs" ,
IsDefault : false ,
Run : checkCommitGraph ,
Priority : 9 ,
} )
2020-12-02 07:56:04 +03:00
}