mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-11-04 08:21:11 +00:00 
			
		
		
		
	* support changing label colors * support changing issue state * use helpers to keep type conversions DRY * drop the x/exp license because it is no longer used The tests are performed by the gof3 compliance suite
		
			
				
	
	
		
			320 lines
		
	
	
	
		
			9.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			320 lines
		
	
	
	
		
			9.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright Earl Warren <contact@earl-warren.org>
 | 
						|
// Copyright Loïc Dachary <loic@dachary.org>
 | 
						|
// SPDX-License-Identifier: MIT
 | 
						|
 | 
						|
package driver
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"fmt"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"code.gitea.io/gitea/models/db"
 | 
						|
	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/git"
 | 
						|
	"code.gitea.io/gitea/modules/timeutil"
 | 
						|
	issue_service "code.gitea.io/gitea/services/issue"
 | 
						|
 | 
						|
	"code.forgejo.org/f3/gof3/v3/f3"
 | 
						|
	f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
 | 
						|
	"code.forgejo.org/f3/gof3/v3/tree/generic"
 | 
						|
	f3_util "code.forgejo.org/f3/gof3/v3/util"
 | 
						|
)
 | 
						|
 | 
						|
var _ f3_tree.ForgeDriverInterface = &pullRequest{}
 | 
						|
 | 
						|
type pullRequest struct {
 | 
						|
	common
 | 
						|
 | 
						|
	forgejoPullRequest *issues_model.Issue
 | 
						|
	headRepository     *f3.Reference
 | 
						|
	baseRepository     *f3.Reference
 | 
						|
	fetchFunc          f3.PullRequestFetchFunc
 | 
						|
}
 | 
						|
 | 
						|
func (o *pullRequest) SetNative(pullRequest any) {
 | 
						|
	o.forgejoPullRequest = pullRequest.(*issues_model.Issue)
 | 
						|
}
 | 
						|
 | 
						|
func (o *pullRequest) GetNativeID() string {
 | 
						|
	return fmt.Sprintf("%d", o.forgejoPullRequest.Index)
 | 
						|
}
 | 
						|
 | 
						|
func (o *pullRequest) NewFormat() f3.Interface {
 | 
						|
	node := o.GetNode()
 | 
						|
	return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
 | 
						|
}
 | 
						|
 | 
						|
func (o *pullRequest) repositoryToReference(ctx context.Context, repository *repo_model.Repository) *f3.Reference {
 | 
						|
	if repository == nil {
 | 
						|
		panic("unexpected nil repository")
 | 
						|
	}
 | 
						|
	forge := o.getTree().GetRoot().GetChild(generic.NewNodeID(f3_tree.KindForge)).GetDriver().(*forge)
 | 
						|
	owners := forge.getOwnersPath(ctx, fmt.Sprintf("%d", repository.OwnerID))
 | 
						|
	return f3_tree.NewRepositoryReference(owners.String(), repository.OwnerID, repository.ID)
 | 
						|
}
 | 
						|
 | 
						|
func (o *pullRequest) referenceToRepository(reference *f3.Reference) int64 {
 | 
						|
	var project int64
 | 
						|
	if reference.Get() == "../../repository/vcs" {
 | 
						|
		project = f3_tree.GetProjectID(o.GetNode())
 | 
						|
	} else {
 | 
						|
		p := f3_tree.ToPath(generic.PathAbsolute(o.GetNode().GetCurrentPath().String(), reference.Get()))
 | 
						|
		o.Trace("%v %v", o.GetNode().GetCurrentPath().String(), p)
 | 
						|
		_, project = p.OwnerAndProjectID()
 | 
						|
	}
 | 
						|
	return project
 | 
						|
}
 | 
						|
 | 
						|
