codeflash-internal/js/cf-api/github/create-pr-from-diffcontents.ts
HeshamHM28 a805f4cfbf revert: rollback PR #2601 and dependent fixes to ec39cd51
Reverts the following commits from main:
- d7a8b8f2 perf: fix CI build + lazy-load heavy libs + parallelize DB queries (#2601)
- 48b5e2b4 fix: make tree-sitter WASM build failure non-fatal when cache exists (#2602)
- c372b6bc Merge pull request #2603 from codeflash-ai/fix/deploy-build-common
- b656bb1d fix: cf-api deploy broken by pnpm workspace migration
- c1b0076c fix: align TypeScript versions to deduplicate @prisma/client in pnpm
- 09ed4d4b fix: use redirect instead of throw for auth failures during prerender
- 71127055 fix: redirect remaining auth throws that crash prerendering

PR #2601 introduced 18 bugs including 5 authorization bypass vulnerabilities:
- Cross-org data access via forged currentOrganizationId cookie
- Cross-repo/cross-org member role escalation and deletion (unscoped lookups)
- Missing replayTests/concolicTests in approval flow
- repository_id filter silently broken for personal accounts
- Tests mocking wrong Prisma method ($queryRawUnsafe vs $queryRaw)

The subsequent PRs (#2602, #2603, and follow-up commits) were dependent
fixes for issues caused by #2601 and are reverted together.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 18:59:11 +00:00

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,
} from "./pr-changes-utils.js"
import type { RestEndpointMethodTypes } from "@octokit/rest"
import { buildResultHeader, buildResultDetails, buildResultTestReport } from "./pr-changes-utils.js"
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)
if (optReviewBadge) {
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 }
}