#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 [ |  | ||||||
| 
 | 
 | ||||||
| ##### 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"> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue