mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	* Move login related structs and functions to models/login * Fix test * Fix lint * Fix lint * Fix lint of windows * Fix lint * Fix test * Fix test * Only load necessary fixtures when preparing unit tests envs * Fix lint * Fix test * Fix test * Fix error log * Fix error log * Fix error log * remove unnecessary change * fix error log * merge main branch
		
			
				
	
	
		
			293 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			293 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2020 The Gitea 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 repository
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"fmt"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"code.gitea.io/gitea/models"
 | 
						|
	"code.gitea.io/gitea/models/db"
 | 
						|
	"code.gitea.io/gitea/modules/git"
 | 
						|
	"code.gitea.io/gitea/modules/log"
 | 
						|
	"code.gitea.io/gitea/modules/setting"
 | 
						|
	"code.gitea.io/gitea/modules/util"
 | 
						|
	"github.com/gobwas/glob"
 | 
						|
)
 | 
						|
 | 
						|
// AdoptRepository adopts a repository for the user/organization.
 | 
						|
func AdoptRepository(doer, u *models.User, opts models.CreateRepoOptions) (*models.Repository, error) {
 | 
						|
	if !doer.IsAdmin && !u.CanCreateRepo() {
 | 
						|
		return nil, models.ErrReachLimitOfRepo{
 | 
						|
			Limit: u.MaxRepoCreation,
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if len(opts.DefaultBranch) == 0 {
 | 
						|
		opts.DefaultBranch = setting.Repository.DefaultBranch
 | 
						|
	}
 | 
						|
 | 
						|
	repo := &models.Repository{
 | 
						|
		OwnerID:                         u.ID,
 | 
						|
		Owner:                           u,
 | 
						|
		OwnerName:                       u.Name,
 | 
						|
		Name:                            opts.Name,
 | 
						|
		LowerName:                       strings.ToLower(opts.Name),
 | 
						|
		Description:                     opts.Description,
 | 
						|
		OriginalURL:                     opts.OriginalURL,
 | 
						|
		OriginalServiceType:             opts.GitServiceType,
 | 
						|
		IsPrivate:                       opts.IsPrivate,
 | 
						|
		IsFsckEnabled:                   !opts.IsMirror,
 | 
						|
		CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch,
 | 
						|
		Status:                          opts.Status,
 | 
						|
		IsEmpty:                         !opts.AutoInit,
 | 
						|
	}
 | 
						|
 | 
						|
	if err := db.WithTx(func(ctx context.Context) error {
 | 
						|
		repoPath := models.RepoPath(u.Name, repo.Name)
 | 
						|
		isExist, err := util.IsExist(repoPath)
 | 
						|
		if err != nil {
 | 
						|
			log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		if !isExist {
 | 
						|
			return models.ErrRepoNotExist{
 | 
						|
				OwnerName: u.Name,
 | 
						|
				Name:      repo.Name,
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if err := models.CreateRepository(ctx, doer, u, repo, true); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		if err := adoptRepository(ctx, repoPath, doer, repo, opts); err != nil {
 | 
						|
			return fmt.Errorf("createDelegateHooks: %v", err)
 | 
						|
		}
 | 
						|
 | 
						|
		// Initialize Issue Labels if selected
 | 
						|
		if len(opts.IssueLabels) > 0 {
 | 
						|
			if err := models.InitializeLabels(ctx, repo.ID, opts.IssueLabels, false); err != nil {
 | 
						|
				return fmt.Errorf("InitializeLabels: %v", err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if stdout, err := git.NewCommand("update-server-info").
 | 
						|
			SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath)).
 | 
						|
			RunInDir(repoPath); err != nil {
 | 
						|
			log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
 | 
						|
			return fmt.Errorf("CreateRepository(git update-server-info): %v", err)
 | 
						|
		}
 | 
						|
		return nil
 | 
						|
	}); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return repo, nil
 | 
						|
}
 | 
						|
 | 
						|
// DeleteUnadoptedRepository deletes unadopted repository files from the filesystem
 | 
						|
func DeleteUnadoptedRepository(doer, u *models.User, repoName string) error {
 | 
						|
	if err := models.IsUsableRepoName(repoName); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	repoPath := models.RepoPath(u.Name, repoName)
 | 
						|
	isExist, err := util.IsExist(repoPath)
 | 
						|
	if err != nil {
 | 
						|
		log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if !isExist {
 | 
						|
		return models.ErrRepoNotExist{
 | 
						|
			OwnerName: u.Name,
 | 
						|
			Name:      repoName,
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if exist, err := models.IsRepositoryExist(u, repoName); err != nil {
 | 
						|
		return err
 | 
						|
	} else if exist {
 | 
						|
		return models.ErrRepoAlreadyExist{
 | 
						|
			Uname: u.Name,
 | 
						|
			Name:  repoName,
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return util.RemoveAll(repoPath)
 | 
						|
}
 | 
						|
 | 
						|
// ListUnadoptedRepositories lists all the unadopted repositories that match the provided query
 | 
						|
func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, int, error) {
 | 
						|
	globUser, _ := glob.Compile("*")
 | 
						|
	globRepo, _ := glob.Compile("*")
 | 
						|
 | 
						|
	qsplit := strings.SplitN(query, "/", 2)
 | 
						|
	if len(qsplit) > 0 && len(query) > 0 {
 | 
						|
		var err error
 | 
						|
		globUser, err = glob.Compile(qsplit[0])
 | 
						|
		if err != nil {
 | 
						|
			log.Info("Invalid glob expression '%s' (skipped): %v", qsplit[0], err)
 | 
						|
		}
 | 
						|
		if len(qsplit) > 1 {
 | 
						|
			globRepo, err = glob.Compile(qsplit[1])
 | 
						|
			if err != nil {
 | 
						|
				log.Info("Invalid glob expression '%s' (skipped): %v", qsplit[1], err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	start := (opts.Page - 1) * opts.PageSize
 | 
						|
	end := start + opts.PageSize
 | 
						|
 | 
						|
	repoNamesToCheck := make([]string, 0, opts.PageSize)
 | 
						|
 | 
						|
	repoNames := make([]string, 0, opts.PageSize)
 | 
						|
	var ctxUser *models.User
 | 
						|
 | 
						|
	count := 0
 | 
						|
 | 
						|
	// We're going to iterate by pagesize.
 | 
						|
	root := filepath.Join(setting.RepoRootPath)
 | 
						|
	if err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		if !info.IsDir() || path == root {
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
 | 
						|
		if !strings.ContainsRune(path[len(root)+1:], filepath.Separator) {
 | 
						|
			// Got a new user
 | 
						|
 | 
						|
			// Clean up old repoNamesToCheck
 | 
						|
			if len(repoNamesToCheck) > 0 {
 | 
						|
				repos, _, err := models.GetUserRepositories(&models.SearchRepoOptions{
 | 
						|
					Actor:   ctxUser,
 | 
						|
					Private: true,
 | 
						|
					ListOptions: db.ListOptions{
 | 
						|
						Page:     1,
 | 
						|
						PageSize: opts.PageSize,
 | 
						|
					}, LowerNames: repoNamesToCheck})
 | 
						|
				if err != nil {
 | 
						|
					return err
 | 
						|
				}
 | 
						|
				for _, name := range repoNamesToCheck {
 | 
						|
					found := false
 | 
						|
				repoLoopCatchup:
 | 
						|
					for i, repo := range repos {
 | 
						|
						if repo.LowerName == name {
 | 
						|
							found = true
 | 
						|
							repos = append(repos[:i], repos[i+1:]...)
 | 
						|
							break repoLoopCatchup
 | 
						|
						}
 | 
						|
					}
 | 
						|
					if !found {
 | 
						|
						if count >= start && count < end {
 | 
						|
							repoNames = append(repoNames, fmt.Sprintf("%s/%s", ctxUser.Name, name))
 | 
						|
						}
 | 
						|
						count++
 | 
						|
					}
 | 
						|
				}
 | 
						|
				repoNamesToCheck = repoNamesToCheck[:0]
 | 
						|
			}
 | 
						|
 | 
						|
			if !globUser.Match(info.Name()) {
 | 
						|
				return filepath.SkipDir
 | 
						|
			}
 | 
						|
 | 
						|
			ctxUser, err = models.GetUserByName(info.Name())
 | 
						|
			if err != nil {
 | 
						|
				if models.IsErrUserNotExist(err) {
 | 
						|
					log.Debug("Missing user: %s", info.Name())
 | 
						|
					return filepath.SkipDir
 | 
						|
				}
 | 
						|
				return err
 | 
						|
			}
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
 | 
						|
		name := info.Name()
 | 
						|
 | 
						|
		if !strings.HasSuffix(name, ".git") {
 | 
						|
			return filepath.SkipDir
 | 
						|
		}
 | 
						|
		name = name[:len(name)-4]
 | 
						|
		if models.IsUsableRepoName(name) != nil || strings.ToLower(name) != name || !globRepo.Match(name) {
 | 
						|
			return filepath.SkipDir
 | 
						|
		}
 | 
						|
		if count < end {
 | 
						|
			repoNamesToCheck = append(repoNamesToCheck, name)
 | 
						|
			if len(repoNamesToCheck) >= opts.PageSize {
 | 
						|
				repos, _, err := models.GetUserRepositories(&models.SearchRepoOptions{
 | 
						|
					Actor:   ctxUser,
 | 
						|
					Private: true,
 | 
						|
					ListOptions: db.ListOptions{
 | 
						|
						Page:     1,
 | 
						|
						PageSize: opts.PageSize,
 | 
						|
					}, LowerNames: repoNamesToCheck})
 | 
						|
				if err != nil {
 | 
						|
					return err
 | 
						|
				}
 | 
						|
				for _, name := range repoNamesToCheck {
 | 
						|
					found := false
 | 
						|
				repoLoop:
 | 
						|
					for i, repo := range repos {
 | 
						|
						if repo.LowerName == name {
 | 
						|
							found = true
 | 
						|
							repos = append(repos[:i], repos[i+1:]...)
 | 
						|
							break repoLoop
 | 
						|
						}
 | 
						|
					}
 | 
						|
					if !found {
 | 
						|
						if count >= start && count < end {
 | 
						|
							repoNames = append(repoNames, fmt.Sprintf("%s/%s", ctxUser.Name, name))
 | 
						|
						}
 | 
						|
						count++
 | 
						|
					}
 | 
						|
				}
 | 
						|
				repoNamesToCheck = repoNamesToCheck[:0]
 | 
						|
			}
 | 
						|
			return filepath.SkipDir
 | 
						|
		}
 | 
						|
		count++
 | 
						|
		return filepath.SkipDir
 | 
						|
	}); err != nil {
 | 
						|
		return nil, 0, err
 | 
						|
	}
 | 
						|
 | 
						|
	if len(repoNamesToCheck) > 0 {
 | 
						|
		repos, _, err := models.GetUserRepositories(&models.SearchRepoOptions{
 | 
						|
			Actor:   ctxUser,
 | 
						|
			Private: true,
 | 
						|
			ListOptions: db.ListOptions{
 | 
						|
				Page:     1,
 | 
						|
				PageSize: opts.PageSize,
 | 
						|
			}, LowerNames: repoNamesToCheck})
 | 
						|
		if err != nil {
 | 
						|
			return nil, 0, err
 | 
						|
		}
 | 
						|
		for _, name := range repoNamesToCheck {
 | 
						|
			found := false
 | 
						|
		repoLoop:
 | 
						|
			for i, repo := range repos {
 | 
						|
				if repo.LowerName == name {
 | 
						|
					found = true
 | 
						|
					repos = append(repos[:i], repos[i+1:]...)
 | 
						|
					break repoLoop
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if !found {
 | 
						|
				if count >= start && count < end {
 | 
						|
					repoNames = append(repoNames, fmt.Sprintf("%s/%s", ctxUser.Name, name))
 | 
						|
				}
 | 
						|
				count++
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return repoNames, count, nil
 | 
						|
}
 |