Skip to content

Commit bf8d347

Browse files
committed
added complete once if possible, added jira integration
1 parent c487865 commit bf8d347

File tree

3 files changed

+165
-21
lines changed

3 files changed

+165
-21
lines changed

cmd/description/main.go

+81-21
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/sashabaranov/go-openai"
1313

1414
ghClient "github.com/ravilushqa/gpt-pullrequest-updater/github"
15+
"github.com/ravilushqa/gpt-pullrequest-updater/jira"
1516
oAIClient "github.com/ravilushqa/gpt-pullrequest-updater/openai"
1617
)
1718

@@ -22,6 +23,7 @@ var opts struct {
2223
Repo string `long:"repo" env:"REPO" description:"GitHub repo" required:"true"`
2324
PRNumber int `long:"pr-number" env:"PR_NUMBER" description:"Pull request number" required:"true"`
2425
Test bool `long:"test" env:"TEST" description:"Test mode"`
26+
JiraURL string `long:"jira-url" env:"JIRA_URL" description:"Jira URL"`
2527
}
2628

2729
func main() {
@@ -35,6 +37,10 @@ func main() {
3537
os.Exit(0)
3638
}
3739

40+
if opts.Test {
41+
fmt.Println("Test mode")
42+
}
43+
3844
if err := run(ctx); err != nil {
3945
panic(err)
4046
}
@@ -54,50 +60,104 @@ func run(ctx context.Context) error {
5460
return fmt.Errorf("error getting commits: %w", err)
5561
}
5662

57-
var OverallDescribeCompletion string
58-
OverallDescribeCompletion += fmt.Sprintf("Pull request title: %s, body: %s\n\n", pr.GetTitle(), pr.GetBody())
63+
var sumDiffs int
64+
for _, file := range diff.Files {
65+
sumDiffs += len(*file.Patch)
66+
}
67+
68+
var completion string
69+
if sumDiffs < 4000 {
70+
completion, err = genCompletionOnce(ctx, openAIClient, diff)
71+
if err != nil {
72+
return fmt.Errorf("error generating completition once: %w", err)
73+
}
74+
} else {
75+
completion, err = genCompletionPerFile(ctx, openAIClient, diff, pr)
76+
if err != nil {
77+
return fmt.Errorf("error generating completition twice: %w", err)
78+
}
79+
}
80+
81+
if opts.JiraURL != "" {
82+
fmt.Println("Adding Jira ticket")
83+
id, err := jira.ExtractJiraTicketID(*pr.Title)
84+
if err != nil {
85+
return err
86+
}
87+
completion = fmt.Sprintf("### JIRA ticket: [%s](%s) \n\n%s", id, jira.GenerateJiraTicketURL(opts.JiraURL, id), completion)
88+
}
89+
90+
if opts.Test {
91+
fmt.Println(completion)
92+
return nil
93+
}
94+
95+
// Update the pull request description
96+
fmt.Println("Updating pull request")
97+
updatePr := &github.PullRequest{Body: github.String(completion)}
98+
if _, err = githubClient.UpdatePullRequest(ctx, opts.Owner, opts.Repo, opts.PRNumber, updatePr); err != nil {
99+
return fmt.Errorf("error updating pull request: %w", err)
100+
}
101+
102+
return nil
103+
}
104+
105+
func genCompletionOnce(ctx context.Context, client *oAIClient.Client, diff *github.CommitsComparison) (string, error) {
106+
fmt.Println("Generating completion once")
107+
messages := make([]openai.ChatCompletionMessage, 0, len(diff.Files))
108+
messages = append(messages, openai.ChatCompletionMessage{
109+
Role: openai.ChatMessageRoleUser,
110+
Content: oAIClient.PromptDescribeChanges,
111+
})
59112
for _, file := range diff.Files {
113+
messages = append(messages, openai.ChatCompletionMessage{
114+
Role: openai.ChatMessageRoleUser,
115+
Content: *file.Patch,
116+
})
117+
}
118+
119+
fmt.Println("Sending prompt to OpenAI")
120+
completion, err := client.ChatCompletion(ctx, messages)
121+
if err != nil {
122+
return "", fmt.Errorf("error completing prompt: %w", err)
123+
}
124+
125+
return completion, nil
126+
}
127+
128+
func genCompletionPerFile(ctx context.Context, client *oAIClient.Client, diff *github.CommitsComparison, pr *github.PullRequest) (string, error) {
129+
fmt.Println("Generating completion per file")
130+
OverallDescribeCompletion := fmt.Sprintf("Pull request title: %s, body: %s\n\n", pr.GetTitle(), pr.GetBody())
131+
132+
for i, file := range diff.Files {
60133
prompt := fmt.Sprintf(oAIClient.PromptDescribeChanges, *file.Patch)
61134

62135
if len(prompt) > 4096 {
63136
prompt = fmt.Sprintf("%s...", prompt[:4093])
64137
}
65138

66-
completion, err := openAIClient.ChatCompletion(ctx, []openai.ChatCompletionMessage{
139+
fmt.Printf("Sending prompt to OpenAI for file %d/%d\n", i+1, len(diff.Files))
140+
completion, err := client.ChatCompletion(ctx, []openai.ChatCompletionMessage{
67141
{
68142
Role: openai.ChatMessageRoleUser,
69143
Content: prompt,
70144
},
71145
})
72146
if err != nil {
73-
return fmt.Errorf("error getting review: %w", err)
147+
return "", fmt.Errorf("error getting review: %w", err)
74148
}
75149
OverallDescribeCompletion += fmt.Sprintf("File: %s \nDescription: %s \n\n", file.GetFilename(), completion)
76150
}
77151

78-
overallCompletion, err := openAIClient.ChatCompletion(ctx, []openai.ChatCompletionMessage{
152+
overallCompletion, err := client.ChatCompletion(ctx, []openai.ChatCompletionMessage{
79153
{
80154
Role: openai.ChatMessageRoleUser,
81155
Content: fmt.Sprintf(oAIClient.PromptOverallDescribe, OverallDescribeCompletion),
82156
},
83157
})
84158
if err != nil {
85-
return fmt.Errorf("error getting overall review: %w", err)
86-
}
87-
88-
if opts.Test {
89-
fmt.Println(OverallDescribeCompletion)
90-
fmt.Println("=====================================")
91-
fmt.Println(overallCompletion)
92-
93-
return nil
159+
return "", fmt.Errorf("error getting overall review: %w", err)
94160
}
95161

96-
// Update the pull request description
97-
updatePr := &github.PullRequest{Body: github.String(overallCompletion)}
98-
if _, err = githubClient.UpdatePullRequest(ctx, opts.Owner, opts.Repo, opts.PRNumber, updatePr); err != nil {
99-
return fmt.Errorf("error updating pull request: %w", err)
100-
}
101-
102-
return nil
162+
return overallCompletion, nil
103163
}

jira/jira.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package jira
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
)
7+
8+
const ticketUrlFormat = "%s/browse/%s"
9+
10+
// ExtractJiraTicketID returns the first JIRA ticket ID found in the input string.
11+
func ExtractJiraTicketID(s string) (string, error) {
12+
// This regular expression pattern matches a JIRA ticket ID (e.g. PROJ-123).
13+
pattern := `([aA-zZ]+-\d+)`
14+
re, err := regexp.Compile(pattern)
15+
if err != nil {
16+
return "", fmt.Errorf("error compiling regex: %w", err)
17+
}
18+
19+
matches := re.FindStringSubmatch(s)
20+
if len(matches) == 0 {
21+
return "", fmt.Errorf("no JIRA ticket ID found in the input string")
22+
}
23+
24+
return matches[0], nil
25+
}
26+
27+
func GenerateJiraTicketURL(jiraURL, ticketID string) string {
28+
return fmt.Sprintf(ticketUrlFormat, jiraURL, ticketID)
29+
}

jira/jira_test.go

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package jira
2+
3+
import "testing"
4+
5+
func TestExtractJiraTicketID(t *testing.T) {
6+
testCases := []struct {
7+
name string
8+
input string
9+
expected string
10+
expectError bool
11+
}{
12+
{
13+
name: "Valid ticket ID",
14+
input: "This is a sample text with a JIRA ticket ID: PROJ-123, let's extract it.",
15+
expected: "PROJ-123",
16+
expectError: false,
17+
},
18+
{
19+
name: "No ticket ID",
20+
input: "This is a sample text without a JIRA ticket ID.",
21+
expectError: true,
22+
},
23+
{
24+
name: "Multiple ticket IDs",
25+
input: "This text has multiple JIRA ticket IDs: PROJ-123, TASK-456, and BUG-789.",
26+
expected: "PROJ-123",
27+
expectError: false,
28+
},
29+
{
30+
name: "Valid ticket ID. Lowercase.",
31+
input: "This is an invalid JIRA ticket ID: Proj-123.",
32+
expected: "Proj-123",
33+
expectError: false,
34+
},
35+
}
36+
37+
for _, tc := range testCases {
38+
t.Run(tc.name, func(t *testing.T) {
39+
result, err := ExtractJiraTicketID(tc.input)
40+
if tc.expectError {
41+
if err == nil {
42+
t.Errorf("expected an error, but got none")
43+
}
44+
} else {
45+
if err != nil {
46+
t.Errorf("unexpected error: %v", err)
47+
}
48+
49+
if result != tc.expected {
50+
t.Errorf("expected result '%s', but got '%s'", tc.expected, result)
51+
}
52+
}
53+
})
54+
}
55+
}

0 commit comments

Comments
 (0)