Skip to content

feat: Github App For Keploy (Beta) #9

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

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
00bb3aa
feat: initialize keploy-github-app-beta with Docker support and essen…
SkySingh04 Feb 12, 2025
f8f5aa8
feat: add check for /keploy folder existence in pull request events
SkySingh04 Feb 12, 2025
2865040
feat: added Keploy Github App
SkySingh04 Feb 12, 2025
0bd2663
refactor: introduce type definitions and improve type safety across t…
SkySingh04 Mar 15, 2025
0d8de5c
refactor: enhance type definitions and improve function signatures in…
SkySingh04 Mar 15, 2025
0674256
refactor: enhance diff parsing and error handling in diffparser.ts
SkySingh04 Mar 15, 2025
8040006
refactor: enhance type safety and logging in various modules
SkySingh04 Mar 15, 2025
aec11b4
refactor: improve error handling and logging in workflow triggers and…
SkySingh04 Mar 15, 2025
4ac4230
feat: implement structured error handling and improve configuration l…
SkySingh04 Mar 15, 2025
161ebcd
refactor: optimize asynchronous operations in PR and workflow handling
SkySingh04 Mar 15, 2025
d0cd28f
feat: enhance linked issue handling in PR analysis
SkySingh04 Mar 15, 2025
817708e
fix: improve validation of response content in getRulesForLLM
SkySingh04 Mar 15, 2025
8bfd0a1
feat: enhance label determination logic in addLabel.ts
SkySingh04 Mar 15, 2025
518bc31
feat: add PDF export functionality for pull request summaries
SkySingh04 Mar 16, 2025
d1c86ed
feat: implement user feedback improvements in PR analysis
SkySingh04 Mar 16, 2025
31dbc3f
fix: update API endpoint in configuration
SkySingh04 Mar 16, 2025
de71c91
fix: clear selected model in configuration
SkySingh04 Mar 16, 2025
7628857
refactor: remove unnecessary comment from types.ts
SkySingh04 Mar 20, 2025
e8f9aec
refactor: remove unnecessary comment from pr.ts
SkySingh04 Mar 20, 2025
fd313b9
chore: add comment for core data collection functions in pr.ts
SkySingh04 Mar 20, 2025
27192ee
feat: add CI workflows for testing, linting, and security scanning
SkySingh04 Mar 20, 2025
ce2c164
refactor: update logging for LLM output in PR analysis
SkySingh04 Mar 20, 2025
76fc8ae
feat: add README for Keploy Github App
SkySingh04 Mar 20, 2025
b0d8d32
feat: update JavaScript linting configuration
SkySingh04 Mar 20, 2025
0a1126c
feat: add ESLint formatter for improved reporting
SkySingh04 Mar 20, 2025
0f3a9a0
feat: add whitespace for improved code formatting
SkySingh04 Mar 20, 2025
5f0f1ed
feat: enhance error logging across the application
SkySingh04 Mar 21, 2025
29f7c3a
feat: improve data handling and type safety in PR and types files
SkySingh04 Mar 21, 2025
6c0de73
feat: enhance type safety and structure in diffparser.ts
SkySingh04 Mar 21, 2025
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
20 changes: 20 additions & 0 deletions .github/workflows/keploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Run test-cases

on:
pull_request:
types: [opened, synchronize]

