Merge branch 'limit-repo-size' of https://github.com/DmitryFrolovTri/gitea into limit-repo-size

This commit is contained in:
truecode112
2023-05-26 16:53:25 +03:00
246 changed files with 8807 additions and 1303 deletions
+4 -4
View File
@@ -204,7 +204,7 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa
if err != nil {
break
}
if !workflowpattern.Skip(patterns, []string{refName.ShortName()}, &workflowpattern.EmptyTraceWriter{}) {
if !workflowpattern.Skip(patterns, []string{refName.BranchName()}, &workflowpattern.EmptyTraceWriter{}) {
matchTimes++
}
case "branches-ignore":
@@ -216,7 +216,7 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa
if err != nil {
break
}
if !workflowpattern.Filter(patterns, []string{refName.ShortName()}, &workflowpattern.EmptyTraceWriter{}) {
if !workflowpattern.Filter(patterns, []string{refName.BranchName()}, &workflowpattern.EmptyTraceWriter{}) {
matchTimes++
}
case "tags":
@@ -228,7 +228,7 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa
if err != nil {
break
}
if !workflowpattern.Skip(patterns, []string{refName.ShortName()}, &workflowpattern.EmptyTraceWriter{}) {
if !workflowpattern.Skip(patterns, []string{refName.TagName()}, &workflowpattern.EmptyTraceWriter{}) {
matchTimes++
}
case "tags-ignore":
@@ -240,7 +240,7 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa
if err != nil {
break
}
if !workflowpattern.Filter(patterns, []string{refName.ShortName()}, &workflowpattern.EmptyTraceWriter{}) {
if !workflowpattern.Filter(patterns, []string{refName.TagName()}, &workflowpattern.EmptyTraceWriter{}) {
matchTimes++
}
case "paths":
+104 -21
View File
@@ -6,6 +6,8 @@ package git
import (
"regexp"
"strings"
"code.gitea.io/gitea/modules/util"
)
const (
@@ -13,8 +15,6 @@ const (
RemotePrefix = "refs/remotes/"
// PullPrefix is the base directory of the pull information of git.
PullPrefix = "refs/pull/"
pullLen = len(PullPrefix)
)
// refNamePatternInvalid is regular expression with unallowed characters in git reference name
@@ -53,19 +53,32 @@ func (ref *Reference) Commit() (*Commit, error) {
return ref.repo.getCommit(ref.Object)
}
// ShortName returns the short name of the reference
func (ref *Reference) ShortName() string {
return RefName(ref.Name).ShortName()
}
// RefGroup returns the group type of the reference
func (ref *Reference) RefGroup() string {
return RefName(ref.Name).RefGroup()
}
// RefName represents a git reference name
// ForPrefix special ref to create a pull request: refs/for/<target-branch>/<topic-branch>
// or refs/for/<targe-branch> -o topic='<topic-branch>'
const ForPrefix = "refs/for/"
// TODO: /refs/for-review for suggest change interface
// RefName represents a full git reference name
type RefName string
func RefNameFromBranch(shortName string) RefName {
return RefName(BranchPrefix + shortName)
}
func RefNameFromTag(shortName string) RefName {
return RefName(TagPrefix + shortName)
}
func (ref RefName) String() string {
return string(ref)
}
func (ref RefName) IsBranch() bool {
return strings.HasPrefix(string(ref), BranchPrefix)
}
@@ -74,20 +87,71 @@ func (ref RefName) IsTag() bool {
return strings.HasPrefix(string(ref), TagPrefix)
}
func (ref RefName) IsRemote() bool {
return strings.HasPrefix(string(ref), RemotePrefix)
}
func (ref RefName) IsPull() bool {
return strings.HasPrefix(string(ref), PullPrefix) && strings.IndexByte(string(ref)[len(PullPrefix):], '/') > -1
}
func (ref RefName) IsFor() bool {
return strings.HasPrefix(string(ref), ForPrefix)
}
func (ref RefName) nameWithoutPrefix(prefix string) string {
if strings.HasPrefix(string(ref), prefix) {
return strings.TrimPrefix(string(ref), prefix)
}
return ""
}
// TagName returns simple tag name if it's an operation to a tag
func (ref RefName) TagName() string {
return ref.nameWithoutPrefix(TagPrefix)
}
// BranchName returns simple branch name if it's an operation to branch
func (ref RefName) BranchName() string {
return ref.nameWithoutPrefix(BranchPrefix)
}
// PullName returns the pull request name part of refs like refs/pull/<pull_name>/head
func (ref RefName) PullName() string {
refName := string(ref)
lastIdx := strings.LastIndexByte(refName[len(PullPrefix):], '/')
if strings.HasPrefix(refName, PullPrefix) && lastIdx > -1 {
return refName[len(PullPrefix) : lastIdx+len(PullPrefix)]
}
return ""
}
// ForBranchName returns the branch name part of refs like refs/for/<branch_name>
func (ref RefName) ForBranchName() string {
return ref.nameWithoutPrefix(ForPrefix)
}
func (ref RefName) RemoteName() string {
return ref.nameWithoutPrefix(RemotePrefix)
}
// ShortName returns the short name of the reference name
func (ref RefName) ShortName() string {
refName := string(ref)
if strings.HasPrefix(refName, BranchPrefix) {
return strings.TrimPrefix(refName, BranchPrefix)
if ref.IsBranch() {
return ref.BranchName()
}
if strings.HasPrefix(refName, TagPrefix) {
return strings.TrimPrefix(refName, TagPrefix)
if ref.IsTag() {
return ref.TagName()
}
if strings.HasPrefix(refName, RemotePrefix) {
return strings.TrimPrefix(refName, RemotePrefix)
if ref.IsRemote() {
return ref.RemoteName()
}
if strings.HasPrefix(refName, PullPrefix) && strings.IndexByte(refName[pullLen:], '/') > -1 {
return refName[pullLen : strings.IndexByte(refName[pullLen:], '/')+pullLen]
if ref.IsPull() {
return ref.PullName()
}
if ref.IsFor() {
return ref.ForBranchName()
}
return refName
@@ -95,18 +159,37 @@ func (ref RefName) ShortName() string {
// RefGroup returns the group type of the reference
func (ref RefName) RefGroup() string {
refName := string(ref)
if strings.HasPrefix(refName, BranchPrefix) {
if ref.IsBranch() {
return "heads"
}
if strings.HasPrefix(refName, TagPrefix) {
if ref.IsTag() {
return "tags"
}
if strings.HasPrefix(refName, RemotePrefix) {
if ref.IsRemote() {
return "remotes"
}
if strings.HasPrefix(refName, PullPrefix) && strings.IndexByte(refName[pullLen:], '/') > -1 {
if ref.IsPull() {
return "pull"
}
if ref.IsFor() {
return "for"
}
return ""
}
// RefURL returns the absolute URL for a ref in a repository
func RefURL(repoURL, ref string) string {
refFullName := RefName(ref)
refName := util.PathEscapeSegments(refFullName.ShortName())
switch {
case refFullName.IsBranch():
return repoURL + "/src/branch/" + refName
case refFullName.IsTag():
return repoURL + "/src/tag/" + refName
case !IsValidSHAPattern(ref):
// assume they mean a branch
return repoURL + "/src/branch/" + refName
default:
return repoURL + "/src/commit/" + refName
}
}
+38
View File
@@ -0,0 +1,38 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package git
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestRefName(t *testing.T) {
// Test branch names (with and without slash).
assert.Equal(t, "foo", RefName("refs/heads/foo").BranchName())
assert.Equal(t, "feature/foo", RefName("refs/heads/feature/foo").BranchName())
// Test tag names (with and without slash).
assert.Equal(t, "foo", RefName("refs/tags/foo").TagName())
assert.Equal(t, "release/foo", RefName("refs/tags/release/foo").TagName())
// Test pull names
assert.Equal(t, "1", RefName("refs/pull/1/head").PullName())
assert.Equal(t, "my/pull", RefName("refs/pull/my/pull/head").PullName())
// Test for branch names
assert.Equal(t, "main", RefName("refs/for/main").ForBranchName())
assert.Equal(t, "my/branch", RefName("refs/for/my/branch").ForBranchName())
// Test commit hashes.
assert.Equal(t, "c0ffee", RefName("c0ffee").ShortName())
}
func TestRefURL(t *testing.T) {
repoURL := "/user/repo"
assert.Equal(t, repoURL+"/src/branch/foo", RefURL(repoURL, "refs/heads/foo"))
assert.Equal(t, repoURL+"/src/tag/foo", RefURL(repoURL, "refs/tags/foo"))
assert.Equal(t, repoURL+"/src/commit/c0ffee", RefURL(repoURL, "c0ffee"))
}
-8
View File
@@ -14,14 +14,6 @@ import (
// BranchPrefix base dir of the branch information file store on git
const BranchPrefix = "refs/heads/"
// AGit Flow
// PullRequestPrefix special ref to create a pull request: refs/for/<targe-branch>/<topic-branch>
// or refs/for/<targe-branch> -o topic='<topic-branch>'
const PullRequestPrefix = "refs/for/"
// TODO: /refs/for-review for suggest change interface
// IsReferenceExist returns true if given reference exists in the repository.
func IsReferenceExist(ctx context.Context, repoPath, name string) bool {
_, _, err := NewCommand(ctx, "show-ref", "--verify").AddDashesAndList(name).RunStdString(&RunOpts{Dir: repoPath})
+39
View File
@@ -3,7 +3,46 @@
package git
import (
"strings"
"unicode"
)
const (
fileSizeLimit int64 = 16 * 1024 // 16 KiB
bigFileSize int64 = 1024 * 1024 // 1 MiB
)
// mergeLanguageStats mergers language names with different cases. The name with most upper case letters is used.
func mergeLanguageStats(stats map[string]int64) map[string]int64 {
names := map[string]struct {
uniqueName string
upperCount int
}{}
countUpper := func(s string) (count int) {
for _, r := range s {
if unicode.IsUpper(r) {
count++
}
}
return count
}
for name := range stats {
cnt := countUpper(name)
lower := strings.ToLower(name)
if cnt >= names[lower].upperCount {
names[lower] = struct {
uniqueName string
upperCount int
}{uniqueName: name, upperCount: cnt}
}
}
res := make(map[string]int64, len(names))
for name, num := range stats {
res[names[strings.ToLower(name)].uniqueName] += num
}
return res
}
+1 -1
View File
@@ -156,7 +156,7 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
sizes[firstExcludedLanguage] = firstExcludedLanguageSize
}
return sizes, nil
return mergeLanguageStats(sizes), nil
}
func readFile(f *object.File, limit int64) ([]byte, error) {
+4 -4
View File
@@ -180,7 +180,7 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
// FIXME: Why can't we split this and the IsGenerated tests to avoid reading the blob unless absolutely necessary?
// - eg. do the all the detection tests using filename first before reading content.
language := analyze.GetCodeLanguage(f.Name(), content)
if language == enry.OtherLanguage || language == "" {
if language == "" {
continue
}
@@ -192,8 +192,8 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
included, checked := includedLanguage[language]
if !checked {
langtype := enry.GetLanguageType(language)
included = langtype == enry.Programming || langtype == enry.Markup
langType := enry.GetLanguageType(language)
included = langType == enry.Programming || langType == enry.Markup
includedLanguage[language] = included
}
if included {
@@ -210,7 +210,7 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
sizes[firstExcludedLanguage] = firstExcludedLanguageSize
}
return sizes, nil
return mergeLanguageStats(sizes), nil
}
func discardFull(rd *bufio.Reader, discard int64) error {
+14
View File
@@ -30,3 +30,17 @@ func TestRepository_GetLanguageStats(t *testing.T) {
"Java": 112,
}, stats)
}
func TestMergeLanguageStats(t *testing.T) {
assert.EqualValues(t, map[string]int64{
"PHP": 1,
"python": 10,
"JAVA": 700,
}, mergeLanguageStats(map[string]int64{
"PHP": 1,
"python": 10,
"Java": 100,
"java": 200,
"JAVA": 400,
}))
}
-44
View File
@@ -10,8 +10,6 @@ import (
"strconv"
"strings"
"sync"
"code.gitea.io/gitea/modules/util"
)
// ObjectCache provides thread-safe cache operations.
@@ -78,48 +76,6 @@ func ConcatenateError(err error, stderr string) error {
return fmt.Errorf("%w - %s", err, stderr)
}
// RefEndName return the end name of a ref name
func RefEndName(refStr string) string {
if strings.HasPrefix(refStr, BranchPrefix) {
return refStr[len(BranchPrefix):]
}
if strings.HasPrefix(refStr, TagPrefix) {
return refStr[len(TagPrefix):]
}
return refStr
}
// RefURL returns the absolute URL for a ref in a repository
func RefURL(repoURL, ref string) string {
refName := util.PathEscapeSegments(RefEndName(ref))
switch {
case strings.HasPrefix(ref, BranchPrefix):
return repoURL + "/src/branch/" + refName
case strings.HasPrefix(ref, TagPrefix):
return repoURL + "/src/tag/" + refName
case !IsValidSHAPattern(ref):
// assume they mean a branch
return repoURL + "/src/branch/" + refName
default:
return repoURL + "/src/commit/" + refName
}
}
// SplitRefName splits a full refname to reftype and simple refname
func SplitRefName(refStr string) (string, string) {
if strings.HasPrefix(refStr, BranchPrefix) {
return BranchPrefix, refStr[len(BranchPrefix):]
}
if strings.HasPrefix(refStr, TagPrefix) {
return TagPrefix, refStr[len(TagPrefix):]
}
return "", refStr
}
// ParseBool returns the boolean value represented by the string as per git's git_config_bool
// true will be returned for the result if the string is empty, but valid will be false.
// "true", "yes", "on" are all true, true
-30
View File
@@ -1,30 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package git
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestRefEndName(t *testing.T) {
// Test branch names (with and without slash).
assert.Equal(t, "foo", RefEndName("refs/heads/foo"))
assert.Equal(t, "feature/foo", RefEndName("refs/heads/feature/foo"))
// Test tag names (with and without slash).
assert.Equal(t, "foo", RefEndName("refs/tags/foo"))
assert.Equal(t, "release/foo", RefEndName("refs/tags/release/foo"))
// Test commit hashes.
assert.Equal(t, "c0ffee", RefEndName("c0ffee"))
}
func TestRefURL(t *testing.T) {
repoURL := "/user/repo"
assert.Equal(t, repoURL+"/src/branch/foo", RefURL(repoURL, "refs/heads/foo"))
assert.Equal(t, repoURL+"/src/tag/foo", RefURL(repoURL, "refs/tags/foo"))
assert.Equal(t, repoURL+"/src/commit/c0ffee", RefURL(repoURL, "c0ffee"))
}
-43
View File
@@ -5,51 +5,8 @@ package graceful
import (
"context"
"time"
)
// ChannelContext is a context that wraps a channel and error as a context
type ChannelContext struct {
done <-chan struct{}
err error
}
// NewChannelContext creates a ChannelContext from a channel and error
func NewChannelContext(done <-chan struct{}, err error) *ChannelContext {
return &ChannelContext{
done: done,
err: err,
}
}
// Deadline returns the time when work done on behalf of this context
// should be canceled. There is no Deadline for a ChannelContext
func (ctx *ChannelContext) Deadline() (deadline time.Time, ok bool) {
return deadline, ok
}
// Done returns the channel provided at the creation of this context.
// When closed, work done on behalf of this context should be canceled.
func (ctx *ChannelContext) Done() <-chan struct{} {
return ctx.done
}
// Err returns nil, if Done is not closed. If Done is closed,
// Err returns the error provided at the creation of this context
func (ctx *ChannelContext) Err() error {
select {
case <-ctx.done:
return ctx.err
default:
return nil
}
}
// Value returns nil for all calls as no values are or can be associated with this context
func (ctx *ChannelContext) Value(key interface{}) interface{} {
return nil
}
// ShutdownContext returns a context.Context that is Done at shutdown
// Callers using this context should ensure that they are registered as a running server
// in order that they are waited for.
+11 -51
View File
@@ -23,6 +23,11 @@ const (
stateTerminate
)
type RunCanceler interface {
Run()
Cancel()
}
// There are some places that could inherit sockets:
//
// * HTTP or HTTPS main listener
@@ -55,46 +60,19 @@ func InitManager(ctx context.Context) {
})
}
// WithCallback is a runnable to call when the caller has finished
type WithCallback func(callback func())
// RunnableWithShutdownFns is a runnable with functions to run at shutdown and terminate
// After the callback to atShutdown is called and is complete, the main function must return.
// Similarly the callback function provided to atTerminate must return once termination is complete.
// Please note that use of the atShutdown and atTerminate callbacks will create go-routines that will wait till their respective signals
// - users must therefore be careful to only call these as necessary.
type RunnableWithShutdownFns func(atShutdown, atTerminate func(func()))
// RunWithShutdownFns takes a function that has both atShutdown and atTerminate callbacks
// After the callback to atShutdown is called and is complete, the main function must return.
// Similarly the callback function provided to atTerminate must return once termination is complete.
// Please note that use of the atShutdown and atTerminate callbacks will create go-routines that will wait till their respective signals
// - users must therefore be careful to only call these as necessary.
func (g *Manager) RunWithShutdownFns(run RunnableWithShutdownFns) {
// RunWithCancel helps to run a function with a custom context, the Cancel function will be called at shutdown
// The Cancel function should stop the Run function in predictable time.
func (g *Manager) RunWithCancel(rc RunCanceler) {
g.RunAtShutdown(context.Background(), rc.Cancel)
g.runningServerWaitGroup.Add(1)
defer g.runningServerWaitGroup.Done()
defer func() {
if err := recover(); err != nil {
log.Critical("PANIC during RunWithShutdownFns: %v\nStacktrace: %s", err, log.Stack(2))
log.Critical("PANIC during RunWithCancel: %v\nStacktrace: %s", err, log.Stack(2))
g.doShutdown()
}
}()
run(func(atShutdown func()) {
g.lock.Lock()
defer g.lock.Unlock()
g.toRunAtShutdown = append(g.toRunAtShutdown,
func() {
defer func() {
if err := recover(); err != nil {
log.Critical("PANIC during RunWithShutdownFns: %v\nStacktrace: %s", err, log.Stack(2))
g.doShutdown()
}
}()
atShutdown()
})
}, func(atTerminate func()) {
g.RunAtTerminate(atTerminate)
})
rc.Run()
}
// RunWithShutdownContext takes a function that has a context to watch for shutdown.
@@ -151,21 +129,6 @@ func (g *Manager) RunAtShutdown(ctx context.Context, shutdown func()) {
})
}
// RunAtHammer creates a go-routine to run the provided function at shutdown
func (g *Manager) RunAtHammer(hammer func()) {
g.lock.Lock()
defer g.lock.Unlock()
g.toRunAtHammer = append(g.toRunAtHammer,
func() {
defer func() {
if err := recover(); err != nil {
log.Critical("PANIC during RunAtHammer: %v\nStacktrace: %s", err, log.Stack(2))
}
}()
hammer()
})
}
func (g *Manager) doShutdown() {
if !g.setStateTransition(stateRunning, stateShuttingDown) {
g.DoImmediateHammer()
@@ -206,9 +169,6 @@ func (g *Manager) doHammerTime(d time.Duration) {
g.hammerCtxCancel()
atHammerCtx := pprof.WithLabels(g.terminateCtx, pprof.Labels("graceful-lifecycle", "post-hammer"))
pprof.SetGoroutineLabels(atHammerCtx)
for _, fn := range g.toRunAtHammer {
go fn()
}
}
g.lock.Unlock()
}
-1
View File
@@ -41,7 +41,6 @@ type Manager struct {
terminateWaitGroup sync.WaitGroup
toRunAtShutdown []func()
toRunAtHammer []func()
toRunAtTerminate []func()
}
-1
View File
@@ -50,7 +50,6 @@ type Manager struct {
shutdownRequested chan struct{}
toRunAtShutdown []func()
toRunAtHammer []func()
toRunAtTerminate []func()
}
+30 -3
View File
@@ -19,6 +19,7 @@ import (
"code.gitea.io/gitea/modules/queue"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
)
// SearchResult result of performing a search in a repo
@@ -91,6 +92,32 @@ func index(ctx context.Context, indexer Indexer, repoID int64) error {
return err
}
repoTypes := setting.Indexer.RepoIndexerRepoTypes
if len(repoTypes) == 0 {
repoTypes = []string{"sources"}
}
// skip forks from being indexed if unit is not present
if !util.SliceContains(repoTypes, "forks") && repo.IsFork {
return nil
}
// skip mirrors from being indexed if unit is not present
if !util.SliceContains(repoTypes, "mirrors") && repo.IsMirror {
return nil
}
// skip templates from being indexed if unit is not present
if !util.SliceContains(repoTypes, "templates") && repo.IsTemplate {
return nil
}
// skip regular repos from being indexed if unit is not present
if !util.SliceContains(repoTypes, "sources") && !repo.IsFork && !repo.IsMirror && !repo.IsTemplate {
return nil
}
sha, err := getDefaultBranchSha(ctx, repo)
if err != nil {
return err
@@ -139,7 +166,7 @@ func Init() {
handler := func(items ...*IndexerData) (unhandled []*IndexerData) {
idx, err := indexer.get()
if idx == nil || err != nil {
log.Error("Codes indexer handler: unable to get indexer!")
log.Warn("Codes indexer handler: indexer is not ready, retry later.")
return items
}
@@ -174,7 +201,7 @@ func Init() {
return unhandled
}
indexerQueue = queue.CreateUniqueQueue("code_indexer", handler)
indexerQueue = queue.CreateUniqueQueue(ctx, "code_indexer", handler)
if indexerQueue == nil {
log.Fatal("Unable to create codes indexer queue")
}
@@ -232,7 +259,7 @@ func Init() {
indexer.set(rIndexer)
// Start processing the queue
go graceful.GetManager().RunWithShutdownFns(indexerQueue.Run)
go graceful.GetManager().RunWithCancel(indexerQueue)
if populate {
go graceful.GetManager().RunWithShutdownContext(populateRepoIndexer)
+32 -39
View File
@@ -102,7 +102,7 @@ var (
func InitIssueIndexer(syncReindex bool) {
ctx, _, finished := process.GetManager().AddTypedContext(context.Background(), "Service: IssueIndexer", process.SystemProcessType, false)
waitChannel := make(chan time.Duration, 1)
indexerInitWaitChannel := make(chan time.Duration, 1)
// Create the Queue
switch setting.Indexer.IssueType {
@@ -110,7 +110,7 @@ func InitIssueIndexer(syncReindex bool) {
handler := func(items ...*IndexerData) (unhandled []*IndexerData) {
indexer := holder.get()
if indexer == nil {
log.Error("Issue indexer handler: unable to get indexer.")
log.Warn("Issue indexer handler: indexer is not ready, retry later.")
return items
}
toIndex := make([]*IndexerData, 0, len(items))
@@ -138,15 +138,17 @@ func InitIssueIndexer(syncReindex bool) {
return unhandled
}
issueIndexerQueue = queue.CreateSimpleQueue("issue_indexer", handler)
issueIndexerQueue = queue.CreateSimpleQueue(ctx, "issue_indexer", handler)
if issueIndexerQueue == nil {
log.Fatal("Unable to create issue indexer queue")
}
default:
issueIndexerQueue = queue.CreateSimpleQueue[*IndexerData]("issue_indexer", nil)
issueIndexerQueue = queue.CreateSimpleQueue[*IndexerData](ctx, "issue_indexer", nil)
}
graceful.GetManager().RunAtTerminate(finished)
// Create the Indexer
go func() {
pprof.SetGoroutineLabels(ctx)
@@ -178,51 +180,41 @@ func InitIssueIndexer(syncReindex bool) {
if issueIndexer != nil {
issueIndexer.Close()
}
finished()
log.Info("PID: %d Issue Indexer closed", os.Getpid())
})
log.Debug("Created Bleve Indexer")
case "elasticsearch":
graceful.GetManager().RunWithShutdownFns(func(_, atTerminate func(func())) {
pprof.SetGoroutineLabels(ctx)
issueIndexer, err := NewElasticSearchIndexer(setting.Indexer.IssueConnStr, setting.Indexer.IssueIndexerName)
if err != nil {
log.Fatal("Unable to initialize Elastic Search Issue Indexer at connection: %s Error: %v", setting.Indexer.IssueConnStr, err)
}
exist, err := issueIndexer.Init()
if err != nil {
log.Fatal("Unable to issueIndexer.Init with connection %s Error: %v", setting.Indexer.IssueConnStr, err)
}
populate = !exist
holder.set(issueIndexer)
atTerminate(finished)
})
issueIndexer, err := NewElasticSearchIndexer(setting.Indexer.IssueConnStr, setting.Indexer.IssueIndexerName)
if err != nil {
log.Fatal("Unable to initialize Elastic Search Issue Indexer at connection: %s Error: %v", setting.Indexer.IssueConnStr, err)
}
exist, err := issueIndexer.Init()
if err != nil {
log.Fatal("Unable to issueIndexer.Init with connection %s Error: %v", setting.Indexer.IssueConnStr, err)
}
populate = !exist
holder.set(issueIndexer)
case "db":
issueIndexer := &DBIndexer{}
holder.set(issueIndexer)
graceful.GetManager().RunAtTerminate(finished)
case "meilisearch":
graceful.GetManager().RunWithShutdownFns(func(_, atTerminate func(func())) {
pprof.SetGoroutineLabels(ctx)
issueIndexer, err := NewMeilisearchIndexer(setting.Indexer.IssueConnStr, setting.Indexer.IssueConnAuth, setting.Indexer.IssueIndexerName)
if err != nil {
log.Fatal("Unable to initialize Meilisearch Issue Indexer at connection: %s Error: %v", setting.Indexer.IssueConnStr, err)
}
exist, err := issueIndexer.Init()
if err != nil {
log.Fatal("Unable to issueIndexer.Init with connection %s Error: %v", setting.Indexer.IssueConnStr, err)
}
populate = !exist
holder.set(issueIndexer)
atTerminate(finished)
})
issueIndexer, err := NewMeilisearchIndexer(setting.Indexer.IssueConnStr, setting.Indexer.IssueConnAuth, setting.Indexer.IssueIndexerName)
if err != nil {
log.Fatal("Unable to initialize Meilisearch Issue Indexer at connection: %s Error: %v", setting.Indexer.IssueConnStr, err)
}
exist, err := issueIndexer.Init()
if err != nil {
log.Fatal("Unable to issueIndexer.Init with connection %s Error: %v", setting.Indexer.IssueConnStr, err)
}
populate = !exist
holder.set(issueIndexer)
default:
holder.cancel()
log.Fatal("Unknown issue indexer type: %s", setting.Indexer.IssueType)
}
// Start processing the queue
go graceful.GetManager().RunWithShutdownFns(issueIndexerQueue.Run)
go graceful.GetManager().RunWithCancel(issueIndexerQueue)
// Populate the index
if populate {
@@ -232,13 +224,14 @@ func InitIssueIndexer(syncReindex bool) {
go graceful.GetManager().RunWithShutdownContext(populateIssueIndexer)
}
}
waitChannel <- time.Since(start)
close(waitChannel)
indexerInitWaitChannel <- time.Since(start)
close(indexerInitWaitChannel)
}()
if syncReindex {
select {
case <-waitChannel:
case <-indexerInitWaitChannel:
case <-graceful.GetManager().IsShutdown():
}
} else if setting.Indexer.StartupTimeout > 0 {
@@ -249,7 +242,7 @@ func InitIssueIndexer(syncReindex bool) {
timeout += setting.GracefulHammerTime
}
select {
case duration := <-waitChannel:
case duration := <-indexerInitWaitChannel:
log.Info("Issue Indexer Initialization took %v", duration)
case <-graceful.GetManager().IsShutdown():
log.Warn("Shutdown occurred before issue index initialisation was complete")
+3 -5
View File
@@ -29,13 +29,11 @@ func handler(items ...int64) []int64 {
}
func initStatsQueue() error {
statsQueue = queue.CreateUniqueQueue("repo_stats_update", handler)
statsQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "repo_stats_update", handler)
if statsQueue == nil {
return fmt.Errorf("Unable to create repo_stats_update Queue")
return fmt.Errorf("unable to create repo_stats_update queue")
}
go graceful.GetManager().RunWithShutdownFns(statsQueue.Run)
go graceful.GetManager().RunWithCancel(statsQueue)
return nil
}
+10 -1
View File
@@ -8,6 +8,7 @@ import (
"fmt"
"io"
"regexp"
"runtime/pprof"
"time"
)
@@ -143,9 +144,17 @@ func eventWriterStartGo(ctx context.Context, w EventWriter, shared bool) {
}
w.Base().shared = shared
w.Base().stopped = make(chan struct{})
ctxDesc := "Logger: EventWriter: " + w.GetWriterName()
if shared {
ctxDesc = "Logger: EventWriter (shared): " + w.GetWriterName()
}
writerCtx, writerCancel := newContext(ctx, ctxDesc)
go func() {
defer writerCancel()
defer close(w.Base().stopped)
w.Run(ctx)
pprof.SetGoroutineLabels(writerCtx)
w.Run(writerCtx)
}()
}
+1 -1
View File
@@ -40,7 +40,7 @@ func TestConnLogger(t *testing.T) {
level := INFO
flags := LstdFlags | LUTC | Lfuncname
logger := NewLoggerWithWriters(context.Background(), NewEventWriterConn("test-conn", WriterMode{
logger := NewLoggerWithWriters(context.Background(), "test", NewEventWriterConn("test-conn", WriterMode{
Level: level,
Prefix: prefix,
Flags: FlagsFromBits(flags),
+18 -1
View File
@@ -4,14 +4,19 @@
package log
import (
"context"
"runtime"
"strings"
"sync/atomic"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/util/rotatingfilewriter"
)
var projectPackagePrefix string
var (
projectPackagePrefix string
processTraceDisabled atomic.Int64
)
func init() {
_, filename, _, _ := runtime.Caller(0)
@@ -24,6 +29,10 @@ func init() {
rotatingfilewriter.ErrorPrintf = FallbackErrorf
process.Trace = func(start bool, pid process.IDType, description string, parentPID process.IDType, typ string) {
// the logger manager has its own mutex lock, so it's safe to use "Load" here
if processTraceDisabled.Load() != 0 {
return
}
if start && parentPID != "" {
Log(1, TRACE, "Start %s: %s (from %s) (%s)", NewColoredValue(pid, FgHiYellow), description, NewColoredValue(parentPID, FgYellow), NewColoredValue(typ, Reset))
} else if start {
@@ -33,3 +42,11 @@ func init() {
}
}
}
func newContext(parent context.Context, desc string) (ctx context.Context, cancel context.CancelFunc) {
// the "process manager" also calls "log.Trace()" to output logs, so if we want to create new contexts by the manager, we need to disable the trace temporarily
processTraceDisabled.Add(1)
defer processTraceDisabled.Add(-1)
ctx, _, cancel = process.GetManager().AddTypedContext(parent, desc, process.SystemProcessType, false)
return ctx, cancel
}
+1 -1
View File
@@ -10,7 +10,7 @@ import (
// FallbackErrorf is the last chance to show an error if the logger has internal errors
func FallbackErrorf(format string, args ...any) {
_, _ = fmt.Fprintf(os.Stderr, format+"\n", args)
_, _ = fmt.Fprintf(os.Stderr, format+"\n", args...)
}
func GetLevel() Level {
+2 -2
View File
@@ -228,9 +228,9 @@ func (l *LoggerImpl) GetLevel() Level {
return Level(l.level.Load())
}
func NewLoggerWithWriters(ctx context.Context, writer ...EventWriter) *LoggerImpl {
func NewLoggerWithWriters(ctx context.Context, name string, writer ...EventWriter) *LoggerImpl {
l := &LoggerImpl{}
l.ctx, l.ctxCancel = context.WithCancel(ctx)
l.ctx, l.ctxCancel = newContext(ctx, "Logger: "+name)
l.LevelLogger = BaseLoggerToGeneralLogger(l)
l.eventWriters = map[string]EventWriter{}
l.syncLevelInternal()
+4 -4
View File
@@ -53,7 +53,7 @@ func newDummyWriter(name string, level Level, delay time.Duration) *dummyWriter
}
func TestLogger(t *testing.T) {
logger := NewLoggerWithWriters(context.Background())
logger := NewLoggerWithWriters(context.Background(), "test")
dump := logger.DumpWriters()
assert.EqualValues(t, 0, len(dump))
@@ -88,7 +88,7 @@ func TestLogger(t *testing.T) {
}
func TestLoggerPause(t *testing.T) {
logger := NewLoggerWithWriters(context.Background())
logger := NewLoggerWithWriters(context.Background(), "test")
w1 := newDummyWriter("dummy-1", DEBUG, 0)
logger.AddWriters(w1)
@@ -117,7 +117,7 @@ func (t testLogString) LogString() string {
}
func TestLoggerLogString(t *testing.T) {
logger := NewLoggerWithWriters(context.Background())
logger := NewLoggerWithWriters(context.Background(), "test")
w1 := newDummyWriter("dummy-1", DEBUG, 0)
w1.Mode.Colorize = true
@@ -130,7 +130,7 @@ func TestLoggerLogString(t *testing.T) {
}
func TestLoggerExpressionFilter(t *testing.T) {
logger := NewLoggerWithWriters(context.Background())
logger := NewLoggerWithWriters(context.Background(), "test")
w1 := newDummyWriter("dummy-1", DEBUG, 0)
w1.Mode.Expression = "foo.*"
+2 -2
View File
@@ -39,7 +39,7 @@ func (m *LoggerManager) GetLogger(name string) *LoggerImpl {
logger := m.loggers[name]
if logger == nil {
logger = NewLoggerWithWriters(m.ctx)
logger = NewLoggerWithWriters(m.ctx, name)
m.loggers[name] = logger
if name == DEFAULT {
m.defaultLogger.Store(logger)
@@ -137,6 +137,6 @@ func GetManager() *LoggerManager {
func NewManager() *LoggerManager {
m := &LoggerManager{writers: map[string]EventWriter{}, loggers: map[string]*LoggerImpl{}}
m.ctx, m.ctxCancel = context.WithCancel(context.Background())
m.ctx, m.ctxCancel = newContext(context.Background(), "LoggerManager")
return m
}
+5 -3
View File
@@ -33,9 +33,11 @@ func StartSyncMirrors(queueHandle func(data ...*SyncRequest) []*SyncRequest) {
if !setting.Mirror.Enabled {
return
}
mirrorQueue = queue.CreateUniqueQueue("mirror", queueHandle)
go graceful.GetManager().RunWithShutdownFns(mirrorQueue.Run)
mirrorQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "mirror", queueHandle)
if mirrorQueue == nil {
log.Fatal("Unable to create mirror queue")
}
go graceful.GetManager().RunWithCancel(mirrorQueue)
}
// AddPullMirrorToQueue adds repoID to mirror queue
+15 -14
View File
@@ -13,6 +13,7 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification/base"
@@ -321,7 +322,7 @@ func (a *actionNotifier) NotifyPushCommits(ctx context.Context, pusher *user_mod
opType := activities_model.ActionCommitRepo
// Check it's tag push or branch.
if opts.IsTag() {
if opts.RefFullName.IsTag() {
opType = activities_model.ActionPushTag
if opts.IsDelRef() {
opType = activities_model.ActionDeleteTag
@@ -337,16 +338,16 @@ func (a *actionNotifier) NotifyPushCommits(ctx context.Context, pusher *user_mod
Content: string(data),
RepoID: repo.ID,
Repo: repo,
RefName: opts.RefFullName,
RefName: opts.RefFullName.String(),
IsPrivate: repo.IsPrivate,
}); err != nil {
log.Error("NotifyWatchers: %v", err)
}
}
func (a *actionNotifier) NotifyCreateRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string) {
func (a *actionNotifier) NotifyCreateRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string) {
opType := activities_model.ActionCommitRepo
if refType == "tag" {
if refFullName.IsTag() {
// has sent same action in `NotifyPushCommits`, so skip it.
return
}
@@ -357,15 +358,15 @@ func (a *actionNotifier) NotifyCreateRef(ctx context.Context, doer *user_model.U
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
RefName: refFullName,
RefName: refFullName.String(),
}); err != nil {
log.Error("NotifyWatchers: %v", err)
}
}
func (a *actionNotifier) NotifyDeleteRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refType, refFullName string) {
func (a *actionNotifier) NotifyDeleteRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName) {
opType := activities_model.ActionDeleteBranch
if refType == "tag" {
if refFullName.IsTag() {
// has sent same action in `NotifyPushCommits`, so skip it.
return
}
@@ -376,7 +377,7 @@ func (a *actionNotifier) NotifyDeleteRef(ctx context.Context, doer *user_model.U
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
RefName: refFullName,
RefName: refFullName.String(),
}); err != nil {
log.Error("NotifyWatchers: %v", err)
}
@@ -396,14 +397,14 @@ func (a *actionNotifier) NotifySyncPushCommits(ctx context.Context, pusher *user
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
RefName: opts.RefFullName,
RefName: opts.RefFullName.String(),
Content: string(data),
}); err != nil {
log.Error("NotifyWatchers: %v", err)
}
}
func (a *actionNotifier) NotifySyncCreateRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string) {
func (a *actionNotifier) NotifySyncCreateRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string) {
if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
ActUserID: repo.OwnerID,
ActUser: repo.MustOwner(ctx),
@@ -411,13 +412,13 @@ func (a *actionNotifier) NotifySyncCreateRef(ctx context.Context, doer *user_mod
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
RefName: refFullName,
RefName: refFullName.String(),
}); err != nil {
log.Error("NotifyWatchers: %v", err)
}
}
func (a *actionNotifier) NotifySyncDeleteRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refType, refFullName string) {
func (a *actionNotifier) NotifySyncDeleteRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName) {
if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
ActUserID: repo.OwnerID,
ActUser: repo.MustOwner(ctx),
@@ -425,7 +426,7 @@ func (a *actionNotifier) NotifySyncDeleteRef(ctx context.Context, doer *user_mod
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
RefName: refFullName,
RefName: refFullName.String(),
}); err != nil {
log.Error("NotifyWatchers: %v", err)
}
@@ -444,7 +445,7 @@ func (a *actionNotifier) NotifyNewRelease(ctx context.Context, rel *repo_model.R
Repo: rel.Repo,
IsPrivate: rel.Repo.IsPrivate,
Content: rel.Title,
RefName: rel.TagName,
RefName: rel.TagName, // FIXME: use a full ref name?
}); err != nil {
log.Error("NotifyWatchers: %v", err)
}
+5 -4
View File
@@ -10,6 +10,7 @@ import (
packages_model "code.gitea.io/gitea/models/packages"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/repository"
)
@@ -54,11 +55,11 @@ type Notifier interface {
NotifyUpdateRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release)
NotifyDeleteRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release)
NotifyPushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits)
NotifyCreateRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string)
NotifyDeleteRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refType, refFullName string)
NotifyCreateRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string)
NotifyDeleteRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName)
NotifySyncPushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits)
NotifySyncCreateRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string)
NotifySyncDeleteRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refType, refFullName string)
NotifySyncCreateRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string)
NotifySyncDeleteRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName)
NotifyRepoPendingTransfer(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository)
NotifyPackageCreate(ctx context.Context, doer *user_model.User, pd *packages_model.PackageDescriptor)
NotifyPackageDelete(ctx context.Context, doer *user_model.User, pd *packages_model.PackageDescriptor)
+5 -4
View File
@@ -10,6 +10,7 @@ import (
packages_model "code.gitea.io/gitea/models/packages"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/repository"
)
@@ -161,11 +162,11 @@ func (*NullNotifier) NotifyPushCommits(ctx context.Context, pusher *user_model.U
}
// NotifyCreateRef notifies branch or tag creation to notifiers
func (*NullNotifier) NotifyCreateRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string) {
func (*NullNotifier) NotifyCreateRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string) {
}
// NotifyDeleteRef notifies branch or tag deletion to notifiers
func (*NullNotifier) NotifyDeleteRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refType, refFullName string) {
func (*NullNotifier) NotifyDeleteRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName) {
}
// NotifyRenameRepository places a place holder function
@@ -181,11 +182,11 @@ func (*NullNotifier) NotifySyncPushCommits(ctx context.Context, pusher *user_mod
}
// NotifySyncCreateRef places a place holder function
func (*NullNotifier) NotifySyncCreateRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string) {
func (*NullNotifier) NotifySyncCreateRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string) {
}
// NotifySyncDeleteRef places a place holder function
func (*NullNotifier) NotifySyncDeleteRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refType, refFullName string) {
func (*NullNotifier) NotifySyncDeleteRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName) {
}
// NotifyRepoPendingTransfer places a place holder function
+10 -3
View File
@@ -9,7 +9,6 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
code_indexer "code.gitea.io/gitea/modules/indexer/code"
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
stats_indexer "code.gitea.io/gitea/modules/indexer/stats"
@@ -126,7 +125,11 @@ func (r *indexerNotifier) NotifyMigrateRepository(ctx context.Context, doer, u *
}
func (r *indexerNotifier) NotifyPushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
if setting.Indexer.RepoIndexerEnabled && opts.RefFullName == git.BranchPrefix+repo.DefaultBranch {
if !opts.RefFullName.IsBranch() {
return
}
if setting.Indexer.RepoIndexerEnabled && opts.RefFullName.BranchName() == repo.DefaultBranch {
code_indexer.UpdateRepoIndexer(repo)
}
if err := stats_indexer.UpdateRepoIndexer(repo); err != nil {
@@ -135,7 +138,11 @@ func (r *indexerNotifier) NotifyPushCommits(ctx context.Context, pusher *user_mo
}
func (r *indexerNotifier) NotifySyncPushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
if setting.Indexer.RepoIndexerEnabled && opts.RefFullName == git.BranchPrefix+repo.DefaultBranch {
if !opts.RefFullName.IsBranch() {
return
}
if setting.Indexer.RepoIndexerEnabled && opts.RefFullName.BranchName() == repo.DefaultBranch {
code_indexer.UpdateRepoIndexer(repo)
}
if err := stats_indexer.UpdateRepoIndexer(repo); err != nil {
+9 -8
View File
@@ -10,6 +10,7 @@ import (
packages_model "code.gitea.io/gitea/models/packages"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification/action"
"code.gitea.io/gitea/modules/notification/base"
@@ -316,16 +317,16 @@ func NotifyPushCommits(ctx context.Context, pusher *user_model.User, repo *repo_
}
// NotifyCreateRef notifies branch or tag creation to notifiers
func NotifyCreateRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string) {
func NotifyCreateRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string) {
for _, notifier := range notifiers {
notifier.NotifyCreateRef(ctx, pusher, repo, refType, refFullName, refID)
notifier.NotifyCreateRef(ctx, pusher, repo, refFullName, refID)
}
}
// NotifyDeleteRef notifies branch or tag deletion to notifiers
func NotifyDeleteRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refType, refFullName string) {
func NotifyDeleteRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refFullName git.RefName) {
for _, notifier := range notifiers {
notifier.NotifyDeleteRef(ctx, pusher, repo, refType, refFullName)
notifier.NotifyDeleteRef(ctx, pusher, repo, refFullName)
}
}
@@ -337,16 +338,16 @@ func NotifySyncPushCommits(ctx context.Context, pusher *user_model.User, repo *r
}
// NotifySyncCreateRef notifies branch or tag creation to notifiers
func NotifySyncCreateRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string) {
func NotifySyncCreateRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string) {
for _, notifier := range notifiers {
notifier.NotifySyncCreateRef(ctx, pusher, repo, refType, refFullName, refID)
notifier.NotifySyncCreateRef(ctx, pusher, repo, refFullName, refID)
}
}
// NotifySyncDeleteRef notifies branch or tag deletion to notifiers
func NotifySyncDeleteRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refType, refFullName string) {
func NotifySyncDeleteRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refFullName git.RefName) {
for _, notifier := range notifiers {
notifier.NotifySyncDeleteRef(ctx, pusher, repo, refType, refFullName)
notifier.NotifySyncDeleteRef(ctx, pusher, repo, refFullName)
}
}
+5 -2
View File
@@ -37,7 +37,10 @@ var _ base.Notifier = &notificationService{}
// NewNotifier create a new notificationService notifier
func NewNotifier() base.Notifier {
ns := &notificationService{}
ns.issueQueue = queue.CreateSimpleQueue("notification-service", handler)
ns.issueQueue = queue.CreateSimpleQueue(graceful.GetManager().ShutdownContext(), "notification-service", handler)
if ns.issueQueue == nil {
log.Fatal("Unable to create notification-service queue")
}
return ns
}
@@ -51,7 +54,7 @@ func handler(items ...issueNotificationOpts) []issueNotificationOpts {
}
func (ns *notificationService) Run() {
go graceful.GetManager().RunWithShutdownFns(ns.issueQueue.Run)
go graceful.GetManager().RunWithCancel(ns.issueQueue) // TODO: using "go" here doesn't seem right, just leave it as old code
}
func (ns *notificationService) NotifyCreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository,
+3 -2
View File
@@ -10,6 +10,7 @@ import (
"strconv"
"time"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
)
@@ -44,7 +45,7 @@ func (g GitPushOptions) Bool(key string, def bool) bool {
type HookOptions struct {
OldCommitIDs []string
NewCommitIDs []string
RefFullNames []string
RefFullNames []git.RefName
UserID int64
UserName string
GitObjectDirectory string
@@ -89,7 +90,7 @@ type HookProcReceiveRefResult struct {
OldOID string
NewOID string
Ref string
OriginalRef string
OriginalRef git.RefName
IsForcePush bool
IsNotMatched bool
Err string
+9 -2
View File
@@ -167,6 +167,7 @@ func (pm *Manager) Add(ctx context.Context, description string, cancel context.C
pm.processMap[pid] = process
pm.mutex.Unlock()
Trace(true, pid, description, parentPID, processType)
pprofCtx := pprof.WithLabels(ctx, pprof.Labels(DescriptionPProfLabel, description, PPIDPProfLabel, string(parentPID), PIDPProfLabel, string(pid), ProcessTypePProfLabel, processType))
@@ -200,10 +201,16 @@ func (pm *Manager) nextPID() (start time.Time, pid IDType) {
}
func (pm *Manager) remove(process *process) {
deleted := false
pm.mutex.Lock()
defer pm.mutex.Unlock()
if p := pm.processMap[process.PID]; p == process {
if pm.processMap[process.PID] == process {
delete(pm.processMap, process.PID)
deleted = true
}
pm.mutex.Unlock()
if deleted {
Trace(false, process.PID, process.Description, process.ParentPID, process.Type)
}
}
+6 -6
View File
@@ -88,22 +88,22 @@ func (m *Manager) FlushAll(ctx context.Context, timeout time.Duration) error {
}
// CreateSimpleQueue creates a simple queue from global setting config provider by name
func CreateSimpleQueue[T any](name string, handler HandlerFuncT[T]) *WorkerPoolQueue[T] {
return createWorkerPoolQueue(name, setting.CfgProvider, handler, false)
func CreateSimpleQueue[T any](ctx context.Context, name string, handler HandlerFuncT[T]) *WorkerPoolQueue[T] {
return createWorkerPoolQueue(ctx, name, setting.CfgProvider, handler, false)
}
// CreateUniqueQueue creates a unique queue from global setting config provider by name
func CreateUniqueQueue[T any](name string, handler HandlerFuncT[T]) *WorkerPoolQueue[T] {
return createWorkerPoolQueue(name, setting.CfgProvider, handler, true)
func CreateUniqueQueue[T any](ctx context.Context, name string, handler HandlerFuncT[T]) *WorkerPoolQueue[T] {
return createWorkerPoolQueue(ctx, name, setting.CfgProvider, handler, true)
}
func createWorkerPoolQueue[T any](name string, cfgProvider setting.ConfigProvider, handler HandlerFuncT[T], unique bool) *WorkerPoolQueue[T] {
func createWorkerPoolQueue[T any](ctx context.Context, name string, cfgProvider setting.ConfigProvider, handler HandlerFuncT[T], unique bool) *WorkerPoolQueue[T] {
queueSetting, err := setting.GetQueueSettings(cfgProvider, name)
if err != nil {
log.Error("Failed to get queue settings for %q: %v", name, err)
return nil
}
w, err := NewWorkerPoolQueueBySetting(name, queueSetting, handler, unique)
w, err := NewWorkerPoolQueueWithContext(ctx, name, queueSetting, handler, unique)
if err != nil {
log.Error("Failed to create queue %q: %v", name, err)
return nil
+3 -3
View File
@@ -29,7 +29,7 @@ func TestManager(t *testing.T) {
if err != nil {
return nil, err
}
return NewWorkerPoolQueueBySetting(name, qs, func(s ...int) (unhandled []int) { return nil }, false)
return newWorkerPoolQueueForTest(name, qs, func(s ...int) (unhandled []int) { return nil }, false)
}
// test invalid CONN_STR
@@ -80,7 +80,7 @@ MAX_WORKERS = 2
assert.NoError(t, err)
q1 := createWorkerPoolQueue[string]("no-such", cfgProvider, nil, false)
q1 := createWorkerPoolQueue[string](context.Background(), "no-such", cfgProvider, nil, false)
assert.Equal(t, "no-such", q1.GetName())
assert.Equal(t, "dummy", q1.GetType()) // no handler, so it becomes dummy
assert.Equal(t, filepath.Join(setting.AppDataPath, "queues/dir1"), q1.baseConfig.DataFullDir)
@@ -96,7 +96,7 @@ MAX_WORKERS = 2
assert.Equal(t, "string", q1.GetItemTypeName())
qid1 := GetManager().qidCounter
q2 := createWorkerPoolQueue("sub", cfgProvider, func(s ...int) (unhandled []int) { return nil }, false)
q2 := createWorkerPoolQueue(context.Background(), "sub", cfgProvider, func(s ...int) (unhandled []int) { return nil }, false)
assert.Equal(t, "sub", q2.GetName())
assert.Equal(t, "level", q2.GetType())
assert.Equal(t, filepath.Join(setting.AppDataPath, "queues/dir2"), q2.baseConfig.DataFullDir)
+15 -9
View File
@@ -5,6 +5,7 @@ package queue
import (
"context"
"runtime/pprof"
"sync"
"sync/atomic"
"time"
@@ -13,9 +14,10 @@ import (
)
var (
infiniteTimerC = make(chan time.Time)
batchDebounceDuration = 100 * time.Millisecond
workerIdleDuration = 1 * time.Second
infiniteTimerC = make(chan time.Time)
batchDebounceDuration = 100 * time.Millisecond
workerIdleDuration = 1 * time.Second
shutdownDefaultTimeout = 2 * time.Second
unhandledItemRequeueDuration atomic.Int64 // to avoid data race during test
)
@@ -116,13 +118,15 @@ func (q *WorkerPoolQueue[T]) doWorkerHandle(batch []T) {
// If the queue is shutting down, it returns true and try to push the items
// Otherwise it does nothing and returns false
func (q *WorkerPoolQueue[T]) basePushForShutdown(items ...T) bool {
ctxShutdown := q.ctxShutdown.Load()
if ctxShutdown == nil {
shutdownTimeout := time.Duration(q.shutdownTimeout.Load())
if shutdownTimeout == 0 {
return false
}
ctxShutdown, ctxShutdownCancel := context.WithTimeout(context.Background(), shutdownTimeout)
defer ctxShutdownCancel()
for _, item := range items {
// if there is still any error, the queue can do nothing instead of losing the items
if err := q.baseQueue.PushItem(*ctxShutdown, q.marshal(item)); err != nil {
if err := q.baseQueue.PushItem(ctxShutdown, q.marshal(item)); err != nil {
log.Error("Failed to requeue item for queue %q when shutting down: %v", q.GetName(), err)
}
}
@@ -246,6 +250,8 @@ var skipFlushChan = make(chan flushType) // an empty flush chan, used to skip re
// doRun is the main loop of the queue. All related "doXxx" functions are executed in its context.
func (q *WorkerPoolQueue[T]) doRun() {
pprof.SetGoroutineLabels(q.ctxRun)
log.Debug("Queue %q starts running", q.GetName())
defer log.Debug("Queue %q stops running", q.GetName())
@@ -271,8 +277,8 @@ func (q *WorkerPoolQueue[T]) doRun() {
}
}
ctxShutdownPtr := q.ctxShutdown.Load()
if ctxShutdownPtr != nil {
shutdownTimeout := time.Duration(q.shutdownTimeout.Load())
if shutdownTimeout != 0 {
// if there is a shutdown context, try to push the items back to the base queue
q.basePushForShutdown(unhandled...)
workerDone := make(chan struct{})
@@ -280,7 +286,7 @@ func (q *WorkerPoolQueue[T]) doRun() {
go func() { wg.wg.Wait(); close(workerDone) }()
select {
case <-workerDone:
case <-(*ctxShutdownPtr).Done():
case <-time.After(shutdownTimeout):
log.Error("Queue %q is shutting down, but workers are still running after timeout", q.GetName())
}
} else {
+18 -15
View File
@@ -10,9 +10,9 @@ import (
"sync/atomic"
"time"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
)
@@ -21,8 +21,9 @@ import (
type WorkerPoolQueue[T any] struct {
ctxRun context.Context
ctxRunCancel context.CancelFunc
ctxShutdown atomic.Pointer[context.Context]
shutdownDone chan struct{}
shutdownDone chan struct{}
shutdownTimeout atomic.Int64 // in case some buggy handlers (workers) would hang forever, "shutdown" should finish in predictable time
origHandler HandlerFuncT[T]
safeHandler HandlerFuncT[T]
@@ -175,22 +176,19 @@ func (q *WorkerPoolQueue[T]) Has(data T) (bool, error) {
return q.baseQueue.HasItem(q.ctxRun, q.marshal(data))
}
func (q *WorkerPoolQueue[T]) Run(atShutdown, atTerminate func(func())) {
atShutdown(func() {
// in case some queue handlers are slow or have hanging bugs, at most wait for a short time
q.ShutdownWait(1 * time.Second)
})
func (q *WorkerPoolQueue[T]) Run() {
q.doRun()
}
func (q *WorkerPoolQueue[T]) Cancel() {
q.ctxRunCancel()
}
// ShutdownWait shuts down the queue, waits for all workers to finish their jobs, and pushes the unhandled items back to the base queue
// It waits for all workers (handlers) to finish their jobs, in case some buggy handlers would hang forever, a reasonable timeout is needed
func (q *WorkerPoolQueue[T]) ShutdownWait(timeout time.Duration) {
shutdownCtx, shutdownCtxCancel := context.WithTimeout(context.Background(), timeout)
defer shutdownCtxCancel()
if q.ctxShutdown.CompareAndSwap(nil, &shutdownCtx) {
q.ctxRunCancel()
}
q.shutdownTimeout.Store(int64(timeout))
q.ctxRunCancel()
<-q.shutdownDone
}
@@ -207,7 +205,11 @@ func getNewQueueFn(t string) (string, func(cfg *BaseConfig, unique bool) (baseQu
}
}
func NewWorkerPoolQueueBySetting[T any](name string, queueSetting setting.QueueSettings, handler HandlerFuncT[T], unique bool) (*WorkerPoolQueue[T], error) {
func newWorkerPoolQueueForTest[T any](name string, queueSetting setting.QueueSettings, handler HandlerFuncT[T], unique bool) (*WorkerPoolQueue[T], error) {
return NewWorkerPoolQueueWithContext(context.Background(), name, queueSetting, handler, unique)
}
func NewWorkerPoolQueueWithContext[T any](ctx context.Context, name string, queueSetting setting.QueueSettings, handler HandlerFuncT[T], unique bool) (*WorkerPoolQueue[T], error) {
if handler == nil {
log.Debug("Use dummy queue for %q because handler is nil and caller doesn't want to process the queue items", name)
queueSetting.Type = "dummy"
@@ -224,10 +226,11 @@ func NewWorkerPoolQueueBySetting[T any](name string, queueSetting setting.QueueS
}
log.Trace("Created queue %q of type %q", name, queueType)
w.ctxRun, w.ctxRunCancel = context.WithCancel(graceful.GetManager().ShutdownContext())
w.ctxRun, _, w.ctxRunCancel = process.GetManager().AddTypedContext(ctx, "Queue: "+w.GetName(), process.SystemProcessType, false)
w.batchChan = make(chan []T)
w.flushChan = make(chan flushType)
w.shutdownDone = make(chan struct{})
w.shutdownTimeout.Store(int64(shutdownDefaultTimeout))
w.workerMaxNum = queueSetting.MaxWorkers
w.batchLength = queueSetting.BatchLength
+11 -19
View File
@@ -16,17 +16,9 @@ import (
)
func runWorkerPoolQueue[T any](q *WorkerPoolQueue[T]) func() {
var stop func()
started := make(chan struct{})
stopped := make(chan struct{})
go func() {
q.Run(func(f func()) { stop = f; close(started) }, nil)
close(stopped)
}()
<-started
go q.Run()
return func() {
stop()
<-stopped
q.ShutdownWait(1 * time.Second)
}
}
@@ -57,7 +49,7 @@ func TestWorkerPoolQueueUnhandled(t *testing.T) {
return unhandled
}
q, _ := NewWorkerPoolQueueBySetting("test-workpoolqueue", queueSetting, handler, false)
q, _ := newWorkerPoolQueueForTest("test-workpoolqueue", queueSetting, handler, false)
stop := runWorkerPoolQueue(q)
for i := 0; i < queueSetting.Length; i++ {
testRecorder.Record("push:%v", i)
@@ -145,7 +137,7 @@ func testWorkerPoolQueuePersistence(t *testing.T, queueSetting setting.QueueSett
return nil
}
q, _ := NewWorkerPoolQueueBySetting("pr_patch_checker_test", queueSetting, testHandler, true)
q, _ := newWorkerPoolQueueForTest("pr_patch_checker_test", queueSetting, testHandler, true)
stop := runWorkerPoolQueue(q)
for i := 0; i < testCount; i++ {
_ = q.Push("task-" + strconv.Itoa(i))
@@ -169,7 +161,7 @@ func testWorkerPoolQueuePersistence(t *testing.T, queueSetting setting.QueueSett
return nil
}
q, _ := NewWorkerPoolQueueBySetting("pr_patch_checker_test", queueSetting, testHandler, true)
q, _ := newWorkerPoolQueueForTest("pr_patch_checker_test", queueSetting, testHandler, true)
stop := runWorkerPoolQueue(q)
assert.NoError(t, q.FlushWithContext(context.Background(), 0))
stop()
@@ -194,7 +186,7 @@ func TestWorkerPoolQueueActiveWorkers(t *testing.T) {
return nil
}
q, _ := NewWorkerPoolQueueBySetting("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 1, Length: 100}, handler, false)
q, _ := newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 1, Length: 100}, handler, false)
stop := runWorkerPoolQueue(q)
for i := 0; i < 5; i++ {
assert.NoError(t, q.Push(i))
@@ -210,7 +202,7 @@ func TestWorkerPoolQueueActiveWorkers(t *testing.T) {
assert.EqualValues(t, 1, q.GetWorkerNumber()) // there is at least one worker after the queue begins working
stop()
q, _ = NewWorkerPoolQueueBySetting("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 3, Length: 100}, handler, false)
q, _ = newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 3, Length: 100}, handler, false)
stop = runWorkerPoolQueue(q)
for i := 0; i < 15; i++ {
assert.NoError(t, q.Push(i))
@@ -238,23 +230,23 @@ func TestWorkerPoolQueueShutdown(t *testing.T) {
if items[0] == 0 {
close(handlerCalled)
}
time.Sleep(100 * time.Millisecond)
time.Sleep(400 * time.Millisecond)
return items
}
qs := setting.QueueSettings{Type: "level", Datadir: t.TempDir() + "/queue", BatchLength: 3, MaxWorkers: 4, Length: 20}
q, _ := NewWorkerPoolQueueBySetting("test-workpoolqueue", qs, handler, false)
q, _ := newWorkerPoolQueueForTest("test-workpoolqueue", qs, handler, false)
stop := runWorkerPoolQueue(q)
for i := 0; i < qs.Length; i++ {
assert.NoError(t, q.Push(i))
}
<-handlerCalled
time.Sleep(50 * time.Millisecond) // wait for a while to make sure all workers are active
time.Sleep(200 * time.Millisecond) // wait for a while to make sure all workers are active
assert.EqualValues(t, 4, q.GetWorkerActiveNumber())
stop() // stop triggers shutdown
assert.EqualValues(t, 0, q.GetWorkerActiveNumber())
// no item was ever handled, so we still get all of them again
q, _ = NewWorkerPoolQueueBySetting("test-workpoolqueue", qs, handler, false)
q, _ = newWorkerPoolQueueForTest("test-workpoolqueue", qs, handler, false)
assert.EqualValues(t, 20, q.GetQueueItemNumber())
}
+7 -34
View File
@@ -4,8 +4,6 @@
package repository
import (
"strings"
"code.gitea.io/gitea/modules/git"
)
@@ -15,7 +13,7 @@ type PushUpdateOptions struct {
PusherName string
RepoUserName string
RepoName string
RefFullName string // branch, tag or other name to push
RefFullName git.RefName // branch, tag or other name to push
OldCommitID string
NewCommitID string
}
@@ -35,59 +33,34 @@ func (opts *PushUpdateOptions) IsUpdateRef() bool {
return !opts.IsNewRef() && !opts.IsDelRef()
}
// IsTag return true if it's an operation to a tag
func (opts *PushUpdateOptions) IsTag() bool {
return strings.HasPrefix(opts.RefFullName, git.TagPrefix)
}
// IsNewTag return true if it's a creation to a tag
func (opts *PushUpdateOptions) IsNewTag() bool {
return opts.IsTag() && opts.IsNewRef()
return opts.RefFullName.IsTag() && opts.IsNewRef()
}
// IsDelTag return true if it's a deletion to a tag
func (opts *PushUpdateOptions) IsDelTag() bool {
return opts.IsTag() && opts.IsDelRef()
}
// IsBranch return true if it's a push to branch
func (opts *PushUpdateOptions) IsBranch() bool {
return strings.HasPrefix(opts.RefFullName, git.BranchPrefix)
return opts.RefFullName.IsTag() && opts.IsDelRef()
}
// IsNewBranch return true if it's the first-time push to a branch
func (opts *PushUpdateOptions) IsNewBranch() bool {
return opts.IsBranch() && opts.IsNewRef()
return opts.RefFullName.IsBranch() && opts.IsNewRef()
}
// IsUpdateBranch return true if it's not the first push to a branch
func (opts *PushUpdateOptions) IsUpdateBranch() bool {
return opts.IsBranch() && opts.IsUpdateRef()
return opts.RefFullName.IsBranch() && opts.IsUpdateRef()
}
// IsDelBranch return true if it's a deletion to a branch
func (opts *PushUpdateOptions) IsDelBranch() bool {
return opts.IsBranch() && opts.IsDelRef()
}
// TagName returns simple tag name if it's an operation to a tag
func (opts *PushUpdateOptions) TagName() string {
return opts.RefFullName[len(git.TagPrefix):]
}
// BranchName returns simple branch name if it's an operation to branch
func (opts *PushUpdateOptions) BranchName() string {
return opts.RefFullName[len(git.BranchPrefix):]
return opts.RefFullName.IsBranch() && opts.IsDelRef()
}
// RefName returns simple name for ref
func (opts *PushUpdateOptions) RefName() string {
if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
return opts.RefFullName[len(git.TagPrefix):]
} else if strings.HasPrefix(opts.RefFullName, git.BranchPrefix) {
return opts.RefFullName[len(git.BranchPrefix):]
}
return ""
return opts.RefFullName.ShortName()
}
// RepoFullName returns repo full name
+10 -1
View File
@@ -200,7 +200,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
mirrorModel.Interval = 0
mirrorModel.NextUpdateUnix = 0
} else if parsedInterval < setting.Mirror.MinInterval {
err := fmt.Errorf("Interval %s is set below Minimum Interval of %s", parsedInterval, setting.Mirror.MinInterval)
err := fmt.Errorf("interval %s is set below Minimum Interval of %s", parsedInterval, setting.Mirror.MinInterval)
log.Error("Interval: %s is too frequent", opts.MirrorInterval)
return repo, err
} else {
@@ -217,6 +217,15 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
if err = UpdateRepository(ctx, repo, false); err != nil {
return nil, err
}
// this is necessary for sync local tags from remote
configName := fmt.Sprintf("remote.%s.fetch", mirrorModel.GetRemoteName())
if stdout, _, err := git.NewCommand(ctx, "config").
AddOptionValues("--add", configName, `+refs/tags/*:refs/tags/*`).
RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
log.Error("MigrateRepositoryGitData(git config --add <remote> +refs/tags/*:refs/tags/*) in %v: Stdout: %s\nError: %v", repo, stdout, err)
return repo, fmt.Errorf("error in MigrateRepositoryGitData(git config --add <remote> +refs/tags/*:refs/tags/*): %w", err)
}
} else {
if err = UpdateRepoSize(ctx, repo); err != nil {
log.Error("Failed to update size for repository: %v", err)
+19 -16
View File
@@ -23,15 +23,16 @@ var Indexer = struct {
IssueIndexerName string
StartupTimeout time.Duration
RepoIndexerEnabled bool
RepoType string
RepoPath string
RepoConnStr string
RepoIndexerName string
MaxIndexerFileSize int64
IncludePatterns []glob.Glob
ExcludePatterns []glob.Glob
ExcludeVendored bool
RepoIndexerEnabled bool
RepoIndexerRepoTypes []string
RepoType string
RepoPath string
RepoConnStr string
RepoIndexerName string
MaxIndexerFileSize int64
IncludePatterns []glob.Glob
ExcludePatterns []glob.Glob
ExcludeVendored bool
}{
IssueType: "bleve",
IssuePath: "indexers/issues.bleve",
@@ -39,13 +40,14 @@ var Indexer = struct {
IssueConnAuth: "",
IssueIndexerName: "gitea_issues",
RepoIndexerEnabled: false,
RepoType: "bleve",
RepoPath: "indexers/repos.bleve",
RepoConnStr: "",
RepoIndexerName: "gitea_codes",
MaxIndexerFileSize: 1024 * 1024,
ExcludeVendored: true,
RepoIndexerEnabled: false,
RepoIndexerRepoTypes: []string{"sources", "forks", "mirrors", "templates"},
RepoType: "bleve",
RepoPath: "indexers/repos.bleve",
RepoConnStr: "",
RepoIndexerName: "gitea_codes",
MaxIndexerFileSize: 1024 * 1024,
ExcludeVendored: true,
}
func loadIndexerFrom(rootCfg ConfigProvider) {
@@ -71,6 +73,7 @@ func loadIndexerFrom(rootCfg ConfigProvider) {
Indexer.IssueIndexerName = sec.Key("ISSUE_INDEXER_NAME").MustString(Indexer.IssueIndexerName)
Indexer.RepoIndexerEnabled = sec.Key("REPO_INDEXER_ENABLED").MustBool(false)
Indexer.RepoIndexerRepoTypes = strings.Split(sec.Key("REPO_INDEXER_REPO_TYPES").MustString("sources,forks,mirrors,templates"), ",")
Indexer.RepoType = sec.Key("REPO_INDEXER_TYPE").MustString("bleve")
Indexer.RepoPath = filepath.ToSlash(sec.Key("REPO_INDEXER_PATH").MustString(filepath.ToSlash(filepath.Join(AppDataPath, "indexers/repos.bleve"))))
if !filepath.IsAbs(Indexer.RepoPath) {
+3
View File
@@ -92,6 +92,7 @@ var (
// Issue Setting
Issue struct {
LockReasons []string
MaxPinned int
} `ini:"repository.issue"`
Release struct {
@@ -229,8 +230,10 @@ var (
// Issue settings
Issue: struct {
LockReasons []string
MaxPinned int
}{
LockReasons: strings.Split("Too heated,Off-topic,Spam,Resolved", ","),
MaxPinned: 3,
},
Release: struct {
+7 -1
View File
@@ -251,7 +251,13 @@ func loadRunModeFrom(rootCfg ConfigProvider) {
if RunMode == "" {
RunMode = rootSec.Key("RUN_MODE").MustString("prod")
}
IsProd = strings.EqualFold(RunMode, "prod")
// non-dev mode is treated as prod mode, to protect users from accidentally running in dev mode if there is a typo in this value.
RunMode = strings.ToLower(RunMode)
if RunMode != "dev" {
RunMode = "prod"
}
IsProd = RunMode != "dev"
// check if we run as root
if os.Getuid() == 0 {
+13 -8
View File
@@ -342,6 +342,10 @@ const (
HookIssueDemilestoned HookIssueAction = "demilestoned"
// HookIssueReviewed is an issue action for when a pull request is reviewed
HookIssueReviewed HookIssueAction = "reviewed"
// HookIssueReviewRequested is an issue action for when a reviewer is requested for a pull request.
HookIssueReviewRequested HookIssueAction = "review_requested"
// HookIssueReviewRequestRemoved is an issue action for removing a review request to someone on a pull request.
HookIssueReviewRequestRemoved HookIssueAction = "review_request_removed"
)
// IssuePayload represents the payload information that is sent along with an issue event.
@@ -381,14 +385,15 @@ type ChangesPayload struct {
// PullRequestPayload represents a payload information of pull request event.
type PullRequestPayload struct {
Action HookIssueAction `json:"action"`
Index int64 `json:"number"`
Changes *ChangesPayload `json:"changes,omitempty"`
PullRequest *PullRequest `json:"pull_request"`
Repository *Repository `json:"repository"`
Sender *User `json:"sender"`
CommitID string `json:"commit_id"`
Review *ReviewPayload `json:"review"`
Action HookIssueAction `json:"action"`
Index int64 `json:"number"`
Changes *ChangesPayload `json:"changes,omitempty"`
PullRequest *PullRequest `json:"pull_request"`
RequestedReviewer *User `json:"requested_reviewer"`
Repository *Repository `json:"repository"`
Sender *User `json:"sender"`
CommitID string `json:"commit_id"`
Review *ReviewPayload `json:"review"`
}
// JSONPayload FIXME
+2
View File
@@ -75,6 +75,8 @@ type Issue struct {
PullRequest *PullRequestMeta `json:"pull_request"`
Repo *RepositoryMeta `json:"repository"`
PinOrder int `json:"pin_order"`
}
// CreateIssueOption options to create one issue
+16 -13
View File
@@ -9,19 +9,20 @@ import (
// PullRequest represents a pull request
type PullRequest struct {
ID int64 `json:"id"`
URL string `json:"url"`
Index int64 `json:"number"`
Poster *User `json:"user"`
Title string `json:"title"`
Body string `json:"body"`
Labels []*Label `json:"labels"`
Milestone *Milestone `json:"milestone"`
Assignee *User `json:"assignee"`
Assignees []*User `json:"assignees"`
State StateType `json:"state"`
IsLocked bool `json:"is_locked"`
Comments int `json:"comments"`
ID int64 `json:"id"`
URL string `json:"url"`
Index int64 `json:"number"`
Poster *User `json:"user"`
Title string `json:"title"`
Body string `json:"body"`
Labels []*Label `json:"labels"`
Milestone *Milestone `json:"milestone"`
Assignee *User `json:"assignee"`
Assignees []*User `json:"assignees"`
RequestedReviewers []*User `json:"requested_reviewers"`
State StateType `json:"state"`
IsLocked bool `json:"is_locked"`
Comments int `json:"comments"`
HTMLURL string `json:"html_url"`
DiffURL string `json:"diff_url"`
@@ -48,6 +49,8 @@ type PullRequest struct {
Updated *time.Time `json:"updated_at"`
// swagger:strfmt date-time
Closed *time.Time `json:"closed_at"`
PinOrder int `json:"pin_order"`
}
// PRBranchInfo information about a branch
+6
View File
@@ -378,3 +378,9 @@ type RepoTransfer struct {
Recipient *User `json:"recipient"`
Teams []*Team `json:"teams"`
}
// NewIssuePinsAllowed represents an API response that says if new Issue Pins are allowed
type NewIssuePinsAllowed struct {
Issues bool `json:"issues"`
PullRequests bool `json:"pull_requests"`
}
+14 -7
View File
@@ -97,6 +97,7 @@ func HTMLRenderer() *HTMLRender {
}
func ReloadHTMLTemplates() error {
log.Trace("Reloading HTML templates")
if err := htmlRender.CompileTemplates(); err != nil {
log.Error("Template error: %v\n%s", err, log.Stack(2))
return err
@@ -114,11 +115,11 @@ func initHTMLRenderer() {
htmlRender = &HTMLRender{}
if err := htmlRender.CompileTemplates(); err != nil {
p := &templateErrorPrettier{assets: AssetFS()}
wrapFatal(p.handleFuncNotDefinedError(err))
wrapFatal(p.handleUnexpectedOperandError(err))
wrapFatal(p.handleExpectedEndError(err))
wrapFatal(p.handleGenericTemplateError(err))
log.Fatal("HTMLRenderer CompileTemplates error: %v", err)
wrapTmplErrMsg(p.handleFuncNotDefinedError(err))
wrapTmplErrMsg(p.handleUnexpectedOperandError(err))
wrapTmplErrMsg(p.handleExpectedEndError(err))
wrapTmplErrMsg(p.handleGenericTemplateError(err))
wrapTmplErrMsg(fmt.Sprintf("CompileTemplates error: %v", err))
}
if !setting.IsProd {
@@ -128,11 +129,17 @@ func initHTMLRenderer() {
}
}
func wrapFatal(msg string) {
func wrapTmplErrMsg(msg string) {
if msg == "" {
return
}
log.Fatal("Unable to compile templates, %s", msg)
if setting.IsProd {
// in prod mode, Gitea must have correct templates to run
log.Fatal("Gitea can't run with template errors: %s", msg)
} else {
// in dev mode, do not need to really exit, because the template errors could be fixed by developer soon and the templates get reloaded
log.Error("There are template errors but Gitea continues to run in dev mode: %s", msg)
}
}
type templateErrorPrettier struct {
+11 -4
View File
@@ -61,7 +61,10 @@ func Mailer(ctx context.Context) (*texttmpl.Template, *template.Template) {
bodyTemplates.Funcs(NewFuncMap())
assetFS := AssetFS()
refreshTemplates := func() {
refreshTemplates := func(firstRun bool) {
if !firstRun {
log.Trace("Reloading mail templates")
}
assetPaths, err := ListMailTemplateAssetNames(assetFS)
if err != nil {
log.Error("Failed to list mail templates: %v", err)
@@ -75,17 +78,21 @@ func Mailer(ctx context.Context) (*texttmpl.Template, *template.Template) {
continue
}
tmplName := strings.TrimPrefix(strings.TrimSuffix(assetPath, ".tmpl"), "mail/")
log.Trace("Adding mail template %s: %s by %s", tmplName, assetPath, layerName)
if firstRun {
log.Trace("Adding mail template %s: %s by %s", tmplName, assetPath, layerName)
}
buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, tmplName, content)
}
}
refreshTemplates()
refreshTemplates(true)
if !setting.IsProd {
// Now subjectTemplates and bodyTemplates are both synchronized
// thus it is safe to call refresh from a different goroutine
go assetFS.WatchLocalChanges(ctx, refreshTemplates)
go assetFS.WatchLocalChanges(ctx, func() {
refreshTemplates(false)
})
}
return subjectTemplates, bodyTemplates
+21 -20
View File
@@ -5,26 +5,27 @@ package webhook
// HookEvents is a set of web hook events
type HookEvents struct {
Create bool `json:"create"`
Delete bool `json:"delete"`
Fork bool `json:"fork"`
Issues bool `json:"issues"`
IssueAssign bool `json:"issue_assign"`
IssueLabel bool `json:"issue_label"`
IssueMilestone bool `json:"issue_milestone"`
IssueComment bool `json:"issue_comment"`
Push bool `json:"push"`
PullRequest bool `json:"pull_request"`
PullRequestAssign bool `json:"pull_request_assign"`
PullRequestLabel bool `json:"pull_request_label"`
PullRequestMilestone bool `json:"pull_request_milestone"`
PullRequestComment bool `json:"pull_request_comment"`
PullRequestReview bool `json:"pull_request_review"`
PullRequestSync bool `json:"pull_request_sync"`
Wiki bool `json:"wiki"`
Repository bool `json:"repository"`
Release bool `json:"release"`
Package bool `json:"package"`
Create bool `json:"create"`
Delete bool `json:"delete"`
Fork bool `json:"fork"`
Issues bool `json:"issues"`
IssueAssign bool `json:"issue_assign"`
IssueLabel bool `json:"issue_label"`
IssueMilestone bool `json:"issue_milestone"`
IssueComment bool `json:"issue_comment"`
Push bool `json:"push"`
PullRequest bool `json:"pull_request"`
PullRequestAssign bool `json:"pull_request_assign"`
PullRequestLabel bool `json:"pull_request_label"`
PullRequestMilestone bool `json:"pull_request_milestone"`
PullRequestComment bool `json:"pull_request_comment"`
PullRequestReview bool `json:"pull_request_review"`
PullRequestSync bool `json:"pull_request_sync"`
PullRequestReviewRequest bool `json:"pull_request_review_request"`
Wiki bool `json:"wiki"`
Repository bool `json:"repository"`
Release bool `json:"release"`
Package bool `json:"package"`
}
// HookEvent represents events that will delivery hook.
+2 -1
View File
@@ -26,6 +26,7 @@ const (
HookEventPullRequestReviewRejected HookEventType = "pull_request_review_rejected"
HookEventPullRequestReviewComment HookEventType = "pull_request_review_comment"
HookEventPullRequestSync HookEventType = "pull_request_sync"
HookEventPullRequestReviewRequest HookEventType = "pull_request_review_request"
HookEventWiki HookEventType = "wiki"
HookEventRepository HookEventType = "repository"
HookEventRelease HookEventType = "release"
@@ -46,7 +47,7 @@ func (h HookEventType) Event() string {
case HookEventIssues, HookEventIssueAssign, HookEventIssueLabel, HookEventIssueMilestone:
return "issues"
case HookEventPullRequest, HookEventPullRequestAssign, HookEventPullRequestLabel, HookEventPullRequestMilestone,
HookEventPullRequestSync:
HookEventPullRequestSync, HookEventPullRequestReviewRequest:
return "pull_request"
case HookEventIssueComment, HookEventPullRequestComment:
return "issue_comment"