Skip to content

Commit

Permalink
Merge pull request #28 from bemijonathan/feat/update-prompt
Browse files Browse the repository at this point in the history
update prompt WOOT-1
  • Loading branch information
bemijonathan authored Apr 29, 2024
2 parents 265016d + f594358 commit 65a1e98
Show file tree
Hide file tree
Showing 15 changed files with 271 additions and 255 deletions.
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

0 comments on commit 65a1e98

Please sign in to comment.