mirror of
https://github.com/codeflash-ai/codeflash-internal.git
synced 2026-05-04 18:25:18 +00:00
## Summary - **Fix CI build failure**: Auth0Client crashes during Next.js prerendering when env vars aren't set. Returns a no-op stub (`getSession → null`) when domain is missing — semantically correct for static generation - **Lazy-load markdown libs (~260kb)**: ReactMarkdown, remarkGfm, and react-syntax-highlighter were eagerly imported in monaco-diff-viewer but only rendered when user expands "Generated Tests". Extracted into a dynamic component - **Parallelize repo detail query**: `getRepositoryById` ran the activity count sequentially after the repo lookup. Since `repoId` is already available, all three queries now run in parallel ## Test plan - [ ] CI `build` check passes (was failing since #2598) - [ ] Trace page still renders generated tests correctly when expanded - [ ] Repository detail page loads correctly with activity status
498 lines
14 KiB
TypeScript
498 lines
14 KiB
TypeScript
import type { FileDiffContent } from "@codeflash-ai/code-suggester/build/src/types.js"
|
|
import type { Octokit } from "octokit"
|
|
import { addLabelToPullRequest } from "./github-utils.js"
|
|
import {
|
|
buildBenchmarkInfo,
|
|
buildDependentPrTitle,
|
|
buildPrTitle,
|
|
buildResultFooter,
|
|
generateOptimizationReviewTemplate,
|
|
originalPRComment,
|
|
buildResultHeader,
|
|
buildResultDetails,
|
|
buildResultTestReport,
|
|
} from "./pr-changes-utils.js"
|
|
import type { RestEndpointMethodTypes } from "@octokit/rest"
|
|
import { AnyOctokit, PullRequestCreationResponse } from "../types.js"
|
|
import * as Sentry from "@sentry/node"
|
|
|
|
type GitCreateTreeParamsTree =
|
|
RestEndpointMethodTypes["git"]["createTree"]["parameters"]["tree"][number]
|
|
|
|
interface BenchmarkDetail {
|
|
benchmark_name: string
|
|
test_function: string
|
|
original_timing: number
|
|
expected_new_timing: number
|
|
speedup_percent: number
|
|
}
|
|
|
|
export interface PrCommentFields {
|
|
best_runtime: string
|
|
original_runtime: string
|
|
function_name: string
|
|
file_path: string
|
|
speedup_x: string
|
|
speedup_pct: string
|
|
loop_count: string
|
|
optimization_explanation: string
|
|
report_table: any
|
|
benchmark_details?: BenchmarkDetail[]
|
|
original_async_throughput?: string
|
|
best_async_throughput?: string
|
|
}
|
|
|
|
// Dependencies interface for easier testing
|
|
export interface CreatePrFromDiffContentsDependencies {
|
|
addLabelToPullRequest: typeof addLabelToPullRequest
|
|
buildBenchmarkInfo: typeof buildBenchmarkInfo
|
|
buildDependentPrTitle: typeof buildDependentPrTitle
|
|
buildPrTitle: typeof buildPrTitle
|
|
buildResultFooter: typeof buildResultFooter
|
|
buildResultHeader: typeof buildResultHeader
|
|
buildResultDetails: typeof buildResultDetails
|
|
buildResultTestReport: typeof buildResultTestReport
|
|
originalPRComment: typeof originalPRComment
|
|
}
|
|
|
|
// Default dependencies
|
|
let dependencies: CreatePrFromDiffContentsDependencies = {
|
|
addLabelToPullRequest,
|
|
buildBenchmarkInfo,
|
|
buildDependentPrTitle,
|
|
buildPrTitle,
|
|
buildResultFooter,
|
|
buildResultHeader,
|
|
buildResultDetails,
|
|
buildResultTestReport,
|
|
originalPRComment,
|
|
}
|
|
|
|
// For testing - allow dependency injection
|
|
export function setCreatePrFromDiffContentsDependencies(
|
|
deps: Partial<CreatePrFromDiffContentsDependencies>,
|
|
) {
|
|
dependencies = { ...dependencies, ...deps }
|
|
}
|
|
|
|
export function resetCreatePrFromDiffContentsDependencies() {
|
|
dependencies = {
|
|
addLabelToPullRequest,
|
|
buildBenchmarkInfo,
|
|
buildDependentPrTitle,
|
|
buildPrTitle,
|
|
buildResultFooter,
|
|
buildResultHeader,
|
|
buildResultDetails,
|
|
buildResultTestReport,
|
|
originalPRComment,
|
|
}
|
|
}
|
|
|
|
export async function createNewBranchFromDiffContents(
|
|
installationOctokit: Octokit,
|
|
owner: string,
|
|
repo: string,
|
|
newBranchName: string,
|
|
baseBranch: string,
|
|
diffContentsMap: Map<string, FileDiffContent>,
|
|
commitMessage: string,
|
|
): Promise<boolean> {
|
|
try {
|
|
// Create a new branch off of the specified base branch
|
|
await installationOctokit.rest.git.createRef({
|
|
owner,
|
|
repo,
|
|
ref: `refs/heads/${newBranchName}`,
|
|
sha: (
|
|
await installationOctokit.rest.git.getRef({
|
|
owner,
|
|
repo,
|
|
ref: `heads/${baseBranch}`,
|
|
})
|
|
).data.object.sha,
|
|
})
|
|
|
|
const treeItems: GitCreateTreeParamsTree[] = []
|
|
for (const [filePath, fileDiffContent] of diffContentsMap) {
|
|
try {
|
|
await installationOctokit.rest.repos.getContent({
|
|
owner,
|
|
repo,
|
|
path: filePath,
|
|
ref: baseBranch,
|
|
})
|
|
} catch (err: any) {
|
|
if (err.status === 404) {
|
|
const error = new Error(
|
|
`File does not exist in base branch: ${filePath} in ${owner}/${repo}`,
|
|
)
|
|
Sentry.captureException(error)
|
|
throw error
|
|
}
|
|
throw err
|
|
}
|
|
|
|
// Create blobs for the changed files with newContent
|
|
const blobData = await installationOctokit.rest.git.createBlob({
|
|
owner,
|
|
repo,
|
|
content: fileDiffContent.newContent,
|
|
encoding: "utf-8",
|
|
})
|
|
|
|
treeItems.push({
|
|
path: filePath,
|
|
mode: "100644", // blob
|
|
type: "blob",
|
|
sha: blobData.data.sha,
|
|
})
|
|
}
|
|
|
|
// Get the current commit SHA of the new branch
|
|
const refData = await installationOctokit.rest.git.getRef({
|
|
owner,
|
|
repo,
|
|
ref: `heads/${newBranchName}`,
|
|
})
|
|
const currentCommitSha = refData.data.object.sha
|
|
|
|
// Get the tree SHA of the current commit
|
|
const commitData = await installationOctokit.rest.git.getCommit({
|
|
owner,
|
|
repo,
|
|
commit_sha: currentCommitSha,
|
|
})
|
|
const currentTreeSha = commitData.data.tree.sha
|
|
|
|
// Create a new tree with the changes
|
|
const newTreeData = await installationOctokit.rest.git.createTree({
|
|
owner,
|
|
repo,
|
|
base_tree: currentTreeSha,
|
|
tree: treeItems,
|
|
})
|
|
|
|
// Create a new commit with the new tree
|
|
const newCommitData = await installationOctokit.rest.git.createCommit({
|
|
owner,
|
|
repo,
|
|
message: commitMessage,
|
|
tree: newTreeData.data.sha,
|
|
parents: [currentCommitSha],
|
|
})
|
|
|
|
// Update the new branch reference to point to the new commit
|
|
const result = await installationOctokit.rest.git.updateRef({
|
|
owner,
|
|
repo,
|
|
ref: `heads/${newBranchName}`,
|
|
sha: newCommitData.data.sha,
|
|
})
|
|
|
|
return result.status === 200
|
|
} catch (error) {
|
|
console.error("Error creating branch from diff contents:", error)
|
|
Sentry.captureException(`Failed to create branch: ${error.message}`, {
|
|
extra: { owner, repo, newBranchName, baseBranch, commitMessage, diffContentsMap },
|
|
})
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a branch with a single new file using Git Tree API
|
|
* This supports creating new files (unlike createNewBranchFromDiffContents which requires files to exist)
|
|
*/
|
|
export async function createBranchWithNewFile(
|
|
installationOctokit: Octokit,
|
|
owner: string,
|
|
repo: string,
|
|
newBranchName: string,
|
|
baseBranch: string,
|
|
filePath: string,
|
|
fileContent: string,
|
|
commitMessage: string,
|
|
): Promise<boolean> {
|
|
try {
|
|
// Step 1: Get base branch SHA
|
|
const baseRef = await installationOctokit.rest.git.getRef({
|
|
owner,
|
|
repo,
|
|
ref: `heads/${baseBranch}`,
|
|
})
|
|
const baseSha = baseRef.data.object.sha
|
|
|
|
// Step 2: Create new branch
|
|
await installationOctokit.rest.git.createRef({
|
|
owner,
|
|
repo,
|
|
ref: `refs/heads/${newBranchName}`,
|
|
sha: baseSha,
|
|
})
|
|
|
|
// Step 3: Create blob for the file content
|
|
const blobData = await installationOctokit.rest.git.createBlob({
|
|
owner,
|
|
repo,
|
|
content: fileContent,
|
|
encoding: "utf-8",
|
|
})
|
|
|
|
// Step 4: Get current commit and tree from the branch
|
|
const refData = await installationOctokit.rest.git.getRef({
|
|
owner,
|
|
repo,
|
|
ref: `heads/${newBranchName}`,
|
|
})
|
|
const currentCommitSha = refData.data.object.sha
|
|
|
|
const commitData = await installationOctokit.rest.git.getCommit({
|
|
owner,
|
|
repo,
|
|
commit_sha: currentCommitSha,
|
|
})
|
|
const currentTreeSha = commitData.data.tree.sha
|
|
|
|
// Step 5: Create new tree with the file
|
|
// Using base_tree automatically preserves all existing files - we just add/update our file
|
|
const newTreeData = await installationOctokit.rest.git.createTree({
|
|
owner,
|
|
repo,
|
|
base_tree: currentTreeSha,
|
|
tree: [
|
|
{
|
|
path: filePath,
|
|
mode: "100644", // Regular file
|
|
type: "blob",
|
|
sha: blobData.data.sha,
|
|
},
|
|
],
|
|
})
|
|
|
|
// Step 6: Create new commit
|
|
const newCommitData = await installationOctokit.rest.git.createCommit({
|
|
owner,
|
|
repo,
|
|
message: commitMessage,
|
|
tree: newTreeData.data.sha,
|
|
parents: [currentCommitSha],
|
|
})
|
|
|
|
// Step 7: Update branch to point to new commit
|
|
const result = await installationOctokit.rest.git.updateRef({
|
|
owner,
|
|
repo,
|
|
ref: `heads/${newBranchName}`,
|
|
sha: newCommitData.data.sha,
|
|
})
|
|
|
|
return result.status === 200
|
|
} catch (error) {
|
|
console.error("Error creating branch with new file:", error)
|
|
Sentry.captureException(error)
|
|
return false
|
|
}
|
|
}
|
|
|
|
export async function createNewPullRequest(
|
|
installationOctokit: AnyOctokit,
|
|
owner: string,
|
|
repo: string,
|
|
title: string,
|
|
body: string,
|
|
newBranchName: string,
|
|
baseBranch: string,
|
|
): Promise<PullRequestCreationResponse> {
|
|
return await installationOctokit.rest.pulls.create({
|
|
owner,
|
|
repo,
|
|
title,
|
|
head: newBranchName,
|
|
base: baseBranch,
|
|
body,
|
|
})
|
|
}
|
|
|
|
export async function assignReviewer(
|
|
installationOctokit: Octokit,
|
|
owner: string,
|
|
repo: string,
|
|
pullNumber: number,
|
|
reviewer: string,
|
|
): Promise<void> {
|
|
await installationOctokit.rest.pulls.requestReviewers({
|
|
owner,
|
|
repo,
|
|
pull_number: pullNumber,
|
|
reviewers: [reviewer],
|
|
})
|
|
}
|
|
|
|
export async function createStandalonePullRequest(
|
|
installationOctokit: AnyOctokit,
|
|
owner: string,
|
|
repo: string,
|
|
newBranchName: string,
|
|
baseBranch: string,
|
|
prCommentFields: PrCommentFields,
|
|
existingTests: string,
|
|
generatedTests: string,
|
|
coverage_message: string,
|
|
replayTests: string = "",
|
|
concolicTests: string = "",
|
|
): Promise<PullRequestCreationResponse> {
|
|
// Open a new standalone Codeflash PR (that doesn't reference an original PR, likely just to main)
|
|
const prCommentHeader = dependencies.buildResultHeader(prCommentFields)
|
|
// Build benchmark info if available
|
|
const benchmarkInfo =
|
|
prCommentFields.benchmark_details && prCommentFields.benchmark_details.length > 0
|
|
? dependencies.buildBenchmarkInfo(prCommentFields)
|
|
: ""
|
|
// Open a new PR from the new branch onto the original PR's head branch
|
|
const prCommentBody = dependencies.buildResultDetails(prCommentFields)
|
|
const prCommentTestReport = dependencies.buildResultTestReport(
|
|
prCommentFields,
|
|
existingTests,
|
|
generatedTests,
|
|
coverage_message,
|
|
replayTests,
|
|
concolicTests,
|
|
)
|
|
const prCommentFooter = dependencies.buildResultFooter(newBranchName)
|
|
const title: string = dependencies.buildPrTitle(
|
|
prCommentFields.function_name,
|
|
prCommentFields.speedup_pct,
|
|
prCommentFields.speedup_x,
|
|
)
|
|
const body: string = benchmarkInfo
|
|
? `${prCommentHeader}\n${benchmarkInfo}\n${prCommentBody}\n${prCommentTestReport}\n${prCommentFooter}`
|
|
: `${prCommentHeader}\n${prCommentBody}\n${prCommentTestReport}\n${prCommentFooter}`
|
|
|
|
return await createNewPullRequest(
|
|
installationOctokit,
|
|
owner,
|
|
repo,
|
|
title,
|
|
body,
|
|
newBranchName,
|
|
baseBranch,
|
|
)
|
|
}
|
|
|
|
export async function createDependentPullRequest(
|
|
installationOctokit: AnyOctokit,
|
|
owner: string,
|
|
repo: string,
|
|
origPrNumber: number,
|
|
newBranchName: string,
|
|
baseBranch: string,
|
|
prCommentFields: PrCommentFields,
|
|
existingTests: string,
|
|
generatedTests: string,
|
|
coverage_message: string,
|
|
replayTests: string = "",
|
|
concolicTests: string = "",
|
|
optimizationReview: string = "",
|
|
): Promise<PullRequestCreationResponse> {
|
|
const { title, body } = createDependentPRTitleAndBody(
|
|
prCommentFields,
|
|
existingTests,
|
|
generatedTests,
|
|
coverage_message,
|
|
origPrNumber,
|
|
newBranchName,
|
|
baseBranch,
|
|
replayTests,
|
|
concolicTests,
|
|
optimizationReview,
|
|
)
|
|
|
|
const newPrData = await createNewPullRequest(
|
|
installationOctokit,
|
|
owner,
|
|
repo,
|
|
title,
|
|
body,
|
|
newBranchName,
|
|
baseBranch,
|
|
)
|
|
|
|
await dependencies.addLabelToPullRequest(installationOctokit, owner, repo, newPrData.data.number)
|
|
|
|
await dependencies.addLabelToPullRequest(
|
|
installationOctokit,
|
|
owner,
|
|
repo,
|
|
newPrData.data.number,
|
|
`🎯 Quality: ${optimizationReview.charAt(0).toUpperCase() + optimizationReview.slice(1).toLowerCase()}`,
|
|
"FFC043",
|
|
"Optimization Quality according to codeflash",
|
|
)
|
|
|
|
// Make a comment on the original PR with a link to the new PR
|
|
const commentBody: string = dependencies.originalPRComment(
|
|
prCommentFields,
|
|
newPrData.data.number,
|
|
baseBranch,
|
|
optimizationReview,
|
|
)
|
|
await installationOctokit.rest.issues.createComment({
|
|
owner,
|
|
repo,
|
|
issue_number: origPrNumber,
|
|
body: commentBody,
|
|
})
|
|
|
|
return newPrData
|
|
}
|
|
|
|
function createDependentPRTitleAndBody(
|
|
prCommentFields: PrCommentFields,
|
|
existingTests: string,
|
|
generatedTests: string,
|
|
coverage_message: string,
|
|
origPrNumber: number,
|
|
newBranchName: string,
|
|
baseBranch: string,
|
|
replayTests: string = "",
|
|
concolicTests: string = "",
|
|
optimizationReview: string = "",
|
|
): { title: string; body: string } {
|
|
const prCommentHeader = dependencies.buildResultHeader(prCommentFields)
|
|
// Build benchmark info if available
|
|
const benchmarkInfo =
|
|
prCommentFields.benchmark_details && prCommentFields.benchmark_details.length > 0
|
|
? dependencies.buildBenchmarkInfo(prCommentFields)
|
|
: ""
|
|
// Open a new PR from the new branch onto the original PR's head branch
|
|
const prCommentBody = dependencies.buildResultDetails(prCommentFields)
|
|
const prCommentTestReport = dependencies.buildResultTestReport(
|
|
prCommentFields,
|
|
existingTests,
|
|
generatedTests,
|
|
coverage_message,
|
|
replayTests,
|
|
concolicTests,
|
|
)
|
|
const prCommentFooter = dependencies.buildResultFooter(newBranchName)
|
|
const title = dependencies.buildDependentPrTitle(
|
|
prCommentFields.function_name,
|
|
prCommentFields.speedup_pct,
|
|
prCommentFields.speedup_x,
|
|
origPrNumber,
|
|
baseBranch,
|
|
)
|
|
const introSection =
|
|
`## ⚡️ This pull request contains optimizations for PR #${origPrNumber}
|
|
If you approve this dependent PR, these changes will be merged into the original PR branch \`${baseBranch}\`.
|
|
>This PR will be automatically closed if the original PR is merged.\n` + `----\n`
|
|
let optReviewBadge = generateOptimizationReviewTemplate(optimizationReview)
|
|
optReviewBadge &&= ` ${optReviewBadge}\n`
|
|
// Conditionally build the body based on whether benchmark info exists
|
|
const body = benchmarkInfo
|
|
? `${introSection}${prCommentHeader}\n${benchmarkInfo}\n${prCommentBody}\n${prCommentTestReport}\n${prCommentFooter}${optReviewBadge}`
|
|
: `${introSection}${prCommentHeader}\n${prCommentBody}\n${prCommentTestReport}\n${prCommentFooter}${optReviewBadge}`
|
|
|
|
return { title, body }
|
|
}
|