2016-08-31 02:18:33 +03:00
// 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"
2017-09-20 08:26:49 +03:00
"code.gitea.io/git"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/sync"
2016-08-31 02:18:33 +03:00
)
2016-11-26 03:30:21 +03:00
// MirrorQueue holds an UniqueQueue object of the mirror
2016-08-31 02:18:33 +03:00
var MirrorQueue = sync . NewUniqueQueue ( setting . Repository . MirrorQueueLength )
// Mirror represents mirror information of a repository.
type Mirror struct {
2017-01-06 18:14:33 +03:00
ID int64 ` xorm:"pk autoincr" `
RepoID int64 ` xorm:"INDEX" `
2016-08-31 02:18:33 +03:00
Repo * Repository ` xorm:"-" `
2017-04-08 18:27:26 +03:00
Interval time . Duration
EnablePrune bool ` xorm:"NOT NULL DEFAULT true" `
2016-08-31 02:18:33 +03:00
Updated time . Time ` xorm:"-" `
2017-01-06 18:14:33 +03:00
UpdatedUnix int64 ` xorm:"INDEX" `
2016-08-31 02:18:33 +03:00
NextUpdate time . Time ` xorm:"-" `
2017-01-06 18:14:33 +03:00
NextUpdateUnix int64 ` xorm:"INDEX" `
2016-08-31 02:18:33 +03:00
address string ` xorm:"-" `
}
2016-11-26 03:30:21 +03:00
// BeforeInsert will be invoked by XORM before inserting a record
2016-08-31 02:18:33 +03:00
func ( m * Mirror ) BeforeInsert ( ) {
2017-09-13 08:18:22 +03:00
if m != nil {
m . UpdatedUnix = time . Now ( ) . Unix ( )
m . NextUpdateUnix = m . NextUpdate . Unix ( )
}
2016-08-31 02:18:33 +03:00
}
2016-11-26 03:30:21 +03:00
// BeforeUpdate is invoked from XORM before updating this object.
2016-08-31 02:18:33 +03:00
func ( m * Mirror ) BeforeUpdate ( ) {
2017-09-13 08:18:22 +03:00
if m != nil {
m . UpdatedUnix = time . Now ( ) . Unix ( )
m . NextUpdateUnix = m . NextUpdate . Unix ( )
}
2016-08-31 02:18:33 +03:00
}
2016-11-26 03:30:21 +03:00
// AfterSet is invoked from XORM after setting the value of a field of this object.
2016-08-31 02:18:33 +03:00
func ( m * Mirror ) AfterSet ( colName string , _ xorm . Cell ) {
2017-09-13 08:18:22 +03:00
if m == nil {
return
}
2016-08-31 02:18:33 +03:00
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 ( )
2017-04-06 03:56:45 +03:00
case "next_update_unix" :
2016-08-31 02:18:33 +03:00
m . NextUpdate = time . Unix ( m . NextUpdateUnix , 0 ) . Local ( )
}
}
// ScheduleNextUpdate calculates and sets next update time.
func ( m * Mirror ) ScheduleNextUpdate ( ) {
2017-04-08 18:27:26 +03:00
m . NextUpdate = time . Now ( ) . Add ( m . Interval )
2016-08-31 02:18:33 +03:00
}
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" )
}
2017-01-17 08:58:58 +03:00
if _ , stderr , err := process . GetManager ( ) . ExecDir (
2016-08-31 14:31:53 +03:00
timeout , repoPath , fmt . Sprintf ( "Mirror.runSync: %s" , repoPath ) ,
2016-08-31 02:18:33 +03:00
"git" , gitArgs ... ) ; err != nil {
2017-01-29 23:13:57 +03:00
desc := fmt . Sprintf ( "Failed to update mirror repository '%s': %s" , repoPath , stderr )
2016-08-31 02:18:33 +03:00
log . Error ( 4 , desc )
if err = CreateRepositoryNotice ( desc ) ; err != nil {
log . Error ( 4 , "CreateRepositoryNotice: %v" , err )
}
return false
}
2017-04-11 16:30:15 +03:00
2017-09-20 08:26:49 +03:00
gitRepo , err := git . OpenRepository ( repoPath )
if err != nil {
log . Error ( 4 , "OpenRepository: %v" , err )
return false
}
if err = SyncReleasesWithTags ( m . Repo , gitRepo ) ; err != nil {
log . Error ( 4 , "Failed to synchronize tags to releases for repository: %v" , err )
}
2017-04-11 16:30:15 +03:00
if err := m . Repo . UpdateSize ( ) ; err != nil {
log . Error ( 4 , "Failed to update size for mirror repository: %v" , err )
}
2016-08-31 02:18:33 +03:00
if m . Repo . HasWiki ( ) {
2017-01-17 08:58:58 +03:00
if _ , stderr , err := process . GetManager ( ) . ExecDir (
2016-08-31 14:31:53 +03:00
timeout , wikiPath , fmt . Sprintf ( "Mirror.runSync: %s" , wikiPath ) ,
2016-08-31 02:18:33 +03:00
"git" , "remote" , "update" , "--prune" ) ; err != nil {
2017-01-29 23:13:57 +03:00
desc := fmt . Sprintf ( "Failed to update mirror wiki repository '%s': %s" , wikiPath , stderr )
2016-08-31 02:18:33 +03:00
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
}
2016-11-26 03:30:21 +03:00
// UpdateMirror updates the mirror
2016-08-31 02:18:33 +03:00
func UpdateMirror ( m * Mirror ) error {
return updateMirror ( x , m )
}
2016-11-26 03:30:21 +03:00
// DeleteMirrorByRepoID deletes a mirror by repoID
2016-08-31 02:18:33 +03:00
func DeleteMirrorByRepoID ( repoID int64 ) error {
_ , err := x . Delete ( & Mirror { RepoID : repoID } )
return err
}
// MirrorUpdate checks and updates mirror repositories.
func MirrorUpdate ( ) {
2017-05-31 11:57:17 +03:00
if ! taskStatusTable . StartIfNotRunning ( mirrorUpdate ) {
2016-08-31 02:18:33 +03:00
return
}
2016-11-07 19:41:28 +03:00
defer taskStatusTable . Stop ( mirrorUpdate )
2016-08-31 02:18:33 +03:00
log . Trace ( "Doing: MirrorUpdate" )
2016-11-10 18:16:32 +03:00
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
}
2016-08-31 02:18:33 +03:00
2016-11-10 18:16:32 +03:00
MirrorQueue . Add ( m . RepoID )
return nil
} ) ; err != nil {
2016-08-31 02:18:33 +03:00
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 {
2016-12-21 10:09:43 +03:00
log . Error ( 4 , "GetMirrorByRepoID [%s]: %v" , repoID , err )
2016-08-31 02:18:33 +03:00
continue
}
if ! m . runSync ( ) {
continue
}
m . ScheduleNextUpdate ( )
if err = UpdateMirror ( m ) ; err != nil {
2016-12-21 10:09:43 +03:00
log . Error ( 4 , "UpdateMirror [%s]: %v" , repoID , err )
2016-08-31 02:18:33 +03:00
continue
}
}
}
2017-01-05 03:50:34 +03:00
// InitSyncMirrors initializes a go routine to sync the mirrors
2016-08-31 02:18:33 +03:00
func InitSyncMirrors ( ) {
go SyncMirrors ( )
}