Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update prompt WOOT-1 #28

Merged
merged 7 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 20 additions & 16 deletions src/__tests__/run.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { mockdata } from '../mockdata'
import { run } from '../index'
import * as github from '@actions/github'
import {
getJiraTicket,
getChanges,
SummariseChanges,
postSummary
} from '../steps'
import { getChanges, SummarizeChanges } from '../steps'
import * as core from '@actions/core'
import { JiraClient } from '../clients'

jest.mock('@actions/github')
jest.mock('./services')
jest.mock('@actions/core')

describe('run', () => {
const jira = jest.fn(() => {
return {
getJiraTicket: jest.fn(() => ['JIRA-123'])
}
})
it('should execute without errors', async () => {
const jiraIssues = ['JIRA-123']
const changes = ['Change 1', 'Change 2']
Expand All @@ -22,12 +23,6 @@ describe('run', () => {
const acsummaries = 'Summary'

const githubContext = mockdata
// getJiraTicket.mockResolvedValue(jiraIssues)
// getChanges.mockResolvedValue(changes)
// SummariseChanges.summarizeGitChanges.mockResolvedValue(gitSummary)
// SummariseChanges.summariseJiraTickets.mockResolvedValue(jiraSummary)
// SummariseChanges.checkedCodeReviewAgainstCriteria.mockResolvedValue(acsummaries)
// postComment.mockResolvedValue()

// await run()

Expand All @@ -37,11 +32,20 @@ describe('run', () => {
// body: githubContext.payload.pull_request.body
// })

// expect(getChanges).toHaveBeenCalledWith(githubContext.payload.pull_request.number)
// expect(getChanges).toHaveBeenCalledWith(
// githubContext.payload.pull_request.number
// )
// expect(SummariseChanges.summarizeGitChanges).toHaveBeenCalledWith(changes)
// expect(SummariseChanges.summariseJiraTickets).toHaveBeenCalledWith(jiraIssues)
// expect(SummariseChanges.checkedCodeReviewAgainstCriteria).toHaveBeenCalledWith(gitSummary, jiraSummary)
// expect(postComment).toHaveBeenCalledWith(githubContext.payload.pull_request.number, gitSummary)
// expect(SummariseChanges.summariseJiraTickets).toHaveBeenCalledWith(
// jiraIssues
// )
// expect(
// SummariseChanges.checkedCodeReviewAgainstCriteria
// ).toHaveBeenCalledWith(gitSummary, jiraSummary)
// expect(postComment).toHaveBeenCalledWith(
// githubContext.payload.pull_request.number,
// gitSummary
// )
})

it('should handle errors', async () => {
Expand Down
49 changes: 49 additions & 0 deletions src/ai.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {
prompt,
jiraPrompt,
acSummariesPrompt,
compareOldSummaryTemplate
} from './constants.js'
import * as core from '@actions/core'
import OpenAI from 'openai'
import { Logger } from './utils.js'

export class Ai {
constructor() {
const openAiKey = core.getInput('openAIKey') || process.env.OPENAI_API_KEY
if (!openAiKey) {
throw new Error('OpenAI key is required')
}
this.model = new OpenAI({
apiKey: openAiKey
})
}
configuration = {
model: 'gpt-3.5-turbo'
}
model: OpenAI
basePromptTemplate = prompt
jiraPromptTemplate = jiraPrompt
acSummariesPromptTemplate = acSummariesPrompt
compareOldSummaryTemplate(oldSummary: string, newSummary: string): string {
return compareOldSummaryTemplate(oldSummary, newSummary)
}
execute = async (prompt: string) => {
try {
const response = await this.model.chat.completions.create({
messages: [
{
role: 'user',
content: prompt
}
],
...this.configuration
})
Logger.log('ai response', { response })
return response.choices[0].message.content
} catch (e) {
Logger.error('error summarizing changes', e)
return null
}
}
}
48 changes: 48 additions & 0 deletions src/clients/github.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import * as core from '@actions/core'
import * as github from '@actions/github'
import { Logger } from '../utils'
import { Ai } from '../ai'
import { GitHub } from '@actions/github/lib/utils'

export class GithubClient {
octokit: InstanceType<typeof GitHub>
repo: { owner: string; repo: string }
constructor() {
const { octokit, repo } = this.getGithubContext()
this.octokit = octokit
this.repo = repo
}

getGithubContext = () => {
const githubToken =
core.getInput('gitHubToken') || process.env.GITHUB_ACCESS_TOKEN || ''
const octokit = github.getOctokit(githubToken)
const repo = github.context.repo
return { octokit, repo, githubToken }
}

async getComments(pullRequestNumber: number) {
try {
const response = await this.octokit.rest.issues.listComments({
...this.repo,
issue_number: pullRequestNumber
})
return response.data
} catch (error) {
Logger.error('error getting comments', JSON.stringify(error))
return []
}
}

async postComment(comment: string, pullRequestNumber: number) {
try {
await this.octokit.rest.pulls.update({
...this.repo,
pull_number: pullRequestNumber,
body: comment
})
} catch (error) {
Logger.error('error posting comment', JSON.stringify(error))
}
}
}
2 changes: 2 additions & 0 deletions src/clients/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './jira.js'
export * from './github.js'
55 changes: 55 additions & 0 deletions src/clients/jira.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { WebhookPayload } from '@actions/github/lib/interfaces'
import { Version2Client } from 'jira.js'
import { Issue } from 'jira.js/out/agile/models'
import * as core from '@actions/core'

import { Logger } from '../utils'

export class JiraClient {
client: Version2Client
constructor() {
this.client = this.initializeJiraClient()
}
initializeJiraClient = () => {
const host = core.getInput('jiraHost') || process.env.JIRA_HOST || ''
return new Version2Client({
host,
authentication: {
basic: {
email: core.getInput('jiraEmail') || process.env.JIRA_EMAIL || '',
apiToken:
core.getInput('jiraApiKey') || process.env.JIRA_API_KEY || ''
}
}
})
}
getJiraTicket = async ({
title,
branchName,
body
}: {
title?: string
branchName: string
body?: string
}): Promise<Issue[]> => {
const ticketRegex = /([A-Z]+-[0-9]+)/g
const allTickets = (`${body} ${branchName} ${title}` || '').match(
ticketRegex
)
if (!allTickets?.length) return []
const ticket = [...new Set(allTickets)]
const issues = await Promise.all(
ticket.map(async t => {
try {
const issue = await this.client.issues.getIssue({
issueIdOrKey: t
})
return issue.fields.description
} catch (e) {
Logger.error(`Error while fetching ${t} from JIRA`)
}
})
)
return issues.filter(e => e) as unknown as Issue[]
}
}
3 changes: 3 additions & 0 deletions src/prompts.ts → src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,6 @@ if the acceptance criteria has been met in the old summary but not in the new su
keep the boxes checked if the acceptance criteria has been met in both summaries
keep the information updated based on the new summary of the code changes
`

// using regex to match the file extensions to ignore
export const ignoredFiles = []
73 changes: 35 additions & 38 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,81 +1,78 @@
import * as core from '@actions/core'
import * as github from '@actions/github'
import {
SummariseChanges,
getChanges,
postSummary,
getJiraTicket,
Ai,
postComment
} from './steps'
import { SummarizeChanges, getChanges, CommentHandler } from './steps'
import dotenv from 'dotenv'
dotenv.config({ path: __dirname + '/.env' })
dotenv.config()

import { Logger } from './utils.js'
import { Logger, Templates } from './utils.js'
import { Ai } from './ai'
import { GithubClient, JiraClient } from './clients'
import { mockdata } from './mockdata'

// instantiate clients
const jiraClient = new JiraClient()
const githubClient = new GithubClient()
const commentsHandler = new CommentHandler(githubClient)
const ai = new Ai()

export async function run(): Promise<void> {
try {
// const githubContext = mockdata
const githubContext = github.context
const githubContext =
process.env.NODE_ENV === 'local' ? mockdata : github.context
const pullRequestNumber = githubContext.payload.pull_request?.number
if (!pullRequestNumber) {
Logger.warn('Could not get pull request number from context, exiting')
return
}
const jiraIssues = await getJiraTicket({

const jiraIssues = await jiraClient.getJiraTicket({
title: githubContext.payload.pull_request?.title,
branchName: githubContext.payload.pull_request?.head.ref,
body: `${githubContext.payload.pull_request?.body} ${githubContext.payload.pull_request?.head.ref}}`
})

if (!jiraIssues.length) {
Logger.warn('Could not get jira ticket, exiting')
await postComment(
`
**⚠️ Warning:**
No jira ticket found.
`,
await commentsHandler.postComment(
Templates.warning(
'No jira ticket found in this pull request, exiting.'
),
pullRequestNumber
)
return
}

const changes = await getChanges(pullRequestNumber)
if (!changes) {
Logger.warn('Could not get changes, exiting')
await postComment(
`
**⚠️ Warning:**
No git changes found in this pull request.
`,
await commentsHandler.postComment(
Templates.warning('No git changes found in this pull request.'),
pullRequestNumber
)

return
}

const ai = new Ai()
const gitSummary = await SummariseChanges.summarizeGitChanges(changes, ai)
const jiraSummary = await SummariseChanges.summariseJiraTickets(
jiraIssues,
ai
)
const [gitSummary, jiraSummary] = await Promise.all([
SummarizeChanges.summarizeGitChanges(changes, ai),
SummarizeChanges.summarizeJiraTickets(jiraIssues, ai)
])

if (!jiraSummary || !gitSummary) {
Logger.warn('Summary is empty, exiting')
await postComment(
`
**⚠️ Warning:**
No jira ticket found.
`,
Logger.warn('No jira ticket found or Summary is empty, exiting')
await commentsHandler.postComment(
Templates.warning('No matching jira ticket found.'),
pullRequestNumber
)
return
}
const acSummaries = await SummariseChanges.checkedCodeReviewAgainstCriteria(

const acSummaries = await SummarizeChanges.checkedCodeReviewAgainstCriteria(
gitSummary,
jiraSummary,
ai
)
await postSummary(pullRequestNumber, acSummaries ?? '', ai)

await commentsHandler.postSummary(pullRequestNumber, acSummaries ?? '', ai)
} catch (error) {
core.setFailed((error as Error)?.message as string)
}
Expand Down
31 changes: 31 additions & 0 deletions src/steps/comments-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as core from '@actions/core'
import * as github from '@actions/github'
import { Logger } from '../utils'
import { Ai } from '../ai'
import { GitHub } from '@actions/github/lib/utils'
import { GithubClient } from '../clients'

export class CommentHandler {
constructor(private readonly repoClient: GithubClient) {}
SIGNATURE = 'Added by woot! 🚂'
async postSummary(pullRequestNumber: number, summary: string, ai: Ai) {
Logger.log('posted comment', github.context)
const comments = await this.repoClient.getComments(pullRequestNumber)
const existingComment = comments.find(
comment => comment.body?.includes(this.SIGNATURE)
)
let comment = `${summary} \n ${this.SIGNATURE}`
if (existingComment?.body) {
Logger.log('found existing comment, updating')
comment = `${await ai.compareOldSummaryTemplate(
existingComment.body,
summary
)} \n ${this.SIGNATURE}`
}
await this.postComment(comment, pullRequestNumber)
}

postComment = async (comment: string, pullRequestNumber: number) => {
return this.repoClient.postComment(comment, pullRequestNumber)
}
}
1 change: 0 additions & 1 deletion src/steps/get-changes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as core from '@actions/core'
import * as github from '@actions/github'
import { Logger } from '../utils'
// import { mockdata } from '../mockdata'

export async function getChanges(
pullRequestNumber: number
Expand Down
3 changes: 1 addition & 2 deletions src/steps/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from './get-changes.js'
export * from './post-comment.js'
export * from './comments-handler.js'
export * from './summarize-changes.js'
export * from './jira.js'
Loading
Loading