@ -14,289 +14,174 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/setting"
)
)
func checkDBConsistency ( logger log . Logger , autofix bool ) error {
type consistencyCheck struct {
// make sure DB version is uptodate
Name string
if err := db . NewEngine ( context . Background ( ) , migrations . EnsureUpToDate ) ; err != nil {
Counter func ( ) ( int64 , error )
logger . Critical ( "Model version on the database does not match the current Gitea version. Model consistency will not be checked until the database is upgraded" )
Fixer func ( ) ( int64 , error )
return err
FixedMessage string
}
}
// find labels without existing repo or org
count , err := models . CountOrphanedLabels ( )
if err != nil {
logger . Critical ( "Error: %v whilst counting orphaned labels" , err )
return err
}
if count > 0 {
if autofix {
if err = models . DeleteOrphanedLabels ( ) ; err != nil {
logger . Critical ( "Error: %v whilst deleting orphaned labels" , err )
return err
}
logger . Info ( "%d labels without existing repository/organisation deleted" , count )
} else {
logger . Warn ( "%d labels without existing repository/organisation" , count )
}
}
// find IssueLabels without existing label
count , err = models . CountOrphanedIssueLabels ( )
if err != nil {
logger . Critical ( "Error: %v whilst counting orphaned issue_labels" , err )
return err
}
if count > 0 {
if autofix {
if err = models . DeleteOrphanedIssueLabels ( ) ; err != nil {
logger . Critical ( "Error: %v whilst deleting orphaned issue_labels" , err )
return err
}
logger . Info ( "%d issue_labels without existing label deleted" , count )
} else {
logger . Warn ( "%d issue_labels without existing label" , count )
}
}
// find issues without existing repository
count , err = models . CountOrphanedIssues ( )
if err != nil {
logger . Critical ( "Error: %v whilst counting orphaned issues" , err )
return err
}
if count > 0 {
if autofix {
if err = models . DeleteOrphanedIssues ( ) ; err != nil {
logger . Critical ( "Error: %v whilst deleting orphaned issues" , err )
return err
}
logger . Info ( "%d issues without existing repository deleted" , count )
} else {
logger . Warn ( "%d issues without existing repository" , count )
}
}
// find releases without existing repository
func ( c * consistencyCheck ) Run ( logger log . Logger , autofix bool ) error {
count , err = models . CountOrphanedObjects ( "release" , "repository" , "release.repo_id=repository.id" )
count , err := c . Counter ( )
if err != nil {
if err != nil {
logger . Critical ( "Error: %v whilst counting orphaned object s" , err )
logger . Critical ( "Error: %v whilst counting %s" , err , c . Name )
return err
return err
}
}
if count > 0 {
if count > 0 {
if autofix {
if autofix {
if err = models . DeleteOrphanedObjects ( "release" , "repository" , "release.repo_id=repository.id" ) ; err != nil {
var fixed int64
logger . Critical ( "Error: %v whilst deleting orphaned objects" , err )
if fixed , err = c . Fixer ( ) ; err != nil {
logger . Critical ( "Error: %v whilst fixing %s" , err , c . Name )
return err
return err
}
}
logger . Info ( "%d releases without existing repository deleted" , count )
} else {
logger . Warn ( "%d releases without existing repository" , count )
}
}
// find pulls without existing issues
prompt := "Deleted"
count , err = models . CountOrphanedObjects ( "pull_request" , "issue" , "pull_request.issue_id=issue.id" )
if c . FixedMessage != "" {
if err != nil {
prompt = c . FixedMessage
logger . Critical ( "Error: %v whilst counting orphaned objects" , err )
return err
}
if count > 0 {
if autofix {
if err = models . DeleteOrphanedObjects ( "pull_request" , "issue" , "pull_request.issue_id=issue.id" ) ; err != nil {
logger . Critical ( "Error: %v whilst deleting orphaned objects" , err )
return err
}
}
logger . Info ( "%d pull requests without existing issue deleted" , count )
} else {
logger . Warn ( "%d pull requests without existing issue" , count )
}
}
// find tracked times without existing issues/pulls
if fixed < 0 {
count , err = models . CountOrphanedObjects ( "tracked_time" , "issue" , "tracked_time.issue_id=issue.id" )
logger . Info ( prompt + " %d %s" , count , c . Name )
if err != nil {
} else {
logger . Critical ( "Error: %v whilst counting orphaned objects" , err )
logger . Info ( prompt + " %d/%d %s" , fixed , count , c . Name )
return err
}
if count > 0 {
if autofix {
if err = models . DeleteOrphanedObjects ( "tracked_time" , "issue" , "tracked_time.issue_id=issue.id" ) ; err != nil {
logger . Critical ( "Error: %v whilst deleting orphaned objects" , err )
return err
}
logger . Info ( "%d tracked times without existing issue deleted" , count )
} else {
logger . Warn ( "%d tracked times without existing issue" , count )
}
}
// find attachments without existing issues or releases
count , err = models . CountOrphanedAttachments ( )
if err != nil {
logger . Critical ( "Error: %v whilst counting orphaned objects" , err )
return err
}
if count > 0 {
if autofix {
if err = models . DeleteOrphanedAttachments ( ) ; err != nil {
logger . Critical ( "Error: %v whilst deleting orphaned objects" , err )
return err
}
}
logger . Info ( "%d attachments without existing issue or release deleted" , count )
} else {
} else {
logger . Warn ( "%d attachments without existing issue or release" , count )
logger . Warn ( "Found %d %s" , count , c . Name )
}
}
}
}
return nil
}
// find null archived repositories
func asFixer ( fn func ( ) error ) func ( ) ( int64 , error ) {
count , err = models . CountNullArchivedRepository ( )
return func ( ) ( int64 , error ) {
if err != nil {
err := fn ( )
logger . Critical ( "Error: %v whilst counting null archived repositories" , err )
return - 1 , err
return err
}
if count > 0 {
if autofix {
updatedCount , err := models . FixNullArchivedRepository ( )
if err != nil {
logger . Critical ( "Error: %v whilst fixing null archived repositories" , err )
return err
}
logger . Info ( "%d repositories with null is_archived updated" , updatedCount )
} else {
logger . Warn ( "%d repositories with null is_archived" , count )
}
}
}
}
// find label comments with empty labels
func genericOrphanCheck ( name , subject , refobject , joincond string ) consistencyCheck {
count , err = models . CountCommentTypeLabelWithEmptyLabel ( )
return consistencyCheck {
if err != nil {
Name : name ,
logger . Critical ( "Error: %v whilst counting label comments with empty labels" , err )
Counter : func ( ) ( int64 , error ) {
return err
return models . CountOrphanedObjects ( subject , refobject , joincond )
}
} ,
if count > 0 {
Fixer : func ( ) ( int64 , error ) {
if autofix {
err := models . DeleteOrphanedObjects ( subject , refobject , joincond )
updatedCount , err := models . FixCommentTypeLabelWithEmptyLabel ( )
return - 1 , err
if err != nil {
} ,
logger . Critical ( "Error: %v whilst removing label comments with empty labels" , err )
return err
}
logger . Info ( "%d label comments with empty labels removed" , updatedCount )
} else {
logger . Warn ( "%d label comments with empty labels" , count )
}
}
}
}
// find label comments with labels from outside the repository
func checkDBConsistency ( logger log . Logger , autofix bool ) error {
count , err = models . CountCommentTypeLabelWithOutsideLabels ( )
// make sure DB version is uptodate
if err != nil {
if err := db . NewEngine ( context . Background ( ) , migrations . EnsureUpToDate ) ; err != nil {
logger . Critical ( "Error: %v whilst counting label comments with outside labels" , err )
logger . Critical ( "Model version on the database does not match the current Gitea version. Model consistency will not be checked until the database is upgraded" )
return err
return err
}
}
if count > 0 {
if autofix {
updatedCount , err := models . FixCommentTypeLabelWithOutsideLabels ( )
if err != nil {
logger . Critical ( "Error: %v whilst removing label comments with outside labels" , err )
return err
}
log . Info ( "%d label comments with outside labels removed" , updatedCount )
} else {
log . Warn ( "%d label comments with outside labels" , count )
}
}
// find issue_label with labels from outside the repository
consistencyChecks := [ ] consistencyCheck {
count , err = models . CountIssueLabelWithOutsideLabels ( )
{
if err != nil {
// find labels without existing repo or org
logger . Critical ( "Error: %v whilst counting issue_labels from outside the repository or organisation" , err )
Name : "Orphaned Labels without existing repository or organisation" ,
return err
Counter : models . CountOrphanedLabels ,
}
Fixer : asFixer ( models . DeleteOrphanedLabels ) ,
if count > 0 {
} ,
if autofix {
{
updatedCount , err := models . FixIssueLabelWithOutsideLabels ( )
// find IssueLabels without existing label
if err != nil {
Name : "Orphaned Issue Labels without existing label" ,
logger . Critical ( "Error: %v whilst removing issue_labels from outside the repository or organisation" , err )
Counter : models . CountOrphanedIssueLabels ,
return err
Fixer : asFixer ( models . DeleteOrphanedIssueLabels ) ,
}
} ,
logger . Info ( "%d issue_labels from outside the repository or organisation removed" , updatedCount )
{
} else {
// find issues without existing repository
logger . Warn ( "%d issue_labels from outside the repository or organisation" , count )
Name : "Orphaned Issues without existing repository" ,
}
Counter : models . CountOrphanedIssues ,
Fixer : asFixer ( models . DeleteOrphanedIssues ) ,
} ,
// find releases without existing repository
genericOrphanCheck ( "Orphaned Releases without existing repository" ,
"release" , "repository" , "release.repo_id=repository.id" ) ,
// find pulls without existing issues
genericOrphanCheck ( "Orphaned PullRequests without existing issue" ,
"pull_request" , "issue" , "pull_request.issue_id=issue.id" ) ,
// find tracked times without existing issues/pulls
genericOrphanCheck ( "Orphaned TrackedTimes without existing issue" ,
"tracked_time" , "issue" , "tracked_time.issue_id=issue.id" ) ,
// find attachments without existing issues or releases
{
Name : "Orphaned Attachments without existing issues or releases" ,
Counter : models . CountOrphanedAttachments ,
Fixer : asFixer ( models . DeleteOrphanedAttachments ) ,
} ,
// find null archived repositories
{
Name : "Repositories with is_archived IS NULL" ,
Counter : models . CountNullArchivedRepository ,
Fixer : models . FixNullArchivedRepository ,
FixedMessage : "Fixed" ,
} ,
// find label comments with empty labels
{
Name : "Label comments with empty labels" ,
Counter : models . CountCommentTypeLabelWithEmptyLabel ,
Fixer : models . FixCommentTypeLabelWithEmptyLabel ,
FixedMessage : "Fixed" ,
} ,
// find label comments with labels from outside the repository
{
Name : "Label comments with labels from outside the repository" ,
Counter : models . CountCommentTypeLabelWithOutsideLabels ,
Fixer : models . FixCommentTypeLabelWithOutsideLabels ,
FixedMessage : "Removed" ,
} ,
// find issue_label with labels from outside the repository
{
Name : "IssueLabels with Labels from outside the repository" ,
Counter : models . CountIssueLabelWithOutsideLabels ,
Fixer : models . FixIssueLabelWithOutsideLabels ,
FixedMessage : "Removed" ,
} ,
}
}
// TODO: function to recalc all counters
// TODO: function to recalc all counters
if setting . Database . UsePostgreSQL {
if setting . Database . UsePostgreSQL {
count , err = db . CountBadSequences ( )
consistencyChecks = append ( consistencyChecks , consistencyCheck {
if err != nil {
Name : "Sequence values" ,
logger . Critical ( "Error: %v whilst checking sequence values" , err )
Counter : db . CountBadSequences ,
Fixer : asFixer ( db . FixBadSequences ) ,
FixedMessage : "Updated" ,
} )
}
consistencyChecks = append ( consistencyChecks ,
// find protected branches without existing repository
genericOrphanCheck ( "Protected Branches without existing repository" ,
"protected_branch" , "repository" , "protected_branch.repo_id=repository.id" ) ,
// find deleted branches without existing repository
genericOrphanCheck ( "Deleted Branches without existing repository" ,
"deleted_branch" , "repository" , "deleted_branch.repo_id=repository.id" ) ,
// find LFS locks without existing repository
genericOrphanCheck ( "LFS locks without existing repository" ,
"lfs_lock" , "repository" , "lfs_lock.repo_id=repository.id" ) ,
// find collaborations without users
genericOrphanCheck ( "Collaborations without existing user" ,
"collaboration" , "user" , "collaboration.user_id=user.id" ) ,
// find collaborations without repository
genericOrphanCheck ( "Collaborations without existing repository" ,
"collaboration" , "repository" , "collaboration.repo_id=repository.id" ) ,
// find access without users
genericOrphanCheck ( "Access entries without existing user" ,
"access" , "user" , "access.user_id=user.id" ) ,
// find access without repository
genericOrphanCheck ( "Access entries without existing repository" ,
"access" , "repository" , "access.repo_id=repository.id" ) ,
)
for _ , c := range consistencyChecks {
if err := c . Run ( logger , autofix ) ; err != nil {
return err
return err
}
}
if count > 0 {
if autofix {
err := db . FixBadSequences ( )
if err != nil {
logger . Critical ( "Error: %v whilst attempting to fix sequences" , err )
return err
}
logger . Info ( "%d sequences updated" , count )
} else {
logger . Warn ( "%d sequences with incorrect values" , count )
}
}
}
// find protected branches without existing repository
count , err = models . CountOrphanedObjects ( "protected_branch" , "repository" , "protected_branch.repo_id=repository.id" )
if err != nil {
logger . Critical ( "Error: %v whilst counting orphaned objects" , err )
return err
}
if count > 0 {
if autofix {
if err = models . DeleteOrphanedObjects ( "protected_branch" , "repository" , "protected_branch.repo_id=repository.id" ) ; err != nil {
logger . Critical ( "Error: %v whilst deleting orphaned objects" , err )
return err
}
logger . Info ( "%d protected branches without existing repository deleted" , count )
} else {
logger . Warn ( "%d protected branches without existing repository" , count )
}
}
// find deleted branches without existing repository
count , err = models . CountOrphanedObjects ( "deleted_branch" , "repository" , "deleted_branch.repo_id=repository.id" )
if err != nil {
logger . Critical ( "Error: %v whilst counting orphaned objects" , err )
return err
}
if count > 0 {
if autofix {
if err = models . DeleteOrphanedObjects ( "deleted_branch" , "repository" , "deleted_branch.repo_id=repository.id" ) ; err != nil {
logger . Critical ( "Error: %v whilst deleting orphaned objects" , err )
return err
}
logger . Info ( "%d deleted branches without existing repository deleted" , count )
} else {
logger . Warn ( "%d deleted branches without existing repository" , count )
}
}
// find LFS locks without existing repository
count , err = models . CountOrphanedObjects ( "lfs_lock" , "repository" , "lfs_lock.repo_id=repository.id" )
if err != nil {
logger . Critical ( "Error: %v whilst counting orphaned objects" , err )
return err
}
if count > 0 {
if autofix {
if err = models . DeleteOrphanedObjects ( "lfs_lock" , "repository" , "lfs_lock.repo_id=repository.id" ) ; err != nil {
logger . Critical ( "Error: %v whilst deleting orphaned objects" , err )
return err
}
logger . Info ( "%d LFS locks without existing repository deleted" , count )
} else {
logger . Warn ( "%d LFS locks without existing repository" , count )
}
}
}
return nil
return nil