2020-12-02 07:56:04 +03: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-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
2021-07-22 14:53:54 +03:00
lru "github.com/hashicorp/golang-lru"
2020-12-02 07:56:04 +03:00
"xorm.io/builder"
)
2021-12-10 04:27:50 +03:00
func iterateRepositories ( each func ( * repo_model . Repository ) error ) error {
2021-09-19 14:49:59 +03:00
err := db . Iterate (
2021-09-23 18:45:36 +03:00
db . DefaultContext ,
2021-12-10 04:27:50 +03:00
new ( repo_model . Repository ) ,
2020-12-02 07:56:04 +03:00
builder . Gt { "id" : 0 } ,
func ( idx int , bean interface { } ) error {
2021-12-10 04:27:50 +03:00
return each ( bean . ( * repo_model . Repository ) )
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 )
return fmt . Errorf ( "ScriptType \"%q\" is not on the current PATH. Error: %v" , setting . ScriptType , err )
}
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 {
2021-12-10 04:27:50 +03:00
if err := iterateRepositories ( 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 )
return fmt . Errorf ( "Unable to check delegate hooks for repo %-v. ERROR: %v" , repo , err )
}
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 )
return fmt . Errorf ( "Unable to recreate delegate hooks for %-v. ERROR: %v" , repo , err )
}
}
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 {
2020-12-02 07:56:04 +03:00
if err := models . DoctorUserStarNum ( ) ; err != nil {
logger . Critical ( "Unable update User Stars numbers" )
return err
}
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
2021-12-10 04:27:50 +03:00
if err := iterateRepositories ( func ( repo * repo_model . Repository ) error {
2020-12-02 07:56:04 +03:00
numRepos ++
2022-01-20 02:26:57 +03:00
r , err := git . OpenRepositoryCtx ( git . DefaultContext , repo . RepoPath ( ) )
2020-12-02 07:56:04 +03:00
if err != nil {
return err
}
defer r . Close ( )
if autofix {
2022-02-06 22:01:47 +03:00
_ , err := git . NewCommand ( ctx , "config" , "receive.advertisePushOptions" , "true" ) . RunInDir ( r . Path )
2020-12-02 07:56:04 +03:00
return err
}
2022-02-06 22:01:47 +03:00
value , err := git . NewCommand ( ctx , "config" , "receive.advertisePushOptions" ) . RunInDir ( 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
cache , err := lru . New ( 512 )
if err != nil {
logger . Critical ( "Unable to create cache: %v" , err )
return err
}
2021-12-10 04:27:50 +03:00
if err := iterateRepositories ( 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 {
2021-12-10 04:27:50 +03:00
if err := repo . GetOwner ( db . DefaultContext ) ; 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
}
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 {
Title : "Enable push options" ,
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 ,
} )
2020-12-02 07:56:04 +03:00
}