feat: 添加 Gitea PR 评审 Action 的初始实现

This commit is contained in:
ren
2026-04-12 23:25:43 +08:00
commit ef24c532b4
7 changed files with 725 additions and 0 deletions

149
.gitignore vendored Normal file
View File

@@ -0,0 +1,149 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.*
!.env.example
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
.output
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Sveltekit cache directory
.svelte-kit/
# vitepress build output
**/.vitepress/dist
# vitepress cache directory
**/.vitepress/cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# Firebase cache directory
.firebase/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# IDE and editors
.vscode/
.cursor/
.trae
# pnpm
.pnpm-store
# yarn v3
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
# Vite files
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
.vite/

65
README.md Normal file
View File

@@ -0,0 +1,65 @@
# Gitea PR 评审 Action
这个 Gitea Action 允许你在 Gitea 的 Pull Request 中添加或更新代码评审Review评论。它专为自动化代码审阅工具设计支持精准的代码行评论。
## 功能特性
- **零配置启动**:自动从运行环境获取 Gitea 地址、仓库名、PR 编号和提交 ID。
- **行级评论**:支持在代码文件的特定行添加评审意见。
- **评审总结**:支持提交整体的评审状态(批准、请求修改或仅评论)。
- **智能更新**通过唯一标识符identifier更新现有评论避免评论堆积。
## 输入参数
| 名称 | 描述 | 必填 | 默认值 |
| --- | --- | --- | --- |
| `gitea-token` | 用于 API 认证的 Gitea token | 是 | |
| `body` | 评论的 Markdown 内容 | 是 | |
| `identifier` | 评论的唯一标识符(用于更新) | 否 | |
| `update-if-exists` | 是否更新具有相同标识符的现有评论 | 否 | `false` |
| `event` | 评审类型 (`COMMENT`, `APPROVE`, `REQUEST_CHANGES`) | 否 | `COMMENT` |
| `commit-id` | 提交的 SHA默认从 PR 自动获取) | 否 | |
| `path` | 文件相对路径(行级评论必填) | 否 | |
| `new-line` | 新文件中的行号 | 否 | |
| `old-line` | 旧文件中的行号 | 否 | |
### 高级/覆盖参数(通常自动推断)
| 名称 | 描述 | 默认值 |
| --- | --- | --- |
| `gitea-base-url` | Gitea 实例的基础 URL | `GITEA_SERVER_URL` |
| `repository` | 仓库路径 (owner/repo) | `GITEA_REPOSITORY` |
| `issue-number` | PR 编号 | 自动从事件 Payload 获取 |
## 使用方法
### 1. 简单的评审总结
```yaml
- name: 批准 PR
uses: ren/gitea-comments@v0.1.0
with:
gitea-token: ${{ secrets.GITEA_TOKEN }}
event: 'APPROVE'
body: "代码审阅通过! ✅"
```
### 2. 自动化的代码行评论(更新模式)
非常适合 Linter 或静态分析工具。
```yaml
- name: 提交代码规范建议
uses: ren/gitea-comments@v0.1.0
with:
gitea-token: ${{ secrets.GITEA_TOKEN }}
path: "src/main.js"
new-line: 15
body: "建议在此处使用 `const` 而不是 `let`。"
identifier: lint-error-line-15
update-if-exists: true
```
## 许可证
MIT

47
action.yml Normal file
View File

@@ -0,0 +1,47 @@
name: 'Gitea PR Review'
description: 'Add or update code review comments on a Gitea Pull Request'
author: 'ren'
inputs:
gitea-token:
description: 'The Gitea token to authenticate with the API'
required: true
body:
description: 'The markdown body of the comment'
required: true
identifier:
description: 'A unique identifier for the comment, used for updating existing comments'
required: false
update-if-exists:
description: 'Whether to update an existing comment with the same identifier'
required: false
default: 'false'
event:
description: 'The review event (COMMENT, APPROVE, REQUEST_CHANGES)'
required: false
default: 'COMMENT'
commit-id:
description: 'The SHA of the commit (Automatically inferred from PR if not provided)'
required: false
path:
description: 'The relative path to the file (Required for line-specific comments)'
required: false
new-line:
description: 'The line number in the new file'
required: false
old-line:
description: 'The line number in the old file'
required: false
# Advanced/Override Inputs (Usually inferred from environment)
gitea-base-url:
description: 'The base URL of your Gitea instance (Automatically inferred if not provided)'
required: false
repository:
description: 'The repository (e.g., owner/repo) (Automatically inferred if not provided)'
required: false
issue-number:
description: 'The PR number to review (Automatically inferred if not provided)'
required: false
runs:
using: 'node20'
main: 'src/index.js'

18
package.json Normal file
View File

