mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-10-31 06:21:11 +00:00 
			
		
		
		
	This adds a new `doctor` check: `fix-push-mirrors-without-git-remote`. The new check looks for push mirrors that do not have their remotes configured in git. If automatic fixing is enabled, it will remove these push mirrors from the database. The check is not run by default, and thus, must be invoked manually. It should be usable in a half-migrated state, too, and as such, fixes #1800. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/1853 Co-authored-by: Gergely Nagy <forgejo@gergo.csillger.hu> Co-committed-by: Gergely Nagy <forgejo@gergo.csillger.hu> (cherry picked from commit9038e07ef3) (cherry picked from commitb15bafcbc7) (cherry picked from commit93ba05a2dd) (cherry picked from commite418ea8082) (cherry picked from commit321790a91e) (cherry picked from commitf4e19d3323) (cherry picked from commit4d9923dee8) (cherry picked from commit049df69eda) Conflicts: services/doctor/push_mirror_consistency.go https://codeberg.org/forgejo/forgejo/pulls/2214
		
			
				
	
	
		
			155 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			155 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2021 The Gitea Authors. All rights reserved.
 | |
| // SPDX-License-Identifier: MIT
 | |
| 
 | |
| package repo
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"code.gitea.io/gitea/models/db"
 | |
| 	"code.gitea.io/gitea/modules/git"
 | |
| 	giturl "code.gitea.io/gitea/modules/git/url"
 | |
| 	"code.gitea.io/gitea/modules/log"
 | |
| 	"code.gitea.io/gitea/modules/setting"
 | |
| 	"code.gitea.io/gitea/modules/timeutil"
 | |
| 	"code.gitea.io/gitea/modules/util"
 | |
| 
 | |
| 	"xorm.io/builder"
 | |
| )
 | |
| 
 | |
| // ErrPushMirrorNotExist mirror does not exist error
 | |
| var ErrPushMirrorNotExist = util.NewNotExistErrorf("PushMirror does not exist")
 | |
| 
 | |
| // PushMirror represents mirror information of a repository.
 | |
| type PushMirror struct {
 | |
| 	ID            int64       `xorm:"pk autoincr"`
 | |
| 	RepoID        int64       `xorm:"INDEX"`
 | |
| 	Repo          *Repository `xorm:"-"`
 | |
| 	RemoteName    string
 | |
| 	RemoteAddress string `xorm:"VARCHAR(2048)"`
 | |
| 
 | |
| 	SyncOnCommit   bool `xorm:"NOT NULL DEFAULT true"`
 | |
| 	Interval       time.Duration
 | |
| 	CreatedUnix    timeutil.TimeStamp `xorm:"created"`
 | |
| 	LastUpdateUnix timeutil.TimeStamp `xorm:"INDEX last_update"`
 | |
| 	LastError      string             `xorm:"text"`
 | |
| }
 | |
| 
 | |
| type PushMirrorOptions struct {
 | |
| 	db.ListOptions
 | |
| 	ID         int64
 | |
| 	RepoID     int64
 | |
| 	RemoteName string
 | |
| }
 | |
| 
 | |
| func (opts PushMirrorOptions) ToConds() builder.Cond {
 | |
| 	cond := builder.NewCond()
 | |
| 	if opts.RepoID > 0 {
 | |
| 		cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
 | |
| 	}
 | |
| 	if opts.RemoteName != "" {
 | |
| 		cond = cond.And(builder.Eq{"remote_name": opts.RemoteName})
 | |
| 	}
 | |
| 	if opts.ID > 0 {
 | |
| 		cond = cond.And(builder.Eq{"id": opts.ID})
 | |
| 	}
 | |
| 	return cond
 | |
| }
 | |
| 
 | |
| func init() {
 | |
| 	db.RegisterModel(new(PushMirror))
 | |
| }
 | |
| 
 | |
| // GetRepository returns the path of the repository.
 | |
| func (m *PushMirror) GetRepository(ctx context.Context) *Repository {
 | |
| 	if m.Repo != nil {
 | |
| 		return m.Repo
 | |
| 	}
 | |
| 	var err error
 | |
| 	m.Repo, err = GetRepositoryByID(ctx, m.RepoID)
 | |
| 	if err != nil {
 | |
| 		log.Error("getRepositoryByID[%d]: %v", m.ID, err)
 | |
| 	}
 | |
| 	return m.Repo
 | |
| }
 | |
