From 15c6bb500b8b395ab7ca61498f5415b9f583213c Mon Sep 17 00:00:00 2001
From: Allen Wild <aswild@users.noreply.github.com>
Date: Tue, 27 Mar 2018 10:13:20 -0400
Subject: [PATCH] Add repository setting to enable/disable health checks
 (#3607)

New Feature:
  * Repository struct field for IsFsckEnabled (default true of course)
  * Admin Settings section on repo options page, accessible only by
    admin users

Possible Enhancements:
  * There's no way to force running health checks on all repos
    regardless of their IsFsckEnabled setting. This would be useful if
    there were an admin API or dashboard button to run fsck immediately.

Issue: https://github.com/go-gitea/gitea/issues/1712
Signed-off-by: Allen Wild <allenwild93@gmail.com>
---
 models/migrations/migrations.go      |  2 ++
 models/migrations/v60.go             | 22 ++++++++++++++++++++++
 models/repo.go                       |  5 +++--
 modules/auth/repo_form.go            |  3 +++
 options/locale/locale_en-US.ini      |  2 ++
 routers/repo/setting.go              | 18 ++++++++++++++++++
 templates/repo/settings/options.tmpl | 23 +++++++++++++++++++++++
 7 files changed, 73 insertions(+), 2 deletions(-)
 create mode 100644 models/migrations/v60.go

diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 6c3404bcd7..36c84a61aa 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -172,6 +172,8 @@ var migrations = []Migration{
 	NewMigration("add label descriptions", addLabelsDescriptions),
 	// v59 -> v60
 	NewMigration("add merge whitelist for protected branches", addProtectedBranchMergeWhitelist),
+	// v60 -> v61
+	NewMigration("add is_fsck_enabled column for repos", addFsckEnabledToRepo),
 }
 
 // Migrate database to current version
diff --git a/models/migrations/v60.go b/models/migrations/v60.go
new file mode 100644
index 0000000000..13ec38241a
--- /dev/null
+++ b/models/migrations/v60.go
@@ -0,0 +1,22 @@
+// Copyright 2018 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 migrations
+
+import (
+	"fmt"
+
+	"github.com/go-xorm/xorm"
+)
+
+func addFsckEnabledToRepo(x *xorm.Engine) error {
+	type Repository struct {
+		IsFsckEnabled bool `xorm:"NOT NULL DEFAULT true"`
+	}
+
+	if err := x.Sync2(new(Repository)); err != nil {
+		return fmt.Errorf("Sync2: %v", err)
+	}
+	return nil
+}
diff --git a/models/repo.go b/models/repo.go
index e9b3307d9a..300bdbe875 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -198,6 +198,7 @@ type Repository struct {
 	BaseRepo      *Repository        `xorm:"-"`
 	Size          int64              `xorm:"NOT NULL DEFAULT 0"`
 	IndexerStatus *RepoIndexerStatus `xorm:"-"`
+	IsFsckEnabled bool               `xorm:"NOT NULL DEFAULT true"`
 
 	CreatedUnix util.TimeStamp `xorm:"INDEX created"`
 	UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
@@ -2173,12 +2174,12 @@ func GitFsck() {
 	log.Trace("Doing: GitFsck")
 
 	if err := x.
-		Where("id>0").BufferSize(setting.IterateBufferSize).
+		Where("id>0 AND is_fsck_enabled=?", true).BufferSize(setting.IterateBufferSize).
 		Iterate(new(Repository),
 			func(idx int, bean interface{}) error {
 				repo := bean.(*Repository)
 				repoPath := repo.RepoPath()
-				log.Trace(fmt.Sprintf("Running health check for repository %s", repoPath))
+				log.Trace("Running health check on repository %s", repoPath)
 				if err := git.Fsck(repoPath, setting.Cron.RepoHealthCheck.Timeout, setting.Cron.RepoHealthCheck.Args...); err != nil {
 					desc := fmt.Sprintf("Failed to health check repository (%s): %v", repoPath, err)
 					log.Warn(desc)
diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go
index 565428c350..7180a05665 100644
--- a/modules/auth/repo_form.go
+++ b/modules/auth/repo_form.go
@@ -113,6 +113,9 @@ type RepoSettingForm struct {
 	PullsAllowSquash                 bool
 	EnableTimetracker                bool
 	AllowOnlyContributorsToTrackTime bool
+
+	// Admin settings
+	EnableHealthCheck bool
 }
 
 // Validate validates the fields
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 0bc8309ff8..136b5e522a 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -910,6 +910,8 @@ settings.pulls.ignore_whitespace = Ignore changes in whitespace when checking co
 settings.pulls.allow_merge_commits = Allow merge commits
 settings.pulls.allow_rebase_merge = Allow rebase to merge commits
 settings.pulls.allow_squash_commits = Allow to squash commits before merging
+settings.admin_settings = Admin Settings
+settings.admin_enable_health_check = Enable health checks (git fsck) for this repo
 settings.danger_zone = Danger Zone
 settings.new_owner_has_same_repo = The new owner already has a repository with same name. Please choose another name.
 settings.convert = Convert To Regular Repository
diff --git a/routers/repo/setting.go b/routers/repo/setting.go
index 6986658b75..0d44cb50a8 100644
--- a/routers/repo/setting.go
+++ b/routers/repo/setting.go
@@ -229,6 +229,24 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
 		ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
 		ctx.Redirect(ctx.Repo.RepoLink + "/settings")
 
+	case "admin":
+		if !ctx.User.IsAdmin {
+			ctx.Error(403)
+			return
+		}
+
+		if repo.IsFsckEnabled != form.EnableHealthCheck {
+			repo.IsFsckEnabled = form.EnableHealthCheck
+			if err := models.UpdateRepository(repo, false); err != nil {
+				ctx.ServerError("UpdateRepository", err)
+				return
+			}
+			log.Trace("Repository admin settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+		}
+
+		ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
+		ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+
 	case "convert":
 		if !ctx.Repo.IsOwner() {
 			ctx.Error(404)
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 01814dcca8..8470d6a5e2 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -236,6 +236,29 @@
 			</form>
 		</div>
 
+		{{if .IsAdmin}}
+		<h4 class="ui top attached header">
+			{{.i18n.Tr "repo.settings.admin_settings"}}
+		</h4>
+		<div class="ui attached segment">
+			<form class="ui form" method="post">
+				{{.CsrfTokenHtml}}
+				<input type="hidden" name="action" value="admin">
+				<div class="field">
+					<div class="ui checkbox">
+						<input name="enable_health_check" type="checkbox" {{if .Repository.IsFsckEnabled}}checked{{end}}>
+						<label>{{.i18n.Tr "repo.settings.admin_enable_health_check"}}</label>
+					</div>
+				</div>
+
+				<div class="ui divider"></div>
+				<div class="field">
+					<button class="ui green button">{{$.i18n.Tr "repo.settings.update_settings"}}</button>
+				</div>
+			</form>
+		</div>
+		{{end}}
+
 		{{if .IsRepositoryOwner}}
 		<h4 class="ui top attached warning header">
 			{{.i18n.Tr "repo.settings.danger_zone"}}