Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
8b354febf5
|
@ -603,7 +603,10 @@ ROUTER = console
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;
|
;;
|
||||||
;; The path of git executable. If empty, Gitea searches through the PATH environment.
|
;; The path of git executable. If empty, Gitea searches through the PATH environment.
|
||||||
PATH =
|
;PATH =
|
||||||
|
;;
|
||||||
|
;; The HOME directory for Git
|
||||||
|
;HOME_PATH = %(APP_DATA_PATH)/home
|
||||||
;;
|
;;
|
||||||
;; Disables highlight of added and removed changes
|
;; Disables highlight of added and removed changes
|
||||||
;DISABLE_DIFF_HIGHLIGHT = false
|
;DISABLE_DIFF_HIGHLIGHT = false
|
||||||
|
|
|
@ -948,6 +948,8 @@ Default templates for project boards:
|
||||||
## Git (`git`)
|
## Git (`git`)
|
||||||
|
|
||||||
- `PATH`: **""**: The path of Git executable. If empty, Gitea searches through the PATH environment.
|
- `PATH`: **""**: The path of Git executable. If empty, Gitea searches through the PATH environment.
|
||||||
|
- `HOME_PATH`: **%(APP_DATA_PATH)/home**: The HOME directory for Git.
|
||||||
|
This directory will be used to contain the `.gitconfig` and possible `.gnupg` directories that Gitea's git calls will use. If you can confirm Gitea is the only application running in this environment, you can set it to the normal home directory for Gitea user.
|
||||||
- `DISABLE_DIFF_HIGHLIGHT`: **false**: Disables highlight of added and removed changes.
|
- `DISABLE_DIFF_HIGHLIGHT`: **false**: Disables highlight of added and removed changes.
|
||||||
- `MAX_GIT_DIFF_LINES`: **1000**: Max number of lines allowed of a single file in diff view.
|
- `MAX_GIT_DIFF_LINES`: **1000**: Max number of lines allowed of a single file in diff view.
|
||||||
- `MAX_GIT_DIFF_LINE_CHARACTERS`: **5000**: Max character count per line highlighted in diff view.
|
- `MAX_GIT_DIFF_LINE_CHARACTERS`: **5000**: Max character count per line highlighted in diff view.
|
||||||
|
|
|
@ -97,11 +97,12 @@ repositories, `SIGNING_KEY=default` could be used to provide different
|
||||||
signing keys on a per-repository basis. However, this is clearly not an
|
signing keys on a per-repository basis. However, this is clearly not an
|
||||||
ideal UI and therefore subject to change.
|
ideal UI and therefore subject to change.
|
||||||
|
|
||||||
**Since 1.17**, Gitea runs git in its own home directory `[repository].ROOT` and uses its own config `{[repository].ROOT}/.gitconfig`.
|
**Since 1.17**, Gitea runs git in its own home directory `[git].HOME_PATH` (default to `%(APP_DATA_PATH)/home`)
|
||||||
|
and uses its own config `{[git].HOME_PATH}/.gitconfig`.
|
||||||
If you have your own customized git config for Gitea, you should set these configs in system git config (aka `/etc/gitconfig`)
|
If you have your own customized git config for Gitea, you should set these configs in system git config (aka `/etc/gitconfig`)
|
||||||
or the Gitea internal git config `{[repository].ROOT}/.gitconfig`.
|
or the Gitea internal git config `{[git].HOME_PATH}/.gitconfig`.
|
||||||
Related home files for git command (like `.gnupg`) should also be put in Gitea's git home directory `[repository].ROOT`.
|
Related home files for git command (like `.gnupg`) should also be put in Gitea's git home directory `[git].HOME_PATH`.
|
||||||
|
If you like to keep the `.gnupg` directory outside of `{[git].HOME_PATH}/`, consider setting the `$GNUPGHOME` environment variable to your preferred location.
|
||||||
|
|
||||||
### `INITIAL_COMMIT`
|
### `INITIAL_COMMIT`
|
||||||
|
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -88,7 +88,7 @@ require (
|
||||||
github.com/valyala/fastjson v1.6.3
|
github.com/valyala/fastjson v1.6.3
|
||||||
github.com/xanzy/go-gitlab v0.64.0
|
github.com/xanzy/go-gitlab v0.64.0
|
||||||
github.com/yohcop/openid-go v1.0.0
|
github.com/yohcop/openid-go v1.0.0
|
||||||
github.com/yuin/goldmark v1.4.12
|
github.com/yuin/goldmark v1.4.13
|
||||||
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
|
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
|
||||||
github.com/yuin/goldmark-meta v1.1.0
|
github.com/yuin/goldmark-meta v1.1.0
|
||||||
go.jolheiser.com/hcaptcha v0.0.4
|
go.jolheiser.com/hcaptcha v0.0.4
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -1550,8 +1550,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.5/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
|
github.com/yuin/goldmark v1.4.5/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
|
||||||
github.com/yuin/goldmark v1.4.12 h1:6hffw6vALvEDqJ19dOJvJKOoAOKe4NDaTqvd2sktGN0=
|
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
||||||
github.com/yuin/goldmark v1.4.12/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 h1:yHfZyN55+5dp1wG7wDKv8HQ044moxkyGq12KFFMFDxg=
|
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 h1:yHfZyN55+5dp1wG7wDKv8HQ044moxkyGq12KFFMFDxg=
|
||||||
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594/go.mod h1:U9ihbh+1ZN7fR5Se3daSPoz1CGF9IYtSvWwVQtnzGHU=
|
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594/go.mod h1:U9ihbh+1ZN7fR5Se3daSPoz1CGF9IYtSvWwVQtnzGHU=
|
||||||
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
|
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
|
||||||
|
|
|
@ -396,6 +396,8 @@ var migrations = []Migration{
|
||||||
NewMigration("Alter hook_task table TEXT fields to LONGTEXT", alterHookTaskTextFieldsToLongText),
|
NewMigration("Alter hook_task table TEXT fields to LONGTEXT", alterHookTaskTextFieldsToLongText),
|
||||||
// v218 -> v219
|
// v218 -> v219
|
||||||
NewMigration("Improve Action table indices v2", improveActionTableIndices),
|
NewMigration("Improve Action table indices v2", improveActionTableIndices),
|
||||||
|
// v219 -> v220
|
||||||
|
NewMigration("Add sync_on_commit column to push_mirror table", addSyncOnCommitColForPushMirror),
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentDBVersion returns the current db version
|
// GetCurrentDBVersion returns the current db version
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2022 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 migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func addSyncOnCommitColForPushMirror(x *xorm.Engine) error {
|
||||||
|
type PushMirror struct {
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
RepoID int64 `xorm:"INDEX"`
|
||||||
|
Repo *repo.Repository `xorm:"-"`
|
||||||
|
RemoteName string
|
||||||
|
|
||||||
|
SyncOnCommit bool `xorm:"NOT NULL DEFAULT true"`
|
||||||
|
Interval time.Duration
|
||||||
|
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||||
|
LastUpdateUnix timeutil.TimeStamp `xorm:"INDEX last_update"`
|
||||||
|
LastError string `xorm:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.Sync2(new(PushMirror))
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ type PushMirror struct {
|
||||||
Repo *Repository `xorm:"-"`
|
Repo *Repository `xorm:"-"`
|
||||||
RemoteName string
|
RemoteName string
|
||||||
|
|
||||||
|
SyncOnCommit bool `xorm:"NOT NULL DEFAULT true"`
|
||||||
Interval time.Duration
|
Interval time.Duration
|
||||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||||
LastUpdateUnix timeutil.TimeStamp `xorm:"INDEX last_update"`
|
LastUpdateUnix timeutil.TimeStamp `xorm:"INDEX last_update"`
|
||||||
|
@ -93,6 +94,14 @@ func GetPushMirrorsByRepoID(repoID int64) ([]*PushMirror, error) {
|
||||||
return mirrors, db.GetEngine(db.DefaultContext).Where("repo_id=?", repoID).Find(&mirrors)
|
return mirrors, db.GetEngine(db.DefaultContext).Where("repo_id=?", repoID).Find(&mirrors)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPushMirrorsSyncedOnCommit returns push-mirrors for this repo that should be updated by new commits
|
||||||
|
func GetPushMirrorsSyncedOnCommit(repoID int64) ([]*PushMirror, error) {
|
||||||
|
mirrors := make([]*PushMirror, 0, 10)
|
||||||
|
return mirrors, db.GetEngine(db.DefaultContext).
|
||||||
|
Where("repo_id=? AND sync_on_commit=?", repoID, true).
|
||||||
|
Find(&mirrors)
|
||||||
|
}
|
||||||
|
|
||||||
// PushMirrorsIterate iterates all push-mirror repositories.
|
// PushMirrorsIterate iterates all push-mirror repositories.
|
||||||
func PushMirrorsIterate(limit int, f func(idx int, bean interface{}) error) error {
|
func PushMirrorsIterate(limit int, f func(idx int, bean interface{}) error) error {
|
||||||
return db.GetEngine(db.DefaultContext).
|
return db.GetEngine(db.DefaultContext).
|
||||||
|
|
|
@ -107,6 +107,8 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
|
||||||
|
|
||||||
setting.Packages.Storage.Path = filepath.Join(setting.AppDataPath, "packages")
|
setting.Packages.Storage.Path = filepath.Join(setting.AppDataPath, "packages")
|
||||||
|
|
||||||
|
setting.Git.HomePath = filepath.Join(setting.AppDataPath, "home")
|
||||||
|
|
||||||
if err = storage.Init(); err != nil {
|
if err = storage.Init(); err != nil {
|
||||||
fatalTestError("storage.Init: %v\n", err)
|
fatalTestError("storage.Init: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,23 +105,36 @@ type RunOpts struct {
|
||||||
PipelineFunc func(context.Context, context.CancelFunc) error
|
PipelineFunc func(context.Context, context.CancelFunc) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func commonBaseEnvs() []string {
|
||||||
|
// at the moment, do not set "GIT_CONFIG_NOSYSTEM", users may have put some configs like "receive.certNonceSeed" in it
|
||||||
|
envs := []string{
|
||||||
|
"HOME=" + HomeDir(), // make Gitea use internal git config only, to prevent conflicts with user's git config
|
||||||
|
"GIT_NO_REPLACE_OBJECTS=1", // ignore replace references (https://git-scm.com/docs/git-replace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// some environment variables should be passed to git command
|
||||||
|
passThroughEnvKeys := []string{
|
||||||
|
"GNUPGHOME", // git may call gnupg to do commit signing
|
||||||
|
}
|
||||||
|
for _, key := range passThroughEnvKeys {
|
||||||
|
if val, ok := os.LookupEnv(key); ok {
|
||||||
|
envs = append(envs, key+"="+val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return envs
|
||||||
|
}
|
||||||
|
|
||||||
// CommonGitCmdEnvs returns the common environment variables for a "git" command.
|
// CommonGitCmdEnvs returns the common environment variables for a "git" command.
|
||||||
func CommonGitCmdEnvs() []string {
|
func CommonGitCmdEnvs() []string {
|
||||||
// at the moment, do not set "GIT_CONFIG_NOSYSTEM", users may have put some configs like "receive.certNonceSeed" in it
|
return append(commonBaseEnvs(), []string{
|
||||||
return []string{
|
"LC_ALL=" + DefaultLocale,
|
||||||
fmt.Sprintf("LC_ALL=%s", DefaultLocale),
|
"GIT_TERMINAL_PROMPT=0", // avoid prompting for credentials interactively, supported since git v2.3
|
||||||
"GIT_TERMINAL_PROMPT=0", // avoid prompting for credentials interactively, supported since git v2.3
|
}...)
|
||||||
"GIT_NO_REPLACE_OBJECTS=1", // ignore replace references (https://git-scm.com/docs/git-replace)
|
|
||||||
"HOME=" + HomeDir(), // make Gitea use internal git config only, to prevent conflicts with user's git config
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommonCmdServEnvs is like CommonGitCmdEnvs but it only returns minimal required environment variables for the "gitea serv" command
|
// CommonCmdServEnvs is like CommonGitCmdEnvs but it only returns minimal required environment variables for the "gitea serv" command
|
||||||
func CommonCmdServEnvs() []string {
|
func CommonCmdServEnvs() []string {
|
||||||
return []string{
|
return commonBaseEnvs()
|
||||||
"GIT_NO_REPLACE_OBJECTS=1", // ignore replace references (https://git-scm.com/docs/git-replace)
|
|
||||||
"HOME=" + HomeDir(), // make Gitea use internal git config only, to prevent conflicts with user's git config
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run runs the command with the RunOpts
|
// Run runs the command with the RunOpts
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -19,7 +20,6 @@ import (
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/hashicorp/go-version"
|
"github.com/hashicorp/go-version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -126,8 +126,8 @@ func VersionInfo() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkInit() error {
|
func checkInit() error {
|
||||||
if setting.RepoRootPath == "" {
|
if setting.Git.HomePath == "" {
|
||||||
return errors.New("can not init Git's HomeDir (RepoRootPath is empty), the setting and git modules are not initialized correctly")
|
return errors.New("unable to init Git's HomeDir, incorrect initialization of the setting and git modules")
|
||||||
}
|
}
|
||||||
if DefaultContext != nil {
|
if DefaultContext != nil {
|
||||||
log.Warn("git module has been initialized already, duplicate init should be fixed")
|
log.Warn("git module has been initialized already, duplicate init should be fixed")
|
||||||
|
@ -137,14 +137,14 @@ func checkInit() error {
|
||||||
|
|
||||||
// HomeDir is the home dir for git to store the global config file used by Gitea internally
|
// HomeDir is the home dir for git to store the global config file used by Gitea internally
|
||||||
func HomeDir() string {
|
func HomeDir() string {
|
||||||
if setting.RepoRootPath == "" {
|
if setting.Git.HomePath == "" {
|
||||||
// strict check, make sure the git module is initialized correctly.
|
// strict check, make sure the git module is initialized correctly.
|
||||||
// attention: when the git module is called in gitea sub-command (serv/hook), the log module is not able to show messages to users.
|
// attention: when the git module is called in gitea sub-command (serv/hook), the log module is not able to show messages to users.
|
||||||
// for example: if there is gitea git hook code calling git.NewCommand before git.InitXxx, the integration test won't show the real failure reasons.
|
// for example: if there is gitea git hook code calling git.NewCommand before git.InitXxx, the integration test won't show the real failure reasons.
|
||||||
log.Fatal("can not get Git's HomeDir (RepoRootPath is empty), the setting and git modules are not initialized correctly")
|
log.Fatal("Unable to init Git's HomeDir, incorrect initialization of the setting and git modules")
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return setting.RepoRootPath
|
return setting.Git.HomePath
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
|
// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
|
||||||
|
@ -175,11 +175,15 @@ func InitOnceWithSync(ctx context.Context) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
initOnce.Do(func() {
|
initOnce.Do(func() {
|
||||||
err = InitSimple(ctx)
|
if err = InitSimple(ctx); err != nil {
|
||||||
if err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// when git works with gnupg (commit signing), there should be a stable home for gnupg commands
|
||||||
|
if _, ok := os.LookupEnv("GNUPGHOME"); !ok {
|
||||||
|
_ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg"))
|
||||||
|
}
|
||||||
|
|
||||||
// Since git wire protocol has been released from git v2.18
|
// Since git wire protocol has been released from git v2.18
|
||||||
if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
|
if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
|
||||||
globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2")
|
globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2")
|
||||||
|
@ -206,7 +210,7 @@ func InitOnceWithSync(ctx context.Context) (err error) {
|
||||||
// syncGitConfig only modifies gitconfig, won't change global variables (otherwise there will be data-race problem)
|
// syncGitConfig only modifies gitconfig, won't change global variables (otherwise there will be data-race problem)
|
||||||
func syncGitConfig() (err error) {
|
func syncGitConfig() (err error) {
|
||||||
if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil {
|
if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil {
|
||||||
return fmt.Errorf("unable to create directory %s, err: %w", setting.RepoRootPath, err)
|
return fmt.Errorf("unable to prepare git home directory %s, err: %w", HomeDir(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Git requires setting user.name and user.email in order to commit changes - old comment: "if they're not set just add some defaults"
|
// Git requires setting user.name and user.email in order to commit changes - old comment: "if they're not set just add some defaults"
|
||||||
|
|
|
@ -21,12 +21,12 @@ import (
|
||||||
func testRun(m *testing.M) error {
|
func testRun(m *testing.M) error {
|
||||||
_ = log.NewLogger(1000, "console", "console", `{"level":"trace","stacktracelevel":"NONE","stderr":true}`)
|
_ = log.NewLogger(1000, "console", "console", `{"level":"trace","stacktracelevel":"NONE","stderr":true}`)
|
||||||
|
|
||||||
repoRootPath, err := os.MkdirTemp(os.TempDir(), "repos")
|
gitHomePath, err := os.MkdirTemp(os.TempDir(), "git-home")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create temp dir: %w", err)
|
return fmt.Errorf("unable to create temp dir: %w", err)
|
||||||
}
|
}
|
||||||
defer util.RemoveAll(repoRootPath)
|
defer util.RemoveAll(gitHomePath)
|
||||||
setting.RepoRootPath = repoRootPath
|
setting.Git.HomePath = gitHomePath
|
||||||
|
|
||||||
if err = InitOnceWithSync(context.Background()); err != nil {
|
if err = InitOnceWithSync(context.Background()); err != nil {
|
||||||
return fmt.Errorf("failed to call Init: %w", err)
|
return fmt.Errorf("failed to call Init: %w", err)
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
// Copyright 2022 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 mirror
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/queue"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
var mirrorQueue queue.UniqueQueue
|
||||||
|
|
||||||
|
// SyncType type of sync request
|
||||||
|
type SyncType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// PullMirrorType for pull mirrors
|
||||||
|
PullMirrorType SyncType = iota
|
||||||
|
// PushMirrorType for push mirrors
|
||||||
|
PushMirrorType
|
||||||
|
)
|
||||||
|
|
||||||
|
// SyncRequest for the mirror queue
|
||||||
|
type SyncRequest struct {
|
||||||
|
Type SyncType
|
||||||
|
ReferenceID int64 // RepoID for pull mirror, MirrorID for push mirror
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartSyncMirrors starts a go routine to sync the mirrors
|
||||||
|
func StartSyncMirrors(queueHandle func(data ...queue.Data) []queue.Data) {
|
||||||
|
if !setting.Mirror.Enabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mirrorQueue = queue.CreateUniqueQueue("mirror", queueHandle, new(SyncRequest))
|
||||||
|
|
||||||
|
go graceful.GetManager().RunWithShutdownFns(mirrorQueue.Run)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPullMirrorToQueue adds repoID to mirror queue
|
||||||
|
func AddPullMirrorToQueue(repoID int64) {
|
||||||
|
addMirrorToQueue(PullMirrorType, repoID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPushMirrorToQueue adds the push mirror to the queue
|
||||||
|
func AddPushMirrorToQueue(mirrorID int64) {
|
||||||
|
addMirrorToQueue(PushMirrorType, mirrorID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addMirrorToQueue(syncType SyncType, referenceID int64) {
|
||||||
|
if !setting.Mirror.Enabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
if err := PushToQueue(syncType, referenceID); err != nil {
|
||||||
|
log.Error("Unable to push sync request for to the queue for pull mirror repo[%d]. Error: %v", referenceID, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushToQueue adds the sync request to the queue
|
||||||
|
func PushToQueue(mirrorType SyncType, referenceID int64) error {
|
||||||
|
return mirrorQueue.Push(&SyncRequest{
|
||||||
|
Type: mirrorType,
|
||||||
|
ReferenceID: referenceID,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2022 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 mirror
|
||||||
|
|
||||||
|
import (
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
mirror_module "code.gitea.io/gitea/modules/mirror"
|
||||||
|
"code.gitea.io/gitea/modules/notification/base"
|
||||||
|
"code.gitea.io/gitea/modules/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mirrorNotifier struct {
|
||||||
|
base.NullNotifier
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ base.Notifier = &mirrorNotifier{}
|
||||||
|
|
||||||
|
// NewNotifier create a new mirrorNotifier notifier
|
||||||
|
func NewNotifier() base.Notifier {
|
||||||
|
return &mirrorNotifier{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mirrorNotifier) NotifyPushCommits(_ *user_model.User, repo *repo_model.Repository, _ *repository.PushUpdateOptions, _ *repository.PushCommits) {
|
||||||
|
syncPushMirrorWithSyncOnCommit(repo.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mirrorNotifier) NotifySyncPushCommits(_ *user_model.User, repo *repo_model.Repository, _ *repository.PushUpdateOptions, _ *repository.PushCommits) {
|
||||||
|
syncPushMirrorWithSyncOnCommit(repo.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func syncPushMirrorWithSyncOnCommit(repoID int64) {
|
||||||
|
pushMirrors, err := repo_model.GetPushMirrorsSyncedOnCommit(repoID)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("repo_model.GetPushMirrorsSyncedOnCommit failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mirror := range pushMirrors {
|
||||||
|
mirror_module.AddPushMirrorToQueue(mirror.ID)
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/notification/base"
|
"code.gitea.io/gitea/modules/notification/base"
|
||||||
"code.gitea.io/gitea/modules/notification/indexer"
|
"code.gitea.io/gitea/modules/notification/indexer"
|
||||||
"code.gitea.io/gitea/modules/notification/mail"
|
"code.gitea.io/gitea/modules/notification/mail"
|
||||||
|
"code.gitea.io/gitea/modules/notification/mirror"
|
||||||
"code.gitea.io/gitea/modules/notification/ui"
|
"code.gitea.io/gitea/modules/notification/ui"
|
||||||
"code.gitea.io/gitea/modules/notification/webhook"
|
"code.gitea.io/gitea/modules/notification/webhook"
|
||||||
"code.gitea.io/gitea/modules/repository"
|
"code.gitea.io/gitea/modules/repository"
|
||||||
|
@ -37,6 +38,7 @@ func NewContext() {
|
||||||
RegisterNotifier(indexer.NewNotifier())
|
RegisterNotifier(indexer.NewNotifier())
|
||||||
RegisterNotifier(webhook.NewNotifier())
|
RegisterNotifier(webhook.NewNotifier())
|
||||||
RegisterNotifier(action.NewNotifier())
|
RegisterNotifier(action.NewNotifier())
|
||||||
|
RegisterNotifier(mirror.NewNotifier())
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifyCreateIssueComment notifies issue comment related message to notifiers
|
// NotifyCreateIssueComment notifies issue comment related message to notifiers
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package setting
|
package setting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
@ -13,6 +14,7 @@ import (
|
||||||
// Git settings
|
// Git settings
|
||||||
var Git = struct {
|
var Git = struct {
|
||||||
Path string
|
Path string
|
||||||
|
HomePath string
|
||||||
DisableDiffHighlight bool
|
DisableDiffHighlight bool
|
||||||
MaxGitDiffLines int
|
MaxGitDiffLines int
|
||||||
MaxGitDiffLineCharacters int
|
MaxGitDiffLineCharacters int
|
||||||
|
@ -67,7 +69,16 @@ var Git = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGit() {
|
func newGit() {
|
||||||
if err := Cfg.Section("git").MapTo(&Git); err != nil {
|
sec := Cfg.Section("git")
|
||||||
|
|
||||||
|
if err := sec.MapTo(&Git); err != nil {
|
||||||
log.Fatal("Failed to map Git settings: %v", err)
|
log.Fatal("Failed to map Git settings: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Git.HomePath = sec.Key("HOME_PATH").MustString("home")
|
||||||
|
if !filepath.IsAbs(Git.HomePath) {
|
||||||
|
Git.HomePath = filepath.Join(AppDataPath, Git.HomePath)
|
||||||
|
} else {
|
||||||
|
Git.HomePath = filepath.Clean(Git.HomePath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -840,8 +840,9 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
|
||||||
SSH.StartBuiltinServer = false
|
SSH.StartBuiltinServer = false
|
||||||
}
|
}
|
||||||
|
|
||||||
trustedUserCaKeys := sec.Key("SSH_TRUSTED_USER_CA_KEYS").Strings(",")
|
SSH.TrustedUserCAKeysFile = sec.Key("SSH_TRUSTED_USER_CA_KEYS_FILENAME").MustString(filepath.Join(SSH.RootPath, "gitea-trusted-user-ca-keys.pem"))
|
||||||
for _, caKey := range trustedUserCaKeys {
|
|
||||||
|
for _, caKey := range SSH.TrustedUserCAKeys {
|
||||||
pubKey, _, _, _, err := gossh.ParseAuthorizedKey([]byte(caKey))
|
pubKey, _, _, _, err := gossh.ParseAuthorizedKey([]byte(caKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to parse TrustedUserCaKeys: %s %v", caKey, err)
|
log.Fatal("Failed to parse TrustedUserCaKeys: %s %v", caKey, err)
|
||||||
|
@ -849,7 +850,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
|
||||||
|
|
||||||
SSH.TrustedUserCAKeysParsed = append(SSH.TrustedUserCAKeysParsed, pubKey)
|
SSH.TrustedUserCAKeysParsed = append(SSH.TrustedUserCAKeysParsed, pubKey)
|
||||||
}
|
}
|
||||||
if len(trustedUserCaKeys) > 0 {
|
if len(SSH.TrustedUserCAKeys) > 0 {
|
||||||
// Set the default as email,username otherwise we can leave it empty
|
// Set the default as email,username otherwise we can leave it empty
|
||||||
sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("username,email")
|
sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("username,email")
|
||||||
} else {
|
} else {
|
||||||
|
@ -858,22 +859,6 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
|
||||||
|
|
||||||
SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(","))
|
SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(","))
|
||||||
|
|
||||||
if !SSH.Disabled && !SSH.StartBuiltinServer {
|
|
||||||
if err := os.MkdirAll(SSH.RootPath, 0o700); err != nil {
|
|
||||||
log.Fatal("Failed to create '%s': %v", SSH.RootPath, err)
|
|
||||||
} else if err = os.MkdirAll(SSH.KeyTestPath, 0o644); err != nil {
|
|
||||||
log.Fatal("Failed to create '%s': %v", SSH.KeyTestPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(trustedUserCaKeys) > 0 && SSH.AuthorizedPrincipalsEnabled {
|
|
||||||
fname := sec.Key("SSH_TRUSTED_USER_CA_KEYS_FILENAME").MustString(filepath.Join(SSH.RootPath, "gitea-trusted-user-ca-keys.pem"))
|
|
||||||
if err := os.WriteFile(fname,
|
|
||||||
[]byte(strings.Join(trustedUserCaKeys, "\n")), 0o600); err != nil {
|
|
||||||
log.Fatal("Failed to create '%s': %v", fname, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool(SSH.MinimumKeySizeCheck)
|
SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool(SSH.MinimumKeySizeCheck)
|
||||||
minimumKeySizes := Cfg.Section("ssh.minimum_key_sizes").Keys()
|
minimumKeySizes := Cfg.Section("ssh.minimum_key_sizes").Keys()
|
||||||
for _, key := range minimumKeySizes {
|
for _, key := range minimumKeySizes {
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
// Copyright 2022 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 ssh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Init() error {
|
||||||
|
if setting.SSH.Disabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if setting.SSH.StartBuiltinServer {
|
||||||
|
Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
|
||||||
|
log.Info("SSH server started on %s. Cipher list (%v), key exchange algorithms (%v), MACs (%v)",
|
||||||
|
net.JoinHostPort(setting.SSH.ListenHost, strconv.Itoa(setting.SSH.ListenPort)),
|
||||||
|
setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs,
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
builtinUnused()
|
||||||
|
|
||||||
|
// FIXME: why 0o644 for a directory .....
|
||||||
|
if err := os.MkdirAll(setting.SSH.KeyTestPath, 0o644); err != nil {
|
||||||
|
return fmt.Errorf("failed to create directory %q for ssh key test: %w", setting.SSH.KeyTestPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(setting.SSH.TrustedUserCAKeys) > 0 && setting.SSH.AuthorizedPrincipalsEnabled {
|
||||||
|
caKeysFileName := setting.SSH.TrustedUserCAKeysFile
|
||||||
|
caKeysFileDir := filepath.Dir(caKeysFileName)
|
||||||
|
|
||||||
|
err := os.MkdirAll(caKeysFileDir, 0o700) // SSH.RootPath by default (That is `~/.ssh` in most cases)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create directory %q for ssh trusted ca keys: %w", caKeysFileDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(caKeysFileName, []byte(strings.Join(setting.SSH.TrustedUserCAKeys, "\n")), 0o600); err != nil {
|
||||||
|
return fmt.Errorf("failed to write ssh trusted ca keys to %q: %w", caKeysFileName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -29,7 +29,7 @@ func listen(server *ssh.Server) {
|
||||||
log.Info("SSH Listener: %s Closed", server.Addr)
|
log.Info("SSH Listener: %s Closed", server.Addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unused informs our cleanup routine that we will not be using a ssh port
|
// builtinUnused informs our cleanup routine that we will not be using a ssh port
|
||||||
func Unused() {
|
func builtinUnused() {
|
||||||
graceful.GetManager().InformCleanup()
|
graceful.GetManager().InformCleanup()
|
||||||
}
|
}
|
||||||
|
|
|
@ -861,7 +861,6 @@ default_branch=Standardbranch
|
||||||
default_branch_helper=Der default Branch ist der Basisbranch für Pull-Requests und Commits.
|
default_branch_helper=Der default Branch ist der Basisbranch für Pull-Requests und Commits.
|
||||||
mirror_prune=Entfernen
|
mirror_prune=Entfernen
|
||||||
mirror_prune_desc=Entferne veraltete remote-tracking Referenzen
|
mirror_prune_desc=Entferne veraltete remote-tracking Referenzen
|
||||||
mirror_interval=Mirror-Intervall. Gültige Zeiteinheiten sind 'h', 'm', sowie 's'. 0 deaktiviert die automatische Synchronisierung. (Minimum: %s)
|
|
||||||
mirror_interval_invalid=Das Spiegel-Intervall ist ungültig.
|
mirror_interval_invalid=Das Spiegel-Intervall ist ungültig.
|
||||||
mirror_address=Klonen via URL
|
mirror_address=Klonen via URL
|
||||||
mirror_address_desc=Gib alle erforderlichen Anmeldedaten im Abschnitt "Authentifizierung" ein.
|
mirror_address_desc=Gib alle erforderlichen Anmeldedaten im Abschnitt "Authentifizierung" ein.
|
||||||
|
|
|
@ -861,7 +861,6 @@ default_branch=Προεπιλεγμένος Κλάδος
|
||||||
default_branch_helper=Ο προεπιλεγμένος κλάδος είναι ο βασικός κλάδος για pull requests και υποβολές κώδικα.
|
default_branch_helper=Ο προεπιλεγμένος κλάδος είναι ο βασικός κλάδος για pull requests και υποβολές κώδικα.
|
||||||
mirror_prune=Καθαρισμός
|
mirror_prune=Καθαρισμός
|
||||||
mirror_prune_desc=Αφαίρεση παρωχημένων αναφορών απομακρυσμένης-παρακολούθησης
|
mirror_prune_desc=Αφαίρεση παρωχημένων αναφορών απομακρυσμένης-παρακολούθησης
|
||||||
mirror_interval=Διάστημα ανανέωσης ειδώλου (έγκυρες μονάδες ώρας είναι 'h', 'm', 's'). 0 για απενεργοποίηση του αυτόματου συγχρονισμού. (Ελάχιστο διάστημα: %s)
|
|
||||||
mirror_interval_invalid=Το χρονικό διάστημα του ειδώλου δεν είναι έγκυρο.
|
mirror_interval_invalid=Το χρονικό διάστημα του ειδώλου δεν είναι έγκυρο.
|
||||||
mirror_address=Κλωνοποίηση Από Το URL
|
mirror_address=Κλωνοποίηση Από Το URL
|
||||||
mirror_address_desc=Τοποθετήστε όλα τα απαιτούμενα διαπιστευτήρια στην ενότητα Εξουσιοδότηση.
|
mirror_address_desc=Τοποθετήστε όλα τα απαιτούμενα διαπιστευτήρια στην ενότητα Εξουσιοδότηση.
|
||||||
|
|
|
@ -861,8 +861,9 @@ default_branch = Default Branch
|
||||||
default_branch_helper = The default branch is the base branch for pull requests and code commits.
|
default_branch_helper = The default branch is the base branch for pull requests and code commits.
|
||||||
mirror_prune = Prune
|
mirror_prune = Prune
|
||||||
mirror_prune_desc = Remove obsolete remote-tracking references
|
mirror_prune_desc = Remove obsolete remote-tracking references
|
||||||
mirror_interval = Mirror Interval (valid time units are 'h', 'm', 's'). 0 to disable automatic sync. (Minimum interval: %s)
|
mirror_interval = Mirror Interval (valid time units are 'h', 'm', 's'). 0 to disable periodic sync. (Minimum interval: %s)
|
||||||
mirror_interval_invalid = The mirror interval is not valid.
|
mirror_interval_invalid = The mirror interval is not valid.
|
||||||
|
mirror_sync_on_commit = Sync when commits are pushed
|
||||||
mirror_address = Clone From URL
|
mirror_address = Clone From URL
|
||||||
mirror_address_desc = Put any required credentials in the Authorization section.
|
mirror_address_desc = Put any required credentials in the Authorization section.
|
||||||
mirror_address_url_invalid = The provided url is invalid. You must escape all components of the url correctly.
|
mirror_address_url_invalid = The provided url is invalid. You must escape all components of the url correctly.
|
||||||
|
@ -1302,6 +1303,7 @@ issues.previous = Previous
|
||||||
issues.next = Next
|
issues.next = Next
|
||||||
issues.open_title = Open
|
issues.open_title = Open
|
||||||
issues.closed_title = Closed
|
issues.closed_title = Closed
|
||||||
|
issues.draft_title = Draft
|
||||||
issues.num_comments = %d comments
|
issues.num_comments = %d comments
|
||||||
issues.commented_at = `commented <a href="#%s">%s</a>`
|
issues.commented_at = `commented <a href="#%s">%s</a>`
|
||||||
issues.delete_comment_confirm = Are you sure you want to delete this comment?
|
issues.delete_comment_confirm = Are you sure you want to delete this comment?
|
||||||
|
|
|
@ -861,8 +861,9 @@ default_branch=Rama por defecto
|
||||||
default_branch_helper=La rama por defecto es la rama base para pull requests y commits de código.
|
default_branch_helper=La rama por defecto es la rama base para pull requests y commits de código.
|
||||||
mirror_prune=Purgar
|
mirror_prune=Purgar
|
||||||
mirror_prune_desc=Eliminar referencias de seguimiento de remotes obsoletas
|
mirror_prune_desc=Eliminar referencias de seguimiento de remotes obsoletas
|
||||||
mirror_interval=Intervalo de replicación (las unidades de tiempo válidas son «h», «m» y «s»). 0 desactiva la sincronización automática. (Intervalo mínimo: %s)
|
mirror_interval=Intervalo de réplica (Las unidades de tiempo válidas son 'h', 'm', 's'). 0 para deshabilitar la sincronización automática. (Intervalo mínimo: %s)
|
||||||
mirror_interval_invalid=El intervalo de réplica no es válido.
|
mirror_interval_invalid=El intervalo de réplica no es válido.
|
||||||
|
mirror_sync_on_commit=Sincronizar cuando los commits sean subidos
|
||||||
mirror_address=Clonar desde URL
|
mirror_address=Clonar desde URL
|
||||||
mirror_address_desc=Ponga cualquier credencial requerida en la sección de Autorización.
|
mirror_address_desc=Ponga cualquier credencial requerida en la sección de Autorización.
|
||||||
mirror_address_url_invalid=La url proporcionada no es válida. Debe escapar correctamente de todos los componentes de la url.
|
mirror_address_url_invalid=La url proporcionada no es válida. Debe escapar correctamente de todos los componentes de la url.
|
||||||
|
|
|
@ -861,7 +861,6 @@ default_branch=デフォルトブランチ
|
||||||
default_branch_helper=デフォルトブランチはプルリクエストとコードコミットのベースブランチとなります。
|
default_branch_helper=デフォルトブランチはプルリクエストとコードコミットのベースブランチとなります。
|
||||||
mirror_prune=Prune
|
mirror_prune=Prune
|
||||||
mirror_prune_desc=不要になった古いリモートトラッキング参照を削除
|
mirror_prune_desc=不要になった古いリモートトラッキング参照を削除
|
||||||
mirror_interval=ミラー間隔 (有効な時間の単位は'h'、'm'、's')。 自動的な同期を無効にする場合は0。(最小間隔: %s)
|
|
||||||
mirror_interval_invalid=ミラー間隔が不正です。
|
mirror_interval_invalid=ミラー間隔が不正です。
|
||||||
mirror_address=クローンするURL
|
mirror_address=クローンするURL
|
||||||
mirror_address_desc=必要な資格情報は「認証」セクションに設定してください。
|
mirror_address_desc=必要な資格情報は「認証」セクションに設定してください。
|
||||||
|
|
|
@ -861,7 +861,6 @@ default_branch=Noklusējuma atzars
|
||||||
default_branch_helper=Noklusētais atzars nosaka pamata atzaru uz kuru tiks veidoti izmaiņu pieprasījumi un koda revīziju iesūtīšana.
|
default_branch_helper=Noklusētais atzars nosaka pamata atzaru uz kuru tiks veidoti izmaiņu pieprasījumi un koda revīziju iesūtīšana.
|
||||||
mirror_prune=Izmest
|
mirror_prune=Izmest
|
||||||
mirror_prune_desc=Izdzēst visas ārējās atsauces, kas ārējā repozitorijā vairs neeksistē
|
mirror_prune_desc=Izdzēst visas ārējās atsauces, kas ārējā repozitorijā vairs neeksistē
|
||||||
mirror_interval=Spoguļošanas intervāls (derīgas laika vienības ir 'h', 'm', 's'). Norādiet 0, lai atslēgtu automātisku spoguļošanu. (Minimālais intervāls: %s)
|
|
||||||
mirror_interval_invalid=Nekorekts spoguļošanas intervāls.
|
mirror_interval_invalid=Nekorekts spoguļošanas intervāls.
|
||||||
mirror_address=Spoguļa adrese
|
mirror_address=Spoguļa adrese
|
||||||
mirror_address_desc=Pieslēgšanās rekvizītus norādiet autorizācijas sadaļā.
|
mirror_address_desc=Pieslēgšanās rekvizītus norādiet autorizācijas sadaļā.
|
||||||
|
|
|
@ -861,8 +861,9 @@ default_branch=Ramo principal
|
||||||
default_branch_helper=O ramo principal é o ramo base para pedidos de integração e cometimentos.
|
default_branch_helper=O ramo principal é o ramo base para pedidos de integração e cometimentos.
|
||||||
mirror_prune=Podar
|
mirror_prune=Podar
|
||||||
mirror_prune_desc=Remover referências obsoletas de seguimento remoto
|
mirror_prune_desc=Remover referências obsoletas de seguimento remoto
|
||||||
mirror_interval=Intervalo entre sincronizações (as unidades de tempo válidas são 'h', 'm' e 's'). O valor zero desabilita a sincronização automática. (Intervalo mínimo: %s)
|
mirror_interval=Intervalo entre sincronizações (as unidades de tempo válidas são 'h', 'm' e 's'). O valor zero desabilita a sincronização periódica. (Intervalo mínimo: %s)
|
||||||
mirror_interval_invalid=O intervalo entre sincronizações não é válido.
|
mirror_interval_invalid=O intervalo entre sincronizações não é válido.
|
||||||
|
mirror_sync_on_commit=Sincronizar quando forem enviados cometimentos
|
||||||
mirror_address=Clonar a partir do URL
|
mirror_address=Clonar a partir do URL
|
||||||
mirror_address_desc=Coloque, na secção de Autorização, as credenciais que, eventualmente, sejam necessárias.
|
mirror_address_desc=Coloque, na secção de Autorização, as credenciais que, eventualmente, sejam necessárias.
|
||||||
mirror_address_url_invalid=O URL fornecido é inválido. Tem que codificar adequadamente todos os componentes do URL.
|
mirror_address_url_invalid=O URL fornecido é inválido. Tem que codificar adequadamente todos os componentes do URL.
|
||||||
|
|
|
@ -861,7 +861,6 @@ default_branch=默认分支
|
||||||
default_branch_helper=默认分支是用于合并请求和代码提交的基础分支。
|
default_branch_helper=默认分支是用于合并请求和代码提交的基础分支。
|
||||||
mirror_prune=修剪
|
mirror_prune=修剪
|
||||||
mirror_prune_desc=删除过时的远程跟踪引用
|
mirror_prune_desc=删除过时的远程跟踪引用
|
||||||
mirror_interval=镜像间隔 (有效的时间单位是 'h', 'm', 's')。0 禁用自动同步 (最短间隔: %s)
|
|
||||||
mirror_interval_invalid=镜像间隔无效。
|
mirror_interval_invalid=镜像间隔无效。
|
||||||
mirror_address=从URL克隆
|
mirror_address=从URL克隆
|
||||||
mirror_address_desc=在授权框中输入必要的凭据。
|
mirror_address_desc=在授权框中输入必要的凭据。
|
||||||
|
|
|
@ -861,7 +861,6 @@ default_branch=預設分支
|
||||||
default_branch_helper=預設分支是合併請求和提交程式碼的基礎分支。
|
default_branch_helper=預設分支是合併請求和提交程式碼的基礎分支。
|
||||||
mirror_prune=裁減
|
mirror_prune=裁減
|
||||||
mirror_prune_desc=刪除過時的遠端追蹤參考
|
mirror_prune_desc=刪除過時的遠端追蹤參考
|
||||||
mirror_interval=鏡像間隔 (有效時間單位為 'h'、'm'、's'),設為 0 以停用自動同步。(最小間隔: %s)
|
|
||||||
mirror_interval_invalid=鏡像週期無效
|
mirror_interval_invalid=鏡像週期無效
|
||||||
mirror_address=從 URL Clone
|
mirror_address=從 URL Clone
|
||||||
mirror_address_desc=在授權資訊中填入必要的資料。
|
mirror_address_desc=在授權資訊中填入必要的資料。
|
||||||
|
|
|
@ -11,8 +11,8 @@ import (
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
mirror_module "code.gitea.io/gitea/modules/mirror"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
mirror_service "code.gitea.io/gitea/services/mirror"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MirrorSync adds a mirrored repository to the sync queue
|
// MirrorSync adds a mirrored repository to the sync queue
|
||||||
|
@ -59,7 +59,7 @@ func MirrorSync(ctx *context.APIContext) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mirror_service.StartToMirror(repo.ID)
|
mirror_module.AddPullMirrorToQueue(repo.ID)
|
||||||
|
|
||||||
ctx.Status(http.StatusOK)
|
ctx.Status(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,8 @@ package routers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||||
|
@ -158,14 +156,8 @@ func GlobalInitInstalled(ctx context.Context) {
|
||||||
|
|
||||||
mustInitCtx(ctx, syncAppPathForGit)
|
mustInitCtx(ctx, syncAppPathForGit)
|
||||||
|
|
||||||
if setting.SSH.StartBuiltinServer {
|
mustInit(ssh.Init)
|
||||||
ssh.Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
|
|
||||||
log.Info("SSH server started on %s. Cipher list (%v), key exchange algorithms (%v), MACs (%v)",
|
|
||||||
net.JoinHostPort(setting.SSH.ListenHost, strconv.Itoa(setting.SSH.ListenPort)),
|
|
||||||
setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
|
|
||||||
} else {
|
|
||||||
ssh.Unused()
|
|
||||||
}
|
|
||||||
auth.Init()
|
auth.Init()
|
||||||
svg.Init()
|
svg.Init()
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/indexer/stats"
|
"code.gitea.io/gitea/modules/indexer/stats"
|
||||||
"code.gitea.io/gitea/modules/lfs"
|
"code.gitea.io/gitea/modules/lfs"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
mirror_module "code.gitea.io/gitea/modules/mirror"
|
||||||
"code.gitea.io/gitea/modules/repository"
|
"code.gitea.io/gitea/modules/repository"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/structs"
|
"code.gitea.io/gitea/modules/structs"
|
||||||
|
@ -272,7 +273,7 @@ func SettingsPost(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mirror_service.StartToMirror(repo.ID)
|
mirror_module.AddPullMirrorToQueue(repo.ID)
|
||||||
|
|
||||||
ctx.Flash.Info(ctx.Tr("repo.settings.mirror_sync_in_progress"))
|
ctx.Flash.Info(ctx.Tr("repo.settings.mirror_sync_in_progress"))
|
||||||
ctx.Redirect(repo.Link() + "/settings")
|
ctx.Redirect(repo.Link() + "/settings")
|
||||||
|
@ -289,7 +290,7 @@ func SettingsPost(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mirror_service.AddPushMirrorToQueue(m.ID)
|
mirror_module.AddPushMirrorToQueue(m.ID)
|
||||||
|
|
||||||
ctx.Flash.Info(ctx.Tr("repo.settings.mirror_sync_in_progress"))
|
ctx.Flash.Info(ctx.Tr("repo.settings.mirror_sync_in_progress"))
|
||||||
ctx.Redirect(repo.Link() + "/settings")
|
ctx.Redirect(repo.Link() + "/settings")
|
||||||
|
@ -357,10 +358,11 @@ func SettingsPost(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &repo_model.PushMirror{
|
m := &repo_model.PushMirror{
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
Repo: repo,
|
Repo: repo,
|
||||||
RemoteName: fmt.Sprintf("remote_mirror_%s", remoteSuffix),
|
RemoteName: fmt.Sprintf("remote_mirror_%s", remoteSuffix),
|
||||||
Interval: interval,
|
SyncOnCommit: form.PushMirrorSyncOnCommit,
|
||||||
|
Interval: interval,
|
||||||
}
|
}
|
||||||
if err := repo_model.InsertPushMirror(m); err != nil {
|
if err := repo_model.InsertPushMirror(m); err != nil {
|
||||||
ctx.ServerError("InsertPushMirror", err)
|
ctx.ServerError("InsertPushMirror", err)
|
||||||
|
|
|
@ -115,23 +115,24 @@ func ParseRemoteAddr(remoteAddr, authUsername, authPassword string) (string, err
|
||||||
|
|
||||||
// RepoSettingForm form for changing repository settings
|
// RepoSettingForm form for changing repository settings
|
||||||
type RepoSettingForm struct {
|
type RepoSettingForm struct {
|
||||||
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
|
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
|
||||||
Description string `binding:"MaxSize(255)"`
|
Description string `binding:"MaxSize(255)"`
|
||||||
Website string `binding:"ValidUrl;MaxSize(255)"`
|
Website string `binding:"ValidUrl;MaxSize(255)"`
|
||||||
Interval string
|
Interval string
|
||||||
MirrorAddress string
|
MirrorAddress string
|
||||||
MirrorUsername string
|
MirrorUsername string
|
||||||
MirrorPassword string
|
MirrorPassword string
|
||||||
LFS bool `form:"mirror_lfs"`
|
LFS bool `form:"mirror_lfs"`
|
||||||
LFSEndpoint string `form:"mirror_lfs_endpoint"`
|
LFSEndpoint string `form:"mirror_lfs_endpoint"`
|
||||||
PushMirrorID string
|
PushMirrorID string
|
||||||
PushMirrorAddress string
|
PushMirrorAddress string
|
||||||
PushMirrorUsername string
|
PushMirrorUsername string
|
||||||
PushMirrorPassword string
|
PushMirrorPassword string
|
||||||
PushMirrorInterval string
|
PushMirrorSyncOnCommit bool
|
||||||
Private bool
|
PushMirrorInterval string
|
||||||
Template bool
|
Private bool
|
||||||
EnablePrune bool
|
Template bool
|
||||||
|
EnablePrune bool
|
||||||
|
|
||||||
// Advanced settings
|
// Advanced settings
|
||||||
EnableWiki bool
|
EnableWiki bool
|
||||||
|
|
|
@ -11,38 +11,21 @@ import (
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/modules/graceful"
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
mirror_module "code.gitea.io/gitea/modules/mirror"
|
||||||
"code.gitea.io/gitea/modules/queue"
|
"code.gitea.io/gitea/modules/queue"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
var mirrorQueue queue.UniqueQueue
|
|
||||||
|
|
||||||
// SyncType type of sync request
|
|
||||||
type SyncType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// PullMirrorType for pull mirrors
|
|
||||||
PullMirrorType SyncType = iota
|
|
||||||
// PushMirrorType for push mirrors
|
|
||||||
PushMirrorType
|
|
||||||
)
|
|
||||||
|
|
||||||
// SyncRequest for the mirror queue
|
|
||||||
type SyncRequest struct {
|
|
||||||
Type SyncType
|
|
||||||
ReferenceID int64 // RepoID for pull mirror, MirrorID fro push mirror
|
|
||||||
}
|
|
||||||
|
|
||||||
// doMirrorSync causes this request to mirror itself
|
// doMirrorSync causes this request to mirror itself
|
||||||
func doMirrorSync(ctx context.Context, req *SyncRequest) {
|
func doMirrorSync(ctx context.Context, req *mirror_module.SyncRequest) {
|
||||||
if req.ReferenceID == 0 {
|
if req.ReferenceID == 0 {
|
||||||
log.Warn("Skipping mirror sync request, no mirror ID was specified")
|
log.Warn("Skipping mirror sync request, no mirror ID was specified")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch req.Type {
|
switch req.Type {
|
||||||
case PushMirrorType:
|
case mirror_module.PushMirrorType:
|
||||||
_ = SyncPushMirror(ctx, req.ReferenceID)
|
_ = SyncPushMirror(ctx, req.ReferenceID)
|
||||||
case PullMirrorType:
|
case mirror_module.PullMirrorType:
|
||||||
_ = SyncPullMirror(ctx, req.ReferenceID)
|
_ = SyncPullMirror(ctx, req.ReferenceID)
|
||||||
default:
|
default:
|
||||||
log.Error("Unknown Request type in queue: %v for MirrorID[%d]", req.Type, req.ReferenceID)
|
log.Error("Unknown Request type in queue: %v for MirrorID[%d]", req.Type, req.ReferenceID)
|
||||||
|
@ -60,28 +43,26 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
|
||||||
log.Trace("Doing: Update")
|
log.Trace("Doing: Update")
|
||||||
|
|
||||||
handler := func(idx int, bean interface{}) error {
|
handler := func(idx int, bean interface{}) error {
|
||||||
var item SyncRequest
|
|
||||||
var repo *repo_model.Repository
|
var repo *repo_model.Repository
|
||||||
|
var mirrorType mirror_module.SyncType
|
||||||
|
var referenceID int64
|
||||||
|
|
||||||
if m, ok := bean.(*repo_model.Mirror); ok {
|
if m, ok := bean.(*repo_model.Mirror); ok {
|
||||||
if m.GetRepository() == nil {
|
if m.GetRepository() == nil {
|
||||||
log.Error("Disconnected mirror found: %d", m.ID)
|
log.Error("Disconnected mirror found: %d", m.ID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
repo = m.Repo
|
repo = m.Repo
|
||||||
item = SyncRequest{
|
mirrorType = mirror_module.PullMirrorType
|
||||||
Type: PullMirrorType,
|
referenceID = m.RepoID
|
||||||
ReferenceID: m.RepoID,
|
|
||||||
}
|
|
||||||
} else if m, ok := bean.(*repo_model.PushMirror); ok {
|
} else if m, ok := bean.(*repo_model.PushMirror); ok {
|
||||||
if m.GetRepository() == nil {
|
if m.GetRepository() == nil {
|
||||||
log.Error("Disconnected push-mirror found: %d", m.ID)
|
log.Error("Disconnected push-mirror found: %d", m.ID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
repo = m.Repo
|
repo = m.Repo
|
||||||
item = SyncRequest{
|
mirrorType = mirror_module.PushMirrorType
|
||||||
Type: PushMirrorType,
|
referenceID = m.ID
|
||||||
ReferenceID: m.ID,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log.Error("Unknown bean: %v", bean)
|
log.Error("Unknown bean: %v", bean)
|
||||||
return nil
|
return nil
|
||||||
|
@ -95,9 +76,9 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push to the Queue
|
// Push to the Queue
|
||||||
if err := mirrorQueue.Push(&item); err != nil {
|
if err := mirror_module.PushToQueue(mirrorType, referenceID); err != nil {
|
||||||
if err == queue.ErrAlreadyInQueue {
|
if err == queue.ErrAlreadyInQueue {
|
||||||
if item.Type == PushMirrorType {
|
if mirrorType == mirror_module.PushMirrorType {
|
||||||
log.Trace("PushMirrors for %-v already queued for sync", repo)
|
log.Trace("PushMirrors for %-v already queued for sync", repo)
|
||||||
} else {
|
} else {
|
||||||
log.Trace("PullMirrors for %-v already queued for sync", repo)
|
log.Trace("PullMirrors for %-v already queued for sync", repo)
|
||||||
|
@ -142,7 +123,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
|
||||||
|
|
||||||
func queueHandle(data ...queue.Data) []queue.Data {
|
func queueHandle(data ...queue.Data) []queue.Data {
|
||||||
for _, datum := range data {
|
for _, datum := range data {
|
||||||
req := datum.(*SyncRequest)
|
req := datum.(*mirror_module.SyncRequest)
|
||||||
doMirrorSync(graceful.GetManager().ShutdownContext(), req)
|
doMirrorSync(graceful.GetManager().ShutdownContext(), req)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -150,43 +131,5 @@ func queueHandle(data ...queue.Data) []queue.Data {
|
||||||
|
|
||||||
// InitSyncMirrors initializes a go routine to sync the mirrors
|
// InitSyncMirrors initializes a go routine to sync the mirrors
|
||||||
func InitSyncMirrors() {
|
func InitSyncMirrors() {
|
||||||
if !setting.Mirror.Enabled {
|
mirror_module.StartSyncMirrors(queueHandle)
|
||||||
return
|
|
||||||
}
|
|
||||||
mirrorQueue = queue.CreateUniqueQueue("mirror", queueHandle, new(SyncRequest))
|
|
||||||
|
|
||||||
go graceful.GetManager().RunWithShutdownFns(mirrorQueue.Run)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartToMirror adds repoID to mirror queue
|
|
||||||
func StartToMirror(repoID int64) {
|
|
||||||
if !setting.Mirror.Enabled {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
err := mirrorQueue.Push(&SyncRequest{
|
|
||||||
Type: PullMirrorType,
|
|
||||||
ReferenceID: repoID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Unable to push sync request for to the queue for pull mirror repo[%d]: Error: %v", repoID, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddPushMirrorToQueue adds the push mirror to the queue
|
|
||||||
func AddPushMirrorToQueue(mirrorID int64) {
|
|
||||||
if !setting.Mirror.Enabled {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
err := mirrorQueue.Push(&SyncRequest{
|
|
||||||
Type: PushMirrorType,
|
|
||||||
ReferenceID: mirrorID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Unable to push sync request to the queue for pull mirror repo[%d]: Error: %v", mirrorID, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,9 +32,9 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .IsFork}}
|
{{if .IsFork}}
|
||||||
{{svg "octicon-repo-forked"}}
|
<span class="tooltip" data-content="{{$.locale.Tr "repo.fork"}}" data-position="bottom center">{{svg "octicon-repo-forked"}}</span>
|
||||||
{{else if .IsMirror}}
|
{{else if .IsMirror}}
|
||||||
{{svg "octicon-mirror"}}
|
<span class="tooltip" data-content="{{$.locale.Tr "mirror"}}" data-position="bottom center">{{svg "octicon-mirror"}}</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -24,7 +24,11 @@
|
||||||
{{else if .Issue.IsClosed}}
|
{{else if .Issue.IsClosed}}
|
||||||
<div class="ui red large label">{{if .Issue.IsPull}}{{svg "octicon-git-pull-request"}}{{else}}{{svg "octicon-issue-closed"}}{{end}} {{.locale.Tr "repo.issues.closed_title"}}</div>
|
<div class="ui red large label">{{if .Issue.IsPull}}{{svg "octicon-git-pull-request"}}{{else}}{{svg "octicon-issue-closed"}}{{end}} {{.locale.Tr "repo.issues.closed_title"}}</div>
|
||||||
{{else if .Issue.IsPull}}
|
{{else if .Issue.IsPull}}
|
||||||
<div class="ui green large label">{{svg "octicon-git-pull-request"}} {{.locale.Tr "repo.issues.open_title"}}</div>
|
{{if .IsPullWorkInProgress}}
|
||||||
|
<div class="ui grey large label">{{svg "octicon-git-pull-request-draft"}} {{.locale.Tr "repo.issues.draft_title"}}</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="ui green large label">{{svg "octicon-git-pull-request"}} {{.locale.Tr "repo.issues.open_title"}}</div>
|
||||||
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="ui green large label">{{svg "octicon-issue-opened"}} {{.locale.Tr "repo.issues.open_title"}}</div>
|
<div class="ui green large label">{{svg "octicon-issue-opened"}} {{.locale.Tr "repo.issues.open_title"}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -219,6 +219,12 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input id="push_mirror_sync_on_commit" name="push_mirror_sync_on_commit" type="checkbox" value="{{.push_mirror_sync_on_commit}}">
|
||||||
|
<label for="push_mirror_sync_on_commit">{{.locale.Tr "repo.mirror_sync_on_commit"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="inline field {{if .Err_PushMirrorInterval}}error{{end}}">
|
<div class="inline field {{if .Err_PushMirrorInterval}}error{{end}}">
|
||||||
<label for="push_mirror_interval">{{.locale.Tr "repo.mirror_interval" .MinimumMirrorInterval}}</label>
|
<label for="push_mirror_interval">{{.locale.Tr "repo.mirror_interval" .MinimumMirrorInterval}}</label>
|
||||||
<input id="push_mirror_interval" name="push_mirror_interval" value="{{if .push_mirror_interval}}{{.push_mirror_interval}}{{else}}{{.DefaultMirrorInterval}}{{end}}">
|
<input id="push_mirror_interval" name="push_mirror_interval" value="{{if .push_mirror_interval}}{{.push_mirror_interval}}{{else}}{{.DefaultMirrorInterval}}{{end}}">
|
||||||
|
|
|
@ -17,7 +17,11 @@
|
||||||
{{if .IsClosed}}
|
{{if .IsClosed}}
|
||||||
{{svg "octicon-git-pull-request" 16 "text red"}}
|
{{svg "octicon-git-pull-request" 16 "text red"}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{svg "octicon-git-pull-request" 16 "text green"}}
|
{{if .PullRequest.IsWorkInProgress}}
|
||||||
|
{{svg "octicon-git-pull-request-draft" 16 "text grey"}}
|
||||||
|
{{else}}
|
||||||
|
{{svg "octicon-git-pull-request" 16 "text green"}}
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
.table.segment {
|
.table.segment {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
overflow-x: scroll;
|
overflow-x: auto;
|
||||||
|
|
||||||
&:not(.striped) {
|
&:not(.striped) {
|
||||||
thead {
|
thead {
|
||||||
|
|
|
@ -3352,7 +3352,7 @@ td.blob-excerpt {
|
||||||
.commit-header-row {
|
.commit-header-row {
|
||||||
.ui.horizontal.list {
|
.ui.horizontal.list {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-x: scroll;
|
overflow-x: auto;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
|
@ -3401,7 +3401,7 @@ td.blob-excerpt {
|
||||||
}
|
}
|
||||||
|
|
||||||
.commit-table {
|
.commit-table {
|
||||||
overflow-x: scroll;
|
overflow-x: auto;
|
||||||
|
|
||||||
td.sha,
|
td.sha,
|
||||||
th.sha {
|
th.sha {
|
||||||
|
|
|
@ -170,5 +170,5 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#notification_div .tab.segment {
|
#notification_div .tab.segment {
|
||||||
overflow-x: scroll;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#git-graph-container {
|
#git-graph-container {
|
||||||
overflow-x: scroll;
|
overflow-x: auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 350px;
|
min-height: 350px;
|
||||||
|
|
||||||
|
|
Reference in New Issue