Files
gitea/services/repository/template.go
wxiaoguang 9dc04289aa refactor: make git package handle all git operations (#38543)
before: gitrepo vs git packages
after: git package fully handle all git operations

by the way, use `WithRepo(repo)` instead of `WithDir(repo.Path)` to hide
path details.

benefits:

1. remove all unnecessary wrappers, developers no need to struggle with
"which package should be used"
2. simplify code, RepositoryFacade can (will) be used everywhere, all
"path" details are (will be) hidden
2026-07-20 16:07:38 +00:00

195 lines
5.8 KiB
Go

// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repository
import (
"context"
"fmt"
"strings"
"gitea.dev/models/db"
git_model "gitea.dev/models/git"
issues_model "gitea.dev/models/issues"
repo_model "gitea.dev/models/repo"
user_model "gitea.dev/models/user"
"gitea.dev/modules/git"
"gitea.dev/modules/log"
notify_service "gitea.dev/services/notify"
)
// GenerateIssueLabels generates issue labels from a template repository
func GenerateIssueLabels(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error {
templateLabels, err := issues_model.GetLabelsByRepoID(ctx, templateRepo.ID, "", db.ListOptions{})
if err != nil {
return err
}
// Prevent insert being called with an empty slice which would result in
// err "no element on slice when insert".
if len(templateLabels) == 0 {
return nil
}
newLabels := make([]*issues_model.Label, 0, len(templateLabels))
for _, templateLabel := range templateLabels {
newLabels = append(newLabels, &issues_model.Label{
RepoID: generateRepo.ID,
Name: templateLabel.Name,
Exclusive: templateLabel.Exclusive,
ExclusiveOrder: templateLabel.ExclusiveOrder,
Description: templateLabel.Description,
Color: templateLabel.Color,
})
}
return db.Insert(ctx, newLabels)
}
func GenerateProtectedBranch(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error {
templateBranches, err := git_model.FindRepoProtectedBranchRules(ctx, templateRepo.ID)
if err != nil {
return err
}
// Prevent insert being called with an empty slice which would result in
// err "no element on slice when insert".
if len(templateBranches) == 0 {
return nil
}
newBranches := make([]*git_model.ProtectedBranch, 0, len(templateBranches))
for _, templateBranch := range templateBranches {
templateBranch.ID = 0
templateBranch.RepoID = generateRepo.ID
templateBranch.UpdatedUnix = 0
templateBranch.CreatedUnix = 0
newBranches = append(newBranches, templateBranch)
}
return db.Insert(ctx, newBranches)
}
// GenerateRepository generates a repository from a template
func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templateRepo *repo_model.Repository, opts GenerateRepoOptions) (_ *repo_model.Repository, err error) {
if !doer.CanCreateRepoIn(owner) {
return nil, repo_model.ErrReachLimitOfRepo{
Limit: owner.MaxRepoCreation,
}
}
generateRepo := &repo_model.Repository{
OwnerID: owner.ID,
Owner: owner,
OwnerName: owner.Name,
Name: opts.Name,
LowerName: strings.ToLower(opts.Name),
Description: opts.Description,
DefaultBranch: opts.DefaultBranch,
IsPrivate: opts.Private,
IsEmpty: !opts.GitContent || templateRepo.IsEmpty,
IsFsckEnabled: templateRepo.IsFsckEnabled,
TemplateID: templateRepo.ID,
TrustModel: templateRepo.TrustModel,
ObjectFormatName: templateRepo.ObjectFormatName,
Status: repo_model.RepositoryBeingMigrated,
}
// 1 - Create the repository in the database
if err := db.WithTx(ctx, func(ctx context.Context) error {
return createRepositoryInDB(ctx, doer, owner, generateRepo, false)
}); err != nil {
return nil, err
}
// last - clean up the repository if something goes wrong
defer func() {
if err != nil {
// we can not use `ctx` because it may be canceled or timed out
cleanupRepository(generateRepo)
}
}()
// 2 - check whether the repository with the same storage exists
isExist, err := git.IsRepositoryExist(ctx, generateRepo)
if err != nil {
log.Error("Unable to check if %s exists. Error: %v", generateRepo.FullName(), err)
return nil, err
}
if isExist {
// Don't return directly, we need err in defer to cleanupRepository
err = repo_model.ErrRepoFilesAlreadyExist{
Uname: generateRepo.OwnerName,
Name: generateRepo.Name,
}
return nil, err
}
// 3 -Init git bare new repository.
if err = git.InitRepository(ctx, generateRepo, generateRepo.ObjectFormatName); err != nil {
return nil, fmt.Errorf("git.InitRepository: %w", err)
} else if err = git.CreateDelegateHooks(ctx, generateRepo); err != nil {
return nil, fmt.Errorf("createDelegateHooks: %w", err)
}
// 4 - Update the git repository
if err = updateGitRepoAfterCreate(ctx, generateRepo); err != nil {
return nil, fmt.Errorf("updateGitRepoAfterCreate: %w", err)
}
// 5 - generate the repository contents according to the template
// Git Content
if opts.GitContent && !templateRepo.IsEmpty {
if err = GenerateGitContent(ctx, templateRepo, generateRepo); err != nil {
return nil, err
}
}
// Topics
if opts.Topics {
if err = repo_model.GenerateTopics(ctx, templateRepo, generateRepo); err != nil {
return nil, err
}
}
// Git Hooks
if opts.GitHooks {
if err = GenerateGitHooks(ctx, templateRepo, generateRepo); err != nil {
return nil, err
}
}
// Webhooks
if opts.Webhooks {
if err = GenerateWebhooks(ctx, templateRepo, generateRepo); err != nil {
return nil, err
}
}
// Avatar
if opts.Avatar && len(templateRepo.Avatar) > 0 {
if err = generateAvatar(ctx, templateRepo, generateRepo); err != nil {
return nil, err
}
}
// Issue Labels
if opts.IssueLabels {
if err = GenerateIssueLabels(ctx, templateRepo, generateRepo); err != nil {
return nil, err
}
}
if opts.ProtectedBranch {
if err = GenerateProtectedBranch(ctx, templateRepo, generateRepo); err != nil {
return nil, err
}
}
// 6 - update repository status to be ready
generateRepo.Status = repo_model.RepositoryReady
if err = repo_model.UpdateRepositoryColsWithAutoTime(ctx, generateRepo, "status"); err != nil {
return nil, fmt.Errorf("UpdateRepositoryCols: %w", err)
}
notify_service.CreateRepository(ctx, doer, owner, generateRepo)
return generateRepo, nil
}