mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-20 01:32:41 +02:00
Merge remote-tracking branch 'upstream/main' into limit-repo-size
Resolved Conflicts: models/migrations/migrations.go
This commit is contained in:
@@ -29,11 +29,10 @@ func GetKeyPair(ctx context.Context, user *user_model.User) (pub, priv string, e
|
||||
return pub, priv, err
|
||||
}
|
||||
return pub, priv, err
|
||||
} else {
|
||||
priv = settings[user_model.UserActivityPubPrivPem].SettingValue
|
||||
pub = settings[user_model.UserActivityPubPubPem].SettingValue
|
||||
return pub, priv, err
|
||||
}
|
||||
priv = settings[user_model.UserActivityPubPrivPem].SettingValue
|
||||
pub = settings[user_model.UserActivityPubPubPem].SettingValue
|
||||
return pub, priv, err
|
||||
}
|
||||
|
||||
// GetPublicKey function returns a user's public key
|
||||
|
||||
@@ -4,16 +4,11 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
|
||||
"github.com/minio/sha256-simd"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
const CookieNameFlash = "gitea_flash"
|
||||
@@ -45,42 +40,3 @@ func (ctx *Context) DeleteSiteCookie(name string) {
|
||||
func (ctx *Context) GetSiteCookie(name string) string {
|
||||
return middleware.GetSiteCookie(ctx.Req, name)
|
||||
}
|
||||
|
||||
// GetSuperSecureCookie returns given cookie value from request header with secret string.
|
||||
func (ctx *Context) GetSuperSecureCookie(secret, name string) (string, bool) {
|
||||
val := ctx.GetSiteCookie(name)
|
||||
return ctx.CookieDecrypt(secret, val)
|
||||
}
|
||||
|
||||
// CookieDecrypt returns given value from with secret string.
|
||||
func (ctx *Context) CookieDecrypt(secret, val string) (string, bool) {
|
||||
if val == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
text, err := hex.DecodeString(val)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
|
||||
text, err = util.AESGCMDecrypt(key, text)
|
||||
return string(text), err == nil
|
||||
}
|
||||
|
||||
// SetSuperSecureCookie sets given cookie value to response header with secret string.
|
||||
func (ctx *Context) SetSuperSecureCookie(secret, name, value string, maxAge int) {
|
||||
text := ctx.CookieEncrypt(secret, value)
|
||||
ctx.SetSiteCookie(name, text, maxAge)
|
||||
}
|
||||
|
||||
// CookieEncrypt encrypts a given value using the provided secret
|
||||
func (ctx *Context) CookieEncrypt(secret, value string) string {
|
||||
key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
|
||||
text, err := util.AESGCMEncrypt(key, []byte(value))
|
||||
if err != nil {
|
||||
panic("error encrypting cookie: " + err.Error())
|
||||
}
|
||||
|
||||
return hex.EncodeToString(text)
|
||||
}
|
||||
|
||||
@@ -144,18 +144,18 @@ func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.Use
|
||||
}
|
||||
|
||||
// CanUseTimetracker returns whether or not a user can use the timetracker.
|
||||
func (r *Repository) CanUseTimetracker(issue *issues_model.Issue, user *user_model.User) bool {
|
||||
func (r *Repository) CanUseTimetracker(ctx context.Context, issue *issues_model.Issue, user *user_model.User) bool {
|
||||
// Checking for following:
|
||||
// 1. Is timetracker enabled
|
||||
// 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this?
|
||||
isAssigned, _ := issues_model.IsUserAssignedToIssue(db.DefaultContext, issue, user)
|
||||
return r.Repository.IsTimetrackerEnabled(db.DefaultContext) && (!r.Repository.AllowOnlyContributorsToTrackTime(db.DefaultContext) ||
|
||||
isAssigned, _ := issues_model.IsUserAssignedToIssue(ctx, issue, user)
|
||||
return r.Repository.IsTimetrackerEnabled(ctx) && (!r.Repository.AllowOnlyContributorsToTrackTime(ctx) ||
|
||||
r.Permission.CanWriteIssuesOrPulls(issue.IsPull) || issue.IsPoster(user.ID) || isAssigned)
|
||||
}
|
||||
|
||||
// CanCreateIssueDependencies returns whether or not a user can create dependencies.
|
||||
func (r *Repository) CanCreateIssueDependencies(user *user_model.User, isPull bool) bool {
|
||||
return r.Repository.IsDependenciesEnabled(db.DefaultContext) && r.Permission.CanWriteIssuesOrPulls(isPull)
|
||||
func (r *Repository) CanCreateIssueDependencies(ctx context.Context, user *user_model.User, isPull bool) bool {
|
||||
return r.Repository.IsDependenciesEnabled(ctx) && r.Permission.CanWriteIssuesOrPulls(isPull)
|
||||
}
|
||||
|
||||
// GetCommitsCount returns cached commit count for current view
|
||||
@@ -495,10 +495,10 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
}
|
||||
|
||||
// Get repository.
|
||||
repo, err := repo_model.GetRepositoryByName(owner.ID, repoName)
|
||||
repo, err := repo_model.GetRepositoryByName(ctx, owner.ID, repoName)
|
||||
if err != nil {
|
||||
if repo_model.IsErrRepoNotExist(err) {
|
||||
redirectRepoID, err := repo_model.LookupRedirect(owner.ID, repoName)
|
||||
redirectRepoID, err := repo_model.LookupRedirect(ctx, owner.ID, repoName)
|
||||
if err == nil {
|
||||
RedirectToRepo(ctx.Base, redirectRepoID)
|
||||
} else if repo_model.IsErrRedirectNotExist(err) {
|
||||
@@ -711,13 +711,13 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
|
||||
// Pull request is allowed if this is a fork repository
|
||||
// and base repository accepts pull requests.
|
||||
if repo.BaseRepo != nil && repo.BaseRepo.AllowsPulls() {
|
||||
if repo.BaseRepo != nil && repo.BaseRepo.AllowsPulls(ctx) {
|
||||
canCompare = true
|
||||
ctx.Data["BaseRepo"] = repo.BaseRepo
|
||||
ctx.Repo.PullRequest.BaseRepo = repo.BaseRepo
|
||||
ctx.Repo.PullRequest.Allowed = canPush
|
||||
ctx.Repo.PullRequest.HeadInfoSubURL = url.PathEscape(ctx.Repo.Owner.Name) + ":" + util.PathEscapeSegments(ctx.Repo.BranchName)
|
||||
} else if repo.AllowsPulls() {
|
||||
} else if repo.AllowsPulls(ctx) {
|
||||
// Or, this is repository accepts pull requests between branches.
|
||||
canCompare = true
|
||||
ctx.Data["BaseRepo"] = repo
|
||||
|
||||
@@ -83,8 +83,7 @@ func LoadRepo(t *testing.T, ctx gocontext.Context, repoID int64) {
|
||||
ctx.Repo = repo
|
||||
doer = ctx.Doer
|
||||
default:
|
||||
assert.Fail(t, "context is not *context.Context or *context.APIContext")
|
||||
return
|
||||
assert.FailNow(t, "context is not *context.Context or *context.APIContext")
|
||||
}
|
||||
|
||||
repo.Repository = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
|
||||
@@ -105,8 +104,7 @@ func LoadRepoCommit(t *testing.T, ctx gocontext.Context) {
|
||||
case *context.APIContext:
|
||||
repo = ctx.Repo
|
||||
default:
|
||||
assert.Fail(t, "context is not *context.Context or *context.APIContext")
|
||||
return
|
||||
assert.FailNow(t, "context is not *context.Context or *context.APIContext")
|
||||
}
|
||||
|
||||
gitRepo, err := git.OpenRepository(ctx, repo.Repository.RepoPath())
|
||||
@@ -130,8 +128,7 @@ func LoadUser(t *testing.T, ctx gocontext.Context, userID int64) {
|
||||
case *context.APIContext:
|
||||
ctx.Doer = doer
|
||||
default:
|
||||
assert.Fail(t, "context is not *context.Context or *context.APIContext")
|
||||
return
|
||||
assert.FailNow(t, "context is not *context.Context or *context.APIContext")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro
|
||||
pr.MergeBase = strings.TrimSpace(pr.MergeBase)
|
||||
if pr.MergeBase != oldMergeBase {
|
||||
if autofix {
|
||||
if err := pr.UpdateCols("merge_base"); err != nil {
|
||||
if err := pr.UpdateCols(ctx, "merge_base"); err != nil {
|
||||
logger.Critical("Failed to update merge_base. ERROR: %v", err)
|
||||
return fmt.Errorf("Failed to update merge_base. ERROR: %w", err)
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func checkHooks(ctx context.Context, logger log.Logger, autofix bool) error {
|
||||
|
||||
func checkUserStarNum(ctx context.Context, logger log.Logger, autofix bool) error {
|
||||
if autofix {
|
||||
if err := models.DoctorUserStarNum(); err != nil {
|
||||
if err := models.DoctorUserStarNum(ctx); err != nil {
|
||||
logger.Critical("Unable update User Stars numbers")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package doctor
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
func handleDeleteOrphanedRepos(ctx context.Context, logger log.Logger, autofix bool) error {
|
||||
test := &consistencyCheck{
|
||||
Name: "Repos with no existing owner",
|
||||
Counter: countOrphanedRepos,
|
||||
Fixer: deleteOrphanedRepos,
|
||||
FixedMessage: "Deleted all content related to orphaned repos",
|
||||
}
|
||||
return test.Run(ctx, logger, autofix)
|
||||
}
|
||||
|
||||
// countOrphanedRepos count repository where user of owner_id do not exist
|
||||
func countOrphanedRepos(ctx context.Context) (int64, error) {
|
||||
return db.CountOrphanedObjects(ctx, "repository", "user", "repository.owner_id=user.id")
|
||||
}
|
||||
|
||||
// deleteOrphanedRepos delete repository where user of owner_id do not exist
|
||||
func deleteOrphanedRepos(ctx context.Context) (int64, error) {
|
||||
batchSize := db.MaxBatchInsertSize("repository")
|
||||
e := db.GetEngine(ctx)
|
||||
var deleted int64
|
||||
adminUser := &user_model.User{IsAdmin: true}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return deleted, ctx.Err()
|
||||
default:
|
||||
var ids []int64
|
||||
if err := e.Table("`repository`").
|
||||
Join("LEFT", "`user`", "repository.owner_id=user.id").
|
||||
Where(builder.IsNull{"`user`.id"}).
|
||||
Select("`repository`.id").Limit(batchSize).Find(&ids); err != nil {
|
||||
return deleted, err
|
||||
}
|
||||
|
||||
// if we don't get ids we have deleted them all
|
||||
if len(ids) == 0 {
|
||||
return deleted, nil
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
if err := repo_service.DeleteRepositoryDirectly(ctx, adminUser, id, true); err != nil {
|
||||
return deleted, err
|
||||
}
|
||||
deleted++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(&Check{
|
||||
Title: "Deleted all content related to orphaned repos",
|
||||
Name: "delete-orphaned-repos",
|
||||
IsDefault: false,
|
||||
Run: handleDeleteOrphanedRepos,
|
||||
Priority: 4,
|
||||
})
|
||||
}
|
||||
@@ -91,7 +91,7 @@ loop:
|
||||
}
|
||||
|
||||
for _, userStopwatches := range usersStopwatches {
|
||||
apiSWs, err := convert.ToStopWatches(userStopwatches.StopWatches)
|
||||
apiSWs, err := convert.ToStopWatches(ctx, userStopwatches.StopWatches)
|
||||
if err != nil {
|
||||
if !issues_model.IsErrIssueNotExist(err) {
|
||||
log.Error("Unable to APIFormat stopwatches: %v", err)
|
||||
|
||||
@@ -27,7 +27,7 @@ func Test_nulSeparatedAttributeWriter_ReadAttribute(t *testing.T) {
|
||||
assert.Equal(t, "linguist-vendored", attr.Attribute)
|
||||
assert.Equal(t, "unspecified", attr.Value)
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
assert.Fail(t, "took too long to read an attribute from the list")
|
||||
assert.FailNow(t, "took too long to read an attribute from the list")
|
||||
}
|
||||
// Write a second attribute again
|
||||
n, err = wr.Write([]byte(testStr))
|
||||
@@ -41,7 +41,7 @@ func Test_nulSeparatedAttributeWriter_ReadAttribute(t *testing.T) {
|
||||
assert.Equal(t, "linguist-vendored", attr.Attribute)
|
||||
assert.Equal(t, "unspecified", attr.Value)
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
assert.Fail(t, "took too long to read an attribute from the list")
|
||||
assert.FailNow(t, "took too long to read an attribute from the list")
|
||||
}
|
||||
|
||||
// Write a partial attribute
|
||||
@@ -52,14 +52,14 @@ func Test_nulSeparatedAttributeWriter_ReadAttribute(t *testing.T) {
|
||||
|
||||
select {
|
||||
case <-wr.ReadAttribute():
|
||||
assert.Fail(t, "There should not be an attribute ready to read")
|
||||
assert.FailNow(t, "There should not be an attribute ready to read")
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
}
|
||||
_, err = wr.Write([]byte("attribute\x00"))
|
||||
assert.NoError(t, err)
|
||||
select {
|
||||
case <-wr.ReadAttribute():
|
||||
assert.Fail(t, "There should not be an attribute ready to read")
|
||||
assert.FailNow(t, "There should not be an attribute ready to read")
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
}
|
||||
|
||||
|
||||
+21
-29
@@ -116,17 +116,13 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co
|
||||
c.AddArguments("-i")
|
||||
|
||||
// add authors if present in search query
|
||||
if len(opts.Authors) > 0 {
|
||||
for _, v := range opts.Authors {
|
||||
c.AddOptionFormat("--author=%s", v)
|
||||
}
|
||||
for _, v := range opts.Authors {
|
||||
c.AddOptionFormat("--author=%s", v)
|
||||
}
|
||||
|
||||
// add committers if present in search query
|
||||
if len(opts.Committers) > 0 {
|
||||
for _, v := range opts.Committers {
|
||||
c.AddOptionFormat("--committer=%s", v)
|
||||
}
|
||||
for _, v := range opts.Committers {
|
||||
c.AddOptionFormat("--committer=%s", v)
|
||||
}
|
||||
|
||||
// add time constraints if present in search query
|
||||
@@ -150,10 +146,8 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co
|
||||
|
||||
// add remaining keywords from search string
|
||||
// note this is done only for command created above
|
||||
if len(opts.Keywords) > 0 {
|
||||
for _, v := range opts.Keywords {
|
||||
cmd.AddOptionFormat("--grep=%s", v)
|
||||
}
|
||||
for _, v := range opts.Keywords {
|
||||
cmd.AddOptionFormat("--grep=%s", v)
|
||||
}
|
||||
|
||||
// search for commits matching given constraints and keywords in commit msg
|
||||
@@ -168,25 +162,23 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co
|
||||
|
||||
// if there are any keywords (ie not committer:, author:, time:)
|
||||
// then let's iterate over them
|
||||
if len(opts.Keywords) > 0 {
|
||||
for _, v := range opts.Keywords {
|
||||
// ignore anything not matching a valid sha pattern
|
||||
if IsValidSHAPattern(v) {
|
||||
// create new git log command with 1 commit limit
|
||||
hashCmd := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat)
|
||||
// add previous arguments except for --grep and --all
|
||||
addCommonSearchArgs(hashCmd)
|
||||
// add keyword as <commit>
|
||||
hashCmd.AddDynamicArguments(v)
|
||||
for _, v := range opts.Keywords {
|
||||
// ignore anything not matching a valid sha pattern
|
||||
if IsValidSHAPattern(v) {
|
||||
// create new git log command with 1 commit limit
|
||||
hashCmd := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat)
|
||||
// add previous arguments except for --grep and --all
|
||||
addCommonSearchArgs(hashCmd)
|
||||
// add keyword as <commit>
|
||||
hashCmd.AddDynamicArguments(v)
|
||||
|
||||
// search with given constraints for commit matching sha hash of v
|
||||
hashMatching, _, err := hashCmd.RunStdBytes(&RunOpts{Dir: repo.Path})
|
||||
if err != nil || bytes.Contains(stdout, hashMatching) {
|
||||
continue
|
||||
}
|
||||
stdout = append(stdout, hashMatching...)
|
||||
stdout = append(stdout, '\n')
|
||||
// search with given constraints for commit matching sha hash of v
|
||||
hashMatching, _, err := hashCmd.RunStdBytes(&RunOpts{Dir: repo.Path})
|
||||
if err != nil || bytes.Contains(stdout, hashMatching) {
|
||||
continue
|
||||
}
|
||||
stdout = append(stdout, hashMatching...)
|
||||
stdout = append(stdout, '\n')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,6 @@ func TestRepository_GetTag(t *testing.T) {
|
||||
if lTag == nil {
|
||||
assert.NotNil(t, lTag)
|
||||
assert.FailNow(t, "nil lTag: %s", lTagName)
|
||||
return
|
||||
}
|
||||
assert.EqualValues(t, lTagName, lTag.Name)
|
||||
assert.EqualValues(t, lTagCommitID, lTag.ID.String())
|
||||
@@ -105,7 +104,6 @@ func TestRepository_GetTag(t *testing.T) {
|
||||
if aTag == nil {
|
||||
assert.NotNil(t, aTag)
|
||||
assert.FailNow(t, "nil aTag: %s", aTagName)
|
||||
return
|
||||
}
|
||||
assert.EqualValues(t, aTagName, aTag.Name)
|
||||
assert.EqualValues(t, aTagID, aTag.ID.String())
|
||||
|
||||
@@ -101,9 +101,8 @@ func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string {
|
||||
return ref.Scheme + "://" + ref.Host + ref.Path
|
||||
} else if urlPrefixHostname == refHostname || refHostname == sshDomain {
|
||||
return urlPrefix + path.Clean(path.Join("/", ref.Path))
|
||||
} else {
|
||||
return "http://" + refHostname + ref.Path
|
||||
}
|
||||
return "http://" + refHostname + ref.Path
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,12 +7,17 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewDialContext returns a DialContext for Transport, the DialContext will do allow/block list check
|
||||
func NewDialContext(usage string, allowList, blockList *HostMatchList) func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return NewDialContextWithProxy(usage, allowList, blockList, nil)
|
||||
}
|
||||
|
||||
func NewDialContextWithProxy(usage string, allowList, blockList *HostMatchList, proxy *url.URL) func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
// How Go HTTP Client works with redirection:
|
||||
// transport.RoundTrip URL=http://domain.com, Host=domain.com
|
||||
// transport.DialContext addrOrHost=domain.com:80
|
||||
@@ -26,11 +31,18 @@ func NewDialContext(usage string, allowList, blockList *HostMatchList) func(ctx
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
|
||||
Control: func(network, ipAddr string, c syscall.RawConn) (err error) {
|
||||
var host string
|
||||
if host, _, err = net.SplitHostPort(addrOrHost); err != nil {
|
||||
Control: func(network, ipAddr string, c syscall.RawConn) error {
|
||||
host, port, err := net.SplitHostPort(addrOrHost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if proxy != nil {
|
||||
// Always allow the host of the proxy, but only on the specified port.
|
||||
if host == proxy.Hostname() && port == proxy.Port() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// in Control func, the addr was already resolved to IP:PORT format, there is no cost to do ResolveTCPAddr here
|
||||
tcpAddr, err := net.ResolveTCPAddr(network, ipAddr)
|
||||
if err != nil {
|
||||
|
||||
@@ -288,7 +288,7 @@ func populateRepoIndexer(ctx context.Context) {
|
||||
return
|
||||
default:
|
||||
}
|
||||
ids, err := repo_model.GetUnindexedRepos(repo_model.RepoIndexerTypeCode, maxRepoID, 0, 50)
|
||||
ids, err := repo_model.GetUnindexedRepos(ctx, repo_model.RepoIndexerTypeCode, maxRepoID, 0, 50)
|
||||
if err != nil {
|
||||
log.Error("populateRepoIndexer: %v", err)
|
||||
return
|
||||
|
||||
@@ -96,11 +96,10 @@ func TestBleveIndexAndSearch(t *testing.T) {
|
||||
idx := bleve.NewIndexer(dir)
|
||||
_, err := idx.Init(context.Background())
|
||||
if err != nil {
|
||||
assert.Fail(t, "Unable to create bleve indexer Error: %v", err)
|
||||
if idx != nil {
|
||||
idx.Close()
|
||||
}
|
||||
return
|
||||
assert.FailNow(t, "Unable to create bleve indexer Error: %v", err)
|
||||
}
|
||||
defer idx.Close()
|
||||
|
||||
@@ -118,11 +117,10 @@ func TestESIndexAndSearch(t *testing.T) {
|
||||
|
||||
indexer := elasticsearch.NewIndexer(u, "gitea_codes")
|
||||
if _, err := indexer.Init(context.Background()); err != nil {
|
||||
assert.Fail(t, "Unable to init ES indexer Error: %v", err)
|
||||
if indexer != nil {
|
||||
indexer.Close()
|
||||
}
|
||||
return
|
||||
assert.FailNow(t, "Unable to init ES indexer Error: %v", err)
|
||||
}
|
||||
|
||||
defer indexer.Close()
|
||||
|
||||
@@ -96,7 +96,6 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m
|
||||
}
|
||||
|
||||
if len(options.IncludedLabelIDs) == 0 && len(options.IncludedAnyLabelIDs) > 0 {
|
||||
_ = ctx // issue_model.GetLabelsByIDs should be called with ctx, this line can be removed when it's done.
|
||||
labels, err := issue_model.GetLabelsByIDs(ctx, options.IncludedAnyLabelIDs, "name")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetLabelsByIDs: %v", err)
|
||||
|
||||
@@ -204,12 +204,13 @@ func getIssueIndexerQueueHandler(ctx context.Context) func(items ...*IndexerMeta
|
||||
func populateIssueIndexer(ctx context.Context) {
|
||||
ctx, _, finished := process.GetManager().AddTypedContext(ctx, "Service: PopulateIssueIndexer", process.SystemProcessType, true)
|
||||
defer finished()
|
||||
if err := PopulateIssueIndexer(ctx, true); err != nil {
|
||||
ctx = contextWithKeepRetry(ctx) // keep retrying since it's a background task
|
||||
if err := PopulateIssueIndexer(ctx); err != nil {
|
||||
log.Error("Issue indexer population failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func PopulateIssueIndexer(ctx context.Context, keepRetrying bool) error {
|
||||
func PopulateIssueIndexer(ctx context.Context) error {
|
||||
for page := 1; ; page++ {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -232,20 +233,8 @@ func PopulateIssueIndexer(ctx context.Context, keepRetrying bool) error {
|
||||
}
|
||||
|
||||
for _, repo := range repos {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("shutdown before completion: %w", ctx.Err())
|
||||
default:
|
||||
}
|
||||
if err := updateRepoIndexer(ctx, repo.ID); err != nil {
|
||||
if keepRetrying && ctx.Err() == nil {
|
||||
log.Warn("Retry to populate issue indexer for repo %d: %v", repo.ID, err)
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("populate issue indexer for repo %d: %v", repo.ID, err)
|
||||
}
|
||||
break
|
||||
if err := updateRepoIndexer(ctx, repo.ID); err != nil {
|
||||
return fmt.Errorf("populate issue indexer for repo %d: %v", repo.ID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,8 +248,8 @@ func UpdateRepoIndexer(ctx context.Context, repoID int64) {
|
||||
}
|
||||
|
||||
// UpdateIssueIndexer add/update an issue to the issue indexer
|
||||
func UpdateIssueIndexer(issueID int64) {
|
||||
if err := updateIssueIndexer(issueID); err != nil {
|
||||
func UpdateIssueIndexer(ctx context.Context, issueID int64) {
|
||||
if err := updateIssueIndexer(ctx, issueID); err != nil {
|
||||
log.Error("Unable to push issue %d to issue indexer: %v", issueID, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD
|
||||
NoLabel: len(labels) == 0,
|
||||
MilestoneID: issue.MilestoneID,
|
||||
ProjectID: projectID,
|
||||
ProjectBoardID: issue.ProjectBoardID(),
|
||||
ProjectBoardID: issue.ProjectBoardID(ctx),
|
||||
PosterID: issue.PosterID,
|
||||
AssigneeID: issue.AssigneeID,
|
||||
MentionIDs: mentionIDs,
|
||||
@@ -127,15 +127,15 @@ func updateRepoIndexer(ctx context.Context, repoID int64) error {
|
||||
return fmt.Errorf("issue_model.GetIssueIDsByRepoID: %w", err)
|
||||
}
|
||||
for _, id := range ids {
|
||||
if err := updateIssueIndexer(id); err != nil {
|
||||
if err := updateIssueIndexer(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateIssueIndexer(issueID int64) error {
|
||||
return pushIssueIndexerQueue(&IndexerMetadata{ID: issueID})
|
||||
func updateIssueIndexer(ctx context.Context, issueID int64) error {
|
||||
return pushIssueIndexerQueue(ctx, &IndexerMetadata{ID: issueID})
|
||||
}
|
||||
|
||||
func deleteRepoIssueIndexer(ctx context.Context, repoID int64) error {
|
||||
@@ -148,13 +148,21 @@ func deleteRepoIssueIndexer(ctx context.Context, repoID int64) error {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
return pushIssueIndexerQueue(&IndexerMetadata{
|
||||
return pushIssueIndexerQueue(ctx, &IndexerMetadata{
|
||||
IDs: ids,
|
||||
IsDelete: true,
|
||||
})
|
||||
}
|
||||
|
||||
func pushIssueIndexerQueue(data *IndexerMetadata) error {
|
||||
type keepRetryKey struct{}
|
||||
|
||||
// contextWithKeepRetry returns a context with a key indicating that the indexer should keep retrying.
|
||||
// Please note that it's for background tasks only, and it should not be used for user requests, or it may cause blocking.
|
||||
func contextWithKeepRetry(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, keepRetryKey{}, true)
|
||||
}
|
||||
|
||||
func pushIssueIndexerQueue(ctx context.Context, data *IndexerMetadata) error {
|
||||
if issueIndexerQueue == nil {
|
||||
// Some unit tests will trigger indexing, but the queue is not initialized.
|
||||
// It's OK to ignore it, but log a warning message in case it's not a unit test.
|
||||
@@ -162,12 +170,26 @@ func pushIssueIndexerQueue(data *IndexerMetadata) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := issueIndexerQueue.Push(data)
|
||||
if errors.Is(err, queue.ErrAlreadyInQueue) {
|
||||
return nil
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
err := issueIndexerQueue.Push(data)
|
||||
if errors.Is(err, queue.ErrAlreadyInQueue) {
|
||||
return nil
|
||||
}
|
||||
if errors.Is(err, context.DeadlineExceeded) { // the queue is full
|
||||
log.Warn("It seems that issue indexer is slow and the queue is full. Please check the issue indexer or increase the queue size.")
|
||||
if ctx.Value(keepRetryKey{}) == nil {
|
||||
return err
|
||||
}
|
||||
// It will be better to increase the queue size instead of retrying, but users may ignore the previous warning message.
|
||||
// However, even it retries, it may still cause index loss when there's a deadline in the context.
|
||||
log.Debug("Retry to push %+v to issue indexer queue", data)
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
log.Warn("It seems that issue indexer is slow and the queue is full. Please check the issue indexer or increase the queue size.")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func (db *DBIndexer) Index(id int64) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = repo_model.UpdateLanguageStats(repo, commitID, stats)
|
||||
err = repo_model.UpdateLanguageStats(ctx, repo, commitID, stats)
|
||||
if err != nil {
|
||||
log.Error("Unable to update language stats for ID %s for default branch %s in %s. Error: %v", commitID, repo.DefaultBranch, repo.RepoPath(), err)
|
||||
return err
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
@@ -28,14 +30,14 @@ func Init() error {
|
||||
return err
|
||||
}
|
||||
|
||||
go populateRepoIndexer()
|
||||
go populateRepoIndexer(db.DefaultContext)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// populateRepoIndexer populate the repo indexer with pre-existing data. This
|
||||
// should only be run when the indexer is created for the first time.
|
||||
func populateRepoIndexer() {
|
||||
func populateRepoIndexer(ctx context.Context) {
|
||||
log.Info("Populating the repo stats indexer with existing repositories")
|
||||
|
||||
isShutdown := graceful.GetManager().IsShutdown()
|
||||
@@ -62,7 +64,7 @@ func populateRepoIndexer() {
|
||||
return
|
||||
default:
|
||||
}
|
||||
ids, err := repo_model.GetUnindexedRepos(repo_model.RepoIndexerTypeStats, maxRepoID, 0, 50)
|
||||
ids, err := repo_model.GetUnindexedRepos(ctx, repo_model.RepoIndexerTypeStats, maxRepoID, 0, 50)
|
||||
if err != nil {
|
||||
log.Error("populateRepoIndexer: %v", err)
|
||||
return
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestRepoStatsIndex(t *testing.T) {
|
||||
status, err := repo_model.GetIndexerStatus(db.DefaultContext, repo, repo_model.RepoIndexerTypeStats)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "65f1bf27bc3bf70f64657658635e66094edbcb4d", status.CommitSha)
|
||||
langs, err := repo_model.GetTopLanguageStats(repo, 5)
|
||||
langs, err := repo_model.GetTopLanguageStats(db.DefaultContext, repo, 5)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, langs)
|
||||
}
|
||||
|
||||
@@ -62,10 +62,9 @@ func TestBasicTransferAdapter(t *testing.T) {
|
||||
json.NewEncoder(payload).Encode(er)
|
||||
|
||||
return &http.Response{StatusCode: http.StatusNotFound, Body: io.NopCloser(payload)}
|
||||
} else {
|
||||
t.Errorf("Unknown test case: %s", url)
|
||||
return nil
|
||||
}
|
||||
t.Errorf("Unknown test case: %s", url)
|
||||
return nil
|
||||
}
|
||||
|
||||
hc := &http.Client{Transport: RoundTripFunc(roundTripHandler)}
|
||||
|
||||
+65
-65
@@ -14,7 +14,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/emoji"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
. "code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
@@ -37,12 +37,12 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
func TestRender_Commits(t *testing.T) {
|
||||
setting.AppURL = TestAppURL
|
||||
setting.AppURL = markup.TestAppURL
|
||||
test := func(input, expected string) {
|
||||
buffer, err := RenderString(&RenderContext{
|
||||
buffer, err := markup.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
RelativePath: ".md",
|
||||
URLPrefix: TestRepoURL,
|
||||
URLPrefix: markup.TestRepoURL,
|
||||
Metas: localMetas,
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
@@ -50,7 +50,7 @@ func TestRender_Commits(t *testing.T) {
|
||||
}
|
||||
|
||||
sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d"
|
||||
repo := TestRepoURL
|
||||
repo := markup.TestRepoURL
|
||||
commit := util.URLJoin(repo, "commit", sha)
|
||||
tree := util.URLJoin(repo, "tree", sha, "src")
|
||||
|
||||
@@ -87,10 +87,10 @@ func TestRender_Commits(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRender_CrossReferences(t *testing.T) {
|
||||
setting.AppURL = TestAppURL
|
||||
setting.AppURL = markup.TestAppURL
|
||||
|
||||
test := func(input, expected string) {
|
||||
buffer, err := RenderString(&RenderContext{
|
||||
buffer, err := markup.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
RelativePath: "a.md",
|
||||
URLPrefix: setting.AppSubURL,
|
||||
@@ -102,43 +102,43 @@ func TestRender_CrossReferences(t *testing.T) {
|
||||
|
||||
test(
|
||||
"gogits/gogs#12345",
|
||||
`<p><a href="`+util.URLJoin(TestAppURL, "gogits", "gogs", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogits/gogs#12345</a></p>`)
|
||||
`<p><a href="`+util.URLJoin(markup.TestAppURL, "gogits", "gogs", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogits/gogs#12345</a></p>`)
|
||||
test(
|
||||
"go-gitea/gitea#12345",
|
||||
`<p><a href="`+util.URLJoin(TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`)
|
||||
`<p><a href="`+util.URLJoin(markup.TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`)
|
||||
test(
|
||||
"/home/gitea/go-gitea/gitea#12345",
|
||||
`<p>/home/gitea/go-gitea/gitea#12345</p>`)
|
||||
test(
|
||||
util.URLJoin(TestAppURL, "gogitea", "gitea", "issues", "12345"),
|
||||
`<p><a href="`+util.URLJoin(TestAppURL, "gogitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogitea/gitea#12345</a></p>`)
|
||||
util.URLJoin(markup.TestAppURL, "gogitea", "gitea", "issues", "12345"),
|
||||
`<p><a href="`+util.URLJoin(markup.TestAppURL, "gogitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogitea/gitea#12345</a></p>`)
|
||||
test(
|
||||
util.URLJoin(TestAppURL, "go-gitea", "gitea", "issues", "12345"),
|
||||
`<p><a href="`+util.URLJoin(TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`)
|
||||
util.URLJoin(markup.TestAppURL, "go-gitea", "gitea", "issues", "12345"),
|
||||
`<p><a href="`+util.URLJoin(markup.TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`)
|
||||
test(
|
||||
util.URLJoin(TestAppURL, "gogitea", "some-repo-name", "issues", "12345"),
|
||||
`<p><a href="`+util.URLJoin(TestAppURL, "gogitea", "some-repo-name", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogitea/some-repo-name#12345</a></p>`)
|
||||
util.URLJoin(markup.TestAppURL, "gogitea", "some-repo-name", "issues", "12345"),
|
||||
`<p><a href="`+util.URLJoin(markup.TestAppURL, "gogitea", "some-repo-name", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogitea/some-repo-name#12345</a></p>`)
|
||||
}
|
||||
|
||||
func TestMisc_IsSameDomain(t *testing.T) {
|
||||
setting.AppURL = TestAppURL
|
||||
setting.AppURL = markup.TestAppURL
|
||||
|
||||
sha := "b6dd6210eaebc915fd5be5579c58cce4da2e2579"
|
||||
commit := util.URLJoin(TestRepoURL, "commit", sha)
|
||||
commit := util.URLJoin(markup.TestRepoURL, "commit", sha)
|
||||
|
||||
assert.True(t, IsSameDomain(commit))
|
||||
assert.False(t, IsSameDomain("http://google.com/ncr"))
|
||||
assert.False(t, IsSameDomain("favicon.ico"))
|
||||
assert.True(t, markup.IsSameDomain(commit))
|
||||
assert.False(t, markup.IsSameDomain("http://google.com/ncr"))
|
||||
assert.False(t, markup.IsSameDomain("favicon.ico"))
|
||||
}
|
||||
|
||||
func TestRender_links(t *testing.T) {
|
||||
setting.AppURL = TestAppURL
|
||||
setting.AppURL = markup.TestAppURL
|
||||
|
||||
test := func(input, expected string) {
|
||||
buffer, err := RenderString(&RenderContext{
|
||||
buffer, err := markup.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
RelativePath: "a.md",
|
||||
URLPrefix: TestRepoURL,
|
||||
URLPrefix: markup.TestRepoURL,
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||
@@ -147,8 +147,8 @@ func TestRender_links(t *testing.T) {
|
||||
|
||||
defaultCustom := setting.Markdown.CustomURLSchemes
|
||||
setting.Markdown.CustomURLSchemes = []string{"ftp", "magnet"}
|
||||
InitializeSanitizer()
|
||||
CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
|
||||
markup.InitializeSanitizer()
|
||||
markup.CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
|
||||
|
||||
test(
|
||||
"https://www.example.com",
|
||||
@@ -227,18 +227,18 @@ func TestRender_links(t *testing.T) {
|
||||
|
||||
// Restore previous settings
|
||||
setting.Markdown.CustomURLSchemes = defaultCustom
|
||||
InitializeSanitizer()
|
||||
CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
|
||||
markup.InitializeSanitizer()
|
||||
markup.CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
|
||||
}
|
||||
|
||||
func TestRender_email(t *testing.T) {
|
||||
setting.AppURL = TestAppURL
|
||||
setting.AppURL = markup.TestAppURL
|
||||
|
||||
test := func(input, expected string) {
|
||||
res, err := RenderString(&RenderContext{
|
||||
res, err := markup.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
RelativePath: "a.md",
|
||||
URLPrefix: TestRepoURL,
|
||||
URLPrefix: markup.TestRepoURL,
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res))
|
||||
@@ -289,15 +289,15 @@ func TestRender_email(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRender_emoji(t *testing.T) {
|
||||
setting.AppURL = TestAppURL
|
||||
setting.StaticURLPrefix = TestAppURL
|
||||
setting.AppURL = markup.TestAppURL
|
||||
setting.StaticURLPrefix = markup.TestAppURL
|
||||
|
||||
test := func(input, expected string) {
|
||||
expected = strings.ReplaceAll(expected, "&", "&")
|
||||
buffer, err := RenderString(&RenderContext{
|
||||
buffer, err := markup.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
RelativePath: "a.md",
|
||||
URLPrefix: TestRepoURL,
|
||||
URLPrefix: markup.TestRepoURL,
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||
@@ -354,19 +354,19 @@ func TestRender_emoji(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRender_ShortLinks(t *testing.T) {
|
||||
setting.AppURL = TestAppURL
|
||||
tree := util.URLJoin(TestRepoURL, "src", "master")
|
||||
setting.AppURL = markup.TestAppURL
|
||||
tree := util.URLJoin(markup.TestRepoURL, "src", "master")
|
||||
|
||||
test := func(input, expected, expectedWiki string) {
|
||||
buffer, err := markdown.RenderString(&RenderContext{
|
||||
buffer, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: tree,
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||
buffer, err = markdown.RenderString(&RenderContext{
|
||||
buffer, err = markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: TestRepoURL,
|
||||
URLPrefix: markup.TestRepoURL,
|
||||
Metas: localMetas,
|
||||
IsWiki: true,
|
||||
}, input)
|
||||
@@ -374,7 +374,7 @@ func TestRender_ShortLinks(t *testing.T) {
|
||||
assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer))
|
||||
}
|
||||
|
||||
rawtree := util.URLJoin(TestRepoURL, "raw", "master")
|
||||
rawtree := util.URLJoin(markup.TestRepoURL, "raw", "master")
|
||||
url := util.URLJoin(tree, "Link")
|
||||
otherURL := util.URLJoin(tree, "Other-Link")
|
||||
encodedURL := util.URLJoin(tree, "Link%3F")
|
||||
@@ -382,13 +382,13 @@ func TestRender_ShortLinks(t *testing.T) {
|
||||
otherImgurl := util.URLJoin(rawtree, "Link+Other.jpg")
|
||||
encodedImgurl := util.URLJoin(rawtree, "Link+%23.jpg")
|
||||
notencodedImgurl := util.URLJoin(rawtree, "some", "path", "Link+#.jpg")
|
||||
urlWiki := util.URLJoin(TestRepoURL, "wiki", "Link")
|
||||
otherURLWiki := util.URLJoin(TestRepoURL, "wiki", "Other-Link")
|
||||
encodedURLWiki := util.URLJoin(TestRepoURL, "wiki", "Link%3F")
|
||||
imgurlWiki := util.URLJoin(TestRepoURL, "wiki", "raw", "Link.jpg")
|
||||
otherImgurlWiki := util.URLJoin(TestRepoURL, "wiki", "raw", "Link+Other.jpg")
|
||||
encodedImgurlWiki := util.URLJoin(TestRepoURL, "wiki", "raw", "Link+%23.jpg")
|
||||
notencodedImgurlWiki := util.URLJoin(TestRepoURL, "wiki", "raw", "some", "path", "Link+#.jpg")
|
||||
urlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Link")
|
||||
otherURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Other-Link")
|
||||
encodedURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Link%3F")
|
||||
imgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link.jpg")
|
||||
otherImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+Other.jpg")
|
||||
encodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "Link+%23.jpg")
|
||||
notencodedImgurlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw", "some", "path", "Link+#.jpg")
|
||||
favicon := "http://google.com/favicon.ico"
|
||||
|
||||
test(
|
||||
@@ -462,20 +462,20 @@ func TestRender_ShortLinks(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRender_RelativeImages(t *testing.T) {
|
||||
setting.AppURL = TestAppURL
|
||||
tree := util.URLJoin(TestRepoURL, "src", "master")
|
||||
setting.AppURL = markup.TestAppURL
|
||||
tree := util.URLJoin(markup.TestRepoURL, "src", "master")
|
||||
|
||||
test := func(input, expected, expectedWiki string) {
|
||||
buffer, err := markdown.RenderString(&RenderContext{
|
||||
buffer, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: tree,
|
||||
Metas: localMetas,
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||
buffer, err = markdown.RenderString(&RenderContext{
|
||||
buffer, err = markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: TestRepoURL,
|
||||
URLPrefix: markup.TestRepoURL,
|
||||
Metas: localMetas,
|
||||
IsWiki: true,
|
||||
}, input)
|
||||
@@ -483,8 +483,8 @@ func TestRender_RelativeImages(t *testing.T) {
|
||||
assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer))
|
||||
}
|
||||
|
||||
rawwiki := util.URLJoin(TestRepoURL, "wiki", "raw")
|
||||
mediatree := util.URLJoin(TestRepoURL, "media", "master")
|
||||
rawwiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw")
|
||||
mediatree := util.URLJoin(markup.TestRepoURL, "media", "master")
|
||||
|
||||
test(
|
||||
`<img src="Link">`,
|
||||
@@ -498,7 +498,7 @@ func TestRender_RelativeImages(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_ParseClusterFuzz(t *testing.T) {
|
||||
setting.AppURL = TestAppURL
|
||||
setting.AppURL = markup.TestAppURL
|
||||
|
||||
localMetas := map[string]string{
|
||||
"user": "go-gitea",
|
||||
@@ -508,7 +508,7 @@ func Test_ParseClusterFuzz(t *testing.T) {
|
||||
data := "<A><maTH><tr><MN><bodY ÿ><temPlate></template><tH><tr></A><tH><d<bodY "
|
||||
|
||||
var res strings.Builder
|
||||
err := PostProcess(&RenderContext{
|
||||
err := markup.PostProcess(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: "https://example.com",
|
||||
Metas: localMetas,
|
||||
@@ -519,7 +519,7 @@ func Test_ParseClusterFuzz(t *testing.T) {
|
||||
data = "<!DOCTYPE html>\n<A><maTH><tr><MN><bodY ÿ><temPlate></template><tH><tr></A><tH><d<bodY "
|
||||
|
||||
res.Reset()
|
||||
err = PostProcess(&RenderContext{
|
||||
err = markup.PostProcess(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: "https://example.com",
|
||||
Metas: localMetas,
|
||||
@@ -530,7 +530,7 @@ func Test_ParseClusterFuzz(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPostProcess_RenderDocument(t *testing.T) {
|
||||
setting.AppURL = TestAppURL
|
||||
setting.AppURL = markup.TestAppURL
|
||||
|
||||
localMetas := map[string]string{
|
||||
"user": "go-gitea",
|
||||
@@ -540,7 +540,7 @@ func TestPostProcess_RenderDocument(t *testing.T) {
|
||||
|
||||
test := func(input, expected string) {
|
||||
var res strings.Builder
|
||||
err := PostProcess(&RenderContext{
|
||||
err := markup.PostProcess(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: "https://example.com",
|
||||
Metas: localMetas,
|
||||
@@ -566,7 +566,7 @@ func TestPostProcess_RenderDocument(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIssue16020(t *testing.T) {
|
||||
setting.AppURL = TestAppURL
|
||||
setting.AppURL = markup.TestAppURL
|
||||
|
||||
localMetas := map[string]string{
|
||||
"user": "go-gitea",
|
||||
@@ -576,7 +576,7 @@ func TestIssue16020(t *testing.T) {
|
||||
data := `<img src="data:image/png;base64,i//V"/>`
|
||||
|
||||
var res strings.Builder
|
||||
err := PostProcess(&RenderContext{
|
||||
err := markup.PostProcess(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: "https://example.com",
|
||||
Metas: localMetas,
|
||||
@@ -593,7 +593,7 @@ func BenchmarkEmojiPostprocess(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
var res strings.Builder
|
||||
err := PostProcess(&RenderContext{
|
||||
err := markup.PostProcess(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: "https://example.com",
|
||||
Metas: localMetas,
|
||||
@@ -604,7 +604,7 @@ func BenchmarkEmojiPostprocess(b *testing.B) {
|
||||
|
||||
func TestFuzz(t *testing.T) {
|
||||
s := "t/l/issues/8#/../../a"
|
||||
renderContext := RenderContext{
|
||||
renderContext := markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: "https://example.com/go-gitea/gitea",
|
||||
Metas: map[string]string{
|
||||
@@ -613,7 +613,7 @@ func TestFuzz(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
err := PostProcess(&renderContext, strings.NewReader(s), io.Discard)
|
||||
err := markup.PostProcess(&renderContext, strings.NewReader(s), io.Discard)
|
||||
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -622,7 +622,7 @@ func TestIssue18471(t *testing.T) {
|
||||
data := `http://domain/org/repo/compare/783b039...da951ce`
|
||||
|
||||
var res strings.Builder
|
||||
err := PostProcess(&RenderContext{
|
||||
err := markup.PostProcess(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: "https://example.com",
|
||||
Metas: localMetas,
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
. "code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
@@ -51,14 +51,14 @@ func TestRender_StandardLinks(t *testing.T) {
|
||||
setting.AppSubURL = AppSubURL
|
||||
|
||||
test := func(input, expected, expectedWiki string) {
|
||||
buffer, err := RenderString(&markup.RenderContext{
|
||||
buffer, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: setting.AppSubURL,
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||
|
||||
buffer, err = RenderString(&markup.RenderContext{
|
||||
buffer, err = markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: setting.AppSubURL,
|
||||
IsWiki: true,
|
||||
@@ -82,7 +82,7 @@ func TestRender_Images(t *testing.T) {
|
||||
setting.AppSubURL = AppSubURL
|
||||
|
||||
test := func(input, expected string) {
|
||||
buffer, err := RenderString(&markup.RenderContext{
|
||||
buffer, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: setting.AppSubURL,
|
||||
}, input)
|
||||
@@ -289,7 +289,7 @@ func TestTotal_RenderWiki(t *testing.T) {
|
||||
answers := testAnswers(util.URLJoin(AppSubURL, "wiki/"), util.URLJoin(AppSubURL, "wiki", "raw/"))
|
||||
|
||||
for i := 0; i < len(sameCases); i++ {
|
||||
line, err := RenderString(&markup.RenderContext{
|
||||
line, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: AppSubURL,
|
||||
Metas: localMetas,
|
||||
@@ -313,7 +313,7 @@ func TestTotal_RenderWiki(t *testing.T) {
|
||||
}
|
||||
|
||||
for i := 0; i < len(testCases); i += 2 {
|
||||
line, err := RenderString(&markup.RenderContext{
|
||||
line, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: AppSubURL,
|
||||
IsWiki: true,
|
||||
@@ -330,7 +330,7 @@ func TestTotal_RenderString(t *testing.T) {
|
||||
answers := testAnswers(util.URLJoin(AppSubURL, "src", "master/"), util.URLJoin(AppSubURL, "raw", "master/"))
|
||||
|
||||
for i := 0; i < len(sameCases); i++ {
|
||||
line, err := RenderString(&markup.RenderContext{
|
||||
line, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: util.URLJoin(AppSubURL, "src", "master/"),
|
||||
Metas: localMetas,
|
||||
@@ -342,7 +342,7 @@ func TestTotal_RenderString(t *testing.T) {
|
||||
testCases := []string{}
|
||||
|
||||
for i := 0; i < len(testCases); i += 2 {
|
||||
line, err := RenderString(&markup.RenderContext{
|
||||
line, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: AppSubURL,
|
||||
}, testCases[i])
|
||||
@@ -353,17 +353,17 @@ func TestTotal_RenderString(t *testing.T) {
|
||||
|
||||
func TestRender_RenderParagraphs(t *testing.T) {
|
||||
test := func(t *testing.T, str string, cnt int) {
|
||||
res, err := RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, str)
|
||||
res, err := markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, str)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, cnt, strings.Count(res, "<p"), "Rendered result for unix should have %d paragraph(s) but has %d:\n%s\n", cnt, strings.Count(res, "<p"), res)
|
||||
|
||||
mac := strings.ReplaceAll(str, "\n", "\r")
|
||||
res, err = RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, mac)
|
||||
res, err = markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, mac)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, cnt, strings.Count(res, "<p"), "Rendered result for mac should have %d paragraph(s) but has %d:\n%s\n", cnt, strings.Count(res, "<p"), res)
|
||||
|
||||
dos := strings.ReplaceAll(str, "\n", "\r\n")
|
||||
res, err = RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, dos)
|
||||
res, err = markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, dos)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, cnt, strings.Count(res, "<p"), "Rendered result for windows should have %d paragraph(s) but has %d:\n%s\n", cnt, strings.Count(res, "<p"), res)
|
||||
}
|
||||
@@ -391,7 +391,7 @@ func TestMarkdownRenderRaw(t *testing.T) {
|
||||
|
||||
for _, testcase := range testcases {
|
||||
log.Info("Test markdown render error with fuzzy data: %x, the following errors can be recovered", testcase)
|
||||
_, err := RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, string(testcase))
|
||||
_, err := markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, string(testcase))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
@@ -403,7 +403,7 @@ func TestRenderSiblingImages_Issue12925(t *testing.T) {
|
||||
expected := `<p><a href="/image1" target="_blank" rel="nofollow noopener"><img src="/image1" alt="image1"></a><br>
|
||||
<a href="/image2" target="_blank" rel="nofollow noopener"><img src="/image2" alt="image2"></a></p>
|
||||
`
|
||||
res, err := RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, testcase)
|
||||
res, err := markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, testcase)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, res)
|
||||
}
|
||||
@@ -412,7 +412,7 @@ func TestRenderEmojiInLinks_Issue12331(t *testing.T) {
|
||||
testcase := `[Link with emoji :moon: in text](https://gitea.io)`
|
||||
expected := `<p><a href="https://gitea.io" rel="nofollow">Link with emoji <span class="emoji" aria-label="waxing gibbous moon">🌔</span> in text</a></p>
|
||||
`
|
||||
res, err := RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, testcase)
|
||||
res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, testcase)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, res)
|
||||
}
|
||||
@@ -446,7 +446,7 @@ func TestColorPreview(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range positiveTests {
|
||||
res, err := RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
|
||||
res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
|
||||
assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
|
||||
assert.Equal(t, test.expected, res, "Unexpected result in testcase %q", test.testcase)
|
||||
|
||||
@@ -466,7 +466,7 @@ func TestColorPreview(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range negativeTests {
|
||||
res, err := RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test)
|
||||
res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test)
|
||||
assert.NoError(t, err, "Unexpected error in testcase: %q", test)
|
||||
assert.NotContains(t, res, `<span class="color-preview" style="background-color: `, "Unexpected result in testcase %q", test)
|
||||
}
|
||||
@@ -513,7 +513,7 @@ func TestMathBlock(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range testcases {
|
||||
res, err := RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
|
||||
res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
|
||||
assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
|
||||
assert.Equal(t, test.expected, res, "Unexpected result in testcase %q", test.testcase)
|
||||
|
||||
@@ -551,7 +551,7 @@ foo: bar
|
||||
}
|
||||
|
||||
for _, test := range testcases {
|
||||
res, err := RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
|
||||
res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
|
||||
assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
|
||||
assert.Equal(t, test.expected, res, "Unexpected result in testcase %q", test.testcase)
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestManager_Cancel(t *testing.T) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
default:
|
||||
assert.Fail(t, "Cancel should cancel the provided context")
|
||||
assert.FailNow(t, "Cancel should cancel the provided context")
|
||||
}
|
||||
finished()
|
||||
|
||||
@@ -62,7 +62,7 @@ func TestManager_Cancel(t *testing.T) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
default:
|
||||
assert.Fail(t, "Cancel should cancel the provided context")
|
||||
assert.FailNow(t, "Cancel should cancel the provided context")
|
||||
}
|
||||
finished()
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ CONN_STR = redis://
|
||||
assert.Equal(t, "default", q.GetName())
|
||||
assert.Equal(t, "level", q.GetType())
|
||||
assert.Equal(t, filepath.Join(setting.AppDataPath, "queues/common"), q.baseConfig.DataFullDir)
|
||||
assert.Equal(t, 100, q.baseConfig.Length)
|
||||
assert.Equal(t, 100000, q.baseConfig.Length)
|
||||
assert.Equal(t, 20, q.batchLength)
|
||||
assert.Equal(t, "", q.baseConfig.ConnStr)
|
||||
assert.Equal(t, "default_queue", q.baseConfig.QueueFullName)
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
system_model "code.gitea.io/gitea/models/system"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@@ -103,12 +102,6 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) {
|
||||
assert.EqualValues(t, []string{"readme.md"}, headCommit.Modified)
|
||||
}
|
||||
|
||||
func initGravatarSource(t *testing.T) {
|
||||
setting.GravatarSource = "https://secure.gravatar.com/avatar"
|
||||
err := system_model.Init(db.DefaultContext)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestPushCommits_AvatarLink(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
@@ -132,7 +125,7 @@ func TestPushCommits_AvatarLink(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
initGravatarSource(t)
|
||||
setting.GravatarSource = "https://secure.gravatar.com/avatar"
|
||||
|
||||
assert.Equal(t,
|
||||
"https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon&s="+strconv.Itoa(28*setting.Avatar.RenderedSizeFactor),
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting/config"
|
||||
)
|
||||
|
||||
type PictureStruct struct {
|
||||
DisableGravatar *config.Value[bool]
|
||||
EnableFederatedAvatar *config.Value[bool]
|
||||
}
|
||||
|
||||
type ConfigStruct struct {
|
||||
Picture *PictureStruct
|
||||
}
|
||||
|
||||
var (
|
||||
defaultConfig *ConfigStruct
|
||||
defaultConfigOnce sync.Once
|
||||
)
|
||||
|
||||
func initDefaultConfig() {
|
||||
config.SetCfgSecKeyGetter(&cfgSecKeyGetter{})
|
||||
defaultConfig = &ConfigStruct{
|
||||
Picture: &PictureStruct{
|
||||
DisableGravatar: config.Bool(false, config.CfgSecKey{Sec: "picture", Key: "DISABLE_GRAVATAR"}, "picture.disable_gravatar"),
|
||||
EnableFederatedAvatar: config.Bool(false, config.CfgSecKey{Sec: "picture", Key: "ENABLE_FEDERATED_AVATAR"}, "picture.enable_federated_avatar"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Config() *ConfigStruct {
|
||||
defaultConfigOnce.Do(initDefaultConfig)
|
||||
return defaultConfig
|
||||
}
|
||||
|
||||
type cfgSecKeyGetter struct{}
|
||||
|
||||
func (c cfgSecKeyGetter) GetValue(sec, key string) (v string, has bool) {
|
||||
cfgSec, err := CfgProvider.GetSection(sec)
|
||||
if err != nil {
|
||||
log.Error("Unable to get config section: %q", sec)
|
||||
return "", false
|
||||
}
|
||||
cfgKey := ConfigSectionKey(cfgSec, key)
|
||||
if cfgKey == nil {
|
||||
return "", false
|
||||
}
|
||||
return cfgKey.Value(), true
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var getterMu sync.RWMutex
|
||||
|
||||
type CfgSecKeyGetter interface {
|
||||
GetValue(sec, key string) (v string, has bool)
|
||||
}
|
||||
|
||||
var cfgSecKeyGetterInternal CfgSecKeyGetter
|
||||
|
||||
func SetCfgSecKeyGetter(p CfgSecKeyGetter) {
|
||||
getterMu.Lock()
|
||||
cfgSecKeyGetterInternal = p
|
||||
getterMu.Unlock()
|
||||
}
|
||||
|
||||
func GetCfgSecKeyGetter() CfgSecKeyGetter {
|
||||
getterMu.RLock()
|
||||
defer getterMu.RUnlock()
|
||||
return cfgSecKeyGetterInternal
|
||||
}
|
||||
|
||||
type DynKeyGetter interface {
|
||||
GetValue(ctx context.Context, key string) (v string, has bool)
|
||||
GetRevision(ctx context.Context) int
|
||||
InvalidateCache()
|
||||
}
|
||||
|
||||
var dynKeyGetterInternal DynKeyGetter
|
||||
|
||||
func SetDynGetter(p DynKeyGetter) {
|
||||
getterMu.Lock()
|
||||
dynKeyGetterInternal = p
|
||||
getterMu.Unlock()
|
||||
}
|
||||
|
||||
func GetDynGetter() DynKeyGetter {
|
||||
getterMu.RLock()
|
||||
defer getterMu.RUnlock()
|
||||
return dynKeyGetterInternal
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type CfgSecKey struct {
|
||||
Sec, Key string
|
||||
}
|
||||
|
||||
type Value[T any] struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
cfgSecKey CfgSecKey
|
||||
dynKey string
|
||||
|
||||
def, value T
|
||||
revision int
|
||||
}
|
||||
|
||||
func (value *Value[T]) parse(s string) (v T) {
|
||||
switch any(v).(type) {
|
||||
case bool:
|
||||
b, _ := strconv.ParseBool(s)
|
||||
return any(b).(T)
|
||||
default:
|
||||
panic("unsupported config type, please complete the code")
|
||||
}
|
||||
}
|
||||
|
||||
func (value *Value[T]) Value(ctx context.Context) (v T) {
|
||||
dg := GetDynGetter()
|
||||
if dg == nil {
|
||||
// this is an edge case: the database is not initialized but the system setting is going to be used
|
||||
// it should panic to avoid inconsistent config values (from config / system setting) and fix the code
|
||||
panic("no config dyn value getter")
|
||||
}
|
||||
|
||||
rev := dg.GetRevision(ctx)
|
||||
|
||||
// if the revision in database doesn't change, use the last value
|
||||
value.mu.RLock()
|
||||
if rev == value.revision {
|
||||
v = value.value
|
||||
value.mu.RUnlock()
|
||||
return v
|
||||
}
|
||||
value.mu.RUnlock()
|
||||
|
||||
// try to parse the config and cache it
|
||||
var valStr *string
|
||||
if dynVal, has := dg.GetValue(ctx, value.dynKey); has {
|
||||
valStr = &dynVal
|
||||
} else if cfgVal, has := GetCfgSecKeyGetter().GetValue(value.cfgSecKey.Sec, value.cfgSecKey.Key); has {
|
||||
valStr = &cfgVal
|
||||
}
|
||||
if valStr == nil {
|
||||
v = value.def
|
||||
} else {
|
||||
v = value.parse(*valStr)
|
||||
}
|
||||
|
||||
value.mu.Lock()
|
||||
value.value = v
|
||||
value.revision = rev
|
||||
value.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
|
||||
func (value *Value[T]) DynKey() string {
|
||||
return value.dynKey
|
||||
}
|
||||
|
||||
func Bool(def bool, cfgSecKey CfgSecKey, dynKey string) *Value[bool] {
|
||||
return &Value[bool]{def: def, cfgSecKey: cfgSecKey, dynKey: dynKey}
|
||||
}
|
||||
@@ -149,8 +149,9 @@ func EnvironmentToConfig(cfg ConfigProvider, envs []string) (changed bool) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
key := section.Key(keyName)
|
||||
key := ConfigSectionKey(section, keyName)
|
||||
if key == nil {
|
||||
changed = true
|
||||
key, err = section.NewKey(keyName, keyValue)
|
||||
if err != nil {
|
||||
log.Error("Error creating key: %s in section: %s with value: %s : %v", keyName, sectionName, keyValue, err)
|
||||
|
||||
@@ -115,3 +115,29 @@ key = old
|
||||
EnvironmentToConfig(cfg, []string{"GITEA__sec__key__FILE=" + tmpFile})
|
||||
assert.Equal(t, "value-from-file\n", cfg.Section("sec").Key("key").String())
|
||||
}
|
||||
|
||||
func TestEnvironmentToConfigSubSecKey(t *testing.T) {
|
||||
// the INI package has a quirk: by default, the keys are inherited.
|
||||
// when maintaining the keys, the newly added sub key should not be affected by the parent key.
|
||||
cfg, err := NewConfigProviderFromData(`
|
||||
[sec]
|
||||
key = some
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
|
||||
changed := EnvironmentToConfig(cfg, []string{"GITEA__sec_0X2E_sub__key=some"})
|
||||
assert.True(t, changed)
|
||||
|
||||
tmpFile := t.TempDir() + "/test-sub-sec-key.ini"
|
||||
defer os.Remove(tmpFile)
|
||||
err = cfg.SaveTo(tmpFile)
|
||||
assert.NoError(t, err)
|
||||
bs, err := os.ReadFile(tmpFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `[sec]
|
||||
key = some
|
||||
|
||||
[sec.sub]
|
||||
key = some
|
||||
`, string(bs))
|
||||
}
|
||||
|
||||
@@ -213,11 +213,9 @@ func NewConfigProviderFromFile(file string, extraConfigs ...string) (ConfigProvi
|
||||
}
|
||||
}
|
||||
|
||||
if len(extraConfigs) > 0 {
|
||||
for _, s := range extraConfigs {
|
||||
if err := cfg.Append([]byte(s)); err != nil {
|
||||
return nil, fmt.Errorf("unable to append more config: %v", err)
|
||||
}
|
||||
for _, s := range extraConfigs {
|
||||
if err := cfg.Append([]byte(s)); err != nil {
|
||||
return nil, fmt.Errorf("unable to append more config: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ var OAuth2 = struct {
|
||||
JWTSigningAlgorithm: "RS256",
|
||||
JWTSigningPrivateKeyFile: "jwt/private.pem",
|
||||
MaxTokenLength: math.MaxInt16,
|
||||
DefaultApplications: []string{"git-credential-oauth", "git-credential-manager"},
|
||||
DefaultApplications: []string{"git-credential-oauth", "git-credential-manager", "tea"},
|
||||
}
|
||||
|
||||
func loadOAuth2From(rootCfg ConfigProvider) {
|
||||
|
||||
@@ -30,7 +30,7 @@ func GetQueueSettings(rootCfg ConfigProvider, name string) (QueueSettings, error
|
||||
queueSettingsDefault := QueueSettings{
|
||||
Type: "level", // dummy, channel, level, redis
|
||||
Datadir: "queues/common", // relative to AppDataPath
|
||||
Length: 100, // queue length before a channel queue will block
|
||||
Length: 100000, // queue length before a channel queue will block
|
||||
|
||||
QueueName: "_queue",
|
||||
SetName: "_unique",
|
||||
|
||||
@@ -19,7 +19,6 @@ var (
|
||||
SecretKey string
|
||||
InternalToken string // internal access token
|
||||
LogInRememberDays int
|
||||
CookieUserName string
|
||||
CookieRememberName string
|
||||
ReverseProxyAuthUser string
|
||||
ReverseProxyAuthEmail string
|
||||
@@ -104,7 +103,6 @@ func loadSecurityFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("security")
|
||||
InstallLock = HasInstallLock(rootCfg)
|
||||
LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7)
|
||||
CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome")
|
||||
SecretKey = loadSecret(sec, "SECRET_KEY_URI", "SECRET_KEY")
|
||||
if SecretKey == "" {
|
||||
// FIXME: https://github.com/go-gitea/gitea/issues/16832
|
||||
|
||||
@@ -76,8 +76,8 @@ var UI = struct {
|
||||
CodeCommentLines: 4,
|
||||
ReactionMaxUserNum: 10,
|
||||
MaxDisplayFileSize: 8388608,
|
||||
DefaultTheme: `auto`,
|
||||
Themes: []string{`auto`, `gitea`, `arc-green`},
|
||||
DefaultTheme: `gitea-auto`,
|
||||
Themes: []string{`gitea-auto`, `gitea-light`, `gitea-dark`},
|
||||
Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
|
||||
CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`},
|
||||
CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"},
|
||||
|
||||
@@ -16,13 +16,16 @@ const (
|
||||
CommitStatusError CommitStatusState = "error"
|
||||
// CommitStatusFailure is for when the CommitStatus is Failure
|
||||
CommitStatusFailure CommitStatusState = "failure"
|
||||
// CommitStatusWarning is for when the CommitStatus is Warning
|
||||
CommitStatusWarning CommitStatusState = "warning"
|
||||
)
|
||||
|
||||
var commitStatusPriorities = map[CommitStatusState]int{
|
||||
CommitStatusError: 0,
|
||||
CommitStatusFailure: 1,
|
||||
CommitStatusPending: 2,
|
||||
CommitStatusSuccess: 3,
|
||||
CommitStatusWarning: 2,
|
||||
CommitStatusPending: 3,
|
||||
CommitStatusSuccess: 4,
|
||||
}
|
||||
|
||||
func (css CommitStatusState) String() string {
|
||||
@@ -32,7 +35,7 @@ func (css CommitStatusState) String() string {
|
||||
// NoBetterThan returns true if this State is no better than the given State
|
||||
// This function only handles the states defined in CommitStatusPriorities
|
||||
func (css CommitStatusState) NoBetterThan(css2 CommitStatusState) bool {
|
||||
// NoBetterThan only handles the 4 states above
|
||||
// NoBetterThan only handles the 5 states above
|
||||
if _, exist := commitStatusPriorities[css]; !exist {
|
||||
return false
|
||||
}
|
||||
@@ -63,3 +66,8 @@ func (css CommitStatusState) IsError() bool {
|
||||
func (css CommitStatusState) IsFailure() bool {
|
||||
return css == CommitStatusFailure
|
||||
}
|
||||
|
||||
// IsWarning represents if commit status state is warning
|
||||
func (css CommitStatusState) IsWarning() bool {
|
||||
return css == CommitStatusWarning
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
|
||||
package system
|
||||
|
||||
import "context"
|
||||
|
||||
// StateStore is the interface to get/set app state items
|
||||
type StateStore interface {
|
||||
Get(item StateItem) error
|
||||
Set(item StateItem) error
|
||||
Get(ctx context.Context, item StateItem) error
|
||||
Set(ctx context.Context, item StateItem) error
|
||||
}
|
||||
|
||||
// StateItem provides the name for a state item. the name will be used to generate filenames, etc
|
||||
|
||||
@@ -6,6 +6,7 @@ package system
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -40,25 +41,25 @@ func TestAppStateDB(t *testing.T) {
|
||||
as := &DBStore{}
|
||||
|
||||
item1 := new(testItem1)
|
||||
assert.NoError(t, as.Get(item1))
|
||||
assert.NoError(t, as.Get(db.DefaultContext, item1))
|
||||
assert.Equal(t, "", item1.Val1)
|
||||
assert.EqualValues(t, 0, item1.Val2)
|
||||
|
||||
item1 = new(testItem1)
|
||||
item1.Val1 = "a"
|
||||
item1.Val2 = 2
|
||||
assert.NoError(t, as.Set(item1))
|
||||
assert.NoError(t, as.Set(db.DefaultContext, item1))
|
||||
|
||||
item2 := new(testItem2)
|
||||
item2.K = "V"
|
||||
assert.NoError(t, as.Set(item2))
|
||||
assert.NoError(t, as.Set(db.DefaultContext, item2))
|
||||
|
||||
item1 = new(testItem1)
|
||||
assert.NoError(t, as.Get(item1))
|
||||
assert.NoError(t, as.Get(db.DefaultContext, item1))
|
||||
assert.Equal(t, "a", item1.Val1)
|
||||
assert.EqualValues(t, 2, item1.Val2)
|
||||
|
||||
item2 = new(testItem2)
|
||||
assert.NoError(t, as.Get(item2))
|
||||
assert.NoError(t, as.Get(db.DefaultContext, item2))
|
||||
assert.Equal(t, "V", item2.K)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models/system"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
|
||||
@@ -14,8 +16,8 @@ import (
|
||||
type DBStore struct{}
|
||||
|
||||
// Get reads the state item
|
||||
func (f *DBStore) Get(item StateItem) error {
|
||||
content, err := system.GetAppStateContent(item.Name())
|
||||
func (f *DBStore) Get(ctx context.Context, item StateItem) error {
|
||||
content, err := system.GetAppStateContent(ctx, item.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -26,10 +28,10 @@ func (f *DBStore) Get(item StateItem) error {
|
||||
}
|
||||
|
||||
// Set saves the state item
|
||||
func (f *DBStore) Set(item StateItem) error {
|
||||
func (f *DBStore) Set(ctx context.Context, item StateItem) error {
|
||||
b, err := json.Marshal(item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return system.SaveAppStateContent(item.Name(), util.BytesToReadOnlyString(b))
|
||||
return system.SaveAppStateContent(ctx, item.Name(), util.BytesToReadOnlyString(b))
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/emoji"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
@@ -131,8 +132,11 @@ func NewFuncMap() template.FuncMap {
|
||||
"DisableImportLocal": func() bool {
|
||||
return !setting.ImportLocalPaths
|
||||
},
|
||||
"DefaultTheme": func() string {
|
||||
return setting.UI.DefaultTheme
|
||||
"ThemeName": func(user *user_model.User) string {
|
||||
if user == nil || user.Theme == "" {
|
||||
return setting.UI.DefaultTheme
|
||||
}
|
||||
return user.Theme
|
||||
},
|
||||
"NotificationSettings": func() map[string]any {
|
||||
return map[string]any{
|
||||
|
||||
@@ -210,9 +210,8 @@ func (p *templateErrorPrettier) handleTemplateRenderingError(err error) string {
|
||||
} else if execErr, ok := err.(texttemplate.ExecError); ok {
|
||||
layerName := p.assets.GetFileLayerName(execErr.Name + ".tmpl")
|
||||
return fmt.Sprintf("asset from: %s, %s", layerName, err.Error())
|
||||
} else {
|
||||
return err.Error()
|
||||
}
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
func HandleTemplateRenderingError(err error) string {
|
||||
|
||||
@@ -74,27 +74,31 @@ func ActionIcon(opType activities_model.ActionType) string {
|
||||
switch opType {
|
||||
case activities_model.ActionCreateRepo, activities_model.ActionTransferRepo, activities_model.ActionRenameRepo:
|
||||
return "repo"
|
||||
case activities_model.ActionCommitRepo, activities_model.ActionPushTag, activities_model.ActionDeleteTag, activities_model.ActionDeleteBranch:
|
||||
case activities_model.ActionCommitRepo:
|
||||
return "git-commit"
|
||||
case activities_model.ActionCreateIssue:
|
||||
return "issue-opened"
|
||||
case activities_model.ActionCreatePullRequest:
|
||||
return "git-pull-request"
|
||||
case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
|
||||
return "comment-discussion"
|
||||
case activities_model.ActionDeleteBranch:
|
||||
return "git-branch"
|
||||
case activities_model.ActionMergePullRequest, activities_model.ActionAutoMergePullRequest:
|
||||
return "git-merge"
|
||||
case activities_model.ActionCloseIssue, activities_model.ActionClosePullRequest:
|
||||
case activities_model.ActionCreatePullRequest:
|
||||
return "git-pull-request"
|
||||
case activities_model.ActionClosePullRequest:
|
||||
return "git-pull-request-closed"
|
||||
case activities_model.ActionCreateIssue:
|
||||
return "issue-opened"
|
||||
case activities_model.ActionCloseIssue:
|
||||
return "issue-closed"
|
||||
case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
|
||||
return "issue-reopened"
|
||||
case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
|
||||
return "comment-discussion"
|
||||
case activities_model.ActionMirrorSyncPush, activities_model.ActionMirrorSyncCreate, activities_model.ActionMirrorSyncDelete:
|
||||
return "mirror"
|
||||
case activities_model.ActionApprovePullRequest:
|
||||
return "check"
|
||||
case activities_model.ActionRejectPullRequest:
|
||||
return "diff"
|
||||
case activities_model.ActionPublishRelease:
|
||||
return "file-diff"
|
||||
case activities_model.ActionPublishRelease, activities_model.ActionPushTag, activities_model.ActionDeleteTag:
|
||||
return "tag"
|
||||
case activities_model.ActionPullReviewDismissed:
|
||||
return "x"
|
||||
|
||||
@@ -179,7 +179,7 @@ func RenderLabel(ctx context.Context, label *issues_model.Label) template.HTML {
|
||||
|
||||
s := fmt.Sprintf("<span class='ui label scope-parent' title='%s'>"+
|
||||
"<div class='ui label scope-left' style='color: %s !important; background-color: %s !important'>%s</div>"+
|
||||
"<div class='ui label scope-right' style='color: %s !important; background-color: %s !important''>%s</div>"+
|
||||
"<div class='ui label scope-right' style='color: %s !important; background-color: %s !important'>%s</div>"+
|
||||
"</span>",
|
||||
description,
|
||||
textColor, scopeColor, scopeText,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package updatechecker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
@@ -58,31 +59,31 @@ func GiteaUpdateChecker(httpEndpoint string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return UpdateRemoteVersion(respData.Latest.Version)
|
||||
return UpdateRemoteVersion(req.Context(), respData.Latest.Version)
|
||||
}
|
||||
|
||||
// UpdateRemoteVersion updates the latest available version of Gitea
|
||||
func UpdateRemoteVersion(version string) (err error) {
|
||||
return system.AppState.Set(&CheckerState{LatestVersion: version})
|
||||
func UpdateRemoteVersion(ctx context.Context, version string) (err error) {
|
||||
return system.AppState.Set(ctx, &CheckerState{LatestVersion: version})
|
||||
}
|
||||
|
||||
// GetRemoteVersion returns the current remote version (or currently installed version if fail to fetch from DB)
|
||||
func GetRemoteVersion() string {
|
||||
func GetRemoteVersion(ctx context.Context) string {
|
||||
item := new(CheckerState)
|
||||
if err := system.AppState.Get(item); err != nil {
|
||||
if err := system.AppState.Get(ctx, item); err != nil {
|
||||
return ""
|
||||
}
|
||||
return item.LatestVersion
|
||||
}
|
||||
|
||||
// GetNeedUpdate returns true whether a newer version of Gitea is available
|
||||
func GetNeedUpdate() bool {
|
||||
func GetNeedUpdate(ctx context.Context) bool {
|
||||
curVer, err := version.NewVersion(setting.AppVer)
|
||||
if err != nil {
|
||||
// return false to fail silently
|
||||
return false
|
||||
}
|
||||
remoteVerStr := GetRemoteVersion()
|
||||
remoteVerStr := GetRemoteVersion(ctx)
|
||||
if remoteVerStr == "" {
|
||||
// no remote version is known
|
||||
return false
|
||||
|
||||
@@ -40,9 +40,8 @@ func PathJoinRel(elem ...string) string {
|
||||
return ""
|
||||
} else if p == "/" {
|
||||
return "."
|
||||
} else {
|
||||
return p[1:]
|
||||
}
|
||||
return p[1:]
|
||||
}
|
||||
|
||||
// PathJoinRelX joins the path elements into a single path like PathJoinRel,
|
||||
|
||||
Reference in New Issue
Block a user