feat: update gitea vendor
runner nix smoke / nix label and flake smoke (push) Failing after 1m28s

This commit is contained in:
2026-09-26 21:18:24 +00:00
parent c439c1b948
commit d9b2a4e787
3538 changed files with 116131 additions and 44340 deletions
+54 -14
View File
@@ -9,9 +9,13 @@ import (
"strings"
"time"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/log"
)
const defaultMaxRerunAttempts = 50
const defaultMaxConcurrentTaskPicks = 16
// Actions settings
var (
Actions = struct {
@@ -27,11 +31,20 @@ var (
AbandonedJobTimeout time.Duration `ini:"ABANDONED_JOB_TIMEOUT"`
SkipWorkflowStrings []string `ini:"SKIP_WORKFLOW_STRINGS"`
WorkflowDirs []string `ini:"WORKFLOW_DIRS"`
ScopedWorkflowDirs []string `ini:"SCOPED_WORKFLOW_DIRS"`
MaxRerunAttempts int64 `ini:"MAX_RERUN_ATTEMPTS"`
// MaxConcurrentTaskPicks bounds how many runners may run the task-assignment
// transaction at once per Gitea instance, to avoid a thundering herd when many
// runners poll together. It is a per-process limit, not a cluster-wide one.
MaxConcurrentTaskPicks int `ini:"MAX_CONCURRENT_TASK_PICKS"`
}{
Enabled: true,
DefaultActionsURL: defaultActionsURLGitHub,
SkipWorkflowStrings: []string{"[skip ci]", "[ci skip]", "[no ci]", "[skip actions]", "[actions skip]"},
WorkflowDirs: []string{".gitea/workflows", ".github/workflows"},
Enabled: true,
DefaultActionsURL: defaultActionsURLGitHub,
SkipWorkflowStrings: []string{"[skip ci]", "[ci skip]", "[no ci]", "[skip actions]", "[actions skip]"},
WorkflowDirs: []string{".gitea/workflows", ".github/workflows"},
ScopedWorkflowDirs: []string{".gitea/scoped_workflows"},
MaxRerunAttempts: defaultMaxRerunAttempts,
MaxConcurrentTaskPicks: defaultMaxConcurrentTaskPicks,
}
)
@@ -118,24 +131,51 @@ func loadActionsFrom(rootCfg ConfigProvider) error {
Actions.EndlessTaskTimeout = sec.Key("ENDLESS_TASK_TIMEOUT").MustDuration(3 * time.Hour)
Actions.AbandonedJobTimeout = sec.Key("ABANDONED_JOB_TIMEOUT").MustDuration(24 * time.Hour)
if Actions.MaxRerunAttempts <= 0 {
Actions.MaxRerunAttempts = defaultMaxRerunAttempts
}
if Actions.MaxConcurrentTaskPicks <= 0 {
Actions.MaxConcurrentTaskPicks = defaultMaxConcurrentTaskPicks
}
if !Actions.LogCompression.IsValid() {
return fmt.Errorf("invalid [actions] LOG_COMPRESSION: %q", Actions.LogCompression)
}
workflowDirs := make([]string, 0, len(Actions.WorkflowDirs))
for _, dir := range Actions.WorkflowDirs {
workflowDirs := normalizeWorkflowDirs(Actions.WorkflowDirs)
if len(workflowDirs) == 0 {
return errors.New("[actions] WORKFLOW_DIRS must contain at least one entry")
}
Actions.WorkflowDirs = workflowDirs
// SCOPED_WORKFLOW_DIRS may be empty (feature disabled), but it must not overlap with WORKFLOW_DIRS:
// a scoped dir nested in (or equal to) a workflow dir would make the same file run both repo-level and scope-level.
Actions.ScopedWorkflowDirs = normalizeWorkflowDirs(Actions.ScopedWorkflowDirs)
for _, scopedDir := range Actions.ScopedWorkflowDirs {
for _, workflowDir := range Actions.WorkflowDirs {
if scopedDir == workflowDir ||
strings.HasPrefix(scopedDir, workflowDir+"/") ||
strings.HasPrefix(workflowDir, scopedDir+"/") {
return fmt.Errorf("[actions] SCOPED_WORKFLOW_DIRS entry %q overlaps with WORKFLOW_DIRS entry %q", scopedDir, workflowDir)
}
}
}
return nil
}
// normalizeWorkflowDirs trims, normalizes separators and drops empty/trailing-slash entries.
func normalizeWorkflowDirs(dirs []string) []string {
normalized := make([]string, 0, len(dirs))
for _, dir := range dirs {
dir = strings.TrimSpace(dir)
if dir == "" {
continue
}
dir = strings.ReplaceAll(dir, `\`, `/`)
dir = strings.TrimRight(dir, "/")
workflowDirs = append(workflowDirs, dir)
normalized = append(normalized, dir)
}
if len(workflowDirs) == 0 {
return errors.New("[actions] WORKFLOW_DIRS must contain at least one entry")
}
Actions.WorkflowDirs = workflowDirs
return nil
return normalized
}
@@ -5,8 +5,11 @@ package setting
import (
"path/filepath"
"slices"
"testing"
"gitea.dev/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -238,3 +241,71 @@ DEFAULT_ACTIONS_URL = gitea
})
}
}
func Test_ScopedWorkflowDirs(t *testing.T) {
defer test.MockVariableValue(&Actions)()
defaultWorkflowDirs := []string{".gitea/workflows", ".github/workflows"}
defaultScopedDirs := []string{".gitea/scoped_workflows"}
tests := []struct {
name string
iniStr string
wantScoped []string
wantErr bool
}{
{
name: "default",
iniStr: `[actions]`,
wantScoped: defaultScopedDirs,
},
{
name: "custom dir",
iniStr: "[actions]\nSCOPED_WORKFLOW_DIRS = .gitea/my-scoped",
wantScoped: []string{".gitea/my-scoped"},
},
{
name: "empty disables the feature",
iniStr: "[actions]\nSCOPED_WORKFLOW_DIRS = , ,",
wantScoped: []string{},
},
{
name: "overlap equal with workflow dir",
iniStr: "[actions]\nWORKFLOW_DIRS = .gitea/workflows\nSCOPED_WORKFLOW_DIRS = .gitea/workflows",
wantErr: true,
},
{
name: "scoped dir nested under workflow dir",
iniStr: "[actions]\nWORKFLOW_DIRS = .gitea/workflows\nSCOPED_WORKFLOW_DIRS = .gitea/workflows/scoped",
wantErr: true,
},
{
name: "workflow dir nested under scoped dir",
iniStr: "[actions]\nWORKFLOW_DIRS = .gitea/workflows/ci\nSCOPED_WORKFLOW_DIRS = .gitea/workflows",
wantErr: true,
},
{
name: "no overlap",
iniStr: "[actions]\nSCOPED_WORKFLOW_DIRS = .gitea/scoped",
wantScoped: []string{".gitea/scoped"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// reset to defaults so MapTo starts clean (absent keys keep the defaults)
Actions.WorkflowDirs = slices.Clone(defaultWorkflowDirs)
Actions.ScopedWorkflowDirs = slices.Clone(defaultScopedDirs)
cfg, err := NewConfigProviderFromData(tt.iniStr)
require.NoError(t, err)
err = loadActionsFrom(cfg)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tt.wantScoped, Actions.ScopedWorkflowDirs)
})
}
}
@@ -4,8 +4,8 @@
package setting
import (
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/container"
"gitea.dev/modules/log"
)
// Admin settings
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"net/url"
"path"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/log"
)
// API settings
@@ -7,7 +7,7 @@ import (
"strings"
"time"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/log"
)
// Cache represents cache settings
+1 -1
View File
@@ -6,7 +6,7 @@ package setting
import (
"strconv"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/log"
)
var Camo = struct {
@@ -7,8 +7,8 @@ import (
"strings"
"sync"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting/config"
"gitea.dev/modules/log"
"gitea.dev/modules/setting/config"
)
type PictureStruct struct {
@@ -8,9 +8,9 @@ import (
"reflect"
"sync"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
"gitea.dev/modules/json"
"gitea.dev/modules/log"
"gitea.dev/modules/util"
)
type CfgSecKey struct {
@@ -78,11 +78,16 @@ func isZeroOrEmpty(v any) bool {
return false
}
var SkipDatabaseConfig bool
func (opt *Option[T]) ValueRevision(ctx context.Context) (v T, rev int, has bool) {
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
if SkipDatabaseConfig {
return opt.DefaultValue(), 0, false
}
panic("no config dyn value getter")
}
@@ -10,7 +10,7 @@ import (
"strconv"
"strings"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/log"
)
const (
@@ -6,7 +6,7 @@ package setting
import (
"time"
"code.gitea.io/gitea/modules/setting/config"
"gitea.dev/modules/setting/config"
)
// WebBannerType fields are directly used in templates,
@@ -12,8 +12,8 @@ import (
"strings"
"time"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
"gitea.dev/modules/log"
"gitea.dev/modules/util"
"gopkg.in/ini.v1" //nolint:depguard // wrapper for this package
)
+31 -144
View File
@@ -4,40 +4,34 @@
package setting
import (
"errors"
"fmt"
"net"
"net/url"
"os"
"path/filepath"
"strings"
"time"
)
const DefaultSQLiteBusyTimeout = 20 * 1000
var (
// SupportedDatabaseTypes includes all XORM supported databases type, sqlite3 maybe added by `database_sqlite3.go`
// SupportedDatabaseTypes includes all XORM supported databases type, sqlite3 maybe added by the tag-controlled drivers
SupportedDatabaseTypes = []string{"mysql", "postgres", "mssql"}
// DatabaseTypeNames contains the friendly names for all database types
DatabaseTypeNames = map[string]string{"mysql": "MySQL", "postgres": "PostgreSQL", "mssql": "MSSQL", "sqlite3": "SQLite3"}
// EnableSQLite3 use SQLite3, set by build flag
EnableSQLite3 bool
DatabaseTypeNames = map[string]string{"mysql": "MySQL", "postgres": "PostgreSQL", "mssql": "MSSQL", DatabaseTypeSQLite3: "SQLite3"}
// Database holds the database settings
Database = struct {
Type DatabaseType
Host string
Name string
User string
Passwd string
Schema string
SSLMode string
Path string
Type DatabaseType
Host string
Name string
User string
Passwd string
Schema string
SSLMode string
Path string
SQLiteBusyTimeout int
SQLiteJournalMode string
LogSQL bool
MysqlCharset string
CharsetCollation string
Timeout int // seconds
SQLiteJournalMode string
DBConnectRetries int
DBConnectBackoff time.Duration
MaxIdleConns int
@@ -47,8 +41,8 @@ var (
AutoMigration bool
SlowQueryThreshold time.Duration
}{
Timeout: 500,
IterateBufferSize: 50,
IterateBufferSize: 50,
SlowQueryThreshold: 5 * time.Second,
}
)
@@ -64,15 +58,20 @@ func loadDBSetting(rootCfg ConfigProvider) {
Database.Host = sec.Key("HOST").String()
Database.Name = sec.Key("NAME").String()
Database.User = sec.Key("USER").String()
if len(Database.Passwd) == 0 {
Database.Passwd = sec.Key("PASSWD").String()
}
Database.Passwd = sec.Key("PASSWD").String()
Database.Schema = sec.Key("SCHEMA").String()
Database.SSLMode = sec.Key("SSL_MODE").MustString("disable")
Database.CharsetCollation = sec.Key("CHARSET_COLLATION").String()
Database.Path = sec.Key("PATH").MustString(filepath.Join(AppDataPath, "gitea.db"))
Database.Timeout = sec.Key("SQLITE_TIMEOUT").MustInt(500)
Database.SQLiteBusyTimeout = sec.Key("SQLITE_TIMEOUT").MustInt(DefaultSQLiteBusyTimeout)
// mattn driver isn't really affected by this timeout, but other drivers are affected
// the default value was 500 (0.5s), to avoid breaking existing users, make sure the timeout is long enough (at least, 5 seconds)
if Database.SQLiteBusyTimeout < 5000 {
Database.SQLiteBusyTimeout = DefaultSQLiteBusyTimeout
}
Database.SQLiteJournalMode = sec.Key("SQLITE_JOURNAL_MODE").MustString("")
Database.MaxIdleConns = sec.Key("MAX_IDLE_CONNS").MustInt(2)
@@ -88,128 +87,16 @@ func loadDBSetting(rootCfg ConfigProvider) {
Database.DBConnectRetries = sec.Key("DB_RETRIES").MustInt(10)
Database.DBConnectBackoff = sec.Key("DB_RETRY_BACKOFF").MustDuration(3 * time.Second)
Database.AutoMigration = sec.Key("AUTO_MIGRATION").MustBool(true)
Database.SlowQueryThreshold = sec.Key("SLOW_QUERY_THRESHOLD").MustDuration(5 * time.Second)
}
// DBConnStr returns database connection string
func DBConnStr() (string, error) {
var connStr string
paramSep := "?"
if strings.Contains(Database.Name, paramSep) {
paramSep = "&"
}
switch Database.Type {
case "mysql":
connType := "tcp"
if len(Database.Host) > 0 && Database.Host[0] == '/' { // looks like a unix socket
connType = "unix"
}
tls := Database.SSLMode
if tls == "disable" { // allow (Postgres-inspired) default value to work in MySQL
tls = "false"
}
connStr = fmt.Sprintf("%s:%s@%s(%s)/%s%sparseTime=true&tls=%s",
Database.User, Database.Passwd, connType, Database.Host, Database.Name, paramSep, tls)
case "postgres":
connStr = getPostgreSQLConnectionString(Database.Host, Database.User, Database.Passwd, Database.Name, Database.SSLMode)
case "mssql":
host, port := ParseMSSQLHostPort(Database.Host)
connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, Database.Name, Database.User, Database.Passwd)
case "sqlite3":
if !EnableSQLite3 {
return "", errors.New("this Gitea binary was not built with SQLite3 support")
}
if err := os.MkdirAll(filepath.Dir(Database.Path), os.ModePerm); err != nil {
return "", fmt.Errorf("Failed to create directories: %w", err)
}
journalMode := ""
if Database.SQLiteJournalMode != "" {
journalMode = "&_journal_mode=" + Database.SQLiteJournalMode
}
connStr = fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d&_txlock=immediate%s",
Database.Path, Database.Timeout, journalMode)
default:
return "", fmt.Errorf("unknown database type: %s", Database.Type)
}
return connStr, nil
}
// parsePostgreSQLHostPort parses given input in various forms defined in
// https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
// and returns proper host and port number.
func parsePostgreSQLHostPort(info string) (host, port string) {
if h, p, err := net.SplitHostPort(info); err == nil {
host, port = h, p
} else {
// treat the "info" as "host", if it's an IPv6 address, remove the wrapper
host = info
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
host = host[1 : len(host)-1]
}
}
// set fallback values
if host == "" {
host = "127.0.0.1"
}
if port == "" {
port = "5432"
}
return host, port
}
func getPostgreSQLConnectionString(dbHost, dbUser, dbPasswd, dbName, dbsslMode string) (connStr string) {
dbName, dbParam, _ := strings.Cut(dbName, "?")
host, port := parsePostgreSQLHostPort(dbHost)
connURL := url.URL{
Scheme: "postgres",
User: url.UserPassword(dbUser, dbPasswd),
Host: net.JoinHostPort(host, port),
Path: dbName,
OmitHost: false,
RawQuery: dbParam,
}
query := connURL.Query()
if strings.HasPrefix(host, "/") { // looks like a unix socket
query.Add("host", host)
connURL.Host = ":" + port
}
query.Set("sslmode", dbsslMode)
connURL.RawQuery = query.Encode()
return connURL.String()
}
// ParseMSSQLHostPort splits the host into host and port
func ParseMSSQLHostPort(info string) (string, string) {
// the default port "0" might be related to MSSQL's dynamic port, maybe it should be double-confirmed in the future
host, port := "127.0.0.1", "0"
if strings.Contains(info, ":") {
host = strings.Split(info, ":")[0]
port = strings.Split(info, ":")[1]
} else if strings.Contains(info, ",") {
host = strings.Split(info, ",")[0]
port = strings.TrimSpace(strings.Split(info, ",")[1])
} else if len(info) > 0 {
host = info
}
if host == "" {
host = "127.0.0.1"
}
if port == "" {
port = "0"
}
return host, port
Database.SlowQueryThreshold = sec.Key("SLOW_QUERY_THRESHOLD").MustDuration(Database.SlowQueryThreshold)
}
// DatabaseType FIXME: it is also used directly with "schemas.DBType", so the names must be consistent
type DatabaseType string
func (t DatabaseType) String() string {
return string(t)
}
const DatabaseTypeSQLite3 = "sqlite3"
func (t DatabaseType) IsSQLite3() bool {
return t == "sqlite3"
return t == DatabaseTypeSQLite3
}
func (t DatabaseType) IsMySQL() bool {
@@ -1,15 +0,0 @@
//go:build sqlite
// Copyright 2014 The Gogs Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
_ "github.com/mattn/go-sqlite3"
)
func init() {
EnableSQLite3 = true
SupportedDatabaseTypes = append(SupportedDatabaseTypes, "sqlite3")
}
@@ -1,109 +0,0 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_parsePostgreSQLHostPort(t *testing.T) {
tests := map[string]struct {
HostPort string
Host string
Port string
}{
"host-port": {
HostPort: "127.0.0.1:1234",
Host: "127.0.0.1",
Port: "1234",
},
"no-port": {
HostPort: "127.0.0.1",
Host: "127.0.0.1",
Port: "5432",
},
"ipv6-port": {
HostPort: "[::1]:1234",
Host: "::1",
Port: "1234",
},
"ipv6-no-port": {
HostPort: "[::1]",
Host: "::1",
Port: "5432",
},
"unix-socket": {
HostPort: "/tmp/pg.sock:1234",
Host: "/tmp/pg.sock",
Port: "1234",
},
"unix-socket-no-port": {
HostPort: "/tmp/pg.sock",
Host: "/tmp/pg.sock",
Port: "5432",
},
}
for k, test := range tests {
t.Run(k, func(t *testing.T) {
t.Log(test.HostPort)
host, port := parsePostgreSQLHostPort(test.HostPort)
assert.Equal(t, test.Host, host)
assert.Equal(t, test.Port, port)
})
}
}
func Test_getPostgreSQLConnectionString(t *testing.T) {
tests := []struct {
Host string
User string
Passwd string
Name string
SSLMode string
Output string
}{
{
Host: "", // empty means default
Output: "postgres://:@127.0.0.1:5432?sslmode=",
},
{
Host: "/tmp/pg.sock",
User: "testuser",
Passwd: "space space !#$%^^%^```-=?=",
Name: "gitea",
SSLMode: "false",
Output: "postgres://testuser:space%20space%20%21%23$%25%5E%5E%25%5E%60%60%60-=%3F=@:5432/gitea?host=%2Ftmp%2Fpg.sock&sslmode=false",
},
{
Host: "/tmp/pg.sock:6432",
User: "testuser",
Passwd: "pass",
Name: "gitea",
SSLMode: "false",
Output: "postgres://testuser:pass@:6432/gitea?host=%2Ftmp%2Fpg.sock&sslmode=false",
},
{
Host: "localhost",
User: "pgsqlusername",
Passwd: "I love Gitea!",
Name: "gitea",
SSLMode: "true",
Output: "postgres://pgsqlusername:I%20love%20Gitea%21@localhost:5432/gitea?sslmode=true",
},
{
Host: "localhost:1234",
User: "user",
Passwd: "pass",
Name: "gitea?param=1",
Output: "postgres://user:pass@localhost:1234/gitea?param=1&sslmode=",
},
}
for _, test := range tests {
connStr := getPostgreSQLConnectionString(test.Host, test.User, test.Passwd, test.Name, test.SSLMode)
assert.Equal(t, test.Output, connStr)
}
}
@@ -4,7 +4,7 @@
package setting
import (
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/log"
"github.com/42wim/httpsig"
)
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"strings"
"time"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/log"
)
// Git settings
@@ -6,7 +6,7 @@ package setting
import (
"testing"
"code.gitea.io/gitea/modules/test"
"gitea.dev/modules/test"
"github.com/stretchr/testify/assert"
)
@@ -4,8 +4,8 @@
package setting
import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/nosql"
"gitea.dev/modules/log"
"gitea.dev/modules/nosql"
)
// GlobalLock represents configuration of global lock
+1 -1
View File
@@ -3,7 +3,7 @@
package setting
import "code.gitea.io/gitea/modules/glob"
import "gitea.dev/modules/glob"
type GlobMatcher struct {
compiledGlob glob.Glob
@@ -9,7 +9,7 @@ import (
"net/mail"
"strings"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/log"
)
const IncomingEmailTokenPlaceholder = "%{token}"
@@ -9,7 +9,7 @@ import (
"strings"
"time"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/log"
)
// Indexer settings
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"fmt"
"time"
"code.gitea.io/gitea/modules/generate"
"gitea.dev/modules/generate"
)
// LFS represents the server-side configuration for Git LFS.
+3 -3
View File
@@ -10,8 +10,8 @@ import (
"path/filepath"
"strings"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
"gitea.dev/modules/log"
"gitea.dev/modules/util"
)
type LogGlobalConfig struct {
@@ -256,7 +256,7 @@ func initLoggerByName(manager *log.LoggerManager, rootCfg ConfigProvider, logger
}
func InitSQLLoggersForCli(level log.Level) {
log.SetConsoleLogger("xorm", "console", level)
log.SetupStderrLogger("xorm", "console-stderr", level)
}
func IsAccessLogEnabled() bool {
@@ -8,8 +8,8 @@ import (
"strings"
"testing"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/json"
"gitea.dev/modules/log"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -11,7 +11,7 @@ import (
"text/template"
"time"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/log"
"github.com/kballard/go-shellquote"
)
@@ -6,7 +6,7 @@ package setting
import (
"testing"
"code.gitea.io/gitea/modules/test"
"gitea.dev/modules/test"
"github.com/stretchr/testify/assert"
)
@@ -8,8 +8,8 @@ import (
"strings"
"sync"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
"gitea.dev/modules/log"
"gitea.dev/modules/util"
)
// ExternalMarkupRenderers represents the external markup renderers
@@ -50,7 +50,8 @@ var Markdown = struct {
MathCodeBlockDetection []string
MathCodeBlockOptions MarkdownMathCodeBlockOptions `ini:"-"`
}{
EnableMath: true,
EnableMath: true,
FileNamePatterns: []string{"*.md"},
}
// MarkupRenderer defines the external parser configured in ini
@@ -237,6 +238,10 @@ func fileExtensionsToPatterns(sectionName string, extensions []string) []string
return patterns
}
// MarkupRenderDefaultSandbox only contains a safe set of "sandbox allow" values, it is used to protect users from XSS attack,
// DO NOT USE "allow-same-origin" by default: if there is XSS in rendered content, same-origin makes the frame page can access parent window and send requests with user's credentials.
const MarkupRenderDefaultSandbox = "allow-scripts allow-forms allow-modals allow-popups allow-downloads"
func newMarkupRenderer(name string, sec ConfigSection) {
if !sec.Key("ENABLED").MustBool(false) {
return
@@ -269,9 +274,7 @@ func newMarkupRenderer(name string, sec ConfigSection) {
renderContentMode = RenderContentModeSanitized
}
// ATTENTION! at the moment, only a safe set like "allow-scripts" are allowed for sandbox mode.
// "allow-same-origin" should NEVER be used, it leads to XSS attack: makes the JS in iframe can access parent window's config and send requests with user's credentials.
renderContentSandbox := sec.Key("RENDER_CONTENT_SANDBOX").MustString("allow-scripts allow-popups")
renderContentSandbox := sec.Key("RENDER_CONTENT_SANDBOX").MustString(MarkupRenderDefaultSandbox)
if renderContentSandbox == "disabled" {
renderContentSandbox = ""
}
@@ -6,7 +6,7 @@ package setting
import (
"time"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/log"
)
// Mirror settings
@@ -8,8 +8,8 @@ import (
"path/filepath"
"sync/atomic"
"code.gitea.io/gitea/modules/generate"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/generate"
"gitea.dev/modules/log"
)
// OAuth2UsernameType is enum describing the way gitea generates its 'username' from oauth2 data
@@ -99,6 +99,7 @@ var OAuth2 = struct {
JWTClaimIssuer string `ini:"JWT_CLAIM_ISSUER"`
MaxTokenLength int
DefaultApplications []string
CustomSchemes []string
}{
Enabled: true,
AccessTokenExpirationTime: 3600,
@@ -107,7 +108,7 @@ var OAuth2 = struct {
JWTSigningAlgorithm: "RS256",
JWTSigningPrivateKeyFile: "jwt/private.pem",
MaxTokenLength: math.MaxInt16,
DefaultApplications: []string{"git-credential-oauth", "git-credential-manager", "tea"},
DefaultApplications: []string{"git-credential-oauth", "git-credential-manager", "tea", "gitea-app"},
}
func loadOAuth2From(rootCfg ConfigProvider) {
@@ -7,8 +7,8 @@ import (
"os"
"testing"
"code.gitea.io/gitea/modules/generate"
"code.gitea.io/gitea/modules/test"
"gitea.dev/modules/generate"
"gitea.dev/modules/test"
"github.com/stretchr/testify/assert"
)
@@ -62,7 +62,7 @@ func TestGetGeneralSigningSecretSave(t *testing.T) {
func TestOauth2DefaultApplications(t *testing.T) {
cfg, _ := NewConfigProviderFromData(``)
loadOAuth2From(cfg)
assert.Equal(t, []string{"git-credential-oauth", "git-credential-manager", "tea"}, OAuth2.DefaultApplications)
assert.Equal(t, []string{"git-credential-oauth", "git-credential-manager", "tea", "gitea-app"}, OAuth2.DefaultApplications)
cfg, _ = NewConfigProviderFromData(`[oauth2]
DEFAULT_APPLICATIONS = tea
@@ -3,7 +3,7 @@
package setting
import "code.gitea.io/gitea/modules/log"
import "gitea.dev/modules/log"
type OtherConfig struct {
ShowFooterVersion bool
+8 -2
View File
@@ -10,8 +10,8 @@ import (
"path/filepath"
"strings"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/tempdir"
"gitea.dev/modules/log"
"gitea.dev/modules/tempdir"
)
var (
@@ -198,6 +198,12 @@ func InitWorkPathAndCfgProvider(getEnvFn func(name string) string, args ArgWorkP
CustomConf = tmpCustomConf.Value
}
func MockBuiltinPaths(workPath, customPath, customConf string) func() {
oldApp, oldCustom, oldConf := appWorkPathBuiltin, customPathBuiltin, customConfBuiltin
appWorkPathBuiltin, customPathBuiltin, customConfBuiltin = workPath, customPath, customConf
return func() { appWorkPathBuiltin, customPathBuiltin, customConfBuiltin = oldApp, oldCustom, oldConf }
}
// AppDataTempDir returns a managed temporary directory for the application data.
// Using empty sub will get the managed base temp directory, and it's safe to delete it.
// Gitea only creates subdirectories under it, but not the APP_TEMP_PATH directory itself.
@@ -6,7 +6,7 @@ package setting
import (
"net/url"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/log"
)
// Proxy settings
@@ -7,8 +7,8 @@ import (
"path/filepath"
"runtime"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/json"
"gitea.dev/modules/log"
)
// QueueSettings represent the settings for a queue from the ini
@@ -8,7 +8,7 @@ import (
"path/filepath"
"strings"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/log"
)
// enumerates all the policy repository creating
@@ -37,6 +37,8 @@ var (
DefaultPrivate string
DefaultPushCreatePrivate bool
MaxCreationLimit int
UserMaxCreationLimit int
OrgMaxCreationLimit int
PreferredLicenses []string
DisableHTTPGit bool
AccessControlAllowOrigin string
@@ -165,6 +167,8 @@ var (
DefaultPrivate: RepoCreatingLastUserVisibility,
DefaultPushCreatePrivate: true,
MaxCreationLimit: -1,
UserMaxCreationLimit: -1,
OrgMaxCreationLimit: -1,
PreferredLicenses: []string{"Apache License 2.0", "MIT License"},
DisableHTTPGit: false,
AccessControlAllowOrigin: "",
@@ -297,7 +301,11 @@ func loadRepositoryFrom(rootCfg ConfigProvider) {
Repository.DisableHTTPGit = sec.Key("DISABLE_HTTP_GIT").MustBool()
Repository.UseCompatSSHURI = sec.Key("USE_COMPAT_SSH_URI").MustBool()
Repository.GoGetCloneURLProtocol = sec.Key("GO_GET_CLONE_URL_PROTOCOL").MustString("https")
// MAX_CREATION_LIMIT is a shortcut that sets the default for the two per-type limits below.
// USER_/ORG_MAX_CREATION_LIMIT take precedence when explicitly set.
Repository.MaxCreationLimit = sec.Key("MAX_CREATION_LIMIT").MustInt(-1)
Repository.UserMaxCreationLimit = sec.Key("USER_MAX_CREATION_LIMIT").MustInt(Repository.MaxCreationLimit)
Repository.OrgMaxCreationLimit = sec.Key("ORG_MAX_CREATION_LIMIT").MustInt(Repository.MaxCreationLimit)
Repository.DefaultBranch = sec.Key("DEFAULT_BRANCH").MustString(Repository.DefaultBranch)
RepoRootPath = sec.Key("ROOT").MustString(filepath.Join(AppDataPath, "gitea-repositories"))
if !filepath.IsAbs(RepoRootPath) {
@@ -0,0 +1,66 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"testing"
"gitea.dev/modules/test"
"github.com/stretchr/testify/assert"
)
func TestLoadRepositoryCreationLimits(t *testing.T) {
defer test.MockVariableValue(&Repository.MaxCreationLimit)()
defer test.MockVariableValue(&Repository.UserMaxCreationLimit)()
defer test.MockVariableValue(&Repository.OrgMaxCreationLimit)()
t.Run("ShortcutPropagatesToBoth", func(t *testing.T) {
cfg, err := NewConfigProviderFromData(`
[repository]
MAX_CREATION_LIMIT = 5
`)
assert.NoError(t, err)
loadRepositoryFrom(cfg)
assert.Equal(t, 5, Repository.MaxCreationLimit)
assert.Equal(t, 5, Repository.UserMaxCreationLimit)
assert.Equal(t, 5, Repository.OrgMaxCreationLimit)
})
t.Run("PerTypeKeysOverrideShortcut", func(t *testing.T) {
cfg, err := NewConfigProviderFromData(`
[repository]
MAX_CREATION_LIMIT = 5
USER_MAX_CREATION_LIMIT = 0
ORG_MAX_CREATION_LIMIT = -1
`)
assert.NoError(t, err)
loadRepositoryFrom(cfg)
assert.Equal(t, 0, Repository.UserMaxCreationLimit)
assert.Equal(t, -1, Repository.OrgMaxCreationLimit)
})
t.Run("PartialOverrideOtherInheritsShortcut", func(t *testing.T) {
cfg, err := NewConfigProviderFromData(`
[repository]
MAX_CREATION_LIMIT = 7
ORG_MAX_CREATION_LIMIT = -1
`)
assert.NoError(t, err)
loadRepositoryFrom(cfg)
assert.Equal(t, 7, Repository.UserMaxCreationLimit)
assert.Equal(t, -1, Repository.OrgMaxCreationLimit)
})
t.Run("NoKeyDefaultsToNoLimit", func(t *testing.T) {
cfg, err := NewConfigProviderFromData(`
[repository]
`)
assert.NoError(t, err)
loadRepositoryFrom(cfg)
assert.Equal(t, -1, Repository.MaxCreationLimit)
assert.Equal(t, -1, Repository.UserMaxCreationLimit)
assert.Equal(t, -1, Repository.OrgMaxCreationLimit)
})
}
@@ -8,17 +8,23 @@ import (
"os"
"strings"
"code.gitea.io/gitea/modules/auth/password/hash"
"code.gitea.io/gitea/modules/generate"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/auth/password/hash"
"gitea.dev/modules/generate"
"gitea.dev/modules/log"
)
// Security settings
var Security = struct {
// TODO: move more settings to this struct in future
XFrameOptions string
XFrameOptions string
XContentTypeOptions string
ContentSecurityPolicyGeneral string // it only supports empty (default policy) or "unset", maybe it can support more in the future
AllowedHostList string
}{
XFrameOptions: "SAMEORIGIN",
XFrameOptions: "SAMEORIGIN",
XContentTypeOptions: "nosniff",
AllowedHostList: "external",
}
var (
@@ -148,11 +154,12 @@ func loadSecurityFrom(rootCfg ConfigProvider) {
SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20)
deprecatedSetting(rootCfg, "cors", "X_FRAME_OPTIONS", "security", "X_FRAME_OPTIONS", "v1.26.0")
if sec.HasKey("X_FRAME_OPTIONS") {
Security.XFrameOptions = sec.Key("X_FRAME_OPTIONS").MustString(Security.XFrameOptions)
} else {
if !sec.HasKey("X_FRAME_OPTIONS") {
Security.XFrameOptions = rootCfg.Section("cors").Key("X_FRAME_OPTIONS").MustString(Security.XFrameOptions)
}
if err := sec.MapTo(&Security); err != nil {
log.Fatal("Failed to map security settings: %v", err)
}
twoFactorAuth := sec.Key("TWO_FACTOR_AUTH").String()
switch twoFactorAuth {
@@ -0,0 +1,29 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestLoadSecurityFrom(t *testing.T) {
assert.Equal(t, "SAMEORIGIN", Security.XFrameOptions)
assert.Equal(t, "nosniff", Security.XContentTypeOptions)
assert.Equal(t, "external", Security.AllowedHostList)
cfg, err := NewConfigProviderFromData(`[security]
X_FRAME_OPTIONS = DENY
X_CONTENT_TYPE_OPTIONS = unset
ALLOWED_HOST_LIST = foo
CONTENT_SECURITY_POLICY_GENERAL = "script-src *; foo"
`)
assert.NoError(t, err)
loadSecurityFrom(cfg)
assert.Equal(t, "DENY", Security.XFrameOptions)
assert.Equal(t, "unset", Security.XContentTypeOptions)
assert.Equal(t, "foo", Security.AllowedHostList)
assert.Equal(t, `"script-src *`, Security.ContentSecurityPolicyGeneral) // holy shit ini package bug
}
+2 -71
View File
@@ -4,7 +4,6 @@
package setting
import (
"encoding/base64"
"net"
"net/url"
"os"
@@ -13,8 +12,7 @@ import (
"strings"
"time"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/log"
)
// Scheme describes protocol types
@@ -112,72 +110,9 @@ var (
StartupTimeout time.Duration
PerWriteTimeout = 30 * time.Second
PerWritePerKbTimeout = 10 * time.Second
StaticURLPrefix string
AbsoluteAssetURL string
ManifestData string
StaticURLPrefix string // no trailing slash, defaults to AppSubURL, the URL can be relative or absolute
)
// MakeManifestData generates web app manifest JSON
func MakeManifestData(appName, appURL, absoluteAssetURL string) []byte {
type manifestIcon struct {
Src string `json:"src"`
Type string `json:"type"`
Sizes string `json:"sizes"`
}
type manifestJSON struct {
Name string `json:"name"`
ShortName string `json:"short_name"`
StartURL string `json:"start_url"`
Icons []manifestIcon `json:"icons"`
}
bytes, err := json.Marshal(&manifestJSON{
Name: appName,
ShortName: appName,
StartURL: appURL,
Icons: []manifestIcon{
{
Src: absoluteAssetURL + "/assets/img/logo.png",
Type: "image/png",
Sizes: "512x512",
},
{
Src: absoluteAssetURL + "/assets/img/logo.svg",
Type: "image/svg+xml",
Sizes: "512x512",
},
},
})
if err != nil {
log.Error("unable to marshal manifest JSON. Error: %v", err)
return make([]byte, 0)
}
return bytes
}
// MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash
func MakeAbsoluteAssetURL(appURL *url.URL, staticURLPrefix string) string {
parsedPrefix, err := url.Parse(strings.TrimSuffix(staticURLPrefix, "/"))
if err != nil {
log.Fatal("Unable to parse STATIC_URL_PREFIX: %v", err)
}
if err == nil && parsedPrefix.Hostname() == "" {
if staticURLPrefix == "" {
return strings.TrimSuffix(appURL.String(), "/")
}
// StaticURLPrefix is just a path
appHostURL := &url.URL{Scheme: appURL.Scheme, Host: appURL.Host}
return appHostURL.String() + "/" + strings.Trim(staticURLPrefix, "/")
}
return strings.TrimSuffix(staticURLPrefix, "/")
}
func loadServerFrom(rootCfg ConfigProvider) {
sec := rootCfg.Section("server")
AppName = rootCfg.Section("").Key("APP_NAME").MustString("Gitea: Git with a cup of tea")
@@ -313,10 +248,6 @@ func loadServerFrom(rootCfg ConfigProvider) {
Domain = urlHostname
}
AbsoluteAssetURL = MakeAbsoluteAssetURL(appURL, StaticURLPrefix)
manifestBytes := MakeManifestData(AppName, AppURL, AbsoluteAssetURL)
ManifestData = `application/json;base64,` + base64.StdEncoding.EncodeToString(manifestBytes)
var defaultLocalURL string
switch Protocol {
case HTTPUnix:
@@ -9,9 +9,9 @@ import (
"strings"
"time"
"code.gitea.io/gitea/modules/glob"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/structs"
"gitea.dev/modules/glob"
"gitea.dev/modules/log"
"gitea.dev/modules/structs"
)
// enumerates all the types of captchas
@@ -6,9 +6,9 @@ package setting
import (
"testing"
"code.gitea.io/gitea/modules/glob"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test"
"gitea.dev/modules/glob"
"gitea.dev/modules/structs"
"gitea.dev/modules/test"
"github.com/stretchr/testify/assert"
)
@@ -8,9 +8,9 @@ import (
"path/filepath"
"strings"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
"gitea.dev/modules/json"
"gitea.dev/modules/log"
"gitea.dev/modules/util"
)
// SessionConfig defines Session settings
@@ -11,10 +11,10 @@ import (
"strings"
"time"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/user"
"code.gitea.io/gitea/modules/util"
"gitea.dev/modules/log"
"gitea.dev/modules/optional"
"gitea.dev/modules/user"
"gitea.dev/modules/util"
)
// settings
@@ -42,9 +42,9 @@ func init() {
AppVer = "dev"
}
// We can rely on log.CanColorStdout being set properly because modules/log/console_windows.go comes before modules/setting/setting.go lexicographically
// FIXME: the logger shouldn't be initialized here, the app entry should initialize the logger
// By default set this logger at Info - we'll change it later, but we need to start with something.
log.SetConsoleLogger(log.DEFAULT, "console", log.INFO)
log.SetupStderrLogger(log.DEFAULT, "console-stderr", log.INFO)
}
// IsRunUserMatchCurrentUser returns false if configured run user does not match
@@ -1,41 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
import (
"net/url"
"testing"
"code.gitea.io/gitea/modules/json"
"github.com/stretchr/testify/assert"
)
func TestMakeAbsoluteAssetURL(t *testing.T) {
appURL1, _ := url.Parse("https://localhost:1234")
appURL2, _ := url.Parse("https://localhost:1234/")
appURLSub1, _ := url.Parse("https://localhost:1234/foo")
appURLSub2, _ := url.Parse("https://localhost:1234/foo/")
// static URL is an absolute URL, so should be used
assert.Equal(t, "https://localhost:2345", MakeAbsoluteAssetURL(appURL1, "https://localhost:2345"))
assert.Equal(t, "https://localhost:2345", MakeAbsoluteAssetURL(appURL1, "https://localhost:2345/"))
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL(appURL1, "/foo"))
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL(appURL2, "/foo"))
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL(appURL1, "/foo/"))
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL(appURLSub1, "/foo"))
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL(appURLSub2, "/foo"))
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL(appURLSub1, "/foo/"))
assert.Equal(t, "https://localhost:1234/bar", MakeAbsoluteAssetURL(appURLSub1, "/bar"))
assert.Equal(t, "https://localhost:1234/bar", MakeAbsoluteAssetURL(appURLSub2, "/bar"))
assert.Equal(t, "https://localhost:1234/bar", MakeAbsoluteAssetURL(appURLSub1, "/bar/"))
}
func TestMakeManifestData(t *testing.T) {
jsonBytes := MakeManifestData(`Example App '\"`, "https://example.com", "https://example.com/foo/bar")
assert.True(t, json.Valid(jsonBytes))
}
+5 -4
View File
@@ -9,8 +9,9 @@ import (
"text/template"
"time"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
"gitea.dev/modules/consts"
"gitea.dev/modules/log"
"gitea.dev/modules/util"
gossh "golang.org/x/crypto/ssh"
)
@@ -52,8 +53,8 @@ var SSH = struct {
Domain: "",
Port: 22,
MinimumKeySizeCheck: true,
MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 3071},
ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"},
MinimumKeySizes: map[string]int{"ed25519": consts.AsymKeyMinBitsEC, "ed25519-sk": consts.AsymKeyMinBitsEC, "ecdsa": consts.AsymKeyMinBitsEC, "ecdsa-sk": consts.AsymKeyMinBitsEC, "rsa": consts.AsymKeyMinBitsRsa},
ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gitea.ed25519", "ssh/gitea.ecdsa", "ssh/gogs.rsa"},
AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}",
PerWriteTimeout: PerWriteTimeout,
PerWritePerKbTimeout: PerWritePerKbTimeout,
+137 -34
View File
@@ -4,69 +4,172 @@
package setting
import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"code.gitea.io/gitea/modules/util"
"gitea.dev/modules/auth/password/hash"
"gitea.dev/modules/log"
"gitea.dev/modules/util"
"github.com/kballard/go-shellquote"
)
var giteaTestSourceRoot *string
var giteaTestSourceRoot *string // intentionally use a pointer to make sure the uninitialized access panics
func GetGiteaTestSourceRoot() string {
return *giteaTestSourceRoot
}
func detectGiteaTestRoot() string {
_, filename, _, _ := runtime.Caller(0)
giteaRoot := filepath.Dir(filepath.Dir(filepath.Dir(filename)))
fixturesDir := filepath.Join(giteaRoot, "models", "fixtures")
if _, err := os.Stat(fixturesDir); err != nil {
panic("in gitea source code directory, fixtures directory not found: " + fixturesDir)
}
return giteaRoot
}
func SetupGiteaTestEnv() {
if giteaTestSourceRoot != nil {
return // already initialized
}
IsInTesting = true
giteaRoot := os.Getenv("GITEA_TEST_ROOT")
if giteaRoot == "" {
_, filename, _, _ := runtime.Caller(0)
giteaRoot = filepath.Dir(filepath.Dir(filepath.Dir(filename)))
fixturesDir := filepath.Join(giteaRoot, "models", "fixtures")
if _, err := os.Stat(fixturesDir); err != nil {
panic("in gitea source code directory, fixtures directory not found: " + fixturesDir)
log.OsExiter = func(code int) {
if code != 0 {
// Non-zero exit code (log.Fatal) shouldn't occur during testing, if it happens:
// * Show a full stacktrace for more details.
// * If the "log.Fatal" is abused in tests, should fix.
panic(fmt.Errorf("non-zero exit code during testing: %d", code))
}
os.Exit(0)
}
initGiteaRoot := func() string {
giteaRoot := os.Getenv("GITEA_TEST_ROOT")
if giteaRoot == "" {
giteaRoot = detectGiteaTestRoot()
}
giteaTestSourceRoot = &giteaRoot
return giteaRoot
}
giteaRoot := initGiteaRoot()
initGiteaPaths := func() {
// need to load assets (options, public) from the source code directory for testing
StaticRootPath = giteaRoot
// during testing, the AppPath must point to the pre-built Gitea binary in the source root
// it needs to be called by git hooks
AppPath = filepath.Join(giteaRoot, "gitea") + util.Iif(IsWindows, ".exe", "")
}
initGiteaConf := func() string {
// giteaConf (GITEA_CONF) must be relative because it is used in the git hooks as "$GITEA_ROOT/$GITEA_CONF"
giteaConf := os.Getenv("GITEA_TEST_CONF")
if giteaConf == "" {
// if no GITEA_TEST_CONF, then it is in unit test, use a temp (non-existing / empty) config file
// do not really use such config file, the test can run concurrently, using the same config file will cause data-race between tests
giteaConf = "custom/conf/app-test-tmp.ini"
customConfBuiltin = filepath.Join(AppWorkPath, giteaConf)
CustomConf = customConfBuiltin
_ = os.Remove(CustomConf)
} else {
// CustomConf must be absolute path to make tests pass.
// At the moment, GITEA_TEST_CONF is always in Gitea's source root
CustomConf = filepath.Join(giteaRoot, giteaConf)
}
return giteaConf
}
cleanUpEnv := func() {
// also unset unnecessary env vars for testing (only keep "GITEA_TEST_*" ones)
UnsetUnnecessaryEnvVars()
for _, env := range os.Environ() {
if strings.HasPrefix(env, "GIT_") || (strings.HasPrefix(env, "GITEA_") && !strings.HasPrefix(env, "GITEA_TEST_")) {
k, _, _ := strings.Cut(env, "=")
_ = os.Unsetenv(k)
}
}
}
appWorkPathBuiltin = giteaRoot
AppWorkPath = giteaRoot
AppPath = filepath.Join(giteaRoot, "gitea") + util.Iif(IsWindows, ".exe", "")
StaticRootPath = giteaRoot // need to load assets (options, public) from the source code directory for testing
initWorkPathAndConfig := func() {
// init paths and config system for testing
getTestEnv := func(key string) string { return "" }
InitWorkPathAndCommonConfig(getTestEnv, ArgWorkPathAndCustomConf{CustomConf: CustomConf})
// giteaConf (GITEA_CONF) must be relative because it is used in the git hooks as "$GITEA_ROOT/$GITEA_CONF"
giteaConf := os.Getenv("GITEA_TEST_CONF")
if giteaConf == "" {
// By default, use sqlite.ini for testing, then IDE like GoLand can start the test process with debugger.
// It's easier for developers to debug bugs step by step with a debugger.
// Notice: when doing "ssh push", Gitea executes sub processes, debugger won't work for the sub processes.
giteaConf = "tests/sqlite.ini"
_, _ = fmt.Fprintf(os.Stderr, "Environment variable GITEA_TEST_CONF not set - defaulting to %s\n", giteaConf)
if !EnableSQLite3 {
_, _ = fmt.Fprintf(os.Stderr, "sqlite3 requires: -tags sqlite,sqlite_unlock_notify\n")
os.Exit(1)
if err := PrepareAppDataPath(); err != nil {
log.Fatal("Can not prepare APP_DATA_PATH: %v", err)
}
// register the dummy hash algorithm function used in the test fixtures
_ = hash.Register("dummy", hash.NewDummyHasher)
PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy")
}
// CustomConf must be absolute path to make tests pass,
CustomConf = filepath.Join(AppWorkPath, giteaConf)
// also unset unnecessary env vars for testing (only keep "GITEA_TEST_*" ones)
UnsetUnnecessaryEnvVars()
for _, env := range os.Environ() {
if strings.HasPrefix(env, "GIT_") || (strings.HasPrefix(env, "GITEA_") && !strings.HasPrefix(env, "GITEA_TEST_")) {
k, _, _ := strings.Cut(env, "=")
_ = os.Unsetenv(k)
}
initGiteaPaths()
giteaConf := initGiteaConf()
cleanUpEnv()
initWorkPathAndConfig()
if RepoRootPath == "" || AppDataPath == "" {
panic("SetupGiteaTestEnv failed, paths are not initialized")
}
// TODO: some git repo hooks (test fixtures) still use these env variables, need to be refactored in the future
_ = os.Setenv("GITEA_ROOT", giteaRoot)
_ = os.Setenv("GITEA_CONF", giteaConf) // test fixture git hooks use "$GITEA_ROOT/$GITEA_CONF" in their scripts
giteaTestSourceRoot = &giteaRoot
}
func PrepareIntegrationTestConfig() error {
giteaTestRoot := detectGiteaTestRoot()
isInCI := os.Getenv("CI") != ""
testDatabase := os.Getenv("GITEA_TEST_DATABASE")
if testDatabase == "" {
if isInCI {
return errors.New("GITEA_TEST_DATABASE environment variable not set")
}
// for local development, default to sqlite. CI needs to explicitly set a database to avoid unexpected results
testDatabase = "sqlite"
_, _ = fmt.Fprintf(os.Stderr, "Environment variable GITEA_TEST_DATABASE not set - defaulting to %s\n", testDatabase)
}
_ = os.Setenv("GITEA_TEST_ROOT", giteaTestRoot)
_ = os.Setenv("GITEA_TEST_CONF", filepath.Join("tests", testDatabase+".ini"))
workPath := filepath.Join(giteaTestRoot, "tests/integration/gitea-integration-"+testDatabase)
if err := os.MkdirAll(workPath, 0o755); err != nil {
return err
}
confFile := filepath.Join(giteaTestRoot, "tests", testDatabase+".ini")
tmplBuf, err := os.ReadFile(confFile + ".tmpl")
if err != nil {
return err
}
tmpl := string(tmplBuf)
envVars, err := shellquote.Split(os.Getenv("MAKEFILE_VARS"))
if err != nil {
return err
}
envVarMap := map[string]string{
"TEST_WORK_PATH": workPath,
"TEST_LOGGER": "test,file",
}
for _, env := range append(os.Environ(), envVars...) {
k, v, _ := strings.Cut(env, "=")
k = strings.TrimSpace(k)
v = strings.TrimSpace(v)
envVarMap[k] = v
}
for k, v := range envVarMap {
tmpl = strings.ReplaceAll(tmpl, fmt.Sprintf("{{%s}}", k), v)
}
err = os.WriteFile(confFile, []byte(tmpl), 0o644)
return err
}
+1 -1
View File
@@ -6,7 +6,7 @@ package setting
import (
"time"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/log"
)
// DefaultUILocation is the location on the UI, so that we can display the time on UI.
+2 -2
View File
@@ -6,8 +6,8 @@ package setting
import (
"time"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/container"
"gitea.dev/modules/log"
)
// UI settings
@@ -6,7 +6,7 @@ package setting
import (
"net/url"
"code.gitea.io/gitea/modules/log"
"gitea.dev/modules/log"
)
// Webhook settings
@@ -34,7 +34,10 @@ func loadWebhookFrom(rootCfg ConfigProvider) {
Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000)
Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
Webhook.AllowedHostList = sec.Key("ALLOWED_HOST_LIST").MustString("")
deprecatedSetting(rootCfg, "webhook", "ALLOWED_HOST_LIST", "security", "ALLOWED_HOST_LIST", "v28.0.0")
Webhook.AllowedHostList = sec.Key("ALLOWED_HOST_LIST").MustString(Security.AllowedHostList)
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams", "feishu", "matrix", "wechatwork", "packagist"}
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
Webhook.ProxyURL = sec.Key("PROXY_URL").MustString("")