| 
 | |
| // GetRemoteName returns the name of the remote.
 | |
| func (m *PushMirror) GetRemoteName() string {
 | |
| 	return m.RemoteName
 | |
| }
 | |
| 
 | |
| // UpdatePushMirror updates the push-mirror
 | |
| func UpdatePushMirror(ctx context.Context, m *PushMirror) error {
 | |
| 	_, err := db.GetEngine(ctx).ID(m.ID).AllCols().Update(m)
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // UpdatePushMirrorInterval updates the push-mirror
 | |
| func UpdatePushMirrorInterval(ctx context.Context, m *PushMirror) error {
 | |
| 	_, err := db.GetEngine(ctx).ID(m.ID).Cols("interval").Update(m)
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func DeletePushMirrors(ctx context.Context, opts PushMirrorOptions) error {
 | |
| 	if opts.RepoID > 0 {
 | |
| 		_, err := db.Delete[PushMirror](ctx, opts)
 | |
| 		return err
 | |
| 	}
 | |
| 	return util.NewInvalidArgumentErrorf("repoID required and must be set")
 | |
| }
 | |
| 
 | |
| // GetPushMirrorsByRepoID returns push-mirror information of a repository.
 | |
| func GetPushMirrorsByRepoID(ctx context.Context, repoID int64, listOptions db.ListOptions) ([]*PushMirror, int64, error) {
 | |
| 	sess := db.GetEngine(ctx).Where("repo_id = ?", repoID)
 | |
| 	if listOptions.Page != 0 {
 | |
| 		sess = db.SetSessionPagination(sess, &listOptions)
 | |
| 		mirrors := make([]*PushMirror, 0, listOptions.PageSize)
 | |
| 		count, err := sess.FindAndCount(&mirrors)
 | |
| 		return mirrors, count, err
 | |
| 	}
 | |
| 	mirrors := make([]*PushMirror, 0, 10)
 | |
| 	count, err := sess.FindAndCount(&mirrors)
 | |
| 	return mirrors, count, err
 | |
| }
 | |
| 
 | |
| // GetPushMirrorsSyncedOnCommit returns push-mirrors for this repo that should be updated by new commits
 | |
| func GetPushMirrorsSyncedOnCommit(ctx context.Context, repoID int64) ([]*PushMirror, error) {
 | |
| 	mirrors := make([]*PushMirror, 0, 10)
 | |
| 	return mirrors, db.GetEngine(ctx).
 | |
| 		Where("repo_id = ? AND sync_on_commit = ?", repoID, true).
 | |
| 		Find(&mirrors)
 | |
| }
 | |
| 
 | |
| // PushMirrorsIterate iterates all push-mirror repositories.
 | |
| func PushMirrorsIterate(ctx context.Context, limit int, f func(idx int, bean any) error) error {
 | |
| 	sess := db.GetEngine(ctx).
 | |
| 		Where("last_update + (`interval` / ?) <= ?", time.Second, time.Now().Unix()).
 | |
| 		And("`interval` != 0").
 | |
| 		OrderBy("last_update ASC")
 | |
| 	if limit > 0 {
 | |
| 		sess = sess.Limit(limit)
 | |
| 	}
 | |
| 	return sess.Iterate(new(PushMirror), f)
 | |
| }
 | |
| 
 | |
| // GetPushMirrorRemoteAddress returns the address of associated with a repository's given remote.
 | |
| func GetPushMirrorRemoteAddress(ownerName, repoName, remoteName string) (string, error) {
 | |
| 	repoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(ownerName), strings.ToLower(repoName)+".git")
 | |
| 
 | |
| 	remoteURL, err := git.GetRemoteAddress(context.Background(), repoPath, remoteName)
 | |
| 	if err != nil {
 | |
| 		return "", fmt.Errorf("get remote %s's address of %s/%s failed: %v", remoteName, ownerName, repoName, err)
 | |
| 	}
 | |
| 
 | |
| 	u, err := giturl.Parse(remoteURL)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	u.User = nil
 | |
| 
 | |
| 	return u.String(), nil
 | |
| }
 |