jobs:
my_job:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Keploy Report
uses: keploy/testgpt@main
with:
working-directory: ${{ github.workspace }}
delay: 10
command: node src/app.js
keploy-path: .
12 changes: 12 additions & 0 deletions keploy-github-app-beta/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
node_modules
npm-debug.log
*.pem
!mock-cert.pem
.env
coverage
lib
scripts/*
/dist

myenv/

114 changes: 114 additions & 0 deletions keploy-github-app-beta/diffparser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// diffParser.ts
import parseDiff from 'parse-diff';


async function parseGitDiffFromLLMOutput(llmOutput: any) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add type safety and null checks to avoid runtime errors instead of using any

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! Adding those all across the codebase!

const diffStart = llmOutput.indexOf('```diff');
const diffEnd = llmOutput.indexOf('```', diffStart + 1);
return llmOutput.substring(diffStart, diffEnd);
}


export async function reviewPR(context: any, app: any, llmOutput: any) {
// export async function reviewPR(context: any, app: any) {
//trim the llmOutput to only include the diff
const ifLGTM = llmOutput.includes('LGTM');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use regex for exact match to avoid false positive

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The relevant Regex has been added!

if (ifLGTM) {
await context.octokit.issues.createComment({
...context.repo(),
issue_number: context.payload.pull_request.number,
body: 'LGTM: LLM analysis is successful'
});
return;
}
const gitDiff = await parseGitDiffFromLLMOutput(llmOutput);
// const gitDiff = `diff --git a/src/index.js b/src/index.js
// index abc1234..def5678 100644
// --- a/src/index.js
// +++ b/src/index.js
// @@ -1,5 +1,5 @@
// function add(a, b) {
// - return a - b; // Bug: Subtraction instead of addition
// + return a + b; // Fixed: Now correctly adds
// }

// function subtract(a, b) {
// @@ -10,7 +10,7 @@ function subtract(a, b) {
// function multiply(a, b) {
// return a * b;
// }

// -function divide(a, b) {
// - return a / b;
// +function divide(a, b) {
// + return b !== 0 ? a / b : NaN; // Added check for division by zero
// }
// diff --git a/tests/test.js b/tests/test.js
// index 1234567..890abcd 100644
// --- a/tests/test.js
// +++ b/tests/test.js
// @@ -5,6 +5,6 @@ describe('Math operations', () => {
// expect(add(2, 3)).toBe(5);
// });

// - test('subtract should return the difference', () => {
// + test('subtract should return the correct difference', () => {
// expect(subtract(5, 3)).toBe(2);
// });
// });
// `;
// Create inline comments from the diff
await createInlineCommentsFromDiff(gitDiff, context, app);

// Post the LLM analysis as a comment
await context.octokit.issues.createComment({
...context.repo(),
issue_number: context.payload.pull_request.number,
body: llmOutput,
});
}


export async function createInlineCommentsFromDiff(diff: string, context: any, app: any) {
const parsedFiles = parseDiff(diff);
const { pull_request, repository } = context.payload;

for (const file of parsedFiles) {
if (file.to === '/dev/null') {
app.log.info(`Skipping deleted file: ${file.from}`);
continue;
}

const filePath = file.to || file.from;

for (const chunk of file.chunks) {
for (const change of chunk.changes) {
if (change.type !== 'add') continue; // Focus on additions for comments

const line = change.ln; // Line number in the new file
const content = change.content.slice(1).trim();
const body = `Suggested change:\n\`\`\`suggestion\n${content}\n\`\`\``;

try {
await context.octokit.pulls.createReviewComment({
owner: repository.owner.login,
repo: repository.name,
pull_number: pull_request.number,
commit_id: pull_request.head.sha,
path: filePath,
body,
line,
mediaType: {
previews: ['comfort-fade'], // Enable comfort-fade preview
},
});
app.log.info(`Created comment on ${filePath} line ${line}`);
} catch (error: any) {
app.log.error(
`Failed to create comment for ${filePath} line ${line}: ${error.message}`
);
}
}
}
}
}
55 changes: 55 additions & 0 deletions keploy-github-app-beta/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Deployments API example
// See: https://developer.github.com/v3/repos/deployments/ to learn more

/**
* This is the main entrypoint to your Probot app
* @param {import('probot').Probot} app
*/
import { getAllPrDetails } from "./pr.js";
import { handlePrAnalysis } from "./llm.js";
import { handleKeployWorkflowTrigger } from "./keploy.js";
import { handleError } from "./utils.js";
import { handleSecurityWorkflowTrigger } from "./security.js";
import { promptUserConfig } from './src/cli.js';
import { reviewPR } from './diffparser.js';
import { handleLintWorkflowTrigger } from "./lint.js";

let config: any;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please avoid using any

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Their existence has been perished!


export default async (app: {
log: { info: (arg0: string, arg1?: string) => void };
on: (arg0: string[], arg1: (context: any) => Promise<void>) => void;
}) => {
try {
// Get user configuration through CLI
config = await promptUserConfig();
// selectedModel = config.model;
app.log.info(`Initialized with API url: ${config.apiEndpoint} for use case: ${config.useCase} and model : ${config.selectedModel}`);
} catch (error) {
app.log.info("Failed to get user configuration");
process.exit(1);
}

app.log.info("Yay, the app was loaded!");

const handlePrEvent = async (context: any) => {
try {
const prData = await getAllPrDetails(context, app);
app.log.info(JSON.stringify(prData), "Full PR data collected");

const llmOutput = await handlePrAnalysis(context, prData , config.apiEndpoint , config.selectedModel, app);
// const stringllmOutput = await JSON.stringify(llmOutput);
// app.log.info(JSON.stringify(stringllmOutput), "LLM analysis complete");
await reviewPR(context, app, llmOutput);
// await reviewPR(context, app);

await handleKeployWorkflowTrigger(context);
await handleSecurityWorkflowTrigger(context);
await handleLintWorkflowTrigger(context);
} catch (error) {
await handleError(context, app, error);
}
};

app.on(["pull_request.opened", "pull_request.synchronize"], handlePrEvent);
};
21 changes: 21 additions & 0 deletions keploy-github-app-beta/keploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export async function handleKeployWorkflowTrigger(context: { repo: () => { owner: any; repo: any; }; payload: { pull_request: { head: { ref: any; }; number: any; }; }; octokit: { repos: { getContent: (arg0: { owner: any; repo: any; path: string; ref: any; }) => any; }; actions: { createWorkflowDispatch: (arg0: { owner: any; repo: any; workflow_id: string; ref: any; }) => any; }; issues: { createComment: (arg0: any) => any; }; }; }) {
const { owner, repo } = context.repo();
const { ref } = context.payload.pull_request.head;

try {
await context.octokit.actions.createWorkflowDispatch({
owner, repo, workflow_id: 'keploy.yaml', ref
});
} catch (error : any) {
if (error.status === 404) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 404 error handling is good, but catching only 404s and ignoring other errors can hide issues. Any specific reason for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a debugging decision, let me refactor the error handling

await context.octokit.issues.createComment({
...context.repo(),
issue_number: context.payload.pull_request.number,
body: '⚠️ Failed to run Keploy Tests'
});
return;
}
throw error;
}
}

