322 lines · 9.3 KB · Go
RawDownload
// Copyright Earl Warren <contact@earl-warren.org>// Copyright Loïc Dachary <loic@dachary.org>// SPDX-License-Identifier: MIT package driver import (	"context"	"fmt"	"time" 	"forgejo.org/models/db"	issues_model "forgejo.org/models/issues"	repo_model "forgejo.org/models/repo"	user_model "forgejo.org/models/user"	"forgejo.org/modules/git"	"forgejo.org/modules/timeutil"	issue_service "forgejo.org/services/issue" 	"code.forgejo.org/f3/gof3/v3/f3"	f3_id "code.forgejo.org/f3/gof3/v3/id"	f3_path "code.forgejo.org/f3/gof3/v3/path"	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(f3_id.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(f3_path.PathAbsolute(generic.NewElementNode, 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) f3_id.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 f3_id.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{}}