PRIX / src /checkout.ts
Rachit-Tw's picture
feat: implement autonomous Auto-PR and optimize Docker image
5cb7295
Raw
History Blame Contribute Delete
4.04 kB
import {mkdirSync, existsSync, rmSync, writeFileSync} from 'fs'
import {join} from 'path'
import {simpleGit, SimpleGit} from 'simple-git'
import {getCurrentContext} from './context'
import pino from 'pino'
const logger = pino({ level: process.env.LOG_LEVEL || 'info' })
export interface CheckoutOptions {
sparse?: boolean
files?: string[]
ref?: string
}
export const checkoutRepo = async (
tempDir: string,
options: CheckoutOptions = {}
): Promise<string> => {
const ctx = getCurrentContext()
if (!ctx) throw new Error('No context available for checkout')
// Verify git is available
const git = simpleGit()
try {
await git.raw(['--version'])
} catch (e) {
throw new Error('git command not found in PATH. Please install git.')
}
const {owner, repo} = ctx.repo
const installationId = ctx.probotContext.payload.installation?.id
if (!installationId) {
throw new Error('Installation ID not found in payload')
}
const {token} = (await ctx.probotContext.octokit.auth({
type: 'installation'
})) as any
if (!token) {
throw new Error(
'Failed to create installation access token: token is undefined'
)
}
// Ensure gh CLI is authenticated for subsequent contexts
process.env.GH_TOKEN = token
process.env.GITHUB_TOKEN = token
// SECURITY: Use token via environment variable, NOT in URL
// This prevents token exposure in process listings and logs
const cloneUrl = `https://github.com/${owner}/${repo}.git`
const isPullRequest = !!ctx.probotContext.payload.pull_request
const headBranch = isPullRequest
? ctx.probotContext.payload.pull_request.head.ref
: (ctx.probotContext.payload.ref?.replace('refs/heads/', '') || ctx.probotContext.payload.repository?.default_branch || 'main')
const headSha = isPullRequest
? ctx.probotContext.payload.pull_request.head.sha
: (options.ref || ctx.probotContext.payload.after)
// Set up git credential helper for secure token authentication
process.env.GIT_ASKPASS = 'echo'
process.env.GIT_USERNAME = 'x-access-token'
process.env.GIT_PASSWORD = token
if (existsSync(tempDir)) {
rmSync(tempDir, {recursive: true, force: true})
}
mkdirSync(tempDir, {recursive: true})
logger.info({ owner, repo, tempDir }, 'Cloning repository')
const repoGit: SimpleGit = simpleGit(tempDir, {
timeout: { block: 120000 } // 2 minute timeout for all git operations
})
try {
if (options.sparse && options.files && options.files.length > 0) {
logger.info({ fileCount: options.files.length }, 'Using sparse checkout')
// Initialize empty repo and configure sparse checkout
await repoGit.init(['--bare'])
await repoGit.addRemote('origin', cloneUrl)
// Configure sparse checkout
await repoGit.raw(['config', 'core.sparseCheckout', 'true'])
writeFileSync(join(tempDir, '.git/info/sparse-checkout'), options.files.join('\n'))
// Fetch and checkout specific commit
await repoGit.fetch(['origin', headSha, '--depth', '1'])
await repoGit.checkout(['FETCH_HEAD'])
} else {
logger.info('Performing full shallow clone')
// Clone with depth 1 for efficiency
await repoGit.clone(cloneUrl, '.', [
'--depth', '1',
'--single-branch',
'--branch', headBranch,
'--no-checkout'
])
// Fetch and checkout specific SHA
await repoGit.fetch(['origin', headSha, '--depth', '1'])
await repoGit.checkout([headSha])
}
// Configure git identity
await repoGit.addConfig('user.name', 'PRIX AI Auditor')
await repoGit.addConfig('user.email', 'bot@prix.ai')
logger.info({ owner, repo, headSha }, 'Repository cloned successfully')
} catch (error) {
logger.error({ error, owner, repo }, 'Git operation failed')
throw new Error(`Failed to clone repository: ${error}`)
}
return tempDir
}