Compare commits

...

8 Commits

Author SHA1 Message Date
GiteaBot 7026a30fdc [skip ci] Updated translations via Crowdin 2021-12-02 00:25:09 +00:00
zeripath 7d0629adf8
Use shadowing script for docker (#17846)
Too many docker users are caught out by the default location for the
app.ini file being environment dependent so that when they docker exec
into the container the gitea commands do not work properly and require
additional -c arguments to correctly pick up the configuration.

This PR simply shadows the gitea binary using variants of the FHS
compatible script to make the command gitea have the default locations
by default.

Fix #14468
Reference #17497
Reference #12082
Reference #8941
... amongst others ...
Replace #17501

Signed-off-by: Andrew Thornton <art27@cantab.net>
2021-12-01 18:08:27 +00:00
wxiaoguang 042cac5fed
Improve install code to avoid low-level mistakes. (#17779)
* Improve install code to avoid low-level mistakes.

If a user tries to do a re-install in a Gitea database, they gets a warning and double check.
When Gitea runs, it never create empty app.ini automatically.

Also some small (related) refactoring:

* Refactor db.InitEngine related logic make it more clean (especially for the install code)
* Move some i18n strings out from setting.go to make the setting.go can be easily maintained.
* Show errors in CLI code if an incorrect app.ini is used.
* APP_DATA_PATH is created when installing, and checked when starting (no empty directory is created any more).
2021-12-01 15:50:01 +08:00
GiteaBot a3517d8668 [skip ci] Updated translations via Crowdin 2021-12-01 00:25:14 +00:00
silverwind 3fd502cca8
Rework commit page header (#17849)
- Remove blue background and use regular header styling
- Use flexbox on author and signature segments
- Add shield-type icons besides gpg key, replacing icon font icons
- Ensure author and signature line are same height
- Remove erronous green background on arc-green signature line
- Turn signing colors into CSS variables
- Consolidate the two warning states into one

Co-authored-by: zeripath <art27@cantab.net>
2021-12-01 01:40:17 +02:00
zeripath 01087e9eef
Make Requests Processes and create process hierarchy. Associate OpenRepository with context. (#17125)
This PR registers requests with the process manager and manages hierarchy within the processes.

Git repos are then associated with a context, (usually the request's context) - with sub commands using this context as their base context.

Signed-off-by: Andrew Thornton <art27@cantab.net>
2021-11-30 20:06:32 +00:00
silverwind d894c90b70
Fix position of label color picker colors (#17866) 2021-11-30 17:54:00 +00:00
singuliere 64b7d0de8e
docs: modules do not rely on models, services do (#17861)
services: provide some services for users, usually use
database (models) modules: provide some basic functions without
database, eg: code parser, etc The major difference is services use
database, while modules don’t.

Signed-off-by: singuliere <singuliere@autistici.org>
2021-11-30 11:56:12 +00:00
118 changed files with 2179 additions and 730 deletions

View File

@ -66,6 +66,5 @@ CMD ["/bin/s6-svscan", "/etc/s6"]
COPY docker/root /
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
RUN chmod 755 /usr/bin/entrypoint /app/gitea/gitea /usr/local/bin/environment-to-ini
RUN chmod 755 /usr/bin/entrypoint /app/gitea/gitea /usr/local/bin/gitea /usr/local/bin/environment-to-ini
RUN chmod 755 /etc/s6/gitea/* /etc/s6/openssh/* /etc/s6/.s6-svscan/*
RUN ln -s /app/gitea/gitea /usr/local/bin/gitea

View File

@ -53,9 +53,9 @@ RUN mkdir -p /var/lib/gitea /etc/gitea
RUN chown git:git /var/lib/gitea /etc/gitea
COPY docker/rootless /
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /usr/local/bin/gitea
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
RUN chmod 755 /usr/local/bin/docker-entrypoint.sh /usr/local/bin/docker-setup.sh /usr/local/bin/gitea /usr/local/bin/environment-to-ini
RUN chmod 755 /usr/local/bin/docker-entrypoint.sh /usr/local/bin/docker-setup.sh /app/gitea/gitea /usr/local/bin/gitea /usr/local/bin/environment-to-ini
#git:git
USER 1000:1000

View File

@ -16,6 +16,7 @@ import (
"syscall"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
@ -57,15 +58,17 @@ func confirm() (bool, error) {
}
func initDB(ctx context.Context) error {
return initDBDisableConsole(ctx, false)
}
func initDBDisableConsole(ctx context.Context, disableConsole bool) error {
setting.NewContext()
setting.LoadFromExisting()
setting.InitDBConfig()
setting.NewXORMLogService(disableConsole)
setting.NewXORMLogService(false)
if setting.Database.Type == "" {
log.Fatal(`Database settings are missing from the configuration file: %q.
Ensure you are running in the correct environment or set the correct configuration file with -c.
If this is the intended configuration file complete the [database] section.`, setting.CustomConf)
}
if err := db.InitEngine(ctx); err != nil {
return fmt.Errorf("models.SetEngine: %v", err)
return fmt.Errorf("unable to initialise the database using the configuration in %q. Error: %v", setting.CustomConf, err)
}
return nil
}

View File

@ -35,7 +35,6 @@ func runConvert(ctx *cli.Context) error {
log.Info("Custom path: %s", setting.CustomPath)
log.Info("Log path: %s", setting.LogRootPath)
log.Info("Configuration file: %s", setting.CustomConf)
setting.InitDBConfig()
if !setting.Database.UseMySQL {
fmt.Println("This command can only be used with a MySQL database")

View File

@ -87,7 +87,7 @@ func runRecreateTable(ctx *cli.Context) error {
golog.SetPrefix("")
golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT)))
setting.NewContext()
setting.LoadFromExisting()
setting.InitDBConfig()
setting.EnableXORMLog = ctx.Bool("debug")

View File

@ -159,7 +159,8 @@ func runDump(ctx *cli.Context) error {
fatal("Deleting default logger failed. Can not write to stdout: %v", err)
}
}
setting.NewContext()
setting.LoadFromExisting()
// make sure we are logging to the console no matter what the configuration tells us do to
if _, err := setting.Cfg.Section("log").NewKey("MODE", "console"); err != nil {
fatal("Setting logging mode to console failed: %v", err)

View File

@ -88,7 +88,6 @@ func runDumpRepository(ctx *cli.Context) error {
log.Info("Custom path: %s", setting.CustomPath)
log.Info("Log path: %s", setting.LogRootPath)
log.Info("Configuration file: %s", setting.CustomConf)
setting.InitDBConfig()
var (
serviceType structs.GitServiceType

View File

@ -115,7 +115,7 @@ func initEmbeddedExtractor(c *cli.Context) error {
log.DelNamedLogger(log.DEFAULT)
// Read configuration file
setting.NewContext()
setting.LoadAllowEmpty()
pats, err := getPatterns(c.Args())
if err != nil {

View File

@ -309,7 +309,7 @@ func runHookPostReceive(c *cli.Context) error {
defer cancel()
// First of all run update-server-info no matter what
if _, err := git.NewCommand("update-server-info").SetParentContext(ctx).Run(); err != nil {
if _, err := git.NewCommandContext(ctx, "update-server-info").Run(); err != nil {
return fmt.Errorf("Failed to call 'git update-server-info': %v", err)
}

View File

@ -18,7 +18,7 @@ func runSendMail(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setting.NewContext()
setting.LoadFromExisting()
if err := argsSet(c, "title"); err != nil {
return err

View File

@ -36,7 +36,6 @@ func runMigrate(ctx *cli.Context) error {
log.Info("Custom path: %s", setting.CustomPath)
log.Info("Log path: %s", setting.LogRootPath)
log.Info("Configuration file: %s", setting.CustomConf)
setting.InitDBConfig()
if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
log.Fatal("Failed to initialize ORM engine: %v", err)

View File

@ -121,7 +121,6 @@ func runMigrateStorage(ctx *cli.Context) error {
log.Info("Custom path: %s", setting.CustomPath)
log.Info("Log path: %s", setting.LogRootPath)
log.Info("Configuration file: %s", setting.CustomConf)
setting.InitDBConfig()
if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
log.Fatal("Failed to initialize ORM engine: %v", err)

View File

@ -50,7 +50,7 @@ func runRestoreRepository(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setting.NewContext()
setting.LoadFromExisting()
statusCode, errStr := private.RestoreRepo(
ctx,

View File

@ -58,7 +58,7 @@ func setup(logPath string, debug bool) {
} else {
_ = log.NewLogger(1000, "console", "console", `{"level":"fatal","stacktracelevel":"NONE","stderr":true}`)
}
setting.NewContext()
setting.LoadFromExisting()
if debug {
setting.RunMode = "dev"
}

View File

@ -124,6 +124,10 @@ func runWeb(ctx *cli.Context) error {
}
c := install.Routes()
err := listen(c, false)
if err != nil {
log.Critical("Unable to open listener for installer. Is Gitea already running?")
graceful.GetManager().DoGracefulShutdown()
}
select {
case <-graceful.GetManager().IsShutdown():
<-graceful.GetManager().Done()
@ -145,7 +149,15 @@ func runWeb(ctx *cli.Context) error {
log.Info("Global init")
// Perform global initialization
routers.GlobalInit(graceful.GetManager().HammerContext())
setting.LoadFromExisting()
routers.GlobalInitInstalled(graceful.GetManager().HammerContext())
// We check that AppDataPath exists here (it should have been created during installation)
// We can't check it in `GlobalInitInstalled`, because some integration tests
// use cmd -> GlobalInitInstalled, but the AppDataPath doesn't exist during those tests.
if _, err := os.Stat(setting.AppDataPath); err != nil {
log.Fatal("Can not find APP_DATA_PATH '%s'", setting.AppDataPath)
}
// Override the provided port number within the configuration
if ctx.IsSet("port") {

View File

@ -156,6 +156,7 @@ func runEnvironmentToIni(c *cli.Context) error {
destination = setting.CustomConf
}
if destination != setting.CustomConf || changed {
log.Info("Settings saved to: %q", destination)
err = cfg.SaveTo(destination)
if err != nil {
return err

View File

@ -1,8 +1,8 @@
#!/bin/bash
########################################################################
# This script some defaults for gitea to run in a FHS compliant manner #
########################################################################
#############################################################################
# This script sets some defaults for gitea to run in a FHS compliant manner #
#############################################################################
# It assumes that you place this script as gitea in /usr/bin
#
@ -36,7 +36,7 @@ if [ -z "$APP_INI_SET" ]; then
CONF_ARG="-c \"$APP_INI\""
fi
# Provide FHS compliant defaults to
exec -a "$0" GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" "$GITEA" $CONF_ARG "$@"
# Provide FHS compliant defaults
GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" exec -a "$0" "$GITEA" $CONF_ARG "$@"

View File

@ -49,7 +49,7 @@ func runPR() {
log.Fatal(err)
}
setting.SetCustomPathAndConf("", "", "")
setting.NewContext()
setting.LoadAllowEmpty()
setting.RepoRootPath, err = os.MkdirTemp(os.TempDir(), "repos")
if err != nil {

View File

@ -2,5 +2,5 @@
[[ -f ./setup ]] && source ./setup
pushd /app/gitea >/dev/null
exec su-exec $USER /app/gitea/gitea web
exec su-exec $USER /usr/local/bin/gitea web
popd

View File

@ -0,0 +1,17 @@
#!/bin/bash
###############################################################
# This script sets defaults for gitea to run in the container #
###############################################################
# It assumes that you place this script as gitea in /usr/local/bin
#
# And place the original in /usr/lib/gitea with working files in /data/gitea
GITEA="/app/gitea/gitea"
WORK_DIR="/app/gitea"
CUSTOM_PATH="/data/gitea"
# Provide docker defaults
GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" GITEA_CUSTOM="${GITEA_CUSTOM:-$CUSTOM_PATH}" exec -a "$0" "$GITEA" $CONF_ARG "$@"

View File

@ -0,0 +1,42 @@
#!/bin/bash
###############################################################
# This script sets defaults for gitea to run in the container #
###############################################################
# It assumes that you place this script as gitea in /usr/local/bin
#
# And place the original in /usr/lib/gitea with working files in /data/gitea
GITEA="/app/gitea/gitea"
WORK_DIR="/var/lib/gitea"
APP_INI="/etc/gitea/app.ini"
APP_INI_SET=""
for i in "$@"; do
case "$i" in
"-c")
APP_INI_SET=1
;;
"-c="*)
APP_INI_SET=1
;;
"--config")
APP_INI_SET=1
;;
"--config="*)
APP_INI_SET=1
;;
*)
;;
esac
done
if [ -z "$APP_INI_SET" ]; then
CONF_ARG="-c \"$APP_INI\""
fi
# Provide docker defaults
GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" exec -a "$0" "$GITEA" $CONF_ARG "$@"

View File

@ -38,7 +38,7 @@ To maintain understandable code and avoid circular dependencies it is important
- `models/db`: Basic database operations. All other `models/xxx` packages should depend on this package. The `GetEngine` function should only be invoked from `models/`.
- `models/fixtures`: Sample data used in unit tests and integration tests. One `yml` file means one table which will be loaded into database when beginning the tests.
- `models/migrations`: Stores database migrations between versions. PRs that change a database structure **MUST** also have a migration step.
- `modules`: Different modules to handle specific functionality in Gitea. Work in Progress: Some of them should be moved to `services`.
- `modules`: Different modules to handle specific functionality in Gitea. Work in Progress: Some of them should be moved to `services`, in particular those that depend on models because they rely on the database.
- `modules/setting`: Store all system configurations read from ini files and has been referenced by everywhere. But they should be used as function parameters when possible.
- `modules/git`: Package to interactive with `Git` command line or Gogit package.
- `public`: Compiled frontend files (javascript, images, css, etc.)

View File

@ -164,8 +164,8 @@ func initIntegrationTest() {
}
setting.SetCustomPathAndConf("", "", "")
setting.NewContext()
util.RemoveAll(models.LocalCopyPath())
setting.LoadForTest()
_ = util.RemoveAll(models.LocalCopyPath())
git.CheckLFSVersion()
setting.InitDBConfig()
if err := storage.Init(); err != nil {
@ -240,7 +240,8 @@ func initIntegrationTest() {
}
defer db.Close()
}
routers.GlobalInit(graceful.GetManager().HammerContext())
routers.GlobalInitInstalled(graceful.GetManager().HammerContext())
}
func prepareTestEnv(t testing.TB, skip ...int) func() {
@ -254,6 +255,7 @@ func prepareTestEnv(t testing.TB, skip ...int) func() {
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
assert.NoError(t, util.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
return deferFn
}

View File

@ -56,7 +56,7 @@ func initMigrationTest(t *testing.T) func() {
setting.CustomConf = giteaConf
}
setting.NewContext()
setting.LoadForTest()
assert.True(t, len(setting.RepoRootPath) != 0)
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))

View File

@ -8,7 +8,6 @@ package db
import (
"context"
"database/sql"
"errors"
"fmt"
"io"
"reflect"
@ -92,8 +91,8 @@ func init() {
}
}
// NewEngine returns a new xorm engine from the configuration
func NewEngine() (*xorm.Engine, error) {
// newXORMEngine returns a new XORM engine from the configuration
func newXORMEngine() (*xorm.Engine, error) {
connStr, err := setting.DBConnStr()
if err != nil {
return nil, err
@ -126,40 +125,49 @@ func SyncAllTables() error {
return x.StoreEngine("InnoDB").Sync2(tables...)
}
// InitEngine sets the xorm.Engine
func InitEngine(ctx context.Context) (err error) {
x, err = NewEngine()
// InitEngine initializes the xorm.Engine and sets it as db.DefaultContext
func InitEngine(ctx context.Context) error {
xormEngine, err := newXORMEngine()
if err != nil {
return fmt.Errorf("Failed to connect to database: %v", err)
return fmt.Errorf("failed to connect to database: %v", err)
}
x.SetMapper(names.GonicMapper{})
xormEngine.SetMapper(names.GonicMapper{})
// WARNING: for serv command, MUST remove the output to os.stdout,
// so use log file to instead print to stdout.
x.SetLogger(NewXORMLogger(setting.Database.LogSQL))
x.ShowSQL(setting.Database.LogSQL)
x.SetMaxOpenConns(setting.Database.MaxOpenConns)
x.SetMaxIdleConns(setting.Database.MaxIdleConns)
x.SetConnMaxLifetime(setting.Database.ConnMaxLifetime)
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)
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,
e: x,
}
x.SetDefaultContext(ctx)
return nil
}
// SetEngine is used by unit test code
func SetEngine(eng *xorm.Engine) {
x = eng
DefaultContext = &Context{
Context: context.Background(),
e: 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
// InitEngineWithMigration initializes a new xorm.Engine and sets it as the db.DefaultContext
// This function must never call .Sync2() 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
@ -226,14 +234,6 @@ func NamesToBean(names ...string) ([]interface{}, error) {
return beans, nil
}
// Ping tests if database is alive
func Ping() error {
if x != nil {
return x.Ping()
}
return errors.New("database not configured")
}
// 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
@ -291,11 +291,3 @@ func GetMaxID(beanOrTableName interface{}) (maxID int64, err error) {
_, err = x.Select("MAX(id)").Table(beanOrTableName).Get(&maxID)
return
}
// FindByMaxID filled results as the condition from database
func FindByMaxID(maxID int64, limit int, results interface{}) error {
return x.Where("id <= ?", maxID).
OrderBy("id DESC").
Limit(limit).
Find(results)
}

65
models/db/install/db.go Normal file
View File

@ -0,0 +1,65 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package install
import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/setting"
"xorm.io/xorm"
)
func getXORMEngine() *xorm.Engine {
return db.DefaultContext.(*db.Context).Engine().(*xorm.Engine)
}
// CheckDatabaseConnection checks the database connection
func CheckDatabaseConnection() error {
e := db.GetEngine(db.DefaultContext)
_, err := e.Exec("SELECT 1")
return err
}
// GetMigrationVersion gets the database migration version
func GetMigrationVersion() (int64, error) {
var installedDbVersion int64
x := getXORMEngine()
exist, err := x.IsTableExist("version")
if err != nil {
return 0, err
}
if !exist {
return 0, nil
}
_, err = x.Table("version").Cols("version").Get(&installedDbVersion)
if err != nil {
return 0, err
}
return installedDbVersion, nil
}
// HasPostInstallationUsers checks whether there are users after installation
func HasPostInstallationUsers() (bool, error) {
x := getXORMEngine()
exist, err := x.IsTableExist("user")
if err != nil {
return false, err
}
if !exist {
return false, nil
}
// if there are 2 or more users in database, we consider there are users created after installation
threshold := 2
if !setting.IsProd {
// to debug easily, with non-prod RUN_MODE, we only check the count to 1
threshold = 1
}
res, err := x.Table("user").Cols("id").Limit(threshold).Query()
if err != nil {
return false, err
}
return len(res) >= threshold, nil
}

View File

@ -5,6 +5,7 @@
package migrations
import (
"context"
"database/sql"
"fmt"
"os"
@ -57,7 +58,7 @@ func TestMain(m *testing.M) {
}
setting.SetCustomPathAndConf("", "", "")
setting.NewContext()
setting.LoadForTest()
git.CheckLFSVersion()
setting.InitDBConfig()
setting.NewLogServices(true)
@ -85,21 +86,11 @@ func removeAllWithRetry(dir string) error {
return err
}
// newEngine sets the xorm.Engine
func newEngine() (*xorm.Engine, error) {
x, err := db.NewEngine()
if err != nil {
return x, fmt.Errorf("Failed to connect to database: %v", err)
func newXORMEngine() (*xorm.Engine, error) {
if err := db.InitEngine(context.Background()); err != nil {
return nil, err
}
x.SetMapper(names.GonicMapper{})
// WARNING: for serv command, MUST remove the output to os.stdout,
// so use log file to instead print to stdout.
x.SetLogger(db.NewXORMLogger(setting.Database.LogSQL))
x.ShowSQL(setting.Database.LogSQL)
x.SetMaxOpenConns(setting.Database.MaxOpenConns)
x.SetMaxIdleConns(setting.Database.MaxIdleConns)
x.SetConnMaxLifetime(setting.Database.ConnMaxLifetime)
x := unittest.GetXORMEngine()
return x, nil
}
@ -213,7 +204,7 @@ func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.En
return nil, deferFn
}
x, err := newEngine()
x, err := newXORMEngine()
assert.NoError(t, err)
if x != nil {
oldDefer := deferFn

View File

@ -16,7 +16,7 @@ import (
func init() {
setting.SetCustomPathAndConf("", "", "")
setting.NewContext()
setting.LoadForTest()
}
func Test_SSHParsePublicKey(t *testing.T) {

View File

@ -18,7 +18,8 @@ import (
var fixtures *testfixtures.Loader
func getXORMEngine(engine ...*xorm.Engine) (x *xorm.Engine) {
// GetXORMEngine gets the XORM engine
func GetXORMEngine(engine ...*xorm.Engine) (x *xorm.Engine) {
if len(engine) == 1 {
return engine[0]
}
@ -27,7 +28,7 @@ func getXORMEngine(engine ...*xorm.Engine) (x *xorm.Engine) {
// InitFixtures initialize test fixtures for a test database
func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) {
e := getXORMEngine(engine...)
e := GetXORMEngine(engine...)
var testfiles func(*testfixtures.Loader) error
if opts.Dir != "" {
testfiles = testfixtures.Directory(opts.Dir)
@ -69,7 +70,7 @@ func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) {
// LoadFixtures load fixtures for a test database
func LoadFixtures(engine ...*xorm.Engine) error {
e := getXORMEngine(engine...)
e := GetXORMEngine(engine...)
var err error
// Database transaction conflicts could occur and result in ROLLBACK
// As a simple workaround, we just retry 20 times.

View File

@ -5,6 +5,7 @@
package unittest
import (
"context"
"fmt"
"net/url"
"os"
@ -124,7 +125,7 @@ func CreateTestEngine(opts FixturesOptions) error {
return err
}
x.SetMapper(names.GonicMapper{})
db.SetEngine(x)
db.SetDefaultEngine(context.Background(), x)
if err = db.SyncAllTables(); err != nil {
return err

View File

@ -534,7 +534,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
return
}
gitRepo, err := git.OpenRepository(models.RepoPath(userName, repoName))
gitRepo, err := git.OpenRepositoryCtx(ctx, models.RepoPath(userName, repoName))
if err != nil {
if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") {
log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RepoPath(), err)
@ -792,7 +792,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
if ctx.Repo.GitRepo == nil {
repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
ctx.Repo.GitRepo, err = git.OpenRepository(repoPath)
ctx.Repo.GitRepo, err = git.OpenRepositoryCtx(ctx, repoPath)
if err != nil {
ctx.ServerError("RepoRef Invalid repo "+repoPath, err)
return

View File

@ -44,7 +44,7 @@ func (w *wrappedLevelLogger) Log(skip int, level log.Level, format string, v ...
}
func initDBDisableConsole(ctx context.Context, disableConsole bool) error {
setting.NewContext()
setting.LoadFromExisting()
setting.InitDBConfig()
setting.NewXORMLogService(disableConsole)

View File

@ -67,7 +67,7 @@ func checkConfigurationFiles(logger log.Logger, autofix bool) error {
return err
}
setting.NewContext()
setting.LoadFromExisting()
configurationFiles := []configurationFile{
{"Configuration File Path", setting.CustomConf, false, true, false},

View File

@ -28,17 +28,15 @@ type WriteCloserError interface {
}
// CatFileBatchCheck opens git cat-file --batch-check in the provided repo and returns a stdin pipe, a stdout reader and cancel function
func CatFileBatchCheck(repoPath string) (WriteCloserError, *bufio.Reader, func()) {
func CatFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError, *bufio.Reader, func()) {
batchStdinReader, batchStdinWriter := io.Pipe()
batchStdoutReader, batchStdoutWriter := io.Pipe()
ctx, ctxCancel := context.WithCancel(DefaultContext)
ctx, ctxCancel := context.WithCancel(ctx)
closed := make(chan struct{})
cancel := func() {
_ = batchStdinReader.Close()
_ = batchStdinWriter.Close()
_ = batchStdoutReader.Close()
_ = batchStdoutWriter.Close()
ctxCancel()
_ = batchStdoutReader.Close()
_ = batchStdinWriter.Close()
<-closed
}
@ -67,19 +65,17 @@ func CatFileBatchCheck(repoPath string) (WriteCloserError, *bufio.Reader, func()
}
// CatFileBatch opens git cat-file --batch in the provided repo and returns a stdin pipe, a stdout reader and cancel function
func CatFileBatch(repoPath string) (WriteCloserError, *bufio.Reader, func()) {
func CatFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufio.Reader, func()) {
// We often want to feed the commits in order into cat-file --batch, followed by their trees and sub trees as necessary.
// so let's create a batch stdin and stdout
batchStdinReader, batchStdinWriter := io.Pipe()
batchStdoutReader, batchStdoutWriter := nio.Pipe(buffer.New(32 * 1024))
ctx, ctxCancel := context.WithCancel(DefaultContext)
ctx, ctxCancel := context.WithCancel(ctx)
closed := make(chan struct{})
cancel := func() {
_ = batchStdinReader.Close()
ctxCancel()
_ = batchStdinWriter.Close()
_ = batchStdoutReader.Close()
_ = batchStdoutWriter.Close()
ctxCancel()
<-closed
}

View File

@ -24,12 +24,12 @@ type BlamePart struct {
// BlameReader returns part of file blame one by one
type BlameReader struct {
cmd *exec.Cmd
pid int64
output io.ReadCloser
reader *bufio.Reader
lastSha *string
cancel context.CancelFunc
cmd *exec.Cmd
output io.ReadCloser
reader *bufio.Reader
lastSha *string
cancel context.CancelFunc // Cancels the context that this reader runs in
finished process.FinishedFunc // Tells the process manager we're finished and it can remove the associated process from the process table
}
var shaLineRegex = regexp.MustCompile("^([a-z0-9]{40})")
@ -100,8 +100,8 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
// Close BlameReader - don't run NextPart after invoking that
func (r *BlameReader) Close() error {
defer process.GetManager().Remove(r.pid)
r.cancel()
defer r.finished() // Only remove the process from the process table when the underlying command is closed
r.cancel() // However, first cancel our own context early
_ = r.output.Close()
@ -114,7 +114,7 @@ func (r *BlameReader) Close() error {
// CreateBlameReader creates reader for given repository, commit and file
func CreateBlameReader(ctx context.Context, repoPath, commitID, file string) (*BlameReader, error) {
gitRepo, err := OpenRepository(repoPath)
gitRepo, err := OpenRepositoryCtx(ctx, repoPath)
if err != nil {
return nil, err
}
@ -125,32 +125,31 @@ func CreateBlameReader(ctx context.Context, repoPath, commitID, file string) (*B
func createBlameReader(ctx context.Context, dir string, command ...string) (*BlameReader, error) {
// Here we use the provided context - this should be tied to the request performing the blame so that it does not hang around.
ctx, cancel := context.WithCancel(ctx)
ctx, cancel, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("GetBlame [repo_path: %s]", dir))
cmd := exec.CommandContext(ctx, command[0], command[1:]...)
cmd.Dir = dir
cmd.Stderr = os.Stderr
stdout, err := cmd.StdoutPipe()
if err != nil {
defer cancel()
defer finished()
return nil, fmt.Errorf("StdoutPipe: %v", err)
}
if err = cmd.Start(); err != nil {
defer cancel()
defer finished()
_ = stdout.Close()
return nil, fmt.Errorf("Start: %v", err)
}
pid := process.GetManager().Add(fmt.Sprintf("GetBlame [repo_path: %s]", dir), cancel)
reader := bufio.NewReader(stdout)
return &BlameReader{
cmd,
pid,
stdout,
reader,
nil,
cancel,
cmd: cmd,
output: stdout,
reader: reader,
cancel: cancel,
finished: finished,
}, nil
}

View File

@ -29,7 +29,7 @@ type Blob struct {
// DataAsync gets a ReadCloser for the contents of a blob without reading it all.
// Calling the Close function on the result will discard all unread output.
func (b *Blob) DataAsync() (io.ReadCloser, error) {
wr, rd, cancel := b.repo.CatFileBatch()
wr, rd, cancel := b.repo.CatFileBatch(b.repo.Ctx)
_, err := wr.Write([]byte(b.ID.String() + "\n"))
if err != nil {
@ -67,7 +67,7 @@ func (b *Blob) Size() int64 {
return b.size
}
wr, rd, cancel := b.repo.CatFileBatchCheck()
wr, rd, cancel := b.repo.CatFileBatchCheck(b.repo.Ctx)
defer cancel()
_, err := wr.Write([]byte(b.ID.String() + "\n"))
if err != nil {

View File

@ -143,8 +143,13 @@ func (c *Command) RunWithContext(rc *RunContext) error {
log.Debug("%s: %v", rc.Dir, c)
}
ctx, cancel := context.WithTimeout(c.parentContext, rc.Timeout)
defer cancel()
desc := c.desc
if desc == "" {
desc = fmt.Sprintf("%s %s [repo_path: %s]", c.name, strings.Join(c.args, " "), rc.Dir)
}
ctx, cancel, finished := process.GetManager().AddContextTimeout(c.parentContext, rc.Timeout, desc)
defer finished()
cmd := exec.CommandContext(ctx, c.name, c.args...)
if rc.Env == nil {
@ -172,13 +177,6 @@ func (c *Command) RunWithContext(rc *RunContext) error {
return err
}
desc := c.desc
if desc == "" {
desc = fmt.Sprintf("%s %s %s [repo_path: %s]", GitExecutable, c.name, strings.Join(c.args, " "), rc.Dir)
}
pid := process.GetManager().Add(desc, cancel)
defer process.GetManager().Remove(pid)
if rc.PipelineFunc != nil {
err := rc.PipelineFunc(ctx, cancel)
if err != nil {

View File

@ -100,7 +100,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
}
func getLastCommitForPathsByCache(ctx context.Context, commitID, treePath string, paths []string, cache *LastCommitCache) (map[string]*Commit, []string, error) {
wr, rd, cancel := cache.repo.CatFileBatch()
wr, rd, cancel := cache.repo.CatFileBatch(ctx)
defer cancel()
var unHitEntryPaths []string
@ -129,7 +129,7 @@ func GetLastCommitForPaths(ctx context.Context, cache *LastCommitCache, commit *
return nil, err
}
batchStdinWriter, batchReader, cancel := commit.repo.CatFileBatch()
batchStdinWriter, batchReader, cancel := commit.repo.CatFileBatch(ctx)
defer cancel()
commitsMap := map[string]*Commit{}

View File

@ -56,8 +56,8 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
fileArgs = append(fileArgs, "--", file)
}
// FIXME: graceful: These commands should have a timeout
ctx, cancel := context.WithCancel(DefaultContext)
defer cancel()
ctx, _, finished := process.GetManager().AddContext(repo.Ctx, fmt.Sprintf("GetRawDiffForFile: [repo_path: %s]", repo.Path))
defer finished()
var cmd *exec.Cmd
switch diffType {
@ -90,8 +90,6 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
cmd.Dir = repo.Path
cmd.Stdout = writer
cmd.Stderr = stderr
pid := process.GetManager().Add(fmt.Sprintf("GetRawDiffForFile: [repo_path: %s]", repo.Path), cancel)
defer process.GetManager().Remove(pid)
if err = cmd.Run(); err != nil {
return fmt.Errorf("Run: %v - %s", err, stderr)

View File

@ -63,7 +63,7 @@ func FindLFSFile(repo *git.Repository, hash git.SHA1) ([]*LFSResult, error) {
// Next feed the commits in order into cat-file --batch, followed by their trees and sub trees as necessary.
// so let's create a batch stdin and stdout
batchStdinWriter, batchReader, cancel := repo.CatFileBatch()
batchStdinWriter, batchReader, cancel := repo.CatFileBatch(repo.Ctx)
defer cancel()
// We'll use a scanner for the revList because it's simpler than a bufio.Reader

View File

@ -4,19 +4,22 @@
package git
import "net/url"
import (
"context"
"net/url"
)
// GetRemoteAddress returns the url of a specific remote of the repository.
func GetRemoteAddress(repoPath, remoteName string) (*url.URL, error) {
func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (*url.URL, error) {
err := LoadGitVersion()
if err != nil {
return nil, err
}
var cmd *Command
if CheckGitVersionAtLeast("2.7") == nil {
cmd = NewCommand("remote", "get-url", remoteName)
cmd = NewCommandContext(ctx, "remote", "get-url", remoteName)
} else {
cmd = NewCommand("config", "--get", "remote."+remoteName+".url")
cmd = NewCommandContext(ctx, "config", "--get", "remote."+remoteName+".url")
}
result, err := cmd.RunInDir(repoPath)

View File

@ -211,8 +211,8 @@ type PushOptions struct {
}
// Push pushs local commits to given remote branch.
func Push(repoPath string, opts PushOptions) error {
cmd := NewCommand("push")
func Push(ctx context.Context, repoPath string, opts PushOptions) error {
cmd := NewCommandContext(ctx, "push")
if opts.Force {
cmd.AddArguments("-f")
}

View File

@ -74,7 +74,7 @@ func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[
}
}
cmd := NewCommand(cmdArgs...)
cmd := NewCommandContext(repo.Ctx, cmdArgs...)
if err := cmd.RunInDirTimeoutEnvPipeline(env, -1, repo.Path, stdOut, stdErr); err != nil {
return nil, fmt.Errorf("failed to run check-attr: %v\n%s\n%s", err, stdOut.String(), stdErr.String())

View File

@ -9,6 +9,7 @@
package git
import (
"context"
"errors"
"path/filepath"
@ -30,10 +31,17 @@ type Repository struct {
gogitRepo *gogit.Repository
gogitStorage *filesystem.Storage
gpgSettings *GPGSettings
Ctx context.Context
}
// OpenRepository opens the repository at the given path.
func OpenRepository(repoPath string) (*Repository, error) {
return OpenRepositoryCtx(DefaultContext, repoPath)
}
// OpenRepositoryCtx opens the repository at the given path within the context.Context
func OpenRepositoryCtx(ctx context.Context, repoPath string) (*Repository, error) {
repoPath, err := filepath.Abs(repoPath)
if err != nil {
return nil, err
@ -60,6 +68,7 @@ func OpenRepository(repoPath string) (*Repository, error) {
gogitRepo: gogitRepo,
gogitStorage: storage,
tagCache: newObjectCache(),
Ctx: ctx,
}, nil
}

View File

@ -32,10 +32,17 @@ type Repository struct {
checkCancel context.CancelFunc
checkReader *bufio.Reader
checkWriter WriteCloserError
Ctx context.Context
}
// OpenRepository opens the repository at the given path.
func OpenRepository(repoPath string) (*Repository, error) {
return OpenRepositoryCtx(DefaultContext, repoPath)
}
// OpenRepositoryCtx opens the repository at the given path with the provided context.
func OpenRepositoryCtx(ctx context.Context, repoPath string) (*Repository, error) {
repoPath, err := filepath.Abs(repoPath)
if err != nil {
return nil, err
@ -46,28 +53,29 @@ func OpenRepository(repoPath string) (*Repository, error) {
repo := &Repository{
Path: repoPath,
tagCache: newObjectCache(),
Ctx: ctx,
}
repo.batchWriter, repo.batchReader, repo.batchCancel = CatFileBatch(repoPath)
repo.checkWriter, repo.checkReader, repo.checkCancel = CatFileBatchCheck(repo.Path)
repo.batchWriter, repo.batchReader, repo.batchCancel = CatFileBatch(ctx, repoPath)
repo.checkWriter, repo.checkReader, repo.checkCancel = CatFileBatchCheck(ctx, repo.Path)
return repo, nil
}
// CatFileBatch obtains a CatFileBatch for this repository
func (repo *Repository) CatFileBatch() (WriteCloserError, *bufio.Reader, func()) {
func (repo *Repository) CatFileBatch(ctx context.Context) (WriteCloserError, *bufio.Reader, func()) {
if repo.batchCancel == nil || repo.batchReader.Buffered() > 0 {
log.Debug("Opening temporary cat file batch for: %s", repo.Path)
return CatFileBatch(repo.Path)
return CatFileBatch(ctx, repo.Path)
}
return repo.batchWriter, repo.batchReader, func() {}
}
// CatFileBatchCheck obtains a CatFileBatchCheck for this repository
func (repo *Repository) CatFileBatchCheck() (WriteCloserError, *bufio.Reader, func()) {
func (repo *Repository) CatFileBatchCheck(ctx context.Context) (WriteCloserError, *bufio.Reader, func()) {
if repo.checkCancel == nil || repo.checkReader.Buffered() > 0 {
log.Debug("Opening temporary cat file batch-check: %s", repo.Path)
return CatFileBatchCheck(repo.Path)
return CatFileBatchCheck(ctx, repo.Path)
}
return repo.checkWriter, repo.checkReader, func() {}
}

View File

@ -8,12 +8,12 @@ import "fmt"
// FileBlame return the Blame object of file
func (repo *Repository) FileBlame(revision, path, file string) ([]byte, error) {
return NewCommand("blame", "--root", "--", file).RunInDirBytes(path)
return NewCommandContext(repo.Ctx, "blame", "--root", "--", file).RunInDirBytes(path)
}
// LineBlame returns the latest commit at the given line
func (repo *Repository) LineBlame(revision, path, file string, line uint) (*Commit, error) {
res, err := NewCommand("blame", fmt.Sprintf("-L %d,%d", line, line), "-p", revision, "--", file).RunInDir(path)
res, err := NewCommandContext(repo.Ctx, "blame", fmt.Sprintf("-L %d,%d", line, line), "-p", revision, "--", file).RunInDir(path)
if err != nil {
return nil, err
}

View File

@ -6,6 +6,7 @@
package git
import (
"context"
"fmt"
"strings"
)
@ -22,14 +23,14 @@ const PullRequestPrefix = "refs/for/"
// TODO: /refs/for-review for suggest change interface
// IsReferenceExist returns true if given reference exists in the repository.
func IsReferenceExist(repoPath, name string) bool {
_, err := NewCommand("show-ref", "--verify", "--", name).RunInDir(repoPath)
func IsReferenceExist(ctx context.Context, repoPath, name string) bool {
_, err := NewCommandContext(ctx, "show-ref", "--verify", "--", name).RunInDir(repoPath)
return err == nil
}
// IsBranchExist returns true if given branch exists in the repository.
func IsBranchExist(repoPath, name string) bool {
return IsReferenceExist(repoPath, BranchPrefix+name)
func IsBranchExist(ctx context.Context, repoPath, name string) bool {
return IsReferenceExist(ctx, repoPath, BranchPrefix+name)
}
// Branch represents a Git branch.
@ -45,7 +46,7 @@ func (repo *Repository) GetHEADBranch() (*Branch, error) {
if repo == nil {
return nil, fmt.Errorf("nil repo")
}
stdout, err := NewCommand("symbolic-ref", "HEAD").RunInDir(repo.Path)
stdout, err := NewCommandContext(repo.Ctx, "symbolic-ref", "HEAD").RunInDir(repo.Path)
if err != nil {
return nil, err
}
@ -64,13 +65,13 @@ func (repo *Repository) GetHEADBranch() (*Branch, error) {
// SetDefaultBranch sets default branch of repository.
func (repo *Repository) SetDefaultBranch(name string) error {
_, err := NewCommand("symbolic-ref", "HEAD", BranchPrefix+name).RunInDir(repo.Path)
_, err := NewCommandContext(repo.Ctx, "symbolic-ref", "HEAD", BranchPrefix+name).RunInDir(repo.Path)
return err
}
// GetDefaultBranch gets default branch of repository.
func (repo *Repository) GetDefaultBranch() (string, error) {
return NewCommand("symbolic-ref", "HEAD").RunInDir(repo.Path)
return NewCommandContext(repo.Ctx, "symbolic-ref", "HEAD").RunInDir(repo.Path)
}
// GetBranch returns a branch by it's name
@ -118,7 +119,7 @@ type DeleteBranchOptions struct {
// DeleteBranch delete a branch by name on repository.
func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) error {
cmd := NewCommand("branch")
cmd := NewCommandContext(repo.Ctx, "branch")
if opts.Force {
cmd.AddArguments("-D")
@ -134,7 +135,7 @@ func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) erro
// CreateBranch create a new branch
func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error {
cmd := NewCommand("branch")
cmd := NewCommandContext(repo.Ctx, "branch")
cmd.AddArguments("--", branch, oldbranchOrCommit)
_, err := cmd.RunInDir(repo.Path)
@ -144,7 +145,7 @@ func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error {
// AddRemote adds a new remote to repository.
func (repo *Repository) AddRemote(name, url string, fetch bool) error {
cmd := NewCommand("remote", "add")
cmd := NewCommandContext(repo.Ctx, "remote", "add")
if fetch {
cmd.AddArguments("-f")
}
@ -156,7 +157,7 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error {
// RemoveRemote removes a remote from repository.
func (repo *Repository) RemoveRemote(name string) error {
_, err := NewCommand("remote", "rm", name).RunInDir(repo.Path)
_, err := NewCommandContext(repo.Ctx, "remote", "rm", name).RunInDir(repo.Path)
return err
}
@ -167,6 +168,6 @@ func (branch *Branch) GetCommit() (*Commit, error) {
// RenameBranch rename a branch
func (repo *Repository) RenameBranch(from, to string) error {
_, err := NewCommand("branch", "-m", from, to).RunInDir(repo.Path)
_, err := NewCommandContext(repo.Ctx, "branch", "-m", from, to).RunInDir(repo.Path)
return err
}

View File

@ -11,6 +11,7 @@ package git
import (
"bufio"
"bytes"
"context"
"io"
"strings"
@ -23,7 +24,7 @@ func (repo *Repository) IsObjectExist(name string) bool {
return false
}
wr, rd, cancel := repo.CatFileBatchCheck()
wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx)
defer cancel()
_, err := wr.Write([]byte(name + "\n"))
if err != nil {
@ -40,7 +41,7 @@ func (repo *Repository) IsReferenceExist(name string) bool {
return false
}
wr, rd, cancel := repo.CatFileBatchCheck()
wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx)
defer cancel()
_, err := wr.Write([]byte(name + "\n"))
if err != nil {
@ -63,11 +64,11 @@ func (repo *Repository) IsBranchExist(name string) bool {
// GetBranches returns branches from the repository, skipping skip initial branches and
// returning at most limit branches, or all branches if limit is 0.
func (repo *Repository) GetBranches(skip, limit int) ([]string, int, error) {
return callShowRef(repo.Path, BranchPrefix, "--heads", skip, limit)
return callShowRef(repo.Ctx, repo.Path, BranchPrefix, "--heads", skip, limit)
}
// callShowRef return refs, if limit = 0 it will not limit
func callShowRef(repoPath, prefix, arg string, skip, limit int) (branchNames []string, countAll int, err error) {
func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit int) (branchNames []string, countAll int, err error) {
stdoutReader, stdoutWriter := io.Pipe()
defer func() {
_ = stdoutReader.Close()
@ -76,7 +77,7 @@ func callShowRef(repoPath, prefix, arg string, skip, limit int) (branchNames []s
go func() {
stderrBuilder := &strings.Builder{}
err := NewCommand("show-ref", arg).RunInDirPipeline(repoPath, stdoutWriter, stderrBuilder)
err := NewCommandContext(ctx, "show-ref", arg).RunInDirPipeline(repoPath, stdoutWriter, stderrBuilder)
if err != nil {
if stderrBuilder.Len() == 0 {
_ = stdoutWriter.Close()

View File

@ -58,7 +58,7 @@ func (repo *Repository) getCommitByPathWithID(id SHA1, relpath string) (*Commit,
relpath = `\` + relpath
}
stdout, err := NewCommand("log", "-1", prettyLogFormat, id.String(), "--", relpath).RunInDir(repo.Path)
stdout, err := NewCommandContext(repo.Ctx, "log", "-1", prettyLogFormat, id.String(), "--", relpath).RunInDir(repo.Path)
if err != nil {
return nil, err
}
@ -73,7 +73,7 @@ func (repo *Repository) getCommitByPathWithID(id SHA1, relpath string) (*Commit,
// GetCommitByPath returns the last commit of relative path.
func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
stdout, err := NewCommand("log", "-1", prettyLogFormat, "--", relpath).RunInDirBytes(repo.Path)
stdout, err := NewCommandContext(repo.Ctx, "log", "-1", prettyLogFormat, "--", relpath).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
}
@ -86,7 +86,7 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
}
func (repo *Repository) commitsByRange(id SHA1, page, pageSize int) ([]*Commit, error) {
stdout, err := NewCommand("log", id.String(), "--skip="+strconv.Itoa((page-1)*pageSize),
stdout, err := NewCommandContext(repo.Ctx, "log", id.String(), "--skip="+strconv.Itoa((page-1)*pageSize),
"--max-count="+strconv.Itoa(pageSize), prettyLogFormat).RunInDirBytes(repo.Path)
if err != nil {
@ -97,7 +97,7 @@ func (repo *Repository) commitsByRange(id SHA1, page, pageSize int) ([]*Commit,
func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Commit, error) {
// create new git log command with limit of 100 commis
cmd := NewCommand("log", id.String(), "-100", prettyLogFormat)
cmd := NewCommandContext(repo.Ctx, "log", id.String(), "-100", prettyLogFormat)
// ignore case
args := []string{"-i"}
@ -155,7 +155,7 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co
// ignore anything below 4 characters as too unspecific
if len(v) >= 4 {
// create new git log command with 1 commit limit
hashCmd := NewCommand("log", "-1", prettyLogFormat)
hashCmd := NewCommandContext(repo.Ctx, "log", "-1", prettyLogFormat)
// add previous arguments except for --grep and --all
hashCmd.AddArguments(args...)
// add keyword as <commit>
@ -176,7 +176,7 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co
}
func (repo *Repository) getFilesChanged(id1, id2 string) ([]string, error) {
stdout, err := NewCommand("diff", "--name-only", id1, id2).RunInDirBytes(repo.Path)
stdout, err := NewCommandContext(repo.Ctx, "diff", "--name-only", id1, id2).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
}
@ -186,7 +186,7 @@ func (repo *Repository) getFilesChanged(id1, id2 string) ([]string, error) {
// FileChangedBetweenCommits Returns true if the file changed between commit IDs id1 and id2
// You must ensure that id1 and id2 are valid commit ids.
func (repo *Repository) FileChangedBetweenCommits(filename, id1, id2 string) (bool, error) {
stdout, err := NewCommand("diff", "--name-only", "-z", id1, id2, "--", filename).RunInDirBytes(repo.Path)
stdout, err := NewCommandContext(repo.Ctx, "diff", "--name-only", "-z", id1, id2, "--", filename).RunInDirBytes(repo.Path)
if err != nil {
return false, err
}
@ -209,7 +209,7 @@ func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (
}()
go func() {
stderr := strings.Builder{}
err := NewCommand("log", revision, "--follow",
err := NewCommandContext(repo.Ctx, "log", revision, "--follow",
"--max-count="+strconv.Itoa(setting.Git.CommitsRangeSize*page),
prettyLogFormat, "--", file).
RunInDirPipeline(repo.Path, stdoutWriter, &stderr)
@ -240,7 +240,7 @@ func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (
// CommitsByFileAndRangeNoFollow return the commits according revision file and the page
func (repo *Repository) CommitsByFileAndRangeNoFollow(revision, file string, page int) ([]*Commit, error) {
stdout, err := NewCommand("log", revision, "--skip="+strconv.Itoa((page-1)*50),
stdout, err := NewCommandContext(repo.Ctx, "log", revision, "--skip="+strconv.Itoa((page-1)*50),
"--max-count="+strconv.Itoa(setting.Git.CommitsRangeSize), prettyLogFormat, "--", file).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
@ -250,11 +250,11 @@ func (repo *Repository) CommitsByFileAndRangeNoFollow(revision, file string, pag
// FilesCountBetween return the number of files changed between two commits
func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (int, error) {
stdout, err := NewCommand("diff", "--name-only", startCommitID+"..."+endCommitID).RunInDir(repo.Path)
stdout, err := NewCommandContext(repo.Ctx, "diff", "--name-only", startCommitID+"..."+endCommitID).RunInDir(repo.Path)
if err != nil && strings.Contains(err.Error(), "no merge base") {
// git >= 2.28 now returns an error if startCommitID and endCommitID have become unrelated.
// previously it would return the results of git diff --name-only startCommitID endCommitID so let's try that...
stdout, err = NewCommand("diff", "--name-only", startCommitID, endCommitID).RunInDir(repo.Path)
stdout, err = NewCommandContext(repo.Ctx, "diff", "--name-only", startCommitID, endCommitID).RunInDir(repo.Path)
}
if err != nil {
return 0, err
@ -268,13 +268,13 @@ func (repo *Repository) CommitsBetween(last *Commit, before *Commit) ([]*Commit,
var stdout []byte
var err error
if before == nil {
stdout, err = NewCommand("rev-list", last.ID.String()).RunInDirBytes(repo.Path)
stdout, err = NewCommandContext(repo.Ctx, "rev-list", last.ID.String()).RunInDirBytes(repo.Path)
} else {
stdout, err = NewCommand("rev-list", before.ID.String()+".."+last.ID.String()).RunInDirBytes(repo.Path)
stdout, err = NewCommandContext(repo.Ctx, "rev-list", before.ID.String()+".."+last.ID.String()).RunInDirBytes(repo.Path)
if err != nil && strings.Contains(err.Error(), "no merge base") {
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
// previously it would return the results of git rev-list before last so let's try that...
stdout, err = NewCommand("rev-list", before.ID.String(), last.ID.String()).RunInDirBytes(repo.Path)
stdout, err = NewCommandContext(repo.Ctx, "rev-list", before.ID.String(), last.ID.String()).RunInDirBytes(repo.Path)
}
}
if err != nil {
@ -288,13 +288,13 @@ func (repo *Repository) CommitsBetweenLimit(last *Commit, before *Commit, limit,
var stdout []byte
var err error
if before == nil {
stdout, err = NewCommand("rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), last.ID.String()).RunInDirBytes(repo.Path)
stdout, err = NewCommandContext(repo.Ctx, "rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), last.ID.String()).RunInDirBytes(repo.Path)
} else {
stdout, err = NewCommand("rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), before.ID.String()+".."+last.ID.String()).RunInDirBytes(repo.Path)
stdout, err = NewCommandContext(repo.Ctx, "rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), before.ID.String()+".."+last.ID.String()).RunInDirBytes(repo.Path)
if err != nil && strings.Contains(err.Error(), "no merge base") {
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
// previously it would return the results of git rev-list --max-count n before last so let's try that...
stdout, err = NewCommand("rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), before.ID.String(), last.ID.String()).RunInDirBytes(repo.Path)
stdout, err = NewCommandContext(repo.Ctx, "rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), before.ID.String(), last.ID.String()).RunInDirBytes(repo.Path)
}
}
if err != nil {
@ -333,7 +333,7 @@ func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) {
// commitsBefore the limit is depth, not total number of returned commits.
func (repo *Repository) commitsBefore(id SHA1, limit int) ([]*Commit, error) {
cmd := NewCommand("log")
cmd := NewCommandContext(repo.Ctx, "log")
if limit > 0 {
cmd.AddArguments("-"+strconv.Itoa(limit), prettyLogFormat, id.String())
} else {
@ -377,7 +377,7 @@ func (repo *Repository) getCommitsBeforeLimit(id SHA1, num int) ([]*Commit, erro
func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error) {
if CheckGitVersionAtLeast("2.7.0") == nil {
stdout, err := NewCommand("for-each-ref", "--count="+strconv.Itoa(limit), "--format=%(refname:strip=2)", "--contains", commit.ID.String(), BranchPrefix).RunInDir(repo.Path)
stdout, err := NewCommandContext(repo.Ctx, "for-each-ref", "--count="+strconv.Itoa(limit), "--format=%(refname:strip=2)", "--contains", commit.ID.String(), BranchPrefix).RunInDir(repo.Path)
if err != nil {
return nil, err
}
@ -386,7 +386,7 @@ func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error)
return branches, nil
}
stdout, err := NewCommand("branch", "--contains", commit.ID.String()).RunInDir(repo.Path)
stdout, err := NewCommandContext(repo.Ctx, "branch", "--contains", commit.ID.String()).RunInDir(repo.Path)
if err != nil {
return nil, err
}
@ -425,7 +425,7 @@ func (repo *Repository) GetCommitsFromIDs(commitIDs []string) []*Commit {
// IsCommitInBranch check if the commit is on the branch
func (repo *Repository) IsCommitInBranch(commitID, branch string) (r bool, err error) {
stdout, err := NewCommand("branch", "--contains", commitID, branch).RunInDir(repo.Path)
stdout, err := NewCommandContext(repo.Ctx, "branch", "--contains", commitID, branch).RunInDir(repo.Path)
if err != nil {
return false, err
}

View File

@ -40,7 +40,7 @@ func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
}
}
actualCommitID, err := NewCommand("rev-parse", "--verify", commitID).RunInDir(repo.Path)
actualCommitID, err := NewCommandContext(repo.Ctx, "rev-parse", "--verify", commitID).RunInDir(repo.Path)
if err != nil {
if strings.Contains(err.Error(), "unknown revision or path") ||
strings.Contains(err.Error(), "fatal: Needed a single revision") {

View File

@ -18,7 +18,7 @@ import (
// ResolveReference resolves a name to a reference
func (repo *Repository) ResolveReference(name string) (string, error) {
stdout, err := NewCommand("show-ref", "--hash", name).RunInDir(repo.Path)
stdout, err := NewCommandContext(repo.Ctx, "show-ref", "--hash", name).RunInDir(repo.Path)
if err != nil {
if strings.Contains(err.Error(), "not a valid ref") {
return "", ErrNotExist{name, ""}
@ -35,7 +35,7 @@ func (repo *Repository) ResolveReference(name string) (string, error) {
// GetRefCommitID returns the last commit ID string of given reference (branch or tag).
func (repo *Repository) GetRefCommitID(name string) (string, error) {
wr, rd, cancel := repo.CatFileBatchCheck()
wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx)
defer cancel()
_, _ = wr.Write([]byte(name + "\n"))
shaBs, _, _, err := ReadBatchLine(rd)
@ -48,12 +48,12 @@ func (repo *Repository) GetRefCommitID(name string) (string, error) {
// IsCommitExist returns true if given commit exists in current repository.
func (repo *Repository) IsCommitExist(name string) bool {
_, err := NewCommand("cat-file", "-e", name).RunInDir(repo.Path)
_, err := NewCommandContext(repo.Ctx, "cat-file", "-e", name).RunInDir(repo.Path)
return err == nil
}
func (repo *Repository) getCommit(id SHA1) (*Commit, error) {
wr, rd, cancel := repo.CatFileBatch()
wr, rd, cancel := repo.CatFileBatch(repo.Ctx)
defer cancel()
_, _ = wr.Write([]byte(id.String() + "\n"))
@ -132,7 +132,7 @@ func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
}
}
wr, rd, cancel := repo.CatFileBatchCheck()
wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx)
defer cancel()
_, err := wr.Write([]byte(commitID + "\n"))
if err != nil {

View File

@ -35,13 +35,13 @@ func (repo *Repository) GetMergeBase(tmpRemote string, base, head string) (strin
if tmpRemote != "origin" {
tmpBaseName := "refs/remotes/" + tmpRemote + "/tmp_" + base
// Fetch commit into a temporary branch in order to be able to handle commits and tags
_, err := NewCommand("fetch", tmpRemote, base+":"+tmpBaseName).RunInDir(repo.Path)
_, err := NewCommandContext(repo.Ctx, "fetch", tmpRemote, base+":"+tmpBaseName).RunInDir(repo.Path)
if err == nil {
base = tmpBaseName
}
}
stdout, err := NewCommand("merge-base", "--", base, head).RunInDir(repo.Path)
stdout, err := NewCommandContext(repo.Ctx, "merge-base", "--", base, head).RunInDir(repo.Path)
return strings.TrimSpace(stdout), base, err
}
@ -88,7 +88,7 @@ func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string,
// We have a common base - therefore we know that ... should work
if !fileOnly {
logs, err := NewCommand("log", baseCommitID+separator+headBranch, prettyLogFormat).RunInDirBytes(repo.Path)
logs, err := NewCommandContext(repo.Ctx, "log", baseCommitID+separator+headBranch, prettyLogFormat).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
}
@ -141,14 +141,14 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis
separator = ".."
}
if err := NewCommand("diff", "-z", "--name-only", base+separator+head).
if err := NewCommandContext(repo.Ctx, "diff", "-z", "--name-only", base+separator+head).
RunInDirPipeline(repo.Path, w, stderr); err != nil {
if strings.Contains(stderr.String(), "no merge base") {
// git >= 2.28 now returns an error if base and head have become unrelated.
// previously it would return the results of git diff -z --name-only base head so let's try that...
w = &lineCountWriter{}
stderr.Reset()
if err = NewCommand("diff", "-z", "--name-only", base, head).RunInDirPipeline(repo.Path, w, stderr); err == nil {
if err = NewCommandContext(repo.Ctx, "diff", "-z", "--name-only", base, head).RunInDirPipeline(repo.Path, w, stderr); err == nil {
return w.numLines, nil
}
}
@ -231,23 +231,23 @@ func (repo *Repository) GetDiffOrPatch(base, head string, w io.Writer, patch, bi
// GetDiff generates and returns patch data between given revisions, optimized for human readability
func (repo *Repository) GetDiff(base, head string, w io.Writer) error {
return NewCommand("diff", "-p", base, head).
return NewCommandContext(repo.Ctx, "diff", "-p", base, head).
RunInDirPipeline(repo.Path, w, nil)
}
// GetDiffBinary generates and returns patch data between given revisions, including binary diffs.
func (repo *Repository) GetDiffBinary(base, head string, w io.Writer) error {
return NewCommand("diff", "-p", "--binary", base, head).
return NewCommandContext(repo.Ctx, "diff", "-p", "--binary", base, head).
RunInDirPipeline(repo.Path, w, nil)
}
// GetPatch generates and returns format-patch data between given revisions, able to be used with `git apply`
func (repo *Repository) GetPatch(base, head string, w io.Writer) error {
stderr := new(bytes.Buffer)
err := NewCommand("format-patch", "--binary", "--stdout", base+"..."+head).
err := NewCommandContext(repo.Ctx, "format-patch", "--binary", "--stdout", base+"..."+head).
RunInDirPipeline(repo.Path, w, stderr)
if err != nil && bytes.Contains(stderr.Bytes(), []byte("no merge base")) {
return NewCommand("format-patch", "--binary", "--stdout", base, head).
return NewCommandContext(repo.Ctx, "format-patch", "--binary", "--stdout", base, head).
RunInDirPipeline(repo.Path, w, nil)
}
return err
@ -256,7 +256,7 @@ func (repo *Repository) GetPatch(base, head string, w io.Writer) error {
// GetDiffFromMergeBase generates and return patch data from merge base to head
func (repo *Repository) GetDiffFromMergeBase(base, head string, w io.Writer) error {
stderr := new(bytes.Buffer)
err := NewCommand("diff", "-p", "--binary", base+"..."+head).
err := NewCommandContext(repo.Ctx, "diff", "-p", "--binary", base+"..."+head).
RunInDirPipeline(repo.Path, w, stderr)
if err != nil && bytes.Contains(stderr.Bytes(), []byte("no merge base")) {
return repo.GetDiffBinary(base, head, w)

View File

@ -34,7 +34,7 @@ func (repo *Repository) GetDefaultPublicGPGKey(forceUpdate bool) (*GPGSettings,
Sign: true,
}
value, _ := NewCommand("config", "--get", "commit.gpgsign").RunInDir(repo.Path)
value, _ := NewCommandContext(repo.Ctx, "config", "--get", "commit.gpgsign").RunInDir(repo.Path)
sign, valid := ParseBool(strings.TrimSpace(value))
if !sign || !valid {
gpgSettings.Sign = false
@ -42,13 +42,13 @@ func (repo *Repository) GetDefaultPublicGPGKey(forceUpdate bool) (*GPGSettings,
return gpgSettings, nil
}
signingKey, _ := NewCommand("config", "--get", "user.signingkey").RunInDir(repo.Path)
signingKey, _ := NewCommandContext(repo.Ctx, "config", "--get", "user.signingkey").RunInDir(repo.Path)
gpgSettings.KeyID = strings.TrimSpace(signingKey)
defaultEmail, _ := NewCommand("config", "--get", "user.email").RunInDir(repo.Path)
defaultEmail, _ := NewCommandContext(repo.Ctx, "config", "--get", "user.email").RunInDir(repo.Path)
gpgSettings.Email = strings.TrimSpace(defaultEmail)
defaultName, _ := NewCommand("config", "--get", "user.name").RunInDir(repo.Path)
defaultName, _ := NewCommandContext(repo.Ctx, "config", "--get", "user.name").RunInDir(repo.Path)
gpgSettings.Name = strings.TrimSpace(defaultName)
if err := gpgSettings.LoadPublicKeyContent(); err != nil {

View File

@ -18,7 +18,7 @@ import (
// ReadTreeToIndex reads a treeish to the index
func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string) error {
if len(treeish) != 40 {
res, err := NewCommand("rev-parse", "--verify", treeish).RunInDir(repo.Path)
res, err := NewCommandContext(repo.Ctx, "rev-parse", "--verify", treeish).RunInDir(repo.Path)
if err != nil {
return err
}
@ -38,7 +38,7 @@ func (repo *Repository) readTreeToIndex(id SHA1, indexFilename ...string) error
if len(indexFilename) > 0 {
env = append(os.Environ(), "GIT_INDEX_FILE="+indexFilename[0])
}
_, err := NewCommand("read-tree", id.String()).RunInDirWithEnv(repo.Path, env)
_, err := NewCommandContext(repo.Ctx, "read-tree", id.String()).RunInDirWithEnv(repo.Path, env)
if err != nil {
return err
}
@ -69,13 +69,13 @@ func (repo *Repository) ReadTreeToTemporaryIndex(treeish string) (filename, tmpD
// EmptyIndex empties the index
func (repo *Repository) EmptyIndex() error {
_, err := NewCommand("read-tree", "--empty").RunInDir(repo.Path)
_, err := NewCommandContext(repo.Ctx, "read-tree", "--empty").RunInDir(repo.Path)
return err
}
// LsFiles checks if the given filenames are in the index
func (repo *Repository) LsFiles(filenames ...string) ([]string, error) {
cmd := NewCommand("ls-files", "-z", "--")
cmd := NewCommandContext(repo.Ctx, "ls-files", "-z", "--")
for _, arg := range filenames {
if arg != "" {
cmd.AddArguments(arg)
@ -95,7 +95,7 @@ func (repo *Repository) LsFiles(filenames ...string) ([]string, error) {
// RemoveFilesFromIndex removes given filenames from the index - it does not check whether they are present.
func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
cmd := NewCommand("update-index", "--remove", "-z", "--index-info")
cmd := NewCommandContext(repo.Ctx, "update-index", "--remove", "-z", "--index-info")
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
buffer := new(bytes.Buffer)
@ -111,14 +111,14 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
// AddObjectToIndex adds the provided object hash to the index at the provided filename
func (repo *Repository) AddObjectToIndex(mode string, object SHA1, filename string) error {
cmd := NewCommand("update-index", "--add", "--replace", "--cacheinfo", mode, object.String(), filename)
cmd := NewCommandContext(repo.Ctx, "update-index", "--add", "--replace", "--cacheinfo", mode, object.String(), filename)
_, err := cmd.RunInDir(repo.Path)
return err
}
// WriteTree writes the current index as a tree to the object db and returns its hash
func (repo *Repository) WriteTree() (*Tree, error) {
res, err := NewCommand("write-tree").RunInDir(repo.Path)
res, err := NewCommandContext(repo.Ctx, "write-tree").RunInDir(repo.Path)
if err != nil {
return nil, err
}

View File

@ -25,7 +25,7 @@ import (
func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, error) {
// We will feed the commit IDs in order into cat-file --batch, followed by blobs as necessary.
// so let's create a batch stdin and stdout
batchStdinWriter, batchReader, cancel := repo.CatFileBatch()
batchStdinWriter, batchReader, cancel := repo.CatFileBatch(repo.Ctx)
defer cancel()
writeID := func(id string) error {
@ -76,7 +76,7 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
IndexFile: indexFilename,
WorkTree: worktree,
}
ctx, cancel := context.WithCancel(DefaultContext)
ctx, cancel := context.WithCancel(repo.Ctx)
if err := checker.Init(ctx); err != nil {
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
} else {
@ -96,6 +96,12 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
var content []byte
sizes := make(map[string]int64)
for _, f := range entries {
select {
case <-repo.Ctx.Done():
return sizes, repo.Ctx.Err()
default:
}
contentBuf.Reset()
content = contentBuf.Bytes()

View File

@ -42,7 +42,7 @@ func (repo *Repository) HashObject(reader io.Reader) (SHA1, error) {
}
func (repo *Repository) hashObject(reader io.Reader) (string, error) {
cmd := NewCommand("hash-object", "-w", "--stdin")
cmd := NewCommandContext(repo.Ctx, "hash-object", "-w", "--stdin")
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
err := cmd.RunInDirFullPipeline(repo.Path, stdout, stderr, reader)

View File

@ -23,7 +23,7 @@ func (repo *Repository) GetRefsFiltered(pattern string) ([]*Reference, error) {
go func() {
stderrBuilder := &strings.Builder{}
err := NewCommand("for-each-ref").RunInDirPipeline(repo.Path, stdoutWriter, stderrBuilder)
err := NewCommandContext(repo.Ctx, "for-each-ref").RunInDirPipeline(repo.Path, stdoutWriter, stderrBuilder)
if err != nil {
_ = stdoutWriter.CloseWithError(ConcatenateError(err, stderrBuilder.String()))
} else {

View File

@ -39,7 +39,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
since := fromTime.Format(time.RFC3339)
stdout, err := NewCommand("rev-list", "--count", "--no-merges", "--branches=*", "--date=iso", fmt.Sprintf("--since='%s'", since)).RunInDirBytes(repo.Path)
stdout, err := NewCommandContext(repo.Ctx, "rev-list", "--count", "--no-merges", "--branches=*", "--date=iso", fmt.Sprintf("--since='%s'", since)).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
}
@ -67,7 +67,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
}
stderr := new(strings.Builder)
err = NewCommand(args...).RunInDirTimeoutEnvFullPipelineFunc(
err = NewCommandContext(repo.Ctx, args...).RunInDirTimeoutEnvFullPipelineFunc(
nil, -1, repo.Path,
stdoutWriter, stderr, nil,
func(ctx context.Context, cancel context.CancelFunc) error {

View File

@ -6,6 +6,7 @@
package git
import (
"context"
"fmt"
"strings"
@ -17,19 +18,19 @@ import (
const TagPrefix = "refs/tags/"
// IsTagExist returns true if given tag exists in the repository.
func IsTagExist(repoPath, name string) bool {
return IsReferenceExist(repoPath, TagPrefix+name)
func IsTagExist(ctx context.Context, repoPath, name string) bool {
return IsReferenceExist(ctx, repoPath, TagPrefix+name)
}
// CreateTag create one tag in the repository
func (repo *Repository) CreateTag(name, revision string) error {
_, err := NewCommand("tag", "--", name, revision).RunInDir(repo.Path)
_, err := NewCommandContext(repo.Ctx, "tag", "--", name, revision).RunInDir(repo.Path)
return err
}
// CreateAnnotatedTag create one annotated tag in the repository
func (repo *Repository) CreateAnnotatedTag(name, message, revision string) error {
_, err := NewCommand("tag", "-a", "-m", message, "--", name, revision).RunInDir(repo.Path)
_, err := NewCommandContext(repo.Ctx, "tag", "-a", "-m", message, "--", name, revision).RunInDir(repo.Path)
return err
}
@ -79,7 +80,7 @@ func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
}
// The tag is an annotated tag with a message.
data, err := NewCommand("cat-file", "-p", tagID.String()).RunInDirBytes(repo.Path)
data, err := NewCommandContext(repo.Ctx, "cat-file", "-p", tagID.String()).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
}
@ -104,7 +105,7 @@ func (repo *Repository) GetTagNameBySHA(sha string) (string, error) {
return "", fmt.Errorf("SHA is too short: %s", sha)
}
stdout, err := NewCommand("show-ref", "--tags", "-d").RunInDir(repo.Path)
stdout, err := NewCommandContext(repo.Ctx, "show-ref", "--tags", "-d").RunInDir(repo.Path)
if err != nil {
return "", err
}
@ -127,7 +128,7 @@ func (repo *Repository) GetTagNameBySHA(sha string) (string, error) {
// GetTagID returns the object ID for a tag (annotated tags have both an object SHA AND a commit SHA)
func (repo *Repository) GetTagID(name string) (string, error) {
stdout, err := NewCommand("show-ref", "--tags", "--", name).RunInDir(repo.Path)
stdout, err := NewCommandContext(repo.Ctx, "show-ref", "--tags", "--", name).RunInDir(repo.Path)
if err != nil {
return "", err
}
@ -163,7 +164,7 @@ func (repo *Repository) GetTag(name string) (*Tag, error) {
// GetTagInfos returns all tag infos of the repository.
func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
// TODO this a slow implementation, makes one git command per tag
stdout, err := NewCommand("tag").RunInDir(repo.Path)
stdout, err := NewCommandContext(repo.Ctx, "tag").RunInDir(repo.Path)
if err != nil {
return nil, 0, err
}
@ -196,7 +197,7 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
func (repo *Repository) GetTagType(id SHA1) (string, error) {
// Get tag type
stdout, err := NewCommand("cat-file", "-t", id.String()).RunInDir(repo.Path)
stdout, err := NewCommandContext(repo.Ctx, "cat-file", "-t", id.String()).RunInDir(repo.Path)
if err != nil {
return "", err
}

View File

@ -20,6 +20,6 @@ func (repo *Repository) IsTagExist(name string) bool {
// GetTags returns all tags of the repository.
// returning at most limit tags, or all if limit is 0.
func (repo *Repository) GetTags(skip, limit int) (tags []string, err error) {
tags, _, err = callShowRef(repo.Path, TagPrefix, "--tags", skip, limit)
tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, "--tags", skip, limit)
return
}

View File

@ -40,7 +40,7 @@ func (repo *Repository) CommitTree(author *Signature, committer *Signature, tree
"GIT_COMMITTER_EMAIL="+committer.Email,
"GIT_COMMITTER_DATE="+commitTimeStr,
)
cmd := NewCommand("commit-tree", tree.ID.String())
cmd := NewCommandContext(repo.Ctx, "commit-tree", tree.ID.String())
for _, parent := range opts.Parents {
cmd.AddArguments("-p", parent)

View File

@ -22,7 +22,7 @@ func (repo *Repository) getTree(id SHA1) (*Tree, error) {
// GetTree find the tree object in the repository.
func (repo *Repository) GetTree(idStr string) (*Tree, error) {
if len(idStr) != 40 {
res, err := NewCommand("rev-parse", "--verify", idStr).RunInDir(repo.Path)
res, err := NewCommandContext(repo.Ctx, "rev-parse", "--verify", idStr).RunInDir(repo.Path)
if err != nil {
return nil, err
}

View File

@ -12,7 +12,7 @@ import (
)
func (repo *Repository) getTree(id SHA1) (*Tree, error) {
wr, rd, cancel := repo.CatFileBatch()
wr, rd, cancel := repo.CatFileBatch(repo.Ctx)
defer cancel()
_, _ = wr.Write([]byte(id.String() + "\n"))

View File

@ -36,7 +36,7 @@ func (t *Tree) ListEntries() (Entries, error) {
}
if t.repo != nil {
wr, rd, cancel := t.repo.CatFileBatch()
wr, rd, cancel := t.repo.CatFileBatch(t.repo.Ctx)
defer cancel()
_, _ = wr.Write([]byte(t.ID.String() + "\n"))

View File

@ -275,7 +275,7 @@ func (b *BleveIndexer) Index(repo *models.Repository, sha string, changes *repoC
batch := gitea_bleve.NewFlushingBatch(b.indexer, maxBatchSize)
if len(changes.Updates) > 0 {
batchWriter, batchReader, cancel := git.CatFileBatch(repo.RepoPath())
batchWriter, batchReader, cancel := git.CatFileBatch(git.DefaultContext, repo.RepoPath())
defer cancel()
for _, update := range changes.Updates {

View File

@ -248,7 +248,7 @@ func (b *ElasticSearchIndexer) Index(repo *models.Repository, sha string, change
reqs := make([]elastic.BulkableRequest, 0)
if len(changes.Updates) > 0 {
batchWriter, batchReader, cancel := git.CatFileBatch(repo.RepoPath())
batchWriter, batchReader, cancel := git.CatFileBatch(git.DefaultContext, repo.RepoPath())
defer cancel()
for _, update := range changes.Updates {

View File

@ -5,9 +5,13 @@
package stats
import (
"fmt"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
)
// DBIndexer implements Indexer interface to use database's like search
@ -16,6 +20,9 @@ type DBIndexer struct {
// Index repository status function
func (db *DBIndexer) Index(id int64) error {
ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().ShutdownContext(), fmt.Sprintf("Stats.DB Index Repo[%d]", id))
defer finished()
repo, err := models.GetRepositoryByID(id)
if err != nil {
return err
@ -29,7 +36,7 @@ func (db *DBIndexer) Index(id int64) error {
return err
}
gitRepo, err := git.OpenRepository(repo.RepoPath())
gitRepo, err := git.OpenRepositoryCtx(ctx, repo.RepoPath())
if err != nil {
return err
}

View File

@ -5,7 +5,6 @@
package external
import (
"context"
"fmt"
"io"
"os"
@ -107,11 +106,8 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.
ctx.Ctx = graceful.GetManager().ShutdownContext()
}
processCtx, cancel := context.WithCancel(ctx.Ctx)
defer cancel()
pid := process.GetManager().Add(fmt.Sprintf("Render [%s] for %s", commands[0], ctx.URLPrefix), cancel)
defer process.GetManager().Remove(pid)
processCtx, _, finished := process.GetManager().AddContext(ctx.Ctx, fmt.Sprintf("Render [%s] for %s", commands[0], ctx.URLPrefix))
defer finished()
cmd := exec.CommandContext(processCtx, commands[0], args...)
cmd.Env = append(

View File

@ -13,14 +13,18 @@ import (
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)
func newRequest(ctx context.Context, url, method string) *httplib.Request {
if setting.InternalToken == "" {
log.Fatal(`The INTERNAL_TOKEN setting is missing from the configuration file: %q.
Ensure you are running in the correct environment or set the correct configuration file with -c.`, setting.CustomConf)
}
return httplib.NewRequest(url, method).
SetContext(ctx).
Header("Authorization",
fmt.Sprintf("Bearer %s", setting.InternalToken))
Header("Authorization", fmt.Sprintf("Bearer %s", setting.InternalToken))
}
// Response internal request response
@ -44,9 +48,6 @@ func newInternalRequest(ctx context.Context, url, method string) *httplib.Reques
})
if setting.Protocol == setting.UnixSocket {
req.SetTransport(&http.Transport{
Dial: func(_, _ string) (net.Conn, error) {
return net.Dial("unix", setting.HTTPAddr)
},
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
var d net.Dialer
return d.DialContext(ctx, "unix", setting.HTTPAddr)

View File

@ -0,0 +1,69 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package process
import (
"context"
)
// Context is a wrapper around context.Context and contains the current pid for this context
type Context struct {
context.Context
pid IDType
}
// GetPID returns the PID for this context
func (c *Context) GetPID() IDType {
return c.pid
}
// GetParent returns the parent process context (if any)
func (c *Context) GetParent() *Context {
return GetContext(c.Context)
}
// Value is part of the interface for context.Context. We mostly defer to the internal context - but we return this in response to the ProcessContextKey
func (c *Context) Value(key interface{}) interface{} {
if key == ProcessContextKey {
return c
}
return c.Context.Value(key)
}
// ProcessContextKey is the key under which process contexts are stored
var ProcessContextKey interface{} = "process-context"
// GetContext will return a process context if one exists
func GetContext(ctx context.Context) *Context {
if pCtx, ok := ctx.(*Context); ok {
return pCtx
}
pCtxInterface := ctx.Value(ProcessContextKey)
if pCtxInterface == nil {
return nil
}
if pCtx, ok := pCtxInterface.(*Context); ok {
return pCtx
}
return nil
}
// GetPID returns the PID for this context
func GetPID(ctx context.Context) IDType {
pCtx := GetContext(ctx)
if pCtx == nil {
return ""
}
return pCtx.GetPID()
}
// GetParentPID returns the ParentPID for this context
func GetParentPID(ctx context.Context) IDType {
var parentPID IDType
if parentProcess := GetContext(ctx); parentProcess != nil {
parentPID = parentProcess.GetPID()
}
return parentPID
}

View File

@ -12,6 +12,7 @@ import (
"io"
"os/exec"
"sort"
"strconv"
"sync"
"time"
)
@ -28,57 +29,151 @@ var (
DefaultContext = context.Background()
)
// Process represents a working process inheriting from Gitea.
type Process struct {
PID int64 // Process ID, not system one.
Description string
Start time.Time
Cancel context.CancelFunc
}
// IDType is a pid type
type IDType string
// Manager knows about all processes and counts PIDs.
// FinishedFunc is a function that marks that the process is finished and can be removed from the process table
// - it is simply an alias for context.CancelFunc and is only for documentary purposes
type FinishedFunc = context.CancelFunc
// Manager manages all processes and counts PIDs.
type Manager struct {
mutex sync.Mutex
counter int64
processes map[int64]*Process
next int64
lastTime int64
processes map[IDType]*Process
}
// GetManager returns a Manager and initializes one as singleton if there's none yet
func GetManager() *Manager {
managerInit.Do(func() {
manager = &Manager{
processes: make(map[int64]*Process),
processes: make(map[IDType]*Process),
next: 1,
}
})
return manager
}
// Add a process to the ProcessManager and returns its PID.
func (pm *Manager) Add(description string, cancel context.CancelFunc) int64 {
// AddContext creates a new context and adds it as a process. Once the process is finished, finished must be called
// to remove the process from the process table. It should not be called until the process is finished but must always be called.
//
// cancel should be used to cancel the returned context, however it will not remove the process from the process table.
// finished will cancel the returned context and remove it from the process table.
//
// Most processes will not need to use the cancel function but there will be cases whereby you want to cancel the process but not immediately remove it from the
// process table.
func (pm *Manager) AddContext(parent context.Context, description string) (ctx context.Context, cancel context.CancelFunc, finished FinishedFunc) {
parentPID := GetParentPID(parent)
ctx, cancel = context.WithCancel(parent)
pid, finished := pm.Add(parentPID, description, cancel)
return &Context{
Context: ctx,
pid: pid,
}, cancel, finished
}
// AddContextTimeout creates a new context and add it as a process. Once the process is finished, finished must be called
// to remove the process from the process table. It should not be called until the process is finsihed but must always be called.
//
// cancel should be used to cancel the returned context, however it will not remove the process from the process table.
// finished will cancel the returned context and remove it from the process table.
//
// Most processes will not need to use the cancel function but there will be cases whereby you want to cancel the process but not immediately remove it from the
// process table.
func (pm *Manager) AddContextTimeout(parent context.Context, timeout time.Duration, description string) (ctx context.Context, cancel context.CancelFunc, finshed FinishedFunc) {
parentPID := GetParentPID(parent)
ctx, cancel = context.WithTimeout(parent, timeout)
pid, finshed := pm.Add(parentPID, description, cancel)
return &Context{
Context: ctx,
pid: pid,
}, cancel, finshed
}
// Add create a new process
func (pm *Manager) Add(parentPID IDType, description string, cancel context.CancelFunc) (IDType, FinishedFunc) {
pm.mutex.Lock()
pid := pm.counter + 1
pm.processes[pid] = &Process{
start, pid := pm.nextPID()
parent := pm.processes[parentPID]
if parent == nil {
parentPID = ""
}
process := &Process{
PID: pid,
ParentPID: parentPID,
Description: description,
Start: time.Now(),
Start: start,
Cancel: cancel,
}
pm.counter = pid
finished := func() {
cancel()
pm.remove(process)
}
if parent != nil {
parent.AddChild(process)
}
pm.processes[pid] = process
pm.mutex.Unlock()
return pid
return pid, finished
}
// nextPID will return the next available PID. pm.mutex should already be locked.
func (pm *Manager) nextPID() (start time.Time, pid IDType) {
start = time.Now()
startUnix := start.Unix()
if pm.lastTime == startUnix {
pm.next++
} else {
pm.next = 1
}
pm.lastTime = startUnix
pid = IDType(strconv.FormatInt(start.Unix(), 16))
if pm.next == 1 {
return
}
pid = IDType(string(pid) + "-" + strconv.FormatInt(pm.next, 10))
return
}
// Remove a process from the ProcessManager.
func (pm *Manager) Remove(pid int64) {
func (pm *Manager) Remove(pid IDType) {
pm.mutex.Lock()
delete(pm.processes, pid)
pm.mutex.Unlock()
}
func (pm *Manager) remove(process *Process) {
pm.mutex.Lock()
if p := pm.processes[process.PID]; p == process {
delete(pm.processes, process.PID)
}
parent := pm.processes[process.ParentPID]
pm.mutex.Unlock()
if parent == nil {
return
}
parent.RemoveChild(process)
}
// Cancel a process in the ProcessManager.
func (pm *Manager) Cancel(pid int64) {
func (pm *Manager) Cancel(pid IDType) {
pm.mutex.Lock()
process, ok := pm.processes[pid]
pm.mutex.Unlock()
@ -88,14 +183,28 @@ func (pm *Manager) Cancel(pid int64) {
}
// Processes gets the processes in a thread safe manner
func (pm *Manager) Processes() []*Process {
func (pm *Manager) Processes(onlyRoots bool) []*Process {
pm.mutex.Lock()
processes := make([]*Process, 0, len(pm.processes))
for _, process := range pm.processes {
processes = append(processes, process)
if onlyRoots {
for _, process := range pm.processes {
if _, has := pm.processes[process.ParentPID]; !has {
processes = append(processes, process)
}
}
} else {
for _, process := range pm.processes {
processes = append(processes, process)
}
}
pm.mutex.Unlock()
sort.Sort(processList(processes))
sort.Slice(processes, func(i, j int) bool {
left, right := processes[i], processes[j]
return left.Start.Before(right.Start)
})
return processes
}
@ -134,8 +243,8 @@ func (pm *Manager) ExecDirEnvStdIn(timeout time.Duration, dir, desc string, env
stdOut := new(bytes.Buffer)
stdErr := new(bytes.Buffer)
ctx, cancel := context.WithTimeout(DefaultContext, timeout)
defer cancel()
ctx, _, finished := pm.AddContextTimeout(DefaultContext, timeout, desc)
defer finished()
cmd := exec.CommandContext(ctx, cmdName, args...)
cmd.Dir = dir
@ -150,13 +259,11 @@ func (pm *Manager) ExecDirEnvStdIn(timeout time.Duration, dir, desc string, env
return "", "", err
}
pid := pm.Add(desc, cancel)
err := cmd.Wait()
pm.Remove(pid)
if err != nil {
err = &Error{
PID: pid,
PID: GetPID(ctx),
Description: desc,
Err: err,
CtxErr: ctx.Err(),
@ -168,23 +275,9 @@ func (pm *Manager) ExecDirEnvStdIn(timeout time.Duration, dir, desc string, env
return stdOut.String(), stdErr.String(), err
}
type processList []*Process
func (l processList) Len() int {
return len(l)
}
func (l processList) Less(i, j int) bool {
return l[i].PID < l[j].PID
}
func (l processList) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}
// Error is a wrapped error describing the error results of Process Execution
type Error struct {
PID int64
PID IDType
Description string
Err error
CtxErr error
@ -193,7 +286,7 @@ type Error struct {
}
func (err *Error) Error() string {
return fmt.Sprintf("exec(%d:%s) failed: %v(%v) stdout: %s stderr: %s", err.PID, err.Description, err.Err, err.CtxErr, err.Stdout, err.Stderr)
return fmt.Sprintf("exec(%s:%s) failed: %v(%v) stdout: %s stderr: %s", err.PID, err.Description, err.Err, err.CtxErr, err.Stdout, err.Stderr)
}
// Unwrap implements the unwrappable implicit interface for go1.13 Unwrap()

View File

@ -21,44 +21,72 @@ func TestGetManager(t *testing.T) {
assert.NotNil(t, pm)
}
func TestManager_Add(t *testing.T) {
pm := Manager{processes: make(map[int64]*Process)}
func TestManager_AddContext(t *testing.T) {
pm := Manager{processes: make(map[IDType]*Process), next: 1}
pid := pm.Add("foo", nil)
assert.Equal(t, int64(1), pid, "expected to get pid 1 got %d", pid)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
pid = pm.Add("bar", nil)
assert.Equal(t, int64(2), pid, "expected to get pid 2 got %d", pid)
p1Ctx, _, finished := pm.AddContext(ctx, "foo")
defer finished()
assert.NotEmpty(t, GetContext(p1Ctx).GetPID(), "expected to get non-empty pid")
p2Ctx, _, finished := pm.AddContext(p1Ctx, "bar")
defer finished()
assert.NotEmpty(t, GetContext(p2Ctx).GetPID(), "expected to get non-empty pid")
assert.NotEqual(t, GetContext(p1Ctx).GetPID(), GetContext(p2Ctx).GetPID(), "expected to get different pids %s == %s", GetContext(p2Ctx).GetPID(), GetContext(p1Ctx).GetPID())
assert.Equal(t, GetContext(p1Ctx).GetPID(), GetContext(p2Ctx).GetParent().GetPID(), "expected to get pid %s got %s", GetContext(p1Ctx).GetPID(), GetContext(p2Ctx).GetParent().GetPID())
}
func TestManager_Cancel(t *testing.T) {
pm := Manager{processes: make(map[int64]*Process)}
pm := Manager{processes: make(map[IDType]*Process), next: 1}
ctx, cancel := context.WithCancel(context.Background())
pid := pm.Add("foo", cancel)
ctx, _, finished := pm.AddContext(context.Background(), "foo")
defer finished()
pm.Cancel(pid)
pm.Cancel(GetPID(ctx))
select {
case <-ctx.Done():
default:
assert.Fail(t, "Cancel should cancel the provided context")
}
finished()
ctx, cancel, finished := pm.AddContext(context.Background(), "foo")
defer finished()
cancel()
select {
case <-ctx.Done():
default:
assert.Fail(t, "Cancel should cancel the provided context")
}
finished()
}
func TestManager_Remove(t *testing.T) {
pm := Manager{processes: make(map[int64]*Process)}
pm := Manager{processes: make(map[IDType]*Process), next: 1}
pid1 := pm.Add("foo", nil)
assert.Equal(t, int64(1), pid1, "expected to get pid 1 got %d", pid1)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
pid2 := pm.Add("bar", nil)
assert.Equal(t, int64(2), pid2, "expected to get pid 2 got %d", pid2)
p1Ctx, _, finished := pm.AddContext(ctx, "foo")
defer finished()
assert.NotEmpty(t, GetContext(p1Ctx).GetPID(), "expected to have non-empty PID")
pm.Remove(pid2)
p2Ctx, _, finished := pm.AddContext(p1Ctx, "bar")
defer finished()
_, exists := pm.processes[pid2]
assert.False(t, exists, "PID %d is in the list but shouldn't", pid2)
assert.NotEqual(t, GetContext(p1Ctx).GetPID(), GetContext(p2Ctx).GetPID(), "expected to get different pids got %s == %s", GetContext(p2Ctx).GetPID(), GetContext(p1Ctx).GetPID())
pm.Remove(GetPID(p2Ctx))
_, exists := pm.processes[GetPID(p2Ctx)]
assert.False(t, exists, "PID %d is in the list but shouldn't", GetPID(p2Ctx))
}
func TestExecTimeoutNever(t *testing.T) {

View File

@ -0,0 +1,66 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package process
import (
"context"
"sync"
"time"
)
// Process represents a working process inheriting from Gitea.
type Process struct {
PID IDType // Process ID, not system one.
ParentPID IDType
Description string
Start time.Time
Cancel context.CancelFunc
lock sync.Mutex
children []*Process
}
// Children gets the children of the process
// Note: this function will behave nicely even if p is nil
func (p *Process) Children() (children []*Process) {
if p == nil {
return
}
p.lock.Lock()
defer p.lock.Unlock()
children = make([]*Process, len(p.children))
copy(children, p.children)
return children
}
// AddChild adds a child process
// Note: this function will behave nicely even if p is nil
func (p *Process) AddChild(child *Process) {
if p == nil {
return
}
p.lock.Lock()
defer p.lock.Unlock()
p.children = append(p.children, child)
}
// RemoveChild removes a child process
// Note: this function will behave nicely even if p is nil
func (p *Process) RemoveChild(process *Process) {
if p == nil {
return
}
p.lock.Lock()
defer p.lock.Unlock()
for i, child := range p.children {
if child == process {
p.children = append(p.children[:i], p.children[i+1:]...)
return
}
}
}

View File

@ -0,0 +1,40 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package setting
import (
"fmt"
"os"
)
// PrepareAppDataPath creates app data directory if necessary
func PrepareAppDataPath() error {
// FIXME: There are too many calls to MkdirAll in old code. It is incorrect.
// For example, if someDir=/mnt/vol1/gitea-home/data, if the mount point /mnt/vol1 is not mounted when Gitea runs,
// then gitea will make new empty directories in /mnt/vol1, all are stored in the root filesystem.
// The correct behavior should be: creating parent directories is end users' duty. We only create sub-directories in existing parent directories.
// For quickstart, the parent directories should be created automatically for first startup (eg: a flag or a check of INSTALL_LOCK).
// Now we can take the first step to do correctly (using Mkdir) in other packages, and prepare the AppDataPath here, then make a refactor in future.
st, err := os.Stat(AppDataPath)
if os.IsNotExist(err) {
err = os.MkdirAll(AppDataPath, os.ModePerm)
if err != nil {
return fmt.Errorf("unable to create the APP_DATA_PATH directory: %q, Error: %v", AppDataPath, err)
}
return nil
}
if err != nil {
return fmt.Errorf("unable to use APP_DATA_PATH %q. Error: %v", AppDataPath, err)
}
if !st.IsDir() /* also works for symlink */ {
return fmt.Errorf("the APP_DATA_PATH %q is not a directory (or symlink to a directory) and can't be used", AppDataPath)
}
return nil
}

51
modules/setting/i18n.go Normal file
View File

@ -0,0 +1,51 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package setting
// defaultI18nLangNames must be a slice, we need the order
var defaultI18nLangNames = []string{
"en-US", "English",
"zh-CN", "简体中文",
"zh-HK", "繁體中文(香港)",
"zh-TW", "繁體中文(台灣)",
"de-DE", "Deutsch",
"fr-FR", "français",
"nl-NL", "Nederlands",
"lv-LV", "latviešu",
"ru-RU", "русский",
"uk-UA", "Українська",
"ja-JP", "日本語",
"es-ES", "español",
"pt-BR", "português do Brasil",
"pt-PT", "Português de Portugal",
"pl-PL", "polski",
"bg-BG", "български",
"it-IT", "italiano",
"fi-FI", "suomi",
"tr-TR", "Türkçe",
"cs-CZ", "čeština",
"sr-SP", "српски",
"sv-SE", "svenska",
"ko-KR", "한국어",
"el-GR", "ελληνικά",
"fa-IR", "فارسی",
"hu-HU", "magyar nyelv",
"id-ID", "bahasa Indonesia",
"ml-IN", "മലയാളം",
}
func defaultI18nLangs() (res []string) {
for i := 0; i < len(defaultI18nLangNames); i += 2 {
res = append(res, defaultI18nLangNames[i])
}
return
}
func defaultI18nNames() (res []string) {
for i := 0; i < len(defaultI18nLangNames); i += 2 {
res = append(res, defaultI18nLangNames[i+1])
}
return
}

View File

@ -546,9 +546,27 @@ func SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath string)
}
}
// NewContext initializes configuration context.
// LoadFromExisting initializes setting options from an existing config file (app.ini)
func LoadFromExisting() {
loadFromConf(false)
}
// LoadAllowEmpty initializes setting options, it's also fine that if the config file (app.ini) doesn't exist
func LoadAllowEmpty() {
loadFromConf(true)
}
// LoadForTest initializes setting options for tests
func LoadForTest() {
loadFromConf(true)
if err := PrepareAppDataPath(); err != nil {
log.Fatal("Can not prepare APP_DATA_PATH: %v", err)
}
}
// loadFromConf initializes configuration context.
// NOTE: do not print any log except error.
func NewContext() {
func loadFromConf(allowEmpty bool) {
Cfg = ini.Empty()
if WritePIDFile && len(PIDFile) > 0 {
@ -563,9 +581,10 @@ func NewContext() {
if err := Cfg.Append(CustomConf); err != nil {
log.Fatal("Failed to load custom conf '%s': %v", CustomConf, err)
}
} else {
log.Warn("Custom config '%s' not found, ignore this if you're running first time", CustomConf)
}
} else if !allowEmpty {
log.Fatal("Unable to find configuration file: %q.\nEnsure you are running in the correct environment or set the correct configuration file with -c.", CustomConf)
} // else: no config file, a config file might be created at CustomConf later (might not)
Cfg.NameMapper = ini.SnackCase
homeDir, err := com.HomeDir()
@ -698,18 +717,7 @@ func NewContext() {
StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(StaticRootPath)
StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour)
AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data"))
if _, err = os.Stat(AppDataPath); err != nil {
// FIXME: There are too many calls to MkdirAll in old code. It is incorrect.
// For example, if someDir=/mnt/vol1/gitea-home/data, if the mount point /mnt/vol1 is not mounted when Gitea runs,
// then gitea will make new empty directories in /mnt/vol1, all are stored in the root filesystem.
// The correct behavior should be: creating parent directories is end users' duty. We only create sub-directories in existing parent directories.
// For quickstart, the parent directories should be created automatically for first startup (eg: a flag or a check of INSTALL_LOCK).
// Now we can take the first step to do correctly (using Mkdir) in other packages, and prepare the AppDataPath here, then make a refactor in future.
err = os.MkdirAll(AppDataPath, os.ModePerm)
if err != nil {
log.Fatal("Failed to create the directory for app data path '%s'", AppDataPath)
}
}
EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false)
PprofDataPath = sec.Key("PPROF_DATA_PATH").MustString(path.Join(AppWorkPath, "data/tmp/pprof"))
@ -864,6 +872,10 @@ func NewContext() {
SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20)
InternalToken = loadInternalToken(sec)
if InstallLock && InternalToken == "" {
// if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate
generateSaveInternalToken()
}
cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")
if len(cfgdata) == 0 {
@ -975,19 +987,11 @@ func NewContext() {
Langs = Cfg.Section("i18n").Key("LANGS").Strings(",")
if len(Langs) == 0 {
Langs = []string{
"en-US", "zh-CN", "zh-HK", "zh-TW", "de-DE", "fr-FR", "nl-NL", "lv-LV",
"ru-RU", "uk-UA", "ja-JP", "es-ES", "pt-BR", "pt-PT", "pl-PL", "bg-BG",
"it-IT", "fi-FI", "tr-TR", "cs-CZ", "sr-SP", "sv-SE", "ko-KR", "el-GR",
"fa-IR", "hu-HU", "id-ID", "ml-IN"}
Langs = defaultI18nLangs()
}
Names = Cfg.Section("i18n").Key("NAMES").Strings(",")
if len(Names) == 0 {
Names = []string{"English", "简体中文", "繁體中文(香港)", "繁體中文(台灣)", "Deutsch",
"français", "Nederlands", "latviešu", "русский", "Українська", "日本語",
"español", "português do Brasil", "Português de Portugal", "polski", "български",
"italiano", "suomi", "Türkçe", "čeština", "српски", "svenska", "한국어", "ελληνικά",
"فارسی", "magyar nyelv", "bahasa Indonesia", "മലയാളം"}
Names = defaultI18nNames()
}
ShowFooterBranding = Cfg.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool(false)
@ -1054,8 +1058,8 @@ func parseAuthorizedPrincipalsAllow(values []string) ([]string, bool) {
func loadInternalToken(sec *ini.Section) string {
uri := sec.Key("INTERNAL_TOKEN_URI").String()
if len(uri) == 0 {
return loadOrGenerateInternalToken(sec)
if uri == "" {
return sec.Key("INTERNAL_TOKEN").String()
}
tempURI, err := url.Parse(uri)
if err != nil {
@ -1092,21 +1096,17 @@ func loadInternalToken(sec *ini.Section) string {
return ""
}
func loadOrGenerateInternalToken(sec *ini.Section) string {
var err error
token := sec.Key("INTERNAL_TOKEN").String()
if len(token) == 0 {
token, err = generate.NewInternalToken()
if err != nil {
log.Fatal("Error generate internal token: %v", err)
}
// Save secret
CreateOrAppendToCustomConf(func(cfg *ini.File) {
cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
})
// generateSaveInternalToken generates and saves the internal token to app.ini
func generateSaveInternalToken() {
token, err := generate.NewInternalToken()
if err != nil {
log.Fatal("Error generate internal token: %v", err)
}
return token
InternalToken = token
CreateOrAppendToCustomConf(func(cfg *ini.File) {
cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
})
}
// MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash
@ -1186,6 +1186,8 @@ func CreateOrAppendToCustomConf(callback func(cfg *ini.File)) {
callback(cfg)
log.Info("Settings saved to: %q", CustomConf)
if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
log.Fatal("failed to create '%s': %v", CustomConf, err)
return

View File

@ -957,7 +957,7 @@ type remoteAddress struct {
func mirrorRemoteAddress(m models.RemoteMirrorer) remoteAddress {
a := remoteAddress{}
u, err := git.GetRemoteAddress(m.GetRepository().RepoPath(), m.GetRemoteName())
u, err := git.GetRemoteAddress(git.DefaultContext, m.GetRepository().RepoPath(), m.GetRemoteName())
if err != nil {
log.Error("GetRemoteAddress %v", err)
return a

View File

@ -138,6 +138,11 @@ ssl_mode = SSL
charset = Charset
path = Path
sqlite_helper = File path for the SQLite3 database.<br>Enter an absolute path if you run Gitea as a service.
reinstall_error = You are trying to install into an existing Gitea database
reinstall_confirm_message = Re-installing with an existing Gitea database can cause multiple problems. In most cases, you should use your existing "app.ini" to run Gitea. If you know what you are doing, confirm the following:
reinstall_confirm_check_1 = The data encrypted by the SECRET_KEY in app.ini may be lost: users may not be able to log in with 2FA/OTP & mirrors may not function correctly. By checking this box you confirm that the current app.ini file contains the correct the SECRET_KEY.
reinstall_confirm_check_2 = The repositories and settings may need to be re-synchronized. By checking this box you confirm that you will resynchronize the hooks for the repositories and authorized_keys file manually. You confirm that you will ensure that repository and mirror settings are correct.
reinstall_confirm_check_3 = You confirm that you are absolutely sure that this Gitea is running with the correct app.ini location and that you are sure that you have have to re-install. You confirm that you acknowledge the above risks.
err_empty_db_path = The SQLite3 database path cannot be empty.
no_admin_and_disable_registration = You cannot disable user self-registration without creating an administrator account.
err_empty_admin_password = The administrator password cannot be empty.
@ -203,8 +208,12 @@ install_btn_confirm = Install Gitea
test_git_failed = Could not test 'git' command: %v
sqlite3_not_available = This Gitea version does not support SQLite3. Please download the official binary version from %s (not the 'gobuild' version).
invalid_db_setting = The database settings are invalid: %v
invalid_db_table = The database table '%s' is invalid: %v
invalid_repo_path = The repository root path is invalid: %v
invalid_app_data_path = The app data path is invalid: %v
run_user_not_match = The 'run as' username is not the current username: %s -> %s
internal_token_failed = Failed to generate internal token: %v
secret_key_failed = Failed to generate secret key: %v
save_config_failed = Failed to save configuration: %v
invalid_admin_setting = Administrator account setting is invalid: %v
install_success = Welcome! Thank you for choosing Gitea. Have fun and take care!
@ -2696,6 +2705,7 @@ monitor.execute_time = Execution Time
monitor.process.cancel = Cancel process
monitor.process.cancel_desc = Cancelling a process may cause data loss
monitor.process.cancel_notices = Cancel: <strong>%s</strong>?
monitor.process.children = Children
monitor.queues = Queues
monitor.queue = Queue: %s
monitor.queue.name = Name

View File

@ -85,6 +85,11 @@ remove=Remover
remove_all=Excluir todos
edit=Editar
copy=Copiar
copy_url=Copiar URL
copy_branch=Copiar nome do branch
copy_success=Copiado!
copy_error=A cópia falhou
write=Escrever
preview=Pré-visualização
@ -332,6 +337,7 @@ register_success=Cadastro bem-sucedido
release.new.subject=%s em %s lançado
release.new.text=<b>@%[1]s</b> lançou a versão %[2]s em %[3]s
repo.transfer.subject_to=%s gostaria de transferir "%s" para %s
repo.transfer.subject_to_you=%s gostaria de transferir "%s" para você
@ -497,6 +503,7 @@ delete_current_avatar=Excluir o avatar atual
uploaded_avatar_not_a_image=O arquivo enviado não é uma imagem.
uploaded_avatar_is_too_big=O arquivo enviado excedeu o tamanho máximo.
update_avatar_success=Seu avatar foi atualizado.
update_user_avatar_success=O avatar do usuário foi atualizado.
change_password=Atualizar senha
old_password=Senha atual
@ -732,6 +739,7 @@ repo_desc=Descrição
repo_desc_helper=Digite uma breve descrição (opcional)
repo_lang=Linguagem
repo_gitignore_helper=Selecione modelos do .gitignore.
repo_gitignore_helper_desc=Escolha os arquivos que não serão rastreados da lista de modelos para linguagens comuns. Artefatos típicos gerados pelos compiladores de cada linguagem estão incluídos no .gitignore por padrão.
issue_labels=Etiquetas de issue
issue_labels_helper=Selecione um conjunto de etiquetas de issue.
license=Licença
@ -743,6 +751,7 @@ readme_helper_desc=Aqui você pode escrever uma descrição completa para o seu
auto_init=Inicializar o repositório (adicionando .gitignore, licença e LEIA-ME)
trust_model_helper=Selecione o modelo de confiança para verificação de assinatura. As opções possíveis são:
trust_model_helper_collaborator=Colaborador: Confiar em assinaturas de colaboradores
trust_model_helper_committer=Committer: Confiar em assinaturas que correspondem aos committers
trust_model_helper_default=Padrão: Usar o modelo de confiança padrão para esta instalação
create_repo=Criar repositório
default_branch=Branch padrão
@ -840,6 +849,7 @@ migrate.clone_address_desc=URL HTTP (S) ou Git 'clone' de um repositório existe
migrate.github_token_desc=Você pode colocar aqui um ou mais tokens separados com vírgula para tornar a migração mais rápida devido ao limite de taxa de API do Github. AVISO: abusar desse recurso pode violar a política do provedor de serviço e levar ao bloqueio da conta.
migrate.clone_local_path=ou um caminho de servidor local
migrate.permission_denied=Você não pode importar repositórios locais.
migrate.permission_denied_blocked=Você não pode importar dos hosts não permitidos, por favor peça ao administrador para verificar as configurações ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS.
migrate.invalid_local_path=O caminho local é inválido. Ele não existe ou não é um diretório.
migrate.invalid_lfs_endpoint=O destino LFS não é válido.
migrate.failed=Migração falhou: %v
@ -886,6 +896,7 @@ clone_this_repo=Clonar este repositório
create_new_repo_command=Criando um novo repositório por linha de comando
push_exist_repo=Realizando push para um repositório existente por linha de comando
empty_message=Este repositório está vazio.
broken_message=Os dados do git subjacente a este repositório não podem ser lidos. Entre em contato com o administrador desta instância ou exclua este repositório.
code=Código
code.desc=Acesso a código-fonte, arquivos, commits e branches.
@ -908,6 +919,7 @@ commit=Commit
release=Versão
releases=Versões
released_this=lançou isto
file.title=%s em %s
file_raw=Original
file_history=Histórico
file_view_source=Exibir código-fonte
@ -1101,6 +1113,9 @@ issues.add_assignee_at=`foi atribuído por <b>%s</b> %s`
issues.remove_assignee_at=`teve sua atribuição removida por <b>%s</b> %s`
issues.remove_self_assignment=`removeu sua atribuição %s`
issues.change_title_at=`alterou o título de <b><strike>%s</strike></b> para <b>%s</b> %s`
issues.change_ref_at=`mudou a referência de <b><strike>%s</strike></b> para <b>%s</b> %s`
issues.remove_ref_at=`removeu a referência <b>%s</b> %s`
issues.add_ref_at=`adicionou a referência <b>%s</b> %s`
issues.delete_branch_at=`excluiu branch <b>%s</b> %s`
issues.open_tab=%d aberto
issues.close_tab=%d fechado
@ -1297,6 +1312,7 @@ pulls.switch_head_and_base=Trocar cabeça e base
pulls.filter_branch=Filtrar branch
pulls.no_results=Nada encontrado.
pulls.nothing_to_compare=Estes branches são iguais. Não há nenhuma necessidade para criar um pull request.
pulls.has_pull_request=`Um pull request entre esses branches já existe: <a href="%[1]s">%[2]s#%[3]d</a>`
pulls.create=Criar pull request
pulls.title_desc=quer aplicar o merge de %[1]d commits de <code>%[2]s</code> em <code id="branch_target">%[3]s</code>
pulls.merged_title_desc=aplicou merge dos %[1]d commits de <code>%[2]s</code> em <code>%[3]s</code> %[4]s
@ -1813,6 +1829,7 @@ diff.file_byte_size=Tamanho
diff.file_suppressed=Diferenças do arquivo suprimidas por serem muito extensas
diff.too_many_files=Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff
diff.show_more=Mostrar mais
diff.load=Carregar Diff
diff.generated=gerado
diff.vendored=externo
diff.comment.placeholder=Deixe um comentário
@ -2373,6 +2390,7 @@ monitor.execute_time=Tempo de execução
monitor.process.cancel=Cancelar processo
monitor.process.cancel_desc=Cancelar um processo pode causar perda de dados
monitor.process.cancel_notices=Cancelar: <strong>%s</strong>?
monitor.process.children=Descendentes
monitor.queues=Filas
monitor.queue=Fila: %s
monitor.queue.name=Nome
@ -2444,13 +2462,31 @@ notices.delete_success=Os avisos do sistema foram excluídos.
[action]
create_repo=criou o repositório <a href="%s">%s</a>
rename_repo=renomeou o repositório <code>%[1]s</code> para <a href="%[2]s">%[3]s</a>
commit_repo=push feito para <a href="%[2]s">%[3]s</a> em <a href="%[1]s">%[4]s</a>
create_issue=`abriu a issue <a href="%[1]s">%[3]s#%[2]s</a>`
close_issue=`fechou a issue <a href="%[1]s">%[3]s#%[2]s</a>`
reopen_issue=`reabriu a issue <a href="%[1]s">%[3]s#%[2]s</a>`
create_pull_request=`criou o pull request <a href="%[1]s">%[3]s#%[2]s</a>`
close_pull_request=`fechou o pull request <a href="%[1]s">%[3]s#%[2]s</a>`
reopen_pull_request=`reabriu o pull request <a href="%[1]s">%[3]s#%[2]s</a>`
comment_issue=`comentou na issue <a href="%[1]s">%[3]s#%[2]s</a>`
comment_pull=`comentou no pull request <a href="%[1]s">%[3]s#%[2]s</a>`
merge_pull_request=`fez merge do pull request <a href="%[1]s">%[3]s#%[2]s</a>`
transfer_repo=transferiu repositório de <code>%s</code> para <a href="%s">%s</a>
push_tag=fez push da tag <a href="%[2]s">%[3]s</a> to <a href="%[1]s">%[4]s</a>
delete_tag=excluiu tag %[2]s de <a href="%[1]s"> %[3]s</a>
delete_branch=excluiu branch %[2]s de <a href="%[1]s">%[3]s</a>
compare_branch=Comparar
compare_commits=Compare %d commits
compare_commits_general=Comparar commits
mirror_sync_push=sincronizou os commits para <a href="%[2]s">%[3]s</a> em <a href="%[1]s">%[4]s</a> do espelho
mirror_sync_create=sincronizou a nova referência <a href="%[2]s">%[3]s</a> para <a href="%[1]s">%[4]s</a> do espelho
mirror_sync_delete=referência excluída e sincronizada <code>%[2]s</code> em <a href="%[1]s">%[3]s</a> do espelhamento
approve_pull_request=`aprovou <a href="%[1]s">%[3]s#%[2]s</a>`
reject_pull_request=`sugeriu modificações para <a href="%[1]s">%[3]s#%[2]s</a>`
publish_release=`lançou a versão <a href="%[2]s"> "%[4]s" </a> em <a href="%[1]s">%[3]s</a>`
review_dismissed=`descartou a revisão de <b>%[4]s</b> para <a href="%[1]s">%[3]s#%[2]s</a>`
create_branch=criou o branch <a href="%[2]s">%[3]s</a> em <a href="%[1]s">%[4]s</a>
watched_repo=começou a observar <a href="%[1]s">%[2]s</a>
[tool]

View File

@ -1598,7 +1598,6 @@ activity.closed_issues_count_1=questão encerrada
activity.closed_issues_count_n=questões encerradas
activity.title.issues_1=%d questão
activity.title.issues_n=%d questões
activity.title.issues_closed_from=%s resolvidas de %s
activity.title.issues_created_by=%s criada por %s
activity.closed_issue_label=Encerrada
activity.new_issues_count_1=questão nova
@ -2037,6 +2036,7 @@ diff.file_suppressed_line_too_long=A apresentação das diferenças entre fichei
diff.too_many_files=Alguns ficheiros não foram mostrados porque foram modificados demasiados ficheiros neste diff
diff.show_more=Mostrar mais
diff.load=Carregar diff
diff.generated=gerado
diff.comment.placeholder=Deixar um comentário
diff.comment.markdown_info=A formatação com markdown é suportada.
diff.comment.add_single_comment=Adicionar um único comentário
@ -2389,6 +2389,8 @@ users.list_status_filter.is_restricted=Restrito
users.list_status_filter.not_restricted=Não restrito
users.list_status_filter.is_prohibit_login=Proibir início de sessão
users.list_status_filter.not_prohibit_login=Permitir início de sessão
users.list_status_filter.is_2fa_enabled=Autenticação em dois passos habilitada
users.list_status_filter.not_2fa_enabled=Autenticação em dois passos desabilitada
emails.email_manage_panel=Gestão de endereços de email do utilizador
emails.primary=Principal
@ -2454,6 +2456,7 @@ auths.attribute_name=Atributo do Primeiro Nome
auths.attribute_surname=Atributo do Sobrenome
auths.attribute_mail=Atributo do email
auths.attribute_ssh_public_key=Atributo da chave pública SSH
auths.attribute_avatar=Atributo do avatar
auths.attributes_in_bind=Buscar os atributos no contexto de Bind DN
auths.allow_deactivate_all=Permitir que um resultado de pesquisa vazio desabilite todos os utilizadores
auths.use_paged_search=Usar pesquisa paginada
@ -2683,6 +2686,7 @@ monitor.execute_time=Tempo de execução
monitor.process.cancel=Cancelar processo
monitor.process.cancel_desc=Cancelar um processo pode resultar na perda de dados
monitor.process.cancel_notices=Cancelar: <strong>%s</strong>?
monitor.process.children=Descendentes
monitor.queues=Filas
monitor.queue=Fila: %s
monitor.queue.name=Nome
@ -2780,6 +2784,7 @@ publish_release=`lançou <a href="%[2]s"> "%[4]s" </a> em <a href="%[1]s">%[3]s<
review_dismissed=`descartou a revisão de <b>%[4]s</b> para <a href="%[1]s">%[3]s#%[2]s</a>`
review_dismissed_reason=Motivo:
create_branch=criou o ramo <a href="%[2]s">%[3]s</a> em <a href="%[1]s">%[4]s</a>
starred_repo=juntou <a href="%[1]s">%[2]s</a> aos favoritos
watched_repo=começou a vigiar <a href="%[1]s">%[2]s</a>
[tool]

View File

@ -0,0 +1,854 @@
home=මුල් පිටුව
dashboard=උපකරණ පුවරුව
explore=ගවේෂණය
help=උපකාර
sign_up=ලියාපදිංචිය
register=ලියාපදිංචිය
website=වියමන අඩවිය
version=අනුවාදය
page=පිටුව
language=භාෂාව
notifications=දැනුම්දීම්
create_new=සාදන්න…
user_profile_and_more=පැතිකඩ සහ සැකසුම්…
enable_javascript=මෙම වියමන අඩවිය ජාවාස්ක්‍රිප්ට් සමඟ හොදින් ක්‍රියා කරයි.
toc=පටුන
licenses=බලපත්‍ර
return_to_gitea=ගිටියා වෙත ආපසු
username=පරිශීලක නාමය
email=වි-තැපැල් ලිපිනය
password=මුරපදය
passcode=මුරකේතය
u2f_insert_key=ආරක්‍ෂණ යතුර ඇතුල් කරන්න
u2f_unsupported_browser=අතිරික්සුව යූ2එෆ් ආරක්‍ෂණ යතුරු සඳහා සහාය නොදක්වයි.
u2f_reload=නැවත පූරණය
repository=කෝෂ්ඨය
organization=සංවිධානය
new_repo=නව කෝෂ්ඨය
new_org=නව සංවිධානය
new_project=නව ව්‍යාපෘතිය
manage_org=සංවිධාන කළමනාකරණය
admin_panel=අඩවිය පරිපාලනය
account_settings=ගිණුමේ සැකසුම්
settings=සැකසුම්
your_profile=පැතිකඩ
your_settings=සැකසුම්
all=සියල්ල
sources=මූලාශ්‍ර
activities=ක්‍රියාකාරකම්
ok=හරි
cancel=අවලංගු කරන්න
save=සුරකින්න
add=එකතු
remove=ඉවත් කරන්න
remove_all=සියල්ල ඉවත් කරන්න
edit=සංස්කරණය
copy=පිටපත් කරන්න
copy_branch=ශාඛාවේ නම පිටපත් කරන්න
copy_success=පිටපත් විය!
copy_error=පිටපත් වීමට අසමත් විය
write=ලියන්න
preview=පෙරදසුන
loading=පූරණය වෙමින්...
step1=පියවර 1:
step2=පියවර 2:
error=දෝෂයකි
[error]
[startpage]
install=ස්ථාපනයට පහසුය
license=විවෘත මූලාශ්‍ර
[install]
db_title=දත්ත සමුදායේ සැකසුම්
db_type=දත්ත සමුදායේ වර්ගය
user=පරිශීලක නාමය
password=මුරපදය
db_name=දත්ත සමුදායේ නම
err_admin_name_is_invalid=පරිපාලක පරිශීලක නාමය වලංගු නොවේ
app_name=අඩවියේ සිරැසිය
domain=සේවාදායකයේ වසම
email_title=වි-තැපෑලේ සැකසුම්
smtp_from=ලෙස වි-තැපෑල යවන්න
mail_notify=වි-තැපැල් දැනුම්දීම් සබල කරන්න
server_service_title=සේවාදායකය සහ තෙවන පාර්ශවීය සේවා සැකසුම්
disable_gravatar=ග්‍රැවටාර් අබල කරන්න
admin_title=පරිපාලක ගිණුමේ සැකසුම්
admin_name=පරිපාලක පරිශීලක නාමය
admin_password=මුරපදය
admin_email=වි-තැපැල් ලිපිනය
install_btn_confirm=ගිටියා ස්ථාපනය කරන්න
install_success=සාදරයෙන් පිළිගනිමු! ගිටියා තෝරා ගැනීම ගැන ස්තූතියි. විනෝද වන්න!
no_reply_address=සැඟවුණු වි-තැපැල් වසම
[home]
uname_holder=පරිශීලක නාමය හෝ වි-තැපෑල
password_holder=මුරපදය
my_repos=කෝෂ්ඨ
show_more_repos=තව කෝෂ්ඨ පෙන්වන්න…
my_orgs=මාගේ සංවිධාන
view_home=%s දකින්න
filter=වෙනත් පෙරහන්
filter_by_team_repositories=කණ්ඩායම් කෝෂ්ඨ අනුව පෙරන්න
show_archived=සංරක්ෂිත
show_only_archived=සංරක්ෂිත පමණක් පෙන්වයි
show_private=පෞද්ගලික
show_both_private_public=ප්‍රසිද්ධ හා පෞද්ගලික පෙන්වයි
show_only_private=පෞද්ගලික පමණක් පෙන්වයි
show_only_public=ප්‍රසිද්ධ පමණක් පෙන්වයි
issues.in_your_repos=ඔබගේ කෝෂ්ඨවල
[explore]
repos=කෝෂ්ඨ
users=පරිශීලකයින්
organizations=සංවිධාන
search=සොයන්න
code=කේතය
code_search_results='%s' සඳහා සෙවුම් ප්‍රතිඵල
[auth]
create_new_account=ගිණුමක් ලියාපදිංචි කරන්න
remember_me=උපාංගය මතක තබාගන්න
forgot_password_title=මුරපදය අමතක වුණා
forgot_password=මුරපදය අමතක වුණා ද?
sign_up_now=ගිණුමක් ඇවැසිද? දැන් ලියාපදිංචි වන්න.
sign_up_successful=ගිණුම සාර්ථකව සෑදිණි.
must_change_password=මුරපදය යාවත්කාල කරන්න
active_your_account=ඔබගේ ගිණුම ක්‍රියාත්මක කරන්න
account_activated=ඔබගේ ගිණුම ක්‍රියාත්මක කර ඇත
reset_password=ගිණුම ප්‍රතිසාධනය
reset_password_helper=ගිණුම ප්‍රතිසාධනය
oauth_signup_tab=නව ගිණුමක් ලියාපදිංචි කරන්න
oauth_signup_title=නව ගිණුම සම්පූර්ණ කරන්න
oauth_signup_submit=ගිණුම සම්පූර්ණ කරන්න
openid_connect_submit=සම්බන්ධ වන්න
openid_register_title=නව ගිණුමක් සාදන්න
[mail]
hi_user_x=ආයුබෝ <b>%s</b>,
activate_account=ඔබගේ ගිණුම ක්‍රියාත්මක කරන්න
activate_account.title=%s, ඔබගේ ගිණුම ක්‍රියාත්මක කරන්න
activate_account.text_1=ආයුබෝ <b>%[1]s</b>, %[2]s හි ලියාපදිංචි වීමට තුති!
register_notify=ගිටියා වෙත සාදරයෙන් පිළිගනිමු
register_notify.title=%[1]s, %[2]s වෙත සාදරයෙන් පිළිගනිමු
reset_password=ඔබගේ ගිණුම ප්‍රතිසාධනය
register_success=ලියාපදිංචි වීම සාර්ථකයි
release.title=සිරැසිය: %s
release.note=සටහන:
release.downloads=බාගැනීම්:
release.download.zip=ප්‍රභව කේතය (ZIP)
release.download.targz=ප්‍රභව කේතය (TAR.GZ)
repo.transfer.to_you=ඔබ
repo.collaborator.added.subject=%s ඔබව %s ට එකතු කළා
[modal]
yes=ඔව්
no=නැහැ
modify=යාවත්කාල
[form]
UserName=පරිශීලක නාමය
RepoName=කෝෂ්ඨයේ නම
Email=වි-තැපැල් ලිපිනය
Password=මුරපදය
TeamName=කණ්ඩායමේ නම
AdminEmail=පරිපාලකගේ වි-තැපෑල
NewBranchName=නව ශාඛාවේ නම
Content=අන්තර්ගතය
SSPIDefaultLanguage=පෙරනිමි භාෂාව
unknown_error=නොදන්නා දෝෂය:
password_not_match=මුරපද නොගැලපේ.
username_been_taken=පරිශීලක නාමය දැනටමත් ගෙන ඇත.
repo_name_been_taken=කෝෂ්ඨයේ නම භාවිතා කර ඇත.
org_name_been_taken=සංවිධානයේ නම දැනටමත් ගෙන ඇත.
team_name_been_taken=කණ්ඩායමේ නම දැනටමත් ගෙන ඇත.
email_been_used=වි-තැපෑල දැනටමත් භාවිතා කර ඇත.
email_invalid=වි-තැපැල් ලිපිනය වලංගු නොවේ.
username_password_incorrect=පරිශීලක නාමය හෝ මුරපදය සාවද්‍යයි.
enterred_invalid_owner_name=නව හිමිකරුගේ නම වලංගු නොවේ.
enterred_invalid_password=ඔබ ඇතුල් කළ මුරපදය සාවද්‍යයි.
user_not_exist=පරිශීලක නොපවතී.
team_not_exist=කණ්ඩායම නොපවතී.
[user]
repositories=කෝෂ්ඨ
activity=ප්‍රසිද්ධ ක්‍රියාකාරකම
projects=ව්‍යාපෘති
[settings]
profile=පැතිකඩ
account=ගිණුම
password=මුරපදය
security=ආරක්‍ෂාව
social=සමාජ ගිණුම්
applications=යෙදුම්
orgs=සංවිධාන කළමනාකරණය
repos=කෝෂ්ඨ
organization=සංවිධාන
u2f=ආරක්‍ෂණ යතුරු
public_profile=ප්‍රසිද්ධ පැතිකඩ
full_name=සම්පූර්ණ නම
website=වියමන අඩවිය
location=ස්ථානය
update_language_not_found='%s' භාෂාව නැත.
update_language_success=භාෂාව යාවත්කාල වී ඇත.
continue=ඉදිරියට
cancel=අවලංගු කරන්න
language=භාෂාව
ui=තේමාව
privacy=පෞද්ගලිකත්වය
change_password=මුරපදය යාවත්කාල කරන්න
old_password=වත්මන් මුරපදය
new_password=නව මුරපදය
emails=වි-තැපැල් ලිපින
manage_emails=වි-තැපැල් ලිපින කළමනාකරණය
delete_email=ඉවත් කරන්න
email_deletion=වි-තැපෑල ඉවත් කරන්න
theme_update_success=ඔබගේ තේමාව යාවත්කාල කෙරිණි.
add_email=වි-තැපෑල එකතු කරන්න
keep_email_private=වි-තැපෑල සඟවන්න
manage_gpg_keys=ජීපීජී යතුරු කළමනාකරණය
add_key=යතුර එක්කරන්න
key_id=යතුරෙහි හැඳු.
key_name=යතුරෙහි නම
key_content=අන්තර්ගතය
principal_content=අන්තර්ගතය
delete_key=ඉවත් කරන්න
gpg_key_deletion=ජීපීජී යතුර ඉවත් කරන්න
valid_until=තෙක් වලංගු වේ
valid_forever=සදහටම වලංගු වේ
no_activity=මෑත ක්‍රියාකාරකම් නැත
show_openid=පැතිකඩ මත පෙන්වන්න
hide_openid=පැතිකඩෙන් සඟවන්න
create_oauth2_application_button=යෙදුම සාදන්න
oauth2_application_name=යෙදුමේ නම
save_application=සුරකින්න
oauth2_client_id=අනුග්‍රාහකයේ හැඳු.
oauth2_client_secret=අනුග්‍රාහකයේ රහස
oauth2_regenerate_secret_hint=ඔබගේ රහස නැති වුනාද?
oauth2_application_edit=සංස්කරණය
u2f_nickname=අපනාමය
u2f_delete_key=ආරක්ෂක යතුර ඉවත් කරන්න
email_notifications.enable=වි-තැපැල් දැනුම්දීම් සබල කරන්න
email_notifications.onmention=සැඳහුම් සඳහා තැපැල් කරන්න
email_notifications.disable=වි-තැපැල් දැනුම්දීම් අබල කරන්න
visibility.public=ප්‍රසිද්ධ
visibility.private=පෞද්ගලික
[repo]
owner=හිමිකරු
repo_name=කෝෂ්ඨයේ නම
repo_size=කෝෂ්ඨයේ ප්‍රමාණය
download_zip=ZIP බාගන්න
download_tar=TAR.GZ බාගන්න
repo_desc=සවිස්තරය
repo_lang=භාෂාව
license=බලපත්‍රය
readme=මෙයකියවන්න
create_repo=කෝෂ්ඨය සාදන්න
default_branch=පෙරනිමි ශාඛාව
reactions_more=සහ තවත් %d
language_other=වෙනත්
adopt_preexisting_content=%s වෙතින් කෝෂ්ඨය සාදන්න
desc.private=පෞද්ගලික
desc.public=ප්‍රසිද්ධ
desc.internal=අභ්‍යන්තර
desc.archived=සංරක්ෂිත
template.topics=මාතෘකා
migrate_items_merge_requests=සංයුක්ත කිරීමේ ඉල්ලීම්
migrate_items_releases=නිකුතු
migrate.migrating_failed.error=දෝෂය: %s
download_archive=කෝෂ්ඨය බාගන්න
no_desc=සවිස්තරයක් නැත
code=කේතය
branch=ශාඛාව
branches=ශාඛා
project_board=ව්‍යාපෘති
org_labels_desc_manage=කළමනාකරණය
release=නිකුතුව
releases=නිකුතු
file_history=ඉතිහාසය
file_view_source=මූලාශ්‍රය දකින්න
commit_graph.color=වර්ණය
download_file=ගොනුව බාගන්න
editor.new_file=නව ගොනුව
editor.upload_file=ගොනුව උඩුගත කරන්න
editor.edit_file=ගොනුව සංස්කරණය
editor.preview_changes=වෙනස්කම් පෙරදසුන
editor.edit_this_file=ගොනුව සංස්කරණය
editor.this_file_locked=ගොනුවට අගුළු ලා ඇත
editor.name_your_file=ගොනුව නම් කරන්න…
editor.or=හෝ
editor.cancel_lower=අවලංගු කරන්න
editor.add_tmpl='<filename>' එකතු කරන්න
editor.add='%s' එකතු කරන්න
editor.update='%s' යාවත්කාල කරන්න
editor.new_branch_name_desc=නව ශාඛාවේ නම…
editor.cancel=අවලංගු කරන්න
editor.filename_is_invalid=ගොනුවේ නම වලංගු නොවේ: '%s'.
editor.branch_does_not_exist=මෙම කෝෂ්ඨයෙහි '%s' ශාඛාව නොපවතී.
editor.branch_already_exists=මෙම කෝෂ්ඨයෙහි '%s' ශාඛාව දැනටමත් පවතී.
editor.no_changes_to_show=පෙන්වීමට කිසිදු වෙනසක් නැත.
editor.fail_to_update_file='%s' ගොනුව යාවත්කාල/සෑදීමට අසමත් විය.
editor.fail_to_update_file_summary=දෝෂ පණිවිඩය:
editor.upload_files_to_dir='%s' වෙත ගොනු උඩුගත කරන්න
commits.find=සොයන්න
commits.search_all=සියළුම ශාඛා
commits.author=කතෘ
commits.message=පණිවිඩය
commits.date=දිනය
commits.gpg_key_id=ජීපීජී යතුරෙහි හැඳු.
projects=ව්‍යාපෘති
projects.description_placeholder=සවිස්තරය
projects.create=ව්‍යාපෘතිය සාදන්න
projects.title=සිරැසිය
projects.new=නව ව්‍යාපෘතිය
projects.edit=ව්‍යාපෘති සංස්කරණය
projects.modify=ව්‍යාපෘතිය යාවත්කාල කරන්න
projects.edit_success='%s' ව්‍යාපෘතිය යාවත්කාල කර ඇත.
projects.type.uncategorized=ප්‍රවර්ග ගත නැති
projects.board.edit=පුවරුව සංස්කරණය
projects.board.edit_title=නව පුවරුවේ නම
projects.board.new_title=නව පුවරුවේ නම
projects.board.new_submit=යොමන්න
projects.board.new=නව පුවරුව
projects.board.color=වර්ණය
projects.open=විවෘත
projects.close=වසන්න
issues.filter_projects=ව්‍යාපෘතිය පෙරන්න
issues.new.projects=ව්‍යාපෘති
issues.new.add_project_title=ව්‍යාපෘතිය සකසන්න
issues.new.no_projects=ව්‍යාපෘති නැත
issues.new.open_projects=විවෘත ව්‍යාපෘති
issues.new.closed_projects=සංවෘත ව්‍යාපෘති
issues.choose.blank=පෙරනිමි
issues.new_label_desc_placeholder=සවිස්තරය
issues.filter_type=වර්ගය
issues.filter_sort.recentupdate=මෑතදී යාවත්කාල
issues.action_open=විවෘත
issues.action_close=වසන්න
issues.next=ඊළඟ
issues.open_title=විවෘත
issues.closed_title=වසා ඇත
issues.num_comments=අදහස් %d
issues.context.copy_link=සබැඳිය පිටපත්
issues.context.edit=සංස්කරණය
issues.no_content=තවම අන්තර්ගතයක් නැත.
issues.close_issue=වසන්න
issues.close_comment_issue=අදහස් දක්වා වසන්න
issues.reopen_comment_issue=අදහස් දක්වා විවෘත කරන්න
issues.create_comment=අදහස
issues.owner=හිමිකරු
issues.edit=සංස්කරණය
issues.cancel=අවලංගු කරන්න
issues.save=සුරකින්න
issues.label_edit=සංස්කරණය
issues.label.filter_sort.by_size=කුඩාම ප්‍රමාණය
issues.label.filter_sort.reverse_by_size=විශාලම ප්‍රමාණය
issues.num_participants=සහභාගිවන්නන් %d
issues.attachment.download=`"%s“ බාගැනීමට ඔබන්න`
issues.lock_confirm=අගුළු ලන්න
issues.unlock_confirm=අගුළු හරින්න
issues.lock.reason=අගුළු දැමීමට හේතුව
issues.add_time_cancel=අවලංගු කරන්න
issues.add_time_hours=පැය
issues.add_time_minutes=විනාඩි
issues.due_date_form_edit=සංස්කරණය
issues.due_date_form_remove=ඉවත් කරන්න
issues.dependency.title=පරායත්ත
issues.dependency.add=පරායත්ත එක් කරන්න…
issues.dependency.cancel=අවලංගු කරන්න
issues.dependency.remove=ඉවත් කරන්න
issues.dependency.remove_info=මෙම පරායත්තය ඉවත් කරන්න
issues.dependency.added_dependency=`%s නව පරායත්තයක් එකතු කළා`
issues.dependency.removed_dependency=`%s පරායත්තයක් ඉවත් කළා`
issues.dependency.remove_header=පරායත්තය ඉවත් කරන්න
issues.dependency.add_error_dep_not_exist=පරායත්තය නොපවතී.
issues.dependency.add_error_dep_exists=පරායත්තය දැනටමත් පවතී.
issues.review.left_comment=අදහසක් හැරගියා
issues.review.reject=%s ඉල්ලූ වෙනස්කම්
issues.content_history.options=විකල්ප
compare.compare_head=සසඳන්න
pulls.filter_branch=ශාඛාව පෙරන්න
pulls.tab_files=වෙනස් වූ ගොනු
pulls.merged=සංයුක්ත කෙරිණි
pulls.manually_merged=අතින් සංයුක්ත කර ඇත
pulls.reject_count_1=වෙනස් කිරීමේ ඉල්ලීම් %d
pulls.reject_count_n=වෙනස් කිරීමේ ඉල්ලීම් %d
pulls.merge_manually=අතින් සංයුක්ත කර ඇත
pulls.merge_conflict_summary=දෝෂ පණිවිඩය
pulls.rebase_conflict_summary=දෝෂ පණිවිඩය
; </summary><code>%[2]s<br>%[3]s</code></details>
pulls.status_checks_requested=ඇවැසිය
pulls.status_checks_details=වැණුම
pulls.update_branch=සංයුක්ත කිරීමෙන් ශාඛාව යාවත්කාල කරන්න
milestones.closed=%s වසා ඇත
milestones.close=වසන්න
milestones.title=සිරැසිය
milestones.desc=සවිස්තරය
milestones.cancel=අවලංගු කරන්න
wiki.create_first_page=පළමු පිටුව සාදන්න
wiki.page=පිටුව
wiki.filter_page=පිටුව පෙරන්න
wiki.new_page=පිටුව
wiki.save_page=පිටුව සුරකින්න
wiki.edit_page_button=සංස්කරණය
wiki.new_page_button=නව පිටුව
wiki.pages=පිටු
activity=ක්‍රියාකාරකම
activity.period.daily=දවස් 1
activity.period.halfweekly=දවස් 3
activity.period.weekly=සති 1
activity.period.monthly=මාස 1
activity.period.quarterly=මාස 3
activity.period.semiyearly=මාස 6
activity.period.yearly=වසර 1
activity.overview=දළ විශ්ලේෂණය
activity.title.user_1=පරිශීලකයින් %d
activity.title.user_n=පරිශීලකයින් %d
activity.merged_prs_label=සංයුක්ත කෙරිණි
activity.closed_issue_label=වසා ඇත
activity.title.releases_1=නිකුතු %d
activity.title.releases_n=නිකුතු %d
activity.git_stats_file_1=ගොනු %d
activity.git_stats_file_n=ගොනු %d
activity.git_stats_and_deletions=සහ
search=සොයන්න
search.search_repo=කෝෂ්ඨය සොයන්න
search.results=<a href="%s">%s</a> හි "%s" සඳහා සෙවුම් ප්‍රතිඵල
settings=සැකසුම්
settings.options=කෝෂ්ඨය
settings.collaboration.admin=පරිපාලක
settings.collaboration.owner=හිමිකරු
settings.basic_settings=මූලික සැකසුම්
settings.sync_mirror=සමමුහූර්ත කරන්න
settings.email_notifications.enable=වි-තැපැල් දැනුම්දීම් සබල කරන්න
settings.email_notifications.onmention=සැඳහුම් සඳහා තැපැල් කරන්න
settings.email_notifications.disable=වි-තැපැල් දැනුම්දීම් අබල කරන්න
settings.site=වියමන අඩවිය
settings.update_settings=යාවත්කාල සැකසුම්
settings.branches.update_default_branch=පෙරනිමි ශාඛාව යාවත්කාල කරන්න
settings.advanced_settings=වැඩිදුර සැකසුම්
settings.admin_settings=පරිපාලක සැකසුම්
settings.danger_zone=අන්තරාය කලාපය
settings.transfer_owner=නව හිමිකරු
settings.delete_collaborator=ඉවත් කරන්න
settings.search_user_placeholder=පරිශීලක සොයන්න…
settings.teams=කණ්ඩායම්
settings.add_team_success=කණ්ඩායමට දැන් කෝෂ්ඨයට ප්‍රවේශය ඇත.
settings.search_team=කණ්ඩායම සොයන්න…
settings.change_team_permission_tip=කණ්ඩායමේ අවසරය කණ්ඩායම් සැකසුම් පිටුවේ සකසන අතර කෝෂ්ඨය අනුව වෙනස් කළ නොහැකිය
settings.delete_team_tip=මෙම කණ්ඩායම සියළුම කෝෂ්ඨවලට ප්‍රවේශය ඇති අතර ඉවත් කළ නොහැකිය
settings.remove_team_success=කෝෂ්ඨය වෙත කණ්ඩායමේ ප්‍රවේශය ඉවත් කර ඇත.
settings.webhook.request=ඉල්ලීම
settings.webhook.response=ප්‍රතිචාරය
settings.webhook.headers=ශීර්ෂ
settings.webhook.payload=අන්තර්ගතය
settings.http_method=HTTP ක්‍රමය
settings.secret=රහස
settings.slack_username=පරිශීලක නාමය
settings.slack_icon_url=නිරූපකයේ ඒ.ස.නි.
settings.slack_color=වර්ණය
settings.discord_username=පරිශීලක නාමය
settings.discord_icon_url=නිරූපකයේ ඒ.ස.නි.
settings.event_send_everything=සියළුම සිදුවීම්
settings.event_choose=අභිරුචි සිදුවීම්…
settings.event_header_repository=කෝෂ්ඨයේ සිදුවීම්
settings.event_create=සාදන්න
settings.event_release=නිකුතුව
settings.event_repository=කෝෂ්ඨය
settings.branch_filter=ශාඛා පෙරහන
settings.slack_domain=වසම
settings.slack_channel=නාලිකාව
settings.is_writable=ලිවීමේ ප්‍රවේශය සබල කරන්න
settings.title=සිරැසිය
settings.deploy_key_content=අන්තර්ගතය
settings.branches=ශාඛා
settings.protect_whitelist_search_users=පරිශීලකයින් සොයන්න…
settings.protect_whitelist_search_teams=කණ්ඩායම් සොයන්න…
settings.protect_check_status_contexts=තත්වය පරීක්‍ෂාව සබල කරන්න
settings.edit_protected_branch=සංස්කරණය
settings.tags.protection.allowed=ඉඩ දී ඇත
settings.tags.protection.allowed.users=ඉඩ දී ඇති පරිශීලකයින්
settings.tags.protection.allowed.teams=ඉඩ දී ඇති කණ්ඩායම්
settings.matrix.room_id=කාමරයේ හැඳු.
settings.matrix.message_type=පණිවිඩ වර්ගය
settings.archive.button=කෝෂ්ඨය සංරක්‍ෂණය
settings.archive.header=මෙම කෝෂ්ඨය සංරක්‍ෂණය
settings.archive.success=කෝෂ්ඨය සාර්ථකව සංරක්‍ෂණය කෙරිණි.
settings.lfs_locks=අගුළු
settings.lfs_lock=අගුල
settings.lfs_locks_no_locks=අගුළු නැත
settings.lfs_force_unlock=බලාත්මක අගුළු හැරීම
settings.rename_branch_from=පරණ ශාඛාවේ නම
settings.rename_branch_to=නව ශාඛාවේ නම
settings.rename_branch=ශාඛාව යළි නම් කරන්න
diff.browse_source=මූලාශ්‍රය පිරික්සන්න
diff.git-notes=සටහන්
diff.whitespace_show_everything=සියළුම වෙනස්කම් පෙන්වන්න
diff.bin_not_shown=ද්විමය ගොනුව පෙන්වා නැත.
diff.view_file=ගොනුව දකින්න
diff.file_image_width=පළල
diff.file_image_height=උස
diff.file_byte_size=ප්‍රමාණය
diff.show_more=තව පෙන්වන්න
diff.comment.placeholder=අදහසක් හැරයන්න
diff.comment.add_review_comment=අදහස එකතු කරන්න
diff.comment.reply=පිළිතුර
diff.review.comment=අදහස
diff.review.reject=වෙනස්කම් ඉල්ලන්න
release.releases=නිකුතු
release.detail=නිකුතුවේ විස්තර
release.new_release=නව නිකුතුව
release.draft=කටුපිටපත
release.prerelease=පූර්ව-නිකුතුව
release.stable=ස්ථාවර
release.compare=සසඳන්න
release.edit=සංස්කරණය
release.source_code=ප්‍රභව කේතය
release.title=සිරැසිය
release.content=අන්තර්ගතය
release.cancel=අවලංගු කරන්න
release.save_draft=කටුපිටපත සුරකින්න
release.edit_release=නිකුතුව යාවත්කාල කරන්න
release.downloads=බාගැනීම්
release.download_count=බාගැනීම්: %s
branch.name=ශාඛාවේ නම
branch.search=ශාඛා සොයන්න
branch.create_from='%s' වෙතින්
branch.restore_success='%s' ශාඛාව ප්‍රත්‍යර්පණය කෙරිණි.
branch.restore_failed='%s' ශාඛාව ප්‍රත්‍යර්පණයට අසමත් විය.
branch.restore=ශාඛාව '%s' ප්‍රත්‍යර්පණය
branch.download='%s' ශාඛාව බාගන්න
branch.included_desc=මෙම ශාඛාව පෙරනිමි ශාඛාවේ කොටසකි
branch.included=ඇතුළත්
branch.create_new_branch=ශාඛාවෙන් ශාඛාව සාදන්න:
branch.confirm_create_branch=ශාඛාව සාදන්න
branch.new_branch=නව ශාඛාවක් සාදන්න
branch.new_branch_from='%s' වෙතින් නව ශාඛාවක් සාදන්න
topic.manage_topics=මාතෘකා කළමනාකරණය
[org]
org_name_holder=සංවිධානයේ නම
org_full_name_holder=සංවිධානයේ සම්පූර්ණ නම
create_org=සංවිධානය සාදන්න
repo_updated=යාවත්කාල කෙරිණි
people=මිනිසුන්
teams=කණ්ඩායම්
lower_members=සාමාජිකයින්
lower_repositories=කෝෂ්ඨ
create_new_team=නව කණ්ඩායම
create_team=කණ්ඩායම සාදන්න
org_desc=සවිස්තරය
team_name=කණ්ඩායමේ නම
team_desc=සවිස්තරය
team_access_desc=කෝෂ්ඨයට ප්‍රවේශය
team_permission_desc=අවසරය
team_unit_disabled=(අබල කර ඇත)
settings=සැකසුම්
settings.options=සංවිධානය
settings.full_name=සම්පූර්ණ නම
settings.website=වියමන අඩවිය
settings.location=ස්ථානය
settings.permission=අවසර
settings.visibility.public=ප්‍රසිද්ධ
settings.visibility.private_shortname=පෞද්ගලික
settings.update_settings=සැකසුම් යාවත්කාල කරන්න
settings.update_setting_success=සංවිධානයේ සැකසුම් යාවත්කාල කර ඇත.
settings.change_orgname_prompt=සටහන: සංවිධානයේ නම වෙනස් කිරීම සංවිධානයේ ඒ.ස.නි. ද වෙනස් කරයි.
members.private=සැඟවුනු
members.owner=හිමිකරු
members.member=සාමාජික
members.remove=ඉවත් කරන්න
members.leave=හැරයන්න
members.leave.detail=%s හැරයනවාද?
members.invite_now=දැන් ආරාධනා කරන්න
teams.join=එක්වන්න
teams.leave=හැරයන්න
teams.leave.detail=%s හැරයනවාද?
teams.can_create_org_repo=කෝෂ්ඨය සාදන්න
teams.can_create_org_repo_helper=සාමාජිකයින්ට සංවිධානයෙහි නව කෝෂ්ඨ සෑදීමට හැකිය. නිර්මාතෘ ට නව කෝෂ්ඨයෙහි පරිපාලක ප්‍රවේශය ලැබෙනු ඇත.
teams.read_access=කියවීමේ ප්‍රවේශය
teams.write_access=ලිවීමේ ප්‍රවේශය
teams.admin_access=පරිපාලක ප්‍රවේශය
teams.no_desc=මෙම කණ්ඩායමට සවිස්තරයක් නැත
teams.settings=සැකසුම්
teams.members=කණ්ඩායමේ සාමාජිකයින්
teams.update_settings=සැකසුම් යාවත්කාල කරන්න
teams.repositories=කණ්ඩායම් කෝෂ්ඨ
teams.search_repo_placeholder=කෝෂ්ඨය සොයන්න…
teams.remove_all_repos_title=සියළුම කණ්ඩායම් කෝෂ්ඨ ඉවත් කරන්න
teams.remove_all_repos_desc=මෙය කණ්ඩායමෙන් සියළුම කෝෂ්ඨ ඉවත් කෙරෙනු ඇත.
teams.add_all_repos_title=සියළුම කෝෂ්ඨ එක්කරන්න
teams.add_all_repos_desc=මෙය සංවිධානයේ සියළුම කෝෂ්ඨ කණ්ඩායමට එකතු කෙරෙනු ඇත.
teams.add_nonexistent_repo=ඔබ එකතු කිරීමට උත්සාහ කරන කෝෂ්ඨය නොපවතී; පළමුව එය සාදන්න.
teams.add_duplicate_users=පරිශීලක දැනටමත් කණ්ඩායමේ සාමාජිකයෙකි.
teams.members.none=මෙම කණ්ඩායමෙහි සාමාජිකයින් නැත.
teams.specific_repositories=විශේෂිත කෝෂ්ඨ
teams.all_repositories=සියළුම කෝෂ්ඨ
[admin]
dashboard=උපකරණ පුවරුව
users=පරිශීලක ගිණුම්
organizations=සංවිධාන
repositories=කෝෂ්ඨ
emails=පරිශීලක වි-තැපැල්
config=වින්‍යාසය
notices=පද්ධතියේ දැන්වීම්
total=මුළු: %d
dashboard.statistic=සාරාංශය
dashboard.operations=නඩත්තු මෙහෙයුම්
dashboard.system_status=පද්ධතියේ තත්වය
dashboard.operation_name=මෙහෙයුමේ නම
dashboard.operation_switch=මාරුවන්න
dashboard.operation_run=ධාවනය
dashboard.check_repo_stats=සියළුම කෝෂ්ඨවල සංඛ්‍යාලේඛන පරීක්‍ෂා කරන්න
dashboard.sync_external_users=බාහිර පරිශීලක දත්ත සමමුහූර්තනය
dashboard.current_memory_usage=වත්මන් මතක භාවිතය
users.user_manage_panel=පරිශීලක ගිණුම් කළමනාකරණය
users.new_account=පරිශීලක ගිණුමක් සාදන්න
users.name=පරිශීලක නාමය
users.full_name=සම්පූර්ණ නම
users.admin=පරිපාලක
users.send_register_notify=පරිශීලක ලියාපදිංචි දැනුම්දීම යවන්න
users.edit=සංස්කරණය
users.local=ස්ථානීය
users.edit_account=පරිශීලක ගිණුම සංස්කරණය
users.max_repo_creation=උපරිම කෝෂ්ඨ ගණන
users.is_activated=පරිශීලක ගිණුම ක්‍රියාත්මකයි
users.is_admin=පරිපාලකයෙකි
users.allow_import_local=ස්ථානීය කෝෂ්ඨ ආයාත කළ හැකිය
users.allow_create_organization=සංවිධාන සෑදිය හැකිය
users.update_profile=පරිශීලක ගිණුම යාවත්කාල කරන්න
users.list_status_filter.menu_text=පෙරහන
users.list_status_filter.is_admin=පරිපාලක
users.list_status_filter.not_admin=පරිපාලක නොවන
emails.filter_sort.email=වි-තැපෑල
emails.filter_sort.name=පරිශීලක නාමය
emails.updated=වි-තැපෑල යාවත්කාල කෙරිණි
orgs.org_manage_panel=සංවිධාන කළමනාකරණය
orgs.name=නම
orgs.teams=කණ්ඩායම්
orgs.members=සාමාජිකයින්
orgs.new_orga=නව සංවිධානය
repos.repo_manage_panel=කෝෂ්ඨ කළමනාකරණය
repos.owner=හිමිකරු
repos.name=නම
repos.private=පෞද්ගලික
repos.size=ප්‍රමාණය
auths.name=නම
auths.type=වර්ගය
auths.enabled=සබල කර ඇත
auths.syncenabled=පරිශීලක සමමුහූර්තය සබල කරන්න
auths.updated=යාවත්කාල කෙරිණි
auths.security_protocol=ආරක්ෂණ කෙටුම්පත
auths.domain=වසම
auths.search_page_size=පිටුවේ ප්‍රමාණය
auths.filter=පරිශීලක පෙරහන
auths.admin_filter=පරිපාලක පෙරහන
auths.allowed_domains=ඉඩ දී ඇති වසම්
auths.oauth2_icon_url=නිරූපකයේ ඒ.ස.නි.
auths.oauth2_clientID=අනුග්‍රාහකයේ හැඳු. (යතුර)
auths.oauth2_clientSecret=අනුග්‍රාහකයේ රහස
auths.oauth2_profileURL=පැතිකඩ ඒ.ස.නි.
auths.oauth2_emailURL=වි-තැපෑල ඒ.ස.නි.
auths.enable_auto_register=ස්වයං ලියාපදිංචිය සබල කරන්න
auths.sspi_auto_create_users=ස්වයංක්‍රීයව පරිශීලකයින් සාදන්න
auths.sspi_strip_domain_names=පරිශීලක නාම වලින් වසම් නාම ඉවත් කරන්න
auths.sspi_default_language=පෙරනිමි පරිශීලක භාෂාව
config.server_config=සේවාදායකයේ වින්‍යාසය
config.app_name=අඩවියේ සිරැසිය
config.app_ver=ගිටියා අනුවාදය
config.domain=සේවාදායකයේ වසම
config.ssh_enabled=සබල කර ඇත
config.ssh_minimum_key_sizes=අවම යතුරෙහි ප්‍රමාණ
config.lfs_enabled=සබල කර ඇත
config.db_config=දත්ත සමුදායෙහි වින්‍යාසය
config.db_type=වර්ගය
config.db_name=නම
config.db_user=පරිශීලක නාමය
config.service_config=සේවාවේ වින්‍යාසය
config.show_registration_button=ලියාපදිංචි බොත්තම පෙන්වන්න
config.mail_notify=වි-තැපැල් දැනුම්දීම් සබල කරන්න
config.mailer_enabled=සබල කර ඇත
config.mailer_name=නම
config.mailer_user=පරිශීලක
config.mailer_use_sendmail=සෙන්ඩ්මේල් භාවිතා කරන්න
config.test_email_placeholder=වි-තැපෑල (උදා. පරීක්‍ෂාව@උදාහරණය.ලංකා)
config.send_test_mail=අත්හදා බැලීමේ වි-තැපෑල යවන්න
config.oauth_enabled=සබල කර ඇත
config.cache_config=නිහිතයේ වින්‍යාසය
config.cache_conn=නිහිතයේ සම්බන්ධතාවය
config.https_only=HTTPS පමණි
config.disable_gravatar=ග්‍රැවටාර් අබල කරන්න
config.disabled_logger=අබල කර ඇත
monitor.name=නම
monitor.next=ඊළඟ වේලාව
monitor.desc=සවිස්තරය
monitor.start=ආරම්භක වේලාව
monitor.process.cancel_notices=<strong>%s</strong>: අවලංගු කරන්නද?
monitor.queues=පෝලිම්
monitor.queue=පෝලිම: %s
monitor.queue.name=නම
monitor.queue.type=වර්ගය
monitor.queue.settings.submit=සැකසුම් යාවත්කාල කරන්න
monitor.queue.settings.changed=සැකසුම් යාවත්කාල කෙරිණි
notices.system_notice_list=පද්ධතියේ දැන්වීම්
notices.view_detail_header=දැන්වීමේ විස්තර දකින්න
notices.actions=ක්‍රියාමාර්ග
notices.type=වර්ගය
notices.type_1=කෝෂ්ඨය
notices.type_2=කාර්යය
notices.desc=සවිස්තරය
[action]
compare_branch=සසඳන්න
review_dismissed_reason=හේතුව:
[tool]
now=දැන්
1s=තත්පර 1
1m=විනාඩි 1
1h=පැය 1
1d=දවස් 1
1w=සති 1
1mon=මාස 1
1y=වසර 1
seconds=තත්පර %d
minutes=විනාඩි %d
hours=පැය %d
days=දවස් %d
weeks=සති %d
months=මාස %d
years=අවුරුදු %d
raw_seconds=තත්පර
raw_minutes=විනාඩි
[dropzone]
default_message=ගොනු දමන්න හෝ උඩුගත කිරීමට මෙතැන ඔබන්න.
remove_file=ගොනුව ඉවත් කරන්න
[notification]
notifications=දැනුම්දීම්
unread=නොකියවූ
read=කියවූ
no_unread=නොකියවූ දැනුම්දීම් නැත.
no_read=කියවූ දැනුම්දීම් නැත.
[gpg]
[units]

View File

@ -2214,7 +2214,7 @@ members.member_role=成员角色:
members.owner=管理员
members.member=普通成员
members.remove=移除成员
members.remove.detail=从 %[2] 中移除 %[1] 吗?
members.remove.detail=从 %[2]s 中移除 %[1]s 吗?
members.leave=离开组织
members.leave.detail=离开 %s
members.invite_desc=邀请新的用户加入 %s

View File

@ -2696,6 +2696,7 @@ monitor.execute_time=已執行時間
monitor.process.cancel=結束處理程序
monitor.process.cancel_desc=結束處理程序可能造成資料遺失
monitor.process.cancel_notices=結束: <strong>%s</strong>?
monitor.process.children=子程序
monitor.queues=佇列
monitor.queue=佇列: %s
monitor.queue.name=名稱

View File

@ -405,7 +405,7 @@ func CreateBranchProtection(ctx *context.APIContext) {
repo := ctx.Repo.Repository
// Currently protection must match an actual branch
if !git.IsBranchExist(ctx.Repo.Repository.RepoPath(), form.BranchName) {
if !git.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository.RepoPath(), form.BranchName) {
ctx.NotFound()
return
}

View File

@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
"github.com/chi-middleware/proxy"
@ -22,7 +23,9 @@ func Middlewares() []func(http.Handler) http.Handler {
var handlers = []func(http.Handler) http.Handler{
func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
next.ServeHTTP(context.NewResponse(resp), req)
ctx, _, finished := process.GetManager().AddContext(req.Context(), fmt.Sprintf("%s: %s", req.Method, req.RequestURI))
defer finished()
next.ServeHTTP(context.NewResponse(resp), req.WithContext(ctx))
})
},
}

View File

@ -95,9 +95,8 @@ func syncAppPathForGit(ctx context.Context) error {
return nil
}
// GlobalInit is for global configuration reload-able.
func GlobalInit(ctx context.Context) {
setting.NewContext()
// GlobalInitInstalled is for global installed configuration.
func GlobalInitInstalled(ctx context.Context) {
if !setting.InstallLock {
log.Fatal("Gitea is not installed")
}

View File

@ -15,6 +15,7 @@ import (
"time"
"code.gitea.io/gitea/models/db"
db_install "code.gitea.io/gitea/models/db/install"
"code.gitea.io/gitea/models/migrations"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
@ -161,10 +162,81 @@ func Install(ctx *context.Context) {
ctx.HTML(http.StatusOK, tplInstall)
}
func checkDatabase(ctx *context.Context, form *forms.InstallForm) bool {
var err error
if (setting.Database.Type == "sqlite3") &&
len(setting.Database.Path) == 0 {
ctx.Data["Err_DbPath"] = true
ctx.RenderWithErr(ctx.Tr("install.err_empty_db_path"), tplInstall, form)
return false
}
// Check if the user is trying to re-install in an installed database
db.UnsetDefaultEngine()
defer db.UnsetDefaultEngine()
if err = db.InitEngine(ctx); err != nil {
if strings.Contains(err.Error(), `Unknown database type: sqlite3`) {
ctx.Data["Err_DbType"] = true
ctx.RenderWithErr(ctx.Tr("install.sqlite3_not_available", "https://docs.gitea.io/en-us/install-from-binary/"), tplInstall, form)
} else {
ctx.Data["Err_DbSetting"] = true
ctx.RenderWithErr(ctx.Tr("install.invalid_db_setting", err), tplInstall, form)
}
return false
}
err = db_install.CheckDatabaseConnection()
if err != nil {
ctx.Data["Err_DbSetting"] = true
ctx.RenderWithErr(ctx.Tr("install.invalid_db_setting", err), tplInstall, form)
return false
}
hasPostInstallationUser, err := db_install.HasPostInstallationUsers()
if err != nil {
ctx.Data["Err_DbSetting"] = true
ctx.RenderWithErr(ctx.Tr("install.invalid_db_table", "user", err), tplInstall, form)
return false
}
dbMigrationVersion, err := db_install.GetMigrationVersion()
if err != nil {
ctx.Data["Err_DbSetting"] = true
ctx.RenderWithErr(ctx.Tr("install.invalid_db_table", "version", err), tplInstall, form)
return false
}
if hasPostInstallationUser && dbMigrationVersion > 0 {
log.Error("The database is likely to have been used by Gitea before, database migration version=%d", dbMigrationVersion)
confirmed := form.ReinstallConfirmFirst && form.ReinstallConfirmSecond && form.ReinstallConfirmThird
if !confirmed {
ctx.Data["Err_DbInstalledBefore"] = true
ctx.RenderWithErr(ctx.Tr("install.reinstall_error"), tplInstall, form)
return false
}
log.Info("User confirmed reinstallation of Gitea into a pre-existing database")
}
if hasPostInstallationUser || dbMigrationVersion > 0 {
log.Info("Gitea will be installed in a database with: hasPostInstallationUser=%v, dbMigrationVersion=%v", hasPostInstallationUser, dbMigrationVersion)
}
return true
}
// SubmitInstall response for submit install items
func SubmitInstall(ctx *context.Context) {
form := *web.GetForm(ctx).(*forms.InstallForm)
var err error
form := *web.GetForm(ctx).(*forms.InstallForm)
// fix form values
if form.AppURL != "" && form.AppURL[len(form.AppURL)-1] != '/' {
form.AppURL += "/"
}
ctx.Data["CurDbOption"] = form.DbType
if ctx.HasError() {
@ -186,9 +258,9 @@ func SubmitInstall(ctx *context.Context) {
return
}
// Pass basic check, now test configuration.
// Test database setting.
// ---- Basic checks are passed, now test configuration.
// Test database setting.
setting.Database.Type = setting.GetDBTypeByName(form.DbType)
setting.Database.Host = form.DbHost
setting.Database.User = form.DbUser
@ -201,22 +273,13 @@ func SubmitInstall(ctx *context.Context) {
setting.Database.LogSQL = !setting.IsProd
setting.PasswordHashAlgo = form.PasswordAlgorithm
if (setting.Database.Type == "sqlite3") &&
len(setting.Database.Path) == 0 {
ctx.Data["Err_DbPath"] = true
ctx.RenderWithErr(ctx.Tr("install.err_empty_db_path"), tplInstall, &form)
if !checkDatabase(ctx, &form) {
return
}
// Set test engine.
if err = db.InitEngineWithMigration(ctx, migrations.Migrate); err != nil {
if strings.Contains(err.Error(), `Unknown database type: sqlite3`) {
ctx.Data["Err_DbType"] = true
ctx.RenderWithErr(ctx.Tr("install.sqlite3_not_available", "https://docs.gitea.io/en-us/install-from-binary/"), tplInstall, &form)
} else {
ctx.Data["Err_DbSetting"] = true
ctx.RenderWithErr(ctx.Tr("install.invalid_db_setting", err), tplInstall, &form)
}
// Prepare AppDataPath, it is very important for Gitea
if err = setting.PrepareAppDataPath(); err != nil {
ctx.RenderWithErr(ctx.Tr("install.invalid_app_data_path", err), tplInstall, &form)
return
}
@ -299,9 +362,14 @@ func SubmitInstall(ctx *context.Context) {
}
}
if form.AppURL[len(form.AppURL)-1] != '/' {
form.AppURL += "/"
// Init the engine with migration
if err = db.InitEngineWithMigration(ctx, migrations.Migrate); err != nil {
db.UnsetDefaultEngine()
ctx.Data["Err_DbSetting"] = true
ctx.RenderWithErr(ctx.Tr("install.invalid_db_setting", err), tplInstall, &form)
return
}
db.UnsetDefaultEngine()
// Save settings.
cfg := ini.Empty()
@ -344,12 +412,12 @@ func SubmitInstall(ctx *context.Context) {
if form.LFSRootPath != "" {
cfg.Section("server").Key("LFS_START_SERVER").SetValue("true")
cfg.Section("server").Key("LFS_CONTENT_PATH").SetValue(form.LFSRootPath)
var secretKey string
if secretKey, err = generate.NewJwtSecretBase64(); err != nil {
var lfsJwtSecret string
if lfsJwtSecret, err = generate.NewJwtSecretBase64(); err != nil {
ctx.RenderWithErr(ctx.Tr("install.lfs_jwt_secret_failed", err), tplInstall, &form)
return
}
cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(secretKey)
cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(lfsJwtSecret)
} else {
cfg.Section("server").Key("LFS_START_SERVER").SetValue("false")
}
@ -390,16 +458,30 @@ func SubmitInstall(ctx *context.Context) {
cfg.Section("log").Key("ROUTER").SetValue("console")
cfg.Section("security").Key("INSTALL_LOCK").SetValue("true")
var secretKey string
if secretKey, err = generate.NewSecretKey(); err != nil {
ctx.RenderWithErr(ctx.Tr("install.secret_key_failed", err), tplInstall, &form)
var internalToken string
if internalToken, err = generate.NewInternalToken(); err != nil {
ctx.RenderWithErr(ctx.Tr("install.internal_token_failed", err), tplInstall, &form)
return
}
cfg.Section("security").Key("SECRET_KEY").SetValue(secretKey)
cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(internalToken)
// if there is already a SECRET_KEY, we should not overwrite it, otherwise the encrypted data will not be able to be decrypted
if setting.SecretKey == "" {
var secretKey string
if secretKey, err = generate.NewSecretKey(); err != nil {
ctx.RenderWithErr(ctx.Tr("install.secret_key_failed", err), tplInstall, &form)
return
}
cfg.Section("security").Key("SECRET_KEY").SetValue(secretKey)
}
if len(form.PasswordAlgorithm) > 0 {
cfg.Section("security").Key("PASSWORD_HASH_ALGO").SetValue(form.PasswordAlgorithm)
}
log.Info("Save settings to custom config file %s", setting.CustomConf)
err = os.MkdirAll(filepath.Dir(setting.CustomConf), os.ModePerm)
if err != nil {
ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form)
@ -411,8 +493,10 @@ func SubmitInstall(ctx *context.Context) {
return
}
// Re-read settings
ReloadSettings(ctx)
// ---- All checks are passed
// Reload settings (and re-initialize database connection)
reloadSettings(ctx)
// Create admin account
if len(form.AdminName) > 0 {

View File

@ -16,17 +16,17 @@ import (
// PreloadSettings preloads the configuration to check if we need to run install
func PreloadSettings(ctx context.Context) bool {
setting.NewContext()
setting.LoadAllowEmpty()
if !setting.InstallLock {
log.Info("AppPath: %s", setting.AppPath)
log.Info("AppWorkPath: %s", setting.AppWorkPath)
log.Info("Custom path: %s", setting.CustomPath)
log.Info("Log path: %s", setting.LogRootPath)
log.Info("Configuration file: %s", setting.CustomConf)
log.Info("Preparing to run install page")
log.Info("Prepare to run install page")
translation.InitLocales()
if setting.EnableSQLite3 {
log.Info("SQLite3 Supported")
log.Info("SQLite3 is supported")
}
setting.InitDBConfig()
setting.NewServicesForInstall()
@ -36,9 +36,9 @@ func PreloadSettings(ctx context.Context) bool {
return !setting.InstallLock
}
// ReloadSettings rereads the settings and starts up the database
func ReloadSettings(ctx context.Context) {
setting.NewContext()
// reloadSettings reloads the existing settings and starts up the database
func reloadSettings(ctx context.Context) {
setting.LoadFromExisting()
setting.InitDBConfig()
if setting.InstallLock {
if err := common.InitDBEngine(ctx); err == nil {

View File

@ -326,7 +326,7 @@ func Monitor(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.monitor")
ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminMonitor"] = true
ctx.Data["Processes"] = process.GetManager().Processes()
ctx.Data["Processes"] = process.GetManager().Processes(true)
ctx.Data["Entries"] = cron.ListTasks()
ctx.Data["Queues"] = queue.GetManager().ManagedQueues()
ctx.HTML(http.StatusOK, tplMonitor)
@ -334,8 +334,8 @@ func Monitor(ctx *context.Context) {
// MonitorCancel cancels a process
func MonitorCancel(ctx *context.Context) {
pid := ctx.ParamsInt64("pid")
process.GetManager().Cancel(pid)
pid := ctx.Params("pid")
process.GetManager().Cancel(process.IDType(pid))
ctx.JSON(http.StatusOK, map[string]interface{}{
"redirect": setting.AppSubURL + "/admin/monitor",
})

View File

@ -124,7 +124,7 @@ func RestoreBranchPost(ctx *context.Context) {
return
}
if err := git.Push(ctx.Repo.Repository.RepoPath(), git.PushOptions{
if err := git.Push(ctx, ctx.Repo.Repository.RepoPath(), git.PushOptions{
Remote: ctx.Repo.Repository.RepoPath(),
Branch: fmt.Sprintf("%s:%s%s", deletedBranch.Commit, git.BranchPrefix, deletedBranch.Name),
Env: models.PushingEnvironment(ctx.User, ctx.Repo.Repository),

View File

@ -8,7 +8,6 @@ package repo
import (
"bytes"
"compress/gzip"
gocontext "context"
"fmt"
"net/http"
"os"
@ -485,8 +484,10 @@ func serviceRPC(h serviceHandler, service string) {
h.environ = append(h.environ, "GIT_PROTOCOL="+protocol)
}
ctx, cancel := gocontext.WithCancel(git.DefaultContext)
defer cancel()
// ctx, cancel := gocontext.WithCancel(git.DefaultContext)
ctx, _, finished := process.GetManager().AddContext(h.r.Context(), fmt.Sprintf("%s %s %s [repo_path: %s]", git.GitExecutable, service, "--stateless-rpc", h.dir))
defer finished()
var stderr bytes.Buffer
cmd := exec.CommandContext(ctx, git.GitExecutable, service, "--stateless-rpc", h.dir)
cmd.Dir = h.dir
@ -495,9 +496,6 @@ func serviceRPC(h serviceHandler, service string) {
cmd.Stdin = reqBody
cmd.Stderr = &stderr
pid := process.GetManager().Add(fmt.Sprintf("%s %s %s [repo_path: %s]", git.GitExecutable, service, "--stateless-rpc", h.dir), cancel)
defer process.GetManager().Remove(pid)
if err := cmd.Run(); err != nil {
log.Error("Fail to serve RPC(%s) in %s: %v - %s", service, h.dir, err, stderr.String())
return

View File

@ -1594,7 +1594,7 @@ func ViewIssue(ctx *context.Context) {
}
ctx.Data["IsPullBranchDeletable"] = canDelete &&
pull.HeadRepo != nil &&
git.IsBranchExist(pull.HeadRepo.RepoPath(), pull.HeadBranch) &&
git.IsBranchExist(ctx, pull.HeadRepo.RepoPath(), pull.HeadBranch) &&
(!pull.HasMerged || ctx.Data["HeadBranchCommitID"] == ctx.Data["PullHeadCommitID"])
stillCanManualMerge := func() bool {

View File

@ -436,7 +436,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.Compare
if pull.Flow == models.PullRequestFlowGithub {
headBranchExist = headGitRepo.IsBranchExist(pull.HeadBranch)
} else {
headBranchExist = git.IsReferenceExist(baseGitRepo.Path, pull.GetGitRefName())
headBranchExist = git.IsReferenceExist(ctx, baseGitRepo.Path, pull.GetGitRefName())
}
if headBranchExist {

View File

@ -178,7 +178,7 @@ func SettingsPost(ctx *context.Context) {
}
}
u, _ := git.GetRemoteAddress(ctx.Repo.Repository.RepoPath(), ctx.Repo.Mirror.GetRemoteName())
u, _ := git.GetRemoteAddress(ctx, ctx.Repo.Repository.RepoPath(), ctx.Repo.Mirror.GetRemoteName())
if u.User != nil && form.MirrorPassword == "" && form.MirrorUsername == u.User.Username() {
form.MirrorPassword, _ = u.User.Password()
}

View File

@ -82,11 +82,10 @@ func (t *Task) RunWithUser(doer *user_model.User, config Config) {
}
}()
graceful.GetManager().RunWithShutdownContext(func(baseCtx context.Context) {
ctx, cancel := context.WithCancel(baseCtx)
defer cancel()
pm := process.GetManager()
pid := pm.Add(config.FormatMessage(t.Name, "process", doer), cancel)
defer pm.Remove(pid)
ctx, _, finished := pm.AddContext(baseCtx, config.FormatMessage(t.Name, "process", doer))
defer finished()
if err := t.fun(ctx, doer, config); err != nil {
if db.IsErrCancelled(err) {
message := err.(db.ErrCancelled).Message

View File

@ -67,6 +67,11 @@ type InstallForm struct {
AdminPasswd string `binding:"OmitEmpty;MaxSize(255)" locale:"install.admin_password"`
AdminConfirmPasswd string
AdminEmail string `binding:"OmitEmpty;MinSize(3);MaxSize(254);Include(@)" locale:"install.admin_email"`
// ReinstallConfirmFirst we can not use 1/2/3 or A/B/C here, there is a framework bug, can not parse "reinstall_confirm_1" or "reinstall_confirm_a"
ReinstallConfirmFirst bool
ReinstallConfirmSecond bool
ReinstallConfirmThird bool
}
// Validate validates the fields

View File

@ -1303,8 +1303,9 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff
return nil, err
}
ctx, cancel := context.WithTimeout(git.DefaultContext, time.Duration(setting.Git.Timeout.Default)*time.Second)
defer cancel()
timeout := time.Duration(setting.Git.Timeout.Default) * time.Second
ctx, _, finished := process.GetManager().AddContextTimeout(gitRepo.Ctx, timeout, fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath))
defer finished()
argsLength := 6
if len(opts.WhitespaceBehavior) > 0 {
@ -1369,9 +1370,6 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff
return nil, fmt.Errorf("error during Start: %w", err)
}
pid := process.GetManager().Add(fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath), cancel)
defer process.GetManager().Remove(pid)
diff, err := ParsePatch(opts.MaxLines, opts.MaxLineCharacters, opts.MaxFiles, stdout, parsePatchSkipToFile)
if err != nil {
return nil, fmt.Errorf("unable to ParsePatch: %w", err)

View File

@ -7,7 +7,6 @@ package mailer
import (
"bytes"
"context"
"crypto/tls"
"fmt"
"io"
@ -258,11 +257,10 @@ func (s *sendmailSender) Send(from string, to []string, msg io.WriterTo) error {
args = append(args, to...)
log.Trace("Sending with: %s %v", setting.MailService.SendmailPath, args)
pm := process.GetManager()
desc := fmt.Sprintf("SendMail: %s %v", setting.MailService.SendmailPath, args)
ctx, cancel := context.WithTimeout(graceful.GetManager().HammerContext(), setting.MailService.SendmailTimeout)
defer cancel()
ctx, _, finished := process.GetManager().AddContextTimeout(graceful.GetManager().HammerContext(), setting.MailService.SendmailTimeout, desc)
defer finished()
cmd := exec.CommandContext(ctx, setting.MailService.SendmailPath, args...)
pipe, err := cmd.StdinPipe()
@ -272,18 +270,17 @@ func (s *sendmailSender) Send(from string, to []string, msg io.WriterTo) error {
}
if err = cmd.Start(); err != nil {
_ = pipe.Close()
return err
}
pid := pm.Add(desc, cancel)
_, err = msg.WriteTo(pipe)
// we MUST close the pipe or sendmail will hang waiting for more of the message
// Also we should wait on our sendmail command even if something fails
closeError = pipe.Close()
waitError = cmd.Wait()
pm.Remove(pid)
if err != nil {
return err
} else if closeError != nil {

View File

@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/process"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
@ -192,7 +193,7 @@ func runSync(ctx context.Context, m *models.Mirror) ([]*mirrorSyncResult, bool)
}
gitArgs = append(gitArgs, m.GetRemoteName())
remoteAddr, remoteErr := git.GetRemoteAddress(repoPath, m.GetRemoteName())
remoteAddr, remoteErr := git.GetRemoteAddress(ctx, repoPath, m.GetRemoteName())
if remoteErr != nil {
log.Error("GetRemoteAddress Error %v", remoteErr)
}
@ -287,7 +288,7 @@ func runSync(ctx context.Context, m *models.Mirror) ([]*mirrorSyncResult, bool)
// sanitize the output, since it may contain the remote address, which may
// contain a password
remoteAddr, remoteErr := git.GetRemoteAddress(wikiPath, m.GetRemoteName())
remoteAddr, remoteErr := git.GetRemoteAddress(ctx, wikiPath, m.GetRemoteName())
if remoteErr != nil {
log.Error("GetRemoteAddress Error %v", remoteErr)
}
@ -367,6 +368,9 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
return false
}
ctx, _, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("Syncing Mirror %s/%s", m.Repo.OwnerName, m.Repo.Name))
defer finished()
log.Trace("SyncMirrors [repo: %-v]: Running Sync", m.Repo)
results, ok := runSync(ctx, m)
if !ok {
@ -385,7 +389,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
log.Trace("SyncMirrors [repo: %-v]: no branches updated", m.Repo)
} else {
log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results))
gitRepo, err = git.OpenRepository(m.Repo.RepoPath())
gitRepo, err = git.OpenRepositoryCtx(ctx, m.Repo.RepoPath())
if err != nil {
log.Error("OpenRepository [%d]: %v", m.RepoID, err)
return false

View File

@ -7,6 +7,7 @@ package mirror
import (
"context"
"errors"
"fmt"
"io"
"regexp"
"time"
@ -15,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
@ -92,6 +94,9 @@ func SyncPushMirror(ctx context.Context, mirrorID int64) bool {
m.LastError = ""
ctx, _, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("Syncing PushMirror %s/%s to %s", m.Repo.OwnerName, m.Repo.Name, m.RemoteName))
defer finished()
log.Trace("SyncPushMirror [mirror: %d][repo: %-v]: Running Sync", m.ID, m.Repo)
err = runPushSync(ctx, m)
if err != nil {
@ -116,7 +121,7 @@ func runPushSync(ctx context.Context, m *models.PushMirror) error {
timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second
performPush := func(path string) error {
remoteAddr, err := git.GetRemoteAddress(path, m.RemoteName)
remoteAddr, err := git.GetRemoteAddress(ctx, path, m.RemoteName)
if err != nil {
log.Error("GetRemoteAddress(%s) Error %v", path, err)
return errors.New("Unexpected error")
@ -125,7 +130,7 @@ func runPushSync(ctx context.Context, m *models.PushMirror) error {
if setting.LFS.StartServer {
log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo)
gitRepo, err := git.OpenRepository(path)
gitRepo, err := git.OpenRepositoryCtx(ctx, path)
if err != nil {
log.Error("OpenRepository: %v", err)
return errors.New("Unexpected error")
@ -141,7 +146,7 @@ func runPushSync(ctx context.Context, m *models.PushMirror) error {
log.Trace("Pushing %s mirror[%d] remote %s", path, m.ID, m.RemoteName)
if err := git.Push(path, git.PushOptions{
if err := git.Push(ctx, path, git.PushOptions{
Remote: m.RemoteName,
Force: true,
Mirror: true,
@ -162,7 +167,7 @@ func runPushSync(ctx context.Context, m *models.PushMirror) error {
if m.Repo.HasWiki() {
wikiPath := m.Repo.WikiPath()
_, err := git.GetRemoteAddress(wikiPath, m.RemoteName)
_, err := git.GetRemoteAddress(ctx, wikiPath, m.RemoteName)
if err == nil {
err := performPush(wikiPath)
if err != nil {

Some files were not shown because too many files have changed in this diff Show More