return repo, UpdateRepository(ctx, repo, false)
}
+// SyncRepoTags synchronizes releases table with repository tags
+func SyncRepoTags(ctx context.Context, repoID int64) error {
+ repo, err := repo_model.GetRepositoryByID(ctx, repoID)
+ if err != nil {
+ return err
+ }
+
+ gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())
+ if err != nil {
+ return err
+ }
+ defer gitRepo.Close()
+
+ return SyncReleasesWithTags(ctx, repo, gitRepo)
+}
+
// SyncReleasesWithTags synchronizes release table with repository tags
func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository) error {
log.Debug("SyncReleasesWithTags: in Repo[%d:%s/%s]", repo.ID, repo.OwnerName, repo.Name)
dashboard.delete_missing_repos.started = Delete all repositories missing their Git files task started.
dashboard.delete_generated_repository_avatars = Delete generated repository avatars
dashboard.sync_repo_branches = Sync missed branches from git data to databases
+dashboard.sync_repo_tags = Sync tags from git data to database
dashboard.update_mirrors = Update Mirrors
dashboard.repo_health_check = Health check all repositories
dashboard.check_repo_stats = Check all repository statistics
dashboard.cancel_abandoned_jobs = Cancel abandoned jobs
dashboard.start_schedule_tasks = Start schedule tasks
dashboard.sync_branch.started = Branches Sync started
+dashboard.sync_tag.started = Tags Sync started
dashboard.rebuild_issue_indexer = Rebuild issue indexer
users.user_manage_panel = User Account Management
repo_migrations "code.gitea.io/gitea/services/migrations"
mirror_service "code.gitea.io/gitea/services/mirror"
pull_service "code.gitea.io/gitea/services/pull"
+ release_service "code.gitea.io/gitea/services/release"
repo_service "code.gitea.io/gitea/services/repository"
"code.gitea.io/gitea/services/repository/archiver"
"code.gitea.io/gitea/services/task"
mustInit(system.Init)
mustInitCtx(ctx, oauth2.Init)
+ mustInit(release_service.Init)
+
mustInitCtx(ctx, models.Init)
mustInitCtx(ctx, authmodel.Init)
mustInitCtx(ctx, repo_service.Init)
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/cron"
"code.gitea.io/gitea/services/forms"
+ release_service "code.gitea.io/gitea/services/release"
repo_service "code.gitea.io/gitea/services/repository"
)
}
}()
ctx.Flash.Success(ctx.Tr("admin.dashboard.sync_branch.started"))
+ case "sync_repo_tags":
+ go func() {
+ if err := release_service.AddAllRepoTagsToSyncQueue(graceful.GetManager().ShutdownContext()); err != nil {
+ log.Error("AddAllRepoTagsToSyncQueue: %v: %v", ctx.Doer.ID, err)
+ }
+ }()
+ ctx.Flash.Success(ctx.Tr("admin.dashboard.sync_tag.started"))
default:
task := cron.GetTask(form.Op)
if task != nil {
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/storage"
return nil
}
+
+// Init start release service
+func Init() error {
+ return initTagSyncQueue(graceful.GetManager().ShutdownContext())
+}
--- /dev/null
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package release
+
+import (
+ "context"
+ "errors"
+ "fmt"
+
+ "code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/modules/graceful"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/queue"
+ repo_module "code.gitea.io/gitea/modules/repository"
+
+ "xorm.io/builder"
+)
+
+type TagSyncOptions struct {
+ RepoID int64
+}
+
+// tagSyncQueue represents a queue to handle tag sync jobs.
+var tagSyncQueue *queue.WorkerPoolQueue[*TagSyncOptions]
+
+func handlerTagSync(items ...*TagSyncOptions) []*TagSyncOptions {
+ for _, opts := range items {
+ err := repo_module.SyncRepoTags(graceful.GetManager().ShutdownContext(), opts.RepoID)
+ if err != nil {
+ log.Error("syncRepoTags [%d] failed: %v", opts.RepoID, err)
+ }
+ }
+ return nil
+}
+
+func addRepoToTagSyncQueue(repoID int64) error {
+ return tagSyncQueue.Push(&TagSyncOptions{
+ RepoID: repoID,
+ })
+}
+
+func initTagSyncQueue(ctx context.Context) error {
+ tagSyncQueue = queue.CreateUniqueQueue(ctx, "tag_sync", handlerTagSync)
+ if tagSyncQueue == nil {
+ return errors.New("unable to create tag_sync queue")
+ }
+ go graceful.GetManager().RunWithCancel(tagSyncQueue)
+
+ return nil
+}
+
+func AddAllRepoTagsToSyncQueue(ctx context.Context) error {
+ if err := db.Iterate(ctx, builder.Eq{"is_empty": false}, func(ctx context.Context, repo *repo_model.Repository) error {
+ return addRepoToTagSyncQueue(repo.ID)
+ }); err != nil {
+ return fmt.Errorf("run sync all tags failed: %v", err)
+ }
+ return nil
+}
<td>{{ctx.Locale.Tr "admin.dashboard.sync_repo_branches"}}</td>
<td class="text right"><button type="submit" class="ui primary button" name="op" value="sync_repo_branches">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr>
+ <tr>
+ <td>{{ctx.Locale.Tr "admin.dashboard.sync_repo_tags"}}</td>
+ <td class="text right"><button type="submit" class="ui primary button" name="op" value="sync_repo_tags">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
+ </tr>
</tbody>
</table>
</form>