Skip to content

Commit 60bb6b7

Browse files
committed
monitor Linter CI check and comment @Copilot fixs it
A
1 parent 33c737d commit 60bb6b7

File tree

6 files changed

+423
-1
lines changed

6 files changed

+423
-1
lines changed

controllers/pr_check.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright 2021 The casbin Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package controllers
16+
17+
import (
18+
"strconv"
19+
20+
"github.com/casbin/casbin-oa/object"
21+
)
22+
23+
// GetPrChecks retrieves all PR check records for a specific PR.
24+
func (c *ApiController) GetPrChecks() {
25+
org := c.Input().Get("org")
26+
repo := c.Input().Get("repo")
27+
prNumberStr := c.Input().Get("prNumber")
28+
29+
if org == "" || repo == "" || prNumberStr == "" {
30+
c.Data["json"] = map[string]interface{}{
31+
"status": "error",
32+
"msg": "Missing required parameters",
33+
}
34+
c.ServeJSON()
35+
return
36+
}
37+
38+
prNumber, err := strconv.Atoi(prNumberStr)
39+
if err != nil {
40+
c.Data["json"] = map[string]interface{}{
41+
"status": "error",
42+
"msg": "Invalid PR number",
43+
}
44+
c.ServeJSON()
45+
return
46+
}
47+
48+
prChecks := object.GetPrChecksByPR(org, repo, prNumber)
49+
c.Data["json"] = prChecks
50+
c.ServeJSON()
51+
}
52+
53+
// GetPrCheck retrieves a specific PR check record.
54+
func (c *ApiController) GetPrCheck() {
55+
org := c.Input().Get("org")
56+
repo := c.Input().Get("repo")
57+
prNumberStr := c.Input().Get("prNumber")
58+
checkName := c.Input().Get("checkName")
59+
60+
if org == "" || repo == "" || prNumberStr == "" || checkName == "" {
61+
c.Data["json"] = map[string]interface{}{
62+
"status": "error",
63+
"msg": "Missing required parameters",
64+
}
65+
c.ServeJSON()
66+
return
67+
}
68+
69+
prNumber, err := strconv.Atoi(prNumberStr)
70+
if err != nil {
71+
c.Data["json"] = map[string]interface{}{
72+
"status": "error",
73+
"msg": "Invalid PR number",
74+
}
75+
c.ServeJSON()
76+
return
77+
}
78+
79+
prCheck := object.GetPrCheck(org, repo, prNumber, checkName)
80+
c.Data["json"] = prCheck
81+
c.ServeJSON()
82+
}

