mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-27 05:31:21 +02:00
Merge branch 'limit-repo-size' of https://github.com/DmitryFrolovTri/gitea into limit-repo-size
This commit is contained in:
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
@@ -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})
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ type Manager struct {
|
||||
terminateWaitGroup sync.WaitGroup
|
||||
|
||||
toRunAtShutdown []func()
|
||||
toRunAtHammer []func()
|
||||
toRunAtTerminate []func()
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,6 @@ type Manager struct {
|
||||
shutdownRequested chan struct{}
|
||||
|
||||
toRunAtShutdown []func()
|
||||
toRunAtHammer []func()
|
||||
toRunAtTerminate []func()
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.*"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,10 @@ var _ base.Notifier = ¬ificationService{}
|
||||
// NewNotifier create a new notificationService notifier
|
||||
func NewNotifier() base.Notifier {
|
||||
ns := ¬ificationService{}
|
||||
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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user