func (o *pullRequest) ToFormat() f3.Interface {
 | 
						|
	if o.forgejoPullRequest == nil {
 | 
						|
		return o.NewFormat()
 | 
						|
	}
 | 
						|
 | 
						|
	var milestone *f3.Reference
 | 
						|
	if o.forgejoPullRequest.Milestone != nil {
 | 
						|
		milestone = f3_tree.NewIssueMilestoneReference(o.forgejoPullRequest.Milestone.ID)
 | 
						|
	}
 | 
						|
 | 
						|
	var mergedTime *time.Time
 | 
						|
	if o.forgejoPullRequest.PullRequest.HasMerged {
 | 
						|
		mergedTime = o.forgejoPullRequest.PullRequest.MergedUnix.AsTimePtr()
 | 
						|
	}
 | 
						|
 | 
						|
	var closedTime *time.Time
 | 
						|
	if o.forgejoPullRequest.IsClosed {
 | 
						|
		closedTime = o.forgejoPullRequest.ClosedUnix.AsTimePtr()
 | 
						|
	}
 | 
						|
 | 
						|
	makePullRequestBranch := func(repo *repo_model.Repository, branch string) f3.PullRequestBranch {
 | 
						|
		r, err := git.OpenRepository(context.Background(), repo.RepoPath())
 | 
						|
		if err != nil {
 | 
						|
			panic(err)
 | 
						|
		}
 | 
						|
		defer r.Close()
 | 
						|
 | 
						|
		b, err := r.GetBranch(branch)
 | 
						|
		if err != nil {
 | 
						|
			panic(err)
 | 
						|
		}
 | 
						|
 | 
						|
		c, err := b.GetCommit()
 | 
						|
		if err != nil {
 | 
						|
			panic(err)
 | 
						|
		}
 | 
						|
 | 
						|
		return f3.PullRequestBranch{
 | 
						|
			Ref: branch,
 | 
						|
			SHA: c.ID.String(),
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if err := o.forgejoPullRequest.PullRequest.LoadHeadRepo(db.DefaultContext); err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
	head := makePullRequestBranch(o.forgejoPullRequest.PullRequest.HeadRepo, o.forgejoPullRequest.PullRequest.HeadBranch)
 | 
						|
	head.Repository = o.headRepository
 | 
						|
	if err := o.forgejoPullRequest.PullRequest.LoadBaseRepo(db.DefaultContext); err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
	base := makePullRequestBranch(o.forgejoPullRequest.PullRequest.BaseRepo, o.forgejoPullRequest.PullRequest.BaseBranch)
 | 
						|
	base.Repository = o.baseRepository
 | 
						|
 | 
						|
	return &f3.PullRequest{
 | 
						|
		Common:         f3.NewCommon(o.GetNativeID()),
 | 
						|
		PosterID:       f3_tree.NewUserReference(o.forgejoPullRequest.Poster.ID),
 | 
						|
		Title:          o.forgejoPullRequest.Title,
 | 
						|
		Content:        o.forgejoPullRequest.Content,
 | 
						|
		Milestone:      milestone,
 | 
						|
		State:          string(o.forgejoPullRequest.State()),
 | 
						|
		IsLocked:       o.forgejoPullRequest.IsLocked,
 | 
						|
		Created:        o.forgejoPullRequest.CreatedUnix.AsTime(),
 | 
						|
		Updated:        o.forgejoPullRequest.UpdatedUnix.AsTime(),
 | 
						|
		Closed:         closedTime,
 | 
						|
		Merged:         o.forgejoPullRequest.PullRequest.HasMerged,
 | 
						|
		MergedTime:     mergedTime,
 | 
						|
		MergeCommitSHA: o.forgejoPullRequest.PullRequest.MergedCommitID,
 | 
						|
		Head:           head,
 | 
						|
		Base:           base,
 | 
						|
		FetchFunc:      o.fetchFunc,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (o *pullRequest) FromFormat(content f3.Interface) {
 | 
						|
	pullRequest := content.(*f3.PullRequest)
 | 
						|
	var milestone *issues_model.Milestone
 | 
						|
	if pullRequest.Milestone != nil {
 | 
						|
		milestone = &issues_model.Milestone{
 | 
						|
			ID: pullRequest.Milestone.GetIDAsInt(),
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	o.headRepository = pullRequest.Head.Repository
 | 
						|
	o.baseRepository = pullRequest.Base.Repository
 | 
						|
	pr := issues_model.PullRequest{
 | 
						|
		HeadBranch: pullRequest.Head.Ref,
 | 
						|
		HeadRepoID: o.referenceToRepository(o.headRepository),
 | 
						|
		BaseBranch: pullRequest.Base.Ref,
 | 
						|
		BaseRepoID: o.referenceToRepository(o.baseRepository),
 | 
						|
 | 
						|
		MergeBase: pullRequest.Base.SHA,
 | 
						|
		Index:     f3_util.ParseInt(pullRequest.GetID()),
 | 
						|
		HasMerged: pullRequest.Merged,
 | 
						|
	}
 | 
						|
 | 
						|
	o.forgejoPullRequest = &issues_model.Issue{
 | 
						|
		Index:    f3_util.ParseInt(pullRequest.GetID()),
 | 
						|
		PosterID: pullRequest.PosterID.GetIDAsInt(),
 | 
						|
		Poster: &user_model.User{
 | 
						|
			ID: pullRequest.PosterID.GetIDAsInt(),
 | 
						|
		},
 | 
						|
		Title:       pullRequest.Title,
 | 
						|
		Content:     pullRequest.Content,
 | 
						|
		Milestone:   milestone,
 | 
						|
		IsClosed:    pullRequest.State == f3.PullRequestStateClosed,
 | 
						|
		CreatedUnix: timeutil.TimeStamp(pullRequest.Created.Unix()),
 | 
						|
		UpdatedUnix: timeutil.TimeStamp(pullRequest.Updated.Unix()),
 | 
						|
		IsLocked:    pullRequest.IsLocked,
 | 
						|
		PullRequest: &pr,
 | 
						|
		IsPull:      true,
 | 
						|
	}
 | 
						|
 | 
						|
	if pullRequest.Closed != nil {
 | 
						|
		o.forgejoPullRequest.ClosedUnix = timeutil.TimeStamp(pullRequest.Closed.Unix())
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (o *pullRequest) Get(ctx context.Context) bool {
 | 
						|
	node := o.GetNode()
 | 
						|
	o.Trace("%s", node.GetID())
 | 
						|
 | 
						|
	project := f3_tree.GetProjectID(o.GetNode())
 | 
						|
	id := node.GetID().Int64()
 | 
						|
 | 
						|
	issue, err := issues_model.GetIssueByIndex(ctx, project, id)
 | 
						|
	if issues_model.IsErrIssueNotExist(err) {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		panic(fmt.Errorf("issue %v %w", id, err))
 | 
						|
	}
 | 
						|
	if err := issue.LoadAttributes(ctx); err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
	if err := issue.PullRequest.LoadHeadRepo(ctx); err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
	o.headRepository = o.repositoryToReference(ctx, issue.PullRequest.HeadRepo)
 | 
						|
	if err := issue.PullRequest.LoadBaseRepo(ctx); err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
	o.baseRepository = o.repositoryToReference(ctx, issue.PullRequest.BaseRepo)
 | 
						|
 | 
						|
	o.forgejoPullRequest = issue
 | 
						|
	o.Trace("ID = %s", o.forgejoPullRequest.ID)
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
func (o *pullRequest) Patch(ctx context.Context) {
 | 
						|
	node := o.GetNode()
 | 
						|
	project := f3_tree.GetProjectID(o.GetNode())
 | 
						|
	id := node.GetID().Int64()
 | 
						|
	o.Trace("repo_id = %d, index = %d", project, id)
 | 
						|
	if _, err := db.GetEngine(ctx).Where("`repo_id` = ? AND `index` = ?", project, id).Cols("name", "content").Update(o.forgejoPullRequest); err != nil {
 | 
						|
		panic(fmt.Errorf("%v %v", o.forgejoPullRequest, err))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (o *pullRequest) GetPullRequestPushRefs() []string {
 | 
						|
	return []string{
 | 
						|
		fmt.Sprintf("refs/f3/%s/head", o.GetNativeID()),
 | 
						|
		fmt.Sprintf("refs/pull/%s/head", o.GetNativeID()),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (o *pullRequest) GetPullRequestRef() string {
 | 
						|
	return fmt.Sprintf("refs/pull/%s/head", o.GetNativeID())
 | 
						|
}
 | 
						|
 | 
						|
func (o *pullRequest) Put(ctx context.Context) generic.NodeID {
 | 
						|
	node := o.GetNode()
 | 
						|
	o.Trace("%s", node.GetID())
 | 
						|
 | 
						|
	o.forgejoPullRequest.RepoID = f3_tree.GetProjectID(o.GetNode())
 | 
						|
 | 
						|
	ctx, committer, err := db.TxContext(ctx)
 | 
						|
	if err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
	defer committer.Close()
 | 
						|
 | 
						|
	idx, err := db.GetNextResourceIndex(ctx, "issue_index", o.forgejoPullRequest.RepoID)
 | 
						|
	if err != nil {
 | 
						|
		panic(fmt.Errorf("generate issue index failed: %w", err))
 | 
						|
	}
 | 
						|
	o.forgejoPullRequest.Index = idx
 | 
						|
 | 
						|
	sess := db.GetEngine(ctx)
 | 
						|
 | 
						|
	if _, err = sess.NoAutoTime().Insert(o.forgejoPullRequest); err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
 | 
						|
	pr := o.forgejoPullRequest.PullRequest
 | 
						|
	pr.Index = o.forgejoPullRequest.Index
 | 
						|
	pr.IssueID = o.forgejoPullRequest.ID
 | 
						|
	pr.HeadRepoID = o.referenceToRepository(o.headRepository)
 | 
						|
	if pr.HeadRepoID == 0 {
 | 
						|
		panic(fmt.Errorf("HeadRepoID == 0 in %v", pr))
 | 
						|
	}
 | 
						|
	pr.BaseRepoID = o.referenceToRepository(o.baseRepository)
 | 
						|
	if pr.BaseRepoID == 0 {
 | 
						|
		panic(fmt.Errorf("BaseRepoID == 0 in %v", pr))
 | 
						|
	}
 | 
						|
 | 
						|
	if _, err = sess.NoAutoTime().Insert(pr); err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
 | 
						|
	if err = committer.Commit(); err != nil {
 | 
						|
		panic(fmt.Errorf("Commit: %w", err))
 | 
						|
	}
 | 
						|
 | 
						|
	if err := pr.LoadBaseRepo(ctx); err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
	if err := pr.LoadHeadRepo(ctx); err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
 | 
						|
	o.Trace("pullRequest created %d/%d", o.forgejoPullRequest.ID, o.forgejoPullRequest.Index)
 | 
						|
	return generic.NewNodeID(o.forgejoPullRequest.Index)
 | 
						|
}
 | 
						|
 | 
						|
func (o *pullRequest) Delete(ctx context.Context) {
 | 
						|
	node := o.GetNode()
 | 
						|
	o.Trace("%s", node.GetID())
 | 
						|
 | 
						|
	owner := f3_tree.GetOwnerName(o.GetNode())
 | 
						|
	project := f3_tree.GetProjectName(o.GetNode())
 | 
						|
	repoPath := repo_model.RepoPath(owner, project)
 | 
						|
	gitRepo, err := git.OpenRepository(ctx, repoPath)
 | 
						|
	if err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
	defer gitRepo.Close()
 | 
						|
 | 
						|
	doer, err := user_model.GetAdminUser(ctx)
 | 
						|
	if err != nil {
 | 
						|
		panic(fmt.Errorf("GetAdminUser %w", err))
 | 
						|
	}
 | 
						|
 | 
						|
	if err := issue_service.DeleteIssue(ctx, doer, gitRepo, o.forgejoPullRequest); err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func newPullRequest() generic.NodeDriverInterface {
 | 
						|
	return &pullRequest{}
 | 
						|
}
 |