controllers/webhook.go

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package controllers
1717
import (
1818
"encoding/json"
1919
"fmt"
20+
"time"
2021

2122
"github.com/casbin/casbin-oa/object"
2223
"github.com/casbin/casbin-oa/util"
@@ -26,9 +27,26 @@ import (
2627
func (c *ApiController) WebhookOpen() {
2728
var issueEvent github.IssuesEvent
2829
var pullRequestEvent github.PullRequestEvent
30+
var checkRunEvent github.CheckRunEvent
31+
32+
// Try to parse as different event types.
33+
eventType := c.Ctx.Request.Header.Get("X-GitHub-Event")
34+
35+
result := false
36+
switch eventType {
37+
case "check_run":
38+
err := json.Unmarshal(c.Ctx.Input.RequestBody, &checkRunEvent)
39+
if err == nil && checkRunEvent.CheckRun != nil {
40+
result = HandleCheckRunEvent(checkRunEvent)
41+
c.Data["json"] = result
42+
c.ServeJSON()
43+
return
44+
}
45+
}
46+
47+
// Legacy handling for issues and pull requests.
2948
json.Unmarshal(c.Ctx.Input.RequestBody, &pullRequestEvent)
3049

31-
var result bool
3250
if pullRequestEvent.PullRequest != nil {
3351
result = PullRequestOpen(pullRequestEvent)
3452
} else {
@@ -106,6 +124,89 @@ func PullRequestOpen(pullRequestEvent github.PullRequestEvent) bool {
106124
go util.Comment(commentStr, owner, repo, pullRequestEvent.GetNumber())
107125
}
108126
}
127+
// Request automatic code review from copilot for human-created PRs.
128+
go util.RequestCopilotReview(owner, repo, pullRequestEvent.GetNumber())
129+
}
130+
return true
131+
}
132+
133+
// HandleCheckRunEvent handles check_run webhook events.
134+
func HandleCheckRunEvent(event github.CheckRunEvent) bool {
135+
if event.GetAction() != "completed" {
136+
return false
137+
}
138+
139+
checkRun := event.GetCheckRun()
140+
if checkRun == nil {
141+
return false
142+
}
143+
144+
// Only process failed checks.
145+
if checkRun.GetConclusion() != "failure" && checkRun.GetConclusion() != "cancelled" {
146+
return false
147+
}
148+
149+
// Get PR information.
150+
prs := checkRun.PullRequests
151+
if len(prs) == 0 {
152+
return false
153+
}
154+
155+
owner, repo := util.GetOwnerAndNameFromId(event.Repo.GetFullName())
156+
//issueWebhook := object.GetIssueIfExist(owner, repo)
157+
//if issueWebhook == nil {
158+
// return false
159+
//}
160+
161+
for _, pr := range prs {
162+
prNumber := pr.GetNumber()
163+
checkName := checkRun.GetName()
164+
165+
// Check if this is a linter check.
166+
if !util.IsLinterCheck(checkName) {
167+
continue
168+
}
169+
170+
// Check if we should attempt to fix (max 3 attempts).
171+
if !object.ShouldAttemptFix(owner, repo, prNumber, checkName) {
172+
continue
173+
}
174+
175+
// Get or create PR check record.
176+
prCheck := object.GetPrCheck(owner, repo, prNumber, checkName)
177+
if prCheck == nil {
178+
// Create new record.
179+
prCheck = &object.PrCheck{
180+
Org: owner,
181+
Repo: repo,
182+
PrNumber: prNumber,
183+
CheckRunId: checkRun.GetID(),
184+
CheckName: checkName,
185+
Status: checkRun.GetStatus(),
186+
Conclusion: checkRun.GetConclusion(),
187+
FailureReason: util.GetCheckFailureDetails(checkRun),
188+
FixAttempts: 0,
189+
LastAttemptAt: time.Now(),
190+
IsFixed: false,
191+
CreatedAt: time.Now(),
192+
}
193+
object.AddPrCheck(prCheck)
194+
} else {
195+
// Update existing record.
196+
prCheck.CheckRunId = checkRun.GetID()
197+
prCheck.Status = checkRun.GetStatus()
198+
prCheck.Conclusion = checkRun.GetConclusion()
199+
prCheck.FailureReason = util.GetCheckFailureDetails(checkRun)
200+
object.UpdatePrCheck(prCheck.Id, prCheck)
201+
}
202+
203+
// Increment fix attempts and get updated record.
204+
prCheck = object.IncrementFixAttempts(owner, repo, prNumber, checkName)
205+
if prCheck != nil {
206+
// Comment on PR with failure details and tag copilot.
207+
go util.CommentOnPRWithCopilotTag(owner, repo, prNumber, prCheck.FailureReason, prCheck.FixAttempts)
208+
}
109209
}
210+
110211
return true
111212
}

object/adapter.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,9 @@ func (a *Adapter) createTable() {
132132
if err != nil {
133133
panic(err)
134134
}
135+
136+
err = a.Engine.Sync2(new(PrCheck))
137+
if err != nil {
138+
panic(err)
139+
}
135140
}

object/pr_check.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright 2021 The casbin Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package object
16+
17+
import (
18+
"time"
19+
20+
"github.com/casbin/casbin-oa/util"
21+
)
22+
23+
type PrCheck struct {
24+
Id int `xorm:"int notnull pk autoincr" json:"id"`
25+
Org string `xorm:"varchar(100)" json:"org"`
26+
Repo string `xorm:"varchar(100)" json:"repo"`
27+
PrNumber int `xorm:"int" json:"prNumber"`
28+
CheckRunId int64 `xorm:"bigint" json:"checkRunId"`
29+
CheckName string `xorm:"varchar(200)" json:"checkName"`
30+
Status string `xorm:"varchar(50)" json:"status"`
31+
Conclusion string `xorm:"varchar(50)" json:"conclusion"`
32+
FailureReason string `xorm:"text" json:"failureReason"`
33+
FixAttempts int `xorm:"int" json:"fixAttempts"`
34+
LastAttemptAt time.Time `xorm:"datetime" json:"lastAttemptAt"`
35+
IsFixed bool `xorm:"bool" json:"isFixed"`
36+
CreatedAt time.Time `xorm:"datetime" json:"createdAt"`
37+
}
38+
39+
// GetPrCheck retrieves a PR check by org, repo, PR number and check name.
40+
func GetPrCheck(org string, repo string, prNumber int, checkName string) *PrCheck {
41+
prCheck := PrCheck{}
42+
existed, err := adapter.Engine.Where("org = ? and repo = ? and pr_number = ? and check_name = ?",
43+
org, repo, prNumber, checkName).Desc("id").Get(&prCheck)
44+
if err != nil {
45+
panic(err)
46+
}
47+
if existed {
48+
return &prCheck
49+
}
50+
return nil
51+
}
52+
53+
// GetPrChecksByPR retrieves all checks for a specific PR.
54+
func GetPrChecksByPR(org string, repo string, prNumber int) []*PrCheck {
55+
prChecks := []*PrCheck{}
56+
err := adapter.Engine.Where("org = ? and repo = ? and pr_number = ?",
57+
org, repo, prNumber).Find(&prChecks)
58+
if err != nil {
59+
panic(err)
60+
}
61+
return prChecks
62+
}
63+
64+
// AddPrCheck adds a new PR check record.
65+
func AddPrCheck(prCheck *PrCheck) bool {
66+
affected, err := adapter.Engine.Insert(prCheck)
67+
if err != nil {
68+
panic(err)
69+
}
70+
return affected != 0
71+
}
72+
73+
// UpdatePrCheck updates an existing PR check record.
74+
func UpdatePrCheck(id int, prCheck *PrCheck) bool {
75+
_, err := adapter.Engine.ID(id).AllCols().Update(prCheck)
76+
if err != nil {
77+
panic(err)
78+
}
79+
return true
80+
}
81+
82+
// IncrementFixAttempts increments the fix attempts counter and returns the updated record.
83+
func IncrementFixAttempts(org string, repo string, prNumber int, checkName string) *PrCheck {
84+
prCheck := GetPrCheck(org, repo, prNumber, checkName)
85+
if prCheck == nil {
86+
return nil
87+
}
88+
prCheck.FixAttempts++
89+
prCheck.LastAttemptAt = time.Now()
90+
UpdatePrCheck(prCheck.Id, prCheck)
91+
return prCheck
92+
}
93+
94+
// ShouldAttemptFix checks if we should attempt to fix this check (max attempts configurable).
95+
func ShouldAttemptFix(org string, repo string, prNumber int, checkName string) bool {
96+
prCheck := GetPrCheck(org, repo, prNumber, checkName)
97+
// First time, should attempt.
98+
if prCheck == nil {
99+
return true
100+
}
101+
return prCheck.FixAttempts < util.MaxFixAttempts && !prCheck.IsFixed
102+
}

routers/router.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ func initAPI() {
7979
beego.Router("/api/webhook", &controllers.ApiController{}, "Post:WebhookOpen")
8080
beego.Router("/api/is-mainland-ip", &controllers.ApiController{}, "GET:IsMainlandIp")
8181

82+
beego.Router("/api/get-pr-checks", &controllers.ApiController{}, "GET:GetPrChecks")
83+
beego.Router("/api/get-pr-check", &controllers.ApiController{}, "GET:GetPrCheck")
84+
8285
beego.Router("/api/get-machines", &controllers.ApiController{}, "GET:GetMachines")
8386
beego.Router("/api/get-machine", &controllers.ApiController{}, "GET:GetMachine")
8487
beego.Router("/api/update-machine", &controllers.ApiController{}, "POST:UpdateMachine")

0 commit comments

Comments
 (0)