mirror of
https://github.com/go-gitea/gitea
synced 2024-12-22 09:37:53 +01:00
Merge branch 'main' into strictFunctionTypes
This commit is contained in:
commit
853f50bd76
8
Makefile
8
Makefile
@ -26,17 +26,17 @@ COMMA := ,
|
|||||||
XGO_VERSION := go-1.23.x
|
XGO_VERSION := go-1.23.x
|
||||||
|
|
||||||
AIR_PACKAGE ?= github.com/air-verse/air@v1
|
AIR_PACKAGE ?= github.com/air-verse/air@v1
|
||||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0
|
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.0.3
|
||||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0
|
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0
|
||||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2
|
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2
|
||||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
|
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.12
|
||||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.5.1
|
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.6.0
|
||||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0
|
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0
|
||||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||||
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
|
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
|
||||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
|
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
|
||||||
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1
|
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1
|
||||||
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.15.3
|
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.17.0
|
||||||
|
|
||||||
DOCKER_IMAGE ?= gitea/gitea
|
DOCKER_IMAGE ?= gitea/gitea
|
||||||
DOCKER_TAG ?= latest
|
DOCKER_TAG ?= latest
|
||||||
|
@ -13,6 +13,8 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"xorm.io/xorm"
|
||||||
"xorm.io/xorm/schemas"
|
"xorm.io/xorm/schemas"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -54,7 +56,8 @@ func TestDumpAuthSource(t *testing.T) {
|
|||||||
|
|
||||||
sb := new(strings.Builder)
|
sb := new(strings.Builder)
|
||||||
|
|
||||||
db.DumpTables([]*schemas.Table{authSourceSchema}, sb)
|
// TODO: this test is quite hacky, it should use a low-level "select" (without model processors) but not a database dump
|
||||||
|
engine := db.GetEngine(db.DefaultContext).(*xorm.Engine)
|
||||||
|
require.NoError(t, engine.DumpTables([]*schemas.Table{authSourceSchema}, sb))
|
||||||
assert.Contains(t, sb.String(), `"Provider":"ConvertibleSourceName"`)
|
assert.Contains(t, sb.String(), `"Provider":"ConvertibleSourceName"`)
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ func CheckCollations(x *xorm.Engine) (*CheckCollationsResult, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CheckCollationsDefaultEngine() (*CheckCollationsResult, error) {
|
func CheckCollationsDefaultEngine() (*CheckCollationsResult, error) {
|
||||||
return CheckCollations(x)
|
return CheckCollations(xormEngine)
|
||||||
}
|
}
|
||||||
|
|
||||||
func alterDatabaseCollation(x *xorm.Engine, collation string) error {
|
func alterDatabaseCollation(x *xorm.Engine, collation string) error {
|
||||||
|
@ -94,7 +94,7 @@ func GetEngine(ctx context.Context) Engine {
|
|||||||
if e := getExistingEngine(ctx); e != nil {
|
if e := getExistingEngine(ctx); e != nil {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
return x.Context(ctx)
|
return xormEngine.Context(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getExistingEngine gets an existing db Engine/Statement from this context or returns nil
|
// getExistingEngine gets an existing db Engine/Statement from this context or returns nil
|
||||||
@ -155,7 +155,7 @@ func TxContext(parentCtx context.Context) (*Context, Committer, error) {
|
|||||||
return newContext(parentCtx, sess), &halfCommitter{committer: sess}, nil
|
return newContext(parentCtx, sess), &halfCommitter{committer: sess}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
sess := xormEngine.NewSession()
|
||||||
if err := sess.Begin(); err != nil {
|
if err := sess.Begin(); err != nil {
|
||||||
_ = sess.Close()
|
_ = sess.Close()
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@ -179,7 +179,7 @@ func WithTx(parentCtx context.Context, f func(ctx context.Context) error) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func txWithNoCheck(parentCtx context.Context, f func(ctx context.Context) error) error {
|
func txWithNoCheck(parentCtx context.Context, f func(ctx context.Context) error) error {
|
||||||
sess := x.NewSession()
|
sess := xormEngine.NewSession()
|
||||||
defer sess.Close()
|
defer sess.Close()
|
||||||
if err := sess.Begin(); err != nil {
|
if err := sess.Begin(); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -322,7 +322,7 @@ func CountByBean(ctx context.Context, bean any) (int64, error) {
|
|||||||
|
|
||||||
// TableName returns the table name according a bean object
|
// TableName returns the table name according a bean object
|
||||||
func TableName(bean any) string {
|
func TableName(bean any) string {
|
||||||
return x.TableName(bean)
|
return xormEngine.TableName(bean)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InTransaction returns true if the engine is in a transaction otherwise return false
|
// InTransaction returns true if the engine is in a transaction otherwise return false
|
||||||
|
@ -16,30 +16,30 @@ import (
|
|||||||
|
|
||||||
// ConvertDatabaseTable converts database and tables from utf8 to utf8mb4 if it's mysql and set ROW_FORMAT=dynamic
|
// ConvertDatabaseTable converts database and tables from utf8 to utf8mb4 if it's mysql and set ROW_FORMAT=dynamic
|
||||||
func ConvertDatabaseTable() error {
|
func ConvertDatabaseTable() error {
|
||||||
if x.Dialect().URI().DBType != schemas.MYSQL {
|
if xormEngine.Dialect().URI().DBType != schemas.MYSQL {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := CheckCollations(x)
|
r, err := CheckCollations(xormEngine)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = x.Exec(fmt.Sprintf("ALTER DATABASE `%s` CHARACTER SET utf8mb4 COLLATE %s", setting.Database.Name, r.ExpectedCollation))
|
_, err = xormEngine.Exec(fmt.Sprintf("ALTER DATABASE `%s` CHARACTER SET utf8mb4 COLLATE %s", setting.Database.Name, r.ExpectedCollation))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tables, err := x.DBMetas()
|
tables, err := xormEngine.DBMetas()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, table := range tables {
|
for _, table := range tables {
|
||||||
if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` ROW_FORMAT=dynamic", table.Name)); err != nil {
|
if _, err := xormEngine.Exec(fmt.Sprintf("ALTER TABLE `%s` ROW_FORMAT=dynamic", table.Name)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` CONVERT TO CHARACTER SET utf8mb4 COLLATE %s", table.Name, r.ExpectedCollation)); err != nil {
|
if _, err := xormEngine.Exec(fmt.Sprintf("ALTER TABLE `%s` CONVERT TO CHARACTER SET utf8mb4 COLLATE %s", table.Name, r.ExpectedCollation)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,11 +49,11 @@ func ConvertDatabaseTable() error {
|
|||||||
|
|
||||||
// ConvertVarcharToNVarchar converts database and tables from varchar to nvarchar if it's mssql
|
// ConvertVarcharToNVarchar converts database and tables from varchar to nvarchar if it's mssql
|
||||||
func ConvertVarcharToNVarchar() error {
|
func ConvertVarcharToNVarchar() error {
|
||||||
if x.Dialect().URI().DBType != schemas.MSSQL {
|
if xormEngine.Dialect().URI().DBType != schemas.MSSQL {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
sess := xormEngine.NewSession()
|
||||||
defer sess.Close()
|
defer sess.Close()
|
||||||
res, err := sess.QuerySliceString(`SELECT 'ALTER TABLE ' + OBJECT_NAME(SC.object_id) + ' MODIFY SC.name NVARCHAR(' + CONVERT(VARCHAR(5),SC.max_length) + ')'
|
res, err := sess.QuerySliceString(`SELECT 'ALTER TABLE ' + OBJECT_NAME(SC.object_id) + ' MODIFY SC.name NVARCHAR(' + CONVERT(VARCHAR(5),SC.max_length) + ')'
|
||||||
FROM SYS.columns SC
|
FROM SYS.columns SC
|
||||||
|
@ -8,17 +8,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
|
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
"xorm.io/xorm/contexts"
|
|
||||||
"xorm.io/xorm/names"
|
|
||||||
"xorm.io/xorm/schemas"
|
"xorm.io/xorm/schemas"
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql" // Needed for the MySQL driver
|
_ "github.com/go-sql-driver/mysql" // Needed for the MySQL driver
|
||||||
@ -27,9 +20,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
x *xorm.Engine
|
xormEngine *xorm.Engine
|
||||||
tables []any
|
registeredModels []any
|
||||||
initFuncs []func() error
|
registeredInitFuncs []func() error
|
||||||
)
|
)
|
||||||
|
|
||||||
// Engine represents a xorm engine or session.
|
// Engine represents a xorm engine or session.
|
||||||
@ -70,167 +63,38 @@ type Engine interface {
|
|||||||
|
|
||||||
// TableInfo returns table's information via an object
|
// TableInfo returns table's information via an object
|
||||||
func TableInfo(v any) (*schemas.Table, error) {
|
func TableInfo(v any) (*schemas.Table, error) {
|
||||||
return x.TableInfo(v)
|
return xormEngine.TableInfo(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DumpTables dump tables information
|
// RegisterModel registers model, if initFuncs provided, it will be invoked after data model sync
|
||||||
func DumpTables(tables []*schemas.Table, w io.Writer, tp ...schemas.DBType) error {
|
|
||||||
return x.DumpTables(tables, w, tp...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterModel registers model, if initfunc provided, it will be invoked after data model sync
|
|
||||||
func RegisterModel(bean any, initFunc ...func() error) {
|
func RegisterModel(bean any, initFunc ...func() error) {
|
||||||
tables = append(tables, bean)
|
registeredModels = append(registeredModels, bean)
|
||||||
if len(initFuncs) > 0 && initFunc[0] != nil {
|
if len(registeredInitFuncs) > 0 && initFunc[0] != nil {
|
||||||
initFuncs = append(initFuncs, initFunc[0])
|
registeredInitFuncs = append(registeredInitFuncs, initFunc[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
|
||||||
gonicNames := []string{"SSL", "UID"}
|
|
||||||
for _, name := range gonicNames {
|
|
||||||
names.LintGonicMapper[name] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// newXORMEngine returns a new XORM engine from the configuration
|
|
||||||
func newXORMEngine() (*xorm.Engine, error) {
|
|
||||||
connStr, err := setting.DBConnStr()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var engine *xorm.Engine
|
|
||||||
|
|
||||||
if setting.Database.Type.IsPostgreSQL() && len(setting.Database.Schema) > 0 {
|
|
||||||
// OK whilst we sort out our schema issues - create a schema aware postgres
|
|
||||||
registerPostgresSchemaDriver()
|
|
||||||
engine, err = xorm.NewEngine("postgresschema", connStr)
|
|
||||||
} else {
|
|
||||||
engine, err = xorm.NewEngine(setting.Database.Type.String(), connStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if setting.Database.Type == "mysql" {
|
|
||||||
engine.Dialect().SetParams(map[string]string{"rowFormat": "DYNAMIC"})
|
|
||||||
} else if setting.Database.Type == "mssql" {
|
|
||||||
engine.Dialect().SetParams(map[string]string{"DEFAULT_VARCHAR": "nvarchar"})
|
|
||||||
}
|
|
||||||
engine.SetSchema(setting.Database.Schema)
|
|
||||||
return engine, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SyncAllTables sync the schemas of all tables, is required by unit test code
|
// SyncAllTables sync the schemas of all tables, is required by unit test code
|
||||||
func SyncAllTables() error {
|
func SyncAllTables() error {
|
||||||
_, err := x.StoreEngine("InnoDB").SyncWithOptions(xorm.SyncOptions{
|
_, err := xormEngine.StoreEngine("InnoDB").SyncWithOptions(xorm.SyncOptions{
|
||||||
WarnIfDatabaseColumnMissed: true,
|
WarnIfDatabaseColumnMissed: true,
|
||||||
}, tables...)
|
}, registeredModels...)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitEngine initializes the xorm.Engine and sets it as db.DefaultContext
|
|
||||||
func InitEngine(ctx context.Context) error {
|
|
||||||
xormEngine, err := newXORMEngine()
|
|
||||||
if err != nil {
|
|
||||||
if strings.Contains(err.Error(), "SQLite3 support") {
|
|
||||||
return fmt.Errorf(`sqlite3 requires: -tags sqlite,sqlite_unlock_notify%s%w`, "\n", err)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("failed to connect to database: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
xormEngine.SetMapper(names.GonicMapper{})
|
|
||||||
// WARNING: for serv command, MUST remove the output to os.stdout,
|
|
||||||
// so use log file to instead print to stdout.
|
|
||||||
xormEngine.SetLogger(NewXORMLogger(setting.Database.LogSQL))
|
|
||||||
xormEngine.ShowSQL(setting.Database.LogSQL)
|
|
||||||
xormEngine.SetMaxOpenConns(setting.Database.MaxOpenConns)
|
|
||||||
xormEngine.SetMaxIdleConns(setting.Database.MaxIdleConns)
|
|
||||||
xormEngine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime)
|
|
||||||
xormEngine.SetDefaultContext(ctx)
|
|
||||||
|
|
||||||
if setting.Database.SlowQueryThreshold > 0 {
|
|
||||||
xormEngine.AddHook(&SlowQueryHook{
|
|
||||||
Threshold: setting.Database.SlowQueryThreshold,
|
|
||||||
Logger: log.GetLogger("xorm"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
SetDefaultEngine(ctx, xormEngine)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDefaultEngine sets the default engine for db
|
|
||||||
func SetDefaultEngine(ctx context.Context, eng *xorm.Engine) {
|
|
||||||
x = eng
|
|
||||||
DefaultContext = &Context{Context: ctx, engine: x}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnsetDefaultEngine closes and unsets the default engine
|
|
||||||
// We hope the SetDefaultEngine and UnsetDefaultEngine can be paired, but it's impossible now,
|
|
||||||
// there are many calls to InitEngine -> SetDefaultEngine directly to overwrite the `x` and DefaultContext without close
|
|
||||||
// Global database engine related functions are all racy and there is no graceful close right now.
|
|
||||||
func UnsetDefaultEngine() {
|
|
||||||
if x != nil {
|
|
||||||
_ = x.Close()
|
|
||||||
x = nil
|
|
||||||
}
|
|
||||||
DefaultContext = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitEngineWithMigration initializes a new xorm.Engine and sets it as the db.DefaultContext
|
|
||||||
// This function must never call .Sync() if the provided migration function fails.
|
|
||||||
// When called from the "doctor" command, the migration function is a version check
|
|
||||||
// that prevents the doctor from fixing anything in the database if the migration level
|
|
||||||
// is different from the expected value.
|
|
||||||
func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err error) {
|
|
||||||
if err = InitEngine(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = x.Ping(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
preprocessDatabaseCollation(x)
|
|
||||||
|
|
||||||
// We have to run migrateFunc here in case the user is re-running installation on a previously created DB.
|
|
||||||
// If we do not then table schemas will be changed and there will be conflicts when the migrations run properly.
|
|
||||||
//
|
|
||||||
// Installation should only be being re-run if users want to recover an old database.
|
|
||||||
// However, we should think carefully about should we support re-install on an installed instance,
|
|
||||||
// as there may be other problems due to secret reinitialization.
|
|
||||||
if err = migrateFunc(x); err != nil {
|
|
||||||
return fmt.Errorf("migrate: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = SyncAllTables(); err != nil {
|
|
||||||
return fmt.Errorf("sync database struct error: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, initFunc := range initFuncs {
|
|
||||||
if err := initFunc(); err != nil {
|
|
||||||
return fmt.Errorf("initFunc failed: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NamesToBean return a list of beans or an error
|
// NamesToBean return a list of beans or an error
|
||||||
func NamesToBean(names ...string) ([]any, error) {
|
func NamesToBean(names ...string) ([]any, error) {
|
||||||
beans := []any{}
|
beans := []any{}
|
||||||
if len(names) == 0 {
|
if len(names) == 0 {
|
||||||
beans = append(beans, tables...)
|
beans = append(beans, registeredModels...)
|
||||||
return beans, nil
|
return beans, nil
|
||||||
}
|
}
|
||||||
// Need to map provided names to beans...
|
// Need to map provided names to beans...
|
||||||
beanMap := make(map[string]any)
|
beanMap := make(map[string]any)
|
||||||
for _, bean := range tables {
|
for _, bean := range registeredModels {
|
||||||
beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean
|
beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean
|
||||||
beanMap[strings.ToLower(x.TableName(bean))] = bean
|
beanMap[strings.ToLower(xormEngine.TableName(bean))] = bean
|
||||||
beanMap[strings.ToLower(x.TableName(bean, true))] = bean
|
beanMap[strings.ToLower(xormEngine.TableName(bean, true))] = bean
|
||||||
}
|
}
|
||||||
|
|
||||||
gotBean := make(map[any]bool)
|
gotBean := make(map[any]bool)
|
||||||
@ -247,36 +111,9 @@ func NamesToBean(names ...string) ([]any, error) {
|
|||||||
return beans, nil
|
return beans, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DumpDatabase dumps all data from database according the special database SQL syntax to file system.
|
|
||||||
func DumpDatabase(filePath, dbType string) error {
|
|
||||||
var tbs []*schemas.Table
|
|
||||||
for _, t := range tables {
|
|
||||||
t, err := x.TableInfo(t)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tbs = append(tbs, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Version struct {
|
|
||||||
ID int64 `xorm:"pk autoincr"`
|
|
||||||
Version int64
|
|
||||||
}
|
|
||||||
t, err := x.TableInfo(&Version{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tbs = append(tbs, t)
|
|
||||||
|
|
||||||
if len(dbType) > 0 {
|
|
||||||
return x.DumpTablesToFile(tbs, filePath, schemas.DBType(dbType))
|
|
||||||
}
|
|
||||||
return x.DumpTablesToFile(tbs, filePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MaxBatchInsertSize returns the table's max batch insert size
|
// MaxBatchInsertSize returns the table's max batch insert size
|
||||||
func MaxBatchInsertSize(bean any) int {
|
func MaxBatchInsertSize(bean any) int {
|
||||||
t, err := x.TableInfo(bean)
|
t, err := xormEngine.TableInfo(bean)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 50
|
return 50
|
||||||
}
|
}
|
||||||
@ -285,18 +122,18 @@ func MaxBatchInsertSize(bean any) int {
|
|||||||
|
|
||||||
// IsTableNotEmpty returns true if table has at least one record
|
// IsTableNotEmpty returns true if table has at least one record
|
||||||
func IsTableNotEmpty(beanOrTableName any) (bool, error) {
|
func IsTableNotEmpty(beanOrTableName any) (bool, error) {
|
||||||
return x.Table(beanOrTableName).Exist()
|
return xormEngine.Table(beanOrTableName).Exist()
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAllRecords will delete all the records of this table
|
// DeleteAllRecords will delete all the records of this table
|
||||||
func DeleteAllRecords(tableName string) error {
|
func DeleteAllRecords(tableName string) error {
|
||||||
_, err := x.Exec(fmt.Sprintf("DELETE FROM %s", tableName))
|
_, err := xormEngine.Exec(fmt.Sprintf("DELETE FROM %s", tableName))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMaxID will return max id of the table
|
// GetMaxID will return max id of the table
|
||||||
func GetMaxID(beanOrTableName any) (maxID int64, err error) {
|
func GetMaxID(beanOrTableName any) (maxID int64, err error) {
|
||||||
_, err = x.Select("MAX(id)").Table(beanOrTableName).Get(&maxID)
|
_, err = xormEngine.Select("MAX(id)").Table(beanOrTableName).Get(&maxID)
|
||||||
return maxID, err
|
return maxID, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,24 +145,3 @@ func SetLogSQL(ctx context.Context, on bool) {
|
|||||||
sess.Engine().ShowSQL(on)
|
sess.Engine().ShowSQL(on)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SlowQueryHook struct {
|
|
||||||
Threshold time.Duration
|
|
||||||
Logger log.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ contexts.Hook = &SlowQueryHook{}
|
|
||||||
|
|
||||||
func (SlowQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
|
|
||||||
return c.Ctx, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error {
|
|
||||||
if c.ExecuteTime >= h.Threshold {
|
|
||||||
// 8 is the amount of skips passed to runtime.Caller, so that in the log the correct function
|
|
||||||
// is being displayed (the function that ultimately wants to execute the query in the code)
|
|
||||||
// instead of the function of the slow query hook being called.
|
|
||||||
h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
33
models/db/engine_dump.go
Normal file
33
models/db/engine_dump.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package db
|
||||||
|
|
||||||
|
import "xorm.io/xorm/schemas"
|
||||||
|
|
||||||
|
// DumpDatabase dumps all data from database according the special database SQL syntax to file system.
|
||||||
|
func DumpDatabase(filePath, dbType string) error {
|
||||||
|
var tbs []*schemas.Table
|
||||||
|
for _, t := range registeredModels {
|
||||||
|
t, err := xormEngine.TableInfo(t)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tbs = append(tbs, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Version struct {
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
Version int64
|
||||||
|
}
|
||||||
|
t, err := xormEngine.TableInfo(&Version{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tbs = append(tbs, t)
|
||||||
|
|
||||||
|
if dbType != "" {
|
||||||
|
return xormEngine.DumpTablesToFile(tbs, filePath, schemas.DBType(dbType))
|
||||||
|
}
|
||||||
|
return xormEngine.DumpTablesToFile(tbs, filePath)
|
||||||
|
}
|
34
models/db/engine_hook.go
Normal file
34
models/db/engine_hook.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
|
"xorm.io/xorm/contexts"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SlowQueryHook struct {
|
||||||
|
Threshold time.Duration
|
||||||
|
Logger log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ contexts.Hook = (*SlowQueryHook)(nil)
|
||||||
|
|
||||||
|
func (*SlowQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
|
||||||
|
return c.Ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error {
|
||||||
|
if c.ExecuteTime >= h.Threshold {
|
||||||
|
// 8 is the amount of skips passed to runtime.Caller, so that in the log the correct function
|
||||||
|
// is being displayed (the function that ultimately wants to execute the query in the code)
|
||||||
|
// instead of the function of the slow query hook being called.
|
||||||
|
h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
140
models/db/engine_init.go
Normal file
140
models/db/engine_init.go
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
"xorm.io/xorm"
|
||||||
|
"xorm.io/xorm/names"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
gonicNames := []string{"SSL", "UID"}
|
||||||
|
for _, name := range gonicNames {
|
||||||
|
names.LintGonicMapper[name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newXORMEngine returns a new XORM engine from the configuration
|
||||||
|
func newXORMEngine() (*xorm.Engine, error) {
|
||||||
|
connStr, err := setting.DBConnStr()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var engine *xorm.Engine
|
||||||
|
|
||||||
|
if setting.Database.Type.IsPostgreSQL() && len(setting.Database.Schema) > 0 {
|
||||||
|
// OK whilst we sort out our schema issues - create a schema aware postgres
|
||||||
|
registerPostgresSchemaDriver()
|
||||||
|
engine, err = xorm.NewEngine("postgresschema", connStr)
|
||||||
|
} else {
|
||||||
|
engine, err = xorm.NewEngine(setting.Database.Type.String(), connStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if setting.Database.Type == "mysql" {
|
||||||
|
engine.Dialect().SetParams(map[string]string{"rowFormat": "DYNAMIC"})
|
||||||
|
} else if setting.Database.Type == "mssql" {
|
||||||
|
engine.Dialect().SetParams(map[string]string{"DEFAULT_VARCHAR": "nvarchar"})
|
||||||
|
}
|
||||||
|
engine.SetSchema(setting.Database.Schema)
|
||||||
|
return engine, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitEngine initializes the xorm.Engine and sets it as db.DefaultContext
|
||||||
|
func InitEngine(ctx context.Context) error {
|
||||||
|
xe, err := newXORMEngine()
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "SQLite3 support") {
|
||||||
|
return fmt.Errorf(`sqlite3 requires: -tags sqlite,sqlite_unlock_notify%s%w`, "\n", err)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("failed to connect to database: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
xe.SetMapper(names.GonicMapper{})
|
||||||
|
// WARNING: for serv command, MUST remove the output to os.stdout,
|
||||||
|
// so use log file to instead print to stdout.
|
||||||
|
xe.SetLogger(NewXORMLogger(setting.Database.LogSQL))
|
||||||
|
xe.ShowSQL(setting.Database.LogSQL)
|
||||||
|
xe.SetMaxOpenConns(setting.Database.MaxOpenConns)
|
||||||
|
xe.SetMaxIdleConns(setting.Database.MaxIdleConns)
|
||||||
|
xe.SetConnMaxLifetime(setting.Database.ConnMaxLifetime)
|
||||||
|
xe.SetDefaultContext(ctx)
|
||||||
|
|
||||||
|
if setting.Database.SlowQueryThreshold > 0 {
|
||||||
|
xe.AddHook(&SlowQueryHook{
|
||||||
|
Threshold: setting.Database.SlowQueryThreshold,
|
||||||
|
Logger: log.GetLogger("xorm"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
SetDefaultEngine(ctx, xe)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefaultEngine sets the default engine for db
|
||||||
|
func SetDefaultEngine(ctx context.Context, eng *xorm.Engine) {
|
||||||
|
xormEngine = eng
|
||||||
|
DefaultContext = &Context{Context: ctx, engine: xormEngine}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsetDefaultEngine closes and unsets the default engine
|
||||||
|
// We hope the SetDefaultEngine and UnsetDefaultEngine can be paired, but it's impossible now,
|
||||||
|
// there are many calls to InitEngine -> SetDefaultEngine directly to overwrite the `xormEngine` and DefaultContext without close
|
||||||
|
// Global database engine related functions are all racy and there is no graceful close right now.
|
||||||
|
func UnsetDefaultEngine() {
|
||||||
|
if xormEngine != nil {
|
||||||
|
_ = xormEngine.Close()
|
||||||
|
xormEngine = nil
|
||||||
|
}
|
||||||
|
DefaultContext = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitEngineWithMigration initializes a new xorm.Engine and sets it as the db.DefaultContext
|
||||||
|
// This function must never call .Sync() if the provided migration function fails.
|
||||||
|
// When called from the "doctor" command, the migration function is a version check
|
||||||
|
// that prevents the doctor from fixing anything in the database if the migration level
|
||||||
|
// is different from the expected value.
|
||||||
|
func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err error) {
|
||||||
|
if err = InitEngine(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = xormEngine.Ping(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
preprocessDatabaseCollation(xormEngine)
|
||||||
|
|
||||||
|
// We have to run migrateFunc here in case the user is re-running installation on a previously created DB.
|
||||||
|
// If we do not then table schemas will be changed and there will be conflicts when the migrations run properly.
|
||||||
|
//
|
||||||
|
// Installation should only be being re-run if users want to recover an old database.
|
||||||
|
// However, we should think carefully about should we support re-install on an installed instance,
|
||||||
|
// as there may be other problems due to secret reinitialization.
|
||||||
|
if err = migrateFunc(xormEngine); err != nil {
|
||||||
|
return fmt.Errorf("migrate: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = SyncAllTables(); err != nil {
|
||||||
|
return fmt.Errorf("sync database struct error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, initFunc := range registeredInitFuncs {
|
||||||
|
if err := initFunc(); err != nil {
|
||||||
|
return fmt.Errorf("initFunc failed: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -16,7 +16,7 @@ var (
|
|||||||
// ErrNameEmpty name is empty error
|
// ErrNameEmpty name is empty error
|
||||||
ErrNameEmpty = util.SilentWrap{Message: "name is empty", Err: util.ErrInvalidArgument}
|
ErrNameEmpty = util.SilentWrap{Message: "name is empty", Err: util.ErrInvalidArgument}
|
||||||
|
|
||||||
// AlphaDashDotPattern characters prohibited in a user name (anything except A-Za-z0-9_.-)
|
// AlphaDashDotPattern characters prohibited in a username (anything except A-Za-z0-9_.-)
|
||||||
AlphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
AlphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,11 +17,11 @@ func CountBadSequences(_ context.Context) (int64, error) {
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
sess := xormEngine.NewSession()
|
||||||
defer sess.Close()
|
defer sess.Close()
|
||||||
|
|
||||||
var sequences []string
|
var sequences []string
|
||||||
schema := x.Dialect().URI().Schema
|
schema := xormEngine.Dialect().URI().Schema
|
||||||
|
|
||||||
sess.Engine().SetSchema("")
|
sess.Engine().SetSchema("")
|
||||||
if err := sess.Table("information_schema.sequences").Cols("sequence_name").Where("sequence_name LIKE 'tmp_recreate__%_id_seq%' AND sequence_catalog = ?", setting.Database.Name).Find(&sequences); err != nil {
|
if err := sess.Table("information_schema.sequences").Cols("sequence_name").Where("sequence_name LIKE 'tmp_recreate__%_id_seq%' AND sequence_catalog = ?", setting.Database.Name).Find(&sequences); err != nil {
|
||||||
@ -38,7 +38,7 @@ func FixBadSequences(_ context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
sess := xormEngine.NewSession()
|
||||||
defer sess.Close()
|
defer sess.Close()
|
||||||
if err := sess.Begin(); err != nil {
|
if err := sess.Begin(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
510
models/error.go
510
models/error.go
@ -1,510 +0,0 @@
|
|||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
|
||||||
"code.gitea.io/gitea/modules/git"
|
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrUserOwnRepos represents a "UserOwnRepos" kind of error.
|
|
||||||
type ErrUserOwnRepos struct {
|
|
||||||
UID int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrUserOwnRepos checks if an error is a ErrUserOwnRepos.
|
|
||||||
func IsErrUserOwnRepos(err error) bool {
|
|
||||||
_, ok := err.(ErrUserOwnRepos)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrUserOwnRepos) Error() string {
|
|
||||||
return fmt.Sprintf("user still has ownership of repositories [uid: %d]", err.UID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrUserHasOrgs represents a "UserHasOrgs" kind of error.
|
|
||||||
type ErrUserHasOrgs struct {
|
|
||||||
UID int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrUserHasOrgs checks if an error is a ErrUserHasOrgs.
|
|
||||||
func IsErrUserHasOrgs(err error) bool {
|
|
||||||
_, ok := err.(ErrUserHasOrgs)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrUserHasOrgs) Error() string {
|
|
||||||
return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrUserOwnPackages notifies that the user (still) owns the packages.
|
|
||||||
type ErrUserOwnPackages struct {
|
|
||||||
UID int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrUserOwnPackages checks if an error is an ErrUserOwnPackages.
|
|
||||||
func IsErrUserOwnPackages(err error) bool {
|
|
||||||
_, ok := err.(ErrUserOwnPackages)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrUserOwnPackages) Error() string {
|
|
||||||
return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrDeleteLastAdminUser represents a "DeleteLastAdminUser" kind of error.
|
|
||||||
type ErrDeleteLastAdminUser struct {
|
|
||||||
UID int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrDeleteLastAdminUser checks if an error is a ErrDeleteLastAdminUser.
|
|
||||||
func IsErrDeleteLastAdminUser(err error) bool {
|
|
||||||
_, ok := err.(ErrDeleteLastAdminUser)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrDeleteLastAdminUser) Error() string {
|
|
||||||
return fmt.Sprintf("can not delete the last admin user [uid: %d]", err.UID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
|
|
||||||
type ErrInvalidCloneAddr struct {
|
|
||||||
Host string
|
|
||||||
IsURLError bool
|
|
||||||
IsInvalidPath bool
|
|
||||||
IsProtocolInvalid bool
|
|
||||||
IsPermissionDenied bool
|
|
||||||
LocalPath bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrInvalidCloneAddr checks if an error is a ErrInvalidCloneAddr.
|
|
||||||
func IsErrInvalidCloneAddr(err error) bool {
|
|
||||||
_, ok := err.(*ErrInvalidCloneAddr)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err *ErrInvalidCloneAddr) Error() string {
|
|
||||||
if err.IsInvalidPath {
|
|
||||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided path is invalid", err.Host)
|
|
||||||
}
|
|
||||||
if err.IsProtocolInvalid {
|
|
||||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url protocol is not allowed", err.Host)
|
|
||||||
}
|
|
||||||
if err.IsPermissionDenied {
|
|
||||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed.", err.Host)
|
|
||||||
}
|
|
||||||
if err.IsURLError {
|
|
||||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url is invalid", err.Host)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed", err.Host)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err *ErrInvalidCloneAddr) Unwrap() error {
|
|
||||||
return util.ErrInvalidArgument
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrUpdateTaskNotExist represents a "UpdateTaskNotExist" kind of error.
|
|
||||||
type ErrUpdateTaskNotExist struct {
|
|
||||||
UUID string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrUpdateTaskNotExist checks if an error is a ErrUpdateTaskNotExist.
|
|
||||||
func IsErrUpdateTaskNotExist(err error) bool {
|
|
||||||
_, ok := err.(ErrUpdateTaskNotExist)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrUpdateTaskNotExist) Error() string {
|
|
||||||
return fmt.Sprintf("update task does not exist [uuid: %s]", err.UUID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrUpdateTaskNotExist) Unwrap() error {
|
|
||||||
return util.ErrNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrInvalidTagName represents a "InvalidTagName" kind of error.
|
|
||||||
type ErrInvalidTagName struct {
|
|
||||||
TagName string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrInvalidTagName checks if an error is a ErrInvalidTagName.
|
|
||||||
func IsErrInvalidTagName(err error) bool {
|
|
||||||
_, ok := err.(ErrInvalidTagName)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrInvalidTagName) Error() string {
|
|
||||||
return fmt.Sprintf("release tag name is not valid [tag_name: %s]", err.TagName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrInvalidTagName) Unwrap() error {
|
|
||||||
return util.ErrInvalidArgument
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrProtectedTagName represents a "ProtectedTagName" kind of error.
|
|
||||||
type ErrProtectedTagName struct {
|
|
||||||
TagName string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrProtectedTagName checks if an error is a ErrProtectedTagName.
|
|
||||||
func IsErrProtectedTagName(err error) bool {
|
|
||||||
_, ok := err.(ErrProtectedTagName)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrProtectedTagName) Error() string {
|
|
||||||
return fmt.Sprintf("release tag name is protected [tag_name: %s]", err.TagName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrProtectedTagName) Unwrap() error {
|
|
||||||
return util.ErrPermissionDenied
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrRepoFileAlreadyExists represents a "RepoFileAlreadyExist" kind of error.
|
|
||||||
type ErrRepoFileAlreadyExists struct {
|
|
||||||
Path string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrRepoFileAlreadyExists checks if an error is a ErrRepoFileAlreadyExists.
|
|
||||||
func IsErrRepoFileAlreadyExists(err error) bool {
|
|
||||||
_, ok := err.(ErrRepoFileAlreadyExists)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrRepoFileAlreadyExists) Error() string {
|
|
||||||
return fmt.Sprintf("repository file already exists [path: %s]", err.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrRepoFileAlreadyExists) Unwrap() error {
|
|
||||||
return util.ErrAlreadyExist
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrRepoFileDoesNotExist represents a "RepoFileDoesNotExist" kind of error.
|
|
||||||
type ErrRepoFileDoesNotExist struct {
|
|
||||||
Path string
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrRepoFileDoesNotExist checks if an error is a ErrRepoDoesNotExist.
|
|
||||||
func IsErrRepoFileDoesNotExist(err error) bool {
|
|
||||||
_, ok := err.(ErrRepoFileDoesNotExist)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrRepoFileDoesNotExist) Error() string {
|
|
||||||
return fmt.Sprintf("repository file does not exist [path: %s]", err.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrRepoFileDoesNotExist) Unwrap() error {
|
|
||||||
return util.ErrNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrFilenameInvalid represents a "FilenameInvalid" kind of error.
|
|
||||||
type ErrFilenameInvalid struct {
|
|
||||||
Path string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrFilenameInvalid checks if an error is an ErrFilenameInvalid.
|
|
||||||
func IsErrFilenameInvalid(err error) bool {
|
|
||||||
_, ok := err.(ErrFilenameInvalid)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrFilenameInvalid) Error() string {
|
|
||||||
return fmt.Sprintf("path contains a malformed path component [path: %s]", err.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrFilenameInvalid) Unwrap() error {
|
|
||||||
return util.ErrInvalidArgument
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrUserCannotCommit represents "UserCannotCommit" kind of error.
|
|
||||||
type ErrUserCannotCommit struct {
|
|
||||||
UserName string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrUserCannotCommit checks if an error is an ErrUserCannotCommit.
|
|
||||||
func IsErrUserCannotCommit(err error) bool {
|
|
||||||
_, ok := err.(ErrUserCannotCommit)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrUserCannotCommit) Error() string {
|
|
||||||
return fmt.Sprintf("user cannot commit to repo [user: %s]", err.UserName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrUserCannotCommit) Unwrap() error {
|
|
||||||
return util.ErrPermissionDenied
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrFilePathInvalid represents a "FilePathInvalid" kind of error.
|
|
||||||
type ErrFilePathInvalid struct {
|
|
||||||
Message string
|
|
||||||
Path string
|
|
||||||
Name string
|
|
||||||
Type git.EntryMode
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrFilePathInvalid checks if an error is an ErrFilePathInvalid.
|
|
||||||
func IsErrFilePathInvalid(err error) bool {
|
|
||||||
_, ok := err.(ErrFilePathInvalid)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrFilePathInvalid) Error() string {
|
|
||||||
if err.Message != "" {
|
|
||||||
return err.Message
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("path is invalid [path: %s]", err.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrFilePathInvalid) Unwrap() error {
|
|
||||||
return util.ErrInvalidArgument
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrFilePathProtected represents a "FilePathProtected" kind of error.
|
|
||||||
type ErrFilePathProtected struct {
|
|
||||||
Message string
|
|
||||||
Path string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrFilePathProtected checks if an error is an ErrFilePathProtected.
|
|
||||||
func IsErrFilePathProtected(err error) bool {
|
|
||||||
_, ok := err.(ErrFilePathProtected)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrFilePathProtected) Error() string {
|
|
||||||
if err.Message != "" {
|
|
||||||
return err.Message
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("path is protected and can not be changed [path: %s]", err.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrFilePathProtected) Unwrap() error {
|
|
||||||
return util.ErrPermissionDenied
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrDisallowedToMerge represents an error that a branch is protected and the current user is not allowed to modify it.
|
|
||||||
type ErrDisallowedToMerge struct {
|
|
||||||
Reason string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrDisallowedToMerge checks if an error is an ErrDisallowedToMerge.
|
|
||||||
func IsErrDisallowedToMerge(err error) bool {
|
|
||||||
_, ok := err.(ErrDisallowedToMerge)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrDisallowedToMerge) Error() string {
|
|
||||||
return fmt.Sprintf("not allowed to merge [reason: %s]", err.Reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrDisallowedToMerge) Unwrap() error {
|
|
||||||
return util.ErrPermissionDenied
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrTagAlreadyExists represents an error that tag with such name already exists.
|
|
||||||
type ErrTagAlreadyExists struct {
|
|
||||||
TagName string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrTagAlreadyExists checks if an error is an ErrTagAlreadyExists.
|
|
||||||
func IsErrTagAlreadyExists(err error) bool {
|
|
||||||
_, ok := err.(ErrTagAlreadyExists)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrTagAlreadyExists) Error() string {
|
|
||||||
return fmt.Sprintf("tag already exists [name: %s]", err.TagName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrTagAlreadyExists) Unwrap() error {
|
|
||||||
return util.ErrAlreadyExist
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrSHADoesNotMatch represents a "SHADoesNotMatch" kind of error.
|
|
||||||
type ErrSHADoesNotMatch struct {
|
|
||||||
Path string
|
|
||||||
GivenSHA string
|
|
||||||
CurrentSHA string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrSHADoesNotMatch checks if an error is a ErrSHADoesNotMatch.
|
|
||||||
func IsErrSHADoesNotMatch(err error) bool {
|
|
||||||
_, ok := err.(ErrSHADoesNotMatch)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrSHADoesNotMatch) Error() string {
|
|
||||||
return fmt.Sprintf("sha does not match [given: %s, expected: %s]", err.GivenSHA, err.CurrentSHA)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrSHANotFound represents a "SHADoesNotMatch" kind of error.
|
|
||||||
type ErrSHANotFound struct {
|
|
||||||
SHA string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrSHANotFound checks if an error is a ErrSHANotFound.
|
|
||||||
func IsErrSHANotFound(err error) bool {
|
|
||||||
_, ok := err.(ErrSHANotFound)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrSHANotFound) Error() string {
|
|
||||||
return fmt.Sprintf("sha not found [%s]", err.SHA)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrSHANotFound) Unwrap() error {
|
|
||||||
return util.ErrNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrCommitIDDoesNotMatch represents a "CommitIDDoesNotMatch" kind of error.
|
|
||||||
type ErrCommitIDDoesNotMatch struct {
|
|
||||||
GivenCommitID string
|
|
||||||
CurrentCommitID string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrCommitIDDoesNotMatch checks if an error is a ErrCommitIDDoesNotMatch.
|
|
||||||
func IsErrCommitIDDoesNotMatch(err error) bool {
|
|
||||||
_, ok := err.(ErrCommitIDDoesNotMatch)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrCommitIDDoesNotMatch) Error() string {
|
|
||||||
return fmt.Sprintf("file CommitID does not match [given: %s, expected: %s]", err.GivenCommitID, err.CurrentCommitID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrSHAOrCommitIDNotProvided represents a "SHAOrCommitIDNotProvided" kind of error.
|
|
||||||
type ErrSHAOrCommitIDNotProvided struct{}
|
|
||||||
|
|
||||||
// IsErrSHAOrCommitIDNotProvided checks if an error is a ErrSHAOrCommitIDNotProvided.
|
|
||||||
func IsErrSHAOrCommitIDNotProvided(err error) bool {
|
|
||||||
_, ok := err.(ErrSHAOrCommitIDNotProvided)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrSHAOrCommitIDNotProvided) Error() string {
|
|
||||||
return "a SHA or commit ID must be proved when updating a file"
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrInvalidMergeStyle represents an error if merging with disabled merge strategy
|
|
||||||
type ErrInvalidMergeStyle struct {
|
|
||||||
ID int64
|
|
||||||
Style repo_model.MergeStyle
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrInvalidMergeStyle checks if an error is a ErrInvalidMergeStyle.
|
|
||||||
func IsErrInvalidMergeStyle(err error) bool {
|
|
||||||
_, ok := err.(ErrInvalidMergeStyle)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrInvalidMergeStyle) Error() string {
|
|
||||||
return fmt.Sprintf("merge strategy is not allowed or is invalid [repo_id: %d, strategy: %s]",
|
|
||||||
err.ID, err.Style)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrInvalidMergeStyle) Unwrap() error {
|
|
||||||
return util.ErrInvalidArgument
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrMergeConflicts represents an error if merging fails with a conflict
|
|
||||||
type ErrMergeConflicts struct {
|
|
||||||
Style repo_model.MergeStyle
|
|
||||||
StdOut string
|
|
||||||
StdErr string
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrMergeConflicts checks if an error is a ErrMergeConflicts.
|
|
||||||
func IsErrMergeConflicts(err error) bool {
|
|
||||||
_, ok := err.(ErrMergeConflicts)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrMergeConflicts) Error() string {
|
|
||||||
return fmt.Sprintf("Merge Conflict Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrMergeUnrelatedHistories represents an error if merging fails due to unrelated histories
|
|
||||||
type ErrMergeUnrelatedHistories struct {
|
|
||||||
Style repo_model.MergeStyle
|
|
||||||
StdOut string
|
|
||||||
StdErr string
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrMergeUnrelatedHistories checks if an error is a ErrMergeUnrelatedHistories.
|
|
||||||
func IsErrMergeUnrelatedHistories(err error) bool {
|
|
||||||
_, ok := err.(ErrMergeUnrelatedHistories)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrMergeUnrelatedHistories) Error() string {
|
|
||||||
return fmt.Sprintf("Merge UnrelatedHistories Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrMergeDivergingFastForwardOnly represents an error if a fast-forward-only merge fails because the branches diverge
|
|
||||||
type ErrMergeDivergingFastForwardOnly struct {
|
|
||||||
StdOut string
|
|
||||||
StdErr string
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrMergeDivergingFastForwardOnly checks if an error is a ErrMergeDivergingFastForwardOnly.
|
|
||||||
func IsErrMergeDivergingFastForwardOnly(err error) bool {
|
|
||||||
_, ok := err.(ErrMergeDivergingFastForwardOnly)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrMergeDivergingFastForwardOnly) Error() string {
|
|
||||||
return fmt.Sprintf("Merge DivergingFastForwardOnly Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrRebaseConflicts represents an error if rebase fails with a conflict
|
|
||||||
type ErrRebaseConflicts struct {
|
|
||||||
Style repo_model.MergeStyle
|
|
||||||
CommitSHA string
|
|
||||||
StdOut string
|
|
||||||
StdErr string
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrRebaseConflicts checks if an error is a ErrRebaseConflicts.
|
|
||||||
func IsErrRebaseConflicts(err error) bool {
|
|
||||||
_, ok := err.(ErrRebaseConflicts)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrRebaseConflicts) Error() string {
|
|
||||||
return fmt.Sprintf("Rebase Error: %v: Whilst Rebasing: %s\n%s\n%s", err.Err, err.CommitSHA, err.StdErr, err.StdOut)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrPullRequestHasMerged represents a "PullRequestHasMerged"-error
|
|
||||||
type ErrPullRequestHasMerged struct {
|
|
||||||
ID int64
|
|
||||||
IssueID int64
|
|
||||||
HeadRepoID int64
|
|
||||||
BaseRepoID int64
|
|
||||||
HeadBranch string
|
|
||||||
BaseBranch string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrPullRequestHasMerged checks if an error is a ErrPullRequestHasMerged.
|
|
||||||
func IsErrPullRequestHasMerged(err error) bool {
|
|
||||||
_, ok := err.(ErrPullRequestHasMerged)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error does pretty-printing :D
|
|
||||||
func (err ErrPullRequestHasMerged) Error() string {
|
|
||||||
return fmt.Sprintf("pull request has merged [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]",
|
|
||||||
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
|
|
||||||
}
|
|
@ -36,6 +36,21 @@ func init() {
|
|||||||
db.RegisterModel(new(OrgUser))
|
db.RegisterModel(new(OrgUser))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrUserHasOrgs represents a "UserHasOrgs" kind of error.
|
||||||
|
type ErrUserHasOrgs struct {
|
||||||
|
UID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrUserHasOrgs checks if an error is a ErrUserHasOrgs.
|
||||||
|
func IsErrUserHasOrgs(err error) bool {
|
||||||
|
_, ok := err.(ErrUserHasOrgs)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrUserHasOrgs) Error() string {
|
||||||
|
return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID)
|
||||||
|
}
|
||||||
|
|
||||||
// GetOrganizationCount returns count of membership of organization of the user.
|
// GetOrganizationCount returns count of membership of organization of the user.
|
||||||
func GetOrganizationCount(ctx context.Context, u *user_model.User) (int64, error) {
|
func GetOrganizationCount(ctx context.Context, u *user_model.User) (int64, error) {
|
||||||
return db.GetEngine(ctx).
|
return db.GetEngine(ctx).
|
||||||
|
@ -301,6 +301,21 @@ func FindUnreferencedPackages(ctx context.Context) ([]*Package, error) {
|
|||||||
Find(&ps)
|
Find(&ps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrUserOwnPackages notifies that the user (still) owns the packages.
|
||||||
|
type ErrUserOwnPackages struct {
|
||||||
|
UID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrUserOwnPackages checks if an error is an ErrUserOwnPackages.
|
||||||
|
func IsErrUserOwnPackages(err error) bool {
|
||||||
|
_, ok := err.(ErrUserOwnPackages)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrUserOwnPackages) Error() string {
|
||||||
|
return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID)
|
||||||
|
}
|
||||||
|
|
||||||
// HasOwnerPackages tests if a user/org has accessible packages
|
// HasOwnerPackages tests if a user/org has accessible packages
|
||||||
func HasOwnerPackages(ctx context.Context, ownerID int64) (bool, error) {
|
func HasOwnerPackages(ctx context.Context, ownerID int64) (bool, error) {
|
||||||
return db.GetEngine(ctx).
|
return db.GetEngine(ctx).
|
||||||
|
@ -37,7 +37,7 @@ type ErrUserDoesNotHaveAccessToRepo struct {
|
|||||||
RepoName string
|
RepoName string
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsErrUserDoesNotHaveAccessToRepo checks if an error is a ErrRepoFileAlreadyExists.
|
// IsErrUserDoesNotHaveAccessToRepo checks if an error is a ErrUserDoesNotHaveAccessToRepo.
|
||||||
func IsErrUserDoesNotHaveAccessToRepo(err error) bool {
|
func IsErrUserDoesNotHaveAccessToRepo(err error) bool {
|
||||||
_, ok := err.(ErrUserDoesNotHaveAccessToRepo)
|
_, ok := err.(ErrUserDoesNotHaveAccessToRepo)
|
||||||
return ok
|
return ok
|
||||||
@ -866,6 +866,21 @@ func (repo *Repository) TemplateRepo(ctx context.Context) *Repository {
|
|||||||
return repo
|
return repo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrUserOwnRepos represents a "UserOwnRepos" kind of error.
|
||||||
|
type ErrUserOwnRepos struct {
|
||||||
|
UID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrUserOwnRepos checks if an error is a ErrUserOwnRepos.
|
||||||
|
func IsErrUserOwnRepos(err error) bool {
|
||||||
|
_, ok := err.(ErrUserOwnRepos)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrUserOwnRepos) Error() string {
|
||||||
|
return fmt.Sprintf("user still has ownership of repositories [uid: %d]", err.UID)
|
||||||
|
}
|
||||||
|
|
||||||
type CountRepositoryOptions struct {
|
type CountRepositoryOptions struct {
|
||||||
OwnerID int64
|
OwnerID int64
|
||||||
Private optional.Option[bool]
|
Private optional.Option[bool]
|
||||||
|
@ -788,6 +788,21 @@ func createUser(ctx context.Context, u *User, meta *Meta, createdByAdmin bool, o
|
|||||||
return committer.Commit()
|
return committer.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrDeleteLastAdminUser represents a "DeleteLastAdminUser" kind of error.
|
||||||
|
type ErrDeleteLastAdminUser struct {
|
||||||
|
UID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrDeleteLastAdminUser checks if an error is a ErrDeleteLastAdminUser.
|
||||||
|
func IsErrDeleteLastAdminUser(err error) bool {
|
||||||
|
_, ok := err.(ErrDeleteLastAdminUser)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrDeleteLastAdminUser) Error() string {
|
||||||
|
return fmt.Sprintf("can not delete the last admin user [uid: %d]", err.UID)
|
||||||
|
}
|
||||||
|
|
||||||
// IsLastAdminUser check whether user is the last admin
|
// IsLastAdminUser check whether user is the last admin
|
||||||
func IsLastAdminUser(ctx context.Context, user *User) bool {
|
func IsLastAdminUser(ctx context.Context, user *User) bool {
|
||||||
if user.IsAdmin && CountUsers(ctx, &CountUserFilter{IsAdmin: optional.Some(true)}) <= 1 {
|
if user.IsAdmin && CountUsers(ctx, &CountUserFilter{IsAdmin: optional.Some(true)}) <= 1 {
|
||||||
|
@ -7,10 +7,8 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"runtime"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -32,7 +30,6 @@ type WriteCloserError interface {
|
|||||||
func ensureValidGitRepository(ctx context.Context, repoPath string) error {
|
func ensureValidGitRepository(ctx context.Context, repoPath string) error {
|
||||||
stderr := strings.Builder{}
|
stderr := strings.Builder{}
|
||||||
err := NewCommand(ctx, "rev-parse").
|
err := NewCommand(ctx, "rev-parse").
|
||||||
SetDescription(fmt.Sprintf("%s rev-parse [repo_path: %s]", GitExecutable, repoPath)).
|
|
||||||
Run(&RunOpts{
|
Run(&RunOpts{
|
||||||
Dir: repoPath,
|
Dir: repoPath,
|
||||||
Stderr: &stderr,
|
Stderr: &stderr,
|
||||||
@ -62,13 +59,9 @@ func catFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError,
|
|||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
_, filename, line, _ := runtime.Caller(2)
|
|
||||||
filename = strings.TrimPrefix(filename, callerPrefix)
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
stderr := strings.Builder{}
|
stderr := strings.Builder{}
|
||||||
err := NewCommand(ctx, "cat-file", "--batch-check").
|
err := NewCommand(ctx, "cat-file", "--batch-check").
|
||||||
SetDescription(fmt.Sprintf("%s cat-file --batch-check [repo_path: %s] (%s:%d)", GitExecutable, repoPath, filename, line)).
|
|
||||||
Run(&RunOpts{
|
Run(&RunOpts{
|
||||||
Dir: repoPath,
|
Dir: repoPath,
|
||||||
Stdin: batchStdinReader,
|
Stdin: batchStdinReader,
|
||||||
@ -114,13 +107,9 @@ func catFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi
|
|||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
_, filename, line, _ := runtime.Caller(2)
|
|
||||||
filename = strings.TrimPrefix(filename, callerPrefix)
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
stderr := strings.Builder{}
|
stderr := strings.Builder{}
|
||||||
err := NewCommand(ctx, "cat-file", "--batch").
|
err := NewCommand(ctx, "cat-file", "--batch").
|
||||||
SetDescription(fmt.Sprintf("%s cat-file --batch [repo_path: %s] (%s:%d)", GitExecutable, repoPath, filename, line)).
|
|
||||||
Run(&RunOpts{
|
Run(&RunOpts{
|
||||||
Dir: repoPath,
|
Dir: repoPath,
|
||||||
Stdin: batchStdinReader,
|
Stdin: batchStdinReader,
|
||||||
@ -320,13 +309,6 @@ func ParseTreeLine(objectFormat ObjectFormat, rd *bufio.Reader, modeBuf, fnameBu
|
|||||||
return mode, fname, sha, n, err
|
return mode, fname, sha, n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var callerPrefix string
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
_, filename, _, _ := runtime.Caller(0)
|
|
||||||
callerPrefix = strings.TrimSuffix(filename, "modules/git/batch_reader.go")
|
|
||||||
}
|
|
||||||
|
|
||||||
func DiscardFull(rd *bufio.Reader, discard int64) error {
|
func DiscardFull(rd *bufio.Reader, discard int64) error {
|
||||||
if discard > math.MaxInt32 {
|
if discard > math.MaxInt32 {
|
||||||
n, err := rd.Discard(math.MaxInt32)
|
n, err := rd.Discard(math.MaxInt32)
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@ -142,9 +141,7 @@ func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath
|
|||||||
// There is no equivalent on Windows. May be implemented if Gitea uses an external git backend.
|
// There is no equivalent on Windows. May be implemented if Gitea uses an external git backend.
|
||||||
cmd.AddOptionValues("--ignore-revs-file", *ignoreRevsFile)
|
cmd.AddOptionValues("--ignore-revs-file", *ignoreRevsFile)
|
||||||
}
|
}
|
||||||
cmd.AddDynamicArguments(commit.ID.String()).
|
cmd.AddDynamicArguments(commit.ID.String()).AddDashesAndList(file)
|
||||||
AddDashesAndList(file).
|
|
||||||
SetDescription(fmt.Sprintf("GetBlame [repo_path: %s]", repoPath))
|
|
||||||
reader, stdout, err := os.Pipe()
|
reader, stdout, err := os.Pipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ignoreRevsFile != nil {
|
if ignoreRevsFile != nil {
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -43,18 +44,24 @@ type Command struct {
|
|||||||
prog string
|
prog string
|
||||||
args []string
|
args []string
|
||||||
parentContext context.Context
|
parentContext context.Context
|
||||||
desc string
|
|
||||||
globalArgsLength int
|
globalArgsLength int
|
||||||
brokenArgs []string
|
brokenArgs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Command) String() string {
|
func logArgSanitize(arg string) string {
|
||||||
return c.toString(false)
|
if strings.Contains(arg, "://") && strings.Contains(arg, "@") {
|
||||||
|
return util.SanitizeCredentialURLs(arg)
|
||||||
|
} else if filepath.IsAbs(arg) {
|
||||||
|
base := filepath.Base(arg)
|
||||||
|
dir := filepath.Dir(arg)
|
||||||
|
return filepath.Join(filepath.Base(dir), base)
|
||||||
|
}
|
||||||
|
return arg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Command) toString(sanitizing bool) string {
|
func (c *Command) LogString() string {
|
||||||
// WARNING: this function is for debugging purposes only. It's much better than old code (which only joins args with space),
|
// WARNING: this function is for debugging purposes only. It's much better than old code (which only joins args with space),
|
||||||
// It's impossible to make a simple and 100% correct implementation of argument quoting for different platforms.
|
// It's impossible to make a simple and 100% correct implementation of argument quoting for different platforms here.
|
||||||
debugQuote := func(s string) string {
|
debugQuote := func(s string) string {
|
||||||
if strings.ContainsAny(s, " `'\"\t\r\n") {
|
if strings.ContainsAny(s, " `'\"\t\r\n") {
|
||||||
return fmt.Sprintf("%q", s)
|
return fmt.Sprintf("%q", s)
|
||||||
@ -63,12 +70,11 @@ func (c *Command) toString(sanitizing bool) string {
|
|||||||
}
|
}
|
||||||
a := make([]string, 0, len(c.args)+1)
|
a := make([]string, 0, len(c.args)+1)
|
||||||
a = append(a, debugQuote(c.prog))
|
a = append(a, debugQuote(c.prog))
|
||||||
for _, arg := range c.args {
|
if c.globalArgsLength > 0 {
|
||||||
if sanitizing && (strings.Contains(arg, "://") && strings.Contains(arg, "@")) {
|
a = append(a, "...global...")
|
||||||
a = append(a, debugQuote(util.SanitizeCredentialURLs(arg)))
|
|
||||||
} else {
|
|
||||||
a = append(a, debugQuote(arg))
|
|
||||||
}
|
}
|
||||||
|
for i := c.globalArgsLength; i < len(c.args); i++ {
|
||||||
|
a = append(a, debugQuote(logArgSanitize(c.args[i])))
|
||||||
}
|
}
|
||||||
return strings.Join(a, " ")
|
return strings.Join(a, " ")
|
||||||
}
|
}
|
||||||
@ -112,12 +118,6 @@ func (c *Command) SetParentContext(ctx context.Context) *Command {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDescription sets the description for this command which be returned on c.String()
|
|
||||||
func (c *Command) SetDescription(desc string) *Command {
|
|
||||||
c.desc = desc
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// isSafeArgumentValue checks if the argument is safe to be used as a value (not an option)
|
// isSafeArgumentValue checks if the argument is safe to be used as a value (not an option)
|
||||||
func isSafeArgumentValue(s string) bool {
|
func isSafeArgumentValue(s string) bool {
|
||||||
return s == "" || s[0] != '-'
|
return s == "" || s[0] != '-'
|
||||||
@ -271,8 +271,12 @@ var ErrBrokenCommand = errors.New("git command is broken")
|
|||||||
|
|
||||||
// Run runs the command with the RunOpts
|
// Run runs the command with the RunOpts
|
||||||
func (c *Command) Run(opts *RunOpts) error {
|
func (c *Command) Run(opts *RunOpts) error {
|
||||||
|
return c.run(1, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Command) run(skip int, opts *RunOpts) error {
|
||||||
if len(c.brokenArgs) != 0 {
|
if len(c.brokenArgs) != 0 {
|
||||||
log.Error("git command is broken: %s, broken args: %s", c.String(), strings.Join(c.brokenArgs, " "))
|
log.Error("git command is broken: %s, broken args: %s", c.LogString(), strings.Join(c.brokenArgs, " "))
|
||||||
return ErrBrokenCommand
|
return ErrBrokenCommand
|
||||||
}
|
}
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
@ -285,20 +289,14 @@ func (c *Command) Run(opts *RunOpts) error {
|
|||||||
timeout = defaultCommandExecutionTimeout
|
timeout = defaultCommandExecutionTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(opts.Dir) == 0 {
|
var desc string
|
||||||
log.Debug("git.Command.Run: %s", c)
|
callerInfo := util.CallerFuncName(1 /* util */ + 1 /* this */ + skip /* parent */)
|
||||||
} else {
|
if pos := strings.LastIndex(callerInfo, "/"); pos >= 0 {
|
||||||
log.Debug("git.Command.RunDir(%s): %s", opts.Dir, c)
|
callerInfo = callerInfo[pos+1:]
|
||||||
}
|
|
||||||
|
|
||||||
desc := c.desc
|
|
||||||
if desc == "" {
|
|
||||||
if opts.Dir == "" {
|
|
||||||
desc = fmt.Sprintf("git: %s", c.toString(true))
|
|
||||||
} else {
|
|
||||||
desc = fmt.Sprintf("git(dir:%s): %s", opts.Dir, c.toString(true))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// these logs are for debugging purposes only, so no guarantee of correctness or stability
|
||||||
|
desc = fmt.Sprintf("git.Run(by:%s, repo:%s): %s", callerInfo, logArgSanitize(opts.Dir), c.LogString())
|
||||||
|
log.Debug("git.Command: %s", desc)
|
||||||
|
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
var cancel context.CancelFunc
|
var cancel context.CancelFunc
|
||||||
@ -401,7 +399,7 @@ func IsErrorExitCode(err error, code int) bool {
|
|||||||
|
|
||||||
// RunStdString runs the command with options and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr).
|
// RunStdString runs the command with options and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr).
|
||||||
func (c *Command) RunStdString(opts *RunOpts) (stdout, stderr string, runErr RunStdError) {
|
func (c *Command) RunStdString(opts *RunOpts) (stdout, stderr string, runErr RunStdError) {
|
||||||
stdoutBytes, stderrBytes, err := c.RunStdBytes(opts)
|
stdoutBytes, stderrBytes, err := c.runStdBytes(opts)
|
||||||
stdout = util.UnsafeBytesToString(stdoutBytes)
|
stdout = util.UnsafeBytesToString(stdoutBytes)
|
||||||
stderr = util.UnsafeBytesToString(stderrBytes)
|
stderr = util.UnsafeBytesToString(stderrBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -413,6 +411,10 @@ func (c *Command) RunStdString(opts *RunOpts) (stdout, stderr string, runErr Run
|
|||||||
|
|
||||||
// RunStdBytes runs the command with options and returns stdout/stderr as bytes. and store stderr to returned error (err combined with stderr).
|
// RunStdBytes runs the command with options and returns stdout/stderr as bytes. and store stderr to returned error (err combined with stderr).
|
||||||
func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunStdError) {
|
func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunStdError) {
|
||||||
|
return c.runStdBytes(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Command) runStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunStdError) {
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = &RunOpts{}
|
opts = &RunOpts{}
|
||||||
}
|
}
|
||||||
@ -435,7 +437,7 @@ func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunS
|
|||||||
PipelineFunc: opts.PipelineFunc,
|
PipelineFunc: opts.PipelineFunc,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := c.Run(newOpts)
|
err := c.run(2, newOpts)
|
||||||
stderr = stderrBuf.Bytes()
|
stderr = stderrBuf.Bytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, stderr, &runStdError{err: err, stderr: util.UnsafeBytesToString(stderr)}
|
return nil, stderr, &runStdError{err: err, stderr: util.UnsafeBytesToString(stderr)}
|
||||||
|
@ -55,8 +55,8 @@ func TestGitArgument(t *testing.T) {
|
|||||||
|
|
||||||
func TestCommandString(t *testing.T) {
|
func TestCommandString(t *testing.T) {
|
||||||
cmd := NewCommandContextNoGlobals(context.Background(), "a", "-m msg", "it's a test", `say "hello"`)
|
cmd := NewCommandContextNoGlobals(context.Background(), "a", "-m msg", "it's a test", `say "hello"`)
|
||||||
assert.EqualValues(t, cmd.prog+` a "-m msg" "it's a test" "say \"hello\""`, cmd.String())
|
assert.EqualValues(t, cmd.prog+` a "-m msg" "it's a test" "say \"hello\""`, cmd.LogString())
|
||||||
|
|
||||||
cmd = NewCommandContextNoGlobals(context.Background(), "url: https://a:b@c/")
|
cmd = NewCommandContextNoGlobals(context.Background(), "url: https://a:b@c/", "/root/dir-a/dir-b")
|
||||||
assert.EqualValues(t, cmd.prog+` "url: https://sanitized-credential@c/"`, cmd.toString(true))
|
assert.EqualValues(t, cmd.prog+` "url: https://sanitized-credential@c/" dir-a/dir-b`, cmd.LogString())
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,12 @@ package git
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
giturl "code.gitea.io/gitea/modules/git/url"
|
giturl "code.gitea.io/gitea/modules/git/url"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetRemoteAddress returns remote url of git repository in the repoPath with special remote name
|
// GetRemoteAddress returns remote url of git repository in the repoPath with special remote name
|
||||||
@ -37,3 +41,61 @@ func GetRemoteURL(ctx context.Context, repoPath, remoteName string) (*giturl.Git
|
|||||||
}
|
}
|
||||||
return giturl.Parse(addr)
|
return giturl.Parse(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
|
||||||
|
type ErrInvalidCloneAddr struct {
|
||||||
|
Host string
|
||||||
|
IsURLError bool
|
||||||
|
IsInvalidPath bool
|
||||||
|
IsProtocolInvalid bool
|
||||||
|
IsPermissionDenied bool
|
||||||
|
LocalPath bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrInvalidCloneAddr checks if an error is a ErrInvalidCloneAddr.
|
||||||
|
func IsErrInvalidCloneAddr(err error) bool {
|
||||||
|
_, ok := err.(*ErrInvalidCloneAddr)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *ErrInvalidCloneAddr) Error() string {
|
||||||
|
if err.IsInvalidPath {
|
||||||
|
return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided path is invalid", err.Host)
|
||||||
|
}
|
||||||
|
if err.IsProtocolInvalid {
|
||||||
|
return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url protocol is not allowed", err.Host)
|
||||||
|
}
|
||||||
|
if err.IsPermissionDenied {
|
||||||
|
return fmt.Sprintf("migration/cloning from '%s' is not allowed.", err.Host)
|
||||||
|
}
|
||||||
|
if err.IsURLError {
|
||||||
|
return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url is invalid", err.Host)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("migration/cloning from '%s' is not allowed", err.Host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *ErrInvalidCloneAddr) Unwrap() error {
|
||||||
|
return util.ErrInvalidArgument
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseRemoteAddr checks if given remote address is valid,
|
||||||
|
// and returns composed URL with needed username and password.
|
||||||
|
func ParseRemoteAddr(remoteAddr, authUsername, authPassword string) (string, error) {
|
||||||
|
remoteAddr = strings.TrimSpace(remoteAddr)
|
||||||
|
// Remote address can be HTTP/HTTPS/Git URL or local path.
|
||||||
|
if strings.HasPrefix(remoteAddr, "http://") ||
|
||||||
|
strings.HasPrefix(remoteAddr, "https://") ||
|
||||||
|
strings.HasPrefix(remoteAddr, "git://") {
|
||||||
|
u, err := url.Parse(remoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
return "", &ErrInvalidCloneAddr{IsURLError: true, Host: remoteAddr}
|
||||||
|
}
|
||||||
|
if len(authUsername)+len(authPassword) > 0 {
|
||||||
|
u.User = url.UserPassword(authUsername, authPassword)
|
||||||
|
}
|
||||||
|
remoteAddr = u.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return remoteAddr, nil
|
||||||
|
}
|
||||||
|
@ -18,7 +18,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/proxy"
|
"code.gitea.io/gitea/modules/proxy"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GPGSettings represents the default GPG settings for this repository
|
// GPGSettings represents the default GPG settings for this repository
|
||||||
@ -160,12 +159,6 @@ func CloneWithArgs(ctx context.Context, args TrustedCmdArgs, from, to string, op
|
|||||||
}
|
}
|
||||||
cmd.AddDashesAndList(from, to)
|
cmd.AddDashesAndList(from, to)
|
||||||
|
|
||||||
if strings.Contains(from, "://") && strings.Contains(from, "@") {
|
|
||||||
cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, util.SanitizeCredentialURLs(from), to, opts.Shared, opts.Mirror, opts.Depth))
|
|
||||||
} else {
|
|
||||||
cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, from, to, opts.Shared, opts.Mirror, opts.Depth))
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Timeout <= 0 {
|
if opts.Timeout <= 0 {
|
||||||
opts.Timeout = -1
|
opts.Timeout = -1
|
||||||
}
|
}
|
||||||
@ -213,12 +206,6 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
|
|||||||
}
|
}
|
||||||
cmd.AddDashesAndList(remoteBranchArgs...)
|
cmd.AddDashesAndList(remoteBranchArgs...)
|
||||||
|
|
||||||
if strings.Contains(opts.Remote, "://") && strings.Contains(opts.Remote, "@") {
|
|
||||||
cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, util.SanitizeCredentialURLs(opts.Remote), opts.Force, opts.Mirror))
|
|
||||||
} else {
|
|
||||||
cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, opts.Remote, opts.Force, opts.Mirror))
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout, stderr, err := cmd.RunStdString(&RunOpts{Env: opts.Env, Timeout: opts.Timeout, Dir: repoPath})
|
stdout, stderr, err := cmd.RunStdString(&RunOpts{Env: opts.Env, Timeout: opts.Timeout, Dir: repoPath})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(stderr, "non-fast-forward") {
|
if strings.Contains(stderr, "non-fast-forward") {
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/gtprof"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/process"
|
"code.gitea.io/gitea/modules/process"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
@ -136,7 +137,7 @@ func (g *Manager) doShutdown() {
|
|||||||
}
|
}
|
||||||
g.lock.Lock()
|
g.lock.Lock()
|
||||||
g.shutdownCtxCancel()
|
g.shutdownCtxCancel()
|
||||||
atShutdownCtx := pprof.WithLabels(g.hammerCtx, pprof.Labels(LifecyclePProfLabel, "post-shutdown"))
|
atShutdownCtx := pprof.WithLabels(g.hammerCtx, pprof.Labels(gtprof.LabelGracefulLifecycle, "post-shutdown"))
|
||||||
pprof.SetGoroutineLabels(atShutdownCtx)
|
pprof.SetGoroutineLabels(atShutdownCtx)
|
||||||
for _, fn := range g.toRunAtShutdown {
|
for _, fn := range g.toRunAtShutdown {
|
||||||
go fn()
|
go fn()
|
||||||
@ -167,7 +168,7 @@ func (g *Manager) doHammerTime(d time.Duration) {
|
|||||||
default:
|
default:
|
||||||
log.Warn("Setting Hammer condition")
|
log.Warn("Setting Hammer condition")
|
||||||
g.hammerCtxCancel()
|
g.hammerCtxCancel()
|
||||||
atHammerCtx := pprof.WithLabels(g.terminateCtx, pprof.Labels(LifecyclePProfLabel, "post-hammer"))
|
atHammerCtx := pprof.WithLabels(g.terminateCtx, pprof.Labels(gtprof.LabelGracefulLifecycle, "post-hammer"))
|
||||||
pprof.SetGoroutineLabels(atHammerCtx)
|
pprof.SetGoroutineLabels(atHammerCtx)
|
||||||
}
|
}
|
||||||
g.lock.Unlock()
|
g.lock.Unlock()
|
||||||
@ -183,7 +184,7 @@ func (g *Manager) doTerminate() {
|
|||||||
default:
|
default:
|
||||||
log.Warn("Terminating")
|
log.Warn("Terminating")
|
||||||
g.terminateCtxCancel()
|
g.terminateCtxCancel()
|
||||||
atTerminateCtx := pprof.WithLabels(g.managerCtx, pprof.Labels(LifecyclePProfLabel, "post-terminate"))
|
atTerminateCtx := pprof.WithLabels(g.managerCtx, pprof.Labels(gtprof.LabelGracefulLifecycle, "post-terminate"))
|
||||||
pprof.SetGoroutineLabels(atTerminateCtx)
|
pprof.SetGoroutineLabels(atTerminateCtx)
|
||||||
|
|
||||||
for _, fn := range g.toRunAtTerminate {
|
for _, fn := range g.toRunAtTerminate {
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/gtprof"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FIXME: it seems that there is a bug when using systemd Type=notify: the "Install Page" (INSTALL_LOCK=false) doesn't notify properly.
|
// FIXME: it seems that there is a bug when using systemd Type=notify: the "Install Page" (INSTALL_LOCK=false) doesn't notify properly.
|
||||||
@ -22,12 +24,6 @@ const (
|
|||||||
watchdogMsg systemdNotifyMsg = "WATCHDOG=1"
|
watchdogMsg systemdNotifyMsg = "WATCHDOG=1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LifecyclePProfLabel is a label marking manager lifecycle phase
|
|
||||||
// Making it compliant with prometheus key regex https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels
|
|
||||||
// would enable someone interested to be able to to continuously gather profiles into pyroscope.
|
|
||||||
// Other labels for pprof (in "modules/process" package) should also follow this rule.
|
|
||||||
const LifecyclePProfLabel = "graceful_lifecycle"
|
|
||||||
|
|
||||||
func statusMsg(msg string) systemdNotifyMsg {
|
func statusMsg(msg string) systemdNotifyMsg {
|
||||||
return systemdNotifyMsg("STATUS=" + msg)
|
return systemdNotifyMsg("STATUS=" + msg)
|
||||||
}
|
}
|
||||||
@ -71,10 +67,10 @@ func (g *Manager) prepare(ctx context.Context) {
|
|||||||
g.hammerCtx, g.hammerCtxCancel = context.WithCancel(ctx)
|
g.hammerCtx, g.hammerCtxCancel = context.WithCancel(ctx)
|
||||||
g.managerCtx, g.managerCtxCancel = context.WithCancel(ctx)
|
g.managerCtx, g.managerCtxCancel = context.WithCancel(ctx)
|
||||||
|
|
||||||
g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels(LifecyclePProfLabel, "with-terminate"))
|
g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels(gtprof.LabelGracefulLifecycle, "with-terminate"))
|
||||||
g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels(LifecyclePProfLabel, "with-shutdown"))
|
g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels(gtprof.LabelGracefulLifecycle, "with-shutdown"))
|
||||||
g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels(LifecyclePProfLabel, "with-hammer"))
|
g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels(gtprof.LabelGracefulLifecycle, "with-hammer"))
|
||||||
g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels(LifecyclePProfLabel, "with-manager"))
|
g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels(gtprof.LabelGracefulLifecycle, "with-manager"))
|
||||||
|
|
||||||
if !g.setStateTransition(stateInit, stateRunning) {
|
if !g.setStateTransition(stateInit, stateRunning) {
|
||||||
panic("invalid graceful manager state: transition from init to running failed")
|
panic("invalid graceful manager state: transition from init to running failed")
|
||||||
|
25
modules/gtprof/gtprof.go
Normal file
25
modules/gtprof/gtprof.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package gtprof
|
||||||
|
|
||||||
|
// This is a Gitea-specific profiling package,
|
||||||
|
// the name is chosen to distinguish it from the standard pprof tool and "GNU gprof"
|
||||||
|
|
||||||
|
// LabelGracefulLifecycle is a label marking manager lifecycle phase
|
||||||
|
// Making it compliant with prometheus key regex https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels
|
||||||
|
// would enable someone interested to be able to continuously gather profiles into pyroscope.
|
||||||
|
// Other labels for pprof should also follow this rule.
|
||||||
|
const LabelGracefulLifecycle = "graceful_lifecycle"
|
||||||
|
|
||||||
|
// LabelPid is a label set on goroutines that have a process attached
|
||||||
|
const LabelPid = "pid"
|
||||||
|
|
||||||
|
// LabelPpid is a label set on goroutines that have a process attached
|
||||||
|
const LabelPpid = "ppid"
|
||||||
|
|
||||||
|
// LabelProcessType is a label set on goroutines that have a process attached
|
||||||
|
const LabelProcessType = "process_type"
|
||||||
|
|
||||||
|
// LabelProcessDescription is a label set on goroutines that have a process attached
|
||||||
|
const LabelProcessDescription = "process_description"
|
@ -13,7 +13,6 @@ import (
|
|||||||
type Event struct {
|
type Event struct {
|
||||||
Time time.Time
|
Time time.Time
|
||||||
|
|
||||||
GoroutinePid string
|
|
||||||
Caller string
|
Caller string
|
||||||
Filename string
|
Filename string
|
||||||
Line int
|
Line int
|
||||||
@ -218,18 +217,17 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms
|
|||||||
}
|
}
|
||||||
|
|
||||||
if flags&Lgopid == Lgopid {
|
if flags&Lgopid == Lgopid {
|
||||||
if event.GoroutinePid != "" {
|
deprecatedGoroutinePid := "no-gopid" // use a dummy value to avoid breaking the log format
|
||||||
buf = append(buf, '[')
|
buf = append(buf, '[')
|
||||||
if mode.Colorize {
|
if mode.Colorize {
|
||||||
buf = append(buf, ColorBytes(FgHiYellow)...)
|
buf = append(buf, ColorBytes(FgHiYellow)...)
|
||||||
}
|
}
|
||||||
buf = append(buf, event.GoroutinePid...)
|
buf = append(buf, deprecatedGoroutinePid...)
|
||||||
if mode.Colorize {
|
if mode.Colorize {
|
||||||
buf = append(buf, resetBytes...)
|
buf = append(buf, resetBytes...)
|
||||||
}
|
}
|
||||||
buf = append(buf, ']', ' ')
|
buf = append(buf, ']', ' ')
|
||||||
}
|
}
|
||||||
}
|
|
||||||
buf = append(buf, msg...)
|
buf = append(buf, msg...)
|
||||||
|
|
||||||
if event.Stacktrace != "" && mode.StacktraceLevel <= event.Level {
|
if event.Stacktrace != "" && mode.StacktraceLevel <= event.Level {
|
||||||
|
@ -28,14 +28,13 @@ func TestEventFormatTextMessage(t *testing.T) {
|
|||||||
Caller: "caller",
|
Caller: "caller",
|
||||||
Filename: "filename",
|
Filename: "filename",
|
||||||
Line: 123,
|
Line: 123,
|
||||||
GoroutinePid: "pid",
|
|
||||||
Level: ERROR,
|
Level: ERROR,
|
||||||
Stacktrace: "stacktrace",
|
Stacktrace: "stacktrace",
|
||||||
},
|
},
|
||||||
"msg format: %v %v", "arg0", NewColoredValue("arg1", FgBlue),
|
"msg format: %v %v", "arg0", NewColoredValue("arg1", FgBlue),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.Equal(t, `[PREFIX] 2020/01/02 03:04:05.000000 filename:123:caller [E] [pid] msg format: arg0 arg1
|
assert.Equal(t, `[PREFIX] 2020/01/02 03:04:05.000000 filename:123:caller [E] [no-gopid] msg format: arg0 arg1
|
||||||
stacktrace
|
stacktrace
|
||||||
|
|
||||||
`, string(res))
|
`, string(res))
|
||||||
@ -46,12 +45,11 @@ func TestEventFormatTextMessage(t *testing.T) {
|
|||||||
Caller: "caller",
|
Caller: "caller",
|
||||||
Filename: "filename",
|
Filename: "filename",
|
||||||
Line: 123,
|
Line: 123,
|
||||||
GoroutinePid: "pid",
|
|
||||||
Level: ERROR,
|
Level: ERROR,
|
||||||
Stacktrace: "stacktrace",
|
Stacktrace: "stacktrace",
|
||||||
},
|
},
|
||||||
"msg format: %v %v", "arg0", NewColoredValue("arg1", FgBlue),
|
"msg format: %v %v", "arg0", NewColoredValue("arg1", FgBlue),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.Equal(t, "[PREFIX] \x1b[36m2020/01/02 03:04:05.000000 \x1b[0m\x1b[32mfilename:123:\x1b[32mcaller\x1b[0m \x1b[1;31m[E]\x1b[0m [\x1b[93mpid\x1b[0m] msg format: arg0 \x1b[34marg1\x1b[0m\n\tstacktrace\n\n", string(res))
|
assert.Equal(t, "[PREFIX] \x1b[36m2020/01/02 03:04:05.000000 \x1b[0m\x1b[32mfilename:123:\x1b[32mcaller\x1b[0m \x1b[1;31m[E]\x1b[0m [\x1b[93mno-gopid\x1b[0m] msg format: arg0 \x1b[34marg1\x1b[0m\n\tstacktrace\n\n", string(res))
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ const (
|
|||||||
LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone
|
LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone
|
||||||
Llevelinitial // Initial character of the provided level in brackets, eg. [I] for info
|
Llevelinitial // Initial character of the provided level in brackets, eg. [I] for info
|
||||||
Llevel // Provided level in brackets [INFO]
|
Llevel // Provided level in brackets [INFO]
|
||||||
Lgopid // the Goroutine-PID of the context
|
Lgopid // the Goroutine-PID of the context, deprecated and it is always a const value
|
||||||
|
|
||||||
Lmedfile = Lshortfile | Llongfile // last 20 characters of the filename
|
Lmedfile = Lshortfile | Llongfile // last 20 characters of the filename
|
||||||
LstdFlags = Ldate | Ltime | Lmedfile | Lshortfuncname | Llevelinitial // default
|
LstdFlags = Ldate | Ltime | Lmedfile | Lshortfuncname | Llevelinitial // default
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package log
|
|
||||||
|
|
||||||
import "unsafe"
|
|
||||||
|
|
||||||
//go:linkname runtime_getProfLabel runtime/pprof.runtime_getProfLabel
|
|
||||||
func runtime_getProfLabel() unsafe.Pointer //nolint
|
|
||||||
|
|
||||||
type labelMap map[string]string
|
|
||||||
|
|
||||||
func getGoroutineLabels() map[string]string {
|
|
||||||
l := (*labelMap)(runtime_getProfLabel())
|
|
||||||
if l == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return *l
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package log
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"runtime/pprof"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_getGoroutineLabels(t *testing.T) {
|
|
||||||
pprof.Do(context.Background(), pprof.Labels(), func(ctx context.Context) {
|
|
||||||
currentLabels := getGoroutineLabels()
|
|
||||||
pprof.ForLabels(ctx, func(key, value string) bool {
|
|
||||||
assert.EqualValues(t, value, currentLabels[key])
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
pprof.Do(ctx, pprof.Labels("Test_getGoroutineLabels", "Test_getGoroutineLabels_child1"), func(ctx context.Context) {
|
|
||||||
currentLabels := getGoroutineLabels()
|
|
||||||
pprof.ForLabels(ctx, func(key, value string) bool {
|
|
||||||
assert.EqualValues(t, value, currentLabels[key])
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if assert.NotNil(t, currentLabels) {
|
|
||||||
assert.EqualValues(t, "Test_getGoroutineLabels_child1", currentLabels["Test_getGoroutineLabels"])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
@ -200,11 +200,6 @@ func (l *LoggerImpl) Log(skip int, level Level, format string, logArgs ...any) {
|
|||||||
event.Stacktrace = Stack(skip + 1)
|
event.Stacktrace = Stack(skip + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
labels := getGoroutineLabels()
|
|
||||||
if labels != nil {
|
|
||||||
event.GoroutinePid = labels["pid"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// get a simple text message without color
|
// get a simple text message without color
|
||||||
msgArgs := make([]any, len(logArgs))
|
msgArgs := make([]any, len(logArgs))
|
||||||
copy(msgArgs, logArgs)
|
copy(msgArgs, logArgs)
|
||||||
|
@ -24,7 +24,7 @@ type GlobalVarsType struct {
|
|||||||
LinkRegex *regexp.Regexp // fast matching a URL link, no any extra validation.
|
LinkRegex *regexp.Regexp // fast matching a URL link, no any extra validation.
|
||||||
}
|
}
|
||||||
|
|
||||||
var GlobalVars = sync.OnceValue[*GlobalVarsType](func() *GlobalVarsType {
|
var GlobalVars = sync.OnceValue(func() *GlobalVarsType {
|
||||||
v := &GlobalVarsType{}
|
v := &GlobalVarsType{}
|
||||||
v.wwwURLRegxp = regexp.MustCompile(`^www\.[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}((?:/|[#?])[-a-zA-Z0-9@:%_\+.~#!?&//=\(\);,'">\^{}\[\]` + "`" + `]*)?`)
|
v.wwwURLRegxp = regexp.MustCompile(`^www\.[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}((?:/|[#?])[-a-zA-Z0-9@:%_\+.~#!?&//=\(\);,'">\^{}\[\]` + "`" + `]*)?`)
|
||||||
v.LinkRegex, _ = xurls.StrictMatchingScheme("https?://")
|
v.LinkRegex, _ = xurls.StrictMatchingScheme("https?://")
|
||||||
|
@ -42,7 +42,7 @@ type globalVarsType struct {
|
|||||||
nulCleaner *strings.Replacer
|
nulCleaner *strings.Replacer
|
||||||
}
|
}
|
||||||
|
|
||||||
var globalVars = sync.OnceValue[*globalVarsType](func() *globalVarsType {
|
var globalVars = sync.OnceValue(func() *globalVarsType {
|
||||||
v := &globalVarsType{}
|
v := &globalVarsType{}
|
||||||
// NOTE: All below regex matching do not perform any extra validation.
|
// NOTE: All below regex matching do not perform any extra validation.
|
||||||
// Thus a link is produced even if the linked entity does not exist.
|
// Thus a link is produced even if the linked entity does not exist.
|
||||||
|
@ -17,7 +17,7 @@ import (
|
|||||||
"golang.org/x/net/html"
|
"golang.org/x/net/html"
|
||||||
)
|
)
|
||||||
|
|
||||||
var reAttrClass = sync.OnceValue[*regexp.Regexp](func() *regexp.Regexp {
|
var reAttrClass = sync.OnceValue(func() *regexp.Regexp {
|
||||||
// TODO: it isn't a problem at the moment because our HTML contents are always well constructed
|
// TODO: it isn't a problem at the moment because our HTML contents are always well constructed
|
||||||
return regexp.MustCompile(`(<[^>]+)\s+class="([^"]+)"([^>]*>)`)
|
return regexp.MustCompile(`(<[^>]+)\s+class="([^"]+)"([^>]*>)`)
|
||||||
})
|
})
|
||||||
|
@ -112,7 +112,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// it is copied from old code, which is quite doubtful whether it is correct
|
// it is copied from old code, which is quite doubtful whether it is correct
|
||||||
var reValidIconName = sync.OnceValue[*regexp.Regexp](func() *regexp.Regexp {
|
var reValidIconName = sync.OnceValue(func() *regexp.Regexp {
|
||||||
return regexp.MustCompile(`^[-\w]+$`) // old: regexp.MustCompile("^[a-z ]+$")
|
return regexp.MustCompile(`^[-\w]+$`) // old: regexp.MustCompile("^[a-z ]+$")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -44,7 +44,8 @@ var (
|
|||||||
|
|
||||||
// https://man.archlinux.org/man/PKGBUILD.5
|
// https://man.archlinux.org/man/PKGBUILD.5
|
||||||
namePattern = regexp.MustCompile(`\A[a-zA-Z0-9@._+-]+\z`)
|
namePattern = regexp.MustCompile(`\A[a-zA-Z0-9@._+-]+\z`)
|
||||||
versionPattern = regexp.MustCompile(`\A(?:[0-9]:)?[a-zA-Z0-9.+~]+(?:-[a-zA-Z0-9.+-~]+)?\z`)
|
// (epoch:pkgver-pkgrel)
|
||||||
|
versionPattern = regexp.MustCompile(`\A(?:\d:)?[\w.+~]+(?:-[-\w.+~]+)?\z`)
|
||||||
)
|
)
|
||||||
|
|
||||||
type Package struct {
|
type Package struct {
|
||||||
|
@ -122,6 +122,14 @@ func TestParsePackageInfo(t *testing.T) {
|
|||||||
assert.ErrorIs(t, err, ErrInvalidName)
|
assert.ErrorIs(t, err, ErrInvalidName)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Regexp", func(t *testing.T) {
|
||||||
|
assert.Regexp(t, versionPattern, "1.2_3~4+5")
|
||||||
|
assert.Regexp(t, versionPattern, "1:2_3~4+5")
|
||||||
|
assert.NotRegexp(t, versionPattern, "a:1.0.0-1")
|
||||||
|
assert.NotRegexp(t, versionPattern, "0.0.1/1-1")
|
||||||
|
assert.NotRegexp(t, versionPattern, "1.0.0 -1")
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("InvalidVersion", func(t *testing.T) {
|
t.Run("InvalidVersion", func(t *testing.T) {
|
||||||
data := createPKGINFOContent(packageName, "")
|
data := createPKGINFOContent(packageName, "")
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/gtprof"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: This packages still uses a singleton for the Manager.
|
// TODO: This packages still uses a singleton for the Manager.
|
||||||
@ -25,18 +27,6 @@ var (
|
|||||||
DefaultContext = context.Background()
|
DefaultContext = context.Background()
|
||||||
)
|
)
|
||||||
|
|
||||||
// DescriptionPProfLabel is a label set on goroutines that have a process attached
|
|
||||||
const DescriptionPProfLabel = "process_description"
|
|
||||||
|
|
||||||
// PIDPProfLabel is a label set on goroutines that have a process attached
|
|
||||||
const PIDPProfLabel = "pid"
|
|
||||||
|
|
||||||
// PPIDPProfLabel is a label set on goroutines that have a process attached
|
|
||||||
const PPIDPProfLabel = "ppid"
|
|
||||||
|
|
||||||
// ProcessTypePProfLabel is a label set on goroutines that have a process attached
|
|
||||||
const ProcessTypePProfLabel = "process_type"
|
|
||||||
|
|
||||||
// IDType is a pid type
|
// IDType is a pid type
|
||||||
type IDType string
|
type IDType string
|
||||||
|
|
||||||
@ -187,7 +177,12 @@ func (pm *Manager) Add(ctx context.Context, description string, cancel context.C
|
|||||||
|
|
||||||
Trace(true, pid, description, parentPID, processType)
|
Trace(true, pid, description, parentPID, processType)
|
||||||
|
|
||||||
pprofCtx := pprof.WithLabels(ctx, pprof.Labels(DescriptionPProfLabel, description, PPIDPProfLabel, string(parentPID), PIDPProfLabel, string(pid), ProcessTypePProfLabel, processType))
|
pprofCtx := pprof.WithLabels(ctx, pprof.Labels(
|
||||||
|
gtprof.LabelProcessDescription, description,
|
||||||
|
gtprof.LabelPpid, string(parentPID),
|
||||||
|
gtprof.LabelPid, string(pid),
|
||||||
|
gtprof.LabelProcessType, processType,
|
||||||
|
))
|
||||||
if currentlyRunning {
|
if currentlyRunning {
|
||||||
pprof.SetGoroutineLabels(pprofCtx)
|
pprof.SetGoroutineLabels(pprofCtx)
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,8 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/gtprof"
|
||||||
|
|
||||||
"github.com/google/pprof/profile"
|
"github.com/google/pprof/profile"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -202,7 +204,7 @@ func (pm *Manager) ProcessStacktraces(flat, noSystem bool) ([]*Process, int, int
|
|||||||
|
|
||||||
// Add the non-process associated labels from the goroutine sample to the Stack
|
// Add the non-process associated labels from the goroutine sample to the Stack
|
||||||
for name, value := range sample.Label {
|
for name, value := range sample.Label {
|
||||||
if name == DescriptionPProfLabel || name == PIDPProfLabel || (!flat && name == PPIDPProfLabel) || name == ProcessTypePProfLabel {
|
if name == gtprof.LabelProcessDescription || name == gtprof.LabelPid || (!flat && name == gtprof.LabelPpid) || name == gtprof.LabelProcessType {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +226,7 @@ func (pm *Manager) ProcessStacktraces(flat, noSystem bool) ([]*Process, int, int
|
|||||||
var process *Process
|
var process *Process
|
||||||
|
|
||||||
// Try to get the PID from the goroutine labels
|
// Try to get the PID from the goroutine labels
|
||||||
if pidvalue, ok := sample.Label[PIDPProfLabel]; ok && len(pidvalue) == 1 {
|
if pidvalue, ok := sample.Label[gtprof.LabelPid]; ok && len(pidvalue) == 1 {
|
||||||
pid := IDType(pidvalue[0])
|
pid := IDType(pidvalue[0])
|
||||||
|
|
||||||
// Now try to get the process from our map
|
// Now try to get the process from our map
|
||||||
@ -238,20 +240,20 @@ func (pm *Manager) ProcessStacktraces(flat, noSystem bool) ([]*Process, int, int
|
|||||||
|
|
||||||
// get the parent PID
|
// get the parent PID
|
||||||
ppid := IDType("")
|
ppid := IDType("")
|
||||||
if value, ok := sample.Label[PPIDPProfLabel]; ok && len(value) == 1 {
|
if value, ok := sample.Label[gtprof.LabelPpid]; ok && len(value) == 1 {
|
||||||
ppid = IDType(value[0])
|
ppid = IDType(value[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
// format the description
|
// format the description
|
||||||
description := "(dead process)"
|
description := "(dead process)"
|
||||||
if value, ok := sample.Label[DescriptionPProfLabel]; ok && len(value) == 1 {
|
if value, ok := sample.Label[gtprof.LabelProcessDescription]; ok && len(value) == 1 {
|
||||||
description = value[0] + " " + description
|
description = value[0] + " " + description
|
||||||
}
|
}
|
||||||
|
|
||||||
// override the type of the process to "code" but add the old type as a label on the first stack
|
// override the type of the process to "code" but add the old type as a label on the first stack
|
||||||
ptype := NoneProcessType
|
ptype := NoneProcessType
|
||||||
if value, ok := sample.Label[ProcessTypePProfLabel]; ok && len(value) == 1 {
|
if value, ok := sample.Label[gtprof.LabelProcessType]; ok && len(value) == 1 {
|
||||||
stack.Labels = append(stack.Labels, &Label{Name: ProcessTypePProfLabel, Value: value[0]})
|
stack.Labels = append(stack.Labels, &Label{Name: gtprof.LabelProcessType, Value: value[0]})
|
||||||
}
|
}
|
||||||
process = &Process{
|
process = &Process{
|
||||||
PID: pid,
|
PID: pid,
|
||||||
|
@ -6,6 +6,7 @@ package queue
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"runtime/pprof"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@ -241,6 +242,9 @@ func NewWorkerPoolQueueWithContext[T any](ctx context.Context, name string, queu
|
|||||||
w.origHandler = handler
|
w.origHandler = handler
|
||||||
w.safeHandler = func(t ...T) (unhandled []T) {
|
w.safeHandler = func(t ...T) (unhandled []T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
|
// FIXME: there is no ctx support in the handler, so process manager is unable to restore the labels
|
||||||
|
// so here we explicitly set the "queue ctx" labels again after the handler is done
|
||||||
|
pprof.SetGoroutineLabels(w.ctxRun)
|
||||||
err := recover()
|
err := recover()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Recovered from panic in queue %q handler: %v\n%s", name, err, log.Stack(2))
|
log.Error("Recovered from panic in queue %q handler: %v\n%s", name, err, log.Stack(2))
|
||||||
|
13
modules/util/runtime.go
Normal file
13
modules/util/runtime.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import "runtime"
|
||||||
|
|
||||||
|
func CallerFuncName(skip int) string {
|
||||||
|
pc := make([]uintptr, 1)
|
||||||
|
runtime.Callers(skip+1, pc)
|
||||||
|
funcName := runtime.FuncForPC(pc[0]).Name()
|
||||||
|
return funcName
|
||||||
|
}
|
32
modules/util/runtime_test.go
Normal file
32
modules/util/runtime_test.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCallerFuncName(t *testing.T) {
|
||||||
|
s := CallerFuncName(1)
|
||||||
|
assert.Equal(t, "code.gitea.io/gitea/modules/util.TestCallerFuncName", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkCallerFuncName(b *testing.B) {
|
||||||
|
// BenchmarkCaller/sprintf-12 12744829 95.49 ns/op
|
||||||
|
b.Run("sprintf", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = fmt.Sprintf("aaaaaaaaaaaaaaaa %s %s %s", "bbbbbbbbbbbbbbbbbbb", b.Name(), "ccccccccccccccccccccc")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// BenchmarkCaller/caller-12 10625133 113.6 ns/op
|
||||||
|
// It is almost as fast as fmt.Sprintf
|
||||||
|
b.Run("caller", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
CallerFuncName(1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -25,7 +25,7 @@ type timeStrGlobalVarsType struct {
|
|||||||
// In the future, it could be some configurable options to help users
|
// In the future, it could be some configurable options to help users
|
||||||
// to convert the working time to different units.
|
// to convert the working time to different units.
|
||||||
|
|
||||||
var timeStrGlobalVars = sync.OnceValue[*timeStrGlobalVarsType](func() *timeStrGlobalVarsType {
|
var timeStrGlobalVars = sync.OnceValue(func() *timeStrGlobalVarsType {
|
||||||
v := &timeStrGlobalVarsType{}
|
v := &timeStrGlobalVarsType{}
|
||||||
v.re = regexp.MustCompile(`(?i)(\d+)\s*([hms])`)
|
v.re = regexp.MustCompile(`(?i)(\d+)\s*([hms])`)
|
||||||
v.units = []struct {
|
v.units = []struct {
|
||||||
|
@ -242,10 +242,10 @@ func TestReserveLineBreakForTextarea(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOptionalArg(t *testing.T) {
|
func TestOptionalArg(t *testing.T) {
|
||||||
foo := func(other any, optArg ...int) int {
|
foo := func(_ any, optArg ...int) int {
|
||||||
return OptionalArg(optArg)
|
return OptionalArg(optArg)
|
||||||
}
|
}
|
||||||
bar := func(other any, optArg ...int) int {
|
bar := func(_ any, optArg ...int) int {
|
||||||
return OptionalArg(optArg, 42)
|
return OptionalArg(optArg, 42)
|
||||||
}
|
}
|
||||||
assert.Equal(t, 0, foo(nil))
|
assert.Equal(t, 0, foo(nil))
|
||||||
|
@ -1669,12 +1669,25 @@ issues.delete.title=このイシューを削除しますか?
|
|||||||
issues.delete.text=本当にこのイシューを削除しますか? (これはすべてのコンテンツを完全に削除します。 保存しておきたい場合は、代わりにクローズすることを検討してください)
|
issues.delete.text=本当にこのイシューを削除しますか? (これはすべてのコンテンツを完全に削除します。 保存しておきたい場合は、代わりにクローズすることを検討してください)
|
||||||
|
|
||||||
issues.tracker=タイムトラッカー
|
issues.tracker=タイムトラッカー
|
||||||
|
issues.timetracker_timer_start=タイマー開始
|
||||||
|
issues.timetracker_timer_stop=タイマー終了
|
||||||
|
issues.timetracker_timer_discard=タイマー破棄
|
||||||
|
issues.timetracker_timer_manually_add=時間を追加
|
||||||
|
|
||||||
|
issues.time_estimate_set=見積時間を設定
|
||||||
|
issues.time_estimate_display=見積時間: %s
|
||||||
|
issues.change_time_estimate_at=が見積時間を <b>%s</b> に変更 %s
|
||||||
|
issues.remove_time_estimate_at=が見積時間を削除 %s
|
||||||
|
issues.time_estimate_invalid=見積時間のフォーマットが不正です
|
||||||
|
issues.start_tracking_history=が作業を開始 %s
|
||||||
issues.tracker_auto_close=タイマーは、このイシューがクローズされると自動的に終了します
|
issues.tracker_auto_close=タイマーは、このイシューがクローズされると自動的に終了します
|
||||||
issues.tracking_already_started=`<a href="%s">別のイシュー</a>で既にタイムトラッキングを開始しています!`
|
issues.tracking_already_started=`<a href="%s">別のイシュー</a>で既にタイムトラッキングを開始しています!`
|
||||||
|
issues.stop_tracking_history=が <b>%s</b> の作業を終了 %s
|
||||||
issues.cancel_tracking_history=`がタイムトラッキングを中止 %s`
|
issues.cancel_tracking_history=`がタイムトラッキングを中止 %s`
|
||||||
issues.del_time=このタイムログを削除
|
issues.del_time=このタイムログを削除
|
||||||
|
issues.add_time_history=が作業時間 <b>%s</b> を追加 %s
|
||||||
issues.del_time_history=`が作業時間を削除 %s`
|
issues.del_time_history=`が作業時間を削除 %s`
|
||||||
|
issues.add_time_manually=時間の手入力
|
||||||
issues.add_time_hours=時間
|
issues.add_time_hours=時間
|
||||||
issues.add_time_minutes=分
|
issues.add_time_minutes=分
|
||||||
issues.add_time_sum_to_small=時間が入力されていません。
|
issues.add_time_sum_to_small=時間が入力されていません。
|
||||||
@ -1694,15 +1707,15 @@ issues.due_date_form_add=期日の追加
|
|||||||
issues.due_date_form_edit=変更
|
issues.due_date_form_edit=変更
|
||||||
issues.due_date_form_remove=削除
|
issues.due_date_form_remove=削除
|
||||||
issues.due_date_not_writer=イシューの期日を変更するには、リポジトリへの書き込み権限が必要です。
|
issues.due_date_not_writer=イシューの期日を変更するには、リポジトリへの書き込み権限が必要です。
|
||||||
issues.due_date_not_set=期日は未設定です。
|
issues.due_date_not_set=期日は設定されていません。
|
||||||
issues.due_date_added=が期日 %s を追加 %s
|
issues.due_date_added=が期日 %s を追加 %s
|
||||||
issues.due_date_modified=が期日を %[2]s から %[1]s に変更 %[3]s
|
issues.due_date_modified=が期日を %[2]s から %[1]s に変更 %[3]s
|
||||||
issues.due_date_remove=が期日 %s を削除 %s
|
issues.due_date_remove=が期日 %s を削除 %s
|
||||||
issues.due_date_overdue=期日は過ぎています
|
issues.due_date_overdue=期日は過ぎています
|
||||||
issues.due_date_invalid=期日が正しくないか範囲を超えています。 'yyyy-mm-dd' の形式で入力してください。
|
issues.due_date_invalid=期日が正しくないか範囲を超えています。 'yyyy-mm-dd' の形式で入力してください。
|
||||||
issues.dependency.title=依存関係
|
issues.dependency.title=依存関係
|
||||||
issues.dependency.issue_no_dependencies=依存関係が設定されていません。
|
issues.dependency.issue_no_dependencies=依存関係は設定されていません。
|
||||||
issues.dependency.pr_no_dependencies=依存関係が設定されていません。
|
issues.dependency.pr_no_dependencies=依存関係は設定されていません。
|
||||||
issues.dependency.no_permission_1=%d 個の依存関係への読み取り権限がありません
|
issues.dependency.no_permission_1=%d 個の依存関係への読み取り権限がありません
|
||||||
issues.dependency.no_permission_n=%d 個の依存関係への読み取り権限がありません
|
issues.dependency.no_permission_n=%d 個の依存関係への読み取り権限がありません
|
||||||
issues.dependency.no_permission.can_remove=この依存関係への読み取り権限はありませんが、この依存関係は削除できます
|
issues.dependency.no_permission.can_remove=この依存関係への読み取り権限はありませんが、この依存関係は削除できます
|
||||||
|
2134
package-lock.json
generated
2134
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
32
package.json
32
package.json
@ -12,7 +12,7 @@
|
|||||||
"@github/relative-time-element": "4.4.4",
|
"@github/relative-time-element": "4.4.4",
|
||||||
"@github/text-expander-element": "2.8.0",
|
"@github/text-expander-element": "2.8.0",
|
||||||
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
|
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
|
||||||
"@primer/octicons": "19.13.0",
|
"@primer/octicons": "19.14.0",
|
||||||
"@silverwind/vue3-calendar-heatmap": "2.0.6",
|
"@silverwind/vue3-calendar-heatmap": "2.0.6",
|
||||||
"add-asset-webpack-plugin": "3.0.0",
|
"add-asset-webpack-plugin": "3.0.0",
|
||||||
"ansi_up": "6.0.2",
|
"ansi_up": "6.0.2",
|
||||||
@ -32,12 +32,12 @@
|
|||||||
"htmx.org": "2.0.4",
|
"htmx.org": "2.0.4",
|
||||||
"idiomorph": "0.3.0",
|
"idiomorph": "0.3.0",
|
||||||
"jquery": "3.7.1",
|
"jquery": "3.7.1",
|
||||||
"katex": "0.16.11",
|
"katex": "0.16.18",
|
||||||
"license-checker-webpack-plugin": "0.2.1",
|
"license-checker-webpack-plugin": "0.2.1",
|
||||||
"mermaid": "11.4.1",
|
"mermaid": "11.4.1",
|
||||||
"mini-css-extract-plugin": "2.9.2",
|
"mini-css-extract-plugin": "2.9.2",
|
||||||
"minimatch": "10.0.1",
|
"minimatch": "10.0.1",
|
||||||
"monaco-editor": "0.52.0",
|
"monaco-editor": "0.52.2",
|
||||||
"monaco-editor-webpack-plugin": "7.1.0",
|
"monaco-editor-webpack-plugin": "7.1.0",
|
||||||
"pdfobject": "2.3.0",
|
"pdfobject": "2.3.0",
|
||||||
"perfect-debounce": "1.0.0",
|
"perfect-debounce": "1.0.0",
|
||||||
@ -46,7 +46,7 @@
|
|||||||
"postcss-nesting": "13.0.1",
|
"postcss-nesting": "13.0.1",
|
||||||
"sortablejs": "1.15.6",
|
"sortablejs": "1.15.6",
|
||||||
"swagger-ui-dist": "5.18.2",
|
"swagger-ui-dist": "5.18.2",
|
||||||
"tailwindcss": "3.4.16",
|
"tailwindcss": "3.4.17",
|
||||||
"throttle-debounce": "5.0.2",
|
"throttle-debounce": "5.0.2",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tippy.js": "6.3.7",
|
"tippy.js": "6.3.7",
|
||||||
@ -59,16 +59,16 @@
|
|||||||
"vue-bar-graph": "2.2.0",
|
"vue-bar-graph": "2.2.0",
|
||||||
"vue-chartjs": "5.3.2",
|
"vue-chartjs": "5.3.2",
|
||||||
"vue-loader": "17.4.2",
|
"vue-loader": "17.4.2",
|
||||||
"webpack": "5.97.0",
|
"webpack": "5.97.1",
|
||||||
"webpack-cli": "5.1.4",
|
"webpack-cli": "5.1.4",
|
||||||
"wrap-ansi": "9.0.0"
|
"wrap-ansi": "9.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint-community/eslint-plugin-eslint-comments": "4.4.1",
|
"@eslint-community/eslint-plugin-eslint-comments": "4.4.1",
|
||||||
"@playwright/test": "1.49.0",
|
"@playwright/test": "1.49.1",
|
||||||
"@silverwind/vue-tsc": "2.1.13",
|
"@silverwind/vue-tsc": "2.1.13",
|
||||||
"@stoplight/spectral-cli": "6.14.2",
|
"@stoplight/spectral-cli": "6.14.2",
|
||||||
"@stylistic/eslint-plugin-js": "2.11.0",
|
"@stylistic/eslint-plugin-js": "2.12.1",
|
||||||
"@stylistic/stylelint-plugin": "3.1.1",
|
"@stylistic/stylelint-plugin": "3.1.1",
|
||||||
"@types/dropzone": "5.7.9",
|
"@types/dropzone": "5.7.9",
|
||||||
"@types/jquery": "3.5.32",
|
"@types/jquery": "3.5.32",
|
||||||
@ -80,19 +80,19 @@
|
|||||||
"@types/throttle-debounce": "5.0.2",
|
"@types/throttle-debounce": "5.0.2",
|
||||||
"@types/tinycolor2": "1.4.6",
|
"@types/tinycolor2": "1.4.6",
|
||||||
"@types/toastify-js": "1.12.3",
|
"@types/toastify-js": "1.12.3",
|
||||||
"@typescript-eslint/eslint-plugin": "8.17.0",
|
"@typescript-eslint/eslint-plugin": "8.18.1",
|
||||||
"@typescript-eslint/parser": "8.17.0",
|
"@typescript-eslint/parser": "8.18.1",
|
||||||
"@vitejs/plugin-vue": "5.2.1",
|
"@vitejs/plugin-vue": "5.2.1",
|
||||||
"eslint": "8.57.0",
|
"eslint": "8.57.0",
|
||||||
"eslint-import-resolver-typescript": "3.7.0",
|
"eslint-import-resolver-typescript": "3.7.0",
|
||||||
"eslint-plugin-array-func": "4.0.0",
|
"eslint-plugin-array-func": "4.0.0",
|
||||||
"eslint-plugin-github": "5.1.3",
|
"eslint-plugin-github": "5.1.4",
|
||||||
"eslint-plugin-import-x": "4.5.0",
|
"eslint-plugin-import-x": "4.6.1",
|
||||||
"eslint-plugin-no-jquery": "3.1.0",
|
"eslint-plugin-no-jquery": "3.1.0",
|
||||||
"eslint-plugin-no-use-extend-native": "0.5.0",
|
"eslint-plugin-no-use-extend-native": "0.5.0",
|
||||||
"eslint-plugin-playwright": "2.1.0",
|
"eslint-plugin-playwright": "2.1.0",
|
||||||
"eslint-plugin-regexp": "2.7.0",
|
"eslint-plugin-regexp": "2.7.0",
|
||||||
"eslint-plugin-sonarjs": "2.0.4",
|
"eslint-plugin-sonarjs": "3.0.1",
|
||||||
"eslint-plugin-unicorn": "56.0.1",
|
"eslint-plugin-unicorn": "56.0.1",
|
||||||
"eslint-plugin-vitest": "0.4.1",
|
"eslint-plugin-vitest": "0.4.1",
|
||||||
"eslint-plugin-vitest-globals": "1.5.0",
|
"eslint-plugin-vitest-globals": "1.5.0",
|
||||||
@ -101,15 +101,15 @@
|
|||||||
"eslint-plugin-wc": "2.2.0",
|
"eslint-plugin-wc": "2.2.0",
|
||||||
"happy-dom": "15.11.7",
|
"happy-dom": "15.11.7",
|
||||||
"markdownlint-cli": "0.43.0",
|
"markdownlint-cli": "0.43.0",
|
||||||
"nolyfill": "1.0.42",
|
"nolyfill": "1.0.43",
|
||||||
"postcss-html": "1.7.0",
|
"postcss-html": "1.7.0",
|
||||||
"stylelint": "16.11.0",
|
"stylelint": "16.12.0",
|
||||||
"stylelint-declaration-block-no-ignored-properties": "2.8.0",
|
"stylelint-declaration-block-no-ignored-properties": "2.8.0",
|
||||||
"stylelint-declaration-strict-value": "1.10.6",
|
"stylelint-declaration-strict-value": "1.10.6",
|
||||||
"stylelint-value-no-unknown-custom-properties": "6.0.1",
|
"stylelint-value-no-unknown-custom-properties": "6.0.1",
|
||||||
"svgo": "3.3.2",
|
"svgo": "3.3.2",
|
||||||
"type-fest": "4.30.0",
|
"type-fest": "4.30.2",
|
||||||
"updates": "16.4.0",
|
"updates": "16.4.1",
|
||||||
"vite-string-plugin": "1.3.4",
|
"vite-string-plugin": "1.3.4",
|
||||||
"vitest": "2.1.8"
|
"vitest": "2.1.8"
|
||||||
},
|
},
|
||||||
|
@ -9,10 +9,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||||
"code.gitea.io/gitea/models/auth"
|
"code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
org_model "code.gitea.io/gitea/models/organization"
|
||||||
|
packages_model "code.gitea.io/gitea/models/packages"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/auth/password"
|
"code.gitea.io/gitea/modules/auth/password"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
@ -247,7 +249,7 @@ func EditUser(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := user_service.UpdateUser(ctx, ctx.ContextUser, opts); err != nil {
|
if err := user_service.UpdateUser(ctx, ctx.ContextUser, opts); err != nil {
|
||||||
if models.IsErrDeleteLastAdminUser(err) {
|
if user_model.IsErrDeleteLastAdminUser(err) {
|
||||||
ctx.Error(http.StatusBadRequest, "LastAdmin", err)
|
ctx.Error(http.StatusBadRequest, "LastAdmin", err)
|
||||||
} else {
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, "UpdateUser", err)
|
ctx.Error(http.StatusInternalServerError, "UpdateUser", err)
|
||||||
@ -299,10 +301,10 @@ func DeleteUser(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := user_service.DeleteUser(ctx, ctx.ContextUser, ctx.FormBool("purge")); err != nil {
|
if err := user_service.DeleteUser(ctx, ctx.ContextUser, ctx.FormBool("purge")); err != nil {
|
||||||
if models.IsErrUserOwnRepos(err) ||
|
if repo_model.IsErrUserOwnRepos(err) ||
|
||||||
models.IsErrUserHasOrgs(err) ||
|
org_model.IsErrUserHasOrgs(err) ||
|
||||||
models.IsErrUserOwnPackages(err) ||
|
packages_model.IsErrUserOwnPackages(err) ||
|
||||||
models.IsErrDeleteLastAdminUser(err) {
|
user_model.IsErrDeleteLastAdminUser(err) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||||
} else {
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, "DeleteUser", err)
|
ctx.Error(http.StatusInternalServerError, "DeleteUser", err)
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
@ -24,6 +23,7 @@ import (
|
|||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
"code.gitea.io/gitea/services/convert"
|
"code.gitea.io/gitea/services/convert"
|
||||||
pull_service "code.gitea.io/gitea/services/pull"
|
pull_service "code.gitea.io/gitea/services/pull"
|
||||||
|
release_service "code.gitea.io/gitea/services/release"
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -247,7 +247,7 @@ func CreateBranch(ctx *context.APIContext) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if git_model.IsErrBranchNotExist(err) {
|
if git_model.IsErrBranchNotExist(err) {
|
||||||
ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
|
ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
|
||||||
} else if models.IsErrTagAlreadyExists(err) {
|
} else if release_service.IsErrTagAlreadyExists(err) {
|
||||||
ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.")
|
ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.")
|
||||||
} else if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
} else if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
||||||
ctx.Error(http.StatusConflict, "", "The branch already exists.")
|
ctx.Error(http.StatusConflict, "", "The branch already exists.")
|
||||||
|
@ -15,7 +15,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
@ -30,6 +29,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/routers/common"
|
"code.gitea.io/gitea/routers/common"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
|
pull_service "code.gitea.io/gitea/services/pull"
|
||||||
archiver_service "code.gitea.io/gitea/services/repository/archiver"
|
archiver_service "code.gitea.io/gitea/services/repository/archiver"
|
||||||
files_service "code.gitea.io/gitea/services/repository/files"
|
files_service "code.gitea.io/gitea/services/repository/files"
|
||||||
)
|
)
|
||||||
@ -736,12 +736,12 @@ func UpdateFile(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleCreateOrUpdateFileError(ctx *context.APIContext, err error) {
|
func handleCreateOrUpdateFileError(ctx *context.APIContext, err error) {
|
||||||
if models.IsErrUserCannotCommit(err) || models.IsErrFilePathProtected(err) {
|
if files_service.IsErrUserCannotCommit(err) || pull_service.IsErrFilePathProtected(err) {
|
||||||
ctx.Error(http.StatusForbidden, "Access", err)
|
ctx.Error(http.StatusForbidden, "Access", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if git_model.IsErrBranchAlreadyExists(err) || models.IsErrFilenameInvalid(err) || models.IsErrSHADoesNotMatch(err) ||
|
if git_model.IsErrBranchAlreadyExists(err) || files_service.IsErrFilenameInvalid(err) || pull_service.IsErrSHADoesNotMatch(err) ||
|
||||||
models.IsErrFilePathInvalid(err) || models.IsErrRepoFileAlreadyExists(err) {
|
files_service.IsErrFilePathInvalid(err) || files_service.IsErrRepoFileAlreadyExists(err) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "Invalid", err)
|
ctx.Error(http.StatusUnprocessableEntity, "Invalid", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -887,17 +887,17 @@ func DeleteFile(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if filesResponse, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, opts); err != nil {
|
if filesResponse, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, opts); err != nil {
|
||||||
if git.IsErrBranchNotExist(err) || models.IsErrRepoFileDoesNotExist(err) || git.IsErrNotExist(err) {
|
if git.IsErrBranchNotExist(err) || files_service.IsErrRepoFileDoesNotExist(err) || git.IsErrNotExist(err) {
|
||||||
ctx.Error(http.StatusNotFound, "DeleteFile", err)
|
ctx.Error(http.StatusNotFound, "DeleteFile", err)
|
||||||
return
|
return
|
||||||
} else if git_model.IsErrBranchAlreadyExists(err) ||
|
} else if git_model.IsErrBranchAlreadyExists(err) ||
|
||||||
models.IsErrFilenameInvalid(err) ||
|
files_service.IsErrFilenameInvalid(err) ||
|
||||||
models.IsErrSHADoesNotMatch(err) ||
|
pull_service.IsErrSHADoesNotMatch(err) ||
|
||||||
models.IsErrCommitIDDoesNotMatch(err) ||
|
files_service.IsErrCommitIDDoesNotMatch(err) ||
|
||||||
models.IsErrSHAOrCommitIDNotProvided(err) {
|
files_service.IsErrSHAOrCommitIDNotProvided(err) {
|
||||||
ctx.Error(http.StatusBadRequest, "DeleteFile", err)
|
ctx.Error(http.StatusBadRequest, "DeleteFile", err)
|
||||||
return
|
return
|
||||||
} else if models.IsErrUserCannotCommit(err) {
|
} else if files_service.IsErrUserCannotCommit(err) {
|
||||||
ctx.Error(http.StatusForbidden, "DeleteFile", err)
|
ctx.Error(http.StatusForbidden, "DeleteFile", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -10,13 +10,13 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
access_model "code.gitea.io/gitea/models/perm/access"
|
access_model "code.gitea.io/gitea/models/perm/access"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/graceful"
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
"code.gitea.io/gitea/modules/lfs"
|
"code.gitea.io/gitea/modules/lfs"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
@ -27,7 +27,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
"code.gitea.io/gitea/services/convert"
|
"code.gitea.io/gitea/services/convert"
|
||||||
"code.gitea.io/gitea/services/forms"
|
|
||||||
"code.gitea.io/gitea/services/migrations"
|
"code.gitea.io/gitea/services/migrations"
|
||||||
notify_service "code.gitea.io/gitea/services/notify"
|
notify_service "code.gitea.io/gitea/services/notify"
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
@ -104,7 +103,7 @@ func Migrate(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteAddr, err := forms.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword)
|
remoteAddr, err := git.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = migrations.IsMigrateURLAllowed(remoteAddr, ctx.Doer)
|
err = migrations.IsMigrateURLAllowed(remoteAddr, ctx.Doer)
|
||||||
}
|
}
|
||||||
@ -237,7 +236,7 @@ func handleMigrateError(ctx *context.APIContext, repoOwner *user_model.User, err
|
|||||||
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' contains invalid characters.", err.(db.ErrNameCharsNotAllowed).Name))
|
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' contains invalid characters.", err.(db.ErrNameCharsNotAllowed).Name))
|
||||||
case db.IsErrNamePatternNotAllowed(err):
|
case db.IsErrNamePatternNotAllowed(err):
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(db.ErrNamePatternNotAllowed).Pattern))
|
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(db.ErrNamePatternNotAllowed).Pattern))
|
||||||
case models.IsErrInvalidCloneAddr(err):
|
case git.IsErrInvalidCloneAddr(err):
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||||
case base.IsErrNotSupported(err):
|
case base.IsErrNotSupported(err):
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||||
@ -256,8 +255,8 @@ func handleMigrateError(ctx *context.APIContext, repoOwner *user_model.User, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleRemoteAddrError(ctx *context.APIContext, err error) {
|
func handleRemoteAddrError(ctx *context.APIContext, err error) {
|
||||||
if models.IsErrInvalidCloneAddr(err) {
|
if git.IsErrInvalidCloneAddr(err) {
|
||||||
addrErr := err.(*models.ErrInvalidCloneAddr)
|
addrErr := err.(*git.ErrInvalidCloneAddr)
|
||||||
switch {
|
switch {
|
||||||
case addrErr.IsURLError:
|
case addrErr.IsURLError:
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||||
|
@ -9,10 +9,10 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
@ -20,7 +20,6 @@ import (
|
|||||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
"code.gitea.io/gitea/services/convert"
|
"code.gitea.io/gitea/services/convert"
|
||||||
"code.gitea.io/gitea/services/forms"
|
|
||||||
"code.gitea.io/gitea/services/migrations"
|
"code.gitea.io/gitea/services/migrations"
|
||||||
mirror_service "code.gitea.io/gitea/services/mirror"
|
mirror_service "code.gitea.io/gitea/services/mirror"
|
||||||
)
|
)
|
||||||
@ -344,7 +343,7 @@ func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirro
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
address, err := forms.ParseRemoteAddr(mirrorOption.RemoteAddress, mirrorOption.RemoteUsername, mirrorOption.RemotePassword)
|
address, err := git.ParseRemoteAddr(mirrorOption.RemoteAddress, mirrorOption.RemoteUsername, mirrorOption.RemotePassword)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = migrations.IsMigrateURLAllowed(address, ctx.ContextUser)
|
err = migrations.IsMigrateURLAllowed(address, ctx.ContextUser)
|
||||||
}
|
}
|
||||||
@ -397,8 +396,8 @@ func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func HandleRemoteAddressError(ctx *context.APIContext, err error) {
|
func HandleRemoteAddressError(ctx *context.APIContext, err error) {
|
||||||
if models.IsErrInvalidCloneAddr(err) {
|
if git.IsErrInvalidCloneAddr(err) {
|
||||||
addrErr := err.(*models.ErrInvalidCloneAddr)
|
addrErr := err.(*git.ErrInvalidCloneAddr)
|
||||||
switch {
|
switch {
|
||||||
case addrErr.IsProtocolInvalid:
|
case addrErr.IsProtocolInvalid:
|
||||||
ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Invalid mirror protocol")
|
ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Invalid mirror protocol")
|
||||||
|
@ -7,13 +7,13 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
|
pull_service "code.gitea.io/gitea/services/pull"
|
||||||
"code.gitea.io/gitea/services/repository/files"
|
"code.gitea.io/gitea/services/repository/files"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -92,12 +92,12 @@ func ApplyDiffPatch(ctx *context.APIContext) {
|
|||||||
|
|
||||||
fileResponse, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, opts)
|
fileResponse, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrUserCannotCommit(err) || models.IsErrFilePathProtected(err) {
|
if files.IsErrUserCannotCommit(err) || pull_service.IsErrFilePathProtected(err) {
|
||||||
ctx.Error(http.StatusForbidden, "Access", err)
|
ctx.Error(http.StatusForbidden, "Access", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if git_model.IsErrBranchAlreadyExists(err) || models.IsErrFilenameInvalid(err) || models.IsErrSHADoesNotMatch(err) ||
|
if git_model.IsErrBranchAlreadyExists(err) || files.IsErrFilenameInvalid(err) || pull_service.IsErrSHADoesNotMatch(err) ||
|
||||||
models.IsErrFilePathInvalid(err) || models.IsErrRepoFileAlreadyExists(err) {
|
files.IsErrFilePathInvalid(err) || files.IsErrRepoFileAlreadyExists(err) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "Invalid", err)
|
ctx.Error(http.StatusUnprocessableEntity, "Invalid", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
activities_model "code.gitea.io/gitea/models/activities"
|
activities_model "code.gitea.io/gitea/models/activities"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
@ -765,7 +764,7 @@ func EditPullRequest(ctx *context.APIContext) {
|
|||||||
} else if issues_model.IsErrIssueIsClosed(err) {
|
} else if issues_model.IsErrIssueIsClosed(err) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "IsErrIssueIsClosed", err)
|
ctx.Error(http.StatusUnprocessableEntity, "IsErrIssueIsClosed", err)
|
||||||
return
|
return
|
||||||
} else if models.IsErrPullRequestHasMerged(err) {
|
} else if pull_service.IsErrPullRequestHasMerged(err) {
|
||||||
ctx.Error(http.StatusConflict, "IsErrPullRequestHasMerged", err)
|
ctx.Error(http.StatusConflict, "IsErrPullRequestHasMerged", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -941,7 +940,7 @@ func MergePullRequest(ctx *context.APIContext) {
|
|||||||
ctx.Error(http.StatusMethodNotAllowed, "PR is a work in progress", "Work in progress PRs cannot be merged")
|
ctx.Error(http.StatusMethodNotAllowed, "PR is a work in progress", "Work in progress PRs cannot be merged")
|
||||||
} else if errors.Is(err, pull_service.ErrNotMergeableState) {
|
} else if errors.Is(err, pull_service.ErrNotMergeableState) {
|
||||||
ctx.Error(http.StatusMethodNotAllowed, "PR not in mergeable state", "Please try again later")
|
ctx.Error(http.StatusMethodNotAllowed, "PR not in mergeable state", "Please try again later")
|
||||||
} else if models.IsErrDisallowedToMerge(err) {
|
} else if pull_service.IsErrDisallowedToMerge(err) {
|
||||||
ctx.Error(http.StatusMethodNotAllowed, "PR is not ready to be merged", err)
|
ctx.Error(http.StatusMethodNotAllowed, "PR is not ready to be merged", err)
|
||||||
} else if asymkey_service.IsErrWontSign(err) {
|
} else if asymkey_service.IsErrWontSign(err) {
|
||||||
ctx.Error(http.StatusMethodNotAllowed, fmt.Sprintf("Protected branch %s requires signed commits but this merge would not be signed", pr.BaseBranch), err)
|
ctx.Error(http.StatusMethodNotAllowed, fmt.Sprintf("Protected branch %s requires signed commits but this merge would not be signed", pr.BaseBranch), err)
|
||||||
@ -954,7 +953,7 @@ func MergePullRequest(ctx *context.APIContext) {
|
|||||||
// handle manually-merged mark
|
// handle manually-merged mark
|
||||||
if manuallyMerged {
|
if manuallyMerged {
|
||||||
if err := pull_service.MergedManually(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
|
if err := pull_service.MergedManually(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
|
||||||
if models.IsErrInvalidMergeStyle(err) {
|
if pull_service.IsErrInvalidMergeStyle(err) {
|
||||||
ctx.Error(http.StatusMethodNotAllowed, "Invalid merge style", fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do)))
|
ctx.Error(http.StatusMethodNotAllowed, "Invalid merge style", fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1004,20 +1003,20 @@ func MergePullRequest(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := pull_service.Merge(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, message, false); err != nil {
|
if err := pull_service.Merge(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, message, false); err != nil {
|
||||||
if models.IsErrInvalidMergeStyle(err) {
|
if pull_service.IsErrInvalidMergeStyle(err) {
|
||||||
ctx.Error(http.StatusMethodNotAllowed, "Invalid merge style", fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do)))
|
ctx.Error(http.StatusMethodNotAllowed, "Invalid merge style", fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do)))
|
||||||
} else if models.IsErrMergeConflicts(err) {
|
} else if pull_service.IsErrMergeConflicts(err) {
|
||||||
conflictError := err.(models.ErrMergeConflicts)
|
conflictError := err.(pull_service.ErrMergeConflicts)
|
||||||
ctx.JSON(http.StatusConflict, conflictError)
|
ctx.JSON(http.StatusConflict, conflictError)
|
||||||
} else if models.IsErrRebaseConflicts(err) {
|
} else if pull_service.IsErrRebaseConflicts(err) {
|
||||||
conflictError := err.(models.ErrRebaseConflicts)
|
conflictError := err.(pull_service.ErrRebaseConflicts)
|
||||||
ctx.JSON(http.StatusConflict, conflictError)
|
ctx.JSON(http.StatusConflict, conflictError)
|
||||||
} else if models.IsErrMergeUnrelatedHistories(err) {
|
} else if pull_service.IsErrMergeUnrelatedHistories(err) {
|
||||||
conflictError := err.(models.ErrMergeUnrelatedHistories)
|
conflictError := err.(pull_service.ErrMergeUnrelatedHistories)
|
||||||
ctx.JSON(http.StatusConflict, conflictError)
|
ctx.JSON(http.StatusConflict, conflictError)
|
||||||
} else if git.IsErrPushOutOfDate(err) {
|
} else if git.IsErrPushOutOfDate(err) {
|
||||||
ctx.Error(http.StatusConflict, "Merge", "merge push out of date")
|
ctx.Error(http.StatusConflict, "Merge", "merge push out of date")
|
||||||
} else if models.IsErrSHADoesNotMatch(err) {
|
} else if pull_service.IsErrSHADoesNotMatch(err) {
|
||||||
ctx.Error(http.StatusConflict, "Merge", "head out of date")
|
ctx.Error(http.StatusConflict, "Merge", "head out of date")
|
||||||
} else if git.IsErrPushRejected(err) {
|
} else if git.IsErrPushRejected(err) {
|
||||||
errPushRej := err.(*git.ErrPushRejected)
|
errPushRej := err.(*git.ErrPushRejected)
|
||||||
@ -1308,10 +1307,10 @@ func UpdatePullRequest(ctx *context.APIContext) {
|
|||||||
message := fmt.Sprintf("Merge branch '%s' into %s", pr.BaseBranch, pr.HeadBranch)
|
message := fmt.Sprintf("Merge branch '%s' into %s", pr.BaseBranch, pr.HeadBranch)
|
||||||
|
|
||||||
if err = pull_service.Update(ctx, pr, ctx.Doer, message, rebase); err != nil {
|
if err = pull_service.Update(ctx, pr, ctx.Doer, message, rebase); err != nil {
|
||||||
if models.IsErrMergeConflicts(err) {
|
if pull_service.IsErrMergeConflicts(err) {
|
||||||
ctx.Error(http.StatusConflict, "Update", "merge failed because of conflict")
|
ctx.Error(http.StatusConflict, "Update", "merge failed because of conflict")
|
||||||
return
|
return
|
||||||
} else if models.IsErrRebaseConflicts(err) {
|
} else if pull_service.IsErrRebaseConflicts(err) {
|
||||||
ctx.Error(http.StatusConflict, "Update", "rebase failed because of conflict")
|
ctx.Error(http.StatusConflict, "Update", "rebase failed because of conflict")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
@ -250,7 +249,7 @@ func CreateRelease(ctx *context.APIContext) {
|
|||||||
if err := release_service.CreateRelease(ctx.Repo.GitRepo, rel, nil, ""); err != nil {
|
if err := release_service.CreateRelease(ctx.Repo.GitRepo, rel, nil, ""); err != nil {
|
||||||
if repo_model.IsErrReleaseAlreadyExist(err) {
|
if repo_model.IsErrReleaseAlreadyExist(err) {
|
||||||
ctx.Error(http.StatusConflict, "ReleaseAlreadyExist", err)
|
ctx.Error(http.StatusConflict, "ReleaseAlreadyExist", err)
|
||||||
} else if models.IsErrProtectedTagName(err) {
|
} else if release_service.IsErrProtectedTagName(err) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "ProtectedTagName", err)
|
ctx.Error(http.StatusUnprocessableEntity, "ProtectedTagName", err)
|
||||||
} else if git.IsErrNotExist(err) {
|
} else if git.IsErrNotExist(err) {
|
||||||
ctx.Error(http.StatusNotFound, "ErrNotExist", fmt.Errorf("target \"%v\" not found: %w", rel.Target, err))
|
ctx.Error(http.StatusNotFound, "ErrNotExist", fmt.Errorf("target \"%v\" not found: %w", rel.Target, err))
|
||||||
@ -408,7 +407,7 @@ func DeleteRelease(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := release_service.DeleteReleaseByID(ctx, ctx.Repo.Repository, rel, ctx.Doer, false); err != nil {
|
if err := release_service.DeleteReleaseByID(ctx, ctx.Repo.Repository, rel, ctx.Doer, false); err != nil {
|
||||||
if models.IsErrProtectedTagName(err) {
|
if release_service.IsErrProtectedTagName(err) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "delTag", "user not allowed to delete protected tag")
|
ctx.Error(http.StatusUnprocessableEntity, "delTag", "user not allowed to delete protected tag")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,10 @@ package repo
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
"code.gitea.io/gitea/services/convert"
|
"code.gitea.io/gitea/services/convert"
|
||||||
releaseservice "code.gitea.io/gitea/services/release"
|
release_service "code.gitea.io/gitea/services/release"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetReleaseByTag get a single release of a repository by tag name
|
// GetReleaseByTag get a single release of a repository by tag name
|
||||||
@ -112,8 +111,8 @@ func DeleteReleaseByTag(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = releaseservice.DeleteReleaseByID(ctx, ctx.Repo.Repository, release, ctx.Doer, false); err != nil {
|
if err = release_service.DeleteReleaseByID(ctx, ctx.Repo.Repository, release, ctx.Doer, false); err != nil {
|
||||||
if models.IsErrProtectedTagName(err) {
|
if release_service.IsErrProtectedTagName(err) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "delTag", "user not allowed to delete protected tag")
|
ctx.Error(http.StatusUnprocessableEntity, "delTag", "user not allowed to delete protected tag")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
@ -19,7 +18,7 @@ import (
|
|||||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
"code.gitea.io/gitea/services/convert"
|
"code.gitea.io/gitea/services/convert"
|
||||||
releaseservice "code.gitea.io/gitea/services/release"
|
release_service "code.gitea.io/gitea/services/release"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListTags list all the tags of a repository
|
// ListTags list all the tags of a repository
|
||||||
@ -205,12 +204,12 @@ func CreateTag(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := releaseservice.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, commit.ID.String(), form.TagName, form.Message); err != nil {
|
if err := release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, commit.ID.String(), form.TagName, form.Message); err != nil {
|
||||||
if models.IsErrTagAlreadyExists(err) {
|
if release_service.IsErrTagAlreadyExists(err) {
|
||||||
ctx.Error(http.StatusConflict, "tag exist", err)
|
ctx.Error(http.StatusConflict, "tag exist", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if models.IsErrProtectedTagName(err) {
|
if release_service.IsErrProtectedTagName(err) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "CreateNewTag", "user not allowed to create protected tag")
|
ctx.Error(http.StatusUnprocessableEntity, "CreateNewTag", "user not allowed to create protected tag")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -280,8 +279,8 @@ func DeleteTag(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = releaseservice.DeleteReleaseByID(ctx, ctx.Repo.Repository, tag, ctx.Doer, true); err != nil {
|
if err = release_service.DeleteReleaseByID(ctx, ctx.Repo.Repository, tag, ctx.Doer, true); err != nil {
|
||||||
if models.IsErrProtectedTagName(err) {
|
if release_service.IsErrProtectedTagName(err) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "delTag", "user not allowed to delete protected tag")
|
ctx.Error(http.StatusUnprocessableEntity, "delTag", "user not allowed to delete protected tag")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
@ -237,7 +236,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
|
|||||||
if len(globs) > 0 {
|
if len(globs) > 0 {
|
||||||
_, err := pull_service.CheckFileProtection(gitRepo, branchName, oldCommitID, newCommitID, globs, 1, ctx.env)
|
_, err := pull_service.CheckFileProtection(gitRepo, branchName, oldCommitID, newCommitID, globs, 1, ctx.env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !models.IsErrFilePathProtected(err) {
|
if !pull_service.IsErrFilePathProtected(err) {
|
||||||
log.Error("Unable to check file protection for commits from %s to %s in %-v: %v", oldCommitID, newCommitID, repo, err)
|
log.Error("Unable to check file protection for commits from %s to %s in %-v: %v", oldCommitID, newCommitID, repo, err)
|
||||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||||
Err: fmt.Sprintf("Unable to check file protection for commits from %s to %s: %v", oldCommitID, newCommitID, err),
|
Err: fmt.Sprintf("Unable to check file protection for commits from %s to %s: %v", oldCommitID, newCommitID, err),
|
||||||
@ -246,7 +245,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
|
|||||||
}
|
}
|
||||||
|
|
||||||
changedProtectedfiles = true
|
changedProtectedfiles = true
|
||||||
protectedFilePath = err.(models.ErrFilePathProtected).Path
|
protectedFilePath = err.(pull_service.ErrFilePathProtected).Path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,7 +373,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
|
|||||||
|
|
||||||
// Check all status checks and reviews are ok
|
// Check all status checks and reviews are ok
|
||||||
if err := pull_service.CheckPullBranchProtections(ctx, pr, true); err != nil {
|
if err := pull_service.CheckPullBranchProtections(ctx, pr, true); err != nil {
|
||||||
if models.IsErrDisallowedToMerge(err) {
|
if pull_service.IsErrDisallowedToMerge(err) {
|
||||||
log.Warn("Forbidden: User %d is not allowed push to protected branch %s in %-v and pr #%d is not ready to be merged: %s", ctx.opts.UserID, branchName, repo, pr.Index, err.Error())
|
log.Warn("Forbidden: User %d is not allowed push to protected branch %s in %-v and pr #%d is not ready to be merged: %s", ctx.opts.UserID, branchName, repo, pr.Index, err.Error())
|
||||||
ctx.JSON(http.StatusForbidden, private.Response{
|
ctx.JSON(http.StatusForbidden, private.Response{
|
||||||
UserMsg: fmt.Sprintf("Not allowed to push to protected branch %s and pr #%d is not ready to be merged: %s", branchName, ctx.opts.PullRequestID, err.Error()),
|
UserMsg: fmt.Sprintf("Not allowed to push to protected branch %s and pr #%d is not ready to be merged: %s", branchName, ctx.opts.PullRequestID, err.Error()),
|
||||||
|
@ -11,10 +11,10 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/auth"
|
"code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
org_model "code.gitea.io/gitea/models/organization"
|
org_model "code.gitea.io/gitea/models/organization"
|
||||||
|
packages_model "code.gitea.io/gitea/models/packages"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/auth/password"
|
"code.gitea.io/gitea/modules/auth/password"
|
||||||
@ -446,7 +446,7 @@ func EditUserPost(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := user_service.UpdateUser(ctx, u, opts); err != nil {
|
if err := user_service.UpdateUser(ctx, u, opts); err != nil {
|
||||||
if models.IsErrDeleteLastAdminUser(err) {
|
if user_model.IsErrDeleteLastAdminUser(err) {
|
||||||
ctx.RenderWithErr(ctx.Tr("auth.last_admin"), tplUserEdit, &form)
|
ctx.RenderWithErr(ctx.Tr("auth.last_admin"), tplUserEdit, &form)
|
||||||
} else {
|
} else {
|
||||||
ctx.ServerError("UpdateUser", err)
|
ctx.ServerError("UpdateUser", err)
|
||||||
@ -501,16 +501,16 @@ func DeleteUser(ctx *context.Context) {
|
|||||||
|
|
||||||
if err = user_service.DeleteUser(ctx, u, ctx.FormBool("purge")); err != nil {
|
if err = user_service.DeleteUser(ctx, u, ctx.FormBool("purge")); err != nil {
|
||||||
switch {
|
switch {
|
||||||
case models.IsErrUserOwnRepos(err):
|
case repo_model.IsErrUserOwnRepos(err):
|
||||||
ctx.Flash.Error(ctx.Tr("admin.users.still_own_repo"))
|
ctx.Flash.Error(ctx.Tr("admin.users.still_own_repo"))
|
||||||
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
||||||
case models.IsErrUserHasOrgs(err):
|
case org_model.IsErrUserHasOrgs(err):
|
||||||
ctx.Flash.Error(ctx.Tr("admin.users.still_has_org"))
|
ctx.Flash.Error(ctx.Tr("admin.users.still_has_org"))
|
||||||
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
||||||
case models.IsErrUserOwnPackages(err):
|
case packages_model.IsErrUserOwnPackages(err):
|
||||||
ctx.Flash.Error(ctx.Tr("admin.users.still_own_packages"))
|
ctx.Flash.Error(ctx.Tr("admin.users.still_own_packages"))
|
||||||
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
||||||
case models.IsErrDeleteLastAdminUser(err):
|
case user_model.IsErrDeleteLastAdminUser(err):
|
||||||
ctx.Flash.Error(ctx.Tr("auth.last_admin"))
|
ctx.Flash.Error(ctx.Tr("auth.last_admin"))
|
||||||
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
||||||
default:
|
default:
|
||||||
|
@ -8,8 +8,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
packages_model "code.gitea.io/gitea/models/packages"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/models/webhook"
|
"code.gitea.io/gitea/models/webhook"
|
||||||
@ -178,10 +178,10 @@ func SettingsDelete(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := org_service.DeleteOrganization(ctx, ctx.Org.Organization, false); err != nil {
|
if err := org_service.DeleteOrganization(ctx, ctx.Org.Organization, false); err != nil {
|
||||||
if models.IsErrUserOwnRepos(err) {
|
if repo_model.IsErrUserOwnRepos(err) {
|
||||||
ctx.Flash.Error(ctx.Tr("form.org_still_own_repo"))
|
ctx.Flash.Error(ctx.Tr("form.org_still_own_repo"))
|
||||||
ctx.Redirect(ctx.Org.OrgLink + "/settings/delete")
|
ctx.Redirect(ctx.Org.OrgLink + "/settings/delete")
|
||||||
} else if models.IsErrUserOwnPackages(err) {
|
} else if packages_model.IsErrUserOwnPackages(err) {
|
||||||
ctx.Flash.Error(ctx.Tr("form.org_still_own_packages"))
|
ctx.Flash.Error(ctx.Tr("form.org_still_own_packages"))
|
||||||
ctx.Redirect(ctx.Org.OrgLink + "/settings/delete")
|
ctx.Redirect(ctx.Org.OrgLink + "/settings/delete")
|
||||||
} else {
|
} else {
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
@ -26,6 +25,7 @@ import (
|
|||||||
"code.gitea.io/gitea/routers/utils"
|
"code.gitea.io/gitea/routers/utils"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
"code.gitea.io/gitea/services/forms"
|
"code.gitea.io/gitea/services/forms"
|
||||||
|
pull_service "code.gitea.io/gitea/services/pull"
|
||||||
release_service "code.gitea.io/gitea/services/release"
|
release_service "code.gitea.io/gitea/services/release"
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
)
|
)
|
||||||
@ -203,14 +203,14 @@ func CreateBranch(ctx *context.Context) {
|
|||||||
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.CommitID, form.NewBranchName)
|
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.CommitID, form.NewBranchName)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrProtectedTagName(err) {
|
if release_service.IsErrProtectedTagName(err) {
|
||||||
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
|
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if models.IsErrTagAlreadyExists(err) {
|
if release_service.IsErrTagAlreadyExists(err) {
|
||||||
e := err.(models.ErrTagAlreadyExists)
|
e := err.(release_service.ErrTagAlreadyExists)
|
||||||
ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName))
|
ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName))
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||||
return
|
return
|
||||||
@ -267,7 +267,7 @@ func MergeUpstream(ctx *context.Context) {
|
|||||||
if errors.Is(err, util.ErrNotExist) {
|
if errors.Is(err, util.ErrNotExist) {
|
||||||
ctx.JSONError(ctx.Tr("error.not_found"))
|
ctx.JSONError(ctx.Tr("error.not_found"))
|
||||||
return
|
return
|
||||||
} else if models.IsErrMergeConflicts(err) {
|
} else if pull_service.IsErrMergeConflicts(err) {
|
||||||
ctx.JSONError(ctx.Tr("repo.pulls.merge_conflict"))
|
ctx.JSONError(ctx.Tr("repo.pulls.merge_conflict"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
@ -131,7 +130,7 @@ func CherryPickPost(ctx *context.Context) {
|
|||||||
ctx.Data["Err_NewBranchName"] = true
|
ctx.Data["Err_NewBranchName"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form)
|
||||||
return
|
return
|
||||||
} else if models.IsErrCommitIDDoesNotMatch(err) {
|
} else if files.IsErrCommitIDDoesNotMatch(err) {
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplPatchFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplPatchFile, &form)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -168,7 +167,7 @@ func CherryPickPost(ctx *context.Context) {
|
|||||||
ctx.Data["Err_NewBranchName"] = true
|
ctx.Data["Err_NewBranchName"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form)
|
||||||
return
|
return
|
||||||
} else if models.IsErrCommitIDDoesNotMatch(err) {
|
} else if files.IsErrCommitIDDoesNotMatch(err) {
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplPatchFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplPatchFile, &form)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
@ -303,12 +302,12 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
|
|||||||
} else if git_model.IsErrLFSFileLocked(err) {
|
} else if git_model.IsErrLFSFileLocked(err) {
|
||||||
ctx.Data["Err_TreePath"] = true
|
ctx.Data["Err_TreePath"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.upload_file_is_locked", err.(git_model.ErrLFSFileLocked).Path, err.(git_model.ErrLFSFileLocked).UserName), tplEditFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.upload_file_is_locked", err.(git_model.ErrLFSFileLocked).Path, err.(git_model.ErrLFSFileLocked).UserName), tplEditFile, &form)
|
||||||
} else if models.IsErrFilenameInvalid(err) {
|
} else if files_service.IsErrFilenameInvalid(err) {
|
||||||
ctx.Data["Err_TreePath"] = true
|
ctx.Data["Err_TreePath"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_invalid", form.TreePath), tplEditFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_invalid", form.TreePath), tplEditFile, &form)
|
||||||
} else if models.IsErrFilePathInvalid(err) {
|
} else if files_service.IsErrFilePathInvalid(err) {
|
||||||
ctx.Data["Err_TreePath"] = true
|
ctx.Data["Err_TreePath"] = true
|
||||||
if fileErr, ok := err.(models.ErrFilePathInvalid); ok {
|
if fileErr, ok := err.(files_service.ErrFilePathInvalid); ok {
|
||||||
switch fileErr.Type {
|
switch fileErr.Type {
|
||||||
case git.EntryModeSymlink:
|
case git.EntryModeSymlink:
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_is_a_symlink", fileErr.Path), tplEditFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.file_is_a_symlink", fileErr.Path), tplEditFile, &form)
|
||||||
@ -322,7 +321,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
|
|||||||
} else {
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
} else if models.IsErrRepoFileAlreadyExists(err) {
|
} else if files_service.IsErrRepoFileAlreadyExists(err) {
|
||||||
ctx.Data["Err_TreePath"] = true
|
ctx.Data["Err_TreePath"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_already_exists", form.TreePath), tplEditFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.file_already_exists", form.TreePath), tplEditFile, &form)
|
||||||
} else if git.IsErrBranchNotExist(err) {
|
} else if git.IsErrBranchNotExist(err) {
|
||||||
@ -340,7 +339,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
|
|||||||
} else {
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
} else if models.IsErrCommitIDDoesNotMatch(err) {
|
} else if files_service.IsErrCommitIDDoesNotMatch(err) {
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.commit_id_not_matching"), tplEditFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.commit_id_not_matching"), tplEditFile, &form)
|
||||||
} else if git.IsErrPushOutOfDate(err) {
|
} else if git.IsErrPushOutOfDate(err) {
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.push_out_of_date"), tplEditFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.push_out_of_date"), tplEditFile, &form)
|
||||||
@ -506,14 +505,14 @@ func DeleteFilePost(ctx *context.Context) {
|
|||||||
Signoff: form.Signoff,
|
Signoff: form.Signoff,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
// This is where we handle all the errors thrown by repofiles.DeleteRepoFile
|
// This is where we handle all the errors thrown by repofiles.DeleteRepoFile
|
||||||
if git.IsErrNotExist(err) || models.IsErrRepoFileDoesNotExist(err) {
|
if git.IsErrNotExist(err) || files_service.IsErrRepoFileDoesNotExist(err) {
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_deleting_no_longer_exists", ctx.Repo.TreePath), tplDeleteFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.file_deleting_no_longer_exists", ctx.Repo.TreePath), tplDeleteFile, &form)
|
||||||
} else if models.IsErrFilenameInvalid(err) {
|
} else if files_service.IsErrFilenameInvalid(err) {
|
||||||
ctx.Data["Err_TreePath"] = true
|
ctx.Data["Err_TreePath"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_invalid", ctx.Repo.TreePath), tplDeleteFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_invalid", ctx.Repo.TreePath), tplDeleteFile, &form)
|
||||||
} else if models.IsErrFilePathInvalid(err) {
|
} else if files_service.IsErrFilePathInvalid(err) {
|
||||||
ctx.Data["Err_TreePath"] = true
|
ctx.Data["Err_TreePath"] = true
|
||||||
if fileErr, ok := err.(models.ErrFilePathInvalid); ok {
|
if fileErr, ok := err.(files_service.ErrFilePathInvalid); ok {
|
||||||
switch fileErr.Type {
|
switch fileErr.Type {
|
||||||
case git.EntryModeSymlink:
|
case git.EntryModeSymlink:
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_is_a_symlink", fileErr.Path), tplDeleteFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.file_is_a_symlink", fileErr.Path), tplDeleteFile, &form)
|
||||||
@ -541,7 +540,7 @@ func DeleteFilePost(ctx *context.Context) {
|
|||||||
} else {
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
} else if models.IsErrCommitIDDoesNotMatch(err) || git.IsErrPushOutOfDate(err) {
|
} else if files_service.IsErrCommitIDDoesNotMatch(err) || git.IsErrPushOutOfDate(err) {
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_deleting", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplDeleteFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_deleting", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplDeleteFile, &form)
|
||||||
} else if git.IsErrPushRejected(err) {
|
} else if git.IsErrPushRejected(err) {
|
||||||
errPushRej := err.(*git.ErrPushRejected)
|
errPushRej := err.(*git.ErrPushRejected)
|
||||||
@ -715,12 +714,12 @@ func UploadFilePost(ctx *context.Context) {
|
|||||||
if git_model.IsErrLFSFileLocked(err) {
|
if git_model.IsErrLFSFileLocked(err) {
|
||||||
ctx.Data["Err_TreePath"] = true
|
ctx.Data["Err_TreePath"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.upload_file_is_locked", err.(git_model.ErrLFSFileLocked).Path, err.(git_model.ErrLFSFileLocked).UserName), tplUploadFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.upload_file_is_locked", err.(git_model.ErrLFSFileLocked).Path, err.(git_model.ErrLFSFileLocked).UserName), tplUploadFile, &form)
|
||||||
} else if models.IsErrFilenameInvalid(err) {
|
} else if files_service.IsErrFilenameInvalid(err) {
|
||||||
ctx.Data["Err_TreePath"] = true
|
ctx.Data["Err_TreePath"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_invalid", form.TreePath), tplUploadFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_invalid", form.TreePath), tplUploadFile, &form)
|
||||||
} else if models.IsErrFilePathInvalid(err) {
|
} else if files_service.IsErrFilePathInvalid(err) {
|
||||||
ctx.Data["Err_TreePath"] = true
|
ctx.Data["Err_TreePath"] = true
|
||||||
fileErr := err.(models.ErrFilePathInvalid)
|
fileErr := err.(files_service.ErrFilePathInvalid)
|
||||||
switch fileErr.Type {
|
switch fileErr.Type {
|
||||||
case git.EntryModeSymlink:
|
case git.EntryModeSymlink:
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_is_a_symlink", fileErr.Path), tplUploadFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.file_is_a_symlink", fileErr.Path), tplUploadFile, &form)
|
||||||
@ -731,7 +730,7 @@ func UploadFilePost(ctx *context.Context) {
|
|||||||
default:
|
default:
|
||||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
} else if models.IsErrRepoFileAlreadyExists(err) {
|
} else if files_service.IsErrRepoFileAlreadyExists(err) {
|
||||||
ctx.Data["Err_TreePath"] = true
|
ctx.Data["Err_TreePath"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_already_exists", form.TreePath), tplUploadFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.file_already_exists", form.TreePath), tplUploadFile, &form)
|
||||||
} else if git.IsErrBranchNotExist(err) {
|
} else if git.IsErrBranchNotExist(err) {
|
||||||
|
@ -458,7 +458,6 @@ func serviceRPC(ctx *context.Context, h *serviceHandler, service string) {
|
|||||||
|
|
||||||
var stderr bytes.Buffer
|
var stderr bytes.Buffer
|
||||||
cmd.AddArguments("--stateless-rpc").AddDynamicArguments(h.getRepoDir())
|
cmd.AddArguments("--stateless-rpc").AddDynamicArguments(h.getRepoDir())
|
||||||
cmd.SetDescription(fmt.Sprintf("%s %s %s [repo_path: %s]", git.GitExecutable, service, "--stateless-rpc", h.getRepoDir()))
|
|
||||||
if err := cmd.Run(&git.RunOpts{
|
if err := cmd.Run(&git.RunOpts{
|
||||||
Dir: h.getRepoDir(),
|
Dir: h.getRepoDir(),
|
||||||
Env: append(os.Environ(), h.environ...),
|
Env: append(os.Environ(), h.environ...),
|
||||||
|
@ -9,12 +9,12 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
admin_model "code.gitea.io/gitea/models/admin"
|
admin_model "code.gitea.io/gitea/models/admin"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
"code.gitea.io/gitea/modules/lfs"
|
"code.gitea.io/gitea/modules/lfs"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
@ -123,8 +123,8 @@ func handleMigrateError(ctx *context.Context, owner *user_model.User, err error,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleMigrateRemoteAddrError(ctx *context.Context, err error, tpl base.TplName, form *forms.MigrateRepoForm) {
|
func handleMigrateRemoteAddrError(ctx *context.Context, err error, tpl base.TplName, form *forms.MigrateRepoForm) {
|
||||||
if models.IsErrInvalidCloneAddr(err) {
|
if git.IsErrInvalidCloneAddr(err) {
|
||||||
addrErr := err.(*models.ErrInvalidCloneAddr)
|
addrErr := err.(*git.ErrInvalidCloneAddr)
|
||||||
switch {
|
switch {
|
||||||
case addrErr.IsProtocolInvalid:
|
case addrErr.IsProtocolInvalid:
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.mirror_address_protocol_invalid"), tpl, form)
|
ctx.RenderWithErr(ctx.Tr("repo.mirror_address_protocol_invalid"), tpl, form)
|
||||||
@ -176,7 +176,7 @@ func MigratePost(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteAddr, err := forms.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword)
|
remoteAddr, err := git.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = migrations.IsMigrateURLAllowed(remoteAddr, ctx.Doer)
|
err = migrations.IsMigrateURLAllowed(remoteAddr, ctx.Doer)
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ package repo
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
@ -101,7 +100,7 @@ func NewDiffPatchPost(ctx *context.Context) {
|
|||||||
ctx.Data["Err_NewBranchName"] = true
|
ctx.Data["Err_NewBranchName"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplEditFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplEditFile, &form)
|
||||||
return
|
return
|
||||||
} else if models.IsErrCommitIDDoesNotMatch(err) {
|
} else if files.IsErrCommitIDDoesNotMatch(err) {
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplPatchFile, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplPatchFile, &form)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
activities_model "code.gitea.io/gitea/models/activities"
|
activities_model "code.gitea.io/gitea/models/activities"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
@ -701,9 +700,6 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
|
|||||||
|
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
} else if prInfo == nil {
|
|
||||||
ctx.NotFound("ViewPullFiles", nil)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
headCommitID, err := gitRepo.GetRefCommitID(pull.GetGitRefName())
|
headCommitID, err := gitRepo.GetRefCommitID(pull.GetGitRefName())
|
||||||
@ -971,8 +967,8 @@ func UpdatePullRequest(ctx *context.Context) {
|
|||||||
message := fmt.Sprintf("Merge branch '%s' into %s", issue.PullRequest.BaseBranch, issue.PullRequest.HeadBranch)
|
message := fmt.Sprintf("Merge branch '%s' into %s", issue.PullRequest.BaseBranch, issue.PullRequest.HeadBranch)
|
||||||
|
|
||||||
if err = pull_service.Update(ctx, issue.PullRequest, ctx.Doer, message, rebase); err != nil {
|
if err = pull_service.Update(ctx, issue.PullRequest, ctx.Doer, message, rebase); err != nil {
|
||||||
if models.IsErrMergeConflicts(err) {
|
if pull_service.IsErrMergeConflicts(err) {
|
||||||
conflictError := err.(models.ErrMergeConflicts)
|
conflictError := err.(pull_service.ErrMergeConflicts)
|
||||||
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{
|
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{
|
||||||
"Message": ctx.Tr("repo.pulls.merge_conflict"),
|
"Message": ctx.Tr("repo.pulls.merge_conflict"),
|
||||||
"Summary": ctx.Tr("repo.pulls.merge_conflict_summary"),
|
"Summary": ctx.Tr("repo.pulls.merge_conflict_summary"),
|
||||||
@ -985,8 +981,8 @@ func UpdatePullRequest(ctx *context.Context) {
|
|||||||
ctx.Flash.Error(flashError)
|
ctx.Flash.Error(flashError)
|
||||||
ctx.Redirect(issue.Link())
|
ctx.Redirect(issue.Link())
|
||||||
return
|
return
|
||||||
} else if models.IsErrRebaseConflicts(err) {
|
} else if pull_service.IsErrRebaseConflicts(err) {
|
||||||
conflictError := err.(models.ErrRebaseConflicts)
|
conflictError := err.(pull_service.ErrRebaseConflicts)
|
||||||
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{
|
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{
|
||||||
"Message": ctx.Tr("repo.pulls.rebase_conflict", utils.SanitizeFlashErrorString(conflictError.CommitSHA)),
|
"Message": ctx.Tr("repo.pulls.rebase_conflict", utils.SanitizeFlashErrorString(conflictError.CommitSHA)),
|
||||||
"Summary": ctx.Tr("repo.pulls.rebase_conflict_summary"),
|
"Summary": ctx.Tr("repo.pulls.rebase_conflict_summary"),
|
||||||
@ -1050,7 +1046,7 @@ func MergePullRequest(ctx *context.Context) {
|
|||||||
ctx.JSONError(ctx.Tr("repo.pulls.no_merge_wip"))
|
ctx.JSONError(ctx.Tr("repo.pulls.no_merge_wip"))
|
||||||
case errors.Is(err, pull_service.ErrNotMergeableState):
|
case errors.Is(err, pull_service.ErrNotMergeableState):
|
||||||
ctx.JSONError(ctx.Tr("repo.pulls.no_merge_not_ready"))
|
ctx.JSONError(ctx.Tr("repo.pulls.no_merge_not_ready"))
|
||||||
case models.IsErrDisallowedToMerge(err):
|
case pull_service.IsErrDisallowedToMerge(err):
|
||||||
ctx.JSONError(ctx.Tr("repo.pulls.no_merge_not_ready"))
|
ctx.JSONError(ctx.Tr("repo.pulls.no_merge_not_ready"))
|
||||||
case asymkey_service.IsErrWontSign(err):
|
case asymkey_service.IsErrWontSign(err):
|
||||||
ctx.JSONError(err.Error()) // has no translation ...
|
ctx.JSONError(err.Error()) // has no translation ...
|
||||||
@ -1067,7 +1063,7 @@ func MergePullRequest(ctx *context.Context) {
|
|||||||
if manuallyMerged {
|
if manuallyMerged {
|
||||||
if err := pull_service.MergedManually(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
|
if err := pull_service.MergedManually(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
|
||||||
switch {
|
switch {
|
||||||
case models.IsErrInvalidMergeStyle(err):
|
case pull_service.IsErrInvalidMergeStyle(err):
|
||||||
ctx.JSONError(ctx.Tr("repo.pulls.invalid_merge_option"))
|
ctx.JSONError(ctx.Tr("repo.pulls.invalid_merge_option"))
|
||||||
case strings.Contains(err.Error(), "Wrong commit ID"):
|
case strings.Contains(err.Error(), "Wrong commit ID"):
|
||||||
ctx.JSONError(ctx.Tr("repo.pulls.wrong_commit_id"))
|
ctx.JSONError(ctx.Tr("repo.pulls.wrong_commit_id"))
|
||||||
@ -1114,10 +1110,10 @@ func MergePullRequest(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := pull_service.Merge(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, message, false); err != nil {
|
if err := pull_service.Merge(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, message, false); err != nil {
|
||||||
if models.IsErrInvalidMergeStyle(err) {
|
if pull_service.IsErrInvalidMergeStyle(err) {
|
||||||
ctx.JSONError(ctx.Tr("repo.pulls.invalid_merge_option"))
|
ctx.JSONError(ctx.Tr("repo.pulls.invalid_merge_option"))
|
||||||
} else if models.IsErrMergeConflicts(err) {
|
} else if pull_service.IsErrMergeConflicts(err) {
|
||||||
conflictError := err.(models.ErrMergeConflicts)
|
conflictError := err.(pull_service.ErrMergeConflicts)
|
||||||
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{
|
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{
|
||||||
"Message": ctx.Tr("repo.editor.merge_conflict"),
|
"Message": ctx.Tr("repo.editor.merge_conflict"),
|
||||||
"Summary": ctx.Tr("repo.editor.merge_conflict_summary"),
|
"Summary": ctx.Tr("repo.editor.merge_conflict_summary"),
|
||||||
@ -1129,8 +1125,8 @@ func MergePullRequest(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
ctx.Flash.Error(flashError)
|
ctx.Flash.Error(flashError)
|
||||||
ctx.JSONRedirect(issue.Link())
|
ctx.JSONRedirect(issue.Link())
|
||||||
} else if models.IsErrRebaseConflicts(err) {
|
} else if pull_service.IsErrRebaseConflicts(err) {
|
||||||
conflictError := err.(models.ErrRebaseConflicts)
|
conflictError := err.(pull_service.ErrRebaseConflicts)
|
||||||
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{
|
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{
|
||||||
"Message": ctx.Tr("repo.pulls.rebase_conflict", utils.SanitizeFlashErrorString(conflictError.CommitSHA)),
|
"Message": ctx.Tr("repo.pulls.rebase_conflict", utils.SanitizeFlashErrorString(conflictError.CommitSHA)),
|
||||||
"Summary": ctx.Tr("repo.pulls.rebase_conflict_summary"),
|
"Summary": ctx.Tr("repo.pulls.rebase_conflict_summary"),
|
||||||
@ -1142,7 +1138,7 @@ func MergePullRequest(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
ctx.Flash.Error(flashError)
|
ctx.Flash.Error(flashError)
|
||||||
ctx.JSONRedirect(issue.Link())
|
ctx.JSONRedirect(issue.Link())
|
||||||
} else if models.IsErrMergeUnrelatedHistories(err) {
|
} else if pull_service.IsErrMergeUnrelatedHistories(err) {
|
||||||
log.Debug("MergeUnrelatedHistories error: %v", err)
|
log.Debug("MergeUnrelatedHistories error: %v", err)
|
||||||
ctx.Flash.Error(ctx.Tr("repo.pulls.unrelated_histories"))
|
ctx.Flash.Error(ctx.Tr("repo.pulls.unrelated_histories"))
|
||||||
ctx.JSONRedirect(issue.Link())
|
ctx.JSONRedirect(issue.Link())
|
||||||
@ -1150,7 +1146,7 @@ func MergePullRequest(ctx *context.Context) {
|
|||||||
log.Debug("MergePushOutOfDate error: %v", err)
|
log.Debug("MergePushOutOfDate error: %v", err)
|
||||||
ctx.Flash.Error(ctx.Tr("repo.pulls.merge_out_of_date"))
|
ctx.Flash.Error(ctx.Tr("repo.pulls.merge_out_of_date"))
|
||||||
ctx.JSONRedirect(issue.Link())
|
ctx.JSONRedirect(issue.Link())
|
||||||
} else if models.IsErrSHADoesNotMatch(err) {
|
} else if pull_service.IsErrSHADoesNotMatch(err) {
|
||||||
log.Debug("MergeHeadOutOfDate error: %v", err)
|
log.Debug("MergeHeadOutOfDate error: %v", err)
|
||||||
ctx.Flash.Error(ctx.Tr("repo.pulls.head_out_of_date"))
|
ctx.Flash.Error(ctx.Tr("repo.pulls.head_out_of_date"))
|
||||||
ctx.JSONRedirect(issue.Link())
|
ctx.JSONRedirect(issue.Link())
|
||||||
@ -1609,7 +1605,7 @@ func UpdatePullRequestTarget(ctx *context.Context) {
|
|||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"user_error": errorMessage,
|
"user_error": errorMessage,
|
||||||
})
|
})
|
||||||
} else if models.IsErrPullRequestHasMerged(err) {
|
} else if pull_service.IsErrPullRequestHasMerged(err) {
|
||||||
errorMessage := ctx.Tr("repo.pulls.has_merged")
|
errorMessage := ctx.Tr("repo.pulls.has_merged")
|
||||||
|
|
||||||
ctx.Flash.Error(errorMessage)
|
ctx.Flash.Error(errorMessage)
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
"code.gitea.io/gitea/models/renderhelper"
|
"code.gitea.io/gitea/models/renderhelper"
|
||||||
@ -30,7 +29,7 @@ import (
|
|||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
"code.gitea.io/gitea/services/context/upload"
|
"code.gitea.io/gitea/services/context/upload"
|
||||||
"code.gitea.io/gitea/services/forms"
|
"code.gitea.io/gitea/services/forms"
|
||||||
releaseservice "code.gitea.io/gitea/services/release"
|
release_service "code.gitea.io/gitea/services/release"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -432,27 +431,27 @@ func NewReleasePost(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(form.TagOnly) > 0 {
|
if len(form.TagOnly) > 0 {
|
||||||
if err = releaseservice.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, form.Target, form.TagName, msg); err != nil {
|
if err = release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, form.Target, form.TagName, msg); err != nil {
|
||||||
if models.IsErrTagAlreadyExists(err) {
|
if release_service.IsErrTagAlreadyExists(err) {
|
||||||
e := err.(models.ErrTagAlreadyExists)
|
e := err.(release_service.ErrTagAlreadyExists)
|
||||||
ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName))
|
ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName))
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if models.IsErrInvalidTagName(err) {
|
if release_service.IsErrInvalidTagName(err) {
|
||||||
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_invalid"))
|
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_invalid"))
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if models.IsErrProtectedTagName(err) {
|
if release_service.IsErrProtectedTagName(err) {
|
||||||
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
|
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.ServerError("releaseservice.CreateNewTag", err)
|
ctx.ServerError("release_service.CreateNewTag", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,14 +474,14 @@ func NewReleasePost(ctx *context.Context) {
|
|||||||
IsTag: false,
|
IsTag: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = releaseservice.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs, msg); err != nil {
|
if err = release_service.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs, msg); err != nil {
|
||||||
ctx.Data["Err_TagName"] = true
|
ctx.Data["Err_TagName"] = true
|
||||||
switch {
|
switch {
|
||||||
case repo_model.IsErrReleaseAlreadyExist(err):
|
case repo_model.IsErrReleaseAlreadyExist(err):
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form)
|
||||||
case models.IsErrInvalidTagName(err):
|
case release_service.IsErrInvalidTagName(err):
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_invalid"), tplReleaseNew, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_invalid"), tplReleaseNew, &form)
|
||||||
case models.IsErrProtectedTagName(err):
|
case release_service.IsErrProtectedTagName(err):
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_protected"), tplReleaseNew, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_protected"), tplReleaseNew, &form)
|
||||||
default:
|
default:
|
||||||
ctx.ServerError("CreateRelease", err)
|
ctx.ServerError("CreateRelease", err)
|
||||||
@ -504,7 +503,7 @@ func NewReleasePost(ctx *context.Context) {
|
|||||||
rel.PublisherID = ctx.Doer.ID
|
rel.PublisherID = ctx.Doer.ID
|
||||||
rel.IsTag = false
|
rel.IsTag = false
|
||||||
|
|
||||||
if err = releaseservice.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, attachmentUUIDs, nil, nil); err != nil {
|
if err = release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, attachmentUUIDs, nil, nil); err != nil {
|
||||||
ctx.Data["Err_TagName"] = true
|
ctx.Data["Err_TagName"] = true
|
||||||
ctx.ServerError("UpdateRelease", err)
|
ctx.ServerError("UpdateRelease", err)
|
||||||
return
|
return
|
||||||
@ -610,7 +609,7 @@ func EditReleasePost(ctx *context.Context) {
|
|||||||
rel.Note = form.Content
|
rel.Note = form.Content
|
||||||
rel.IsDraft = len(form.Draft) > 0
|
rel.IsDraft = len(form.Draft) > 0
|
||||||
rel.IsPrerelease = form.Prerelease
|
rel.IsPrerelease = form.Prerelease
|
||||||
if err = releaseservice.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo,
|
if err = release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo,
|
||||||
rel, addAttachmentUUIDs, delAttachmentUUIDs, editAttachments); err != nil {
|
rel, addAttachmentUUIDs, delAttachmentUUIDs, editAttachments); err != nil {
|
||||||
ctx.ServerError("UpdateRelease", err)
|
ctx.ServerError("UpdateRelease", err)
|
||||||
return
|
return
|
||||||
@ -649,8 +648,8 @@ func deleteReleaseOrTag(ctx *context.Context, isDelTag bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := releaseservice.DeleteReleaseByID(ctx, ctx.Repo.Repository, rel, ctx.Doer, isDelTag); err != nil {
|
if err := release_service.DeleteReleaseByID(ctx, ctx.Repo.Repository, rel, ctx.Doer, isDelTag); err != nil {
|
||||||
if models.IsErrProtectedTagName(err) {
|
if release_service.IsErrProtectedTagName(err) {
|
||||||
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
|
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
|
||||||
} else {
|
} else {
|
||||||
ctx.Flash.Error("DeleteReleaseByID: " + err.Error())
|
ctx.Flash.Error("DeleteReleaseByID: " + err.Error())
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
actions_model "code.gitea.io/gitea/models/actions"
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
@ -223,7 +222,7 @@ func SettingsPost(ctx *context.Context) {
|
|||||||
form.MirrorPassword, _ = u.User.Password()
|
form.MirrorPassword, _ = u.User.Password()
|
||||||
}
|
}
|
||||||
|
|
||||||
address, err := forms.ParseRemoteAddr(form.MirrorAddress, form.MirrorUsername, form.MirrorPassword)
|
address, err := git.ParseRemoteAddr(form.MirrorAddress, form.MirrorUsername, form.MirrorPassword)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = migrations.IsMigrateURLAllowed(address, ctx.Doer)
|
err = migrations.IsMigrateURLAllowed(address, ctx.Doer)
|
||||||
}
|
}
|
||||||
@ -385,7 +384,7 @@ func SettingsPost(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
address, err := forms.ParseRemoteAddr(form.PushMirrorAddress, form.PushMirrorUsername, form.PushMirrorPassword)
|
address, err := git.ParseRemoteAddr(form.PushMirrorAddress, form.PushMirrorUsername, form.PushMirrorPassword)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = migrations.IsMigrateURLAllowed(address, ctx.Doer)
|
err = migrations.IsMigrateURLAllowed(address, ctx.Doer)
|
||||||
}
|
}
|
||||||
@ -980,8 +979,8 @@ func SettingsPost(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleSettingRemoteAddrError(ctx *context.Context, err error, form *forms.RepoSettingForm) {
|
func handleSettingRemoteAddrError(ctx *context.Context, err error, form *forms.RepoSettingForm) {
|
||||||
if models.IsErrInvalidCloneAddr(err) {
|
if git.IsErrInvalidCloneAddr(err) {
|
||||||
addrErr := err.(*models.ErrInvalidCloneAddr)
|
addrErr := err.(*git.ErrInvalidCloneAddr)
|
||||||
switch {
|
switch {
|
||||||
case addrErr.IsProtocolInvalid:
|
case addrErr.IsProtocolInvalid:
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.mirror_address_protocol_invalid"), tplSettingsOptions, form)
|
ctx.RenderWithErr(ctx.Tr("repo.mirror_address_protocol_invalid"), tplSettingsOptions, form)
|
||||||
|
@ -10,7 +10,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
org_model "code.gitea.io/gitea/models/organization"
|
||||||
|
packages_model "code.gitea.io/gitea/models/packages"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/auth/password"
|
"code.gitea.io/gitea/modules/auth/password"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
@ -301,16 +303,16 @@ func DeleteAccount(ctx *context.Context) {
|
|||||||
|
|
||||||
if err := user.DeleteUser(ctx, ctx.Doer, false); err != nil {
|
if err := user.DeleteUser(ctx, ctx.Doer, false); err != nil {
|
||||||
switch {
|
switch {
|
||||||
case models.IsErrUserOwnRepos(err):
|
case repo_model.IsErrUserOwnRepos(err):
|
||||||
ctx.Flash.Error(ctx.Tr("form.still_own_repo"))
|
ctx.Flash.Error(ctx.Tr("form.still_own_repo"))
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
||||||
case models.IsErrUserHasOrgs(err):
|
case org_model.IsErrUserHasOrgs(err):
|
||||||
ctx.Flash.Error(ctx.Tr("form.still_has_org"))
|
ctx.Flash.Error(ctx.Tr("form.still_has_org"))
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
||||||
case models.IsErrUserOwnPackages(err):
|
case packages_model.IsErrUserOwnPackages(err):
|
||||||
ctx.Flash.Error(ctx.Tr("form.still_own_packages"))
|
ctx.Flash.Error(ctx.Tr("form.still_own_packages"))
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
||||||
case models.IsErrDeleteLastAdminUser(err):
|
case user_model.IsErrDeleteLastAdminUser(err):
|
||||||
ctx.Flash.Error(ctx.Tr("auth.last_admin"))
|
ctx.Flash.Error(ctx.Tr("auth.last_admin"))
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
||||||
default:
|
default:
|
||||||
|
@ -1020,14 +1020,15 @@ func registerRoutes(m *web.Router) {
|
|||||||
m.Get("/new", org.RenderNewProject)
|
m.Get("/new", org.RenderNewProject)
|
||||||
m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost)
|
m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost)
|
||||||
m.Group("/{id}", func() {
|
m.Group("/{id}", func() {
|
||||||
m.Post("", web.Bind(forms.EditProjectColumnForm{}), org.AddColumnToProjectPost)
|
|
||||||
m.Post("/move", project.MoveColumns)
|
|
||||||
m.Post("/delete", org.DeleteProject)
|
m.Post("/delete", org.DeleteProject)
|
||||||
|
|
||||||
m.Get("/edit", org.RenderEditProject)
|
m.Get("/edit", org.RenderEditProject)
|
||||||
m.Post("/edit", web.Bind(forms.CreateProjectForm{}), org.EditProjectPost)
|
m.Post("/edit", web.Bind(forms.CreateProjectForm{}), org.EditProjectPost)
|
||||||
m.Post("/{action:open|close}", org.ChangeProjectStatus)
|
m.Post("/{action:open|close}", org.ChangeProjectStatus)
|
||||||
|
|
||||||
|
// TODO: improper name. Others are "delete project", "edit project", but this one is "move columns"
|
||||||
|
m.Post("/move", project.MoveColumns)
|
||||||
|
m.Post("/columns/new", web.Bind(forms.EditProjectColumnForm{}), org.AddColumnToProjectPost)
|
||||||
m.Group("/{columnID}", func() {
|
m.Group("/{columnID}", func() {
|
||||||
m.Put("", web.Bind(forms.EditProjectColumnForm{}), org.EditProjectColumn)
|
m.Put("", web.Bind(forms.EditProjectColumnForm{}), org.EditProjectColumn)
|
||||||
m.Delete("", org.DeleteProjectColumn)
|
m.Delete("", org.DeleteProjectColumn)
|
||||||
@ -1387,14 +1388,15 @@ func registerRoutes(m *web.Router) {
|
|||||||
m.Get("/new", repo.RenderNewProject)
|
m.Get("/new", repo.RenderNewProject)
|
||||||
m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost)
|
m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost)
|
||||||
m.Group("/{id}", func() {
|
m.Group("/{id}", func() {
|
||||||
m.Post("", web.Bind(forms.EditProjectColumnForm{}), repo.AddColumnToProjectPost)
|
|
||||||
m.Post("/move", project.MoveColumns)
|
|
||||||
m.Post("/delete", repo.DeleteProject)
|
m.Post("/delete", repo.DeleteProject)
|
||||||
|
|
||||||
m.Get("/edit", repo.RenderEditProject)
|
m.Get("/edit", repo.RenderEditProject)
|
||||||
m.Post("/edit", web.Bind(forms.CreateProjectForm{}), repo.EditProjectPost)
|
m.Post("/edit", web.Bind(forms.CreateProjectForm{}), repo.EditProjectPost)
|
||||||
m.Post("/{action:open|close}", repo.ChangeProjectStatus)
|
m.Post("/{action:open|close}", repo.ChangeProjectStatus)
|
||||||
|
|
||||||
|
// TODO: improper name. Others are "delete project", "edit project", but this one is "move columns"
|
||||||
|
m.Post("/move", project.MoveColumns)
|
||||||
|
m.Post("/columns/new", web.Bind(forms.EditProjectColumnForm{}), repo.AddColumnToProjectPost)
|
||||||
m.Group("/{columnID}", func() {
|
m.Group("/{columnID}", func() {
|
||||||
m.Put("", web.Bind(forms.EditProjectColumnForm{}), repo.EditProjectColumn)
|
m.Put("", web.Bind(forms.EditProjectColumnForm{}), repo.EditProjectColumn)
|
||||||
m.Delete("", repo.DeleteProjectColumn)
|
m.Delete("", repo.DeleteProjectColumn)
|
||||||
|
@ -6,10 +6,8 @@ package forms
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
project_model "code.gitea.io/gitea/models/project"
|
project_model "code.gitea.io/gitea/models/project"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
@ -90,27 +88,6 @@ func (f *MigrateRepoForm) Validate(req *http.Request, errs binding.Errors) bindi
|
|||||||
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseRemoteAddr checks if given remote address is valid,
|
|
||||||
// and returns composed URL with needed username and password.
|
|
||||||
func ParseRemoteAddr(remoteAddr, authUsername, authPassword string) (string, error) {
|
|
||||||
remoteAddr = strings.TrimSpace(remoteAddr)
|
|
||||||
// Remote address can be HTTP/HTTPS/Git URL or local path.
|
|
||||||
if strings.HasPrefix(remoteAddr, "http://") ||
|
|
||||||
strings.HasPrefix(remoteAddr, "https://") ||
|
|
||||||
strings.HasPrefix(remoteAddr, "git://") {
|
|
||||||
u, err := url.Parse(remoteAddr)
|
|
||||||
if err != nil {
|
|
||||||
return "", &models.ErrInvalidCloneAddr{IsURLError: true, Host: remoteAddr}
|
|
||||||
}
|
|
||||||
if len(authUsername)+len(authPassword) > 0 {
|
|
||||||
u.User = url.UserPassword(authUsername, authPassword)
|
|
||||||
}
|
|
||||||
remoteAddr = u.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
return remoteAddr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RepoSettingForm form for changing repository settings
|
// RepoSettingForm form for changing repository settings
|
||||||
type RepoSettingForm struct {
|
type RepoSettingForm struct {
|
||||||
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
|
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
|
||||||
|
@ -1156,7 +1156,6 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
stderr := &bytes.Buffer{}
|
stderr := &bytes.Buffer{}
|
||||||
cmdDiff.SetDescription(fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath))
|
|
||||||
if err := cmdDiff.Run(&git.RunOpts{
|
if err := cmdDiff.Run(&git.RunOpts{
|
||||||
Timeout: time.Duration(setting.Git.Timeout.Default) * time.Second,
|
Timeout: time.Duration(setting.Git.Timeout.Default) * time.Second,
|
||||||
Dir: repoPath,
|
Dir: repoPath,
|
||||||
|
@ -12,10 +12,10 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
system_model "code.gitea.io/gitea/models/system"
|
system_model "code.gitea.io/gitea/models/system"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/hostmatcher"
|
"code.gitea.io/gitea/modules/hostmatcher"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
base "code.gitea.io/gitea/modules/migration"
|
base "code.gitea.io/gitea/modules/migration"
|
||||||
@ -43,16 +43,16 @@ func IsMigrateURLAllowed(remoteURL string, doer *user_model.User) error {
|
|||||||
// Remote address can be HTTP/HTTPS/Git URL or local path.
|
// Remote address can be HTTP/HTTPS/Git URL or local path.
|
||||||
u, err := url.Parse(remoteURL)
|
u, err := url.Parse(remoteURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &models.ErrInvalidCloneAddr{IsURLError: true, Host: remoteURL}
|
return &git.ErrInvalidCloneAddr{IsURLError: true, Host: remoteURL}
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.Scheme == "file" || u.Scheme == "" {
|
if u.Scheme == "file" || u.Scheme == "" {
|
||||||
if !doer.CanImportLocal() {
|
if !doer.CanImportLocal() {
|
||||||
return &models.ErrInvalidCloneAddr{Host: "<LOCAL_FILESYSTEM>", IsPermissionDenied: true, LocalPath: true}
|
return &git.ErrInvalidCloneAddr{Host: "<LOCAL_FILESYSTEM>", IsPermissionDenied: true, LocalPath: true}
|
||||||
}
|
}
|
||||||
isAbs := filepath.IsAbs(u.Host + u.Path)
|
isAbs := filepath.IsAbs(u.Host + u.Path)
|
||||||
if !isAbs {
|
if !isAbs {
|
||||||
return &models.ErrInvalidCloneAddr{Host: "<LOCAL_FILESYSTEM>", IsInvalidPath: true, LocalPath: true}
|
return &git.ErrInvalidCloneAddr{Host: "<LOCAL_FILESYSTEM>", IsInvalidPath: true, LocalPath: true}
|
||||||
}
|
}
|
||||||
isDir, err := util.IsDir(u.Host + u.Path)
|
isDir, err := util.IsDir(u.Host + u.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -60,18 +60,18 @@ func IsMigrateURLAllowed(remoteURL string, doer *user_model.User) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !isDir {
|
if !isDir {
|
||||||
return &models.ErrInvalidCloneAddr{Host: "<LOCAL_FILESYSTEM>", IsInvalidPath: true, LocalPath: true}
|
return &git.ErrInvalidCloneAddr{Host: "<LOCAL_FILESYSTEM>", IsInvalidPath: true, LocalPath: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.Scheme == "git" && u.Port() != "" && (strings.Contains(remoteURL, "%0d") || strings.Contains(remoteURL, "%0a")) {
|
if u.Scheme == "git" && u.Port() != "" && (strings.Contains(remoteURL, "%0d") || strings.Contains(remoteURL, "%0a")) {
|
||||||
return &models.ErrInvalidCloneAddr{Host: u.Host, IsURLError: true}
|
return &git.ErrInvalidCloneAddr{Host: u.Host, IsURLError: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.Opaque != "" || u.Scheme != "" && u.Scheme != "http" && u.Scheme != "https" && u.Scheme != "git" {
|
if u.Opaque != "" || u.Scheme != "" && u.Scheme != "http" && u.Scheme != "https" && u.Scheme != "git" {
|
||||||
return &models.ErrInvalidCloneAddr{Host: u.Host, IsProtocolInvalid: true, IsPermissionDenied: true, IsURLError: true}
|
return &git.ErrInvalidCloneAddr{Host: u.Host, IsProtocolInvalid: true, IsPermissionDenied: true, IsURLError: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
hostName, _, err := net.SplitHostPort(u.Host)
|
hostName, _, err := net.SplitHostPort(u.Host)
|
||||||
@ -95,12 +95,12 @@ func checkByAllowBlockList(hostName string, addrList []net.IP) error {
|
|||||||
}
|
}
|
||||||
var blockedError error
|
var blockedError error
|
||||||
if blockList.MatchHostName(hostName) || ipBlocked {
|
if blockList.MatchHostName(hostName) || ipBlocked {
|
||||||
blockedError = &models.ErrInvalidCloneAddr{Host: hostName, IsPermissionDenied: true}
|
blockedError = &git.ErrInvalidCloneAddr{Host: hostName, IsPermissionDenied: true}
|
||||||
}
|
}
|
||||||
// if we have an allow-list, check the allow-list before return to get the more accurate error
|
// if we have an allow-list, check the allow-list before return to get the more accurate error
|
||||||
if !allowList.IsEmpty() {
|
if !allowList.IsEmpty() {
|
||||||
if !allowList.MatchHostName(hostName) && !ipAllowed {
|
if !allowList.MatchHostName(hostName) && !ipAllowed {
|
||||||
return &models.ErrInvalidCloneAddr{Host: hostName, IsPermissionDenied: true}
|
return &git.ErrInvalidCloneAddr{Host: hostName, IsPermissionDenied: true}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// otherwise, we always follow the blocked list
|
// otherwise, we always follow the blocked list
|
||||||
|
@ -46,11 +46,6 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd := git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(addr)
|
cmd := git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(addr)
|
||||||
if strings.Contains(addr, "://") && strings.Contains(addr, "@") {
|
|
||||||
cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, util.SanitizeCredentialURLs(addr), repoPath))
|
|
||||||
} else {
|
|
||||||
cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, addr, repoPath))
|
|
||||||
}
|
|
||||||
_, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
|
_, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
|
||||||
if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
|
if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
|
||||||
return err
|
return err
|
||||||
@ -66,11 +61,6 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd = git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(wikiRemotePath)
|
cmd = git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(wikiRemotePath)
|
||||||
if strings.Contains(wikiRemotePath, "://") && strings.Contains(wikiRemotePath, "@") {
|
|
||||||
cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, util.SanitizeCredentialURLs(wikiRemotePath), wikiPath))
|
|
||||||
} else {
|
|
||||||
cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, wikiRemotePath, wikiPath))
|
|
||||||
}
|
|
||||||
_, _, err = cmd.RunStdString(&git.RunOpts{Dir: wikiPath})
|
_, _, err = cmd.RunStdString(&git.RunOpts{Dir: wikiPath})
|
||||||
if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
|
if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
|
||||||
return err
|
return err
|
||||||
@ -197,7 +187,6 @@ func pruneBrokenReferences(ctx context.Context,
|
|||||||
stderrBuilder.Reset()
|
stderrBuilder.Reset()
|
||||||
stdoutBuilder.Reset()
|
stdoutBuilder.Reset()
|
||||||
pruneErr := git.NewCommand(ctx, "remote", "prune").AddDynamicArguments(m.GetRemoteName()).
|
pruneErr := git.NewCommand(ctx, "remote", "prune").AddDynamicArguments(m.GetRemoteName()).
|
||||||
SetDescription(fmt.Sprintf("Mirror.runSync %ssPrune references: %s ", wiki, m.Repo.FullName())).
|
|
||||||
Run(&git.RunOpts{
|
Run(&git.RunOpts{
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
Dir: repoPath,
|
Dir: repoPath,
|
||||||
@ -248,9 +237,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
|
|||||||
|
|
||||||
stdoutBuilder := strings.Builder{}
|
stdoutBuilder := strings.Builder{}
|
||||||
stderrBuilder := strings.Builder{}
|
stderrBuilder := strings.Builder{}
|
||||||
if err := cmd.
|
if err := cmd.Run(&git.RunOpts{
|
||||||
SetDescription(fmt.Sprintf("Mirror.runSync: %s", m.Repo.FullName())).
|
|
||||||
Run(&git.RunOpts{
|
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
Dir: repoPath,
|
Dir: repoPath,
|
||||||
Env: envs,
|
Env: envs,
|
||||||
@ -275,9 +262,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
|
|||||||
// Successful prune - reattempt mirror
|
// Successful prune - reattempt mirror
|
||||||
stderrBuilder.Reset()
|
stderrBuilder.Reset()
|
||||||
stdoutBuilder.Reset()
|
stdoutBuilder.Reset()
|
||||||
if err = cmd.
|
if err = cmd.Run(&git.RunOpts{
|
||||||
SetDescription(fmt.Sprintf("Mirror.runSync: %s", m.Repo.FullName())).
|
|
||||||
Run(&git.RunOpts{
|
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
Dir: repoPath,
|
Dir: repoPath,
|
||||||
Stdout: &stdoutBuilder,
|
Stdout: &stdoutBuilder,
|
||||||
@ -346,7 +331,6 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
|
|||||||
stderrBuilder.Reset()
|
stderrBuilder.Reset()
|
||||||
stdoutBuilder.Reset()
|
stdoutBuilder.Reset()
|
||||||
if err := git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()).
|
if err := git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()).
|
||||||
SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", m.Repo.FullName())).
|
|
||||||
Run(&git.RunOpts{
|
Run(&git.RunOpts{
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
Dir: wikiPath,
|
Dir: wikiPath,
|
||||||
@ -373,7 +357,6 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
|
|||||||
stdoutBuilder.Reset()
|
stdoutBuilder.Reset()
|
||||||
|
|
||||||
if err = git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()).
|
if err = git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()).
|
||||||
SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", m.Repo.FullName())).
|
|
||||||
Run(&git.RunOpts{
|
Run(&git.RunOpts{
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
Dir: wikiPath,
|
Dir: wikiPath,
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
@ -31,11 +30,6 @@ var stripExitStatus = regexp.MustCompile(`exit status \d+ - `)
|
|||||||
func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error {
|
func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error {
|
||||||
addRemoteAndConfig := func(addr, path string) error {
|
addRemoteAndConfig := func(addr, path string) error {
|
||||||
cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push").AddDynamicArguments(m.RemoteName, addr)
|
cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push").AddDynamicArguments(m.RemoteName, addr)
|
||||||
if strings.Contains(addr, "://") && strings.Contains(addr, "@") {
|
|
||||||
cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, util.SanitizeCredentialURLs(addr), path))
|
|
||||||
} else {
|
|
||||||
cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, addr, path))
|
|
||||||
}
|
|
||||||
if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: path}); err != nil {
|
if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: path}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
actions_model "code.gitea.io/gitea/models/actions"
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
org_model "code.gitea.io/gitea/models/organization"
|
org_model "code.gitea.io/gitea/models/organization"
|
||||||
@ -67,14 +66,14 @@ func DeleteOrganization(ctx context.Context, org *org_model.Organization, purge
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetRepositoryCount: %w", err)
|
return fmt.Errorf("GetRepositoryCount: %w", err)
|
||||||
} else if count > 0 {
|
} else if count > 0 {
|
||||||
return models.ErrUserOwnRepos{UID: org.ID}
|
return repo_model.ErrUserOwnRepos{UID: org.ID}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check ownership of packages.
|
// Check ownership of packages.
|
||||||
if ownsPackages, err := packages_model.HasOwnerPackages(ctx, org.ID); err != nil {
|
if ownsPackages, err := packages_model.HasOwnerPackages(ctx, org.ID); err != nil {
|
||||||
return fmt.Errorf("HasOwnerPackages: %w", err)
|
return fmt.Errorf("HasOwnerPackages: %w", err)
|
||||||
} else if ownsPackages {
|
} else if ownsPackages {
|
||||||
return models.ErrUserOwnPackages{UID: org.ID}
|
return packages_model.ErrUserOwnPackages{UID: org.ID}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := deleteOrganization(ctx, org); err != nil {
|
if err := deleteOrganization(ctx, org); err != nil {
|
||||||
|
@ -6,9 +6,9 @@ package org
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ func TestDeleteOrganization(t *testing.T) {
|
|||||||
org = unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
|
org = unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
|
||||||
err := DeleteOrganization(db.DefaultContext, org, false)
|
err := DeleteOrganization(db.DefaultContext, org, false)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.True(t, models.IsErrUserOwnRepos(err))
|
assert.True(t, repo_model.IsErrUserOwnRepos(err))
|
||||||
|
|
||||||
user := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 5})
|
user := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 5})
|
||||||
assert.Error(t, DeleteOrganization(db.DefaultContext, user, false))
|
assert.Error(t, DeleteOrganization(db.DefaultContext, user, false))
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
@ -36,7 +35,7 @@ var prPatchCheckerQueue *queue.WorkerPoolQueue[string]
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
ErrIsClosed = errors.New("pull is closed")
|
ErrIsClosed = errors.New("pull is closed")
|
||||||
ErrUserNotAllowedToMerge = models.ErrDisallowedToMerge{}
|
ErrUserNotAllowedToMerge = ErrDisallowedToMerge{}
|
||||||
ErrHasMerged = errors.New("has already been merged")
|
ErrHasMerged = errors.New("has already been merged")
|
||||||
ErrIsWorkInProgress = errors.New("work in progress PRs cannot be merged")
|
ErrIsWorkInProgress = errors.New("work in progress PRs cannot be merged")
|
||||||
ErrIsChecking = errors.New("cannot merge while conflict checking is in progress")
|
ErrIsChecking = errors.New("cannot merge while conflict checking is in progress")
|
||||||
@ -106,7 +105,7 @@ func CheckPullMergeable(stdCtx context.Context, doer *user_model.User, perm *acc
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := CheckPullBranchProtections(ctx, pr, false); err != nil {
|
if err := CheckPullBranchProtections(ctx, pr, false); err != nil {
|
||||||
if !models.IsErrDisallowedToMerge(err) {
|
if !IsErrDisallowedToMerge(err) {
|
||||||
log.Error("Error whilst checking pull branch protection for %-v: %v", pr, err)
|
log.Error("Error whilst checking pull branch protection for %-v: %v", pr, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
@ -30,6 +29,7 @@ import (
|
|||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
issue_service "code.gitea.io/gitea/services/issue"
|
issue_service "code.gitea.io/gitea/services/issue"
|
||||||
notify_service "code.gitea.io/gitea/services/notify"
|
notify_service "code.gitea.io/gitea/services/notify"
|
||||||
)
|
)
|
||||||
@ -159,6 +159,27 @@ func GetDefaultMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr
|
|||||||
return getMergeMessage(ctx, baseGitRepo, pr, mergeStyle, nil)
|
return getMergeMessage(ctx, baseGitRepo, pr, mergeStyle, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrInvalidMergeStyle represents an error if merging with disabled merge strategy
|
||||||
|
type ErrInvalidMergeStyle struct {
|
||||||
|
ID int64
|
||||||
|
Style repo_model.MergeStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrInvalidMergeStyle checks if an error is a ErrInvalidMergeStyle.
|
||||||
|
func IsErrInvalidMergeStyle(err error) bool {
|
||||||
|
_, ok := err.(ErrInvalidMergeStyle)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrInvalidMergeStyle) Error() string {
|
||||||
|
return fmt.Sprintf("merge strategy is not allowed or is invalid [repo_id: %d, strategy: %s]",
|
||||||
|
err.ID, err.Style)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrInvalidMergeStyle) Unwrap() error {
|
||||||
|
return util.ErrInvalidArgument
|
||||||
|
}
|
||||||
|
|
||||||
// Merge merges pull request to base repository.
|
// Merge merges pull request to base repository.
|
||||||
// Caller should check PR is ready to be merged (review and status checks)
|
// Caller should check PR is ready to be merged (review and status checks)
|
||||||
func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string, wasAutoMerged bool) error {
|
func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string, wasAutoMerged bool) error {
|
||||||
@ -179,7 +200,7 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
|
|||||||
|
|
||||||
// Check if merge style is correct and allowed
|
// Check if merge style is correct and allowed
|
||||||
if !prConfig.IsMergeStyleAllowed(mergeStyle) {
|
if !prConfig.IsMergeStyleAllowed(mergeStyle) {
|
||||||
return models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle}
|
return ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle}
|
||||||
}
|
}
|
||||||
|
|
||||||
releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID))
|
releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID))
|
||||||
@ -283,7 +304,7 @@ func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *use
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return "", models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle}
|
return "", ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OK we should cache our current head and origin/headbranch
|
// OK we should cache our current head and origin/headbranch
|
||||||
@ -374,13 +395,66 @@ func commitAndSignNoAuthor(ctx *mergeContext, message string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrMergeConflicts represents an error if merging fails with a conflict
|
||||||
|
type ErrMergeConflicts struct {
|
||||||
|
Style repo_model.MergeStyle
|
||||||
|
StdOut string
|
||||||
|
StdErr string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrMergeConflicts checks if an error is a ErrMergeConflicts.
|
||||||
|
func IsErrMergeConflicts(err error) bool {
|
||||||
|
_, ok := err.(ErrMergeConflicts)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrMergeConflicts) Error() string {
|
||||||
|
return fmt.Sprintf("Merge Conflict Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMergeUnrelatedHistories represents an error if merging fails due to unrelated histories
|
||||||
|
type ErrMergeUnrelatedHistories struct {
|
||||||
|
Style repo_model.MergeStyle
|
||||||
|
StdOut string
|
||||||
|
StdErr string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrMergeUnrelatedHistories checks if an error is a ErrMergeUnrelatedHistories.
|
||||||
|
func IsErrMergeUnrelatedHistories(err error) bool {
|
||||||
|
_, ok := err.(ErrMergeUnrelatedHistories)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrMergeUnrelatedHistories) Error() string {
|
||||||
|
return fmt.Sprintf("Merge UnrelatedHistories Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMergeDivergingFastForwardOnly represents an error if a fast-forward-only merge fails because the branches diverge
|
||||||
|
type ErrMergeDivergingFastForwardOnly struct {
|
||||||
|
StdOut string
|
||||||
|
StdErr string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrMergeDivergingFastForwardOnly checks if an error is a ErrMergeDivergingFastForwardOnly.
|
||||||
|
func IsErrMergeDivergingFastForwardOnly(err error) bool {
|
||||||
|
_, ok := err.(ErrMergeDivergingFastForwardOnly)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrMergeDivergingFastForwardOnly) Error() string {
|
||||||
|
return fmt.Sprintf("Merge DivergingFastForwardOnly Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
||||||
|
}
|
||||||
|
|
||||||
func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *git.Command) error {
|
func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *git.Command) error {
|
||||||
if err := cmd.Run(ctx.RunOpts()); err != nil {
|
if err := cmd.Run(ctx.RunOpts()); err != nil {
|
||||||
// Merge will leave a MERGE_HEAD file in the .git folder if there is a conflict
|
// Merge will leave a MERGE_HEAD file in the .git folder if there is a conflict
|
||||||
if _, statErr := os.Stat(filepath.Join(ctx.tmpBasePath, ".git", "MERGE_HEAD")); statErr == nil {
|
if _, statErr := os.Stat(filepath.Join(ctx.tmpBasePath, ".git", "MERGE_HEAD")); statErr == nil {
|
||||||
// We have a merge conflict error
|
// We have a merge conflict error
|
||||||
log.Debug("MergeConflict %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
|
log.Debug("MergeConflict %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
|
||||||
return models.ErrMergeConflicts{
|
return ErrMergeConflicts{
|
||||||
Style: mergeStyle,
|
Style: mergeStyle,
|
||||||
StdOut: ctx.outbuf.String(),
|
StdOut: ctx.outbuf.String(),
|
||||||
StdErr: ctx.errbuf.String(),
|
StdErr: ctx.errbuf.String(),
|
||||||
@ -388,7 +462,7 @@ func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *g
|
|||||||
}
|
}
|
||||||
} else if strings.Contains(ctx.errbuf.String(), "refusing to merge unrelated histories") {
|
} else if strings.Contains(ctx.errbuf.String(), "refusing to merge unrelated histories") {
|
||||||
log.Debug("MergeUnrelatedHistories %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
|
log.Debug("MergeUnrelatedHistories %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
|
||||||
return models.ErrMergeUnrelatedHistories{
|
return ErrMergeUnrelatedHistories{
|
||||||
Style: mergeStyle,
|
Style: mergeStyle,
|
||||||
StdOut: ctx.outbuf.String(),
|
StdOut: ctx.outbuf.String(),
|
||||||
StdErr: ctx.errbuf.String(),
|
StdErr: ctx.errbuf.String(),
|
||||||
@ -396,7 +470,7 @@ func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *g
|
|||||||
}
|
}
|
||||||
} else if mergeStyle == repo_model.MergeStyleFastForwardOnly && strings.Contains(ctx.errbuf.String(), "Not possible to fast-forward, aborting") {
|
} else if mergeStyle == repo_model.MergeStyleFastForwardOnly && strings.Contains(ctx.errbuf.String(), "Not possible to fast-forward, aborting") {
|
||||||
log.Debug("MergeDivergingFastForwardOnly %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
|
log.Debug("MergeDivergingFastForwardOnly %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
|
||||||
return models.ErrMergeDivergingFastForwardOnly{
|
return ErrMergeDivergingFastForwardOnly{
|
||||||
StdOut: ctx.outbuf.String(),
|
StdOut: ctx.outbuf.String(),
|
||||||
StdErr: ctx.errbuf.String(),
|
StdErr: ctx.errbuf.String(),
|
||||||
Err: err,
|
Err: err,
|
||||||
@ -431,6 +505,25 @@ func IsUserAllowedToMerge(ctx context.Context, pr *issues_model.PullRequest, p a
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrDisallowedToMerge represents an error that a branch is protected and the current user is not allowed to modify it.
|
||||||
|
type ErrDisallowedToMerge struct {
|
||||||
|
Reason string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrDisallowedToMerge checks if an error is an ErrDisallowedToMerge.
|
||||||
|
func IsErrDisallowedToMerge(err error) bool {
|
||||||
|
_, ok := err.(ErrDisallowedToMerge)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrDisallowedToMerge) Error() string {
|
||||||
|
return fmt.Sprintf("not allowed to merge [reason: %s]", err.Reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrDisallowedToMerge) Unwrap() error {
|
||||||
|
return util.ErrPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
// CheckPullBranchProtections checks whether the PR is ready to be merged (reviews and status checks)
|
// CheckPullBranchProtections checks whether the PR is ready to be merged (reviews and status checks)
|
||||||
func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullRequest, skipProtectedFilesCheck bool) (err error) {
|
func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullRequest, skipProtectedFilesCheck bool) (err error) {
|
||||||
if err = pr.LoadBaseRepo(ctx); err != nil {
|
if err = pr.LoadBaseRepo(ctx); err != nil {
|
||||||
@ -450,29 +543,29 @@ func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullReques
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !isPass {
|
if !isPass {
|
||||||
return models.ErrDisallowedToMerge{
|
return ErrDisallowedToMerge{
|
||||||
Reason: "Not all required status checks successful",
|
Reason: "Not all required status checks successful",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !issues_model.HasEnoughApprovals(ctx, pb, pr) {
|
if !issues_model.HasEnoughApprovals(ctx, pb, pr) {
|
||||||
return models.ErrDisallowedToMerge{
|
return ErrDisallowedToMerge{
|
||||||
Reason: "Does not have enough approvals",
|
Reason: "Does not have enough approvals",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if issues_model.MergeBlockedByRejectedReview(ctx, pb, pr) {
|
if issues_model.MergeBlockedByRejectedReview(ctx, pb, pr) {
|
||||||
return models.ErrDisallowedToMerge{
|
return ErrDisallowedToMerge{
|
||||||
Reason: "There are requested changes",
|
Reason: "There are requested changes",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if issues_model.MergeBlockedByOfficialReviewRequests(ctx, pb, pr) {
|
if issues_model.MergeBlockedByOfficialReviewRequests(ctx, pb, pr) {
|
||||||
return models.ErrDisallowedToMerge{
|
return ErrDisallowedToMerge{
|
||||||
Reason: "There are official review requests",
|
Reason: "There are official review requests",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if issues_model.MergeBlockedByOutdatedBranch(pb, pr) {
|
if issues_model.MergeBlockedByOutdatedBranch(pb, pr) {
|
||||||
return models.ErrDisallowedToMerge{
|
return ErrDisallowedToMerge{
|
||||||
Reason: "The head branch is behind the base branch",
|
Reason: "The head branch is behind the base branch",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -482,7 +575,7 @@ func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullReques
|
|||||||
}
|
}
|
||||||
|
|
||||||
if pb.MergeBlockedByProtectedFiles(pr.ChangedProtectedFiles) {
|
if pb.MergeBlockedByProtectedFiles(pr.ChangedProtectedFiles) {
|
||||||
return models.ErrDisallowedToMerge{
|
return ErrDisallowedToMerge{
|
||||||
Reason: "Changed protected files",
|
Reason: "Changed protected files",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -511,7 +604,7 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use
|
|||||||
|
|
||||||
// Check if merge style is correct and allowed
|
// Check if merge style is correct and allowed
|
||||||
if !prConfig.IsMergeStyleAllowed(repo_model.MergeStyleManuallyMerged) {
|
if !prConfig.IsMergeStyleAllowed(repo_model.MergeStyleManuallyMerged) {
|
||||||
return models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: repo_model.MergeStyleManuallyMerged}
|
return ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: repo_model.MergeStyleManuallyMerged}
|
||||||
}
|
}
|
||||||
|
|
||||||
objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName)
|
objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName)
|
||||||
|
@ -14,7 +14,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
@ -43,6 +42,23 @@ func (ctx *mergeContext) RunOpts() *git.RunOpts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrSHADoesNotMatch represents a "SHADoesNotMatch" kind of error.
|
||||||
|
type ErrSHADoesNotMatch struct {
|
||||||
|
Path string
|
||||||
|
GivenSHA string
|
||||||
|
CurrentSHA string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrSHADoesNotMatch checks if an error is a ErrSHADoesNotMatch.
|
||||||
|
func IsErrSHADoesNotMatch(err error) bool {
|
||||||
|
_, ok := err.(ErrSHADoesNotMatch)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrSHADoesNotMatch) Error() string {
|
||||||
|
return fmt.Sprintf("sha does not match [given: %s, expected: %s]", err.GivenSHA, err.CurrentSHA)
|
||||||
|
}
|
||||||
|
|
||||||
func createTemporaryRepoForMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, expectedHeadCommitID string) (mergeCtx *mergeContext, cancel context.CancelFunc, err error) {
|
func createTemporaryRepoForMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, expectedHeadCommitID string) (mergeCtx *mergeContext, cancel context.CancelFunc, err error) {
|
||||||
// Clone base repo.
|
// Clone base repo.
|
||||||
prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
|
prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
|
||||||
@ -65,7 +81,7 @@ func createTemporaryRepoForMerge(ctx context.Context, pr *issues_model.PullReque
|
|||||||
}
|
}
|
||||||
if strings.TrimSpace(trackingCommitID) != expectedHeadCommitID {
|
if strings.TrimSpace(trackingCommitID) != expectedHeadCommitID {
|
||||||
defer cancel()
|
defer cancel()
|
||||||
return nil, nil, models.ErrSHADoesNotMatch{
|
return nil, nil, ErrSHADoesNotMatch{
|
||||||
GivenSHA: expectedHeadCommitID,
|
GivenSHA: expectedHeadCommitID,
|
||||||
CurrentSHA: trackingCommitID,
|
CurrentSHA: trackingCommitID,
|
||||||
}
|
}
|
||||||
@ -233,8 +249,27 @@ func getDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string, o
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrRebaseConflicts represents an error if rebase fails with a conflict
|
||||||
|
type ErrRebaseConflicts struct {
|
||||||
|
Style repo_model.MergeStyle
|
||||||
|
CommitSHA string
|
||||||
|
StdOut string
|
||||||
|
StdErr string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrRebaseConflicts checks if an error is a ErrRebaseConflicts.
|
||||||
|
func IsErrRebaseConflicts(err error) bool {
|
||||||
|
_, ok := err.(ErrRebaseConflicts)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrRebaseConflicts) Error() string {
|
||||||
|
return fmt.Sprintf("Rebase Error: %v: Whilst Rebasing: %s\n%s\n%s", err.Err, err.CommitSHA, err.StdErr, err.StdOut)
|
||||||
|
}
|
||||||
|
|
||||||
// rebaseTrackingOnToBase checks out the tracking branch as staging and rebases it on to the base branch
|
// rebaseTrackingOnToBase checks out the tracking branch as staging and rebases it on to the base branch
|
||||||
// if there is a conflict it will return a models.ErrRebaseConflicts
|
// if there is a conflict it will return an ErrRebaseConflicts
|
||||||
func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) error {
|
func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) error {
|
||||||
// Checkout head branch
|
// Checkout head branch
|
||||||
if err := git.NewCommand(ctx, "checkout", "-b").AddDynamicArguments(stagingBranch, trackingBranch).
|
if err := git.NewCommand(ctx, "checkout", "-b").AddDynamicArguments(stagingBranch, trackingBranch).
|
||||||
@ -268,11 +303,11 @@ func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Error("Unable to determine failing commit sha for failing rebase in temp repo for %-v. Cannot cast as models.ErrRebaseConflicts.", ctx.pr)
|
log.Error("Unable to determine failing commit sha for failing rebase in temp repo for %-v. Cannot cast as ErrRebaseConflicts.", ctx.pr)
|
||||||
return fmt.Errorf("unable to git rebase staging on to base in temp repo for %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
|
return fmt.Errorf("unable to git rebase staging on to base in temp repo for %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
|
||||||
}
|
}
|
||||||
log.Debug("Conflict when rebasing staging on to base in %-v at %s: %v\n%s\n%s", ctx.pr, commitSha, err, ctx.outbuf.String(), ctx.errbuf.String())
|
log.Debug("Conflict when rebasing staging on to base in %-v at %s: %v\n%s\n%s", ctx.pr, commitSha, err, ctx.outbuf.String(), ctx.errbuf.String())
|
||||||
return models.ErrRebaseConflicts{
|
return ErrRebaseConflicts{
|
||||||
CommitSHA: commitSha,
|
CommitSHA: commitSha,
|
||||||
Style: mergeStyle,
|
Style: mergeStyle,
|
||||||
StdOut: ctx.outbuf.String(),
|
StdOut: ctx.outbuf.String(),
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
@ -502,6 +501,29 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrFilePathProtected represents a "FilePathProtected" kind of error.
|
||||||
|
type ErrFilePathProtected struct {
|
||||||
|
Message string
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrFilePathProtected checks if an error is an ErrFilePathProtected.
|
||||||
|
func IsErrFilePathProtected(err error) bool {
|
||||||
|
_, ok := err.(ErrFilePathProtected)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrFilePathProtected) Error() string {
|
||||||
|
if err.Message != "" {
|
||||||
|
return err.Message
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("path is protected and can not be changed [path: %s]", err.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrFilePathProtected) Unwrap() error {
|
||||||
|
return util.ErrPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
// CheckFileProtection check file Protection
|
// CheckFileProtection check file Protection
|
||||||
func CheckFileProtection(repo *git.Repository, branchName, oldCommitID, newCommitID string, patterns []glob.Glob, limit int, env []string) ([]string, error) {
|
func CheckFileProtection(repo *git.Repository, branchName, oldCommitID, newCommitID string, patterns []glob.Glob, limit int, env []string) ([]string, error) {
|
||||||
if len(patterns) == 0 {
|
if len(patterns) == 0 {
|
||||||
@ -525,7 +547,7 @@ func CheckFileProtection(repo *git.Repository, branchName, oldCommitID, newCommi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(changedProtectedFiles) > 0 {
|
if len(changedProtectedFiles) > 0 {
|
||||||
err = models.ErrFilePathProtected{
|
err = ErrFilePathProtected{
|
||||||
Path: changedProtectedFiles[0],
|
Path: changedProtectedFiles[0],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -575,7 +597,7 @@ func checkPullFilesProtection(ctx context.Context, pr *issues_model.PullRequest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pr.ChangedProtectedFiles, err = CheckFileProtection(gitRepo, pr.HeadBranch, pr.MergeBase, "tracking", pb.GetProtectedFilePatterns(), 10, os.Environ())
|
pr.ChangedProtectedFiles, err = CheckFileProtection(gitRepo, pr.HeadBranch, pr.MergeBase, "tracking", pb.GetProtectedFilePatterns(), 10, os.Environ())
|
||||||
if err != nil && !models.IsErrFilePathProtected(err) {
|
if err != nil && !IsErrFilePathProtected(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
@ -224,6 +223,28 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrPullRequestHasMerged represents a "PullRequestHasMerged"-error
|
||||||
|
type ErrPullRequestHasMerged struct {
|
||||||
|
ID int64
|
||||||
|
IssueID int64
|
||||||
|
HeadRepoID int64
|
||||||
|
BaseRepoID int64
|
||||||
|
HeadBranch string
|
||||||
|
BaseBranch string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrPullRequestHasMerged checks if an error is a ErrPullRequestHasMerged.
|
||||||
|
func IsErrPullRequestHasMerged(err error) bool {
|
||||||
|
_, ok := err.(ErrPullRequestHasMerged)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error does pretty-printing :D
|
||||||
|
func (err ErrPullRequestHasMerged) Error() string {
|
||||||
|
return fmt.Sprintf("pull request has merged [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]",
|
||||||
|
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
|
||||||
|
}
|
||||||
|
|
||||||
// ChangeTargetBranch changes the target branch of this pull request, as the given user.
|
// ChangeTargetBranch changes the target branch of this pull request, as the given user.
|
||||||
func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, targetBranch string) (err error) {
|
func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, targetBranch string) (err error) {
|
||||||
releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID))
|
releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID))
|
||||||
@ -247,7 +268,7 @@ func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer
|
|||||||
}
|
}
|
||||||
|
|
||||||
if pr.HasMerged {
|
if pr.HasMerged {
|
||||||
return models.ErrPullRequestHasMerged{
|
return ErrPullRequestHasMerged{
|
||||||
ID: pr.ID,
|
ID: pr.ID,
|
||||||
IssueID: pr.Index,
|
IssueID: pr.Index,
|
||||||
HeadRepoID: pr.HeadRepoID,
|
HeadRepoID: pr.HeadRepoID,
|
||||||
@ -654,7 +675,7 @@ func RetargetBranchPulls(ctx context.Context, doer *user_model.User, repoID int6
|
|||||||
if err = pr.Issue.LoadRepo(ctx); err != nil {
|
if err = pr.Issue.LoadRepo(ctx); err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
} else if err = ChangeTargetBranch(ctx, pr, doer, targetBranch); err != nil &&
|
} else if err = ChangeTargetBranch(ctx, pr, doer, targetBranch); err != nil &&
|
||||||
!issues_model.IsErrIssueIsClosed(err) && !models.IsErrPullRequestHasMerged(err) &&
|
!issues_model.IsErrIssueIsClosed(err) && !IsErrPullRequestHasMerged(err) &&
|
||||||
!issues_model.IsErrPullRequestAlreadyExists(err) {
|
!issues_model.IsErrPullRequestAlreadyExists(err) {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
@ -26,6 +25,44 @@ import (
|
|||||||
notify_service "code.gitea.io/gitea/services/notify"
|
notify_service "code.gitea.io/gitea/services/notify"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrInvalidTagName represents a "InvalidTagName" kind of error.
|
||||||
|
type ErrInvalidTagName struct {
|
||||||
|
TagName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrInvalidTagName checks if an error is a ErrInvalidTagName.
|
||||||
|
func IsErrInvalidTagName(err error) bool {
|
||||||
|
_, ok := err.(ErrInvalidTagName)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrInvalidTagName) Error() string {
|
||||||
|
return fmt.Sprintf("release tag name is not valid [tag_name: %s]", err.TagName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrInvalidTagName) Unwrap() error {
|
||||||
|
return util.ErrInvalidArgument
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrProtectedTagName represents a "ProtectedTagName" kind of error.
|
||||||
|
type ErrProtectedTagName struct {
|
||||||
|
TagName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrProtectedTagName checks if an error is a ErrProtectedTagName.
|
||||||
|
func IsErrProtectedTagName(err error) bool {
|
||||||
|
_, ok := err.(ErrProtectedTagName)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrProtectedTagName) Error() string {
|
||||||
|
return fmt.Sprintf("release tag name is protected [tag_name: %s]", err.TagName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrProtectedTagName) Unwrap() error {
|
||||||
|
return util.ErrPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Release, msg string) (bool, error) {
|
func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Release, msg string) (bool, error) {
|
||||||
err := rel.LoadAttributes(ctx)
|
err := rel.LoadAttributes(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -58,7 +95,7 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if !isAllowed {
|
if !isAllowed {
|
||||||
return false, models.ErrProtectedTagName{
|
return false, ErrProtectedTagName{
|
||||||
TagName: rel.TagName,
|
TagName: rel.TagName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,7 +108,7 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel
|
|||||||
if len(msg) > 0 {
|
if len(msg) > 0 {
|
||||||
if err = gitRepo.CreateAnnotatedTag(rel.TagName, msg, commit.ID.String()); err != nil {
|
if err = gitRepo.CreateAnnotatedTag(rel.TagName, msg, commit.ID.String()); err != nil {
|
||||||
if strings.Contains(err.Error(), "is not a valid tag name") {
|
if strings.Contains(err.Error(), "is not a valid tag name") {
|
||||||
return false, models.ErrInvalidTagName{
|
return false, ErrInvalidTagName{
|
||||||
TagName: rel.TagName,
|
TagName: rel.TagName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,7 +116,7 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel
|
|||||||
}
|
}
|
||||||
} else if err = gitRepo.CreateTag(rel.TagName, commit.ID.String()); err != nil {
|
} else if err = gitRepo.CreateTag(rel.TagName, commit.ID.String()); err != nil {
|
||||||
if strings.Contains(err.Error(), "is not a valid tag name") {
|
if strings.Contains(err.Error(), "is not a valid tag name") {
|
||||||
return false, models.ErrInvalidTagName{
|
return false, ErrInvalidTagName{
|
||||||
TagName: rel.TagName,
|
TagName: rel.TagName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,13 +196,32 @@ func CreateRelease(gitRepo *git.Repository, rel *repo_model.Release, attachmentU
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrTagAlreadyExists represents an error that tag with such name already exists.
|
||||||
|
type ErrTagAlreadyExists struct {
|
||||||
|
TagName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrTagAlreadyExists checks if an error is an ErrTagAlreadyExists.
|
||||||
|
func IsErrTagAlreadyExists(err error) bool {
|
||||||
|
_, ok := err.(ErrTagAlreadyExists)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrTagAlreadyExists) Error() string {
|
||||||
|
return fmt.Sprintf("tag already exists [name: %s]", err.TagName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrTagAlreadyExists) Unwrap() error {
|
||||||
|
return util.ErrAlreadyExist
|
||||||
|
}
|
||||||
|
|
||||||
// CreateNewTag creates a new repository tag
|
// CreateNewTag creates a new repository tag
|
||||||
func CreateNewTag(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, commit, tagName, msg string) error {
|
func CreateNewTag(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, commit, tagName, msg string) error {
|
||||||
has, err := repo_model.IsReleaseExist(ctx, repo.ID, tagName)
|
has, err := repo_model.IsReleaseExist(ctx, repo.ID, tagName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if has {
|
} else if has {
|
||||||
return models.ErrTagAlreadyExists{
|
return ErrTagAlreadyExists{
|
||||||
TagName: tagName,
|
TagName: tagName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -320,13 +376,12 @@ func DeleteReleaseByID(ctx context.Context, repo *repo_model.Repository, rel *re
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !isAllowed {
|
if !isAllowed {
|
||||||
return models.ErrProtectedTagName{
|
return ErrProtectedTagName{
|
||||||
TagName: rel.TagName,
|
TagName: rel.TagName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if stdout, _, err := git.NewCommand(ctx, "tag", "-d").AddDashesAndList(rel.TagName).
|
if stdout, _, err := git.NewCommand(ctx, "tag", "-d").AddDashesAndList(rel.TagName).
|
||||||
SetDescription(fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID)).
|
|
||||||
RunStdString(&git.RunOpts{Dir: repo.RepoPath()}); err != nil && !strings.Contains(err.Error(), "not found") {
|
RunStdString(&git.RunOpts{Dir: repo.RepoPath()}); err != nil && !strings.Contains(err.Error(), "not found") {
|
||||||
log.Error("DeleteReleaseByID (git tag -d): %d in %v Failed:\nStdout: %s\nError: %v", rel.ID, repo, stdout, err)
|
log.Error("DeleteReleaseByID (git tag -d): %d in %v Failed:\nStdout: %s\nError: %v", rel.ID, repo, stdout, err)
|
||||||
return fmt.Errorf("git tag -d: %w", err)
|
return fmt.Errorf("git tag -d: %w", err)
|
||||||
|
@ -92,7 +92,6 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateR
|
|||||||
}
|
}
|
||||||
|
|
||||||
if stdout, _, err := git.NewCommand(ctx, "update-server-info").
|
if stdout, _, err := git.NewCommand(ctx, "update-server-info").
|
||||||
SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath)).
|
|
||||||
RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
|
RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
|
||||||
log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
|
log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
|
||||||
return fmt.Errorf("CreateRepository(git update-server-info): %w", err)
|
return fmt.Errorf("CreateRepository(git update-server-info): %w", err)
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
actions_model "code.gitea.io/gitea/models/actions"
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
@ -31,6 +30,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
notify_service "code.gitea.io/gitea/services/notify"
|
notify_service "code.gitea.io/gitea/services/notify"
|
||||||
|
release_service "code.gitea.io/gitea/services/release"
|
||||||
files_service "code.gitea.io/gitea/services/repository/files"
|
files_service "code.gitea.io/gitea/services/repository/files"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
@ -274,7 +274,7 @@ func checkBranchName(ctx context.Context, repo *repo_model.Repository, name stri
|
|||||||
BranchName: branchRefName,
|
BranchName: branchRefName,
|
||||||
}
|
}
|
||||||
case refName == git.TagPrefix+name:
|
case refName == git.TagPrefix+name:
|
||||||
return models.ErrTagAlreadyExists{
|
return release_service.ErrTagAlreadyExists{
|
||||||
TagName: name,
|
TagName: name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,8 +86,7 @@ func GitGcRepos(ctx context.Context, timeout time.Duration, args git.TrustedCmdA
|
|||||||
// GitGcRepo calls 'git gc' to remove unnecessary files and optimize the local repository
|
// GitGcRepo calls 'git gc' to remove unnecessary files and optimize the local repository
|
||||||
func GitGcRepo(ctx context.Context, repo *repo_model.Repository, timeout time.Duration, args git.TrustedCmdArgs) error {
|
func GitGcRepo(ctx context.Context, repo *repo_model.Repository, timeout time.Duration, args git.TrustedCmdArgs) error {
|
||||||
log.Trace("Running git gc on %-v", repo)
|
log.Trace("Running git gc on %-v", repo)
|
||||||
command := git.NewCommand(ctx, "gc").AddArguments(args...).
|
command := git.NewCommand(ctx, "gc").AddArguments(args...)
|
||||||
SetDescription(fmt.Sprintf("Repository Garbage Collection: %s", repo.FullName()))
|
|
||||||
var stdout string
|
var stdout string
|
||||||
var err error
|
var err error
|
||||||
stdout, _, err = command.RunStdString(&git.RunOpts{Timeout: timeout, Dir: repo.RepoPath()})
|
stdout, _, err = command.RunStdString(&git.RunOpts{Timeout: timeout, Dir: repo.RepoPath()})
|
||||||
|
@ -68,7 +68,6 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir,
|
|||||||
|
|
||||||
// Clone to temporary path and do the init commit.
|
// Clone to temporary path and do the init commit.
|
||||||
if stdout, _, err := git.NewCommand(ctx, "clone").AddDynamicArguments(repoPath, tmpDir).
|
if stdout, _, err := git.NewCommand(ctx, "clone").AddDynamicArguments(repoPath, tmpDir).
|
||||||
SetDescription(fmt.Sprintf("prepareRepoCommit (git clone): %s to %s", repoPath, tmpDir)).
|
|
||||||
RunStdString(&git.RunOpts{Dir: "", Env: env}); err != nil {
|
RunStdString(&git.RunOpts{Dir: "", Env: env}); err != nil {
|
||||||
log.Error("Failed to clone from %v into %s: stdout: %s\nError: %v", repo, tmpDir, stdout, err)
|
log.Error("Failed to clone from %v into %s: stdout: %s\nError: %v", repo, tmpDir, stdout, err)
|
||||||
return fmt.Errorf("git clone: %w", err)
|
return fmt.Errorf("git clone: %w", err)
|
||||||
@ -301,7 +300,6 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
|
|||||||
}
|
}
|
||||||
|
|
||||||
if stdout, _, err := git.NewCommand(ctx, "update-server-info").
|
if stdout, _, err := git.NewCommand(ctx, "update-server-info").
|
||||||
SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath)).
|
|
||||||
RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
|
RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
|
||||||
log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
|
log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
|
||||||
rollbackRepo = repo
|
rollbackRepo = repo
|
||||||
@ -314,9 +312,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
|
|||||||
if len(opts.License) > 0 {
|
if len(opts.License) > 0 {
|
||||||
licenses = append(licenses, ConvertLicenseName(opts.License))
|
licenses = append(licenses, ConvertLicenseName(opts.License))
|
||||||
|
|
||||||
stdout, _, err := git.NewCommand(ctx, "rev-parse", "HEAD").
|
stdout, _, err := git.NewCommand(ctx, "rev-parse", "HEAD").RunStdString(&git.RunOpts{Dir: repoPath})
|
||||||
SetDescription(fmt.Sprintf("CreateRepository(git rev-parse HEAD): %s", repoPath)).
|
|
||||||
RunStdString(&git.RunOpts{Dir: repoPath})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("CreateRepository(git rev-parse HEAD) in %v: Stdout: %s\nError: %v", repo, stdout, err)
|
log.Error("CreateRepository(git rev-parse HEAD) in %v: Stdout: %s\nError: %v", repo, stdout, err)
|
||||||
rollbackRepo = repo
|
rollbackRepo = repo
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
@ -17,6 +16,22 @@ import (
|
|||||||
"code.gitea.io/gitea/services/pull"
|
"code.gitea.io/gitea/services/pull"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrCommitIDDoesNotMatch represents a "CommitIDDoesNotMatch" kind of error.
|
||||||
|
type ErrCommitIDDoesNotMatch struct {
|
||||||
|
GivenCommitID string
|
||||||
|
CurrentCommitID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrCommitIDDoesNotMatch checks if an error is a ErrCommitIDDoesNotMatch.
|
||||||
|
func IsErrCommitIDDoesNotMatch(err error) bool {
|
||||||
|
_, ok := err.(ErrCommitIDDoesNotMatch)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrCommitIDDoesNotMatch) Error() string {
|
||||||
|
return fmt.Sprintf("file CommitID does not match [given: %s, expected: %s]", err.GivenCommitID, err.CurrentCommitID)
|
||||||
|
}
|
||||||
|
|
||||||
// CherryPick cherrypicks or reverts a commit to the given repository
|
// CherryPick cherrypicks or reverts a commit to the given repository
|
||||||
func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, revert bool, opts *ApplyDiffPatchOptions) (*structs.FileResponse, error) {
|
func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, revert bool, opts *ApplyDiffPatchOptions) (*structs.FileResponse, error) {
|
||||||
if err := opts.Validate(ctx, repo, doer); err != nil {
|
if err := opts.Validate(ctx, repo, doer); err != nil {
|
||||||
@ -57,7 +72,7 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod
|
|||||||
}
|
}
|
||||||
opts.LastCommitID = lastCommitID.String()
|
opts.LastCommitID = lastCommitID.String()
|
||||||
if commit.ID.String() != opts.LastCommitID {
|
if commit.ID.String() != opts.LastCommitID {
|
||||||
return nil, models.ErrCommitIDDoesNotMatch{
|
return nil, ErrCommitIDDoesNotMatch{
|
||||||
GivenCommitID: opts.LastCommitID,
|
GivenCommitID: opts.LastCommitID,
|
||||||
CurrentCommitID: opts.LastCommitID,
|
CurrentCommitID: opts.LastCommitID,
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/gitrepo"
|
"code.gitea.io/gitea/modules/gitrepo"
|
||||||
@ -53,7 +52,7 @@ func GetContentsOrList(ctx context.Context, repo *repo_model.Repository, treePat
|
|||||||
// Check that the path given in opts.treePath is valid (not a git path)
|
// Check that the path given in opts.treePath is valid (not a git path)
|
||||||
cleanTreePath := CleanUploadFileName(treePath)
|
cleanTreePath := CleanUploadFileName(treePath)
|
||||||
if cleanTreePath == "" && treePath != "" {
|
if cleanTreePath == "" && treePath != "" {
|
||||||
return nil, models.ErrFilenameInvalid{
|
return nil, ErrFilenameInvalid{
|
||||||
Path: treePath,
|
Path: treePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,7 +127,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref
|
|||||||
// Check that the path given in opts.treePath is valid (not a git path)
|
// Check that the path given in opts.treePath is valid (not a git path)
|
||||||
cleanTreePath := CleanUploadFileName(treePath)
|
cleanTreePath := CleanUploadFileName(treePath)
|
||||||
if cleanTreePath == "" && treePath != "" {
|
if cleanTreePath == "" && treePath != "" {
|
||||||
return nil, models.ErrFilenameInvalid{
|
return nil, ErrFilenameInvalid{
|
||||||
Path: treePath,
|
Path: treePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,6 +156,25 @@ func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *user_m
|
|||||||
return authorUser, committerUser
|
return authorUser, committerUser
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrFilenameInvalid represents a "FilenameInvalid" kind of error.
|
||||||
|
type ErrFilenameInvalid struct {
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrFilenameInvalid checks if an error is an ErrFilenameInvalid.
|
||||||
|
func IsErrFilenameInvalid(err error) bool {
|
||||||
|
_, ok := err.(ErrFilenameInvalid)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrFilenameInvalid) Error() string {
|
||||||
|
return fmt.Sprintf("path contains a malformed path component [path: %s]", err.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrFilenameInvalid) Unwrap() error {
|
||||||
|
return util.ErrInvalidArgument
|
||||||
|
}
|
||||||
|
|
||||||
// CleanUploadFileName Trims a filename and returns empty string if it is a .git directory
|
// CleanUploadFileName Trims a filename and returns empty string if it is a .git directory
|
||||||
func CleanUploadFileName(name string) string {
|
func CleanUploadFileName(name string) string {
|
||||||
// Rebase the filename
|
// Rebase the filename
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
@ -16,9 +15,29 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/gitrepo"
|
"code.gitea.io/gitea/modules/gitrepo"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/structs"
|
"code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrUserCannotCommit represents "UserCannotCommit" kind of error.
|
||||||
|
type ErrUserCannotCommit struct {
|
||||||
|
UserName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrUserCannotCommit checks if an error is an ErrUserCannotCommit.
|
||||||
|
func IsErrUserCannotCommit(err error) bool {
|
||||||
|
_, ok := err.(ErrUserCannotCommit)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrUserCannotCommit) Error() string {
|
||||||
|
return fmt.Sprintf("user cannot commit to repo [user: %s]", err.UserName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrUserCannotCommit) Unwrap() error {
|
||||||
|
return util.ErrPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
// ApplyDiffPatchOptions holds the repository diff patch update options
|
// ApplyDiffPatchOptions holds the repository diff patch update options
|
||||||
type ApplyDiffPatchOptions struct {
|
type ApplyDiffPatchOptions struct {
|
||||||
LastCommitID string
|
LastCommitID string
|
||||||
@ -74,7 +93,7 @@ func (opts *ApplyDiffPatchOptions) Validate(ctx context.Context, repo *repo_mode
|
|||||||
if protectedBranch != nil {
|
if protectedBranch != nil {
|
||||||
protectedBranch.Repo = repo
|
protectedBranch.Repo = repo
|
||||||
if !protectedBranch.CanUserPush(ctx, doer) {
|
if !protectedBranch.CanUserPush(ctx, doer) {
|
||||||
return models.ErrUserCannotCommit{
|
return ErrUserCannotCommit{
|
||||||
UserName: doer.LowerName,
|
UserName: doer.LowerName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,7 +104,7 @@ func (opts *ApplyDiffPatchOptions) Validate(ctx context.Context, repo *repo_mode
|
|||||||
if !asymkey_service.IsErrWontSign(err) {
|
if !asymkey_service.IsErrWontSign(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return models.ErrUserCannotCommit{
|
return ErrUserCannotCommit{
|
||||||
UserName: doer.LowerName,
|
UserName: doer.LowerName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,7 +156,7 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
|
|||||||
}
|
}
|
||||||
opts.LastCommitID = lastCommitID.String()
|
opts.LastCommitID = lastCommitID.String()
|
||||||
if commit.ID.String() != opts.LastCommitID {
|
if commit.ID.String() != opts.LastCommitID {
|
||||||
return nil, models.ErrCommitIDDoesNotMatch{
|
return nil, ErrCommitIDDoesNotMatch{
|
||||||
GivenCommitID: opts.LastCommitID,
|
GivenCommitID: opts.LastCommitID,
|
||||||
CurrentCommitID: opts.LastCommitID,
|
CurrentCommitID: opts.LastCommitID,
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
@ -187,7 +186,7 @@ func (t *TemporaryUploadRepository) AddObjectToIndex(mode, objectHash, objectPat
|
|||||||
if _, _, err := git.NewCommand(t.ctx, "update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments(mode, objectHash, objectPath).RunStdString(&git.RunOpts{Dir: t.basePath}); err != nil {
|
if _, _, err := git.NewCommand(t.ctx, "update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments(mode, objectHash, objectPath).RunStdString(&git.RunOpts{Dir: t.basePath}); err != nil {
|
||||||
stderr := err.Error()
|
stderr := err.Error()
|
||||||
if matched, _ := regexp.MatchString(".*Invalid path '.*", stderr); matched {
|
if matched, _ := regexp.MatchString(".*Invalid path '.*", stderr); matched {
|
||||||
return models.ErrFilePathInvalid{
|
return ErrFilePathInvalid{
|
||||||
Message: objectPath,
|
Message: objectPath,
|
||||||
Path: objectPath,
|
Path: objectPath,
|
||||||
}
|
}
|
||||||
|
@ -8,18 +8,37 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrSHANotFound represents a "SHADoesNotMatch" kind of error.
|
||||||
|
type ErrSHANotFound struct {
|
||||||
|
SHA string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrSHANotFound checks if an error is a ErrSHANotFound.
|
||||||
|
func IsErrSHANotFound(err error) bool {
|
||||||
|
_, ok := err.(ErrSHANotFound)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrSHANotFound) Error() string {
|
||||||
|
return fmt.Sprintf("sha not found [%s]", err.SHA)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrSHANotFound) Unwrap() error {
|
||||||
|
return util.ErrNotExist
|
||||||
|
}
|
||||||
|
|
||||||
// GetTreeBySHA get the GitTreeResponse of a repository using a sha hash.
|
// GetTreeBySHA get the GitTreeResponse of a repository using a sha hash.
|
||||||
func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, sha string, page, perPage int, recursive bool) (*api.GitTreeResponse, error) {
|
func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, sha string, page, perPage int, recursive bool) (*api.GitTreeResponse, error) {
|
||||||
gitTree, err := gitRepo.GetTree(sha)
|
gitTree, err := gitRepo.GetTree(sha)
|
||||||
if err != nil || gitTree == nil {
|
if err != nil || gitTree == nil {
|
||||||
return nil, models.ErrSHANotFound{
|
return nil, ErrSHANotFound{ // TODO: this error has never been catch outside of this function
|
||||||
SHA: sha,
|
SHA: sha,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
@ -21,7 +20,9 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/structs"
|
"code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
||||||
|
pull_service "code.gitea.io/gitea/services/pull"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IdentityOptions for a person's identity like an author or committer
|
// IdentityOptions for a person's identity like an author or committer
|
||||||
@ -64,6 +65,26 @@ type RepoFileOptions struct {
|
|||||||
executable bool
|
executable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrRepoFileDoesNotExist represents a "RepoFileDoesNotExist" kind of error.
|
||||||
|
type ErrRepoFileDoesNotExist struct {
|
||||||
|
Path string
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrRepoFileDoesNotExist checks if an error is a ErrRepoDoesNotExist.
|
||||||
|
func IsErrRepoFileDoesNotExist(err error) bool {
|
||||||
|
_, ok := err.(ErrRepoFileDoesNotExist)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrRepoFileDoesNotExist) Error() string {
|
||||||
|
return fmt.Sprintf("repository file does not exist [path: %s]", err.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrRepoFileDoesNotExist) Unwrap() error {
|
||||||
|
return util.ErrNotExist
|
||||||
|
}
|
||||||
|
|
||||||
// ChangeRepoFiles adds, updates or removes multiple files in the given repository
|
// ChangeRepoFiles adds, updates or removes multiple files in the given repository
|
||||||
func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, opts *ChangeRepoFilesOptions) (*structs.FilesResponse, error) {
|
func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, opts *ChangeRepoFilesOptions) (*structs.FilesResponse, error) {
|
||||||
err := repo.MustNotBeArchived()
|
err := repo.MustNotBeArchived()
|
||||||
@ -100,14 +121,14 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
|
|||||||
// Check that the path given in opts.treePath is valid (not a git path)
|
// Check that the path given in opts.treePath is valid (not a git path)
|
||||||
treePath := CleanUploadFileName(file.TreePath)
|
treePath := CleanUploadFileName(file.TreePath)
|
||||||
if treePath == "" {
|
if treePath == "" {
|
||||||
return nil, models.ErrFilenameInvalid{
|
return nil, ErrFilenameInvalid{
|
||||||
Path: file.TreePath,
|
Path: file.TreePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If there is a fromTreePath (we are copying it), also clean it up
|
// If there is a fromTreePath (we are copying it), also clean it up
|
||||||
fromTreePath := CleanUploadFileName(file.FromTreePath)
|
fromTreePath := CleanUploadFileName(file.FromTreePath)
|
||||||
if fromTreePath == "" && file.FromTreePath != "" {
|
if fromTreePath == "" && file.FromTreePath != "" {
|
||||||
return nil, models.ErrFilenameInvalid{
|
return nil, ErrFilenameInvalid{
|
||||||
Path: file.FromTreePath,
|
Path: file.FromTreePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,7 +206,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !inFilelist {
|
if !inFilelist {
|
||||||
return nil, models.ErrRepoFileDoesNotExist{
|
return nil, ErrRepoFileDoesNotExist{
|
||||||
Path: file.TreePath,
|
Path: file.TreePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,6 +297,63 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
|
|||||||
return filesResponse, nil
|
return filesResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrRepoFileAlreadyExists represents a "RepoFileAlreadyExist" kind of error.
|
||||||
|
type ErrRepoFileAlreadyExists struct {
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrRepoFileAlreadyExists checks if an error is a ErrRepoFileAlreadyExists.
|
||||||
|
func IsErrRepoFileAlreadyExists(err error) bool {
|
||||||
|
_, ok := err.(ErrRepoFileAlreadyExists)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrRepoFileAlreadyExists) Error() string {
|
||||||
|
return fmt.Sprintf("repository file already exists [path: %s]", err.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrRepoFileAlreadyExists) Unwrap() error {
|
||||||
|
return util.ErrAlreadyExist
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrFilePathInvalid represents a "FilePathInvalid" kind of error.
|
||||||
|
type ErrFilePathInvalid struct {
|
||||||
|
Message string
|
||||||
|
Path string
|
||||||
|
Name string
|
||||||
|
Type git.EntryMode
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrFilePathInvalid checks if an error is an ErrFilePathInvalid.
|
||||||
|
func IsErrFilePathInvalid(err error) bool {
|
||||||
|
_, ok := err.(ErrFilePathInvalid)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrFilePathInvalid) Error() string {
|
||||||
|
if err.Message != "" {
|
||||||
|
return err.Message
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("path is invalid [path: %s]", err.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrFilePathInvalid) Unwrap() error {
|
||||||
|
return util.ErrInvalidArgument
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrSHAOrCommitIDNotProvided represents a "SHAOrCommitIDNotProvided" kind of error.
|
||||||
|
type ErrSHAOrCommitIDNotProvided struct{}
|
||||||
|
|
||||||
|
// IsErrSHAOrCommitIDNotProvided checks if an error is a ErrSHAOrCommitIDNotProvided.
|
||||||
|
func IsErrSHAOrCommitIDNotProvided(err error) bool {
|
||||||
|
_, ok := err.(ErrSHAOrCommitIDNotProvided)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrSHAOrCommitIDNotProvided) Error() string {
|
||||||
|
return "a SHA or commit ID must be proved when updating a file"
|
||||||
|
}
|
||||||
|
|
||||||
// handles the check for various issues for ChangeRepoFiles
|
// handles the check for various issues for ChangeRepoFiles
|
||||||
func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRepoFilesOptions) error {
|
func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRepoFilesOptions) error {
|
||||||
if file.Operation == "update" || file.Operation == "delete" {
|
if file.Operation == "update" || file.Operation == "delete" {
|
||||||
@ -286,7 +364,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep
|
|||||||
if file.SHA != "" {
|
if file.SHA != "" {
|
||||||
// If a SHA was given and the SHA given doesn't match the SHA of the fromTreePath, throw error
|
// If a SHA was given and the SHA given doesn't match the SHA of the fromTreePath, throw error
|
||||||
if file.SHA != fromEntry.ID.String() {
|
if file.SHA != fromEntry.ID.String() {
|
||||||
return models.ErrSHADoesNotMatch{
|
return pull_service.ErrSHADoesNotMatch{
|
||||||
Path: file.Options.treePath,
|
Path: file.Options.treePath,
|
||||||
GivenSHA: file.SHA,
|
GivenSHA: file.SHA,
|
||||||
CurrentSHA: fromEntry.ID.String(),
|
CurrentSHA: fromEntry.ID.String(),
|
||||||
@ -299,7 +377,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep
|
|||||||
if changed, err := commit.FileChangedSinceCommit(file.Options.treePath, opts.LastCommitID); err != nil {
|
if changed, err := commit.FileChangedSinceCommit(file.Options.treePath, opts.LastCommitID); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if changed {
|
} else if changed {
|
||||||
return models.ErrCommitIDDoesNotMatch{
|
return ErrCommitIDDoesNotMatch{
|
||||||
GivenCommitID: opts.LastCommitID,
|
GivenCommitID: opts.LastCommitID,
|
||||||
CurrentCommitID: opts.LastCommitID,
|
CurrentCommitID: opts.LastCommitID,
|
||||||
}
|
}
|
||||||
@ -309,7 +387,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep
|
|||||||
} else {
|
} else {
|
||||||
// When updating a file, a lastCommitID or SHA needs to be given to make sure other commits
|
// When updating a file, a lastCommitID or SHA needs to be given to make sure other commits
|
||||||
// haven't been made. We throw an error if one wasn't provided.
|
// haven't been made. We throw an error if one wasn't provided.
|
||||||
return models.ErrSHAOrCommitIDNotProvided{}
|
return ErrSHAOrCommitIDNotProvided{}
|
||||||
}
|
}
|
||||||
file.Options.executable = fromEntry.IsExecutable()
|
file.Options.executable = fromEntry.IsExecutable()
|
||||||
}
|
}
|
||||||
@ -332,7 +410,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep
|
|||||||
}
|
}
|
||||||
if index < len(treePathParts)-1 {
|
if index < len(treePathParts)-1 {
|
||||||
if !entry.IsDir() {
|
if !entry.IsDir() {
|
||||||
return models.ErrFilePathInvalid{
|
return ErrFilePathInvalid{
|
||||||
Message: fmt.Sprintf("a file exists where you’re trying to create a subdirectory [path: %s]", subTreePath),
|
Message: fmt.Sprintf("a file exists where you’re trying to create a subdirectory [path: %s]", subTreePath),
|
||||||
Path: subTreePath,
|
Path: subTreePath,
|
||||||
Name: part,
|
Name: part,
|
||||||
@ -340,14 +418,14 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if entry.IsLink() {
|
} else if entry.IsLink() {
|
||||||
return models.ErrFilePathInvalid{
|
return ErrFilePathInvalid{
|
||||||
Message: fmt.Sprintf("a symbolic link exists where you’re trying to create a subdirectory [path: %s]", subTreePath),
|
Message: fmt.Sprintf("a symbolic link exists where you’re trying to create a subdirectory [path: %s]", subTreePath),
|
||||||
Path: subTreePath,
|
Path: subTreePath,
|
||||||
Name: part,
|
Name: part,
|
||||||
Type: git.EntryModeSymlink,
|
Type: git.EntryModeSymlink,
|
||||||
}
|
}
|
||||||
} else if entry.IsDir() {
|
} else if entry.IsDir() {
|
||||||
return models.ErrFilePathInvalid{
|
return ErrFilePathInvalid{
|
||||||
Message: fmt.Sprintf("a directory exists where you’re trying to create a file [path: %s]", subTreePath),
|
Message: fmt.Sprintf("a directory exists where you’re trying to create a file [path: %s]", subTreePath),
|
||||||
Path: subTreePath,
|
Path: subTreePath,
|
||||||
Name: part,
|
Name: part,
|
||||||
@ -355,7 +433,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep
|
|||||||
}
|
}
|
||||||
} else if file.Options.fromTreePath != file.Options.treePath || file.Operation == "create" {
|
} else if file.Options.fromTreePath != file.Options.treePath || file.Operation == "create" {
|
||||||
// The entry shouldn't exist if we are creating new file or moving to a new path
|
// The entry shouldn't exist if we are creating new file or moving to a new path
|
||||||
return models.ErrRepoFileAlreadyExists{
|
return ErrRepoFileAlreadyExists{
|
||||||
Path: file.Options.treePath,
|
Path: file.Options.treePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -376,7 +454,7 @@ func CreateOrUpdateFile(ctx context.Context, t *TemporaryUploadRepository, file
|
|||||||
if file.Operation == "create" {
|
if file.Operation == "create" {
|
||||||
for _, indexFile := range filesInIndex {
|
for _, indexFile := range filesInIndex {
|
||||||
if indexFile == file.TreePath {
|
if indexFile == file.TreePath {
|
||||||
return models.ErrRepoFileAlreadyExists{
|
return ErrRepoFileAlreadyExists{
|
||||||
Path: file.TreePath,
|
Path: file.TreePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -479,12 +557,12 @@ func VerifyBranchProtection(ctx context.Context, repo *repo_model.Repository, do
|
|||||||
isUnprotectedFile = protectedBranch.IsUnprotectedFile(globUnprotected, treePath)
|
isUnprotectedFile = protectedBranch.IsUnprotectedFile(globUnprotected, treePath)
|
||||||
}
|
}
|
||||||
if !canUserPush && !isUnprotectedFile {
|
if !canUserPush && !isUnprotectedFile {
|
||||||
return models.ErrUserCannotCommit{
|
return ErrUserCannotCommit{
|
||||||
UserName: doer.LowerName,
|
UserName: doer.LowerName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if protectedBranch.IsProtectedFile(globProtected, treePath) {
|
if protectedBranch.IsProtectedFile(globProtected, treePath) {
|
||||||
return models.ErrFilePathProtected{
|
return pull_service.ErrFilePathProtected{
|
||||||
Path: treePath,
|
Path: treePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -495,7 +573,7 @@ func VerifyBranchProtection(ctx context.Context, repo *repo_model.Repository, do
|
|||||||
if !asymkey_service.IsErrWontSign(err) {
|
if !asymkey_service.IsErrWontSign(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return models.ErrUserCannotCommit{
|
return ErrUserCannotCommit{
|
||||||
UserName: doer.LowerName,
|
UserName: doer.LowerName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,6 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
|
|||||||
}
|
}
|
||||||
repoPath := repo_model.RepoPath(owner.Name, repo.Name)
|
repoPath := repo_model.RepoPath(owner.Name, repo.Name)
|
||||||
if stdout, _, err := cloneCmd.AddDynamicArguments(oldRepoPath, repoPath).
|
if stdout, _, err := cloneCmd.AddDynamicArguments(oldRepoPath, repoPath).
|
||||||
SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", opts.BaseRepo.FullName(), repo.FullName())).
|
|
||||||
RunStdBytes(&git.RunOpts{Timeout: 10 * time.Minute}); err != nil {
|
RunStdBytes(&git.RunOpts{Timeout: 10 * time.Minute}); err != nil {
|
||||||
log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, opts.BaseRepo, stdout, err)
|
log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, opts.BaseRepo, stdout, err)
|
||||||
return fmt.Errorf("git clone: %w", err)
|
return fmt.Errorf("git clone: %w", err)
|
||||||
@ -169,7 +168,6 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
|
|||||||
}
|
}
|
||||||
|
|
||||||
if stdout, _, err := git.NewCommand(txCtx, "update-server-info").
|
if stdout, _, err := git.NewCommand(txCtx, "update-server-info").
|
||||||
SetDescription(fmt.Sprintf("ForkRepository(git update-server-info): %s", repo.FullName())).
|
|
||||||
RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
|
RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
|
||||||
log.Error("Fork Repository (git update-server-info) failed for %v:\nStdout: %s\nError: %v", repo, stdout, err)
|
log.Error("Fork Repository (git update-server-info) failed for %v:\nStdout: %s\nError: %v", repo, stdout, err)
|
||||||
return fmt.Errorf("git update-server-info: %w", err)
|
return fmt.Errorf("git update-server-info: %w", err)
|
||||||
|
@ -237,7 +237,6 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r
|
|||||||
|
|
||||||
repoPath := repo.RepoPath()
|
repoPath := repo.RepoPath()
|
||||||
if stdout, _, err := git.NewCommand(ctx, "remote", "add", "origin").AddDynamicArguments(repoPath).
|
if stdout, _, err := git.NewCommand(ctx, "remote", "add", "origin").AddDynamicArguments(repoPath).
|
||||||
SetDescription(fmt.Sprintf("generateRepoCommit (git remote add): %s to %s", templateRepoPath, tmpDir)).
|
|
||||||
RunStdString(&git.RunOpts{Dir: tmpDir, Env: env}); err != nil {
|
RunStdString(&git.RunOpts{Dir: tmpDir, Env: env}); err != nil {
|
||||||
log.Error("Unable to add %v as remote origin to temporary repo to %s: stdout %s\nError: %v", repo, tmpDir, stdout, err)
|
log.Error("Unable to add %v as remote origin to temporary repo to %s: stdout %s\nError: %v", repo, tmpDir, stdout, err)
|
||||||
return fmt.Errorf("git remote add: %w", err)
|
return fmt.Errorf("git remote add: %w", err)
|
||||||
@ -369,7 +368,6 @@ func generateRepository(ctx context.Context, doer, owner *user_model.User, templ
|
|||||||
}
|
}
|
||||||
|
|
||||||
if stdout, _, err := git.NewCommand(ctx, "update-server-info").
|
if stdout, _, err := git.NewCommand(ctx, "update-server-info").
|
||||||
SetDescription(fmt.Sprintf("GenerateRepository(git update-server-info): %s", repoPath)).
|
|
||||||
RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
|
RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
|
||||||
log.Error("GenerateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", generateRepo, stdout, err)
|
log.Error("GenerateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", generateRepo, stdout, err)
|
||||||
return generateRepo, fmt.Errorf("error in GenerateRepository(git update-server-info): %w", err)
|
return generateRepo, fmt.Errorf("error in GenerateRepository(git update-server-info): %w", err)
|
||||||
|
@ -34,7 +34,6 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
|
|||||||
committerEmail := sig.Email
|
committerEmail := sig.Email
|
||||||
|
|
||||||
if stdout, _, err := git.NewCommand(ctx, "add", "--all").
|
if stdout, _, err := git.NewCommand(ctx, "add", "--all").
|
||||||
SetDescription(fmt.Sprintf("initRepoCommit (git add): %s", tmpPath)).
|
|
||||||
RunStdString(&git.RunOpts{Dir: tmpPath}); err != nil {
|
RunStdString(&git.RunOpts{Dir: tmpPath}); err != nil {
|
||||||
log.Error("git add --all failed: Stdout: %s\nError: %v", stdout, err)
|
log.Error("git add --all failed: Stdout: %s\nError: %v", stdout, err)
|
||||||
return fmt.Errorf("git add --all: %w", err)
|
return fmt.Errorf("git add --all: %w", err)
|
||||||
@ -62,9 +61,8 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
|
|||||||
)
|
)
|
||||||
|
|
||||||
if stdout, _, err := cmd.
|
if stdout, _, err := cmd.
|
||||||
SetDescription(fmt.Sprintf("initRepoCommit (git commit): %s", tmpPath)).
|
|
||||||
RunStdString(&git.RunOpts{Dir: tmpPath, Env: env}); err != nil {
|
RunStdString(&git.RunOpts{Dir: tmpPath, Env: env}); err != nil {
|
||||||
log.Error("Failed to commit: %v: Stdout: %s\nError: %v", cmd.String(), stdout, err)
|
log.Error("Failed to commit: %v: Stdout: %s\nError: %v", cmd.LogString(), stdout, err)
|
||||||
return fmt.Errorf("git commit: %w", err)
|
return fmt.Errorf("git commit: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +71,6 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
|
|||||||
}
|
}
|
||||||
|
|
||||||
if stdout, _, err := git.NewCommand(ctx, "push", "origin").AddDynamicArguments("HEAD:" + defaultBranch).
|
if stdout, _, err := git.NewCommand(ctx, "push", "origin").AddDynamicArguments("HEAD:" + defaultBranch).
|
||||||
SetDescription(fmt.Sprintf("initRepoCommit (git push): %s", tmpPath)).
|
|
||||||
RunStdString(&git.RunOpts{Dir: tmpPath, Env: repo_module.InternalPushingEnvironment(u, repo)}); err != nil {
|
RunStdString(&git.RunOpts{Dir: tmpPath, Env: repo_module.InternalPushingEnvironment(u, repo)}); err != nil {
|
||||||
log.Error("Failed to push back to HEAD: Stdout: %s\nError: %v", stdout, err)
|
log.Error("Failed to push back to HEAD: Stdout: %s\nError: %v", stdout, err)
|
||||||
return fmt.Errorf("git push: %w", err)
|
return fmt.Errorf("git push: %w", err)
|
||||||
|
@ -122,7 +122,6 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if stdout, _, err := git.NewCommand(ctx, "update-server-info").
|
if stdout, _, err := git.NewCommand(ctx, "update-server-info").
|
||||||
SetDescription(fmt.Sprintf("MigrateRepositoryGitData(git update-server-info): %s", repoPath)).
|
|
||||||
RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
|
RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
|
||||||
log.Error("MigrateRepositoryGitData(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
|
log.Error("MigrateRepositoryGitData(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
|
||||||
return repo, fmt.Errorf("error in MigrateRepositoryGitData(git update-server-info): %w", err)
|
return repo, fmt.Errorf("error in MigrateRepositoryGitData(git update-server-info): %w", err)
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
password_module "code.gitea.io/gitea/modules/auth/password"
|
password_module "code.gitea.io/gitea/modules/auth/password"
|
||||||
@ -113,7 +112,7 @@ func UpdateUser(ctx context.Context, u *user_model.User, opts *UpdateOptions) er
|
|||||||
}
|
}
|
||||||
if opts.IsAdmin.Has() {
|
if opts.IsAdmin.Has() {
|
||||||
if !opts.IsAdmin.Value() && user_model.IsLastAdminUser(ctx, u) {
|
if !opts.IsAdmin.Value() && user_model.IsLastAdminUser(ctx, u) {
|
||||||
return models.ErrDeleteLastAdminUser{UID: u.ID}
|
return user_model.ErrDeleteLastAdminUser{UID: u.ID}
|
||||||
}
|
}
|
||||||
|
|
||||||
u.IsAdmin = opts.IsAdmin.Value()
|
u.IsAdmin = opts.IsAdmin.Value()
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user