dailyops:補齊 tea 對等指令(#40)— pulls approve/close/checkout、通用 comment、issues list --labels
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitea.alterminal.com/alterminal/teai/internal/gitea"
|
||||
)
|
||||
@@ -119,8 +120,10 @@ type Pull struct {
|
||||
// pullRef 是 PR 的 head/base 參照。
|
||||
type pullRef struct {
|
||||
Ref string `json:"ref"`
|
||||
SHA string `json:"sha"`
|
||||
Repo *struct {
|
||||
FullName string `json:"full_name"`
|
||||
CloneURL string `json:"clone_url"`
|
||||
} `json:"repo"`
|
||||
}
|
||||
|
||||
@@ -165,8 +168,9 @@ func repoPath(owner, repo string) string {
|
||||
}
|
||||
|
||||
// ListIssues 列出倉庫 issues。state:open|closed|all(預設 open);
|
||||
// type=issues 排除 PR(Gitea 的 issues 端點會連 PR 一起回)。
|
||||
func (o *Ops) ListIssues(ctx context.Context, owner, repo, state string) ([]Issue, error) {
|
||||
// labels 非空時以逗號連接傳給 API 的 labels 參數(Gitea labels 過濾,
|
||||
// 多標籤為 AND 語義);type=issues 排除 PR(Gitea 的 issues 端點會連 PR 一起回)。
|
||||
func (o *Ops) ListIssues(ctx context.Context, owner, repo, state string, labels []string) ([]Issue, error) {
|
||||
if state == "" {
|
||||
state = "open"
|
||||
}
|
||||
@@ -174,6 +178,9 @@ func (o *Ops) ListIssues(ctx context.Context, owner, repo, state string) ([]Issu
|
||||
// open(#33),closed 項目會漏列;all 也不能省略。
|
||||
extra := url.Values{}
|
||||
extra.Set("state", state)
|
||||
if len(labels) > 0 {
|
||||
extra.Set("labels", strings.Join(labels, ","))
|
||||
}
|
||||
// type=issues:排除 PR(PR 在此端點帶 pull_request 欄位)。
|
||||
extra.Set("type", "issues")
|
||||
var issues []Issue
|
||||
@@ -259,12 +266,93 @@ func (o *Ops) ListPulls(ctx context.Context, owner, repo, state string) ([]Pull,
|
||||
|
||||
// GetPull 取得單一 PR。
|
||||
func (o *Ops) GetPull(ctx context.Context, owner, repo string, number int64) (*Pull, error) {
|
||||
var pull Pull
|
||||
var pr Pull
|
||||
path := repoPath(owner, repo) + "/pulls/" + strconv.FormatInt(number, 10)
|
||||
if err := o.Client.GetJSON(ctx, path, &pull); err != nil {
|
||||
if err := o.Client.GetJSON(ctx, path, &pr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pull, nil
|
||||
return &pr, nil
|
||||
}
|
||||
|
||||
// Review 是 pull request 審核的精簡欄位。
|
||||
type Review struct {
|
||||
ID int64 `json:"ID"`
|
||||
Number int64 `json:"number"`
|
||||
State string `json:"state"`
|
||||
Body string `json:"body"`
|
||||
Submitted string `json:"submitted_at"`
|
||||
User *User `json:"user"`
|
||||
}
|
||||
|
||||
// Author 回傳審核者帳號。
|
||||
func (r *Review) Author() string { return r.User.Name() }
|
||||
|
||||
// ApprovePull 核准 PR(POST /repos/{owner}/{repo}/pulls/{n}/reviews,event=APPROVED)。
|
||||
// body 是審核意見,可為空。
|
||||
func (o *Ops) ApprovePull(ctx context.Context, owner, repo string, number int64, body string) (*Review, error) {
|
||||
payload := map[string]any{
|
||||
"event": "APPROVED",
|
||||
"body": body,
|
||||
}
|
||||
var review Review
|
||||
path := repoPath(owner, repo) + "/pulls/" + strconv.FormatInt(number, 10) + "/reviews"
|
||||
if err := o.Client.DoJSON(ctx, "POST", path, "", payload, &review); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &review, nil
|
||||
}
|
||||
|
||||
// ClosePull 關閉 PR 不合併(PATCH /repos/{owner}/{repo}/pulls/{n},state=closed)。
|
||||
func (o *Ops) ClosePull(ctx context.Context, owner, repo string, number int64) (*Pull, error) {
|
||||
payload := map[string]any{"state": "closed"}
|
||||
var pr Pull
|
||||
path := repoPath(owner, repo) + "/pulls/" + strconv.FormatInt(number, 10)
|
||||
if err := o.Client.DoJSON(ctx, "PATCH", path, "", payload, &pr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pr, nil
|
||||
}
|
||||
|
||||
// HeadInfo 是 PR head 參照的完整資訊(checkout 用:同倉庫分支 or 跨倉庫 clone 網址)。
|
||||
type HeadInfo struct {
|
||||
// Ref 是 head 分支名。
|
||||
Ref string
|
||||
// Repo 是 head 所屬倉庫(owner/name);與目標倉庫相同時為空。
|
||||
Repo string
|
||||
// CloneURL 是 head 倉庫的 https clone 網址;同倉庫時為空。
|
||||
CloneURL string
|
||||
// SHA 是 head commit(暫不使用,保留給未來 reset/rebase 用)。
|
||||
SHA string
|
||||
}
|
||||
|
||||
// PullHeadInfo 取得 PR 的 head 資訊。同倉庫分支 → Repo/CloneURL 為空、只剩 Ref;
|
||||
// 跨倉庫 fork → Ref+Repo+CloneURL 齊備,checkout 改以 clone --branch Ref CloneURL。
|
||||
func (o *Ops) PullHeadInfo(ctx context.Context, owner, repo string, number int64) (*HeadInfo, error) {
|
||||
pr, err := o.GetPull(ctx, owner, repo, number)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info := &HeadInfo{Ref: pr.HeadRef()}
|
||||
if pr.Head != nil {
|
||||
info.SHA = pr.Head.SHA
|
||||
if pr.Head.Repo != nil {
|
||||
full := pr.Head.Repo.FullName
|
||||
same := full != "" && full == owner+"/"+repo
|
||||
if !same {
|
||||
info.Repo = full
|
||||
info.CloneURL = pr.Head.Repo.CloneURL
|
||||
}
|
||||
}
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// String 依 head 來源輸出人類可讀描述(輸出用)。
|
||||
func (h *HeadInfo) String() string {
|
||||
if h.Repo != "" {
|
||||
return h.Repo + "#" + h.Ref
|
||||
}
|
||||
return h.Ref
|
||||
}
|
||||
|
||||
// CreatePull 建立 PR。
|
||||
|
||||
Reference in New Issue
Block a user