mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	#2018 able to sync now for mirrors
- Refactor code to use sync.UniqueQueue - Closes #3509
This commit is contained in:
		@@ -8,10 +8,14 @@ end_of_line = lf
 | 
			
		||||
insert_final_newline = true
 | 
			
		||||
trim_trailing_whitespace = true
 | 
			
		||||
 | 
			
		||||
[*.{go,tmpl}]
 | 
			
		||||
[*.go]
 | 
			
		||||
indent_style = tab
 | 
			
		||||
indent_size = 4
 | 
			
		||||
 | 
			
		||||
[*.tmpl]
 | 
			
		||||
indent_style = tab
 | 
			
		||||
indent_size = 2
 | 
			
		||||
 | 
			
		||||
[*.{less,yml}]
 | 
			
		||||
indent_style = space
 | 
			
		||||
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  |
 | 
			
		||||
|:-------------:|:-------:|:-------:|
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								conf/app.ini
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								conf/app.ini
									
									
									
									
									
								
							@@ -17,8 +17,10 @@ ANSI_CHARSET =
 | 
			
		||||
FORCE_PRIVATE = false
 | 
			
		||||
; Global maximum creation limit of repository per user, -1 means no limit
 | 
			
		||||
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
 | 
			
		||||
; Name must match file name in conf/license or custom/conf/license
 | 
			
		||||
PREFERRED_LICENSES = Apache License 2.0,MIT License
 | 
			
		||||
@@ -184,7 +186,7 @@ ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
 | 
			
		||||
ENABLE_CAPTCHA = true
 | 
			
		||||
 | 
			
		||||
[webhook]
 | 
			
		||||
; Hook task queue length
 | 
			
		||||
; Hook task queue length, increase if webhook shooting starts hanging
 | 
			
		||||
QUEUE_LENGTH = 1000
 | 
			
		||||
; Deliver timeout in seconds
 | 
			
		||||
DELIVER_TIMEOUT = 5
 | 
			
		||||
@@ -389,7 +391,7 @@ GC = 60
 | 
			
		||||
 | 
			
		||||
[mirror]
 | 
			
		||||
; Default interval in hours between each check
 | 
			
		||||
DEFAULT_INTERVAL = 24
 | 
			
		||||
DEFAULT_INTERVAL = 8
 | 
			
		||||
 | 
			
		||||
[api]
 | 
			
		||||
; 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_address = Mirror Address
 | 
			
		||||
mirror_address_desc = Please include necessary user credentials in the address.
 | 
			
		||||
mirror_last_synced = Last Synced
 | 
			
		||||
watchers = Watchers
 | 
			
		||||
stargazers = Stargazers
 | 
			
		||||
forks = Forks
 | 
			
		||||
@@ -631,6 +632,9 @@ settings.collaboration.undefined = Undefined
 | 
			
		||||
settings.hooks = Webhooks
 | 
			
		||||
settings.githooks = Git Hooks
 | 
			
		||||
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.update_settings = Update Settings
 | 
			
		||||
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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const APP_VER = "0.9.95.0830"
 | 
			
		||||
