import { spawnSync } from 'child_process'; import fs from 'fs'; import path from 'path'; import { GITEA_SERVER_URL, GITEA_REPOSITORY, GITEA_TOKEN, PR_HEAD_BRANCH, FINDINGS_PATH } from './config.js'; function makeRunner(spawn) { return function run(args, cwd, env) { const opts = { cwd, encoding: 'utf8' }; if (env) opts.env = env; const result = spawn('git', args, opts); if (result.error) throw result.error; if (result.status !== 0) throw new Error((result.stderr || result.stdout || '').trim()); return (result.stdout || '').trim(); }; } export async function commitAndPush(workspace, _spawnSync = spawnSync) { const run = makeRunner(_spawnSync); const baseUrl = GITEA_SERVER_URL.replace(/\/$/, ''); const remoteUrl = `${baseUrl}/${GITEA_REPOSITORY}.git`; const repoDir = path.join(workspace, 'repo'); // Write a temporary askpass script that reads the token from an env var, // so the token value never appears in the script file itself const askpassScript = path.join(workspace, '.git-askpass.sh'); fs.writeFileSync(askpassScript, '#!/bin/sh\necho "$GIT_TOKEN"\n', { mode: 0o700 }); const credEnv = { ...process.env, GIT_ASKPASS: askpassScript, GIT_USERNAME: 'x-token', GIT_TOKEN: GITEA_TOKEN }; try { if (!fs.existsSync(repoDir)) { run(['clone', '--depth=1', '--branch', PR_HEAD_BRANCH, remoteUrl, repoDir], workspace, credEnv); } run(['config', 'user.email', 'ai-review[bot]@gitea'], repoDir); run(['config', 'user.name', 'AI Review Bot'], repoDir); run(['fetch', 'origin', PR_HEAD_BRANCH], repoDir, credEnv); run(['checkout', PR_HEAD_BRANCH], repoDir); // 將 findings.json 從 workspace 複製到 clone 的 repo const srcFindings = path.join(workspace, FINDINGS_PATH); const destFindings = path.join(repoDir, FINDINGS_PATH); fs.mkdirSync(path.dirname(destFindings), { recursive: true }); fs.copyFileSync(srcFindings, destFindings); run(['add', FINDINGS_PATH], repoDir); const status = run(['status', '--porcelain'], repoDir); if (!status) { console.log(' findings.json 無變更,跳過 commit'); return; } const out = run(['commit', '-m', 'chore: update ai-review findings [skip ci]'], repoDir); const commitHash = out.match(/\[.+ ([a-f0-9]+)\]/)?.[1] || 'unknown'; run(['push', remoteUrl, PR_HEAD_BRANCH], repoDir, credEnv); console.log(` ✅ persisted findings commit=${commitHash} push=${PR_HEAD_BRANCH}`); } catch (e) { console.log(` ⚠️ Runner failed: commit/push 失敗: ${e.message}`); } finally { try { fs.unlinkSync(askpassScript); } catch {} } }