From 1dbc58f742febbe58df59d4ce4148d2dbec1a20f Mon Sep 17 00:00:00 2001 From: zeripath Date: Mon, 22 Nov 2021 22:32:16 +0000 Subject: [PATCH] More pleasantly handle broken or missing git repositories (#17747) * More pleasantly handle broken or missing git repositories In #17742 it was noted that there a completely invalid git repository underlying a repo on gitea.com. This happened due to a problem during a migration however, it is not beyond the realms of possibility that a corruption could occur to another user. This PR adds a check to RepoAssignment that will detect if a repository loading has failed due to an absent git repository. It will then show a page suggesting the user contacts the administrator or deletes the repository. Fix #17742 Signed-off-by: Andrew Thornton * Update options/locale/locale_en-US.ini Co-authored-by: techknowlogick Co-authored-by: techknowlogick --- models/repo.go | 6 ++++++ modules/context/repo.go | 34 ++++++++++++++++++++++++++++++++- options/locale/locale_en-US.ini | 1 + routers/private/serv.go | 8 ++++++++ templates/repo/empty.tmpl | 6 +++++- templates/repo/header.tmpl | 12 ++++++++++-- 6 files changed, 63 insertions(+), 4 deletions(-) diff --git a/models/repo.go b/models/repo.go index 7944149a76..860c5c4813 100644 --- a/models/repo.go +++ b/models/repo.go @@ -146,6 +146,7 @@ const ( RepositoryReady RepositoryStatus = iota // a normal repository RepositoryBeingMigrated // repository is migrating RepositoryPendingTransfer // repository pending in ownership transfer state + RepositoryBroken // repository is in a permanently broken state ) // TrustModelType defines the types of trust model for this repository @@ -289,6 +290,11 @@ func (repo *Repository) IsBeingCreated() bool { return repo.IsBeingMigrated() } +// IsBroken indicates that repository is broken +func (repo *Repository) IsBroken() bool { + return repo.Status == RepositoryBroken +} + // AfterLoad is invoked from XORM after setting the values of all fields of this object. func (repo *Repository) AfterLoad() { // FIXME: use models migration to solve all at once. diff --git a/modules/context/repo.go b/modules/context/repo.go index c96d34f2fc..1f1a035267 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -522,14 +522,30 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { } } + isHomeOrSettings := ctx.Link == ctx.Repo.RepoLink || ctx.Link == ctx.Repo.RepoLink+"/settings" || strings.HasPrefix(ctx.Link, ctx.Repo.RepoLink+"/settings/") + // Disable everything when the repo is being created - if ctx.Repo.Repository.IsBeingCreated() { + if ctx.Repo.Repository.IsBeingCreated() || ctx.Repo.Repository.IsBroken() { ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch + if !isHomeOrSettings { + ctx.Redirect(ctx.Repo.RepoLink) + } return } gitRepo, err := git.OpenRepository(models.RepoPath(userName, repoName)) if err != nil { + if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") { + log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RepoPath(), err) + ctx.Repo.Repository.Status = models.RepositoryBroken + ctx.Repo.Repository.IsEmpty = true + ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch + // Only allow access to base of repo or settings + if !isHomeOrSettings { + ctx.Redirect(ctx.Repo.RepoLink) + } + return + } ctx.ServerError("RepoAssignment Invalid repo "+models.RepoPath(userName, repoName), err) return } @@ -551,6 +567,17 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { tags, err := ctx.Repo.GitRepo.GetTags(0, 0) if err != nil { + if strings.Contains(err.Error(), "fatal: not a git repository ") { + log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RepoPath(), err) + ctx.Repo.Repository.Status = models.RepositoryBroken + ctx.Repo.Repository.IsEmpty = true + ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch + // Only allow access to base of repo or settings + if !isHomeOrSettings { + ctx.Redirect(ctx.Repo.RepoLink) + } + return + } ctx.ServerError("GetTags", err) return } @@ -919,6 +946,11 @@ func UnitTypes() func(ctx *Context) { // IssueTemplatesFromDefaultBranch checks for issue templates in the repo's default branch func (ctx *Context) IssueTemplatesFromDefaultBranch() []api.IssueTemplate { var issueTemplates []api.IssueTemplate + + if ctx.Repo.Repository.IsEmpty { + return issueTemplates + } + if ctx.Repo.Commit == nil { var err error ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 6e39b4b03d..ca6cd66625 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -946,6 +946,7 @@ clone_this_repo = Clone this repository create_new_repo_command = Creating a new repository on the command line push_exist_repo = Pushing an existing repository from the command line empty_message = This repository does not contain any content. +broken_message = The git data underlying this repository cannot be read. Contact the administrator of this instance or delete this repository. code = Code code.desc = Access source code, files, commits and branches. diff --git a/routers/private/serv.go b/routers/private/serv.go index a7ef980d2d..329d80476a 100644 --- a/routers/private/serv.go +++ b/routers/private/serv.go @@ -162,6 +162,14 @@ func ServCommand(ctx *context.PrivateContext) { return } + if repo.IsBroken() { + ctx.JSON(http.StatusInternalServerError, private.ErrServCommand{ + Results: results, + Err: "Repository is in a broken state", + }) + return + } + // We can shortcut at this point if the repo is a mirror if mode > models.AccessModeRead && repo.IsMirror { ctx.JSON(http.StatusForbidden, private.ErrServCommand{ diff --git a/templates/repo/empty.tmpl b/templates/repo/empty.tmpl index 20563f59e6..6da9e28e16 100644 --- a/templates/repo/empty.tmpl +++ b/templates/repo/empty.tmpl @@ -10,7 +10,11 @@ {{.i18n.Tr "repo.archive.title"}} {{end}} - {{if .CanWriteCode}} + {{if .Repository.IsBroken}} +
+ {{.i18n.Tr "repo.broken_message"}} +
+ {{else if .CanWriteCode}}

{{.i18n.Tr "repo.quick_guide"}}

diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 554456d230..4fb5e1941b 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -40,7 +40,7 @@ {{if .IsFork}}
{{$.i18n.Tr "repo.forked_from"}} {{.BaseRepo.FullName}}
{{end}} {{if .IsGenerated}}
{{$.i18n.Tr "repo.generated_from"}} {{.TemplateRepo.FullName}}
{{end}} - {{if not .IsBeingCreated}} + {{if not (or .IsBeingCreated .IsBroken)}}
{{if $.RepoTransfer}}
@@ -100,7 +100,7 @@
{{end}}
- {{if not .Repository.IsBeingCreated}} + {{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}} {{end}}
+ {{else if .Permission.IsAdmin}} + {{end}}