| 62 | } |
| 63 | |
| 64 | export const withTestRepoAndParent = async <T>( |
| 65 | repoConfig: { |
| 66 | repoUrl: string |
| 67 | commitSha: string |
| 68 | initCommand?: string |
| 69 | }, |
| 70 | fn: (cwd: string, commitSha: string, parentSha: string) => Promise<T>, |
| 71 | ): Promise<T | null> => { |
| 72 | const { repoUrl, commitSha, initCommand } = repoConfig |
| 73 | |
| 74 | const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codebuff-eval-')) |
| 75 | const repoDir = path.join(tempDir, 'repo') |
| 76 | |
| 77 | try { |
| 78 | execSync(`git clone --depth 1 ${repoUrl} ${repoDir}`, { stdio: 'ignore' }) |
| 79 | |
| 80 | execSync(`git fetch --depth 2 origin ${commitSha}`, { |
| 81 | cwd: repoDir, |
| 82 | stdio: 'ignore', |
| 83 | }) |
| 84 | |
| 85 | execSync(`git checkout ${commitSha}`, { cwd: repoDir, stdio: 'ignore' }) |
| 86 | |
| 87 | let parentSha: string |
| 88 | try { |
| 89 | const parents = execSync(`git log --pretty=%P -n 1 ${commitSha}`, { |
| 90 | cwd: repoDir, |
| 91 | encoding: 'utf-8', |
| 92 | stdio: ['ignore', 'pipe', 'ignore'], |
| 93 | }).trim() |
| 94 | |
| 95 | if (!parents) { |
| 96 | console.warn( |
| 97 | `Commit ${commitSha.slice(0, 8)} has no parent (initial commit)`, |
| 98 | ) |
| 99 | return null |
| 100 | } |
| 101 | |
| 102 | const parentList = parents.split(' ') |
| 103 | if (parentList.length > 1) { |
| 104 | console.warn( |
| 105 | `Commit ${commitSha.slice(0, 8)} is a merge commit (${parentList.length} parents)`, |
| 106 | ) |
| 107 | return null |
| 108 | } |
| 109 | |
| 110 | parentSha = parentList[0] |
| 111 | } catch (error) { |
| 112 | console.error(`Error getting parent for ${commitSha.slice(0, 8)}:`, error) |
| 113 | return null |
| 114 | } |
| 115 | |
| 116 | execSync(`git checkout ${parentSha}`, { cwd: repoDir, stdio: 'ignore' }) |
| 117 | |
| 118 | if (initCommand) { |
| 119 | console.log(`Running init command: ${initCommand}...`) |
| 120 | execSync(initCommand, { cwd: repoDir, stdio: 'ignore' }) |
| 121 | } |