Make dashboard updateable

This commit is contained in:
2026-07-28 12:44:31 +02:00
parent 72e1eb1fda
commit bb8e91039f
21 changed files with 3482 additions and 43 deletions

129
github.go
View File

@@ -25,6 +25,14 @@ type GitHubWriteService interface {
ReplyToThread(context.Context, string, string) (ReviewComment, error)
}
type GitHubPullRequestWriteService interface {
UpdatePullRequest(context.Context, string, PullRequestMetadata) (PullRequestMetadata, error)
}
type GitHubBranchService interface {
ListBranches(context.Context, string, string) ([]RepositoryBranch, error)
}
type GitHubClient struct {
endpoint string
token string
@@ -121,6 +129,84 @@ query PullRequests($query: String!, $first: Int!, $after: String) {
}
}`
const repositoryBranchesQuery = `
query RepositoryBranches($owner: String!, $name: String!, $after: String) {
repository(owner: $owner, name: $name) {
defaultBranchRef { name }
refs(refPrefix: "refs/heads/", first: 100, after: $after) {
pageInfo { hasNextPage endCursor }
nodes {
name
target {
... on Commit { committedDate }
}
}
}
}
}`
func (c *GitHubClient) ListBranches(
ctx context.Context, owner, name string,
) ([]RepositoryBranch, error) {
var branches []RepositoryBranch
after := ""
defaultBranch := ""
for {
var data struct {
Repository *struct {
DefaultBranchRef *struct{ Name string }
Refs struct {
PageInfo githubPageInfo
Nodes []struct {
Name string
Target *struct{ CommittedDate time.Time }
}
}
}
}
if err := c.query(ctx, repositoryBranchesQuery, map[string]any{
"owner": owner,
"name": name,
"after": nullableCursor(after),
}, &data); err != nil {
return nil, err
}
if data.Repository == nil {
return nil, fmt.Errorf("repository %s/%s was not found", owner, name)
}
if data.Repository.DefaultBranchRef != nil {
defaultBranch = data.Repository.DefaultBranchRef.Name
}
for _, node := range data.Repository.Refs.Nodes {
if node.Name == "" {
continue
}
branch := RepositoryBranch{Name: node.Name, IsDefault: node.Name == defaultBranch}
if node.Target != nil {
branch.UpdatedAt = node.Target.CommittedDate
}
branches = append(branches, branch)
}
if !data.Repository.Refs.PageInfo.HasNextPage {
break
}
after = data.Repository.Refs.PageInfo.EndCursor
if after == "" {
return nil, errors.New("GitHub returned an empty branch pagination cursor")
}
}
sort.SliceStable(branches, func(i, j int) bool {
if branches[i].IsDefault != branches[j].IsDefault {
return branches[i].IsDefault
}
if !branches[i].UpdatedAt.Equal(branches[j].UpdatedAt) {
return branches[i].UpdatedAt.After(branches[j].UpdatedAt)
}
return strings.ToLower(branches[i].Name) < strings.ToLower(branches[j].Name)
})
return branches, nil
}
type githubPRSearchNode struct {
ID string `json:"id"`
Number int `json:"number"`
@@ -478,6 +564,15 @@ mutation ReplyToReviewThread($input: AddPullRequestReviewThreadReplyInput!) {
}
}`
const updatePullRequestMutation = `
mutation UpdatePullRequest($input: UpdatePullRequestInput!) {
updatePullRequest(input: $input) {
pullRequest {
id title body baseRefName updatedAt mergeable mergeStateStatus
}
}
}`
type githubActor struct {
Login string `json:"login"`
Name string `json:"name"`
@@ -1190,6 +1285,40 @@ func (c *GitHubClient) ReplyToThread(
return convertReviewComment(*data.AddPullRequestReviewThreadReply.Comment), nil
}
func (c *GitHubClient) UpdatePullRequest(
ctx context.Context,
pullRequestID string,
update PullRequestMetadata,
) (PullRequestMetadata, error) {
var data struct {
UpdatePullRequest *struct {
PullRequest *struct {
ID, Title, Body, BaseRefName, Mergeable, MergeStateStatus string
UpdatedAt time.Time
}
}
}
if err := c.query(ctx, updatePullRequestMutation, map[string]any{
"input": map[string]any{
"pullRequestId": pullRequestID,
"title": update.Title,
"body": update.Body,
"baseRefName": update.BaseRef,
},
}, &data); err != nil {
return PullRequestMetadata{}, err
}
if data.UpdatePullRequest == nil || data.UpdatePullRequest.PullRequest == nil {
return PullRequestMetadata{}, errors.New("GitHub returned no updated pull request")
}
node := data.UpdatePullRequest.PullRequest
return PullRequestMetadata{
Title: node.Title, Body: node.Body, BaseRef: node.BaseRefName,
Mergeable: node.Mergeable, MergeState: node.MergeStateStatus,
UpdatedAt: node.UpdatedAt,
}, nil
}
func convertReviewThread(thread githubReviewThread) ReviewThread {
item := ReviewThread{
ID: thread.ID, Path: thread.Path, DiffSide: thread.DiffSide,