880 lines
24 KiB
Go
880 lines
24 KiB
Go
package common
|
|
|
|
/*
|
|
* This file is part of Autogits.
|
|
*
|
|
* Copyright © 2024 SUSE LLC
|
|
*
|
|
* Autogits is free software: you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License as published by the Free Software
|
|
* Foundation, either version 2 of the License, or (at your option) any later
|
|
* version.
|
|
*
|
|
* Autogits is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* Foobar. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/url"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"slices"
|
|
"time"
|
|
|
|
transport "github.com/go-openapi/runtime/client"
|
|
"github.com/go-openapi/strfmt"
|
|
apiclient "src.opensuse.org/autogits/common/gitea-generated/client"
|
|
"src.opensuse.org/autogits/common/gitea-generated/client/issue"
|
|
"src.opensuse.org/autogits/common/gitea-generated/client/notification"
|
|
"src.opensuse.org/autogits/common/gitea-generated/client/organization"
|
|
"src.opensuse.org/autogits/common/gitea-generated/client/repository"
|
|
"src.opensuse.org/autogits/common/gitea-generated/client/user"
|
|
"src.opensuse.org/autogits/common/gitea-generated/models"
|
|
)
|
|
|
|
//go:generate mockgen -source=gitea_utils.go -destination=mock/gitea_utils.go -typed
|
|
|
|
// maintainer list file in ProjectGit
|
|
const (
|
|
MaintainershipFile = "_maintainership.json"
|
|
MaintainershipDir = "maintainership"
|
|
)
|
|
|
|
const (
|
|
// from Gitea
|
|
// ReviewStateApproved pr is approved
|
|
ReviewStateApproved models.ReviewStateType = "APPROVED"
|
|
// ReviewStatePending pr state is pending
|
|
ReviewStatePending models.ReviewStateType = "PENDING"
|
|
// ReviewStateComment is a comment review
|
|
ReviewStateComment models.ReviewStateType = "COMMENT"
|
|
// ReviewStateRequestChanges changes for pr are requested
|
|
ReviewStateRequestChanges models.ReviewStateType = "REQUEST_CHANGES"
|
|
// ReviewStateRequestReview review is requested from user
|
|
ReviewStateRequestReview models.ReviewStateType = "REQUEST_REVIEW"
|
|
// ReviewStateUnknown state of pr is unknown
|
|
ReviewStateUnknown models.ReviewStateType = ""
|
|
)
|
|
|
|
type GiteaTimelineFetcher interface {
|
|
GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error)
|
|
}
|
|
|
|
type GiteaComment interface {
|
|
AddComment(pr *models.PullRequest, comment string) error
|
|
}
|
|
|
|
type GiteaSetRepoOptions interface {
|
|
SetRepoOptions(owner, repo string, manual_merge bool) (*models.Repository, error)
|
|
}
|
|
|
|
type GiteaMaintainershipReader interface {
|
|
FetchMaintainershipFile(org, prjGit, branch string) ([]byte, string, error)
|
|
FetchMaintainershipDirFile(org, prjGit, branch, pkg string) ([]byte, string, error)
|
|
}
|
|
|
|
type GiteaPRFetcher interface {
|
|
GetPullRequest(org, project string, num int64) (*models.PullRequest, error)
|
|
}
|
|
|
|
type GiteaPRUpdater interface {
|
|
UpdatePullRequest(org, repo string, num int64, options *models.EditPullRequestOption) (*models.PullRequest, error)
|
|
}
|
|
|
|
type GiteaPRTimelineFetcher interface {
|
|
GiteaPRFetcher
|
|
GiteaTimelineFetcher
|
|
}
|
|
|
|
type GiteaCommitFetcher interface {
|
|
GetCommit(org, repo, sha string) (*models.Commit, error)
|
|
}
|
|
|
|
type GiteaReviewFetcher interface {
|
|
GetPullRequestReviews(org, project string, PRnum int64) ([]*models.PullReview, error)
|
|
}
|
|
|
|
type GiteaCommentFetcher interface {
|
|
GetIssueComments(org, project string, issueNo int64) ([]*models.Comment, error)
|
|
}
|
|
|
|
type GiteaReviewTimelineFetcher interface {
|
|
GiteaReviewFetcher
|
|
GiteaTimelineFetcher
|
|
}
|
|
|
|
type GiteaPRChecker interface {
|
|
GiteaReviewTimelineFetcher
|
|
GiteaCommentFetcher
|
|
GiteaMaintainershipReader
|
|
}
|
|
|
|
type GiteaReviewFetcherAndRequester interface {
|
|
GiteaReviewTimelineFetcher
|
|
GiteaCommentFetcher
|
|
GiteaReviewRequester
|
|
}
|
|
|
|
type GiteaReviewRequester interface {
|
|
RequestReviews(pr *models.PullRequest, reviewer ...string) ([]*models.PullReview, error)
|
|
}
|
|
|
|
type GiteaReviewUnrequester interface {
|
|
UnrequestReview(org, repo string, id int64, reviwers ...string) error
|
|
}
|
|
|
|
type GiteaReviewer interface {
|
|
AddReviewComment(pr *models.PullRequest, state models.ReviewStateType, comment string) (*models.PullReview, error)
|
|
}
|
|
|
|
type GiteaRepoFetcher interface {
|
|
GetRepository(org, repo string) (*models.Repository, error)
|
|
}
|
|
|
|
type GiteaFileContentReader interface {
|
|
GetRepositoryFileContent(org, repo, hash, path string) ([]byte, string, error)
|
|
}
|
|
|
|
const (
|
|
CommitStatus_Pending = "pending"
|
|
CommitStatus_Success = "success"
|
|
CommitStatus_Fail = "failure"
|
|
CommitStatus_Error = "error"
|
|
)
|
|
|
|
type GiteaCommitStatusSetter interface {
|
|
SetCommitStatus(org, repo, hash string, status *models.CommitStatus) (*models.CommitStatus, error)
|
|
}
|
|
|
|
type GiteaCommitStatusGetter interface {
|
|
GetCommitStatus(org, repo, hash string) ([]*models.CommitStatus, error)
|
|
}
|
|
|
|
type Gitea interface {
|
|
GiteaComment
|
|
GiteaRepoFetcher
|
|
GiteaReviewRequester
|
|
GiteaReviewUnrequester
|
|
GiteaReviewer
|
|
GiteaPRFetcher
|
|
GiteaPRUpdater
|
|
GiteaCommitFetcher
|
|
GiteaReviewFetcher
|
|
GiteaCommentFetcher
|
|
GiteaTimelineFetcher
|
|
GiteaMaintainershipReader
|
|
GiteaFileContentReader
|
|
GiteaCommitStatusGetter
|
|
GiteaCommitStatusSetter
|
|
GiteaSetRepoOptions
|
|
GiteaTimelineFetcher
|
|
|
|
GetNotifications(Type string, since *time.Time) ([]*models.NotificationThread, error)
|
|
GetDoneNotifications(Type string, page int64) ([]*models.NotificationThread, error)
|
|
SetNotificationRead(notificationId int64) error
|
|
GetOrganization(orgName string) (*models.Organization, error)
|
|
GetOrganizationRepositories(orgName string) ([]*models.Repository, error)
|
|
CreateRepositoryIfNotExist(git Git, org, repoName string) (*models.Repository, error)
|
|
CreatePullRequestIfNotExist(repo *models.Repository, srcId, targetId, title, body string) (*models.PullRequest, error)
|
|
GetPullRequestFileContent(pr *models.PullRequest, path string) ([]byte, string, error)
|
|
GetRecentPullRequests(org, repo, branch string) ([]*models.PullRequest, error)
|
|
GetRecentCommits(org, repo, branch string, commitNo int64) ([]*models.Commit, error)
|
|
GetPullRequests(org, project string) ([]*models.PullRequest, error)
|
|
|
|
GetCurrentUser() (*models.User, error)
|
|
}
|
|
|
|
type GiteaTransport struct {
|
|
transport *transport.Runtime
|
|
client *apiclient.GiteaAPI
|
|
}
|
|
|
|
func AllocateGiteaTransport(giteaUrl string) Gitea {
|
|
var r GiteaTransport
|
|
|
|
url, err := url.Parse(giteaUrl)
|
|
if err != nil {
|
|
log.Panicln("Failed to parse gitea url:", err)
|
|
}
|
|
|
|
r.transport = transport.New(url.Host, apiclient.DefaultBasePath, [](string){url.Scheme})
|
|
r.transport.DefaultAuthentication = transport.BearerToken(giteaToken)
|
|
|
|
r.client = apiclient.New(r.transport, nil)
|
|
|
|
return &r
|
|
}
|
|
|
|
func (gitea *GiteaTransport) FetchMaintainershipFile(org, repo, branch string) ([]byte, string, error) {
|
|
return gitea.GetRepositoryFileContent(org, repo, branch, MaintainershipFile)
|
|
}
|
|
|
|
func (gitea *GiteaTransport) FetchMaintainershipDirFile(org, repo, branch, pkg string) ([]byte, string, error) {
|
|
return gitea.GetRepositoryFileContent(org, repo, branch, path.Join(MaintainershipDir, pkg))
|
|
}
|
|
|
|
func (gitea *GiteaTransport) GetPullRequest(org, project string, num int64) (*models.PullRequest, error) {
|
|
pr, err := gitea.client.Repository.RepoGetPullRequest(
|
|
repository.NewRepoGetPullRequestParams().
|
|
WithDefaults().
|
|
WithOwner(org).
|
|
WithRepo(project).
|
|
WithIndex(num),
|
|
gitea.transport.DefaultAuthentication,
|
|
)
|
|
|
|
return pr.Payload, err
|
|
}
|
|
|
|
func (gitea *GiteaTransport) UpdatePullRequest(org, repo string, num int64, options *models.EditPullRequestOption) (*models.PullRequest, error) {
|
|
pr, err := gitea.client.Repository.RepoEditPullRequest(
|
|
repository.NewRepoEditPullRequestParams().
|
|
WithOwner(org).
|
|
WithRepo(repo).
|
|
WithIndex(num).
|
|
WithBody(options),
|
|
gitea.transport.DefaultAuthentication,
|
|
)
|
|
|
|
return pr.Payload, err
|
|
}
|
|
|
|
func (gitea *GiteaTransport) GetPullRequests(org, repo string) ([]*models.PullRequest, error) {
|
|
var page, limit int64
|
|
|
|
prs := make([]*models.PullRequest, 0)
|
|
limit = 20
|
|
state := "open"
|
|
|
|
for {
|
|
page++
|
|
req, err := gitea.client.Repository.RepoListPullRequests(
|
|
repository.
|
|
NewRepoListPullRequestsParams().
|
|
WithDefaults().
|
|
WithOwner(org).
|
|
WithRepo(repo).
|
|
WithState(&state).
|
|
WithPage(&page).
|
|
WithLimit(&limit),
|
|
gitea.transport.DefaultAuthentication)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot fetch PR list for %s / %s : %w", org, repo, err)
|
|
}
|
|
|
|
prs = slices.Concat(prs, req.Payload)
|
|
if len(req.Payload) < int(limit) {
|
|
break
|
|
}
|
|
}
|
|
|
|
return prs, nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) GetCommitStatus(org, repo, hash string) ([]*models.CommitStatus, error) {
|
|
page := int64(1)
|
|
limit := int64(10)
|
|
var res []*models.CommitStatus
|
|
|
|
for {
|
|
r, err := gitea.client.Repository.RepoListStatuses(
|
|
repository.NewRepoListStatusesParams().WithDefaults().WithOwner(org).WithRepo(repo).WithSha(hash).WithPage(&page).WithLimit(&limit),
|
|
gitea.transport.DefaultAuthentication)
|
|
|
|
if err != nil {
|
|
return res, err
|
|
}
|
|
|
|
res = append(res, r.Payload...)
|
|
if len(r.Payload) < int(limit) {
|
|
break
|
|
}
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) SetCommitStatus(org, repo, hash string, status *models.CommitStatus) (*models.CommitStatus, error) {
|
|
res, err := gitea.client.Repository.RepoCreateStatus(
|
|
repository.NewRepoCreateStatusParams().
|
|
WithDefaults().
|
|
WithOwner(org).
|
|
WithRepo(repo).
|
|
WithSha(hash).
|
|
WithBody(&models.CreateStatusOption{
|
|
TargetURL: status.TargetURL,
|
|
Description: status.Description,
|
|
Context: status.Context,
|
|
State: models.CommitStatusState(status.Status),
|
|
}),
|
|
gitea.transport.DefaultAuthentication,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return res.Payload, err
|
|
}
|
|
|
|
func (gitea *GiteaTransport) GetRepository(org, pkg string) (*models.Repository, error) {
|
|
repo, err := gitea.client.Repository.RepoGet(repository.NewRepoGetParams().WithDefaults().WithOwner(org).WithRepo(pkg), gitea.transport.DefaultAuthentication)
|
|
if err != nil {
|
|
switch err.(type) {
|
|
case *repository.RepoGetNotFound:
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
return repo.Payload, nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) GetPullRequestReviews(org, project string, PRnum int64) ([]*models.PullReview, error) {
|
|
limit := int64(20)
|
|
var page int64
|
|
var allReviews []*models.PullReview
|
|
|
|
for {
|
|
reviews, err := gitea.client.Repository.RepoListPullReviews(
|
|
repository.NewRepoListPullReviewsParams().
|
|
WithDefaults().
|
|
WithOwner(org).
|
|
WithRepo(project).
|
|
WithIndex(PRnum).
|
|
WithPage(&page).
|
|
WithLimit(&limit),
|
|
gitea.transport.DefaultAuthentication,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
allReviews = slices.Concat(allReviews, reviews.Payload)
|
|
if len(reviews.Payload) < int(limit) {
|
|
break
|
|
}
|
|
page++
|
|
}
|
|
|
|
return allReviews, nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) GetCommit(org, repo, sha string) (*models.Commit, error) {
|
|
f := false
|
|
|
|
r, err := gitea.client.Repository.RepoGetSingleCommit(
|
|
repository.NewRepoGetSingleCommitParams().
|
|
WithOwner(org).
|
|
WithRepo(repo).
|
|
WithSha(sha).
|
|
WithStat(&f).
|
|
WithFiles(&f).
|
|
WithVerification(&f),
|
|
gitea.transport.DefaultAuthentication)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return r.Payload, nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) GetIssueComments(org, project string, issueNo int64) ([]*models.Comment, error) {
|
|
// limit := int64(20)
|
|
// var page int64
|
|
// var allComments []*models.Comment
|
|
|
|
// for {
|
|
c, err := gitea.client.Issue.IssueGetComments(
|
|
issue.NewIssueGetCommentsParams().
|
|
WithDefaults().
|
|
WithOwner(org).
|
|
WithRepo(project).
|
|
WithIndex(issueNo),
|
|
gitea.transport.DefaultAuthentication)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return c.Payload, nil
|
|
// if len(c.Payload) < int(limit)
|
|
// }
|
|
|
|
}
|
|
|
|
func (gitea *GiteaTransport) SetRepoOptions(owner, repo string, manual_merge bool) (*models.Repository, error) {
|
|
ok, err := gitea.client.Repository.RepoEdit(repository.NewRepoEditParams().WithOwner(owner).WithRepo(repo).WithBody(
|
|
&models.EditRepoOption{
|
|
AllowManualMerge: manual_merge,
|
|
AutodetectManualMerge: manual_merge,
|
|
}), gitea.transport.DefaultAuthentication)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return ok.Payload, err
|
|
}
|
|
|
|
const (
|
|
GiteaNotificationType_Pull = "Pull"
|
|
)
|
|
|
|
func (gitea *GiteaTransport) GetNotifications(Type string, since *time.Time) ([]*models.NotificationThread, error) {
|
|
bigLimit := int64(20)
|
|
ret := make([]*models.NotificationThread, 0, 100)
|
|
|
|
for page := int64(1); ; page++ {
|
|
params := notification.NewNotifyGetListParams().
|
|
WithDefaults().
|
|
WithSubjectType([]string{Type}).
|
|
WithStatusTypes([]string{"unread"}).
|
|
WithLimit(&bigLimit).
|
|
WithPage(&page)
|
|
|
|
if since != nil {
|
|
s := strfmt.DateTime(*since)
|
|
params.SetSince(&s)
|
|
}
|
|
|
|
list, err := gitea.client.Notification.NotifyGetList(params, gitea.transport.DefaultAuthentication)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ret = slices.Concat(ret, list.Payload)
|
|
if len(list.Payload) < int(bigLimit) {
|
|
break
|
|
}
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) GetDoneNotifications(Type string, page int64) ([]*models.NotificationThread, error) {
|
|
limit := int64(20)
|
|
t := true
|
|
|
|
if page <= 0 {
|
|
return nil, fmt.Errorf("Page is 1-base positive int...")
|
|
}
|
|
list, err := gitea.client.Notification.NotifyGetList(
|
|
notification.NewNotifyGetListParams().
|
|
WithAll(&t).
|
|
WithSubjectType([]string{Type}).
|
|
WithStatusTypes([]string{"read"}).
|
|
WithLimit(&limit).
|
|
WithPage(&page),
|
|
gitea.transport.DefaultAuthentication)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return list.Payload, nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) SetNotificationRead(notificationId int64) error {
|
|
_, err := gitea.client.Notification.NotifyReadThread(
|
|
notification.NewNotifyReadThreadParams().
|
|
WithDefaults().
|
|
WithID(fmt.Sprint(notificationId)),
|
|
gitea.transport.DefaultAuthentication,
|
|
)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("Error setting notification: %d. Err: %w", notificationId, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) GetOrganization(orgName string) (*models.Organization, error) {
|
|
org, err := gitea.client.Organization.OrgGet(
|
|
organization.NewOrgGetParams().WithOrg(orgName),
|
|
gitea.transport.DefaultAuthentication,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error fetching org: '%s' data. Err: %w", orgName, err)
|
|
}
|
|
return org.Payload, nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) GetOrganizationRepositories(orgName string) ([]*models.Repository, error) {
|
|
var page int64
|
|
repos := make([]*models.Repository, 0, 100)
|
|
|
|
page = 1
|
|
for {
|
|
ret, err := gitea.client.Organization.OrgListRepos(
|
|
organization.NewOrgListReposParams().WithOrg(orgName).WithPage(&page),
|
|
gitea.transport.DefaultAuthentication,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error retrieving repository list for org: '%s'. Err: %w", orgName, err)
|
|
}
|
|
|
|
if len(ret.Payload) == 0 {
|
|
break
|
|
}
|
|
|
|
repos = append(repos, ret.Payload...)
|
|
page++
|
|
}
|
|
|
|
return repos, nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) CreateRepositoryIfNotExist(git Git, org, repoName string) (*models.Repository, error) {
|
|
log.Println(org, repoName)
|
|
repo, err := gitea.client.Repository.RepoGet(
|
|
repository.NewRepoGetParams().WithDefaults().WithOwner(org).WithRepo(repoName),
|
|
gitea.transport.DefaultAuthentication)
|
|
|
|
if err != nil {
|
|
switch err.(type) {
|
|
case *repository.RepoGetNotFound:
|
|
log.Println("not found", err)
|
|
repo, err := gitea.client.Organization.CreateOrgRepo(
|
|
organization.NewCreateOrgRepoParams().WithDefaults().WithBody(
|
|
&models.CreateRepoOption{
|
|
AutoInit: false,
|
|
Name: &repoName,
|
|
ObjectFormatName: models.CreateRepoOptionObjectFormatNameSha256,
|
|
},
|
|
).WithOrg(org),
|
|
nil,
|
|
)
|
|
|
|
if err != nil {
|
|
switch err.(type) {
|
|
case *organization.CreateOrgRepoCreated:
|
|
// weird, but ok, repo created
|
|
default:
|
|
return nil, fmt.Errorf("error creating repo '%s' under '%s'. Err: %w", repoName, org, err)
|
|
}
|
|
}
|
|
|
|
// initialize repository
|
|
if err = os.Mkdir(filepath.Join(git.GetPath(), DefaultGitPrj), 0700); err != nil {
|
|
return nil, err
|
|
}
|
|
if err = git.GitExec(DefaultGitPrj, "init", "--object-format="+repo.Payload.ObjectFormatName); err != nil {
|
|
return nil, err
|
|
}
|
|
if err = git.GitExec(DefaultGitPrj, "checkout", "-b", repo.Payload.DefaultBranch); err != nil {
|
|
return nil, err
|
|
}
|
|
readmeFilename := filepath.Join(git.GetPath(), DefaultGitPrj, "README.md")
|
|
{
|
|
file, _ := os.Create(readmeFilename)
|
|
defer file.Close()
|
|
|
|
io.WriteString(file, ReadmeBoilerplate)
|
|
}
|
|
if err = git.GitExec(DefaultGitPrj, "add", "README.md"); err != nil {
|
|
return nil, err
|
|
}
|
|
if err = git.GitExec(DefaultGitPrj, "commit", "-m", "Automatic devel project creation"); err != nil {
|
|
return nil, err
|
|
}
|
|
if err = git.GitExec(DefaultGitPrj, "remote", "add", "origin", repo.Payload.SSHURL); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return repo.Payload, nil
|
|
default:
|
|
return nil, fmt.Errorf("cannot fetch repo data for %s/%s: %w", org, repoName, err)
|
|
}
|
|
}
|
|
|
|
return repo.Payload, nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) CreatePullRequestIfNotExist(repo *models.Repository, srcId, targetId, title, body string) (*models.PullRequest, error) {
|
|
prOptions := models.CreatePullRequestOption{
|
|
Base: targetId,
|
|
Head: srcId,
|
|
Title: title,
|
|
Body: body,
|
|
}
|
|
|
|
if pr, err := gitea.client.Repository.RepoGetPullRequestByBaseHead(
|
|
repository.NewRepoGetPullRequestByBaseHeadParams().WithOwner(repo.Owner.UserName).WithRepo(repo.Name).WithBase(targetId).WithHead(srcId),
|
|
gitea.transport.DefaultAuthentication,
|
|
); err == nil {
|
|
return pr.Payload, nil
|
|
}
|
|
|
|
pr, err := gitea.client.Repository.RepoCreatePullRequest(
|
|
repository.
|
|
NewRepoCreatePullRequestParams().
|
|
WithDefaults().
|
|
WithOwner(repo.Owner.UserName).
|
|
WithRepo(repo.Name).
|
|
WithBody(&prOptions),
|
|
gitea.transport.DefaultAuthentication,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Cannot create pull request. %w", err)
|
|
}
|
|
|
|
return pr.GetPayload(), nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) RequestReviews(pr *models.PullRequest, reviewers ...string) ([]*models.PullReview, error) {
|
|
reviewOptions := models.PullReviewRequestOptions{
|
|
Reviewers: reviewers,
|
|
}
|
|
|
|
review, err := gitea.client.Repository.RepoCreatePullReviewRequests(
|
|
repository.
|
|
NewRepoCreatePullReviewRequestsParams().
|
|
WithOwner(pr.Base.Repo.Owner.UserName).
|
|
WithRepo(pr.Base.Repo.Name).
|
|
WithIndex(pr.Index).
|
|
WithBody(&reviewOptions),
|
|
gitea.transport.DefaultAuthentication,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Cannot create pull request reviews: %w", err)
|
|
}
|
|
|
|
return review.GetPayload(), nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) UnrequestReview(org, repo string, id int64, reviwers ...string) error {
|
|
_, err := gitea.client.Repository.RepoDeletePullReviewRequests(
|
|
repository.NewRepoDeletePullReviewRequestsParams().WithOwner(org).WithRepo(repo).WithIndex(id).WithBody(&models.PullReviewRequestOptions{
|
|
Reviewers: reviwers,
|
|
}), gitea.transport.DefaultAuthentication)
|
|
return err
|
|
}
|
|
|
|
func (gitea *GiteaTransport) AddReviewComment(pr *models.PullRequest, state models.ReviewStateType, comment string) (*models.PullReview, error) {
|
|
c, err := gitea.client.Repository.RepoCreatePullReview(
|
|
repository.NewRepoCreatePullReviewParams().
|
|
WithDefaults().
|
|
WithOwner(pr.Base.Repo.Owner.UserName).
|
|
WithRepo(pr.Base.Repo.Name).
|
|
WithIndex(pr.Index).
|
|
WithBody(&models.CreatePullReviewOptions{
|
|
Event: state,
|
|
Body: comment,
|
|
}),
|
|
gitea.transport.DefaultAuthentication,
|
|
)
|
|
/*
|
|
c, err := client.Repository.RepoSubmitPullReview(
|
|
repository.NewRepoSubmitPullReviewParams().
|
|
WithDefaults().
|
|
WithOwner(pr.Base.Repo.Owner.UserName).
|
|
WithRepo(pr.Base.Repo.Name).
|
|
WithIndex(pr.Index).
|
|
WithID(review.ID).
|
|
WithBody(&models.SubmitPullReviewOptions{
|
|
Event: state,
|
|
Body: comment,
|
|
}),
|
|
transport.DefaultAuthentication,
|
|
)
|
|
*/
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return c.Payload, nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) AddComment(pr *models.PullRequest, comment string) error {
|
|
_, err := gitea.client.Issue.IssueCreateComment(
|
|
issue.NewIssueCreateCommentParams().
|
|
WithDefaults().
|
|
WithOwner(pr.Base.Repo.Owner.UserName).
|
|
WithRepo(pr.Base.Repo.Name).
|
|
WithIndex(pr.Index).
|
|
WithBody(&models.CreateIssueCommentOption{
|
|
Body: &comment,
|
|
}),
|
|
gitea.transport.DefaultAuthentication)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) GetTimeline(org, repo string, idx int64) ([]*models.TimelineComment, error) {
|
|
page := int64(1)
|
|
resCount := 1
|
|
|
|
retData := []*models.TimelineComment{}
|
|
|
|
for resCount > 0 {
|
|
res, err := gitea.client.Issue.IssueGetCommentsAndTimeline(
|
|
issue.NewIssueGetCommentsAndTimelineParams().
|
|
WithOwner(org).
|
|
WithRepo(repo).
|
|
WithIndex(idx).
|
|
WithPage(&page),
|
|
gitea.transport.DefaultAuthentication,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resCount = len(res.Payload)
|
|
LogDebug("page:", page, "len:", resCount)
|
|
page++
|
|
|
|
retData = append(retData, res.Payload...)
|
|
}
|
|
LogDebug("total results:", len(retData))
|
|
|
|
slices.SortFunc(retData, func(a, b *models.TimelineComment) int {
|
|
return time.Time(b.Created).Compare(time.Time(a.Created))
|
|
})
|
|
|
|
return retData, nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) GetRepositoryFileContent(org, repo, hash, path string) ([]byte, string, error) {
|
|
params := repository.NewRepoGetContentsParams().WithOwner(org).WithRepo(repo).WithFilepath(path)
|
|
if len(hash) > 0 {
|
|
params = params.WithRef(&hash)
|
|
}
|
|
content, err := gitea.client.Repository.RepoGetContents(params,
|
|
gitea.transport.DefaultAuthentication,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
if content.Payload.Encoding != "base64" {
|
|
return nil, "", fmt.Errorf("Unhandled content encoding: %s", content.Payload.Encoding)
|
|
}
|
|
|
|
if content.Payload.Size > 10000000 {
|
|
return nil, "", fmt.Errorf("Content length is too large for %s/%s/%s#%s - %d bytes", org, repo, path, hash, content.Payload.Size)
|
|
}
|
|
|
|
data := make([]byte, content.Payload.Size)
|
|
n, err := base64.StdEncoding.Decode(data, []byte(content.Payload.Content))
|
|
if err != nil {
|
|
log.Println(content.Payload.Content[239])
|
|
log.Println(len(content.Payload.Content))
|
|
log.Println(string(data))
|
|
log.Println(content.Payload.Encoding)
|
|
enc, _ := json.MarshalIndent(content.Payload, "", " ")
|
|
log.Println(string(enc))
|
|
return nil, "", fmt.Errorf("Error decoding file %s/%s/%s#%s : %w", org, repo, path, hash, err)
|
|
}
|
|
if n != int(content.Payload.Size) {
|
|
return nil, "", fmt.Errorf("Decoded length doesn't match expected for %s/%s/%s#%s - %d vs %d bytes", org, repo, path, hash, content.Payload.Size, n)
|
|
}
|
|
|
|
return data, content.Payload.SHA, nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) GetPullRequestFileContent(pr *models.PullRequest, path string) ([]byte, string, error) {
|
|
return gitea.GetRepositoryFileContent(pr.Head.Repo.Owner.UserName, pr.Head.Repo.Name, pr.Head.Sha, path)
|
|
}
|
|
|
|
func (gitea *GiteaTransport) GetRecentPullRequests(org, repo, branch string) ([]*models.PullRequest, error) {
|
|
prs := make([]*models.PullRequest, 0, 10)
|
|
var page int64
|
|
page = 1
|
|
sort := "recentupdate"
|
|
|
|
endPrs:
|
|
for {
|
|
res, err := gitea.client.Repository.RepoListPullRequests(
|
|
repository.NewRepoListPullRequestsParams().
|
|
WithOwner(org).
|
|
WithRepo(repo).
|
|
WithPage(&page).
|
|
WithSort(&sort),
|
|
gitea.transport.DefaultAuthentication)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(res.Payload) == 0 {
|
|
break
|
|
}
|
|
|
|
for _, pr := range res.Payload {
|
|
if pr.Base.Name != branch {
|
|
continue
|
|
}
|
|
|
|
// if pr is closed for more than a week, assume that we are done too
|
|
if pr.State == "closed" && time.Since(time.Time(pr.Updated)) > 7*24*time.Hour {
|
|
break endPrs
|
|
}
|
|
|
|
prs = append(prs, pr)
|
|
}
|
|
|
|
page++
|
|
}
|
|
|
|
return prs, nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) GetRecentCommits(org, repo, branch string, commitNo int64) ([]*models.Commit, error) {
|
|
not := false
|
|
var page int64 = 1
|
|
params := repository.NewRepoGetAllCommitsParams().
|
|
WithOwner(org).
|
|
WithRepo(repo).
|
|
WithPage(&page).
|
|
WithStat(¬).
|
|
WithFiles(¬).
|
|
WithVerification(¬).
|
|
WithLimit(&commitNo)
|
|
|
|
if len(branch) > 0 {
|
|
params = params.WithSha(&branch)
|
|
}
|
|
|
|
commits, err := gitea.client.Repository.RepoGetAllCommits(params, gitea.transport.DefaultAuthentication)
|
|
|
|
if err != nil {
|
|
switch err.(type) {
|
|
case *repository.RepoGetAllCommitsNotFound:
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
return commits.Payload, nil
|
|
}
|
|
|
|
func (gitea *GiteaTransport) GetCurrentUser() (*models.User, error) {
|
|
user, err := gitea.client.User.UserGetCurrent(
|
|
user.NewUserGetCurrentParams(),
|
|
gitea.transport.DefaultAuthentication,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return user.GetPayload(), nil
|
|
}
|