@@ -0,0 +1,18 @@
{
"name": "gitea-comments",
"version": "0.1.0",
"description": "Gitea comments action",
"main": "index.js",
"scripts": {
},
"keywords": [
"gitea",
"comments"
],
"author": "ren",
"private": true,
"packageManager": "pnpm@10.33.0",
"dependencies": {
"@actions/core": "^1.11.1"
}
}

266
pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,266 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
'@actions/core':
specifier: ^1.11.1
version: 1.11.1
axios:
specifier: ^1.8.1
version: 1.15.0
devDependencies:
'@vercel/ncc':
specifier: ^0.38.3
version: 0.38.4
packages:
'@actions/core@1.11.1':
resolution: {integrity: sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==}
'@actions/exec@1.1.1':
resolution: {integrity: sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==}
'@actions/http-client@2.2.3':
resolution: {integrity: sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==}
'@actions/io@1.1.3':
resolution: {integrity: sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==}
'@fastify/busboy@2.1.1':
resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==}
engines: {node: '>=14'}
'@vercel/ncc@0.38.4':
resolution: {integrity: sha512-8LwjnlP39s08C08J5NstzriPvW1SP8Zfpp1BvC2sI35kPeZnHfxVkCwu4/+Wodgnd60UtT1n8K8zw+Mp7J9JmQ==}
hasBin: true
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
axios@1.15.0:
resolution: {integrity: sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==}
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
es-set-tostringtag@2.1.0:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
follow-redirects@1.15.11:
resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
form-data@4.0.5:
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
engines: {node: '>= 6'}
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
get-proto@1.0.1:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
has-tostringtag@1.0.2:
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
engines: {node: '>= 0.4'}
hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
proxy-from-env@2.1.0:
resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==}
engines: {node: '>=10'}
tunnel@0.0.6:
resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==}
engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'}
undici@5.29.0:
resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==}
engines: {node: '>=14.0'}
snapshots:
'@actions/core@1.11.1':
dependencies:
'@actions/exec': 1.1.1
'@actions/http-client': 2.2.3
'@actions/exec@1.1.1':
dependencies:
'@actions/io': 1.1.3
'@actions/http-client@2.2.3':
dependencies:
tunnel: 0.0.6
undici: 5.29.0
'@actions/io@1.1.3': {}
'@fastify/busboy@2.1.1': {}
'@vercel/ncc@0.38.4': {}
asynckit@0.4.0: {}
axios@1.15.0:
dependencies:
follow-redirects: 1.15.11
form-data: 4.0.5
proxy-from-env: 2.1.0
transitivePeerDependencies:
- debug
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
delayed-stream@1.0.0: {}
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
es-errors: 1.3.0
gopd: 1.2.0
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
es-set-tostringtag@2.1.0:
dependencies:
es-errors: 1.3.0
get-intrinsic: 1.3.0
has-tostringtag: 1.0.2
hasown: 2.0.2
follow-redirects@1.15.11: {}
form-data@4.0.5:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
es-set-tostringtag: 2.1.0
hasown: 2.0.2
mime-types: 2.1.35
function-bind@1.1.2: {}
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
function-bind: 1.1.2
get-proto: 1.0.1
gopd: 1.2.0
has-symbols: 1.1.0
hasown: 2.0.2
math-intrinsics: 1.1.0
get-proto@1.0.1:
dependencies:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
gopd@1.2.0: {}
has-symbols@1.1.0: {}
has-tostringtag@1.0.2:
dependencies:
has-symbols: 1.1.0
hasown@2.0.2:
dependencies:
function-bind: 1.1.2
math-intrinsics@1.1.0: {}
mime-db@1.52.0: {}
mime-types@2.1.35:
dependencies:
mime-db: 1.52.0
proxy-from-env@2.1.0: {}
tunnel@0.0.6: {}
undici@5.29.0:
dependencies:
'@fastify/busboy': 2.1.1

76
src/gitea.js Normal file
View File

