diff --git a/app/git.js b/app/git.js index fe11ac5..5006d88 100644 --- a/app/git.js +++ b/app/git.js @@ -14,6 +14,34 @@ function makeRunner(spawn) { }; } +/** + * Clone PR head branch to workspace/repo (idempotent) + */ +export function cloneRepo(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'); + + 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); + console.log(` ✅ repo cloned to ${repoDir}`); + } else { + run(['fetch', 'origin', PR_HEAD_BRANCH], repoDir, credEnv); + run(['checkout', PR_HEAD_BRANCH], repoDir); + console.log(` ✅ repo already exists, fetched latest`); + } + } finally { + try { fs.unlinkSync(askpassScript); } catch {} + } + return repoDir; +} + export async function commitAndPush(workspace, _spawnSync = spawnSync) { const run = makeRunner(_spawnSync); diff --git a/app/main.js b/app/main.js index ef0b173..b3bd670 100644 --- a/app/main.js +++ b/app/main.js @@ -3,7 +3,7 @@ import { loadRoles, getRoleIntro } from './roles.js'; import { getPRDiff, postComment } from './gitea.js'; import { analyzeWithRole, loadOldFindings, mergeFindings, sortByLevel, deduplicateWithAI, loadExclusions, applyExclusions } from './findings.js'; import { saveFindings, postOldFindingsComment, postNewNonCriticalComment, postNewCriticalComments } from './comments.js'; -import { commitAndPush } from './git.js'; +import { cloneRepo, commitAndPush } from './git.js'; const WORKSPACE = process.env.GITHUB_WORKSPACE || '/workspace'; @@ -65,7 +65,14 @@ async function main() { // Step3: 讀取舊 findings,合併去重(含 AI 語意去重) console.log('\n🔀 Step3: Findings 合併'); - const oldFindings = loadOldFindings(WORKSPACE); + // Clone repo 以讀取舊 findings 與排除清單 + let repoDir; + try { + repoDir = cloneRepo(WORKSPACE); + } catch (e) { + console.log(` ⚠️ clone repo 失敗(繼續執行): ${e.message}`); + } + const oldFindings = loadOldFindings(repoDir || WORKSPACE); const mergedFindings = mergeFindings(oldFindings, newFindings); console.log(` Step3 merged findings total=${mergedFindings.length}`); @@ -76,7 +83,7 @@ async function main() { // Step4: 讀取排除問題檔案,過濾 PR 問題表格 console.log('\n🚫 Step4: 排除問題過濾'); - const exclusions = loadExclusions(WORKSPACE); + const exclusions = loadExclusions(repoDir || WORKSPACE); const filtered = applyExclusions(sorted, exclusions); console.log(` Step4 完成: findings total=${filtered.length}`);