Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ce53c67cac | |||
| 4702f3814e | |||
| 069e43c689 | |||
| 259d0e42c4 | |||
| b0c4d5a0bc |
@@ -21,10 +21,10 @@
|
|||||||
"is_new": false
|
"is_new": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"level": "warning",
|
"level": "info",
|
||||||
"role": "Rex",
|
"role": "Rex",
|
||||||
"location": "action.yaml:81",
|
"location": "action.yaml:7-9, app/gitea.js:100-104",
|
||||||
"suggestion": "在 `action.yaml` 中,`GITEA_TOKEN` 的設定從 `secrets.GITEA_TOKEN` 的 fallback 移除,現在僅從 `inputs.GITEA_TOKEN` 取得。雖然 `inputs.GITEA_TOKEN` 可以透過 `secrets.MY_GITEA_TOKEN` 安全地傳遞,但此變更將確保敏感資料安全傳遞的責任完全轉移到工作流程的配置者。請確保所有使用此 action 的工作流程都透過 GitHub/Gitea secrets 將 `GITEA_TOKEN` 傳遞給 `inputs.GITEA_TOKEN`,以避免將敏感令牌硬編碼或暴露在日誌中。",
|
"suggestion": "引入 `GITEA_COMMENT_TOKEN` 並在 `postComment` 函數中優先使用它,這是一個很好的安全實踐,遵循最小權限原則。建議為此 token 配置僅限於發布評論的權限,以降低潛在洩漏的風險。",
|
||||||
"is_new": false
|
"is_new": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ jobs:
|
|||||||
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 }}
|
||||||
with:
|
with:
|
||||||
GITEA_TOKEN: ${{ secrets.RUNNER_TOKEN }}
|
GITEA_TOKEN: ${{ secrets.RUNNER_TOKEN }}
|
||||||
|
GITEA_COMMENT_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }},${{ secrets.GEMINI_API_KEY_1 }},${{ secrets.GEMINI_API_KEY_2 }},${{ secrets.GEMINI_API_KEY_3 }},${{ secrets.GEMINI_API_KEY_4 }},${{ secrets.GEMINI_API_KEY_5 }},${{ secrets.GEMINI_API_KEY_6 }},${{ secrets.GEMINI_API_KEY_7 }},${{ secrets.GEMINI_API_KEY_8 }},${{ secrets.GEMINI_API_KEY_9 }},${{ secrets.GEMINI_API_KEY_10 }},${{ secrets.GEMINI_API_KEY_11 }},${{ secrets.GEMINI_API_KEY_12 }},${{ secrets.GEMINI_API_KEY_13 }},${{ secrets.GEMINI_API_KEY_14 }},${{ secrets.GEMINI_API_KEY_15 }},${{ secrets.GEMINI_API_KEY_16 }},${{ secrets.GEMINI_API_KEY_17 }},${{ secrets.GEMINI_API_KEY_18 }},${{ secrets.GEMINI_API_KEY_19 }}
|
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }},${{ secrets.GEMINI_API_KEY_1 }},${{ secrets.GEMINI_API_KEY_2 }},${{ secrets.GEMINI_API_KEY_3 }},${{ secrets.GEMINI_API_KEY_4 }},${{ secrets.GEMINI_API_KEY_5 }},${{ secrets.GEMINI_API_KEY_6 }},${{ secrets.GEMINI_API_KEY_7 }},${{ secrets.GEMINI_API_KEY_8 }},${{ secrets.GEMINI_API_KEY_9 }},${{ secrets.GEMINI_API_KEY_10 }},${{ secrets.GEMINI_API_KEY_11 }},${{ secrets.GEMINI_API_KEY_12 }},${{ secrets.GEMINI_API_KEY_13 }},${{ secrets.GEMINI_API_KEY_14 }},${{ secrets.GEMINI_API_KEY_15 }},${{ secrets.GEMINI_API_KEY_16 }},${{ secrets.GEMINI_API_KEY_17 }},${{ secrets.GEMINI_API_KEY_18 }},${{ secrets.GEMINI_API_KEY_19 }}
|
||||||
GEMINI_BASE_URL: https://generativelanguage.googleapis.com/v1beta
|
GEMINI_BASE_URL: https://generativelanguage.googleapis.com/v1beta
|
||||||
GEMINI_MODEL: ${{ vars.GEMINI_MODEL }}
|
GEMINI_MODEL: ${{ vars.GEMINI_MODEL }}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
> **自動提交排除說明**:此 Action 會將自己的 commit message 標記為 `[ai-review-bot][success]` 或 `[ai-review-bot][failure]`,而且 action 執行時會先透過 Gitea API 檢查這次觸發的 PR head commit(優先用 `pull_request.head.sha`)是否含有這個 marker,若有就直接成功結束,避免 bot commit 造成重複觸發。若外層 workflow 也能先檢查一次,效果最好。
|
> **自動提交排除說明**:此 Action 會將自己的 commit message 標記為 `[ai-review-bot][success]` 或 `[ai-review-bot][failure]`,而且 action 執行時會先透過 Gitea API 檢查這次觸發的 PR head commit(優先用 `pull_request.head.sha`)是否含有這個 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)三項權限,為正常運作所必要,無法縮減。若你想讓 comment 用不同權限的 token,可額外傳 `GITEA_COMMENT_TOKEN`,其餘 Gitea 操作仍使用 `GITEA_TOKEN`。
|
||||||
|
|
||||||
### 1. OpenAI
|
### 1. OpenAI
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ inputs:
|
|||||||
GITEA_TOKEN:
|
GITEA_TOKEN:
|
||||||
description: 'Gitea API Token'
|
description: 'Gitea API Token'
|
||||||
required: true
|
required: true
|
||||||
|
GITEA_COMMENT_TOKEN:
|
||||||
|
description: 'Gitea API Token for posting comments only'
|
||||||
|
required: false
|
||||||
GITEA_SERVER_URL:
|
GITEA_SERVER_URL:
|
||||||
description: 'Gitea Server URL'
|
description: 'Gitea Server URL'
|
||||||
required: false
|
required: false
|
||||||
@@ -82,6 +85,7 @@ runs:
|
|||||||
env:
|
env:
|
||||||
# Gitea context(改為只從 inputs 取得)
|
# Gitea context(改為只從 inputs 取得)
|
||||||
GITEA_TOKEN: ${{ inputs.GITEA_TOKEN }}
|
GITEA_TOKEN: ${{ inputs.GITEA_TOKEN }}
|
||||||
|
GITEA_COMMENT_TOKEN: ${{ inputs.GITEA_COMMENT_TOKEN }}
|
||||||
GITEA_SERVER_URL: ${{ inputs.GITEA_SERVER_URL || gitea.server_url }}
|
GITEA_SERVER_URL: ${{ inputs.GITEA_SERVER_URL || gitea.server_url }}
|
||||||
GITEA_REPOSITORY: ${{ inputs.GITEA_REPOSITORY || gitea.repository }}
|
GITEA_REPOSITORY: ${{ inputs.GITEA_REPOSITORY || gitea.repository }}
|
||||||
GITEA_SKIP_TLS_VERIFY: ${{ inputs.GITEA_SKIP_TLS_VERIFY }}
|
GITEA_SKIP_TLS_VERIFY: ${{ inputs.GITEA_SKIP_TLS_VERIFY }}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export const GITEA_TOKEN = process.env.GITEA_TOKEN || '';
|
export const GITEA_TOKEN = process.env.GITEA_TOKEN || '';
|
||||||
|
export const GITEA_COMMENT_TOKEN = process.env.GITEA_COMMENT_TOKEN || '';
|
||||||
export const GITEA_SERVER_URL = process.env.GITEA_SERVER_URL || 'https://gitea.com';
|
export const GITEA_SERVER_URL = process.env.GITEA_SERVER_URL || 'https://gitea.com';
|
||||||
export const GITEA_REPOSITORY = process.env.GITEA_REPOSITORY || '';
|
export const GITEA_REPOSITORY = process.env.GITEA_REPOSITORY || '';
|
||||||
export const GITEA_SKIP_TLS_VERIFY = process.env.GITEA_SKIP_TLS_VERIFY === 'true';
|
export const GITEA_SKIP_TLS_VERIFY = process.env.GITEA_SKIP_TLS_VERIFY === 'true';
|
||||||
|
|||||||
+7
-3
@@ -1,9 +1,9 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import https from 'https';
|
import https from 'https';
|
||||||
import { GITEA_TOKEN, GITEA_SERVER_URL, GITEA_REPOSITORY, GITEA_SKIP_TLS_VERIFY, PR_NUMBER, PR_HEAD_SHA, PR_HEAD_BRANCH } from './config.js';
|
import { GITEA_TOKEN, GITEA_COMMENT_TOKEN, GITEA_SERVER_URL, GITEA_REPOSITORY, GITEA_SKIP_TLS_VERIFY, PR_NUMBER, PR_HEAD_SHA, PR_HEAD_BRANCH } from './config.js';
|
||||||
|
|
||||||
const httpsAgent = GITEA_SKIP_TLS_VERIFY ? new https.Agent({ rejectUnauthorized: false }) : undefined;
|
const httpsAgent = GITEA_SKIP_TLS_VERIFY ? new https.Agent({ rejectUnauthorized: false }) : undefined;
|
||||||
const headers = () => ({ Authorization: `token ${GITEA_TOKEN}`, 'Content-Type': 'application/json' });
|
const headers = (token = GITEA_TOKEN) => ({ Authorization: `token ${token}`, 'Content-Type': 'application/json' });
|
||||||
const api = (path) => `${GITEA_SERVER_URL.replace(/\/$/, '')}/api/v1${path}`;
|
const api = (path) => `${GITEA_SERVER_URL.replace(/\/$/, '')}/api/v1${path}`;
|
||||||
|
|
||||||
function extractCommitMessage(payload) {
|
function extractCommitMessage(payload) {
|
||||||
@@ -115,6 +115,10 @@ export function filterDiff(diff, excludePrefixes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function postComment(body) {
|
export async function postComment(body) {
|
||||||
const resp = await axios.post(api(`/repos/${GITEA_REPOSITORY}/issues/${PR_NUMBER}/comments`), { body }, { headers: headers(), timeout: 30000, httpsAgent });
|
const resp = await axios.post(
|
||||||
|
api(`/repos/${GITEA_REPOSITORY}/issues/${PR_NUMBER}/comments`),
|
||||||
|
{ body },
|
||||||
|
{ headers: headers(GITEA_COMMENT_TOKEN || GITEA_TOKEN), timeout: 30000, httpsAgent },
|
||||||
|
);
|
||||||
return resp.data;
|
return resp.data;
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-1
@@ -1,7 +1,7 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { GITEA_REPOSITORY, PR_NUMBER, PR_HEAD_BRANCH, PR_BASE_BRANCH, getLLMConfig, FINDINGS_PATH, EXCLUSIONS_PATH } from './config.js';
|
import { GITEA_REPOSITORY, PR_NUMBER, PR_HEAD_BRANCH, PR_BASE_BRANCH, getLLMConfig, FINDINGS_PATH, EXCLUSIONS_PATH } from './config.js';
|
||||||
import { loadRoles, getRoleIntro } from './roles.js';
|
import { loadRoles, getRoleIntro } from './roles.js';
|
||||||
import { getPRDiff, postComment, shouldSkipBotCommit } from './gitea.js';
|
import { getPRDiff, postComment, getCommitMessageBySha, getBotReviewOutcome, shouldSkipBotCommit } 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 } from './git.js';
|
||||||
@@ -15,6 +15,16 @@ 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}`);
|
||||||
|
|
||||||
|
const headSha = process.env.PR_HEAD_SHA || process.env.GITHUB_SHA || '';
|
||||||
|
const headMessage = await getCommitMessageBySha(headSha);
|
||||||
|
const headOutcome = getBotReviewOutcome(headMessage);
|
||||||
|
console.log(` 🔎 head check: sha=${headSha || 'empty'} outcome=${headOutcome}`);
|
||||||
|
if (headMessage.includes('[ai-review-bot]') && headOutcome === 'failure') {
|
||||||
|
console.log(' ❌ 偵測到 [ai-review-bot][failure],直接讓 workflow 失敗');
|
||||||
|
console.log('='.repeat(60));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
if (await shouldSkipBotCommit()) {
|
if (await shouldSkipBotCommit()) {
|
||||||
console.log(' 🤖 偵測到 [ai-review-bot] 自動提交,直接完成 action');
|
console.log(' 🤖 偵測到 [ai-review-bot] 自動提交,直接完成 action');
|
||||||
console.log('='.repeat(60));
|
console.log('='.repeat(60));
|
||||||
|
|||||||
Reference in New Issue
Block a user