@@ -0,0 +1,76 @@
/**
* Gitea API Client for Pull Request Reviews
*/
class GiteaClient {
constructor(baseUrl, token) {
this.baseUrl = baseUrl.replace(/\/+$/, '');
this.token = token;
this.headers = {
'Authorization': `token ${this.token}`,
'Content-Type': 'application/json'
};
}
async request(path, options = {}) {
const url = `${this.baseUrl}/api/v1${path}`;
const fetchOptions = {
...options,
headers: {
...this.headers,
...options.headers
}
};
const response = await fetch(url, fetchOptions);
if (!response.ok) {
const errorBody = await response.text().catch(() => 'Unable to read error response');
throw new Error(`Gitea API Error: ${response.status} ${response.statusText} - ${errorBody}`);
}
if (response.status === 204) {
return null;
}
return await response.json();
}
/**
* List all review comments on a PR
*/
async listPullRequestComments(owner, repo, index) {
return await this.request(`/repos/${owner}/${repo}/pulls/${index}/comments`);
}
/**
* Create a review comment on a specific line of a file in a PR
*/
async createPullRequestComment(owner, repo, index, { body, path, new_line, old_line, commit_id }) {
return await this.request(`/repos/${owner}/${repo}/pulls/${index}/comments`, {
method: 'POST',
body: JSON.stringify({ body, path, new_line, old_line, commit_id })
});
}
/**
* Update an existing review comment
*/
async updatePullRequestComment(owner, repo, commentId, body) {
return await this.request(`/repos/${owner}/${repo}/pulls/comments/${commentId}`, {
method: 'PATCH',
body: JSON.stringify({ body })
});
}
/**
* Create a review (summary + optional comments) on a PR
*/
async createReview(owner, repo, index, { body, event, comments = [] }) {
return await this.request(`/repos/${owner}/${repo}/pulls/${index}/reviews`, {
method: 'POST',
body: JSON.stringify({ body, event, comments })
});
}
}
module.exports = GiteaClient;

104
src/index.js Normal file
View File

@@ -0,0 +1,104 @@
const core = require('@actions/core');
const fs = require('fs');
const GiteaClient = require('./gitea');
async function run() {
try {
const token = core.getInput('gitea-token', { required: true });
const body = core.getInput('body', { required: true });
const identifier = core.getInput('identifier');
const updateIfExists = core.getInput('update-if-exists') === 'true';
// Review Specific Inputs
const event = core.getInput('event') || 'COMMENT';
let commitId = core.getInput('commit-id');
const path = core.getInput('path');
const newLine = core.getInput('new-line');
const oldLine = core.getInput('old-line');
// Get parameters from environment variables or inputs
const baseUrl = core.getInput('gitea-base-url') || process.env.GITEA_SERVER_URL || process.env.GITHUB_SERVER_URL;
const repository = core.getInput('repository') || process.env.GITEA_REPOSITORY || process.env.GITHUB_REPOSITORY;
let index = core.getInput('issue-number');
if (!baseUrl) {
throw new Error('Gitea base URL not found. Please provide gitea-base-url input or set GITEA_SERVER_URL env.');
}
if (!repository) {
throw new Error('Repository not found. Please provide repository input or set GITEA_REPOSITORY env.');
}
// Load event payload to get PR number and commit ID if not provided
const eventPath = process.env.GITEA_EVENT_PATH || process.env.GITHUB_EVENT_PATH;
if (eventPath && fs.existsSync(eventPath)) {
const eventPayload = JSON.parse(fs.readFileSync(eventPath, 'utf8'));
if (!index) {
index = eventPayload.pull_request ? eventPayload.pull_request.number : (eventPayload.issue ? eventPayload.issue.number : undefined);
}
if (!commitId && eventPayload.pull_request) {
commitId = eventPayload.pull_request.head.sha;
}
}
if (!index) {
throw new Error('Issue or PR number not found. Please provide issue-number input.');
}
const [owner, repo] = repository.split('/');
const client = new GiteaClient(baseUrl, token);
const finalBody = identifier ? `${body}\n\n<!-- gitea-comment-identifier: ${identifier} -->` : body;
// 1. Line-specific Review Comment
if (path && commitId && (newLine || oldLine)) {
let existingComment = null;
if (identifier && updateIfExists) {
core.info(`Searching for existing line-specific comment with identifier: ${identifier}`);
try {
const comments = await client.listPullRequestComments(owner, repo, index);
const tag = `<!-- gitea-comment-identifier: ${identifier} -->`;
// Find comment that matches identifier AND path AND line
existingComment = comments.find(c =>
c.body.includes(tag) &&
c.path === path &&
(newLine ? c.new_line === parseInt(newLine) : c.old_line === parseInt(oldLine))
);
} catch (error) {
core.warning(`Error listing PR comments: ${error.message}`);
}
}
if (existingComment) {
core.info(`Updating existing line-specific comment (ID: ${existingComment.id})`);
await client.updatePullRequestComment(owner, repo, existingComment.id, finalBody);
} else {
core.info(`Creating a new line-specific review comment on ${path}`);
await client.createPullRequestComment(owner, repo, index, {
body: finalBody,
path,
commit_id: commitId,
new_line: newLine ? parseInt(newLine) : undefined,
old_line: oldLine ? parseInt(oldLine) : undefined
});
}
}
// 2. Summary Review
else {
core.info(`Creating a PR review with event: ${event}`);
await client.createReview(owner, repo, index, {
body: finalBody,
event: event
});
}
core.info('Action completed successfully.');
} catch (error) {
core.setFailed(`Action failed with error: ${error.message}`);
}
}
run();