mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 19:06:18 +01:00 
			
		
		
		
	Move createrepository from module to service layer (#26927)
Repository creation depends on many models, so moving it to service layer is better.
This commit is contained in:
		| @@ -22,7 +22,6 @@ import ( | ||||
| 	"code.gitea.io/gitea/models/unit" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/models/webhook" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	issue_indexer "code.gitea.io/gitea/modules/indexer/issues" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| @@ -156,142 +155,6 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // CreateRepoOptions contains the create repository options | ||||
| type CreateRepoOptions struct { | ||||
| 	Name           string | ||||
| 	Description    string | ||||
| 	OriginalURL    string | ||||
| 	GitServiceType api.GitServiceType | ||||
| 	Gitignores     string | ||||
| 	IssueLabels    string | ||||
| 	License        string | ||||
| 	Readme         string | ||||
| 	DefaultBranch  string | ||||
| 	IsPrivate      bool | ||||
| 	IsMirror       bool | ||||
| 	IsTemplate     bool | ||||
| 	AutoInit       bool | ||||
| 	Status         repo_model.RepositoryStatus | ||||
| 	TrustModel     repo_model.TrustModelType | ||||
| 	MirrorInterval string | ||||
| } | ||||
|  | ||||
| // CreateRepository creates a repository for the user/organization. | ||||
| func CreateRepository(doer, u *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) { | ||||
| 	if !doer.IsAdmin && !u.CanCreateRepo() { | ||||
| 		return nil, repo_model.ErrReachLimitOfRepo{ | ||||
| 			Limit: u.MaxRepoCreation, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(opts.DefaultBranch) == 0 { | ||||
| 		opts.DefaultBranch = setting.Repository.DefaultBranch | ||||
| 	} | ||||
|  | ||||
| 	// Check if label template exist | ||||
| 	if len(opts.IssueLabels) > 0 { | ||||
| 		if _, err := LoadTemplateLabelsByDisplayName(opts.IssueLabels); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	repo := &repo_model.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, | ||||
| 		IsTemplate:                      opts.IsTemplate, | ||||
| 		CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch, | ||||
| 		Status:                          opts.Status, | ||||
| 		IsEmpty:                         !opts.AutoInit, | ||||
| 		TrustModel:                      opts.TrustModel, | ||||
| 		IsMirror:                        opts.IsMirror, | ||||
| 		DefaultBranch:                   opts.DefaultBranch, | ||||
| 	} | ||||
|  | ||||
| 	var rollbackRepo *repo_model.Repository | ||||
|  | ||||
| 	if err := db.WithTx(db.DefaultContext, func(ctx context.Context) error { | ||||
| 		if err := CreateRepositoryByExample(ctx, doer, u, repo, false, false); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		// No need for init mirror. | ||||
| 		if opts.IsMirror { | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		repoPath := repo_model.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 { | ||||
| 			// repo already exists - We have two or three options. | ||||
| 			// 1. We fail stating that the directory exists | ||||
| 			// 2. We create the db repository to go with this data and adopt the git repo | ||||
| 			// 3. We delete it and start afresh | ||||
| 			// | ||||
| 			// Previously Gitea would just delete and start afresh - this was naughty. | ||||
| 			// So we will now fail and delegate to other functionality to adopt or delete | ||||
| 			log.Error("Files already exist in %s and we are not going to adopt or delete.", repoPath) | ||||
| 			return repo_model.ErrRepoFilesAlreadyExist{ | ||||
| 				Uname: u.Name, | ||||
| 				Name:  repo.Name, | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if err = initRepository(ctx, repoPath, doer, repo, opts); err != nil { | ||||
| 			if err2 := util.RemoveAll(repoPath); err2 != nil { | ||||
| 				log.Error("initRepository: %v", err) | ||||
| 				return fmt.Errorf( | ||||
| 					"delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2) | ||||
| 			} | ||||
| 			return fmt.Errorf("initRepository: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		// Initialize Issue Labels if selected | ||||
| 		if len(opts.IssueLabels) > 0 { | ||||
| 			if err = InitializeLabels(ctx, repo.ID, opts.IssueLabels, false); err != nil { | ||||
| 				rollbackRepo = repo | ||||
| 				rollbackRepo.OwnerID = u.ID | ||||
| 				return fmt.Errorf("InitializeLabels: %w", err) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if err := CheckDaemonExportOK(ctx, repo); err != nil { | ||||
| 			return fmt.Errorf("checkDaemonExportOK: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		if stdout, _, err := git.NewCommand(ctx, "update-server-info"). | ||||
| 			SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath)). | ||||
| 			RunStdString(&git.RunOpts{Dir: repoPath}); err != nil { | ||||
| 			log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err) | ||||
| 			rollbackRepo = repo | ||||
| 			rollbackRepo.OwnerID = u.ID | ||||
| 			return fmt.Errorf("CreateRepository(git update-server-info): %w", err) | ||||
| 		} | ||||
| 		return nil | ||||
| 	}); err != nil { | ||||
| 		if rollbackRepo != nil { | ||||
| 			if errDelete := models.DeleteRepository(doer, rollbackRepo.OwnerID, rollbackRepo.ID); errDelete != nil { | ||||
| 				log.Error("Rollback deleteRepository: %v", errDelete) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return repo, nil | ||||
| } | ||||
|  | ||||
| const notRegularFileMode = os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | os.ModeDevice | os.ModeCharDevice | os.ModeIrregular | ||||
|  | ||||
| // getDirectorySize returns the disk consumption for a given path | ||||
|   | ||||
| @@ -4,151 +4,16 @@ | ||||
| package repository | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
|  | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	activities_model "code.gitea.io/gitea/models/activities" | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	"code.gitea.io/gitea/models/organization" | ||||
| 	"code.gitea.io/gitea/models/perm" | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	"code.gitea.io/gitea/models/unittest" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/structs" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestIncludesAllRepositoriesTeams(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
|  | ||||
| 	testTeamRepositories := func(teamID int64, repoIds []int64) { | ||||
| 		team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}) | ||||
| 		assert.NoError(t, team.LoadRepositories(db.DefaultContext), "%s: GetRepositories", team.Name) | ||||
| 		assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name) | ||||
| 		assert.Len(t, team.Repos, len(repoIds), "%s: repo count", team.Name) | ||||
| 		for i, rid := range repoIds { | ||||
| 			if rid > 0 { | ||||
| 				assert.True(t, models.HasRepository(team, rid), "%s: HasRepository(%d) %d", rid, i) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Get an admin user. | ||||
| 	user, err := user_model.GetUserByID(db.DefaultContext, 1) | ||||
| 	assert.NoError(t, err, "GetUserByID") | ||||
|  | ||||
| 	// Create org. | ||||
| 	org := &organization.Organization{ | ||||
| 		Name:       "All_repo", | ||||
| 		IsActive:   true, | ||||
| 		Type:       user_model.UserTypeOrganization, | ||||
| 		Visibility: structs.VisibleTypePublic, | ||||
| 	} | ||||
| 	assert.NoError(t, organization.CreateOrganization(org, user), "CreateOrganization") | ||||
|  | ||||
| 	// Check Owner team. | ||||
| 	ownerTeam, err := org.GetOwnerTeam(db.DefaultContext) | ||||
| 	assert.NoError(t, err, "GetOwnerTeam") | ||||
| 	assert.True(t, ownerTeam.IncludesAllRepositories, "Owner team includes all repositories") | ||||
|  | ||||
| 	// Create repos. | ||||
| 	repoIds := make([]int64, 0) | ||||
| 	for i := 0; i < 3; i++ { | ||||
| 		r, err := CreateRepository(user, org.AsUser(), CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)}) | ||||
| 		assert.NoError(t, err, "CreateRepository %d", i) | ||||
| 		if r != nil { | ||||
| 			repoIds = append(repoIds, r.ID) | ||||
| 		} | ||||
| 	} | ||||
| 	// Get fresh copy of Owner team after creating repos. | ||||
| 	ownerTeam, err = org.GetOwnerTeam(db.DefaultContext) | ||||
| 	assert.NoError(t, err, "GetOwnerTeam") | ||||
|  | ||||
| 	// Create teams and check repositories. | ||||
| 	teams := []*organization.Team{ | ||||
| 		ownerTeam, | ||||
| 		{ | ||||
| 			OrgID:                   org.ID, | ||||
| 			Name:                    "team one", | ||||
| 			AccessMode:              perm.AccessModeRead, | ||||
| 			IncludesAllRepositories: true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			OrgID:                   org.ID, | ||||
| 			Name:                    "team 2", | ||||
| 			AccessMode:              perm.AccessModeRead, | ||||
| 			IncludesAllRepositories: false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			OrgID:                   org.ID, | ||||
| 			Name:                    "team three", | ||||
| 			AccessMode:              perm.AccessModeWrite, | ||||
| 			IncludesAllRepositories: true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			OrgID:                   org.ID, | ||||
| 			Name:                    "team 4", | ||||
| 			AccessMode:              perm.AccessModeWrite, | ||||
| 			IncludesAllRepositories: false, | ||||
| 		}, | ||||
| 	} | ||||
| 	teamRepos := [][]int64{ | ||||
| 		repoIds, | ||||
| 		repoIds, | ||||
| 		{}, | ||||
| 		repoIds, | ||||
| 		{}, | ||||
| 	} | ||||
| 	for i, team := range teams { | ||||
| 		if i > 0 { // first team is Owner. | ||||
| 			assert.NoError(t, models.NewTeam(team), "%s: NewTeam", team.Name) | ||||
| 		} | ||||
| 		testTeamRepositories(team.ID, teamRepos[i]) | ||||
| 	} | ||||
|  | ||||
| 	// Update teams and check repositories. | ||||
| 	teams[3].IncludesAllRepositories = false | ||||
| 	teams[4].IncludesAllRepositories = true | ||||
| 	teamRepos[4] = repoIds | ||||
| 	for i, team := range teams { | ||||
| 		assert.NoError(t, models.UpdateTeam(team, false, true), "%s: UpdateTeam", team.Name) | ||||
| 		testTeamRepositories(team.ID, teamRepos[i]) | ||||
| 	} | ||||
|  | ||||
| 	// Create repo and check teams repositories. | ||||
| 	r, err := CreateRepository(user, org.AsUser(), CreateRepoOptions{Name: "repo-last"}) | ||||
| 	assert.NoError(t, err, "CreateRepository last") | ||||
| 	if r != nil { | ||||
| 		repoIds = append(repoIds, r.ID) | ||||
| 	} | ||||
| 	teamRepos[0] = repoIds | ||||
| 	teamRepos[1] = repoIds | ||||
| 	teamRepos[4] = repoIds | ||||
| 	for i, team := range teams { | ||||
| 		testTeamRepositories(team.ID, teamRepos[i]) | ||||
| 	} | ||||
|  | ||||
| 	// Remove repo and check teams repositories. | ||||
| 	assert.NoError(t, models.DeleteRepository(user, org.ID, repoIds[0]), "DeleteRepository") | ||||
| 	teamRepos[0] = repoIds[1:] | ||||
| 	teamRepos[1] = repoIds[1:] | ||||
| 	teamRepos[3] = repoIds[1:3] | ||||
| 	teamRepos[4] = repoIds[1:] | ||||
| 	for i, team := range teams { | ||||
| 		testTeamRepositories(team.ID, teamRepos[i]) | ||||
| 	} | ||||
|  | ||||
| 	// Wipe created items. | ||||
| 	for i, rid := range repoIds { | ||||
| 		if i > 0 { // first repo already deleted. | ||||
| 			assert.NoError(t, models.DeleteRepository(user, org.ID, rid), "DeleteRepository %d", i) | ||||
| 		} | ||||
| 	} | ||||
| 	assert.NoError(t, organization.DeleteOrganization(db.DefaultContext, org), "DeleteOrganization") | ||||
| } | ||||
|  | ||||
| func TestUpdateRepositoryVisibilityChanged(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
|  | ||||
|   | ||||
| @@ -241,7 +241,7 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r | ||||
| 		defaultBranch = templateRepo.DefaultBranch | ||||
| 	} | ||||
|  | ||||
| 	return initRepoCommit(ctx, tmpDir, repo, repo.Owner, defaultBranch) | ||||
| 	return InitRepoCommit(ctx, tmpDir, repo, repo.Owner, defaultBranch) | ||||
| } | ||||
|  | ||||
| func generateGitContent(ctx context.Context, repo, templateRepo, generateRepo *repo_model.Repository) (err error) { | ||||
| @@ -356,7 +356,7 @@ func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templ | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if err = checkInitRepository(ctx, owner.Name, generateRepo.Name); err != nil { | ||||
| 	if err = CheckInitRepository(ctx, owner.Name, generateRepo.Name); err != nil { | ||||
| 		return generateRepo, err | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -108,12 +108,7 @@ done | ||||
| } | ||||
|  | ||||
| // CreateDelegateHooks creates all the hooks scripts for the repo | ||||
| func CreateDelegateHooks(repoPath string) error { | ||||
| 	return createDelegateHooks(repoPath) | ||||
| } | ||||
|  | ||||
| // createDelegateHooks creates all the hooks scripts for the repo | ||||
| func createDelegateHooks(repoPath string) (err error) { | ||||
| func CreateDelegateHooks(repoPath string) (err error) { | ||||
| 	hookNames, hookTpls, giteaHookTpls := getHookTemplates() | ||||
| 	hookDir := filepath.Join(repoPath, "hooks") | ||||
|  | ||||
|   | ||||
| @@ -4,7 +4,6 @@ | ||||
| package repository | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| @@ -21,7 +20,6 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/options" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/templates/vars" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	asymkey_service "code.gitea.io/gitea/services/asymkey" | ||||
| ) | ||||
| @@ -126,95 +124,8 @@ func LoadRepoConfig() error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, repoPath string, opts CreateRepoOptions) error { | ||||
| 	commitTimeStr := time.Now().Format(time.RFC3339) | ||||
| 	authorSig := repo.Owner.NewGitSig() | ||||
|  | ||||
| 	// Because this may call hooks we should pass in the environment | ||||
| 	env := append(os.Environ(), | ||||
| 		"GIT_AUTHOR_NAME="+authorSig.Name, | ||||
| 		"GIT_AUTHOR_EMAIL="+authorSig.Email, | ||||
| 		"GIT_AUTHOR_DATE="+commitTimeStr, | ||||
| 		"GIT_COMMITTER_NAME="+authorSig.Name, | ||||
| 		"GIT_COMMITTER_EMAIL="+authorSig.Email, | ||||
| 		"GIT_COMMITTER_DATE="+commitTimeStr, | ||||
| 	) | ||||
|  | ||||
| 	// Clone to temporary path and do the init commit. | ||||
| 	if stdout, _, err := git.NewCommand(ctx, "clone").AddDynamicArguments(repoPath, tmpDir). | ||||
| 		SetDescription(fmt.Sprintf("prepareRepoCommit (git clone): %s to %s", repoPath, tmpDir)). | ||||
| 		RunStdString(&git.RunOpts{Dir: "", Env: env}); err != nil { | ||||
| 		log.Error("Failed to clone from %v into %s: stdout: %s\nError: %v", repo, tmpDir, stdout, err) | ||||
| 		return fmt.Errorf("git clone: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	// README | ||||
| 	data, err := options.Readme(opts.Readme) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("GetRepoInitFile[%s]: %w", opts.Readme, err) | ||||
| 	} | ||||
|  | ||||
| 	cloneLink := repo.CloneLink() | ||||
| 	match := map[string]string{ | ||||
| 		"Name":           repo.Name, | ||||
| 		"Description":    repo.Description, | ||||
| 		"CloneURL.SSH":   cloneLink.SSH, | ||||
| 		"CloneURL.HTTPS": cloneLink.HTTPS, | ||||
| 		"OwnerName":      repo.OwnerName, | ||||
| 	} | ||||
| 	res, err := vars.Expand(string(data), match) | ||||
| 	if err != nil { | ||||
| 		// here we could just log the error and continue the rendering | ||||
| 		log.Error("unable to expand template vars for repo README: %s, err: %v", opts.Readme, err) | ||||
| 	} | ||||
| 	if err = os.WriteFile(filepath.Join(tmpDir, "README.md"), | ||||
| 		[]byte(res), 0o644); err != nil { | ||||
| 		return fmt.Errorf("write README.md: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	// .gitignore | ||||
| 	if len(opts.Gitignores) > 0 { | ||||
| 		var buf bytes.Buffer | ||||
| 		names := strings.Split(opts.Gitignores, ",") | ||||
| 		for _, name := range names { | ||||
| 			data, err = options.Gitignore(name) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("GetRepoInitFile[%s]: %w", name, err) | ||||
| 			} | ||||
| 			buf.WriteString("# ---> " + name + "\n") | ||||
| 			buf.Write(data) | ||||
| 			buf.WriteString("\n") | ||||
| 		} | ||||
|  | ||||
| 		if buf.Len() > 0 { | ||||
| 			if err = os.WriteFile(filepath.Join(tmpDir, ".gitignore"), buf.Bytes(), 0o644); err != nil { | ||||
| 				return fmt.Errorf("write .gitignore: %w", err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// LICENSE | ||||
| 	if len(opts.License) > 0 { | ||||
| 		data, err = getLicense(opts.License, &licenseValues{ | ||||
| 			Owner: repo.OwnerName, | ||||
| 			Email: authorSig.Email, | ||||
| 			Repo:  repo.Name, | ||||
| 			Year:  time.Now().Format("2006"), | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("getLicense[%s]: %w", opts.License, err) | ||||
| 		} | ||||
|  | ||||
| 		if err = os.WriteFile(filepath.Join(tmpDir, "LICENSE"), data, 0o644); err != nil { | ||||
| 			return fmt.Errorf("write LICENSE: %w", err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // initRepoCommit temporarily changes with work directory. | ||||
| func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Repository, u *user_model.User, defaultBranch string) (err error) { | ||||
| // InitRepoCommit temporarily changes with work directory. | ||||
| func InitRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Repository, u *user_model.User, defaultBranch string) (err error) { | ||||
| 	commitTimeStr := time.Now().Format(time.RFC3339) | ||||
|  | ||||
| 	sig := u.NewGitSig() | ||||
| @@ -277,7 +188,7 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func checkInitRepository(ctx context.Context, owner, name string) (err error) { | ||||
| func CheckInitRepository(ctx context.Context, owner, name string) (err error) { | ||||
| 	// Somehow the directory could exist. | ||||
| 	repoPath := repo_model.RepoPath(owner, name) | ||||
| 	isExist, err := util.IsExist(repoPath) | ||||
| @@ -295,77 +206,12 @@ func checkInitRepository(ctx context.Context, owner, name string) (err error) { | ||||
| 	// Init git bare new repository. | ||||
| 	if err = git.InitRepository(ctx, repoPath, true); err != nil { | ||||
| 		return fmt.Errorf("git.InitRepository: %w", err) | ||||
| 	} else if err = createDelegateHooks(repoPath); err != nil { | ||||
| 	} else if err = CreateDelegateHooks(repoPath); err != nil { | ||||
| 		return fmt.Errorf("createDelegateHooks: %w", err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // InitRepository initializes README and .gitignore if needed. | ||||
| func initRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts CreateRepoOptions) (err error) { | ||||
| 	if err = checkInitRepository(ctx, repo.OwnerName, repo.Name); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Initialize repository according to user's choice. | ||||
| 	if opts.AutoInit { | ||||
| 		tmpDir, err := os.MkdirTemp(os.TempDir(), "gitea-"+repo.Name) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("Failed to create temp dir for repository %s: %w", repo.RepoPath(), err) | ||||
| 		} | ||||
| 		defer func() { | ||||
| 			if err := util.RemoveAll(tmpDir); err != nil { | ||||
| 				log.Warn("Unable to remove temporary directory: %s: Error: %v", tmpDir, err) | ||||
| 			} | ||||
| 		}() | ||||
|  | ||||
| 		if err = prepareRepoCommit(ctx, repo, tmpDir, repoPath, opts); err != nil { | ||||
| 			return fmt.Errorf("prepareRepoCommit: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		// Apply changes and commit. | ||||
| 		if err = initRepoCommit(ctx, tmpDir, repo, u, opts.DefaultBranch); err != nil { | ||||
| 			return fmt.Errorf("initRepoCommit: %w", err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Re-fetch the repository from database before updating it (else it would | ||||
| 	// override changes that were done earlier with sql) | ||||
| 	if repo, err = repo_model.GetRepositoryByID(ctx, repo.ID); err != nil { | ||||
| 		return fmt.Errorf("getRepositoryByID: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	if !opts.AutoInit { | ||||
| 		repo.IsEmpty = true | ||||
| 	} | ||||
|  | ||||
| 	repo.DefaultBranch = setting.Repository.DefaultBranch | ||||
|  | ||||
| 	if len(opts.DefaultBranch) > 0 { | ||||
| 		repo.DefaultBranch = opts.DefaultBranch | ||||
| 		gitRepo, err := git.OpenRepository(ctx, repo.RepoPath()) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("openRepository: %w", err) | ||||
| 		} | ||||
| 		defer gitRepo.Close() | ||||
| 		if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil { | ||||
| 			return fmt.Errorf("setDefaultBranch: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		if !repo.IsEmpty { | ||||
| 			if _, err := SyncRepoBranches(ctx, repo.ID, u.ID); err != nil { | ||||
| 				return fmt.Errorf("SyncRepoBranches: %w", err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if err = UpdateRepository(ctx, repo, false); err != nil { | ||||
| 		return fmt.Errorf("updateRepository: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // InitializeLabels adds a label set to a repository using a template | ||||
| func InitializeLabels(ctx context.Context, id int64, labelTemplate string, isOrg bool) error { | ||||
| 	list, err := LoadTemplateLabelsByDisplayName(labelTemplate) | ||||
|   | ||||
| @@ -13,14 +13,14 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/options" | ||||
| ) | ||||
|  | ||||
| type licenseValues struct { | ||||
| type LicenseValues struct { | ||||
| 	Owner string | ||||
| 	Email string | ||||
| 	Repo  string | ||||
| 	Year  string | ||||
| } | ||||
|  | ||||
| func getLicense(name string, values *licenseValues) ([]byte, error) { | ||||
| func GetLicense(name string, values *LicenseValues) ([]byte, error) { | ||||
| 	data, err := options.License(name) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("GetRepoInitFile[%s]: %w", name, err) | ||||
| @@ -28,7 +28,7 @@ func getLicense(name string, values *licenseValues) ([]byte, error) { | ||||
| 	return fillLicensePlaceholder(name, values, data), nil | ||||
| } | ||||
|  | ||||
| func fillLicensePlaceholder(name string, values *licenseValues, origin []byte) []byte { | ||||
| func fillLicensePlaceholder(name string, values *LicenseValues, origin []byte) []byte { | ||||
| 	placeholder := getLicensePlaceholder(name) | ||||
|  | ||||
| 	scanner := bufio.NewScanner(bytes.NewReader(origin)) | ||||
|   | ||||
| @@ -13,7 +13,7 @@ import ( | ||||
| func Test_getLicense(t *testing.T) { | ||||
| 	type args struct { | ||||
| 		name   string | ||||
| 		values *licenseValues | ||||
| 		values *LicenseValues | ||||
| 	} | ||||
| 	tests := []struct { | ||||
| 		name    string | ||||
| @@ -25,7 +25,7 @@ func Test_getLicense(t *testing.T) { | ||||
| 			name: "regular", | ||||
| 			args: args{ | ||||
| 				name:   "MIT", | ||||
| 				values: &licenseValues{Owner: "Gitea", Year: "2023"}, | ||||
| 				values: &LicenseValues{Owner: "Gitea", Year: "2023"}, | ||||
| 			}, | ||||
| 			want: `MIT License | ||||
|  | ||||
| @@ -49,11 +49,11 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI | ||||
| 	} | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			got, err := getLicense(tt.args.name, tt.args.values) | ||||
| 			if !tt.wantErr(t, err, fmt.Sprintf("getLicense(%v, %v)", tt.args.name, tt.args.values)) { | ||||
| 			got, err := GetLicense(tt.args.name, tt.args.values) | ||||
| 			if !tt.wantErr(t, err, fmt.Sprintf("GetLicense(%v, %v)", tt.args.name, tt.args.values)) { | ||||
| 				return | ||||
| 			} | ||||
| 			assert.Equalf(t, tt.want, string(got), "getLicense(%v, %v)", tt.args.name, tt.args.values) | ||||
| 			assert.Equalf(t, tt.want, string(got), "GetLicense(%v, %v)", tt.args.name, tt.args.values) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| @@ -61,7 +61,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI | ||||
| func Test_fillLicensePlaceholder(t *testing.T) { | ||||
| 	type args struct { | ||||
| 		name   string | ||||
| 		values *licenseValues | ||||
| 		values *LicenseValues | ||||
| 		origin string | ||||
| 	} | ||||
| 	tests := []struct { | ||||
| @@ -73,7 +73,7 @@ func Test_fillLicensePlaceholder(t *testing.T) { | ||||
| 			name: "owner", | ||||
| 			args: args{ | ||||
| 				name:   "regular", | ||||
| 				values: &licenseValues{Year: "2023", Owner: "Gitea", Email: "teabot@gitea.io", Repo: "gitea"}, | ||||
| 				values: &LicenseValues{Year: "2023", Owner: "Gitea", Email: "teabot@gitea.io", Repo: "gitea"}, | ||||
| 				origin: ` | ||||
| <name of author> | ||||
| <owner> | ||||
| @@ -104,7 +104,7 @@ Gitea | ||||
| 			name: "email", | ||||
| 			args: args{ | ||||
| 				name:   "regular", | ||||
| 				values: &licenseValues{Year: "2023", Owner: "Gitea", Email: "teabot@gitea.io", Repo: "gitea"}, | ||||
| 				values: &LicenseValues{Year: "2023", Owner: "Gitea", Email: "teabot@gitea.io", Repo: "gitea"}, | ||||
| 				origin: ` | ||||
| [EMAIL] | ||||
| `, | ||||
| @@ -117,7 +117,7 @@ teabot@gitea.io | ||||
| 			name: "repo", | ||||
| 			args: args{ | ||||
| 				name:   "regular", | ||||
| 				values: &licenseValues{Year: "2023", Owner: "Gitea", Email: "teabot@gitea.io", Repo: "gitea"}, | ||||
| 				values: &LicenseValues{Year: "2023", Owner: "Gitea", Email: "teabot@gitea.io", Repo: "gitea"}, | ||||
| 				origin: ` | ||||
| <program> | ||||
| <one line to give the program's name and a brief idea of what it does.> | ||||
| @@ -132,7 +132,7 @@ gitea | ||||
| 			name: "year", | ||||
| 			args: args{ | ||||
| 				name:   "regular", | ||||
| 				values: &licenseValues{Year: "2023", Owner: "Gitea", Email: "teabot@gitea.io", Repo: "gitea"}, | ||||
| 				values: &LicenseValues{Year: "2023", Owner: "Gitea", Email: "teabot@gitea.io", Repo: "gitea"}, | ||||
| 				origin: ` | ||||
| <year> | ||||
| [YEAR] | ||||
| @@ -155,7 +155,7 @@ gitea | ||||
| 			name: "0BSD", | ||||
| 			args: args{ | ||||
| 				name:   "0BSD", | ||||
| 				values: &licenseValues{Year: "2023", Owner: "Gitea", Email: "teabot@gitea.io", Repo: "gitea"}, | ||||
| 				values: &LicenseValues{Year: "2023", Owner: "Gitea", Email: "teabot@gitea.io", Repo: "gitea"}, | ||||
| 				origin: ` | ||||
| Copyright (C) YEAR by AUTHOR EMAIL | ||||
|  | ||||
|   | ||||
| @@ -256,11 +256,11 @@ func cleanUpMigrateGitConfig(ctx context.Context, repoPath string) error { | ||||
| // CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors. | ||||
| func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo_model.Repository, error) { | ||||
| 	repoPath := repo.RepoPath() | ||||
| 	if err := createDelegateHooks(repoPath); err != nil { | ||||
| 	if err := CreateDelegateHooks(repoPath); err != nil { | ||||
| 		return repo, fmt.Errorf("createDelegateHooks: %w", err) | ||||
| 	} | ||||
| 	if repo.HasWiki() { | ||||
| 		if err := createDelegateHooks(repo.WikiPath()); err != nil { | ||||
| 		if err := CreateDelegateHooks(repo.WikiPath()); err != nil { | ||||
| 			return repo, fmt.Errorf("createDelegateHooks.(wiki): %w", err) | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -9,7 +9,6 @@ import ( | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| 	repo_module "code.gitea.io/gitea/modules/repository" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	"code.gitea.io/gitea/routers/api/v1/utils" | ||||
| 	repo_service "code.gitea.io/gitea/services/repository" | ||||
| @@ -109,7 +108,7 @@ func AdoptRepository(ctx *context.APIContext) { | ||||
| 		ctx.NotFound() | ||||
| 		return | ||||
| 	} | ||||
| 	if _, err := repo_service.AdoptRepository(ctx, ctx.Doer, ctxUser, repo_module.CreateRepoOptions{ | ||||
| 	if _, err := repo_service.AdoptRepository(ctx, ctx.Doer, ctxUser, repo_service.CreateRepoOptions{ | ||||
| 		Name:      repoName, | ||||
| 		IsPrivate: true, | ||||
| 	}); err != nil { | ||||
|   | ||||
| @@ -22,7 +22,6 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/lfs" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	base "code.gitea.io/gitea/modules/migration" | ||||
| 	repo_module "code.gitea.io/gitea/modules/repository" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	api "code.gitea.io/gitea/modules/structs" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| @@ -31,6 +30,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/services/forms" | ||||
| 	"code.gitea.io/gitea/services/migrations" | ||||
| 	notify_service "code.gitea.io/gitea/services/notify" | ||||
| 	repo_service "code.gitea.io/gitea/services/repository" | ||||
| ) | ||||
|  | ||||
| // Migrate migrate remote git repository to gitea | ||||
| @@ -170,7 +170,7 @@ func Migrate(ctx *context.APIContext) { | ||||
| 		opts.Releases = false | ||||
| 	} | ||||
|  | ||||
| 	repo, err := repo_module.CreateRepository(ctx.Doer, repoOwner, repo_module.CreateRepoOptions{ | ||||
| 	repo, err := repo_service.CreateRepositoryDirectly(ctx.Doer, repoOwner, repo_service.CreateRepoOptions{ | ||||
| 		Name:           opts.RepoName, | ||||
| 		Description:    opts.Description, | ||||
| 		OriginalURL:    form.CloneAddr, | ||||
|   | ||||
| @@ -240,7 +240,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	repo, err := repo_service.CreateRepository(ctx, ctx.Doer, owner, repo_module.CreateRepoOptions{ | ||||
| 	repo, err := repo_service.CreateRepository(ctx, ctx.Doer, owner, repo_service.CreateRepoOptions{ | ||||
| 		Name:          opt.Name, | ||||
| 		Description:   opt.Description, | ||||
| 		IssueLabels:   opt.IssueLabels, | ||||
|   | ||||
| @@ -14,7 +14,6 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/base" | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	repo_module "code.gitea.io/gitea/modules/repository" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	"code.gitea.io/gitea/routers/web/explore" | ||||
| @@ -144,7 +143,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) { | ||||
| 	if has || !isDir { | ||||
| 		// Fallthrough to failure mode | ||||
| 	} else if action == "adopt" { | ||||
| 		if _, err := repo_service.AdoptRepository(ctx, ctx.Doer, ctxUser, repo_module.CreateRepoOptions{ | ||||
| 		if _, err := repo_service.AdoptRepository(ctx, ctx.Doer, ctxUser, repo_service.CreateRepoOptions{ | ||||
| 			Name:      dirSplit[1], | ||||
| 			IsPrivate: true, | ||||
| 		}); err != nil { | ||||
|   | ||||
| @@ -275,7 +275,7 @@ func CreatePost(ctx *context.Context) { | ||||
| 			return | ||||
| 		} | ||||
| 	} else { | ||||
| 		repo, err = repo_service.CreateRepository(ctx, ctx.Doer, ctxUser, repo_module.CreateRepoOptions{ | ||||
| 		repo, err = repo_service.CreateRepository(ctx, ctx.Doer, ctxUser, repo_service.CreateRepoOptions{ | ||||
| 			Name:          form.RepoName, | ||||
| 			Description:   form.Description, | ||||
| 			Gitignores:    form.Gitignores, | ||||
|   | ||||
| @@ -9,7 +9,6 @@ import ( | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| 	repo_module "code.gitea.io/gitea/modules/repository" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	repo_service "code.gitea.io/gitea/services/repository" | ||||
| @@ -45,7 +44,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) { | ||||
| 	if has || !isDir { | ||||
| 		// Fallthrough to failure mode | ||||
| 	} else if action == "adopt" && allowAdopt { | ||||
| 		if _, err := repo_service.AdoptRepository(ctx, ctxUser, ctxUser, repo_module.CreateRepoOptions{ | ||||
| 		if _, err := repo_service.AdoptRepository(ctx, ctxUser, ctxUser, repo_service.CreateRepoOptions{ | ||||
| 			Name:      dir, | ||||
| 			IsPrivate: true, | ||||
| 		}); err != nil { | ||||
|   | ||||
| @@ -31,6 +31,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/uri" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	"code.gitea.io/gitea/services/pull" | ||||
| 	repo_service "code.gitea.io/gitea/services/repository" | ||||
|  | ||||
| 	"github.com/google/uuid" | ||||
| ) | ||||
| @@ -99,7 +100,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate | ||||
|  | ||||
| 	var r *repo_model.Repository | ||||
| 	if opts.MigrateToRepoID <= 0 { | ||||
| 		r, err = repo_module.CreateRepository(g.doer, owner, repo_module.CreateRepoOptions{ | ||||
| 		r, err = repo_service.CreateRepositoryDirectly(g.doer, owner, repo_service.CreateRepoOptions{ | ||||
| 			Name:           g.repoName, | ||||
| 			Description:    repo.Description, | ||||
| 			OriginalURL:    repo.OriginalURL, | ||||
|   | ||||
| @@ -19,10 +19,10 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	"code.gitea.io/gitea/modules/json" | ||||
| 	cargo_module "code.gitea.io/gitea/modules/packages/cargo" | ||||
| 	repo_module "code.gitea.io/gitea/modules/repository" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/structs" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	repo_service "code.gitea.io/gitea/services/repository" | ||||
| 	files_service "code.gitea.io/gitea/services/repository/files" | ||||
| ) | ||||
|  | ||||
| @@ -206,7 +206,7 @@ func getOrCreateIndexRepository(ctx context.Context, doer, owner *user_model.Use | ||||
| 	repo, err := repo_model.GetRepositoryByOwnerAndName(ctx, owner.Name, IndexRepositoryName) | ||||
| 	if err != nil { | ||||
| 		if errors.Is(err, util.ErrNotExist) { | ||||
| 			repo, err = repo_module.CreateRepository(doer, owner, repo_module.CreateRepoOptions{ | ||||
| 			repo, err = repo_service.CreateRepositoryDirectly(doer, owner, repo_service.CreateRepoOptions{ | ||||
| 				Name: IndexRepositoryName, | ||||
| 			}) | ||||
| 			if err != nil { | ||||
|   | ||||
| @@ -27,7 +27,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| // AdoptRepository adopts pre-existing repository files for the user/organization. | ||||
| func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts repo_module.CreateRepoOptions) (*repo_model.Repository, error) { | ||||
| func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) { | ||||
| 	if !doer.IsAdmin && !u.CanCreateRepo() { | ||||
| 		return nil, repo_model.ErrReachLimitOfRepo{ | ||||
| 			Limit: u.MaxRepoCreation, | ||||
|   | ||||
							
								
								
									
										315
									
								
								services/repository/create.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										315
									
								
								services/repository/create.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,315 @@ | ||||
| // Copyright 2023 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
|  | ||||
| package repository | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/options" | ||||
| 	repo_module "code.gitea.io/gitea/modules/repository" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	api "code.gitea.io/gitea/modules/structs" | ||||
| 	"code.gitea.io/gitea/modules/templates/vars" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| ) | ||||
|  | ||||
| // CreateRepoOptions contains the create repository options | ||||
| type CreateRepoOptions struct { | ||||
| 	Name           string | ||||
| 	Description    string | ||||
| 	OriginalURL    string | ||||
| 	GitServiceType api.GitServiceType | ||||
| 	Gitignores     string | ||||
| 	IssueLabels    string | ||||
| 	License        string | ||||
| 	Readme         string | ||||
| 	DefaultBranch  string | ||||
| 	IsPrivate      bool | ||||
| 	IsMirror       bool | ||||
| 	IsTemplate     bool | ||||
| 	AutoInit       bool | ||||
| 	Status         repo_model.RepositoryStatus | ||||
| 	TrustModel     repo_model.TrustModelType | ||||
| 	MirrorInterval string | ||||
| } | ||||
|  | ||||
| func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, repoPath string, opts CreateRepoOptions) error { | ||||
| 	commitTimeStr := time.Now().Format(time.RFC3339) | ||||
| 	authorSig := repo.Owner.NewGitSig() | ||||
|  | ||||
| 	// Because this may call hooks we should pass in the environment | ||||
| 	env := append(os.Environ(), | ||||
| 		"GIT_AUTHOR_NAME="+authorSig.Name, | ||||
| 		"GIT_AUTHOR_EMAIL="+authorSig.Email, | ||||
| 		"GIT_AUTHOR_DATE="+commitTimeStr, | ||||
| 		"GIT_COMMITTER_NAME="+authorSig.Name, | ||||
| 		"GIT_COMMITTER_EMAIL="+authorSig.Email, | ||||
| 		"GIT_COMMITTER_DATE="+commitTimeStr, | ||||
| 	) | ||||
|  | ||||
| 	// Clone to temporary path and do the init commit. | ||||
| 	if stdout, _, err := git.NewCommand(ctx, "clone").AddDynamicArguments(repoPath, tmpDir). | ||||
| 		SetDescription(fmt.Sprintf("prepareRepoCommit (git clone): %s to %s", repoPath, tmpDir)). | ||||
| 		RunStdString(&git.RunOpts{Dir: "", Env: env}); err != nil { | ||||
| 		log.Error("Failed to clone from %v into %s: stdout: %s\nError: %v", repo, tmpDir, stdout, err) | ||||
| 		return fmt.Errorf("git clone: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	// README | ||||
| 	data, err := options.Readme(opts.Readme) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("GetRepoInitFile[%s]: %w", opts.Readme, err) | ||||
| 	} | ||||
|  | ||||
| 	cloneLink := repo.CloneLink() | ||||
| 	match := map[string]string{ | ||||
| 		"Name":           repo.Name, | ||||
| 		"Description":    repo.Description, | ||||
| 		"CloneURL.SSH":   cloneLink.SSH, | ||||
| 		"CloneURL.HTTPS": cloneLink.HTTPS, | ||||
| 		"OwnerName":      repo.OwnerName, | ||||
| 	} | ||||
| 	res, err := vars.Expand(string(data), match) | ||||
| 	if err != nil { | ||||
| 		// here we could just log the error and continue the rendering | ||||
| 		log.Error("unable to expand template vars for repo README: %s, err: %v", opts.Readme, err) | ||||
| 	} | ||||
| 	if err = os.WriteFile(filepath.Join(tmpDir, "README.md"), | ||||
| 		[]byte(res), 0o644); err != nil { | ||||
| 		return fmt.Errorf("write README.md: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	// .gitignore | ||||
| 	if len(opts.Gitignores) > 0 { | ||||
| 		var buf bytes.Buffer | ||||
| 		names := strings.Split(opts.Gitignores, ",") | ||||
| 		for _, name := range names { | ||||
| 			data, err = options.Gitignore(name) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("GetRepoInitFile[%s]: %w", name, err) | ||||
| 			} | ||||
| 			buf.WriteString("# ---> " + name + "\n") | ||||
| 			buf.Write(data) | ||||
| 			buf.WriteString("\n") | ||||
| 		} | ||||
|  | ||||
| 		if buf.Len() > 0 { | ||||
| 			if err = os.WriteFile(filepath.Join(tmpDir, ".gitignore"), buf.Bytes(), 0o644); err != nil { | ||||
| 				return fmt.Errorf("write .gitignore: %w", err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// LICENSE | ||||
| 	if len(opts.License) > 0 { | ||||
| 		data, err = repo_module.GetLicense(opts.License, &repo_module.LicenseValues{ | ||||
| 			Owner: repo.OwnerName, | ||||
| 			Email: authorSig.Email, | ||||
| 			Repo:  repo.Name, | ||||
| 			Year:  time.Now().Format("2006"), | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("getLicense[%s]: %w", opts.License, err) | ||||
| 		} | ||||
|  | ||||
| 		if err = os.WriteFile(filepath.Join(tmpDir, "LICENSE"), data, 0o644); err != nil { | ||||
| 			return fmt.Errorf("write LICENSE: %w", err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // InitRepository initializes README and .gitignore if needed. | ||||
| func initRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts CreateRepoOptions) (err error) { | ||||
| 	if err = repo_module.CheckInitRepository(ctx, repo.OwnerName, repo.Name); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Initialize repository according to user's choice. | ||||
| 	if opts.AutoInit { | ||||
| 		tmpDir, err := os.MkdirTemp(os.TempDir(), "gitea-"+repo.Name) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("Failed to create temp dir for repository %s: %w", repo.RepoPath(), err) | ||||
| 		} | ||||
| 		defer func() { | ||||
| 			if err := util.RemoveAll(tmpDir); err != nil { | ||||
| 				log.Warn("Unable to remove temporary directory: %s: Error: %v", tmpDir, err) | ||||
| 			} | ||||
| 		}() | ||||
|  | ||||
| 		if err = prepareRepoCommit(ctx, repo, tmpDir, repoPath, opts); err != nil { | ||||
| 			return fmt.Errorf("prepareRepoCommit: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		// Apply changes and commit. | ||||
| 		if err = repo_module.InitRepoCommit(ctx, tmpDir, repo, u, opts.DefaultBranch); err != nil { | ||||
| 			return fmt.Errorf("initRepoCommit: %w", err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Re-fetch the repository from database before updating it (else it would | ||||
| 	// override changes that were done earlier with sql) | ||||
| 	if repo, err = repo_model.GetRepositoryByID(ctx, repo.ID); err != nil { | ||||
| 		return fmt.Errorf("getRepositoryByID: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	if !opts.AutoInit { | ||||
| 		repo.IsEmpty = true | ||||
| 	} | ||||
|  | ||||
| 	repo.DefaultBranch = setting.Repository.DefaultBranch | ||||
|  | ||||
| 	if len(opts.DefaultBranch) > 0 { | ||||
| 		repo.DefaultBranch = opts.DefaultBranch | ||||
| 		gitRepo, err := git.OpenRepository(ctx, repo.RepoPath()) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("openRepository: %w", err) | ||||
| 		} | ||||
| 		defer gitRepo.Close() | ||||
| 		if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil { | ||||
| 			return fmt.Errorf("setDefaultBranch: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		if !repo.IsEmpty { | ||||
| 			if _, err := repo_module.SyncRepoBranches(ctx, repo.ID, u.ID); err != nil { | ||||
| 				return fmt.Errorf("SyncRepoBranches: %w", err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if err = UpdateRepository(ctx, repo, false); err != nil { | ||||
| 		return fmt.Errorf("updateRepository: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // CreateRepositoryDirectly creates a repository for the user/organization. | ||||
| func CreateRepositoryDirectly(doer, u *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) { | ||||
| 	if !doer.IsAdmin && !u.CanCreateRepo() { | ||||
| 		return nil, repo_model.ErrReachLimitOfRepo{ | ||||
| 			Limit: u.MaxRepoCreation, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(opts.DefaultBranch) == 0 { | ||||
| 		opts.DefaultBranch = setting.Repository.DefaultBranch | ||||
| 	} | ||||
|  | ||||
| 	// Check if label template exist | ||||
| 	if len(opts.IssueLabels) > 0 { | ||||
| 		if _, err := repo_module.LoadTemplateLabelsByDisplayName(opts.IssueLabels); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	repo := &repo_model.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, | ||||
| 		IsTemplate:                      opts.IsTemplate, | ||||
| 		CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch, | ||||
| 		Status:                          opts.Status, | ||||
| 		IsEmpty:                         !opts.AutoInit, | ||||
| 		TrustModel:                      opts.TrustModel, | ||||
| 		IsMirror:                        opts.IsMirror, | ||||
| 		DefaultBranch:                   opts.DefaultBranch, | ||||
| 	} | ||||
|  | ||||
| 	var rollbackRepo *repo_model.Repository | ||||
|  | ||||
| 	if err := db.WithTx(db.DefaultContext, func(ctx context.Context) error { | ||||
| 		if err := repo_module.CreateRepositoryByExample(ctx, doer, u, repo, false, false); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		// No need for init mirror. | ||||
| 		if opts.IsMirror { | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		repoPath := repo_model.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 { | ||||
| 			// repo already exists - We have two or three options. | ||||
| 			// 1. We fail stating that the directory exists | ||||
| 			// 2. We create the db repository to go with this data and adopt the git repo | ||||
| 			// 3. We delete it and start afresh | ||||
| 			// | ||||
| 			// Previously Gitea would just delete and start afresh - this was naughty. | ||||
| 			// So we will now fail and delegate to other functionality to adopt or delete | ||||
| 			log.Error("Files already exist in %s and we are not going to adopt or delete.", repoPath) | ||||
| 			return repo_model.ErrRepoFilesAlreadyExist{ | ||||
| 				Uname: u.Name, | ||||
| 				Name:  repo.Name, | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if err = initRepository(ctx, repoPath, doer, repo, opts); err != nil { | ||||
| 			if err2 := util.RemoveAll(repoPath); err2 != nil { | ||||
| 				log.Error("initRepository: %v", err) | ||||
| 				return fmt.Errorf( | ||||
| 					"delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2) | ||||
| 			} | ||||
| 			return fmt.Errorf("initRepository: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		// Initialize Issue Labels if selected | ||||
| 		if len(opts.IssueLabels) > 0 { | ||||
| 			if err = repo_module.InitializeLabels(ctx, repo.ID, opts.IssueLabels, false); err != nil { | ||||
| 				rollbackRepo = repo | ||||
| 				rollbackRepo.OwnerID = u.ID | ||||
| 				return fmt.Errorf("InitializeLabels: %w", err) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if err := repo_module.CheckDaemonExportOK(ctx, repo); err != nil { | ||||
| 			return fmt.Errorf("checkDaemonExportOK: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		if stdout, _, err := git.NewCommand(ctx, "update-server-info"). | ||||
| 			SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath)). | ||||
| 			RunStdString(&git.RunOpts{Dir: repoPath}); err != nil { | ||||
| 			log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err) | ||||
| 			rollbackRepo = repo | ||||
| 			rollbackRepo.OwnerID = u.ID | ||||
| 			return fmt.Errorf("CreateRepository(git update-server-info): %w", err) | ||||
| 		} | ||||
| 		return nil | ||||
| 	}); err != nil { | ||||
| 		if rollbackRepo != nil { | ||||
| 			if errDelete := models.DeleteRepository(doer, rollbackRepo.OwnerID, rollbackRepo.ID); errDelete != nil { | ||||
| 				log.Error("Rollback deleteRepository: %v", errDelete) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return repo, nil | ||||
| } | ||||
							
								
								
									
										148
									
								
								services/repository/create_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								services/repository/create_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | ||||
| // Copyright 2023 The Gitea Authors. All rights reserved. | ||||
| // SPDX-License-Identifier: MIT | ||||
|  | ||||
| package repository | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
|  | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	"code.gitea.io/gitea/models/organization" | ||||
| 	"code.gitea.io/gitea/models/perm" | ||||
| 	"code.gitea.io/gitea/models/unittest" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/structs" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestIncludesAllRepositoriesTeams(t *testing.T) { | ||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||
|  | ||||
| 	testTeamRepositories := func(teamID int64, repoIds []int64) { | ||||
| 		team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}) | ||||
| 		assert.NoError(t, team.LoadRepositories(db.DefaultContext), "%s: GetRepositories", team.Name) | ||||
| 		assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name) | ||||
| 		assert.Len(t, team.Repos, len(repoIds), "%s: repo count", team.Name) | ||||
| 		for i, rid := range repoIds { | ||||
| 			if rid > 0 { | ||||
| 				assert.True(t, models.HasRepository(team, rid), "%s: HasRepository(%d) %d", rid, i) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Get an admin user. | ||||
| 	user, err := user_model.GetUserByID(db.DefaultContext, 1) | ||||
| 	assert.NoError(t, err, "GetUserByID") | ||||
|  | ||||
| 	// Create org. | ||||
| 	org := &organization.Organization{ | ||||
| 		Name:       "All_repo", | ||||
| 		IsActive:   true, | ||||
| 		Type:       user_model.UserTypeOrganization, | ||||
| 		Visibility: structs.VisibleTypePublic, | ||||
| 	} | ||||
| 	assert.NoError(t, organization.CreateOrganization(org, user), "CreateOrganization") | ||||
|  | ||||
| 	// Check Owner team. | ||||
| 	ownerTeam, err := org.GetOwnerTeam(db.DefaultContext) | ||||
| 	assert.NoError(t, err, "GetOwnerTeam") | ||||
| 	assert.True(t, ownerTeam.IncludesAllRepositories, "Owner team includes all repositories") | ||||
|  | ||||
| 	// Create repos. | ||||
| 	repoIds := make([]int64, 0) | ||||
| 	for i := 0; i < 3; i++ { | ||||
| 		r, err := CreateRepositoryDirectly(user, org.AsUser(), CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)}) | ||||
| 		assert.NoError(t, err, "CreateRepository %d", i) | ||||
| 		if r != nil { | ||||
| 			repoIds = append(repoIds, r.ID) | ||||
| 		} | ||||
| 	} | ||||
| 	// Get fresh copy of Owner team after creating repos. | ||||
| 	ownerTeam, err = org.GetOwnerTeam(db.DefaultContext) | ||||
| 	assert.NoError(t, err, "GetOwnerTeam") | ||||
|  | ||||
| 	// Create teams and check repositories. | ||||
| 	teams := []*organization.Team{ | ||||
| 		ownerTeam, | ||||
| 		{ | ||||
| 			OrgID:                   org.ID, | ||||
| 			Name:                    "team one", | ||||
| 			AccessMode:              perm.AccessModeRead, | ||||
| 			IncludesAllRepositories: true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			OrgID:                   org.ID, | ||||
| 			Name:                    "team 2", | ||||
| 			AccessMode:              perm.AccessModeRead, | ||||
| 			IncludesAllRepositories: false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			OrgID:                   org.ID, | ||||
| 			Name:                    "team three", | ||||
| 			AccessMode:              perm.AccessModeWrite, | ||||
| 			IncludesAllRepositories: true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			OrgID:                   org.ID, | ||||
| 			Name:                    "team 4", | ||||
| 			AccessMode:              perm.AccessModeWrite, | ||||
| 			IncludesAllRepositories: false, | ||||
| 		}, | ||||
| 	} | ||||
| 	teamRepos := [][]int64{ | ||||
| 		repoIds, | ||||
| 		repoIds, | ||||
| 		{}, | ||||
| 		repoIds, | ||||
| 		{}, | ||||
| 	} | ||||
| 	for i, team := range teams { | ||||
| 		if i > 0 { // first team is Owner. | ||||
| 			assert.NoError(t, models.NewTeam(team), "%s: NewTeam", team.Name) | ||||
| 		} | ||||
| 		testTeamRepositories(team.ID, teamRepos[i]) | ||||
| 	} | ||||
|  | ||||
| 	// Update teams and check repositories. | ||||
| 	teams[3].IncludesAllRepositories = false | ||||
| 	teams[4].IncludesAllRepositories = true | ||||
| 	teamRepos[4] = repoIds | ||||
| 	for i, team := range teams { | ||||
| 		assert.NoError(t, models.UpdateTeam(team, false, true), "%s: UpdateTeam", team.Name) | ||||
| 		testTeamRepositories(team.ID, teamRepos[i]) | ||||
| 	} | ||||
|  | ||||
| 	// Create repo and check teams repositories. | ||||
| 	r, err := CreateRepositoryDirectly(user, org.AsUser(), CreateRepoOptions{Name: "repo-last"}) | ||||
| 	assert.NoError(t, err, "CreateRepository last") | ||||
| 	if r != nil { | ||||
| 		repoIds = append(repoIds, r.ID) | ||||
| 	} | ||||
| 	teamRepos[0] = repoIds | ||||
| 	teamRepos[1] = repoIds | ||||
| 	teamRepos[4] = repoIds | ||||
| 	for i, team := range teams { | ||||
| 		testTeamRepositories(team.ID, teamRepos[i]) | ||||
| 	} | ||||
|  | ||||
| 	// Remove repo and check teams repositories. | ||||
| 	assert.NoError(t, models.DeleteRepository(user, org.ID, repoIds[0]), "DeleteRepository") | ||||
| 	teamRepos[0] = repoIds[1:] | ||||
| 	teamRepos[1] = repoIds[1:] | ||||
| 	teamRepos[3] = repoIds[1:3] | ||||
| 	teamRepos[4] = repoIds[1:] | ||||
| 	for i, team := range teams { | ||||
| 		testTeamRepositories(team.ID, teamRepos[i]) | ||||
| 	} | ||||
|  | ||||
| 	// Wipe created items. | ||||
| 	for i, rid := range repoIds { | ||||
| 		if i > 0 { // first repo already deleted. | ||||
| 			assert.NoError(t, models.DeleteRepository(user, org.ID, rid), "DeleteRepository %d", i) | ||||
| 		} | ||||
| 	} | ||||
| 	assert.NoError(t, organization.DeleteOrganization(db.DefaultContext, org), "DeleteOrganization") | ||||
| } | ||||
| @@ -40,8 +40,8 @@ type WebSearchResults struct { | ||||
| } | ||||
|  | ||||
| // CreateRepository creates a repository for the user/organization. | ||||
| func CreateRepository(ctx context.Context, doer, owner *user_model.User, opts repo_module.CreateRepoOptions) (*repo_model.Repository, error) { | ||||
| 	repo, err := repo_module.CreateRepository(doer, owner, opts) | ||||
| func CreateRepository(ctx context.Context, doer, owner *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) { | ||||
| 	repo, err := CreateRepositoryDirectly(doer, owner, opts) | ||||
| 	if err != nil { | ||||
| 		// No need to rollback here we should do this in CreateRepository... | ||||
| 		return nil, err | ||||
| @@ -84,7 +84,7 @@ func PushCreateRepo(ctx context.Context, authUser, owner *user_model.User, repoN | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	repo, err := CreateRepository(ctx, authUser, owner, repo_module.CreateRepoOptions{ | ||||
| 	repo, err := CreateRepository(ctx, authUser, owner, CreateRepoOptions{ | ||||
| 		Name:      repoName, | ||||
| 		IsPrivate: setting.Repository.DefaultPushCreatePrivate, | ||||
| 	}) | ||||
|   | ||||
| @@ -14,12 +14,12 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	base "code.gitea.io/gitea/modules/migration" | ||||
| 	"code.gitea.io/gitea/modules/queue" | ||||
| 	repo_module "code.gitea.io/gitea/modules/repository" | ||||
| 	"code.gitea.io/gitea/modules/secret" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/structs" | ||||
| 	"code.gitea.io/gitea/modules/timeutil" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	repo_service "code.gitea.io/gitea/services/repository" | ||||
| ) | ||||
|  | ||||
| // taskQueue is a global queue of tasks | ||||
| @@ -100,7 +100,7 @@ func CreateMigrateTask(doer, u *user_model.User, opts base.MigrateOptions) (*adm | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	repo, err := repo_module.CreateRepository(doer, u, repo_module.CreateRepoOptions{ | ||||
| 	repo, err := repo_service.CreateRepositoryDirectly(doer, u, repo_service.CreateRepoOptions{ | ||||
| 		Name:           opts.RepoName, | ||||
| 		Description:    opts.Description, | ||||
| 		OriginalURL:    opts.OriginalURL, | ||||
|   | ||||
| @@ -18,7 +18,6 @@ import ( | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	actions_module "code.gitea.io/gitea/modules/actions" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	repo_module "code.gitea.io/gitea/modules/repository" | ||||
| 	pull_service "code.gitea.io/gitea/services/pull" | ||||
| 	repo_service "code.gitea.io/gitea/services/repository" | ||||
| 	files_service "code.gitea.io/gitea/services/repository/files" | ||||
| @@ -32,7 +31,7 @@ func TestPullRequestTargetEvent(t *testing.T) { | ||||
| 		user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) // owner of the forked repo | ||||
|  | ||||
| 		// create the base repo | ||||
| 		baseRepo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_module.CreateRepoOptions{ | ||||
| 		baseRepo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{ | ||||
| 			Name:          "repo-pull-request-target", | ||||
| 			Description:   "test pull-request-target event", | ||||
| 			AutoInit:      true, | ||||
|   | ||||
| @@ -16,6 +16,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/repository" | ||||
| 	mirror_service "code.gitea.io/gitea/services/mirror" | ||||
| 	release_service "code.gitea.io/gitea/services/release" | ||||
| 	repo_service "code.gitea.io/gitea/services/repository" | ||||
| 	"code.gitea.io/gitea/tests" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| @@ -38,7 +39,7 @@ func TestMirrorPull(t *testing.T) { | ||||
| 		Releases:    false, | ||||
| 	} | ||||
|  | ||||
| 	mirrorRepo, err := repository.CreateRepository(user, user, repository.CreateRepoOptions{ | ||||
| 	mirrorRepo, err := repo_service.CreateRepositoryDirectly(user, user, repo_service.CreateRepoOptions{ | ||||
| 		Name:        opts.RepoName, | ||||
| 		Description: opts.Description, | ||||
| 		IsPrivate:   opts.Private, | ||||
|   | ||||
| @@ -17,10 +17,10 @@ import ( | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	gitea_context "code.gitea.io/gitea/modules/context" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	"code.gitea.io/gitea/modules/repository" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/services/migrations" | ||||
| 	mirror_service "code.gitea.io/gitea/services/mirror" | ||||
| 	repo_service "code.gitea.io/gitea/services/repository" | ||||
| 	"code.gitea.io/gitea/tests" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| @@ -39,7 +39,7 @@ func testMirrorPush(t *testing.T, u *url.URL) { | ||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||
| 	srcRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | ||||
|  | ||||
| 	mirrorRepo, err := repository.CreateRepository(user, user, repository.CreateRepoOptions{ | ||||
| 	mirrorRepo, err := repo_service.CreateRepositoryDirectly(user, user, repo_service.CreateRepoOptions{ | ||||
| 		Name: "test-push-mirror", | ||||
| 	}) | ||||
| 	assert.NoError(t, err) | ||||
|   | ||||
| @@ -25,7 +25,6 @@ import ( | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/models/webhook" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	repo_module "code.gitea.io/gitea/modules/repository" | ||||
| 	api "code.gitea.io/gitea/modules/structs" | ||||
| 	"code.gitea.io/gitea/modules/test" | ||||
| 	"code.gitea.io/gitea/modules/translation" | ||||
| @@ -356,7 +355,7 @@ func TestConflictChecking(t *testing.T) { | ||||
| 		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||||
|  | ||||
| 		// Create new clean repo to test conflict checking. | ||||
| 		baseRepo, err := repo_service.CreateRepository(db.DefaultContext, user, user, repo_module.CreateRepoOptions{ | ||||
| 		baseRepo, err := repo_service.CreateRepository(db.DefaultContext, user, user, repo_service.CreateRepoOptions{ | ||||
| 			Name:          "conflict-checking", | ||||
| 			Description:   "Tempo repo", | ||||
| 			AutoInit:      true, | ||||
|   | ||||
| @@ -16,7 +16,6 @@ import ( | ||||
| 	"code.gitea.io/gitea/models/unittest" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	repo_module "code.gitea.io/gitea/modules/repository" | ||||
| 	pull_service "code.gitea.io/gitea/services/pull" | ||||
| 	repo_service "code.gitea.io/gitea/services/repository" | ||||
| 	files_service "code.gitea.io/gitea/services/repository/files" | ||||
| @@ -81,7 +80,7 @@ func TestAPIPullUpdateByRebase(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *issues_model.PullRequest { | ||||
| 	baseRepo, err := repo_service.CreateRepository(db.DefaultContext, actor, actor, repo_module.CreateRepoOptions{ | ||||
| 	baseRepo, err := repo_service.CreateRepository(db.DefaultContext, actor, actor, repo_service.CreateRepoOptions{ | ||||
| 		Name:        "repo-pr-update", | ||||
| 		Description: "repo-tmp-pr-update description", | ||||
| 		AutoInit:    true, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user