2023-09-06 20:08:51 +08:00
|
|
|
// 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/db"
|
2024-11-06 19:28:11 -08:00
|
|
|
"code.gitea.io/gitea/models/organization"
|
|
|
|
|
"code.gitea.io/gitea/models/perm"
|
|
|
|
|
access_model "code.gitea.io/gitea/models/perm/access"
|
2023-09-06 20:08:51 +08:00
|
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
2025-04-07 22:12:54 -07:00
|
|
|
system_model "code.gitea.io/gitea/models/system"
|
2024-11-06 19:28:11 -08:00
|
|
|
"code.gitea.io/gitea/models/unit"
|
2023-09-06 20:08:51 +08:00
|
|
|
user_model "code.gitea.io/gitea/models/user"
|
2024-11-06 19:28:11 -08:00
|
|
|
"code.gitea.io/gitea/models/webhook"
|
2023-09-06 20:08:51 +08:00
|
|
|
"code.gitea.io/gitea/modules/git"
|
Simplify how git repositories are opened (#28937)
## Purpose
This is a refactor toward building an abstraction over managing git
repositories.
Afterwards, it does not matter anymore if they are stored on the local
disk or somewhere remote.
## What this PR changes
We used `git.OpenRepository` everywhere previously.
Now, we should split them into two distinct functions:
Firstly, there are temporary repositories which do not change:
```go
git.OpenRepository(ctx, diskPath)
```
Gitea managed repositories having a record in the database in the
`repository` table are moved into the new package `gitrepo`:
```go
gitrepo.OpenRepository(ctx, repo_model.Repo)
```
Why is `repo_model.Repository` the second parameter instead of file
path?
Because then we can easily adapt our repository storage strategy.
The repositories can be stored locally, however, they could just as well
be stored on a remote server.
## Further changes in other PRs
- A Git Command wrapper on package `gitrepo` could be created. i.e.
`NewCommand(ctx, repo_model.Repository, commands...)`. `git.RunOpts{Dir:
repo.RepoPath()}`, the directory should be empty before invoking this
method and it can be filled in the function only. #28940
- Remove the `RepoPath()`/`WikiPath()` functions to reduce the
possibility of mistakes.
---------
Co-authored-by: delvh <dev.lh@web.de>
2024-01-28 04:09:51 +08:00
|
|
|
"code.gitea.io/gitea/modules/gitrepo"
|
2023-09-06 20:08:51 +08:00
|
|
|
"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"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// CreateRepoOptions contains the create repository options
|
|
|
|
|
type CreateRepoOptions struct {
|
2023-12-17 19:56:08 +08:00
|
|
|
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
|
|
|
|
|
ObjectFormatName string
|
2023-09-06 20:08:51 +08:00
|
|
|
}
|
|
|
|
|
|
2025-03-13 20:00:56 -07:00
|
|
|
func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir string, opts CreateRepoOptions) error {
|
2023-09-06 20:08:51 +08:00
|
|
|
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.
|
2025-03-13 20:00:56 -07:00
|
|
|
if stdout, _, err := git.NewCommand("clone").AddDynamicArguments(repo.RepoPath(), tmpDir).
|
2025-03-04 20:56:11 +01:00
|
|
|
RunStdString(ctx, &git.RunOpts{Dir: "", Env: env}); err != nil {
|
2023-09-06 20:08:51 +08:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-07 13:17:44 +08:00
|
|
|
cloneLink := repo.CloneLink(ctx, nil /* no doer so do not generate user-related SSH link */)
|
2023-09-06 20:08:51 +08:00
|
|
|
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.
|
2025-03-13 20:00:56 -07:00
|
|
|
func initRepository(ctx context.Context, u *user_model.User, repo *repo_model.Repository, opts CreateRepoOptions) (err error) {
|
2025-04-07 22:12:54 -07:00
|
|
|
// Init git bare new repository.
|
|
|
|
|
if err = git.InitRepository(ctx, repo.RepoPath(), true, repo.ObjectFormatName); err != nil {
|
|
|
|
|
return fmt.Errorf("git.InitRepository: %w", err)
|
|
|
|
|
} else if err = gitrepo.CreateDelegateHooks(ctx, repo); err != nil {
|
|
|
|
|
return fmt.Errorf("createDelegateHooks: %w", err)
|
2023-09-06 20:08:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize repository according to user's choice.
|
|
|
|
|
if opts.AutoInit {
|
2025-04-08 09:15:28 -07:00
|
|
|
tmpDir, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("repos-" + repo.Name)
|
2023-09-06 20:08:51 +08:00
|
|
|
if err != nil {
|
2025-04-08 09:15:28 -07:00
|
|
|
return fmt.Errorf("failed to create temp dir for repository %s: %w", repo.FullName(), err)
|
2023-09-06 20:08:51 +08:00
|
|
|
}
|
2025-04-08 09:15:28 -07:00
|
|
|
defer cleanup()
|
2023-09-06 20:08:51 +08:00
|
|
|
|
2025-03-13 20:00:56 -07:00
|
|
|
if err = prepareRepoCommit(ctx, repo, tmpDir, opts); err != nil {
|
2023-09-06 20:08:51 +08:00
|
|
|
return fmt.Errorf("prepareRepoCommit: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Apply changes and commit.
|
2024-02-28 21:40:36 +08:00
|
|
|
if err = initRepoCommit(ctx, tmpDir, repo, u, opts.DefaultBranch); err != nil {
|
2023-09-06 20:08:51 +08:00
|
|
|
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
|
2024-03-06 14:26:32 +08:00
|
|
|
repo.DefaultWikiBranch = setting.Repository.DefaultBranch
|
2023-09-06 20:08:51 +08:00
|
|
|
|
|
|
|
|
if len(opts.DefaultBranch) > 0 {
|
|
|
|
|
repo.DefaultBranch = opts.DefaultBranch
|
2024-03-08 15:30:10 +08:00
|
|
|
if err = gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil {
|
2023-09-06 20:08:51 +08:00
|
|
|
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.
|
2025-04-08 06:45:31 +00:00
|
|
|
func CreateRepositoryDirectly(ctx context.Context, doer, owner *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) {
|
|
|
|
|
if !doer.CanCreateRepoIn(owner) {
|
2023-09-06 20:08:51 +08:00
|
|
|
return nil, repo_model.ErrReachLimitOfRepo{
|
2025-04-08 06:45:31 +00:00
|
|
|
Limit: owner.MaxRepoCreation,
|
2023-09-06 20:08:51 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-19 16:05:02 +00:00
|
|
|
if opts.ObjectFormatName == "" {
|
|
|
|
|
opts.ObjectFormatName = git.Sha1ObjectFormat.Name()
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-06 20:08:51 +08:00
|
|
|
repo := &repo_model.Repository{
|
2025-04-08 06:45:31 +00:00
|
|
|
OwnerID: owner.ID,
|
|
|
|
|
Owner: owner,
|
|
|
|
|
OwnerName: owner.Name,
|
2023-09-06 20:08:51 +08:00
|
|
|
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,
|
2024-03-06 14:26:32 +08:00
|
|
|
DefaultWikiBranch: setting.Repository.DefaultBranch,
|
2023-12-17 19:56:08 +08:00
|
|
|
ObjectFormatName: opts.ObjectFormatName,
|
2023-09-06 20:08:51 +08:00
|
|
|
}
|
|
|
|
|
|
2025-04-07 22:12:54 -07:00
|
|
|
needsUpdateStatus := opts.Status != repo_model.RepositoryReady
|
2023-09-06 20:08:51 +08:00
|
|
|
|
2025-04-07 22:12:54 -07:00
|
|
|
// 1 - create the repository database operations first
|
|
|
|
|
err := db.WithTx(ctx, func(ctx context.Context) error {
|
2025-04-08 06:45:31 +00:00
|
|
|
return createRepositoryInDB(ctx, doer, owner, repo, false)
|
2025-04-07 22:12:54 -07:00
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2023-09-06 20:08:51 +08:00
|
|
|
|
2025-04-07 22:12:54 -07:00
|
|
|
// last - clean up if something goes wrong
|
|
|
|
|
// WARNING: Don't override all later err with local variables
|
|
|
|
|
defer func() {
|
2023-09-06 20:08:51 +08:00
|
|
|
if err != nil {
|
2025-04-07 22:12:54 -07:00
|
|
|
// we can not use the ctx because it maybe canceled or timeout
|
|
|
|
|
cleanupRepository(doer, repo.ID)
|
2023-09-06 20:08:51 +08:00
|
|
|
}
|
2025-04-07 22:12:54 -07:00
|
|
|
}()
|
2023-09-06 20:08:51 +08:00
|
|
|
|
2025-04-07 22:12:54 -07:00
|
|
|
// No need for init mirror.
|
|
|
|
|
if opts.IsMirror {
|
|
|
|
|
return repo, nil
|
|
|
|
|
}
|
2023-09-06 20:08:51 +08:00
|
|
|
|
2025-04-07 22:12:54 -07:00
|
|
|
// 2 - check whether the repository with the same storage exists
|
|
|
|
|
var isExist bool
|
|
|
|
|
isExist, err = gitrepo.IsRepositoryExist(ctx, repo)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error("Unable to check if %s exists. Error: %v", repo.FullName(), err)
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if isExist {
|
|
|
|
|
log.Error("Files already exist in %s and we are not going to adopt or delete.", repo.FullName())
|
|
|
|
|
// Don't return directly, we need err in defer to cleanupRepository
|
|
|
|
|
err = repo_model.ErrRepoFilesAlreadyExist{
|
|
|
|
|
Uname: repo.OwnerName,
|
|
|
|
|
Name: repo.Name,
|
2023-09-06 20:08:51 +08:00
|
|
|
}
|
2025-04-07 22:12:54 -07:00
|
|
|
return nil, err
|
|
|
|
|
}
|
2023-09-06 20:08:51 +08:00
|
|
|
|
2025-04-07 22:12:54 -07:00
|
|
|
// 3 - init git repository in storage
|
|
|
|
|
if err = initRepository(ctx, doer, repo, opts); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("initRepository: %w", err)
|
|
|
|
|
}
|
2023-09-06 20:08:51 +08:00
|
|
|
|
2025-04-07 22:12:54 -07:00
|
|
|
// 4 - Initialize Issue Labels if selected
|
|
|
|
|
if len(opts.IssueLabels) > 0 {
|
|
|
|
|
if err = repo_module.InitializeLabels(ctx, repo.ID, opts.IssueLabels, false); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("InitializeLabels: %w", err)
|
2023-09-06 20:08:51 +08:00
|
|
|
}
|
2025-04-07 22:12:54 -07:00
|
|
|
}
|
2024-10-02 04:25:08 +09:00
|
|
|
|
2025-04-07 22:12:54 -07:00
|
|
|
// 5 - Update the git repository
|
|
|
|
|
if err = updateGitRepoAfterCreate(ctx, repo); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("updateGitRepoAfterCreate: %w", err)
|
|
|
|
|
}
|
2024-10-02 04:25:08 +09:00
|
|
|
|
2025-04-07 22:12:54 -07:00
|
|
|
// 6 - update licenses
|
|
|
|
|
var licenses []string
|
|
|
|
|
if len(opts.License) > 0 {
|
|
|
|
|
licenses = append(licenses, opts.License)
|
|
|
|
|
|
|
|
|
|
var stdout string
|
|
|
|
|
stdout, _, err = git.NewCommand("rev-parse", "HEAD").RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error("CreateRepository(git rev-parse HEAD) in %v: Stdout: %s\nError: %v", repo, stdout, err)
|
|
|
|
|
return nil, fmt.Errorf("CreateRepository(git rev-parse HEAD): %w", err)
|
2024-10-02 04:25:08 +09:00
|
|
|
}
|
2025-04-07 22:12:54 -07:00
|
|
|
if err = repo_model.UpdateRepoLicenses(ctx, repo, stdout, licenses); err != nil {
|
|
|
|
|
return nil, err
|
2023-09-06 20:08:51 +08:00
|
|
|
}
|
2025-04-07 22:12:54 -07:00
|
|
|
}
|
2023-09-06 20:08:51 +08:00
|
|
|
|
2025-04-07 22:12:54 -07:00
|
|
|
// 7 - update repository status to be ready
|
|
|
|
|
if needsUpdateStatus {
|
|
|
|
|
repo.Status = repo_model.RepositoryReady
|
|
|
|
|
if err = repo_model.UpdateRepositoryCols(ctx, repo, "status"); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("UpdateRepositoryCols: %w", err)
|
|
|
|
|
}
|
2023-09-06 20:08:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return repo, nil
|
|
|
|
|
}
|
2024-11-06 19:28:11 -08:00
|
|
|
|
2025-04-07 22:12:54 -07:00
|
|
|
// createRepositoryInDB creates a repository for the user/organization.
|
|
|
|
|
func createRepositoryInDB(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, isFork bool) (err error) {
|
2024-11-06 19:28:11 -08:00
|
|
|
if err = repo_model.IsUsableRepoName(repo.Name); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
has, err := repo_model.IsRepositoryModelExist(ctx, u, repo.Name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("IsRepositoryExist: %w", err)
|
|
|
|
|
} else if has {
|
|
|
|
|
return repo_model.ErrRepoAlreadyExist{
|
|
|
|
|
Uname: u.Name,
|
|
|
|
|
Name: repo.Name,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err = db.Insert(ctx, repo); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err = repo_model.DeleteRedirect(ctx, u.ID, repo.Name); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// insert units for repo
|
|
|
|
|
defaultUnits := unit.DefaultRepoUnits
|
2024-11-11 08:11:00 +08:00
|
|
|
switch {
|
|
|
|
|
case isFork:
|
2024-11-06 19:28:11 -08:00
|
|
|
defaultUnits = unit.DefaultForkRepoUnits
|
2024-11-11 08:11:00 +08:00
|
|
|
case repo.IsMirror:
|
|
|
|
|
defaultUnits = unit.DefaultMirrorRepoUnits
|
|
|
|
|
case repo.IsTemplate:
|
|
|
|
|
defaultUnits = unit.DefaultTemplateRepoUnits
|
2024-11-06 19:28:11 -08:00
|
|
|
}
|
|
|
|
|
units := make([]repo_model.RepoUnit, 0, len(defaultUnits))
|
|
|
|
|
for _, tp := range defaultUnits {
|
2025-03-29 22:32:28 +01:00
|
|
|
switch tp {
|
|
|
|
|
case unit.TypeIssues:
|
2024-11-06 19:28:11 -08:00
|
|
|
units = append(units, repo_model.RepoUnit{
|
|
|
|
|
RepoID: repo.ID,
|
|
|
|
|
Type: tp,
|
|
|
|
|
Config: &repo_model.IssuesConfig{
|
|
|
|
|
EnableTimetracker: setting.Service.DefaultEnableTimetracking,
|
|
|
|
|
AllowOnlyContributorsToTrackTime: setting.Service.DefaultAllowOnlyContributorsToTrackTime,
|
|
|
|
|
EnableDependencies: setting.Service.DefaultEnableDependencies,
|
|
|
|
|
},
|
|
|
|
|
})
|
2025-03-29 22:32:28 +01:00
|
|
|
case unit.TypePullRequests:
|
2024-11-06 19:28:11 -08:00
|
|
|
units = append(units, repo_model.RepoUnit{
|
|
|
|
|
RepoID: repo.ID,
|
|
|
|
|
Type: tp,
|
|
|
|
|
Config: &repo_model.PullRequestsConfig{
|
|
|
|
|
AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, AllowFastForwardOnly: true,
|
|
|
|
|
DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle),
|
|
|
|
|
AllowRebaseUpdate: true,
|
|
|
|
|
},
|
|
|
|
|
})
|
2025-03-29 22:32:28 +01:00
|
|
|
case unit.TypeProjects:
|
2024-11-06 19:28:11 -08:00
|
|
|
units = append(units, repo_model.RepoUnit{
|
|
|
|
|
RepoID: repo.ID,
|
|
|
|
|
Type: tp,
|
|
|
|
|
Config: &repo_model.ProjectsConfig{ProjectsMode: repo_model.ProjectsModeAll},
|
|
|
|
|
})
|
2025-03-29 22:32:28 +01:00
|
|
|
default:
|
2024-11-06 19:28:11 -08:00
|
|
|
units = append(units, repo_model.RepoUnit{
|
|
|
|
|
RepoID: repo.ID,
|
|
|
|
|
Type: tp,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err = db.Insert(ctx, units); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remember visibility preference.
|
|
|
|
|
u.LastRepoVisibility = repo.IsPrivate
|
|
|
|
|
if err = user_model.UpdateUserCols(ctx, u, "last_repo_visibility"); err != nil {
|
|
|
|
|
return fmt.Errorf("UpdateUserCols: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err = user_model.IncrUserRepoNum(ctx, u.ID); err != nil {
|
|
|
|
|
return fmt.Errorf("IncrUserRepoNum: %w", err)
|
|
|
|
|
}
|
|
|
|
|
u.NumRepos++
|
|
|
|
|
|
|
|
|
|
// Give access to all members in teams with access to all repositories.
|
|
|
|
|
if u.IsOrganization() {
|
|
|
|
|
teams, err := organization.FindOrgTeams(ctx, u.ID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("FindOrgTeams: %w", err)
|
|
|
|
|
}
|
|
|
|
|
for _, t := range teams {
|
|
|
|
|
if t.IncludesAllRepositories {
|
2024-11-27 13:12:26 -08:00
|
|
|
if err := addRepositoryToTeam(ctx, t, repo); err != nil {
|
2024-11-06 19:28:11 -08:00
|
|
|
return fmt.Errorf("AddRepository: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if isAdmin, err := access_model.IsUserRepoAdmin(ctx, repo, doer); err != nil {
|
|
|
|
|
return fmt.Errorf("IsUserRepoAdmin: %w", err)
|
|
|
|
|
} else if !isAdmin {
|
|
|
|
|
// Make creator repo admin if it wasn't assigned automatically
|
|
|
|
|
if err = AddOrUpdateCollaborator(ctx, repo, doer, perm.AccessModeAdmin); err != nil {
|
|
|
|
|
return fmt.Errorf("AddCollaborator: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
|
|
|
|
|
// Organization automatically called this in AddRepository method.
|
|
|
|
|
return fmt.Errorf("RecalculateAccesses: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if setting.Service.AutoWatchNewRepos {
|
|
|
|
|
if err = repo_model.WatchRepo(ctx, doer, repo, true); err != nil {
|
|
|
|
|
return fmt.Errorf("WatchRepo: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err = webhook.CopyDefaultWebhooksToRepo(ctx, repo.ID); err != nil {
|
|
|
|
|
return fmt.Errorf("CopyDefaultWebhooksToRepo: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2025-04-07 22:12:54 -07:00
|
|
|
|
|
|
|
|
func cleanupRepository(doer *user_model.User, repoID int64) {
|
|
|
|
|
if errDelete := DeleteRepositoryDirectly(db.DefaultContext, doer, repoID); errDelete != nil {
|
|
|
|
|
log.Error("cleanupRepository failed: %v", errDelete)
|
|
|
|
|
// add system notice
|
|
|
|
|
if err := system_model.CreateRepositoryNotice("DeleteRepositoryDirectly failed when cleanup repository: %v", errDelete); err != nil {
|
|
|
|
|
log.Error("CreateRepositoryNotice: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func updateGitRepoAfterCreate(ctx context.Context, repo *repo_model.Repository) error {
|
2025-04-12 11:22:18 -07:00
|
|
|
if err := checkDaemonExportOK(ctx, repo); err != nil {
|
2025-04-07 22:12:54 -07:00
|
|
|
return fmt.Errorf("checkDaemonExportOK: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if stdout, _, err := git.NewCommand("update-server-info").
|
|
|
|
|
RunStdString(ctx, &git.RunOpts{Dir: repo.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): %w", err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|