mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-04 13:34:43 +01:00 
			
		
		
		
	This PR uniform all temporary directory usage so that it will be easier to manage. Relate to #31792 - [x] Added a new setting to allow users to configure the global temporary directory. - [x] Move all temporary files and directories to be placed under os.Temp()/gitea. - [x] `setting.Repository.Local.LocalCopyPath` now will be `setting.TempPath/local-repo` and the customized path is removed. ```diff -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;[repository.local] -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Path for local repository copy. Defaults to TEMP_PATH + `local-repo`, this is deprecated and cannot be changed -;LOCAL_COPY_PATH = local-repo ``` - [x] `setting.Repository.Upload.TempPath` now will be `settting.TempPath/uploads` and the customized path is removed. ```diff ;[repository.upload] -;; -;; Path for uploads. Defaults to TEMP_PATH + `uploads` -;TEMP_PATH = uploads ``` - [x] `setting.Packages.ChunkedUploadPath` now will be `settting.TempPath/package-upload` and the customized path is removed. ```diff ;[packages] -;; -;; Path for chunked uploads. Defaults it's `package-upload` under `TEMP_PATH` unless it's an absolute path. -;CHUNKED_UPLOAD_PATH = package-upload ``` - [x] `setting.SSH.KeyTestPath` now will be `settting.TempPath/ssh_key_test` and the customized path is removed. ```diff [server] -;; -;; Directory to create temporary files in when testing public keys using ssh-keygen, -;; default is the system temporary directory. -;SSH_KEY_TEST_PATH = ``` TODO: - [ ] setting.PprofDataPath haven't been changed because it may need to be kept until somebody read it but temp path may be clean up any time. --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
		
			
				
	
	
		
			195 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			195 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2019 The Gitea Authors.
 | 
						|
// All rights reserved.
 | 
						|
// SPDX-License-Identifier: MIT
 | 
						|
 | 
						|
package pull
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"fmt"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	git_model "code.gitea.io/gitea/models/git"
 | 
						|
	issues_model "code.gitea.io/gitea/models/issues"
 | 
						|
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						|
	"code.gitea.io/gitea/modules/git"
 | 
						|
	"code.gitea.io/gitea/modules/gitrepo"
 | 
						|
	"code.gitea.io/gitea/modules/log"
 | 
						|
	repo_module "code.gitea.io/gitea/modules/repository"
 | 
						|
)
 | 
						|
 | 
						|
// Temporary repos created here use standard branch names to help simplify
 | 
						|
// merging code
 | 
						|
const (
 | 
						|
	baseBranch     = "base"     // equivalent to pr.BaseBranch
 | 
						|
	trackingBranch = "tracking" // equivalent to pr.HeadBranch
 | 
						|
	stagingBranch  = "staging"  // this is used for a working branch
 | 
						|
)
 | 
						|
 | 
						|
type prContext struct {
 | 
						|
	context.Context
 | 
						|
	tmpBasePath string
 | 
						|
	pr          *issues_model.PullRequest
 | 
						|
	outbuf      *strings.Builder // we keep these around to help reduce needless buffer recreation,
 | 
						|
	errbuf      *strings.Builder // any use should be preceded by a Reset and preferably after use
 | 
						|
}
 | 
						|
 | 
						|
