Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9012fe64d1 | |||
| 3ae08052a3 |
@@ -4,27 +4,34 @@
|
|||||||
"role": "Maya",
|
"role": "Maya",
|
||||||
"location": "action.yaml:6, action.yaml:81",
|
"location": "action.yaml:6, action.yaml:81",
|
||||||
"suggestion": "由於 `GITEA_TOKEN` 現在被設定為 `required: true` 且移除了 `secrets.GITEA_TOKEN` 的 fallback 機制,這是一個關鍵性的行為變更。請務必新增整合測試 (integration tests) 來驗證以下情境:\n1. 當 `inputs.GITEA_TOKEN` 未提供時,Action 應如預期般失敗。\n2. 當 `inputs.GITEA_TOKEN` 有提供時,Action 應能正常執行。\n這將確保新的輸入要求和邏輯變更不會導致意外的行為或破壞現有工作流程。",
|
"suggestion": "由於 `GITEA_TOKEN` 現在被設定為 `required: true` 且移除了 `secrets.GITEA_TOKEN` 的 fallback 機制,這是一個關鍵性的行為變更。請務必新增整合測試 (integration tests) 來驗證以下情境:\n1. 當 `inputs.GITEA_TOKEN` 未提供時,Action 應如預期般失敗。\n2. 當 `inputs.GITEA_TOKEN` 有提供時,Action 應能正常執行。\n這將確保新的輸入要求和邏輯變更不會導致意外的行為或破壞現有工作流程。",
|
||||||
"is_new": true
|
"is_new": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"level": "warning",
|
"level": "warning",
|
||||||
"role": "Leo",
|
"role": "Leo",
|
||||||
"location": "action.yaml:5",
|
"location": "action.yaml:5",
|
||||||
"suggestion": "輸入 `GITEA_TOKEN` 的註解 `Gitea 相關(可從 gitea context 自動取得)` 已不再準確。由於 `GITEA_TOKEN` 現在是 `required: true` 且不再從 `secrets.GITEA_TOKEN` 取得,建議更新此註解以明確指出此 Token 必須透過 `inputs` 提供。",
|
"suggestion": "輸入 `GITEA_TOKEN` 的註解 `Gitea 相關(可從 gitea context 自動取得)` 已不再準確。由於 `GITEA_TOKEN` 現在是 `required: true` 且不再從 `secrets.GITEA_TOKEN` 取得,建議更新此註解以明確指出此 Token 必須透過 `inputs` 提供。",
|
||||||
"is_new": true
|
"is_new": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"level": "warning",
|
"level": "warning",
|
||||||
"role": "Leo",
|
"role": "Leo",
|
||||||
"location": "action.yaml:80",
|
"location": "action.yaml:80",
|
||||||
"suggestion": "在 `runs.env` 區塊中,`GITEA_TOKEN` 現在只從 `inputs` 取得,但 `GITEA_SERVER_URL` 和 `GITEA_REPOSITORY` 仍保留從 `gitea context` 取得的備用機制。這種處理方式的不一致性可能會造成未來的維護困擾。建議統一所有 Gitea 相關變數的取得邏輯,或提供明確的註解說明此差異的原因。",
|
"suggestion": "在 `runs.env` 區塊中,`GITEA_TOKEN` 現在只從 `inputs` 取得,但 `GITEA_SERVER_URL` 和 `GITEA_REPOSITORY` 仍保留從 `gitea context` 取得的備用機制。這種處理方式的不一致性可能會造成未來的維護困擾。建議統一所有 Gitea 相關變數的取得邏輯,或提供明確的註解說明此差異的原因。",
|
||||||
"is_new": true
|
"is_new": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"level": "warning",
|
"level": "warning",
|
||||||
"role": "Rex",
|
"role": "Rex",
|
||||||
"location": "action.yaml:83",
|
"location": "action.yaml:83",
|
||||||
"suggestion": "建議將 `GITEA_TOKEN` 的環境變數設定改回 `GITEA_TOKEN: ${{ inputs.GITEA_TOKEN || secrets.GITEA_TOKEN }}`。此變更移除了從 `secrets.GITEA_TOKEN` 安全取得 Token 的備用機制。雖然 `inputs.GITEA_TOKEN` 可以透過 `secrets` 上下文安全傳遞(例如:`with: GITEA_TOKEN: ${{ secrets.MY_GITEA_TOKEN }}`),但若使用者不慎直接將敏感 Token 字串作為 `inputs.GITEA_TOKEN` 的值傳入,該 Token 將可能被記錄在日誌中,導致敏感資訊洩漏。保留備用機制可提供更強健的安全性,降低因使用者操作失誤而導致的風險。",
|
"suggestion": "建議將 `GITEA_TOKEN` 的環境變數設定改回 `GITEA_TOKEN: ${{ inputs.GITEA_TOKEN || secrets.GITEA_TOKEN }}`。此變更移除了從 `secrets.GITEA_TOKEN` 安全取得 Token 的備用機制。雖然 `inputs.GITEA_TOKEN` 可以透過 `secrets` 上下文安全傳遞(例如:`with: GITEA_TOKEN: ${{ secrets.MY_GITEA_TOKEN }}`),但若使用者不慎直接將敏感 Token 字串作為 `inputs.GITEA_TOKEN` 的值傳入,該 Token 將可能被記錄在日誌中,導致敏感資訊洩漏。保留備用機制可提供更強健的安全性,降低因使用者操作失誤而導致的風險。",
|
||||||
|
"is_new": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": "warning",
|
||||||
|
"role": "Maya",
|
||||||
|
"location": "action.yaml:80",
|
||||||
|
"suggestion": "GITEA_TOKEN 的來源已從 `inputs.GITEA_TOKEN || secrets.GITEA_TOKEN` 變更為僅 `inputs.GITEA_TOKEN`。雖然 `required: true` 已經設定,但仍建議在測試中明確涵蓋此邏輯變更,確保 GITEA_TOKEN 確實只從輸入取得,並且不再嘗試回溯到 secrets,以防止未來潛在的誤解或回歸。",
|
||||||
"is_new": true
|
"is_new": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -5,36 +5,9 @@ on:
|
|||||||
- master
|
- master
|
||||||
types: [opened, synchronize]
|
types: [opened, synchronize]
|
||||||
jobs:
|
jobs:
|
||||||
detect-bot-commit:
|
|
||||||
name: 偵測自動提交
|
|
||||||
runs-on: ubuntu
|
|
||||||
outputs:
|
|
||||||
skip: ${{ steps.detect.outputs.skip }}
|
|
||||||
steps:
|
|
||||||
- name: 檢查 head commit marker
|
|
||||||
id: detect
|
|
||||||
env:
|
|
||||||
GITEA_API_URL: ${{ github.api_url }}
|
|
||||||
GITEA_REPOSITORY: ${{ github.repository }}
|
|
||||||
GITEA_SHA: ${{ github.sha }}
|
|
||||||
GITEA_TOKEN: ${{ github.token }}
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
commit_json="$(curl -fsSL -H "Authorization: token ${GITEA_TOKEN}" "${GITEA_API_URL}/repos/${GITEA_REPOSITORY}/git/commits/${GITEA_SHA}")" || {
|
|
||||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
if printf '%s' "$commit_json" | grep -q '\[ai-review-bot\]'; then
|
|
||||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "偵測到 AI Review Bot commit,跳過 review workflow"
|
|
||||||
else
|
|
||||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
version:
|
version:
|
||||||
name: 計算版本號
|
name: 計算版本號
|
||||||
runs-on: ubuntu
|
runs-on: ubuntu
|
||||||
needs: [detect-bot-commit]
|
|
||||||
if: needs.detect-bot-commit.outputs.skip != 'true'
|
|
||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.version.outputs.version }}
|
version: ${{ steps.version.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
@@ -52,8 +25,7 @@ jobs:
|
|||||||
code-review:
|
code-review:
|
||||||
name: Code Review
|
name: Code Review
|
||||||
runs-on: ubuntu
|
runs-on: ubuntu
|
||||||
needs: [detect-bot-commit, version]
|
needs: [version]
|
||||||
if: needs.detect-bot-commit.outputs.skip != 'true'
|
|
||||||
steps:
|
steps:
|
||||||
- name: AI Code Review
|
- name: AI Code Review
|
||||||
uses: https://gitea.jsc.idv.tw/actions/code-review@v${{ needs.version.outputs.version }}
|
uses: https://gitea.jsc.idv.tw/actions/code-review@v${{ needs.version.outputs.version }}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
2. 在 `.gitea/workflows` 資料夾中建立 `ai-review.yaml'
|
2. 在 `.gitea/workflows` 資料夾中建立 `ai-review.yaml'
|
||||||
3. 在 `ai-review.yaml` 中填入以下內容(選擇一個使用):
|
3. 在 `ai-review.yaml` 中填入以下內容(選擇一個使用):
|
||||||
|
|
||||||
> **自動提交排除說明**:此 Action 會將自己的 commit message 標記為 `[ai-review-bot]`。建議在 review workflow 的最前面先檢查 head commit 是否含有這個 marker,若有就直接成功結束,避免 bot commit 造成重複觸發。
|
> **自動提交排除說明**:此 Action 會將自己的 commit message 標記為 `[ai-review-bot]`,而且 action 執行時也會先檢查 head commit 是否含有這個 marker,若有就直接成功結束,避免 bot commit 造成重複觸發。若外層 workflow 也能先檢查一次,效果最好。
|
||||||
|
|
||||||
> **權限說明**:此 Action 需要 `contents: write`(寫入 findings.json)、`pull-requests: write`(發佈 PR comment)、`issues: write`(發佈 issue comment)三項權限,為正常運作所必要,無法縮減。
|
> **權限說明**:此 Action 需要 `contents: write`(寫入 findings.json)、`pull-requests: write`(發佈 PR comment)、`issues: write`(發佈 issue comment)三項權限,為正常運作所必要,無法縮減。
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,15 @@ export function getRepoState(repoDir, _spawnSync = spawnSync) {
|
|||||||
return { repoDir, branch, headSha, shortSha, commitTime };
|
return { repoDir, branch, headSha, shortSha, commitTime };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getHeadCommitMessage(repoDir, _spawnSync = spawnSync) {
|
||||||
|
const run = makeRunner(_spawnSync);
|
||||||
|
return readGitOutput(run, ['show', '-s', '--format=%B', 'HEAD'], repoDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isBotAutoCommit(repoDir, _spawnSync = spawnSync) {
|
||||||
|
return getHeadCommitMessage(repoDir, _spawnSync).includes(BOT_COMMIT_MARKER);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clone PR head branch to workspace/repo (idempotent)
|
* Clone PR head branch to workspace/repo (idempotent)
|
||||||
*/
|
*/
|
||||||
|
|||||||
+10
-1
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { commitAndPush, cloneRepo, SYNC_PATHS, BOT_COMMIT_MARKER } from './git.js';
|
import { commitAndPush, cloneRepo, SYNC_PATHS, BOT_COMMIT_MARKER, getHeadCommitMessage, isBotAutoCommit } from './git.js';
|
||||||
|
|
||||||
// --- helpers ---
|
// --- helpers ---
|
||||||
function makeTmpWorkspace() {
|
function makeTmpWorkspace() {
|
||||||
@@ -241,4 +241,13 @@ describe('cloneRepo', () => {
|
|||||||
const result = cloneRepo(workspace, spawn);
|
const result = cloneRepo(workspace, spawn);
|
||||||
assert.equal(result, path.join(workspace, 'repo'));
|
assert.equal(result, path.join(workspace, 'repo'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('reads head commit message and detects bot auto commits', () => {
|
||||||
|
const spawn = makeSpawn({
|
||||||
|
show: () => ({ status: 0, stdout: `chore: update ai-review findings ${BOT_COMMIT_MARKER}\n`, stderr: '', error: null }),
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(getHeadCommitMessage(workspace, spawn).includes(BOT_COMMIT_MARKER));
|
||||||
|
assert.equal(isBotAutoCommit(workspace, spawn), true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+7
-1
@@ -4,7 +4,7 @@ import { loadRoles, getRoleIntro } from './roles.js';
|
|||||||
import { getPRDiff, postComment } from './gitea.js';
|
import { getPRDiff, postComment } from './gitea.js';
|
||||||
import { analyzeWithRole, loadOldFindings, mergeFindings, sortByLevel, deduplicateWithAI, loadExclusions, applyExclusions, filterFalsePositivesWithAI } from './findings.js';
|
import { analyzeWithRole, loadOldFindings, mergeFindings, sortByLevel, deduplicateWithAI, loadExclusions, applyExclusions, filterFalsePositivesWithAI } from './findings.js';
|
||||||
import { saveFindings, postOldFindingsComment, postNewNonCriticalComment, postNewCriticalComments } from './comments.js';
|
import { saveFindings, postOldFindingsComment, postNewNonCriticalComment, postNewCriticalComments } from './comments.js';
|
||||||
import { cloneRepo, commitAndPush, getRepoState } from './git.js';
|
import { cloneRepo, commitAndPush, getRepoState, isBotAutoCommit } from './git.js';
|
||||||
import { validateJSONArrayFile, ensureJSONArrayFileExists } from './json.js';
|
import { validateJSONArrayFile, ensureJSONArrayFileExists } from './json.js';
|
||||||
|
|
||||||
const WORKSPACE = process.env.GITHUB_WORKSPACE || '/workspace';
|
const WORKSPACE = process.env.GITHUB_WORKSPACE || '/workspace';
|
||||||
@@ -15,6 +15,12 @@ async function main() {
|
|||||||
console.log(` repo=${GITEA_REPOSITORY} PR=#${PR_NUMBER}`);
|
console.log(` repo=${GITEA_REPOSITORY} PR=#${PR_NUMBER}`);
|
||||||
console.log(` ${PR_HEAD_BRANCH} -> ${PR_BASE_BRANCH}`);
|
console.log(` ${PR_HEAD_BRANCH} -> ${PR_BASE_BRANCH}`);
|
||||||
|
|
||||||
|
if (isBotAutoCommit(WORKSPACE)) {
|
||||||
|
console.log(' 🤖 偵測到 [ai-review-bot] 自動提交,直接完成 action');
|
||||||
|
console.log('='.repeat(60));
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
const { provider, baseURL, model } = getLLMConfig();
|
const { provider, baseURL, model } = getLLMConfig();
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
console.error('❌ 未設定任何 LLM API Key,請檢查 action inputs');
|
console.error('❌ 未設定任何 LLM API Key,請檢查 action inputs');
|
||||||
|
|||||||
Reference in New Issue
Block a user