48 changes: 48 additions & 0 deletions keploy-github-app-beta/keploy/test-set-0/tests/test-1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Generated by Keploy (2.4.5)
version: api.keploy.io/v1beta1
kind: Http
name: test-1
spec:
metadata: {}
req:
method: POST
proto_major: 1
proto_minor: 1
url: http://localhost:8080/url
header:
Accept: '*/*'
Content-Length: "33"
Content-Type: application/json
Host: localhost:8080
User-Agent: curl/7.81.0
body: |-
{
"url": "https://google.com"
}
timestamp: 2025-02-22T23:12:25.837014092Z
resp:
status_code: 500
header:
Content-Length: "929"
Content-Type: application/json; charset=utf-8
Date: Sat, 22 Feb 2025 23:12:50 GMT
body: '{"error":{"Desc":{"Servers":[{"Addr":"localhost:27017","Arbiters":null,"AverageRTT":0,"AverageRTTSet":false,"Compression":null,"CanonicalAddr":"","ElectionID":"000000000000000000000000","HeartbeatInterval":0,"HelloOK":false,"Hosts":null,"LastError":{"ConnectionID":"","Wrapped":{"Code":0,"Message":"connection(localhost:27017[-63]) socket was unexpectedly closed: EOF","Labels":["NetworkError"],"Name":"","Wrapped":{"ConnectionID":"localhost:27017[-63]","Wrapped":{}},"TopologyVersion":null}},"LastUpdateTime":"0001-01-01T00:00:00Z","LastWriteTime":"0001-01-01T00:00:00Z","MaxBatchCount":0,"MaxDocumentSize":0,"MaxMessageSize":0,"Members":null,"Passives":null,"Passive":false,"Primary":"","ReadOnly":false,"ServiceID":null,"SessionTimeoutMinutes":0,"SetName":"","SetVersion":0,"Tags":null,"TopologyVersion":null,"Kind":0,"WireVersion":null}],"SetName":"","Kind":0,"SessionTimeoutMinutes":0,"CompatibilityErr":null},"Wrapped":{}}}'
status_message: Internal Server Error
proto_major: 0
proto_minor: 0
timestamp: 2025-02-22T23:12:52.683242659Z
objects: []
assertions:
noise:
body.error.Desc.Servers.LastUpdateTime: []
body.error.Desc.Servers.LastWriteTime: []
header.Date: []
created: 1740265972
curl: |-
curl --request POST \
--url http://localhost:8080/url \
--header 'Host: localhost:8080' \
--header 'User-Agent: curl/7.81.0' \
--header 'Accept: */*' \
--header 'Content-Type: application/json' \
--data "{\n \"url\": \"https://google.com\"\n}"
21 changes: 21 additions & 0 deletions keploy-github-app-beta/lint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export async function handleLintWorkflowTrigger(context: { repo: () => { owner: any; repo: any; }; payload: { pull_request: { head: { ref: any; }; number: any; }; }; octokit: { repos: { getContent: (arg0: { owner: any; repo: any; path: string; ref: any; }) => any; }; actions: { createWorkflowDispatch: (arg0: { owner: any; repo: any; workflow_id: string; ref: any; }) => any; }; issues: { createComment: (arg0: any) => any; }; }; }) {
const { owner, repo } = context.repo();
const { ref } = context.payload.pull_request.head;

try {
await context.octokit.actions.createWorkflowDispatch({
owner, repo, workflow_id: 'lint.yaml', ref
});
} catch (error : any) {
if (error.status === 404) {
await context.octokit.issues.createComment({
...context.repo(),
issue_number: context.payload.pull_request.number,
body: 'Lint workflow failed'
});
return;
}
throw error;
}
}

Loading