func (ctx *prContext) RunOpts() *git.RunOpts {
 | 
						|
	ctx.outbuf.Reset()
 | 
						|
	ctx.errbuf.Reset()
 | 
						|
	return &git.RunOpts{
 | 
						|
		Dir:    ctx.tmpBasePath,
 | 
						|
		Stdout: ctx.outbuf,
 | 
						|
		Stderr: ctx.errbuf,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// createTemporaryRepoForPR creates a temporary repo with "base" for pr.BaseBranch and "tracking" for  pr.HeadBranch
 | 
						|
// it also create a second base branch called "original_base"
 | 
						|
func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest) (prCtx *prContext, cancel context.CancelFunc, err error) {
 | 
						|
	if err := pr.LoadHeadRepo(ctx); err != nil {
 | 
						|
		log.Error("%-v LoadHeadRepo: %v", pr, err)
 | 
						|
		return nil, nil, fmt.Errorf("%v LoadHeadRepo: %w", pr, err)
 | 
						|
	} else if pr.HeadRepo == nil {
 | 
						|
		log.Error("%-v HeadRepo %d does not exist", pr, pr.HeadRepoID)
 | 
						|
		return nil, nil, &repo_model.ErrRepoNotExist{
 | 
						|
			ID: pr.HeadRepoID,
 | 
						|
		}
 | 
						|
	} else if err := pr.LoadBaseRepo(ctx); err != nil {
 | 
						|
		log.Error("%-v LoadBaseRepo: %v", pr, err)
 | 
						|
		return nil, nil, fmt.Errorf("%v LoadBaseRepo: %w", pr, err)
 | 
						|
	} else if pr.BaseRepo == nil {
 | 
						|
		log.Error("%-v BaseRepo %d does not exist", pr, pr.BaseRepoID)
 | 
						|
		return nil, nil, &repo_model.ErrRepoNotExist{
 | 
						|
			ID: pr.BaseRepoID,
 | 
						|
		}
 | 
						|
	} else if err := pr.HeadRepo.LoadOwner(ctx); err != nil {
 | 
						|
		log.Error("%-v HeadRepo.LoadOwner: %v", pr, err)
 | 
						|
		return nil, nil, fmt.Errorf("%v HeadRepo.LoadOwner: %w", pr, err)
 | 
						|
	} else if err := pr.BaseRepo.LoadOwner(ctx); err != nil {
 | 
						|
		log.Error("%-v BaseRepo.LoadOwner: %v", pr, err)
 | 
						|
		return nil, nil, fmt.Errorf("%v BaseRepo.LoadOwner: %w", pr, err)
 | 
						|
	}
 | 
						|
 | 
						|
	// Clone base repo.
 | 
						|
	tmpBasePath, cleanup, err := repo_module.CreateTemporaryPath("pull")
 | 
						|
	if err != nil {
 | 
						|
		log.Error("CreateTemporaryPath[%-v]: %v", pr, err)
 | 
						|
		return nil, nil, err
 | 
						|
	}
 | 
						|
	cancel = cleanup
 | 
						|
 | 
						|
	prCtx = &prContext{
 | 
						|
		Context:     ctx,
 | 
						|
		tmpBasePath: tmpBasePath,
 | 
						|
		pr:          pr,
 | 
						|
		outbuf:      &strings.Builder{},
 | 
						|
		errbuf:      &strings.Builder{},
 | 
						|
	}
 | 
						|
 | 
						|
	baseRepoPath := pr.BaseRepo.RepoPath()
 | 
						|
	headRepoPath := pr.HeadRepo.RepoPath()
 | 
						|
 | 
						|
	if err := git.InitRepository(ctx, tmpBasePath, false, pr.BaseRepo.ObjectFormatName); err != nil {
 | 
						|
		log.Error("Unable to init tmpBasePath for %-v: %v", pr, err)
 | 
						|
		cancel()
 | 
						|
		return nil, nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	remoteRepoName := "head_repo"
 | 
						|
	baseBranch := "base"
 | 
						|
 | 
						|
	fetchArgs := git.TrustedCmdArgs{"--no-tags"}
 | 
						|
	if git.DefaultFeatures().CheckVersionAtLeast("2.25.0") {
 | 
						|
		// Writing the commit graph can be slow and is not needed here
 | 
						|
		fetchArgs = append(fetchArgs, "--no-write-commit-graph")
 | 
						|
	}
 | 
						|
 | 
						|
	// addCacheRepo adds git alternatives for the cacheRepoPath in the repoPath
 | 
						|
	addCacheRepo := func(repoPath, cacheRepoPath string) error {
 | 
						|
		p := filepath.Join(repoPath, ".git", "objects", "info", "alternates")
 | 
						|
		f, err := os.OpenFile(p, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o600)
 | 
						|
		if err != nil {
 | 
						|
			log.Error("Could not create .git/objects/info/alternates file in %s: %v", repoPath, err)
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		defer f.Close()
 | 
						|
		data := filepath.Join(cacheRepoPath, "objects")
 | 
						|
		if _, err := fmt.Fprintln(f, data); err != nil {
 | 
						|
			log.Error("Could not write to .git/objects/info/alternates file in %s: %v", repoPath, err)
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// Add head repo remote.
 | 
						|
	if err := addCacheRepo(tmpBasePath, baseRepoPath); err != nil {
 | 
						|
		log.Error("%-v Unable to add base repository to temporary repo [%s -> %s]: %v", pr, pr.BaseRepo.FullName(), tmpBasePath, err)
 | 
						|
		cancel()
 | 
						|
		return nil, nil, fmt.Errorf("Unable to add base repository to temporary repo [%s -> tmpBasePath]: %w", pr.BaseRepo.FullName(), err)
 | 
						|
	}
 | 
						|
 | 
						|
	if err := git.NewCommand("remote", "add", "-t").AddDynamicArguments(pr.BaseBranch).AddArguments("-m").AddDynamicArguments(pr.BaseBranch).AddDynamicArguments("origin", baseRepoPath).
 | 
						|
		Run(ctx, prCtx.RunOpts()); err != nil {
 | 
						|
		log.Error("%-v Unable to add base repository as origin [%s -> %s]: %v\n%s\n%s", pr, pr.BaseRepo.FullName(), tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
 | 
						|
		cancel()
 | 
						|
		return nil, nil, fmt.Errorf("Unable to add base repository as origin [%s -> tmpBasePath]: %w\n%s\n%s", pr.BaseRepo.FullName(), err, prCtx.outbuf.String(), prCtx.errbuf.String())
 | 
						|
	}
 | 
						|
 | 
						|
	if err := git.NewCommand("fetch", "origin").AddArguments(fetchArgs...).AddDashesAndList(pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch).
 | 
						|
		Run(ctx, prCtx.RunOpts()); err != nil {
 | 
						|
		log.Error("%-v Unable to fetch origin base branch [%s:%s -> base, original_base in %s]: %v:\n%s\n%s", pr, pr.BaseRepo.FullName(), pr.BaseBranch, tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
 | 
						|
		cancel()
 | 
						|
		return nil, nil, fmt.Errorf("Unable to fetch origin base branch [%s:%s -> base, original_base in tmpBasePath]: %w\n%s\n%s", pr.BaseRepo.FullName(), pr.BaseBranch, err, prCtx.outbuf.String(), prCtx.errbuf.String())
 | 
						|
	}
 | 
						|
 | 
						|
	if err := git.NewCommand("symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseBranch).
 | 
						|
		Run(ctx, prCtx.RunOpts()); err != nil {
 | 
						|
		log.Error("%-v Unable to set HEAD as base branch in [%s]: %v\n%s\n%s", pr, tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
 | 
						|
		cancel()
 | 
						|
		return nil, nil, fmt.Errorf("Unable to set HEAD as base branch in tmpBasePath: %w\n%s\n%s", err, prCtx.outbuf.String(), prCtx.errbuf.String())
 | 
						|
	}
 | 
						|
 | 
						|
	if err := addCacheRepo(tmpBasePath, headRepoPath); err != nil {
 | 
						|
		log.Error("%-v Unable to add head repository to temporary repo [%s -> %s]: %v", pr, pr.HeadRepo.FullName(), tmpBasePath, err)
 | 
						|
		cancel()
 | 
						|
		return nil, nil, fmt.Errorf("Unable to add head base repository to temporary repo [%s -> tmpBasePath]: %w", pr.HeadRepo.FullName(), err)
 | 
						|
	}
 | 
						|
 | 
						|
	if err := git.NewCommand("remote", "add").AddDynamicArguments(remoteRepoName, headRepoPath).
 | 
						|
		Run(ctx, prCtx.RunOpts()); err != nil {
 | 
						|
		log.Error("%-v Unable to add head repository as head_repo [%s -> %s]: %v\n%s\n%s", pr, pr.HeadRepo.FullName(), tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
 | 
						|
		cancel()
 | 
						|
		return nil, nil, fmt.Errorf("Unable to add head repository as head_repo [%s -> tmpBasePath]: %w\n%s\n%s", pr.HeadRepo.FullName(), err, prCtx.outbuf.String(), prCtx.errbuf.String())
 | 
						|
	}
 | 
						|
 | 
						|
	trackingBranch := "tracking"
 | 
						|
	objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName)
 | 
						|
	// Fetch head branch
 | 
						|
	var headBranch string
 | 
						|
	if pr.Flow == issues_model.PullRequestFlowGithub {
 | 
						|
		headBranch = git.BranchPrefix + pr.HeadBranch
 | 
						|
	} else if len(pr.HeadCommitID) == objectFormat.FullLength() { // for not created pull request
 | 
						|
		headBranch = pr.HeadCommitID
 | 
						|
	} else {
 | 
						|
		headBranch = pr.GetGitRefName()
 | 
						|
	}
 | 
						|
	if err := git.NewCommand("fetch").AddArguments(fetchArgs...).AddDynamicArguments(remoteRepoName, headBranch+":"+trackingBranch).
 | 
						|
		Run(ctx, prCtx.RunOpts()); err != nil {
 | 
						|
		cancel()
 | 
						|
		if !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) {
 | 
						|
			return nil, nil, git_model.ErrBranchNotExist{
 | 
						|
				BranchName: pr.HeadBranch,
 | 
						|
			}
 | 
						|
		}
 | 
						|
		log.Error("%-v Unable to fetch head_repo head branch [%s:%s -> tracking in %s]: %v:\n%s\n%s", pr, pr.HeadRepo.FullName(), pr.HeadBranch, tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
 | 
						|
		return nil, nil, fmt.Errorf("Unable to fetch head_repo head branch [%s:%s -> tracking in tmpBasePath]: %w\n%s\n%s", pr.HeadRepo.FullName(), headBranch, err, prCtx.outbuf.String(), prCtx.errbuf.String())
 | 
						|
	}
 | 
						|
	prCtx.outbuf.Reset()
 | 
						|
	prCtx.errbuf.Reset()
 | 
						|
 | 
						|
	return prCtx, cancel, nil
 | 
						|
}
 |