#2018 able to sync now for mirrors
- Refactor code to use sync.UniqueQueue - Closes #3509
This commit is contained in:
parent
c1ecb6c60a
commit
8516dfcb6c
16 changed files with 345 additions and 242 deletions
|
@ -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 [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra
|
||||||
|
|
||||||
![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true)
|
![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true)
|
||||||
|
|
||||||
##### 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 |
|
||||||
|:-------------:|:-------:|:-------:|
|
|:-------------:|:-------:|:-------:|
|
||||||
|
|
10
conf/app.ini
10
conf/app.ini
|
@ -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
|
; Mirror sync queue length, increase if mirror syncing starts hanging
|
||||||
PULL_REQUEST_QUEUE_LENGTH = 10000
|
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.
|
||||||
|
|
2
gogs.go
2
gogs.go
|
@ -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())
|
||||||
|
|
201
models/repo.go
201
models/repo.go
|
@ -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) {
|
||||||
|
|
243
models/repo_mirror.go
Normal file
243
models/repo_mirror.go
Normal file
|
@ -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)
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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 {
|
ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
|
||||||
if isNameChanged {
|
ctx.Redirect(repo.Link() + "/settings")
|
||||||
var err error
|
|
||||||
ctx.Repo.Mirror, err = models.GetMirror(repo.ID)
|
|
||||||
if err != nil {
|
|
||||||
ctx.Handle(500, "RefreshRepositoryMirror", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if form.Interval > 0 {
|
case "mirror":
|
||||||
ctx.Repo.Mirror.EnablePrune = form.EnablePrune
|
if !repo.IsMirror {
|
||||||
ctx.Repo.Mirror.Interval = form.Interval
|
ctx.Handle(404, "", nil)
|
||||||
ctx.Repo.Mirror.NextUpdate = time.Now().Add(time.Duration(form.Interval) * time.Hour)
|
return
|
||||||
if err := models.UpdateMirror(ctx.Repo.Mirror); err != nil {
|
}
|
||||||
ctx.Handle(500, "UpdateMirror", err)
|
|
||||||
return
|
if form.Interval > 0 {
|
||||||
}
|
ctx.Repo.Mirror.EnablePrune = form.EnablePrune
|
||||||
}
|
ctx.Repo.Mirror.Interval = form.Interval
|
||||||
if err := ctx.Repo.Mirror.SaveAddress(form.MirrorAddress); err != nil {
|
ctx.Repo.Mirror.NextUpdate = time.Now().Add(time.Duration(form.Interval) * time.Hour)
|
||||||
ctx.Handle(500, "SaveAddress", err)
|
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}}>
|
<input id="enable_prune" name="enable_prune" type="checkbox" {{if .MirrorEnablePrune}}checked{{end}}>
|
||||||
<label>{{.i18n.Tr "repo.mirror_prune_desc"}}</label>
|
<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>
|
<form class="ui form" method="post">
|
||||||
</div>
|
{{.CsrfTokenHtml}}
|
||||||
</form>
|
<input type="hidden" name="action" value="mirror-sync">
|
||||||
</div>
|
<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">
|
||||||
|
|
||||||
|
|
Reference in a new issue