mirror of
https://github.com/go-gitea/gitea.git
synced 2026-04-04 23:10:38 +02:00
When creating a new repository and copying issue labels from a template, the explicit sort order of exclusive labels was previously being lost (resetting to 0). This fix ensures that the original sort order for exclusive labels (e.g., 1, 2) is properly copied and retained in the newly created repository. Fixes #36463 --------- Signed-off-by: Paulo Chen <paulochen@tecnico.ulisboa.pt> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
195 lines
5.9 KiB
Go
195 lines
5.9 KiB
Go
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package repository
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
git_model "code.gitea.io/gitea/models/git"
|
|
issues_model "code.gitea.io/gitea/models/issues"
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
"code.gitea.io/gitea/modules/gitrepo"
|
|
"code.gitea.io/gitea/modules/log"
|
|
notify_service "code.gitea.io/gitea/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 := gitrepo.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 = gitrepo.InitRepository(ctx, generateRepo, generateRepo.ObjectFormatName); err != nil {
|
|
return nil, fmt.Errorf("git.InitRepository: %w", err)
|
|
} else if err = gitrepo.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
|
|
}
|