diff --git a/models/repo.go b/models/repo.go index 7dfe14c85..f2453cc5c 100644 --- a/models/repo.go +++ b/models/repo.go @@ -979,6 +979,7 @@ type CreateRepoOptions struct { AutoInit bool Status RepositoryStatus TrustModel TrustModelType + MirrorInterval string } // GetRepoInitFile returns repository init files diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index 24c2478fa..87f2a5351 100644 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -68,16 +68,17 @@ type MigrateRepoForm struct { // required: true UID int64 `json:"uid" binding:"Required"` // required: true - RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"` - Mirror bool `json:"mirror"` - Private bool `json:"private"` - Description string `json:"description" binding:"MaxSize(255)"` - Wiki bool `json:"wiki"` - Milestones bool `json:"milestones"` - Labels bool `json:"labels"` - Issues bool `json:"issues"` - PullRequests bool `json:"pull_requests"` - Releases bool `json:"releases"` + RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"` + Mirror bool `json:"mirror"` + Private bool `json:"private"` + Description string `json:"description" binding:"MaxSize(255)"` + Wiki bool `json:"wiki"` + Milestones bool `json:"milestones"` + Labels bool `json:"labels"` + Issues bool `json:"issues"` + PullRequests bool `json:"pull_requests"` + Releases bool `json:"releases"` + MirrorInterval string `json:"mirror_interval"` } // Validate validates the fields diff --git a/modules/convert/repository.go b/modules/convert/repository.go index 0f470144b..813201ca6 100644 --- a/modules/convert/repository.go +++ b/modules/convert/repository.go @@ -91,6 +91,13 @@ func innerToRepo(repo *models.Repository, mode models.AccessMode, isParent bool) numReleases, _ := models.GetReleaseCountByRepoID(repo.ID, models.FindReleasesOptions{IncludeDrafts: false, IncludeTags: true}) + mirrorInterval := "" + if repo.IsMirror { + if err := repo.GetMirror(); err == nil { + mirrorInterval = repo.Mirror.Interval.String() + } + } + return &api.Repository{ ID: repo.ID, Owner: ToUser(repo.Owner, mode != models.AccessModeNone, mode >= models.AccessModeAdmin), @@ -134,5 +141,6 @@ func innerToRepo(repo *models.Repository, mode models.AccessMode, isParent bool) AllowSquash: allowSquash, AvatarURL: repo.AvatarLink(), Internal: !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePrivate, + MirrorInterval: mirrorInterval, } } diff --git a/modules/migrations/base/options.go b/modules/migrations/base/options.go index 3c9b2c22f..168f9848c 100644 --- a/modules/migrations/base/options.go +++ b/modules/migrations/base/options.go @@ -33,4 +33,5 @@ type MigrateOptions struct { PullRequests bool ReleaseAssets bool MigrateToRepoID int64 + MirrorInterval string `json:"mirror_interval"` } diff --git a/modules/migrations/gitea_uploader.go b/modules/migrations/gitea_uploader.go index 6118b3b5c..2c79bd4b0 100644 --- a/modules/migrations/gitea_uploader.go +++ b/modules/migrations/gitea_uploader.go @@ -142,6 +142,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate Private: repo.IsPrivate, Wiki: opts.Wiki, Releases: opts.Releases, // if didn't get releases, then sync them from tags + MirrorInterval: opts.MirrorInterval, }) g.repo = r diff --git a/modules/repository/repo.go b/modules/repository/repo.go index 8ecb43ede..ede714673 100644 --- a/modules/repository/repo.go +++ b/modules/repository/repo.go @@ -127,12 +127,33 @@ func MigrateRepositoryGitData(ctx context.Context, u *models.User, repo *models. } if opts.Mirror { - if err = models.InsertMirror(&models.Mirror{ + mirrorModel := models.Mirror{ RepoID: repo.ID, Interval: setting.Mirror.DefaultInterval, EnablePrune: true, NextUpdateUnix: timeutil.TimeStampNow().AddDuration(setting.Mirror.DefaultInterval), - }); err != nil { + } + + if opts.MirrorInterval != "" { + parsedInterval, err := time.ParseDuration(opts.MirrorInterval) + if err != nil { + log.Error("Failed to set Interval: %v", err) + return repo, err + } + if parsedInterval == 0 { + mirrorModel.Interval = 0 + mirrorModel.NextUpdateUnix = 0 + } else if parsedInterval < setting.Mirror.MinInterval { + err := fmt.Errorf("Interval %s is set below Minimum Interval of %s", parsedInterval, setting.Mirror.MinInterval) + log.Error("Interval: %s is too frequent", opts.MirrorInterval) + return repo, err + } else { + mirrorModel.Interval = parsedInterval + mirrorModel.NextUpdateUnix = timeutil.TimeStampNow().AddDuration(parsedInterval) + } + } + + if err = models.InsertMirror(&mirrorModel); err != nil { return repo, fmt.Errorf("InsertOne: %v", err) } diff --git a/modules/structs/repo.go b/modules/structs/repo.go index c12f8e1c1..309273d2f 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -91,6 +91,7 @@ type Repository struct { AllowSquash bool `json:"allow_squash_merge"` AvatarURL string `json:"avatar_url"` Internal bool `json:"internal"` + MirrorInterval string `json:"mirror_interval"` } // CreateRepoOption options when creating repository @@ -168,6 +169,8 @@ type EditRepoOption struct { AllowSquash *bool `json:"allow_squash_merge,omitempty"` // set to `true` to archive this repository. Archived *bool `json:"archived,omitempty"` + // set to a string like `8h30m0s` to set the mirror interval time + MirrorInterval *string `json:"mirror_interval,omitempty"` } // CreateBranchRepoOption options when creating a branch in a repository @@ -249,15 +252,16 @@ type MigrateRepoOptions struct { AuthPassword string `json:"auth_password"` AuthToken string `json:"auth_token"` - Mirror bool `json:"mirror"` - Private bool `json:"private"` - Description string `json:"description" binding:"MaxSize(255)"` - Wiki bool `json:"wiki"` - Milestones bool `json:"milestones"` - Labels bool `json:"labels"` - Issues bool `json:"issues"` - PullRequests bool `json:"pull_requests"` - Releases bool `json:"releases"` + Mirror bool `json:"mirror"` + Private bool `json:"private"` + Description string `json:"description" binding:"MaxSize(255)"` + Wiki bool `json:"wiki"` + Milestones bool `json:"milestones"` + Labels bool `json:"labels"` + Issues bool `json:"issues"` + PullRequests bool `json:"pull_requests"` + Releases bool `json:"releases"` + MirrorInterval string `json:"mirror_interval"` } // TokenAuth represents whether a service type supports token-based auth diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go index 3fd930090..f07599399 100644 --- a/routers/api/v1/repo/migrate.go +++ b/routers/api/v1/repo/migrate.go @@ -141,6 +141,7 @@ func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) { PullRequests: form.PullRequests, Releases: form.Releases, GitServiceType: gitServiceType, + MirrorInterval: form.MirrorInterval, } if opts.Mirror { opts.Issues = false diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index f1df31cca..82d380a81 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -9,6 +9,7 @@ import ( "fmt" "net/http" "strings" + "time" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" @@ -501,6 +502,12 @@ func Edit(ctx *context.APIContext, opts api.EditRepoOption) { } } + if opts.MirrorInterval != nil { + if err := updateMirrorInterval(ctx, opts); err != nil { + return + } + } + ctx.JSON(http.StatusOK, convert.ToRepo(ctx.Repo.Repository, ctx.Repo.AccessMode)) } @@ -783,6 +790,38 @@ func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) e return nil } +// updateMirrorInterval updates the repo's mirror Interval +func updateMirrorInterval(ctx *context.APIContext, opts api.EditRepoOption) error { + repo := ctx.Repo.Repository + + if opts.MirrorInterval != nil { + if !repo.IsMirror { + err := fmt.Errorf("repo is not a mirror, can not change mirror interval") + ctx.Error(http.StatusUnprocessableEntity, err.Error(), err) + return err + } + if err := repo.GetMirror(); err != nil { + log.Error("Failed to get mirror: %s", err) + ctx.Error(http.StatusInternalServerError, "MirrorInterval", err) + return err + } + if interval, err := time.ParseDuration(*opts.MirrorInterval); err == nil { + repo.Mirror.Interval = interval + if err := models.UpdateMirror(repo.Mirror); err != nil { + log.Error("Failed to Set Mirror Interval: %s", err) + ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err) + return err + } + log.Trace("Repository %s/%s Mirror Interval was Updated to %s", ctx.Repo.Owner.Name, repo.Name, interval) + } else { + log.Error("Wrong format for MirrorInternal Sent: %s", err) + ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err) + return err + } + } + return nil +} + // Delete one repository func Delete(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo} repository repoDelete diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 5de056f3c..81ccf4f72 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -13263,6 +13263,11 @@ "internal_tracker": { "$ref": "#/definitions/InternalTracker" }, + "mirror_interval": { + "description": "set to a string like `8h30m0s` to set the mirror interval time", + "type": "string", + "x-go-name": "MirrorInterval" + }, "name": { "description": "name of the repository", "type": "string", @@ -14248,6 +14253,10 @@ "type": "boolean", "x-go-name": "Mirror" }, + "mirror_interval": { + "type": "string", + "x-go-name": "MirrorInterval" + }, "private": { "type": "boolean", "x-go-name": "Private" @@ -14323,6 +14332,10 @@ "type": "boolean", "x-go-name": "Mirror" }, + "mirror_interval": { + "type": "string", + "x-go-name": "MirrorInterval" + }, "private": { "type": "boolean", "x-go-name": "Private" @@ -15307,6 +15320,10 @@ "type": "boolean", "x-go-name": "Mirror" }, + "mirror_interval": { + "type": "string", + "x-go-name": "MirrorInterval" + }, "name": { "type": "string", "x-go-name": "Name"