Add auto-merge setting and merge now functionality

This commit is contained in:
2026-07-28 17:42:49 +02:00
parent 7511311297
commit 42dcceaf4b
10 changed files with 689 additions and 63 deletions

163
github.go
View File

@@ -30,6 +30,11 @@ type GitHubPullRequestWriteService interface {
UpdatePullRequest(context.Context, string, PullRequestMetadata) (PullRequestMetadata, error)
}
type GitHubMergeService interface {
SetPullRequestAutoMerge(context.Context, string, string, string, bool) (*AutoMergeRequest, error)
MergePullRequest(context.Context, string, string, string) (PullRequestMergeResult, error)
}
type GitHubBranchService interface {
ListBranches(context.Context, string, string) ([]RepositoryBranch, error)
}
@@ -429,7 +434,7 @@ func nullableCursor(cursor string) any {
const detailsQuery = `
query PullRequestDetails($owner: String!, $name: String!, $number: Int!) {
repository(owner: $owner, name: $name) {
url
url mergeCommitAllowed squashMergeAllowed rebaseMergeAllowed
viewerPermission
defaultBranchRef { name }
rulesets(first: 100, includeParents: true, targets: [BRANCH]) {
@@ -440,11 +445,13 @@ query PullRequestDetails($owner: String!, $name: String!, $number: Int!) {
}
}
pullRequest(number: $number) {
id number title url body isDraft createdAt updatedAt
id number title url body isDraft createdAt updatedAt state merged mergedAt
mergeable mergeStateStatus reviewDecision
autoMergeRequest { mergeMethod enabledAt enabledBy { login } }
mergeQueueEntry { state position enqueuedAt estimatedTimeToMerge }
baseRefName headRefName headRefOid
viewerCanUpdate viewerCanReact viewerCanSubscribe viewerCanEnableAutoMerge
viewerCanUpdate viewerCanReact viewerCanSubscribe
viewerCanEnableAutoMerge viewerCanDisableAutoMerge
baseRef {
target { ... on Commit { oid } }
branchProtectionRule {
@@ -631,6 +638,29 @@ mutation UpdatePullRequest($input: UpdatePullRequestInput!) {
}
}`
const enableAutoMergeMutation = `
mutation EnablePullRequestAutoMerge($input: EnablePullRequestAutoMergeInput!) {
enablePullRequestAutoMerge(input: $input) {
pullRequest {
autoMergeRequest { mergeMethod enabledAt enabledBy { login } }
}
}
}`
const disableAutoMergeMutation = `
mutation DisablePullRequestAutoMerge($input: DisablePullRequestAutoMergeInput!) {
disablePullRequestAutoMerge(input: $input) {
pullRequest { id autoMergeRequest { mergeMethod } }
}
}`
const mergePullRequestMutation = `
mutation MergePullRequest($input: MergePullRequestInput!) {
mergePullRequest(input: $input) {
pullRequest { merged mergedAt }
}
}`
type githubActor struct {
Login string `json:"login"`
Name string `json:"name"`
@@ -759,14 +789,20 @@ type githubRuleset struct {
}
type githubPullRequestDetails struct {
ID, Title, URL, Body, Mergeable, MergeStateStatus string
ReviewDecision, BaseRefName, HeadRefName, HeadRefOID string
Number, Additions, Deletions, ChangedFiles int
IsDraft bool
CreatedAt, UpdatedAt time.Time
Author *githubActor
ViewerCanUpdate, ViewerCanReact, ViewerCanSubscribe, ViewerCanEnableAutoMerge bool
BaseRef *struct {
ID, Title, URL, Body, State, Mergeable, MergeStateStatus string
ReviewDecision, BaseRefName, HeadRefName, HeadRefOID string
Number, Additions, Deletions, ChangedFiles int
IsDraft, Merged bool
CreatedAt, UpdatedAt, MergedAt time.Time
Author *githubActor
ViewerCanUpdate, ViewerCanReact, ViewerCanSubscribe bool
ViewerCanEnableAutoMerge, ViewerCanDisableAutoMerge bool
AutoMergeRequest *struct {
MergeMethod string
EnabledAt time.Time
EnabledBy *githubActor
}
BaseRef *struct {
Target *struct{ OID string }
BranchProtectionRule *struct {
RequiresApprovingReviews, RequiresStatusChecks bool
@@ -1057,11 +1093,12 @@ func (c *GitHubClient) allCheckAnnotations(
func (c *GitHubClient) GetPullRequest(ctx context.Context, owner, name string, number int) (PRDetails, error) {
var data struct {
Repository *struct {
URL string
ViewerPermission string `json:"viewerPermission"`
DefaultBranchRef *struct{ Name string }
Rulesets struct{ Nodes []githubRuleset }
PullRequest *githubPullRequestDetails `json:"pullRequest"`
URL string
ViewerPermission string `json:"viewerPermission"`
MergeCommitAllowed, SquashMergeAllowed, RebaseMergeAllowed bool
DefaultBranchRef *struct{ Name string }
Rulesets struct{ Nodes []githubRuleset }
PullRequest *githubPullRequestDetails `json:"pullRequest"`
} `json:"repository"`
}
if err := c.query(ctx, detailsQuery, map[string]any{"owner": owner, "name": name, "number": number}, &data); err != nil {
@@ -1138,6 +1175,7 @@ func (c *GitHubClient) GetPullRequest(ctx context.Context, owner, name string, n
},
Body: node.Body, CreatedAt: node.CreatedAt, BaseRef: node.BaseRefName, HeadRef: node.HeadRefName,
HeadOID: node.HeadRefOID, Mergeable: node.Mergeable, MergeState: node.MergeStateStatus,
State: node.State, Merged: node.Merged, MergedAt: node.MergedAt,
RepositoryURL: data.Repository.URL,
Additions: node.Additions, Deletions: node.Deletions, ChangedFiles: node.ChangedFiles,
CommitCount: node.Commits.TotalCount, CommentCount: node.Comments.TotalCount,
@@ -1146,8 +1184,25 @@ func (c *GitHubClient) GetPullRequest(ctx context.Context, owner, name string, n
Repository: data.Repository.ViewerPermission,
CanUpdatePR: node.ViewerCanUpdate, CanReact: node.ViewerCanReact,
CanSubscribe: node.ViewerCanSubscribe, CanEnableMerge: node.ViewerCanEnableAutoMerge,
CanDisableMerge: node.ViewerCanDisableAutoMerge,
},
}
if data.Repository.SquashMergeAllowed {
details.AllowedMergeMethods = append(details.AllowedMergeMethods, "SQUASH")
}
if data.Repository.MergeCommitAllowed {
details.AllowedMergeMethods = append(details.AllowedMergeMethods, "MERGE")
}
if data.Repository.RebaseMergeAllowed {
details.AllowedMergeMethods = append(details.AllowedMergeMethods, "REBASE")
}
if node.AutoMergeRequest != nil {
details.AutoMerge = &AutoMergeRequest{
MergeMethod: node.AutoMergeRequest.MergeMethod,
EnabledAt: node.AutoMergeRequest.EnabledAt,
EnabledBy: actorLogin(node.AutoMergeRequest.EnabledBy),
}
}
if node.BaseRef != nil && node.BaseRef.Target != nil {
details.BaseOID = node.BaseRef.Target.OID
}
@@ -1460,6 +1515,82 @@ func (c *GitHubClient) UpdatePullRequest(
}, nil
}
func (c *GitHubClient) SetPullRequestAutoMerge(
ctx context.Context, pullRequestID, expectedHeadOID, mergeMethod string, enabled bool,
) (*AutoMergeRequest, error) {
if !enabled {
var data struct {
DisablePullRequestAutoMerge *struct{ PullRequest *struct{ ID string } }
}
if err := c.query(ctx, disableAutoMergeMutation, map[string]any{
"input": map[string]any{"pullRequestId": pullRequestID},
}, &data); err != nil {
return nil, err
}
if data.DisablePullRequestAutoMerge == nil ||
data.DisablePullRequestAutoMerge.PullRequest == nil {
return nil, errors.New("GitHub returned no pull request after disabling auto-merge")
}
return nil, nil
}
var data struct {
EnablePullRequestAutoMerge *struct {
PullRequest *struct {
AutoMergeRequest *struct {
MergeMethod string
EnabledAt time.Time
EnabledBy *githubActor
}
}
}
}
input := map[string]any{
"pullRequestId": pullRequestID, "expectedHeadOid": expectedHeadOID,
"mergeMethod": mergeMethod,
}
if err := c.query(ctx, enableAutoMergeMutation, map[string]any{"input": input}, &data); err != nil {
return nil, err
}
if data.EnablePullRequestAutoMerge == nil ||
data.EnablePullRequestAutoMerge.PullRequest == nil ||
data.EnablePullRequestAutoMerge.PullRequest.AutoMergeRequest == nil {
return nil, errors.New("GitHub returned no auto-merge request")
}
node := data.EnablePullRequestAutoMerge.PullRequest.AutoMergeRequest
return &AutoMergeRequest{
MergeMethod: node.MergeMethod, EnabledAt: node.EnabledAt,
EnabledBy: actorLogin(node.EnabledBy),
}, nil
}
func (c *GitHubClient) MergePullRequest(
ctx context.Context, pullRequestID, expectedHeadOID, mergeMethod string,
) (PullRequestMergeResult, error) {
var data struct {
MergePullRequest *struct {
PullRequest *struct {
Merged bool
MergedAt time.Time
}
}
}
if err := c.query(ctx, mergePullRequestMutation, map[string]any{
"input": map[string]any{
"pullRequestId": pullRequestID, "expectedHeadOid": expectedHeadOID,
"mergeMethod": mergeMethod,
},
}, &data); err != nil {
return PullRequestMergeResult{}, err
}
if data.MergePullRequest == nil || data.MergePullRequest.PullRequest == nil ||
!data.MergePullRequest.PullRequest.Merged {
return PullRequestMergeResult{}, errors.New("GitHub did not merge the pull request")
}
return PullRequestMergeResult{
Merged: true, MergedAt: data.MergePullRequest.PullRequest.MergedAt,
}, nil
}
func convertReviewThread(thread githubReviewThread) ReviewThread {
item := ReviewThread{
ID: thread.ID, Path: thread.Path, DiffSide: thread.DiffSide,