const APP_VER = "0.9.96.0830"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	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) {
 | 
			
		||||
	repo.Mirror, err = GetMirror(repo.ID)
 | 
			
		||||
	repo.Mirror, err = GetMirrorByRepoID(repo.ID)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -574,136 +574,6 @@ func (repo *Repository) CloneLink() (cl *CloneLink) {
 | 
			
		||||
	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 {
 | 
			
		||||
	Name        string
 | 
			
		||||
	Description string
 | 
			
		||||
@@ -839,6 +709,11 @@ func cleanUpMigrateGitConfig(configPath string) error {
 | 
			
		||||
	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.
 | 
			
		||||
func CleanUpMigrateInfo(repo *Repository) (*Repository, error) {
 | 
			
		||||
	repoPath := repo.RepoPath()
 | 
			
		||||
@@ -1748,70 +1623,6 @@ const (
 | 
			
		||||
	_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.
 | 
			
		||||
func GitFsck() {
 | 
			
		||||
	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.
 | 
			
		||||
	for repoID := range HookQueue.Queue() {
 | 
			
		||||
		log.Trace("DeliverHooks [%v]: processing delivery hooks", repoID)
 | 
			
		||||
		log.Trace("DeliverHooks [repo_id: %v]", repoID)
 | 
			
		||||
		HookQueue.Remove(repoID)
 | 
			
		||||
 | 
			
		||||
		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
 | 
			
		||||
 | 
			
		||||
		if repo.IsMirror {
 | 
			
		||||
			ctx.Repo.Mirror, err = models.GetMirror(repo.ID)
 | 
			
		||||
			ctx.Repo.Mirror, err = models.GetMirrorByRepoID(repo.ID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ctx.Handle(500, "GetMirror", err)
 | 
			
		||||
				return
 | 
			
		||||
 
 | 
			
		||||
@@ -113,6 +113,7 @@ var (
 | 
			
		||||
		AnsiCharset            string
 | 
			
		||||
		ForcePrivate           bool
 | 
			
		||||
		MaxCreationLimit       int
 | 
			
		||||
		MirrorQueueLength      int
 | 
			
		||||
		PullRequestQueueLength int
 | 
			
		||||
		PreferredLicenses      []string
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -75,6 +75,7 @@ func GlobalInit() {
 | 
			
		||||
 | 
			
		||||
		// Booting long running goroutines.
 | 
			
		||||
		cron.NewContext()
 | 
			
		||||
		models.InitSyncMirrors()
 | 
			
		||||
		models.InitDeliverHooks()
 | 
			
		||||
		models.InitTestPullRequests()
 | 
			
		||||
		log.NewGitLogger(path.Join(setting.LogRootPath, "http.log"))
 | 
			
		||||
 
 | 
			
		||||
@@ -490,7 +490,6 @@ func UploadIssueAttachment(ctx *context.Context) {
 | 
			
		||||
 | 
			
		||||
func ViewIssue(ctx *context.Context) {
 | 
			
		||||
	ctx.Data["RequireHighlightJS"] = true
 | 
			
		||||
	ctx.Data["RequireSimpleMDE"] = true
 | 
			
		||||
	ctx.Data["RequireDropzone"] = true
 | 
			
		||||
	renderAttachmentSettings(ctx)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -104,15 +104,14 @@ 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)
 | 
			
		||||
		ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
 | 
			
		||||
		ctx.Redirect(repo.Link() + "/settings")
 | 
			
		||||
 | 
			
		||||
	case "mirror":
 | 
			
		||||
		if !repo.IsMirror {
 | 
			
		||||
			ctx.Handle(404, "", nil)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		if form.Interval > 0 {
 | 
			
		||||
			ctx.Repo.Mirror.EnablePrune = form.EnablePrune
 | 
			
		||||
@@ -127,11 +126,20 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
 | 
			
		||||
			ctx.Handle(500, "SaveAddress", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
 | 
			
		||||
		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":
 | 
			
		||||
		repo.EnableWiki = form.EnableWiki
 | 
			
		||||
		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.Redirect(ctx.Repo.RepoLink + "/settings")
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		ctx.Handle(404, "", nil)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1 @@
 | 
			
		||||
0.9.95.0830
 | 
			
		||||
0.9.96.0830
 | 
			
		||||
@@ -50,7 +50,21 @@
 | 
			
		||||
								</div>
 | 
			
		||||
							</div>
 | 
			
		||||
						{{end}}
 | 
			
		||||
 | 
			
		||||
						<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}}">
 | 
			
		||||
							  <label>{{.i18n.Tr "repo.mirror_prune"}}</label>
 | 
			
		||||
								<div class="ui checkbox">
 | 
			
		||||
@@ -64,23 +78,36 @@
 | 
			
		||||
							</div>
 | 
			
		||||
							<div class="field">
 | 
			
		||||
								<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>
 | 
			
		||||
							</div>
 | 
			
		||||
						{{end}}
 | 
			
		||||
 | 
			
		||||
						<div class="ui divider"></div>
 | 
			
		||||
							<div class="field">
 | 
			
		||||
								<button class="ui green button">{{$.i18n.Tr "repo.settings.update_settings"}}</button>
 | 
			
		||||
							</div>
 | 
			
		||||
						</form>
 | 
			
		||||
 | 
			
		||||
						<div class="ui divider"></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">
 | 
			
		||||
					{{.i18n.Tr "repo.settings.advanced_settings"}}
 | 
			
		||||
				</h4>
 | 
			
		||||
				<div class="ui attached segment">
 | 
			
		||||
					<form class="ui form" action="{{.Link}}" method="post">
 | 
			
		||||
					<form class="ui form" method="post">
 | 
			
		||||
						{{.CsrfTokenHtml}}
 | 
			
		||||
						<input type="hidden" name="action" value="advanced">
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user