This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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("")
|
||||
|
||||
Reference in New Issue
Block a user