fix(git): bot commit 改用 comment token (PAT) 推送以重新觸發 workflow
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
ef8a0d61c8
commit
fc10ab7266
+14
-5
@@ -1,7 +1,7 @@
|
||||
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';
|
||||
import { GITEA_SERVER_URL, GITEA_REPOSITORY, GITEA_TOKEN, GITEA_COMMENT_TOKEN, PR_HEAD_BRANCH, FINDINGS_PATH } from './config.js';
|
||||
import { line, ok, warn } from './log.js';
|
||||
|
||||
const REVIEW_FILE_PATHS = [FINDINGS_PATH, '.gitea/ai-review/exclusions.json'];
|
||||
@@ -50,14 +50,18 @@ function makeRunner(spawn) {
|
||||
* @template T
|
||||
* @param {string} workspace 寫入暫存 askpass 腳本的目錄。
|
||||
* @param {(credEnv: NodeJS.ProcessEnv) => T} fn 帶入憑證環境變數執行的回呼。
|
||||
* @param {string} [token=GITEA_TOKEN] 供 git HTTP 認證使用的 token。預設為自動的
|
||||
* `GITEA_TOKEN`(適用於唯讀的 clone/ls-remote);需要讓 push 出來的 commit
|
||||
* 重新觸發 workflow 時,呼叫端應改傳真人 PAT(`GITEA_COMMENT_TOKEN`),因為
|
||||
* Gitea 不會為「自動 token」推送的 commit 發出事件。
|
||||
* @returns {T} 即 `fn` 的回傳值(Promise 會被包成 `.finally(cleanup)` 後回傳)。
|
||||
* @throws 透傳 `fn` 丟出的任何例外(同步路徑會先清理暫存腳本再 re-throw)。
|
||||
* @remarks askpass 腳本以權限 0o700 寫出;token 取自 config 的 `GITEA_TOKEN`。
|
||||
* @remarks askpass 腳本以權限 0o700 寫出;token 由參數帶入並透過 `GIT_TOKEN` 提供給腳本。
|
||||
*/
|
||||
function withAskpass(workspace, fn) {
|
||||
function withAskpass(workspace, fn, token = GITEA_TOKEN) {
|
||||
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 };
|
||||
const credEnv = { ...process.env, GIT_ASKPASS: askpassScript, GIT_USERNAME: 'x-token', GIT_TOKEN: token };
|
||||
const cleanup = () => { try { fs.unlinkSync(askpassScript); } catch {} };
|
||||
let result;
|
||||
try {
|
||||
@@ -203,9 +207,14 @@ export function cloneRepo(workspace, _spawnSync = spawnSync) {
|
||||
* @returns {Promise<void>} 無回傳值;所有失敗皆以 log 記錄後吞掉。
|
||||
* @remarks `git reset --hard origin/<branch>` 會丟棄本地未對齊變更,請確認
|
||||
* review 檔是在 reset 之後才複製進來(流程已如此安排)。
|
||||
* @remarks push 優先使用真人 PAT `GITEA_COMMENT_TOKEN`(無則退回 `GITEA_TOKEN`),
|
||||
* 目的是讓 bot commit 能重新觸發 PR 的 workflow:以自動 token 推送的 commit
|
||||
* 不會發出 `synchronize` 事件,新 head commit 便拿不到檢查而卡住。改用 PAT
|
||||
* 推送後會正常重觸發,重跑時由 main.js Step3 偵測 `[ai-review-bot]` 標記後跳過。
|
||||
*/
|
||||
export async function commitAndPush(workspace, repoDir, _spawnSync = spawnSync, _sourceRoot = null, reviewOutcome = 'success') {
|
||||
const run = makeRunner(_spawnSync);
|
||||
const pushToken = GITEA_COMMENT_TOKEN || GITEA_TOKEN;
|
||||
|
||||
try {
|
||||
await withAskpass(workspace, async credEnv => {
|
||||
@@ -242,7 +251,7 @@ export async function commitAndPush(workspace, repoDir, _spawnSync = spawnSync,
|
||||
} catch (pushErr) {
|
||||
warn(`Step8 commit 成功但 push 失敗: commit=${commitHash} push=${PR_HEAD_BRANCH} review_outcome=${reviewOutcome} error=${pushErr.message}`);
|
||||
}
|
||||
});
|
||||
}, pushToken);
|
||||
} catch (e) {
|
||||
warn(`Runner failed: commit/push 失敗: ${e.message}`);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import { commitAndPush, cloneRepo, verifyRemoteAccess, BOT_COMMIT_MARKER, getHeadCommitMessage, isBotAutoCommit } from '../git.js';
|
||||
import { GITEA_TOKEN, GITEA_COMMENT_TOKEN } from '../config.js';
|
||||
|
||||
// --- helpers ---
|
||||
function makeTmpWorkspace() {
|
||||
@@ -74,6 +75,17 @@ describe('commitAndPush', () => {
|
||||
assert.ok(commitCall.args.some(arg => arg.includes('[failure]')), 'expected commit message to include failure outcome');
|
||||
});
|
||||
|
||||
it('pushes with the comment-token (PAT) so the bot commit can re-trigger the workflow', async () => {
|
||||
const spawn = makeSpawn();
|
||||
await commitAndPush(workspace, path.join(workspace, 'repo'), spawn, sourceRoot);
|
||||
|
||||
// push 必須帶「優先 PAT」的 token,Gitea 才會為 bot commit 重新發出 synchronize 事件。
|
||||
const expectedToken = GITEA_COMMENT_TOKEN || GITEA_TOKEN;
|
||||
const pushCall = spawn.calls.find(c => c.args[0] === 'push');
|
||||
assert.ok(pushCall, 'expected git push to run');
|
||||
assert.equal(pushCall.opts?.env?.GIT_TOKEN, expectedToken, 'push must use GITEA_COMMENT_TOKEN (fallback GITEA_TOKEN)');
|
||||
});
|
||||
|
||||
it('uses GIT_ASKPASS env for network operations (fetch, push, clone)', async () => {
|
||||
const spawn = makeSpawn();
|
||||
await commitAndPush(workspace, path.join(workspace, 'repo'), spawn, sourceRoot);
|
||||
|
||||
Reference in New Issue
Block a user