- Refactor code to use sync.UniqueQueue - Closes #3509tags/v1.21.12.1
| @@ -8,10 +8,14 @@ end_of_line = lf | |||||
| insert_final_newline = true | insert_final_newline = true | ||||
| trim_trailing_whitespace = true | trim_trailing_whitespace = true | ||||
| [*.{go,tmpl}] | |||||
| [*.go] | |||||
| indent_style = tab | indent_style = tab | ||||
| indent_size = 4 | indent_size = 4 | ||||
| [*.tmpl] | |||||
| indent_style = tab | |||||
| indent_size = 2 | |||||
| [*.{less,yml}] | [*.{less,yml}] | ||||
| indent_style = space | indent_style = space | ||||
| indent_size = 2 | indent_size = 2 | ||||
| @@ -3,7 +3,7 @@ Gogs - Go Git Service [ |  | ||||
| ##### Current tip version: 0.9.95 (see [Releases](https://github.com/gogits/gogs/releases) for binary versions) | |||||
| ##### Current tip version: 0.9.96 (see [Releases](https://github.com/gogits/gogs/releases) for binary versions) | |||||
| | Web | UI | Preview | | | Web | UI | Preview | | ||||
| |:-------------:|:-------:|:-------:| | |:-------------:|:-------:|:-------:| | ||||
| @@ -17,8 +17,10 @@ ANSI_CHARSET = | |||||
| FORCE_PRIVATE = false | FORCE_PRIVATE = false | ||||
| ; Global maximum creation limit of repository per user, -1 means no limit | ; Global maximum creation limit of repository per user, -1 means no limit | ||||
| MAX_CREATION_LIMIT = -1 | MAX_CREATION_LIMIT = -1 | ||||
| ; Patch test queue length, make it as large as possible | |||||
| PULL_REQUEST_QUEUE_LENGTH = 10000 | |||||
| ; Mirror sync queue length, increase if mirror syncing starts hanging | |||||
| MIRROR_QUEUE_LENGTH = 1000 | |||||
| ; Patch test queue length, increase if pull request patch testing starts hanging | |||||
| PULL_REQUEST_QUEUE_LENGTH = 1000 | |||||
| ; Preferred Licenses to place at the top of the List | ; Preferred Licenses to place at the top of the List | ||||
| ; Name must match file name in conf/license or custom/conf/license | ; Name must match file name in conf/license or custom/conf/license | ||||
| PREFERRED_LICENSES = Apache License 2.0,MIT License | PREFERRED_LICENSES = Apache License 2.0,MIT License | ||||
| @@ -184,7 +186,7 @@ ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false | |||||
| ENABLE_CAPTCHA = true | ENABLE_CAPTCHA = true | ||||
| [webhook] | [webhook] | ||||
| ; Hook task queue length | |||||
| ; Hook task queue length, increase if webhook shooting starts hanging | |||||
| QUEUE_LENGTH = 1000 | QUEUE_LENGTH = 1000 | ||||
| ; Deliver timeout in seconds | ; Deliver timeout in seconds | ||||
| DELIVER_TIMEOUT = 5 | DELIVER_TIMEOUT = 5 | ||||
| @@ -389,7 +391,7 @@ GC = 60 | |||||
| [mirror] | [mirror] | ||||
| ; Default interval in hours between each check | ; Default interval in hours between each check | ||||
| DEFAULT_INTERVAL = 24 | |||||
| DEFAULT_INTERVAL = 8 | |||||
| [api] | [api] | ||||
| ; Max number of items will response in a page | ; Max number of items will response in a page | ||||
| @@ -370,6 +370,7 @@ mirror_prune_desc = Remove any remote-tracking references that no longer exist o | |||||
| mirror_interval = Mirror Interval (hour) | mirror_interval = Mirror Interval (hour) | ||||
| mirror_address = Mirror Address | mirror_address = Mirror Address | ||||
| mirror_address_desc = Please include necessary user credentials in the address. | mirror_address_desc = Please include necessary user credentials in the address. | ||||
| mirror_last_synced = Last Synced | |||||
| watchers = Watchers | watchers = Watchers | ||||
| stargazers = Stargazers | stargazers = Stargazers | ||||
| forks = Forks | forks = Forks | ||||
| @@ -631,6 +632,9 @@ settings.collaboration.undefined = Undefined | |||||
| settings.hooks = Webhooks | settings.hooks = Webhooks | ||||
| settings.githooks = Git Hooks | settings.githooks = Git Hooks | ||||
| settings.basic_settings = Basic Settings | settings.basic_settings = Basic Settings | ||||
| settings.mirror_settings = Mirror Settings | |||||
| settings.sync_mirror = Sync Now | |||||
| settings.mirror_sync_in_progress = Mirror syncing is in progress, please refresh page in about a minute. | |||||
| settings.site = Official Site | settings.site = Official Site | ||||
| settings.update_settings = Update Settings | settings.update_settings = Update Settings | ||||
| settings.change_reponame_prompt = This change will affect how links relate to the repository. | settings.change_reponame_prompt = This change will affect how links relate to the repository. | ||||
| @@ -17,7 +17,7 @@ import ( | |||||
| "github.com/gogits/gogs/modules/setting" | "github.com/gogits/gogs/modules/setting" | ||||
| ) | ) | ||||
| const APP_VER = "0.9.95.0830" | |||||
| const APP_VER = "0.9.96.0830" | |||||
| func init() { | func init() { | ||||
| runtime.GOMAXPROCS(runtime.NumCPU()) | runtime.GOMAXPROCS(runtime.NumCPU()) | ||||
| @@ -381,7 +381,7 @@ func (repo *Repository) IssueStats(uid int64, filterMode int, isPull bool) (int6 | |||||
| } | } | ||||
| func (repo *Repository) GetMirror() (err error) { | func (repo *Repository) GetMirror() (err error) { | ||||
| repo.Mirror, err = GetMirror(repo.ID) | |||||
| repo.Mirror, err = GetMirrorByRepoID(repo.ID) | |||||
| return err | return err | ||||
| } | } | ||||
| @@ -574,136 +574,6 @@ func (repo *Repository) CloneLink() (cl *CloneLink) { | |||||
| return repo.cloneLink(false) | return repo.cloneLink(false) | ||||
| } | } | ||||
| // Mirror represents a mirror information of repository. | |||||
| type Mirror struct { | |||||
| ID int64 `xorm:"pk autoincr"` | |||||
| RepoID int64 | |||||
| Repo *Repository `xorm:"-"` | |||||
| Interval int // Hour. | |||||
| EnablePrune bool `xorm:"NOT NULL DEFAULT true"` | |||||
| Updated time.Time `xorm:"-"` | |||||
| UpdatedUnix int64 | |||||
| NextUpdate time.Time `xorm:"-"` | |||||
| NextUpdateUnix int64 | |||||
| address string `xorm:"-"` | |||||
| } | |||||
| func (m *Mirror) BeforeInsert() { | |||||
| m.NextUpdateUnix = m.NextUpdate.Unix() | |||||
| } | |||||
| func (m *Mirror) BeforeUpdate() { | |||||
| m.UpdatedUnix = time.Now().Unix() | |||||
| m.NextUpdateUnix = m.NextUpdate.Unix() | |||||
| } | |||||
| func (m *Mirror) AfterSet(colName string, _ xorm.Cell) { | |||||
| var err error | |||||
| switch colName { | |||||
| case "repo_id": | |||||
| m.Repo, err = GetRepositoryByID(m.RepoID) | |||||
| if err != nil { | |||||
| log.Error(3, "GetRepositoryByID[%d]: %v", m.ID, err) | |||||
| } | |||||
| case "updated_unix": | |||||
| m.Updated = time.Unix(m.UpdatedUnix, 0).Local() | |||||
| case "next_updated_unix": | |||||
| m.NextUpdate = time.Unix(m.NextUpdateUnix, 0).Local() | |||||
| } | |||||
| } | |||||
| func (m *Mirror) readAddress() { | |||||
| if len(m.address) > 0 { | |||||
| return | |||||
| } | |||||
| cfg, err := ini.Load(m.Repo.GitConfigPath()) | |||||
| if err != nil { | |||||
| log.Error(4, "Load: %v", err) | |||||
| return | |||||
| } | |||||
| m.address = cfg.Section("remote \"origin\"").Key("url").Value() | |||||
| } | |||||
| // HandleCloneUserCredentials replaces user credentials from HTTP/HTTPS URL | |||||
| // with placeholder <credentials>. | |||||
| // It will fail for any other forms of clone addresses. | |||||
| func HandleCloneUserCredentials(url string, mosaics bool) string { | |||||
| i := strings.Index(url, "@") | |||||
| if i == -1 { | |||||
| return url | |||||
| } | |||||
| start := strings.Index(url, "://") | |||||
| if start == -1 { | |||||
| return url | |||||
| } | |||||
| if mosaics { | |||||
| return url[:start+3] + "<credentials>" + url[i:] | |||||
| } | |||||
| return url[:start+3] + url[i+1:] | |||||
| } | |||||
| // Address returns mirror address from Git repository config without credentials. | |||||
| func (m *Mirror) Address() string { | |||||
| m.readAddress() | |||||
| return HandleCloneUserCredentials(m.address, false) | |||||
| } | |||||
| // FullAddress returns mirror address from Git repository config. | |||||
| func (m *Mirror) FullAddress() string { | |||||
| m.readAddress() | |||||
| return m.address | |||||
| } | |||||
| // SaveAddress writes new address to Git repository config. | |||||
| func (m *Mirror) SaveAddress(addr string) error { | |||||
| configPath := m.Repo.GitConfigPath() | |||||
| cfg, err := ini.Load(configPath) | |||||
| if err != nil { | |||||
| return fmt.Errorf("Load: %v", err) | |||||
| } | |||||
| cfg.Section("remote \"origin\"").Key("url").SetValue(addr) | |||||
| return cfg.SaveToIndent(configPath, "\t") | |||||
| } | |||||
| func getMirror(e Engine, repoId int64) (*Mirror, error) { | |||||
| m := &Mirror{RepoID: repoId} | |||||
| has, err := e.Get(m) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } else if !has { | |||||
| return nil, ErrMirrorNotExist | |||||
| } | |||||
| return m, nil | |||||
| } | |||||
| // GetMirror returns mirror object by given repository ID. | |||||
| func GetMirror(repoId int64) (*Mirror, error) { | |||||
| return getMirror(x, repoId) | |||||
| } | |||||
| func updateMirror(e Engine, m *Mirror) error { | |||||
| _, err := e.Id(m.ID).AllCols().Update(m) | |||||
| return err | |||||
| } | |||||
| func UpdateMirror(m *Mirror) error { | |||||
| return updateMirror(x, m) | |||||
| } | |||||
| func DeleteMirrorByRepoID(repoID int64) error { | |||||
| _, err := x.Delete(&Mirror{RepoID: repoID}) | |||||
| return err | |||||
| } | |||||
| func createUpdateHook(repoPath string) error { | |||||
| return git.SetUpdateHook(repoPath, | |||||
| fmt.Sprintf(_TPL_UPDATE_HOOK, setting.ScriptType, "\""+setting.AppPath+"\"", setting.CustomConf)) | |||||
| } | |||||
| type MigrateRepoOptions struct { | type MigrateRepoOptions struct { | ||||
| Name string | Name string | ||||
| Description string | Description string | ||||
| @@ -839,6 +709,11 @@ func cleanUpMigrateGitConfig(configPath string) error { | |||||
| return nil | return nil | ||||
| } | } | ||||
| func createUpdateHook(repoPath string) error { | |||||
| return git.SetUpdateHook(repoPath, | |||||
| fmt.Sprintf(_TPL_UPDATE_HOOK, setting.ScriptType, "\""+setting.AppPath+"\"", setting.CustomConf)) | |||||
| } | |||||
| // Finish migrating repository and/or wiki with things that don't need to be done for mirrors. | // Finish migrating repository and/or wiki with things that don't need to be done for mirrors. | ||||
| func CleanUpMigrateInfo(repo *Repository) (*Repository, error) { | func CleanUpMigrateInfo(repo *Repository) (*Repository, error) { | ||||
| repoPath := repo.RepoPath() | repoPath := repo.RepoPath() | ||||
| @@ -1748,70 +1623,6 @@ const ( | |||||
| _CHECK_REPOs = "check_repos" | _CHECK_REPOs = "check_repos" | ||||
| ) | ) | ||||
| // MirrorUpdate checks and updates mirror repositories. | |||||
| func MirrorUpdate() { | |||||
| if taskStatusTable.IsRunning(_MIRROR_UPDATE) { | |||||
| return | |||||
| } | |||||
| taskStatusTable.Start(_MIRROR_UPDATE) | |||||
| defer taskStatusTable.Stop(_MIRROR_UPDATE) | |||||
| log.Trace("Doing: MirrorUpdate") | |||||
| mirrors := make([]*Mirror, 0, 10) | |||||
| if err := x.Where("next_update_unix<=?", time.Now().Unix()).Iterate(new(Mirror), func(idx int, bean interface{}) error { | |||||
| m := bean.(*Mirror) | |||||
| if m.Repo == nil { | |||||
| log.Error(4, "Disconnected mirror repository found: %d", m.ID) | |||||
| return nil | |||||
| } | |||||
| repoPath := m.Repo.RepoPath() | |||||
| wikiPath := m.Repo.WikiPath() | |||||
| timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second | |||||
| gitArgs := []string{"remote", "update"} | |||||
| if m.EnablePrune { | |||||
| gitArgs = append(gitArgs, "--prune") | |||||
| } | |||||
| if _, stderr, err := process.ExecDir( | |||||
| timeout, repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath), | |||||
| "git", gitArgs...); err != nil { | |||||
| desc := fmt.Sprintf("Fail to update mirror repository(%s): %s", repoPath, stderr) | |||||
| log.Error(4, desc) | |||||
| if err = CreateRepositoryNotice(desc); err != nil { | |||||
| log.Error(4, "CreateRepositoryNotice: %v", err) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| if m.Repo.HasWiki() { | |||||
| if _, stderr, err := process.ExecDir( | |||||
| timeout, wikiPath, fmt.Sprintf("MirrorUpdate: %s", wikiPath), | |||||
| "git", "remote", "update", "--prune"); err != nil { | |||||
| desc := fmt.Sprintf("Fail to update mirror wiki repository(%s): %s", wikiPath, stderr) | |||||
| log.Error(4, desc) | |||||
| if err = CreateRepositoryNotice(desc); err != nil { | |||||
| log.Error(4, "CreateRepositoryNotice: %v", err) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| } | |||||
| m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour) | |||||
| mirrors = append(mirrors, m) | |||||
| return nil | |||||
| }); err != nil { | |||||
| log.Error(4, "MirrorUpdate: %v", err) | |||||
| } | |||||
| for i := range mirrors { | |||||
| if err := UpdateMirror(mirrors[i]); err != nil { | |||||
| log.Error(4, "UpdateMirror[%d]: %v", mirrors[i].ID, err) | |||||
| } | |||||
| } | |||||
| } | |||||
| // GitFsck calls 'git fsck' to check repository health. | // GitFsck calls 'git fsck' to check repository health. | ||||
| func GitFsck() { | func GitFsck() { | ||||
| if taskStatusTable.IsRunning(_GIT_FSCK) { | if taskStatusTable.IsRunning(_GIT_FSCK) { | ||||
| @@ -0,0 +1,243 @@ | |||||
| // Copyright 2016 The Gogs 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 models | |||||
| import ( | |||||
| "fmt" | |||||
| "strings" | |||||
| "time" | |||||
| "github.com/Unknwon/com" | |||||
| "github.com/go-xorm/xorm" | |||||
| "gopkg.in/ini.v1" | |||||
| "github.com/gogits/gogs/modules/log" | |||||
| "github.com/gogits/gogs/modules/process" | |||||
| "github.com/gogits/gogs/modules/setting" | |||||
| "github.com/gogits/gogs/modules/sync" | |||||
| ) | |||||
| var MirrorQueue = sync.NewUniqueQueue(setting.Repository.MirrorQueueLength) | |||||
| // Mirror represents mirror information of a repository. | |||||
| type Mirror struct { | |||||
| ID int64 `xorm:"pk autoincr"` | |||||
| RepoID int64 | |||||
| Repo *Repository `xorm:"-"` | |||||
| Interval int // Hour. | |||||
| EnablePrune bool `xorm:"NOT NULL DEFAULT true"` | |||||
| Updated time.Time `xorm:"-"` | |||||
| UpdatedUnix int64 | |||||
| NextUpdate time.Time `xorm:"-"` | |||||
| NextUpdateUnix int64 | |||||
| address string `xorm:"-"` | |||||
| } | |||||
| func (m *Mirror) BeforeInsert() { | |||||
| m.NextUpdateUnix = m.NextUpdate.Unix() | |||||
| } | |||||
| func (m *Mirror) BeforeUpdate() { | |||||
| m.UpdatedUnix = time.Now().Unix() | |||||
| m.NextUpdateUnix = m.NextUpdate.Unix() | |||||
| } | |||||
| func (m *Mirror) AfterSet(colName string, _ xorm.Cell) { | |||||
| var err error | |||||
| switch colName { | |||||
| case "repo_id": | |||||
| m.Repo, err = GetRepositoryByID(m.RepoID) | |||||
| if err != nil { | |||||
| log.Error(3, "GetRepositoryByID[%d]: %v", m.ID, err) | |||||
| } | |||||
| case "updated_unix": | |||||
| m.Updated = time.Unix(m.UpdatedUnix, 0).Local() | |||||
| case "next_updated_unix": | |||||
| m.NextUpdate = time.Unix(m.NextUpdateUnix, 0).Local() | |||||
| } | |||||
| } | |||||
| // ScheduleNextUpdate calculates and sets next update time. | |||||
| func (m *Mirror) ScheduleNextUpdate() { | |||||
| m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour) | |||||
| } | |||||
| func (m *Mirror) readAddress() { | |||||
| if len(m.address) > 0 { | |||||
| return | |||||
| } | |||||
| cfg, err := ini.Load(m.Repo.GitConfigPath()) | |||||
| if err != nil { | |||||
| log.Error(4, "Load: %v", err) | |||||
| return | |||||
| } | |||||
| m.address = cfg.Section("remote \"origin\"").Key("url").Value() | |||||
| } | |||||
| // HandleCloneUserCredentials replaces user credentials from HTTP/HTTPS URL | |||||
| // with placeholder <credentials>. | |||||
| // It will fail for any other forms of clone addresses. | |||||
| func HandleCloneUserCredentials(url string, mosaics bool) string { | |||||
| i := strings.Index(url, "@") | |||||
| if i == -1 { | |||||
| return url | |||||
| } | |||||
| start := strings.Index(url, "://") | |||||
| if start == -1 { | |||||
| return url | |||||
| } | |||||
| if mosaics { | |||||
| return url[:start+3] + "<credentials>" + url[i:] | |||||
| } | |||||
| return url[:start+3] + url[i+1:] | |||||
| } | |||||
| // Address returns mirror address from Git repository config without credentials. | |||||
| func (m *Mirror) Address() string { | |||||
| m.readAddress() | |||||
| return HandleCloneUserCredentials(m.address, false) | |||||
| } | |||||
| // FullAddress returns mirror address from Git repository config. | |||||
| func (m *Mirror) FullAddress() string { | |||||
| m.readAddress() | |||||
| return m.address | |||||
| } | |||||
| // SaveAddress writes new address to Git repository config. | |||||
| func (m *Mirror) SaveAddress(addr string) error { | |||||
| configPath := m.Repo.GitConfigPath() | |||||
| cfg, err := ini.Load(configPath) | |||||
| if err != nil { | |||||
| return fmt.Errorf("Load: %v", err) | |||||
| } | |||||
| cfg.Section("remote \"origin\"").Key("url").SetValue(addr) | |||||
| return cfg.SaveToIndent(configPath, "\t") | |||||
| } | |||||
| // runSync returns true if sync finished without error. | |||||
| func (m *Mirror) runSync() bool { | |||||
| repoPath := m.Repo.RepoPath() | |||||
| wikiPath := m.Repo.WikiPath() | |||||
| timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second | |||||
| gitArgs := []string{"remote", "update"} | |||||
| if m.EnablePrune { | |||||
| gitArgs = append(gitArgs, "--prune") | |||||
| } | |||||
| if _, stderr, err := process.ExecDir( | |||||
| timeout, repoPath, fmt.Sprintf("runSync: %s", repoPath), | |||||
| "git", gitArgs...); err != nil { | |||||
| desc := fmt.Sprintf("Fail to update mirror repository '%s': %s", repoPath, stderr) | |||||
| log.Error(4, desc) | |||||
| if err = CreateRepositoryNotice(desc); err != nil { | |||||
| log.Error(4, "CreateRepositoryNotice: %v", err) | |||||
| } | |||||
| return false | |||||
| } | |||||
| if m.Repo.HasWiki() { | |||||
| if _, stderr, err := process.ExecDir( | |||||
| timeout, wikiPath, fmt.Sprintf("runSync: %s", wikiPath), | |||||
| "git", "remote", "update", "--prune"); err != nil { | |||||
| desc := fmt.Sprintf("Fail to update mirror wiki repository '%s': %s", wikiPath, stderr) | |||||
| log.Error(4, desc) | |||||
| if err = CreateRepositoryNotice(desc); err != nil { | |||||
| log.Error(4, "CreateRepositoryNotice: %v", err) | |||||
| } | |||||
| return false | |||||
| } | |||||
| } | |||||
| return true | |||||
| } | |||||
| func getMirrorByRepoID(e Engine, repoID int64) (*Mirror, error) { | |||||
| m := &Mirror{RepoID: repoID} | |||||
| has, err := e.Get(m) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } else if !has { | |||||
| return nil, ErrMirrorNotExist | |||||
| } | |||||
| return m, nil | |||||
| } | |||||
| // GetMirrorByRepoID returns mirror information of a repository. | |||||
| func GetMirrorByRepoID(repoID int64) (*Mirror, error) { | |||||
| return getMirrorByRepoID(x, repoID) | |||||
| } | |||||
| func updateMirror(e Engine, m *Mirror) error { | |||||
| _, err := e.Id(m.ID).AllCols().Update(m) | |||||
| return err | |||||
| } | |||||
| func UpdateMirror(m *Mirror) error { | |||||
| return updateMirror(x, m) | |||||
| } | |||||
| func DeleteMirrorByRepoID(repoID int64) error { | |||||
| _, err := x.Delete(&Mirror{RepoID: repoID}) | |||||
| return err | |||||
| } | |||||
| // MirrorUpdate checks and updates mirror repositories. | |||||
| func MirrorUpdate() { | |||||
| if taskStatusTable.IsRunning(_MIRROR_UPDATE) { | |||||
| return | |||||
| } | |||||
| taskStatusTable.Start(_MIRROR_UPDATE) | |||||
| defer taskStatusTable.Stop(_MIRROR_UPDATE) | |||||
| log.Trace("Doing: MirrorUpdate") | |||||
| if err := x.Where("next_update_unix<=?", time.Now().Unix()).Iterate(new(Mirror), func(idx int, bean interface{}) error { | |||||
| m := bean.(*Mirror) | |||||
| if m.Repo == nil { | |||||
| log.Error(4, "Disconnected mirror repository found: %d", m.ID) | |||||
| return nil | |||||
| } | |||||
| MirrorQueue.Add(m.RepoID) | |||||
| return nil | |||||
| }); err != nil { | |||||
| log.Error(4, "MirrorUpdate: %v", err) | |||||
| } | |||||
| } | |||||
| // SyncMirrors checks and syncs mirrors. | |||||
| // TODO: sync more mirrors at same time. | |||||
| func SyncMirrors() { | |||||
| // Start listening on new sync requests. | |||||
| for repoID := range MirrorQueue.Queue() { | |||||
| log.Trace("SyncMirrors [repo_id: %v]", repoID) | |||||
| MirrorQueue.Remove(repoID) | |||||
| m, err := GetMirrorByRepoID(com.StrTo(repoID).MustInt64()) | |||||
| if err != nil { | |||||
| log.Error(4, "GetMirrorByRepoID [%d]: %v", repoID, err) | |||||
| continue | |||||
| } | |||||
| if !m.runSync() { | |||||
| continue | |||||
| } | |||||
| m.ScheduleNextUpdate() | |||||
| if err = UpdateMirror(m); err != nil { | |||||
| log.Error(4, "UpdateMirror [%d]: %v", repoID, err) | |||||
| continue | |||||
| } | |||||
| } | |||||
| } | |||||
| func InitSyncMirrors() { | |||||
| go SyncMirrors() | |||||
| } | |||||
| @@ -597,7 +597,7 @@ func DeliverHooks() { | |||||
| // Start listening on new hook requests. | // Start listening on new hook requests. | ||||
| for repoID := range HookQueue.Queue() { | for repoID := range HookQueue.Queue() { | ||||
| log.Trace("DeliverHooks [%v]: processing delivery hooks", repoID) | |||||
| log.Trace("DeliverHooks [repo_id: %v]", repoID) | |||||
| HookQueue.Remove(repoID) | HookQueue.Remove(repoID) | ||||
| tasks = make([]*HookTask, 0, 5) | tasks = make([]*HookTask, 0, 5) | ||||
| @@ -216,7 +216,7 @@ func RepoAssignment(args ...bool) macaron.Handler { | |||||
| ctx.Data["HasAccess"] = true | ctx.Data["HasAccess"] = true | ||||
| if repo.IsMirror { | if repo.IsMirror { | ||||
| ctx.Repo.Mirror, err = models.GetMirror(repo.ID) | |||||
| ctx.Repo.Mirror, err = models.GetMirrorByRepoID(repo.ID) | |||||
| if err != nil { | if err != nil { | ||||
| ctx.Handle(500, "GetMirror", err) | ctx.Handle(500, "GetMirror", err) | ||||
| return | return | ||||
| @@ -113,6 +113,7 @@ var ( | |||||
| AnsiCharset string | AnsiCharset string | ||||
| ForcePrivate bool | ForcePrivate bool | ||||
| MaxCreationLimit int | MaxCreationLimit int | ||||
| MirrorQueueLength int | |||||
| PullRequestQueueLength int | PullRequestQueueLength int | ||||
| PreferredLicenses []string | PreferredLicenses []string | ||||
| @@ -75,6 +75,7 @@ func GlobalInit() { | |||||
| // Booting long running goroutines. | // Booting long running goroutines. | ||||
| cron.NewContext() | cron.NewContext() | ||||
| models.InitSyncMirrors() | |||||
| models.InitDeliverHooks() | models.InitDeliverHooks() | ||||
| models.InitTestPullRequests() | models.InitTestPullRequests() | ||||
| log.NewGitLogger(path.Join(setting.LogRootPath, "http.log")) | log.NewGitLogger(path.Join(setting.LogRootPath, "http.log")) | ||||
| @@ -490,7 +490,6 @@ func UploadIssueAttachment(ctx *context.Context) { | |||||
| func ViewIssue(ctx *context.Context) { | func ViewIssue(ctx *context.Context) { | ||||
| ctx.Data["RequireHighlightJS"] = true | ctx.Data["RequireHighlightJS"] = true | ||||
| ctx.Data["RequireSimpleMDE"] = true | |||||
| ctx.Data["RequireDropzone"] = true | ctx.Data["RequireDropzone"] = true | ||||
| renderAttachmentSettings(ctx) | renderAttachmentSettings(ctx) | ||||
| @@ -104,34 +104,42 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | |||||
| } | } | ||||
| } | } | ||||
| if repo.IsMirror { | |||||
| if isNameChanged { | |||||
| var err error | |||||
| ctx.Repo.Mirror, err = models.GetMirror(repo.ID) | |||||
| if err != nil { | |||||
| ctx.Handle(500, "RefreshRepositoryMirror", err) | |||||
| return | |||||
| } | |||||
| } | |||||
| ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) | |||||
| ctx.Redirect(repo.Link() + "/settings") | |||||
| if form.Interval > 0 { | |||||
| ctx.Repo.Mirror.EnablePrune = form.EnablePrune | |||||
| ctx.Repo.Mirror.Interval = form.Interval | |||||
| ctx.Repo.Mirror.NextUpdate = time.Now().Add(time.Duration(form.Interval) * time.Hour) | |||||
| if err := models.UpdateMirror(ctx.Repo.Mirror); err != nil { | |||||
| ctx.Handle(500, "UpdateMirror", err) | |||||
| return | |||||
| } | |||||
| } | |||||
| if err := ctx.Repo.Mirror.SaveAddress(form.MirrorAddress); err != nil { | |||||
| ctx.Handle(500, "SaveAddress", err) | |||||
| case "mirror": | |||||
| if !repo.IsMirror { | |||||
| ctx.Handle(404, "", nil) | |||||
| return | |||||
| } | |||||
| if form.Interval > 0 { | |||||
| ctx.Repo.Mirror.EnablePrune = form.EnablePrune | |||||
| ctx.Repo.Mirror.Interval = form.Interval | |||||
| ctx.Repo.Mirror.NextUpdate = time.Now().Add(time.Duration(form.Interval) * time.Hour) | |||||
| if err := models.UpdateMirror(ctx.Repo.Mirror); err != nil { | |||||
| ctx.Handle(500, "UpdateMirror", err) | |||||
| return | return | ||||
| } | } | ||||
| } | } | ||||
| if err := ctx.Repo.Mirror.SaveAddress(form.MirrorAddress); err != nil { | |||||
| ctx.Handle(500, "SaveAddress", err) | |||||
| return | |||||
| } | |||||
| ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) | ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) | ||||
| ctx.Redirect(repo.Link() + "/settings") | ctx.Redirect(repo.Link() + "/settings") | ||||
| case "mirror-sync": | |||||
| if !repo.IsMirror { | |||||
| ctx.Handle(404, "", nil) | |||||
| return | |||||
| } | |||||
| go models.MirrorQueue.Add(repo.ID) | |||||
| ctx.Flash.Info(ctx.Tr("repo.settings.mirror_sync_in_progress")) | |||||
| ctx.Redirect(repo.Link() + "/settings") | |||||
| case "advanced": | case "advanced": | ||||
| repo.EnableWiki = form.EnableWiki | repo.EnableWiki = form.EnableWiki | ||||
| repo.EnableExternalWiki = form.EnableExternalWiki | repo.EnableExternalWiki = form.EnableExternalWiki | ||||
| @@ -278,6 +286,9 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | |||||
| ctx.Flash.Success(ctx.Tr("repo.settings.wiki_deletion_success")) | ctx.Flash.Success(ctx.Tr("repo.settings.wiki_deletion_success")) | ||||
| ctx.Redirect(ctx.Repo.RepoLink + "/settings") | ctx.Redirect(ctx.Repo.RepoLink + "/settings") | ||||
| default: | |||||
| ctx.Handle(404, "", nil) | |||||
| } | } | ||||
| } | } | ||||
| @@ -1 +1 @@ | |||||
| 0.9.95.0830 | |||||
| 0.9.96.0830 | |||||
| @@ -50,12 +50,26 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{end}} | {{end}} | ||||
| {{if .Repository.IsMirror}} | |||||
| <div class="field"> | |||||
| <button class="ui green button">{{$.i18n.Tr "repo.settings.update_settings"}}</button> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| {{if .Repository.IsMirror}} | |||||
| <h4 class="ui top attached header"> | |||||
| {{.i18n.Tr "repo.settings.mirror_settings"}} | |||||
| </h4> | |||||
| <div class="ui attached segment"> | |||||
| <form class="ui form" method="post"> | |||||
| {{.CsrfTokenHtml}} | |||||
| <input type="hidden" name="action" value="mirror"> | |||||
| <div class="inline field {{if .Err_EnablePrune}}error{{end}}"> | <div class="inline field {{if .Err_EnablePrune}}error{{end}}"> | ||||
| <label>{{.i18n.Tr "repo.mirror_prune"}}</label> | |||||
| <label>{{.i18n.Tr "repo.mirror_prune"}}</label> | |||||
| <div class="ui checkbox"> | <div class="ui checkbox"> | ||||
| <input id="enable_prune" name="enable_prune" type="checkbox" {{if .MirrorEnablePrune}}checked{{end}}> | |||||
| <label>{{.i18n.Tr "repo.mirror_prune_desc"}}</label> | |||||
| <input id="enable_prune" name="enable_prune" type="checkbox" {{if .MirrorEnablePrune}}checked{{end}}> | |||||
| <label>{{.i18n.Tr "repo.mirror_prune_desc"}}</label> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="inline field {{if .Err_Interval}}error{{end}}"> | <div class="inline field {{if .Err_Interval}}error{{end}}"> | ||||
| @@ -64,23 +78,36 @@ | |||||
| </div> | </div> | ||||
| <div class="field"> | <div class="field"> | ||||
| <label for="mirror_address">{{.i18n.Tr "repo.mirror_address"}}</label> | <label for="mirror_address">{{.i18n.Tr "repo.mirror_address"}}</label> | ||||
| <input id="mirror_address" name="mirror_address" value="{{.Mirror.FullAddress}}"> | |||||
| <input id="mirror_address" name="mirror_address" value="{{.Mirror.FullAddress}}" required> | |||||
| <p class="help">{{.i18n.Tr "repo.mirror_address_desc"}}</p> | <p class="help">{{.i18n.Tr "repo.mirror_address_desc"}}</p> | ||||
| </div> | </div> | ||||
| {{end}} | |||||
| <div class="field"> | |||||
| <button class="ui green button">{{$.i18n.Tr "repo.settings.update_settings"}}</button> | |||||
| </div> | |||||
| </form> | |||||
| <div class="ui divider"></div> | <div class="ui divider"></div> | ||||
| <div class="field"> | |||||
| <button class="ui green button">{{$.i18n.Tr "repo.settings.update_settings"}}</button> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| <form class="ui form" method="post"> | |||||
| {{.CsrfTokenHtml}} | |||||
| <input type="hidden" name="action" value="mirror-sync"> | |||||
| <div class="inline field"> | |||||
| <label>{{.i18n.Tr "repo.mirror_last_synced"}}</label> | |||||
| <span>{{.Mirror.Updated}}</span> | |||||
| </div> | |||||
| <div class="field"> | |||||
| <button class="ui blue button">{{$.i18n.Tr "repo.settings.sync_mirror"}}</button> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| {{end}} | |||||
| <h4 class="ui top attached header"> | <h4 class="ui top attached header"> | ||||
| {{.i18n.Tr "repo.settings.advanced_settings"}} | {{.i18n.Tr "repo.settings.advanced_settings"}} | ||||
| </h4> | </h4> | ||||
| <div class="ui attached segment"> | <div class="ui attached segment"> | ||||
| <form class="ui form" action="{{.Link}}" method="post"> | |||||
| <form class="ui form" method="post"> | |||||
| {{.CsrfTokenHtml}} | {{.CsrfTokenHtml}} | ||||
| <input type="hidden" name="action" value="advanced"> | <input type="hidden" name="action" value="advanced"> | ||||