- Refactor code to use sync.UniqueQueue - Closes #3509master
@@ -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 | | |||
|:-------------:|:-------:|:-------:| | |||
@@ -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. | |||
@@ -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()) | |||
@@ -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) { | |||
@@ -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) | |||
@@ -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,34 +104,42 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | |||
} | |||
} | |||
if repo.IsMirror { | |||
if isNameChanged { | |||
var err error | |||
ctx.Repo.Mirror, err = models.GetMirror(repo.ID) | |||
if err != nil { | |||
ctx.Handle(500, "RefreshRepositoryMirror", err) | |||
return | |||
} | |||
} | |||
ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) | |||
ctx.Redirect(repo.Link() + "/settings") | |||
if form.Interval > 0 { | |||
ctx.Repo.Mirror.EnablePrune = form.EnablePrune | |||
ctx.Repo.Mirror.Interval = form.Interval | |||
ctx.Repo.Mirror.NextUpdate = time.Now().Add(time.Duration(form.Interval) * time.Hour) | |||
if err := models.UpdateMirror(ctx.Repo.Mirror); err != nil { | |||
ctx.Handle(500, "UpdateMirror", err) | |||
return | |||
} | |||
} | |||
if err := ctx.Repo.Mirror.SaveAddress(form.MirrorAddress); err != nil { | |||
ctx.Handle(500, "SaveAddress", err) | |||
case "mirror": | |||
if !repo.IsMirror { | |||
ctx.Handle(404, "", nil) | |||
return | |||
} | |||
if form.Interval > 0 { | |||
ctx.Repo.Mirror.EnablePrune = form.EnablePrune | |||
ctx.Repo.Mirror.Interval = form.Interval | |||
ctx.Repo.Mirror.NextUpdate = time.Now().Add(time.Duration(form.Interval) * time.Hour) | |||
if err := models.UpdateMirror(ctx.Repo.Mirror); err != nil { | |||
ctx.Handle(500, "UpdateMirror", err) | |||
return | |||
} | |||
} | |||
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.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,12 +50,26 @@ | |||
</div> | |||
</div> | |||
{{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}}"> | |||
<label>{{.i18n.Tr "repo.mirror_prune"}}</label> | |||
<label>{{.i18n.Tr "repo.mirror_prune"}}</label> | |||
<div class="ui checkbox"> | |||
<input id="enable_prune" name="enable_prune" type="checkbox" {{if .MirrorEnablePrune}}checked{{end}}> | |||
<label>{{.i18n.Tr "repo.mirror_prune_desc"}}</label> | |||
<input id="enable_prune" name="enable_prune" type="checkbox" {{if .MirrorEnablePrune}}checked{{end}}> | |||
<label>{{.i18n.Tr "repo.mirror_prune_desc"}}</label> | |||
</div> | |||
</div> | |||
<div class="inline field {{if .Err_Interval}}error{{end}}"> | |||
@@ -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="field"> | |||
<button class="ui green button">{{$.i18n.Tr "repo.settings.update_settings"}}</button> | |||
</div> | |||
</form> | |||
<div class="ui divider"></div> | |||
<div class="field"> | |||
<button class="ui green button">{{$.i18n.Tr "repo.settings.update_settings"}}</button> | |||
</div> | |||
</form> | |||
</div> | |||
<form class="ui form" method="post"> | |||
{{.CsrfTokenHtml}} | |||
<input type="hidden" name="action" value="mirror-sync"> | |||
<div class="inline field"> | |||
<label>{{.i18n.Tr "repo.mirror_last_synced"}}</label> | |||
<span>{{.Mirror.Updated}}</span> | |||
</div> | |||
<div class="field"> | |||
<button class="ui blue button">{{$.i18n.Tr "repo.settings.sync_mirror"}}</button> | |||
</div> | |||
</form> | |||
</div> | |||
{{end}} | |||
<h4 class="ui top attached header"> | |||
{{.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"> | |||