修正 Codex Action 重複輸出最後回答 #3

Merged
jiantw83 merged 5 commits from ai-review-resolve/20260624102518 into develop 2026-06-24 14:30:13 +00:00
3 changed files with 60 additions and 10 deletions
+10 -4
View File
@@ -1,10 +1,16 @@
[ [
{
"level": "warning",
"role": "Bard",
"location": "app/main.js:241",
"problem": "`runCodex` 中 `resultOutput` 的計算邏輯過於緊湊且不易維護,且直接進行檔案同步讀取,若檔案過大可能阻塞 Node.js 事件循環。",
"suggestion": "建議拆解複雜的邏輯為獨立的輔助函數,並改用非同步的檔案讀取方式。"
},
{ {
"level": "info", "level": "info",
"role": "Bard", "role": "Bard",
"problem": "setupAuth 函式職責過於繁雜。", "location": "app/main.js:321",
"suggestion": "將鎖定機制與驗證機制拆分為獨立輔助函式。", "problem": "確保字串結尾換行的樣板程式碼邏輯重複出現。",
"location": "app/main.js:283", "suggestion": "建立 `ensureNewline` 共用工具函式。"
"is_new": false
} }
] ]
+15 -6
View File
@@ -183,11 +183,13 @@ function readExecutionConfig() {
}; };
} }
function codexExecArgs(model, prompt) { function codexExecArgs(model, prompt, lastMessageFile) {
return [ return [
"exec", "exec",
"--dangerously-bypass-approvals-and-sandbox", "--dangerously-bypass-approvals-and-sandbox",
"--skip-git-repo-check", "--skip-git-repo-check",
"--output-last-message",
lastMessageFile,
"--model", "--model",
model, model,
prompt, prompt,
@@ -197,7 +199,10 @@ function codexExecArgs(model, prompt) {
function runCodex(model, prompt) { function runCodex(model, prompt) {
return new Promise((resolve) => { return new Promise((resolve) => {
Ghost marked this conversation as resolved
Review

嚴重等級🔵 建議
審查員:Rogue
問題:每次執行 runCodex 都在建立新的 temp 目錄與追蹤檔案,這種重複性的檔案系統配置開銷對於高頻率呼叫來說是完全浪費的資源。
建議:如果執行環境是持續存在的,應快取 tempDir 路徑或使用預設的共享路徑,避免頻繁的檔案系統建立與追蹤開銷。

**嚴重等級**:🔵 建議 **審查員**:Rogue **問題**:每次執行 runCodex 都在建立新的 temp 目錄與追蹤檔案,這種重複性的檔案系統配置開銷對於高頻率呼叫來說是完全浪費的資源。 **建議**:如果執行環境是持續存在的,應快取 tempDir 路徑或使用預設的共享路徑,避免頻繁的檔案系統建立與追蹤開銷。
const { timeoutMs, outputLimitBytes, workspace } = readExecutionConfig(); const { timeoutMs, outputLimitBytes, workspace } = readExecutionConfig();
const args = codexExecArgs(model, prompt); const tempDir = makeTempDir(process.env.CODEX_HOME || "/root/.codex");
const lastMessageFile = path.join(tempDir, "last-message.txt");
tempFiles.trackFile(lastMessageFile);
const args = codexExecArgs(model, prompt, lastMessageFile);
// This Docker Action runs inside an ephemeral CI container where Codex must be // This Docker Action runs inside an ephemeral CI container where Codex must be
// able to edit the checked-out workspace without interactive approvals. // able to edit the checked-out workspace without interactive approvals.
@@ -210,9 +215,6 @@ function runCodex(model, prompt) {
output.append(Buffer.from(`Codex execution timed out after ${timeoutMs} ms.\n`)); output.append(Buffer.from(`Codex execution timed out after ${timeoutMs} ms.\n`));
}, timeoutMs); }, timeoutMs);
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stdout);
child.stdout.on("data", (chunk) => { child.stdout.on("data", (chunk) => {
output.append(chunk); output.append(chunk);
}); });
@@ -235,7 +237,11 @@ function runCodex(model, prompt) {
output.append(Buffer.from(`Codex process terminated by signal ${signal}.\n`)); output.append(Buffer.from(`Codex process terminated by signal ${signal}.\n`));
} }
resolve({ status: code ?? 1, output: output.toString() }); const lastMessage = fs.existsSync(lastMessageFile) ? fs.readFileSync(lastMessageFile, "utf8") : null;
const diagnosticOutput = output.toString();
Review

嚴重等級🟡 警告
審查員:Bard
問題runCodexresultOutput 的計算邏輯過於緊湊且不易維護,且直接進行檔案同步讀取,若檔案過大可能阻塞 Node.js 事件循環。
建議:建議拆解複雜的邏輯為獨立的輔助函數,並改用非同步的檔案讀取方式。

**嚴重等級**:🟡 警告 **審查員**:Bard **問題**:`runCodex` 中 `resultOutput` 的計算邏輯過於緊湊且不易維護,且直接進行檔案同步讀取,若檔案過大可能阻塞 Node.js 事件循環。 **建議**:建議拆解複雜的邏輯為獨立的輔助函數,並改用非同步的檔案讀取方式。
const resultOutput = code === 0 && lastMessage !== null ? lastMessage : diagnosticOutput || lastMessage || "";
resolve({ status: code ?? 1, output: resultOutput });
}); });
Ghost marked this conversation as resolved Outdated
Outdated
Review

嚴重等級🟡 警告
審查員:Bard
問題:在 runCodex 函式中,對 lastMessageFile 的讀取缺乏明確的錯誤處理策略,直接將錯誤靜默處理並初始化為空字串,可能掩蓋真正的檔案系統問題,且導致邏輯錯誤。
建議:建議在 catch 區塊中至少加入簡單的日誌記錄(如 console.error),並明確區分錯誤類型(如檔案不存在 vs 權限問題),給予清晰的回饋。

**嚴重等級**:🟡 警告 **審查員**:Bard **問題**:在 `runCodex` 函式中,對 `lastMessageFile` 的讀取缺乏明確的錯誤處理策略,直接將錯誤靜默處理並初始化為空字串,可能掩蓋真正的檔案系統問題,且導致邏輯錯誤。 **建議**:建議在 `catch` 區塊中至少加入簡單的日誌記錄(如 `console.error`),並明確區分錯誤類型(如檔案不存在 vs 權限問題),給予清晰的回饋。
}); });
} }
1
@@ -312,6 +318,9 @@ function setupAuth(oauth, codexHome) {
async function runCodexAction({ oauth, model, codexHome, prompt }) { async function runCodexAction({ oauth, model, codexHome, prompt }) {
const lockHandle = setupAuth(oauth, codexHome); const lockHandle = setupAuth(oauth, codexHome);
const result = await runCodex(model, prompt); const result = await runCodex(model, prompt);
if (result.output) {
Review

嚴重等級🔵 建議
審查員:Bard
問題:確保字串結尾換行的樣板程式碼邏輯重複出現。
建議:建立 ensureNewline 共用工具函式。

**嚴重等級**:🔵 建議 **審查員**:Bard **問題**:確保字串結尾換行的樣板程式碼邏輯重複出現。 **建議**:建立 `ensureNewline` 共用工具函式。
process.stdout.write(result.output.endsWith("\n") ? result.output : `${result.output}\n`);
}
appendGithubOutput(result.status === 0 ? "completed" : "failed", result.output); appendGithubOutput(result.status === 0 ? "completed" : "failed", result.output);
if (lockHandle !== undefined) { if (lockHandle !== undefined) {
+35
View File
@@ -56,6 +56,26 @@ SH
chmod +x "$dir/codex" chmod +x "$dir/codex"
} }
make_fake_transcript_codex() {
local dir="$1"
cat > "$dir/codex" <<'SH'
#!/bin/sh
last_message_file=""
while [ "$#" -gt 0 ]; do
if [ "$1" = "--output-last-message" ]; then
shift
last_message_file="$1"
fi
shift
done
printf '%s\n' 'codex' 'final answer' 'tokens used' '123' 'final answer' >&2
printf '%s\n' 'final answer' > "$last_message_file"
SH
chmod +x "$dir/codex"
}
encoded_json() { encoded_json() {
printf '%s' "$1" | base64 | tr -d '\n' printf '%s' "$1" | base64 | tr -d '\n'
} }
@@ -269,6 +289,20 @@ test_codex_output_truncation() {
rm -rf "$tmpdir" rm -rf "$tmpdir"
} }
test_last_message_output_without_transcript_duplicate() {
local tmpdir
tmpdir="$(mktemp -d)"
make_fake_transcript_codex "$tmpdir"
run_entrypoint "$tmpdir" OAUTH="$(encoded_json '{}')" MODEL="gpt-test" PROMPT="hello"
[[ "$(grep -c "final answer" "$tmpdir/stdout")" -eq 1 ]] || die "stdout printed final answer more than once"
grep -q "status=completed" "$tmpdir/github-output" || die "last message completed status"
[[ "$(grep -c "final answer" "$tmpdir/github-output")" -eq 1 ]] || die "github output included transcript duplicate"
! grep -q "tokens used" "$tmpdir/github-output" || die "github output included codex transcript"
rm -rf "$tmpdir"
}
test_invalid_execution_config_uses_defaults() { test_invalid_execution_config_uses_defaults() {
local tmpdir local tmpdir
tmpdir="$(mktemp -d)" tmpdir="$(mktemp -d)"
@@ -299,6 +333,7 @@ test_missing_codex_command_output
test_codex_timeout_output test_codex_timeout_output
test_codex_signal_output test_codex_signal_output
test_codex_output_truncation test_codex_output_truncation
test_last_message_output_without_transcript_duplicate
test_invalid_execution_config_uses_defaults test_invalid_execution_config_uses_defaults
echo "entrypoint tests passed" echo "entrypoint tests passed"