1
0
mirror of https://github.com/hustcer/deepseek-review.git synced 2026-05-13 05:16:05 +08:00

16 Commits
v1.16 ... v1.19

Author SHA1 Message Date
hustcer
938790c65b Bump version to v1.19 2025-07-24 20:05:51 +08:00
Justin Ma
a4d125ecba deps: Upgrade Nu to v0.106 (#186) 2025-07-24 20:01:49 +08:00
hustcer
930e0ae68f fix: Fix getting Nu binary path for Nushell 0.106 2025-07-15 20:16:21 +08:00
Justin Ma
d0b2ea125a fix: Fix "variable not found" error (#185) 2025-07-14 15:28:49 +08:00
hustcer
9852113028 Bump version to v1.18 2025-06-11 15:37:14 +08:00
Justin Ma
9ad3373cd5 chore: Upgrade Nu to 0.105 and pin hustcer/setup-nu to v3.19 (#183) 2025-06-11 15:04:04 +08:00
Justin Ma
981ef03409 feat: Set defalut temperature to 0.3 for code review (#181) 2025-04-29 17:09:53 +08:00
Justin Ma
9852118ba7 deps: Upgrade nutest to v1.1.0 (#179)
* deps: Upgrade nutest to v1.1.0

* fix nutest upgrading
2025-04-29 16:36:21 +08:00
Justin Ma
985f205ae5 chore: Refine diff flag descriptions in docs and scripts (#177) 2025-04-18 11:06:46 +08:00
hustcer
29aec71797 Bump to v1.17 2025-04-11 19:40:59 +08:00
hustcer
957db0afb6 Update CHANGELOG.md 2025-04-11 19:39:49 +08:00
Justin Ma
c53edfe925 doc: Update README.md add output file functionality for local code reviews (#175) 2025-04-11 19:28:26 +08:00
Justin Ma
0298773233 feat: Add write code review result to file support (#172)
* feat: Add write code review result to file support

* feat: Add write code review result to file support

* feat: Add write code review result to file support

* feat: Add write code review result to file support
2025-04-11 11:39:47 +08:00
Justin Ma
9d6bb02502 feat: Add code review for git show head:path/to/file patch command support (#171) 2025-04-10 18:16:39 +08:00
Justin Ma
26e38f1543 fix: Read default include and exclude patterns from config for local code review (#170) 2025-04-10 18:07:34 +08:00
hustcer
8b7262a6b4 [ci skip] 2025-04-09 17:29:21 +08:00
17 changed files with 173 additions and 81 deletions

View File

@@ -15,6 +15,7 @@ on:
branches:
- main
- develop
- feature/test
paths-ignore:
- '**.md'
- 'docs/**'
@@ -46,7 +47,7 @@ jobs:
- name: Checkout Nutest Repo
uses: actions/checkout@v4
with:
ref: v1.0.1
ref: v1.1.0
path: nutest
repository: vyadh/nutest
sparse-checkout: nutest/

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
.env
.env.dev
.env.local
review.md
config.yml
prompts.yaml

View File

@@ -1,6 +1,45 @@
# Changelog
All notable changes to this project will be documented in this file.
## [1.19.0] - 2025-07-23
### Bug Fixes
- Fix "variable not found" error (#185)
- Fix getting Nu binary path for Nushell 0.106
### Deps
- Upgrade Nu to v0.106 (#186)
## [1.18.0] - 2025-06-11
### Features
- Set default `temperature` to **0.3** for code review (#181)
### Miscellaneous Tasks
- Refine diff flag descriptions in docs and scripts (#177)
- Upgrade `Nu` to 0.105 and pin [`hustcer/setup-nu`](https://github.com/hustcer/setup-nu) to v3.19 (#183)
### Deps
- Upgrade `nutest` to v1.1.0 (#179)
## [1.17.0] - 2025-04-11
### Bug Fixes
- Read default `include` and `exclude` patterns from config for local code review (#170)
### Features
All the following changes are for local code review only:
- Add code review for `git show head:path/to/file` patch command support (#171)
- Add write code review result to file by `--output` flag support (#172)
## [1.16.0] - 2025-04-05
### Documentation
@@ -13,9 +52,9 @@ All notable changes to this project will be documented in this file.
### Miscellaneous Tasks
- Add alias guide for `fish`
- Add alias setup guide for `fish`
- Add openrouter.ai config example
- Set minimum required nushell version to v0.103
- Set minimum required `nushell` version to v0.103
### Refactor

View File

@@ -28,7 +28,7 @@ alias cr := code-review
# Used to handle the path separator issue
DEEPSEEK_REVIEW_PATH := parent_directory(justfile())
NU_DIR := parent_directory(`(which nu).path.0`)
NU_DIR := parent_directory(`$nu.current-exe`)
_query_plugin := if os_family() == 'windows' { 'nu_plugin_query.exe' } else { 'nu_plugin_query' }
# To pass arguments to a dependency, put the dependency

View File

@@ -23,6 +23,7 @@
- Review Remote GitHub PRs Directly from Your Local CLI
- Review Commit Changes with DeepSeek for Any Local Repository by CLI
- Support On-demand Changes Generation via `git show`/`git diff` Command for Further Code Review
- Output Code Review Result to Specified File in Markdown Format
- Cross-platform Compatibility: Designed to function seamlessly across all platforms capable of running [Nushell](https://github.com/nushell/nushell)
### Both GH Action & Local
@@ -125,7 +126,7 @@ With this setup, DeepSeek code review will not run automatically upon PR creatio
| max-length | Int | Optional, Maximum length(Unicode width) of the content for review, if the content length exceeds this value, the review will be skipped. Default `0` means no limit. |
| sys-prompt | String | Optional, System prompt corresponding to `$sys_prompt` in the payload, default value see note below |
| user-prompt | String | Optional, User prompt corresponding to `$user_prompt` in the payload, default value see note below |
| temperature | Number | Optional, The temperature for the model to generate the response, between `0` and `2`, default value `1.0` |
| temperature | Number | Optional, The temperature for the model to generate the response, between `0` and `2`, default value `0.3` |
| include-patterns | String | Optional, The comma separated file patterns to include in the code review. No default |
| exclude-patterns | String | Optional, The comma separated file patterns to exclude in the code review. Default to `pnpm-lock.yaml,package-lock.json,*.lock` |
| github-token | String | Optional, The `GITHUB_TOKEN` secret or personal access token to authenticate. Defaults to `${{ github.token }}`. |
@@ -178,8 +179,8 @@ Flags:
-r, --repo <string>: GitHub repo name, e.g. hustcer/deepseek-review
-n, --pr-number <string>: GitHub PR number
-k, --gh-token <string>: Your GitHub token, fallback to GITHUB_TOKEN env var
-t, --diff-to <string>: Diff to git REF
-f, --diff-from <string>: Diff from git REF
-f, --diff-from <string>: Git diff starting commit SHA
-t, --diff-to <string>: Git diff ending commit SHA
-c, --patch-cmd <string>: The `git show` or `git diff` command to get the diff content, for local CR only
-l, --max-length <int>: Maximum length of the content for review, 0 means no limit.
-m, --model <string>: Model name, or read from CHAT_MODEL env var, `deepseek-chat` by default
@@ -189,8 +190,9 @@ Flags:
-u, --user-prompt <string>: Default to $DEFAULT_OPTIONS.USER_PROMPT,
-i, --include <string>: Comma separated file patterns to include in the code review
-x, --exclude <string>: Comma separated file patterns to exclude in the code review
-T, --temperature <float>: Temperature for the model, between `0` and `2`, default value `1.0`
-T, --temperature <float>: Temperature for the model, between `0` and `2`, default value `0.3`
-C, --config <string>: Config file path, default to `config.yml`
-o, --output <string>: Output file path
-h, --help: Display the help message for this command
Parameters:
@@ -242,6 +244,8 @@ To review a local repository:
cr
# Perform code review on the `git diff f536acc` changes in current directory
cr --diff-from f536acc
# Perform code review on the `git diff f536acc` changes and output result to review.md
cr --diff-from f536acc --output review.md
# Perform code review on the `git diff f536acc 0dd0eb5` changes in current directory
cr --diff-from f536acc --diff-to 0dd0eb5
# Review the changes in current directory using the `--patch-cmd` flag

View File

@@ -21,6 +21,7 @@
- 通过本地 CLI 直接审查远程 GitHub PR
- 通过本地 CLI 使用 DeepSeek 审查任何本地仓库的提交变更
- 允许通过自定义 `git show`/`git diff` 命令生成变更记录并进行审查
- 允许将代码审查结果以 Markdown 格式输出到指定文件
- 跨平台:理论上只要能运行 [Nushell](https://github.com/nushell/nushell) 即可使用本工具
### 本地或 GH Action
@@ -123,7 +124,7 @@ jobs:
| max-length | Int | 可选,待审查内容的最大 Unicode 长度, 默认 `0` 表示没有限制,超过非零值则跳过审查 |
| sys-prompt | String | 可选,系统提示词对应入参中的 `$sys_prompt`, 默认值见后文注释 |
| user-prompt | String | 可选,用户提示词对应入参中的 `$user_prompt`, 默认值见后文注释 |
| temperature | Number | 可选,采样温度,介于 `0``2` 之间, 默认值 `1.0` |
| temperature | Number | 可选,采样温度,介于 `0``2` 之间, 默认值 `0.3` |
| include-patterns | String | 可选,代码审查中要包含的以逗号分隔的文件模式,无默认值 |
| exclude-patterns | String | 可选,代码审查中要排除的以逗号分隔的文件模式,默认值为 `pnpm-lock.yaml,package-lock.json,*.lock` |
| github-token | String | 可选,用于访问 API 进行 PR 管理的 GitHub Token默认为 `${{ github.token }}` |
@@ -175,8 +176,8 @@ Flags:
-r, --repo <string>: GitHub repo name, e.g. hustcer/deepseek-review
-n, --pr-number <string>: GitHub PR number
-k, --gh-token <string>: Your GitHub token, fallback to GITHUB_TOKEN env var
-t, --diff-to <string>: Diff to git REF
-f, --diff-from <string>: Diff from git REF
-f, --diff-from <string>: Git diff starting commit SHA
-t, --diff-to <string>: Git diff ending commit SHA
-c, --patch-cmd <string>: The `git show` or `git diff` command to get the diff content, for local CR only
-l, --max-length <int>: Maximum length of the content for review, 0 means no limit.
-m, --model <string>: Model name, or read from CHAT_MODEL env var, `deepseek-chat` by default
@@ -186,8 +187,9 @@ Flags:
-u, --user-prompt <string>: Default to $DEFAULT_OPTIONS.USER_PROMPT,
-i, --include <string>: Comma separated file patterns to include in the code review
-x, --exclude <string>: Comma separated file patterns to exclude in the code review
-T, --temperature <float>: Temperature for the model, between `0` and `2`, default value `1.0`
-T, --temperature <float>: Temperature for the model, between `0` and `2`, default value `0.3`
-C, --config <string>: Config file path, default to `config.yml`
-o, --output <string>: Output file path
-h, --help: Display the help message for this command
Parameters:
@@ -238,6 +240,8 @@ function cr {
cr
# 对本地当前目录所在仓库 `git diff f536acc` 修改内容进行代码审查
cr --diff-from f536acc
# 对本地当前目录所在仓库 `git diff f536acc` 修改内容进行代码审查并将审查结果输出到 review.md
cr --diff-from f536acc --output review.md
# 对本地当前目录所在仓库 `git diff f536acc 0dd0eb5` 修改内容进行代码审查
cr --diff-from f536acc --diff-to 0dd0eb5
# 通过 --patch-cmd 参数对本地当前目录所在仓库变更内容进行审查

View File

@@ -28,7 +28,7 @@ inputs:
description: 'The DeepSeek model to choose for code review.'
temperature:
required: false
default: 1.0
default: 0.3
description: 'The temperature of the model.'
base-url:
required: false
@@ -58,9 +58,9 @@ runs:
using: 'composite'
steps:
- name: Setup Nu
uses: hustcer/setup-nu@v3
uses: hustcer/setup-nu@v3.19
with:
version: 0.103.0
version: 0.106.0
- name: DeepSeek Code Review
shell: nu {0}
@@ -78,7 +78,7 @@ runs:
let includePatterns = '${{ inputs.include-patterns }}'
let excludePatterns = '${{ inputs.exclude-patterns }}'
let maxLength = try { '${{ inputs.max-length }}' | into int } catch { 0 }
let temperature = try { '${{ inputs.temperature }}' | into float } catch { 1.0 }
let temperature = try { '${{ inputs.temperature }}' | into float } catch { 0.3 }
(deepseek-review $token
--model $model
--repo $repo

View File

@@ -17,8 +17,8 @@ settings:
# If the content length exceeds the non-zero limit, the review will be skipped
# Note that it's unicode width not LLM token length
max-length: 0
# The temperature of the model, The value should be between 0 and 2, with default value 1.0
temperature: 1.0
# The temperature of the model, The value should be between 0 and 2, with default value 0.3
temperature: 0.3
# The user prompt name to use for DeepSeek API select from 'prompts.user'
user-prompt: 'default'
# The system prompt name to use for DeepSeek API select from 'prompts.system'

14
cr
View File

@@ -4,8 +4,8 @@
# Description: A wrapper for nu/review.nu as the main entry point of the project.
use nu/config.nu *
use nu/common.nu [hr-line, check-nushell, ECODE]
use nu/review.nu [deepseek-review]
use nu/common.nu [hr-line, check-nushell, ECODE]
# Use DeepSeek AI to review code changes locally or in GitHub Actions
def main [
@@ -14,8 +14,8 @@ def main [
--repo(-r): string, # GitHub repo name, e.g. hustcer/deepseek-review
--pr-number(-n): string, # GitHub PR number
--gh-token(-k): string, # Your GitHub token, fallback to GITHUB_TOKEN env var
--diff-to(-t): string, # Diff to git REF
--diff-from(-f): string, # Diff from git REF
--diff-from(-f): string, # Git diff starting commit SHA
--diff-to(-t): string, # Git diff ending commit SHA
--patch-cmd(-c): string, # The `git show` or `git diff` command to get the diff content, for local CR only
--max-length(-l): int, # Maximum length of the content for review, 0 means no limit.
--model(-m): string, # Model name, or read from CHAT_MODEL env var, `deepseek-chat` by default
@@ -25,8 +25,9 @@ def main [
--user-prompt(-u): string # Default to $DEFAULT_OPTIONS.USER_PROMPT,
--include(-i): string, # Comma separated file patterns to include in the code review
--exclude(-x): string, # Comma separated file patterns to exclude in the code review
--temperature(-T): float, # Temperature for the model, between `0` and `2`, default value `1.0`
--temperature(-T): float, # Temperature for the model, between `0` and `2`, default value `0.3`
--config(-C): string # Config file path, default to `config.yml`
--output(-o): string, # Output file path
] {
check-nushell
@@ -36,8 +37,7 @@ def main [
deepseek-review $token
--repo=$repo
--debug=$debug
--include=$include
--exclude=$exclude
--output=$output
--model=$env.CHAT_MODEL
--base-url=$base_url
--chat-url=$chat_url
@@ -50,5 +50,7 @@ def main [
--sys-prompt=$sys_prompt
--user-prompt=$user_prompt
--temperature=$temperature
--include=($include | default $env.INCLUDE_PATTERNS?)
--exclude=($exclude | default $env.EXCLUDE_PATTERNS?)
)
}

View File

@@ -1,7 +1,7 @@
{
"name": "deepseek-review",
"version": "1.16.0",
"actionVer": "v1.16",
"version": "1.19.0",
"actionVer": "v1.19",
"author": "hustcer",
"license": "MIT",
"github": "https://github.com/hustcer/deepseek-review",

View File

@@ -51,8 +51,8 @@ export def compare-ver [v1: string, v2: string] {
# If you want to compare more parts use the following code:
# for i in 0..([2 ($a | length) ($b | length)] | math max)
for i in 0..2 {
let x = $a | get -i $i | default 0
let y = $b | get -i $i | default 0
let x = $a | get -o $i | default 0
let y = $b | get -o $i | default 0
if $x > $y { return 1 }
if $x < $y { return (-1) }
}
@@ -102,7 +102,7 @@ export def 'from env' []: string -> record {
# Compact the record by removing empty columns
export def compact-record []: record -> record {
let record = $in
let empties = $record | columns | filter {|it| $record | get $it | is-empty }
let empties = $record | columns | where {|it| $record | get $it | is-empty }
$record | reject ...$empties
}
@@ -171,6 +171,6 @@ export def has-ref [
# Notify the user that the `CHAT_TOKEN` hasn't been configured
export const NO_TOKEN_TIP = (
"**Notice:** It looks like you're using [`hustcer/deepseek-review`](https://github.com/hustcer/deepseek-review), but the `CHAT_TOKEN` hasn't" +
"been configured in your repo's Variables/Secrets. Please ensure this token is set for proper functionality. For step-by-step guidance, refer" +
"**Notice:** It looks like you're using [`hustcer/deepseek-review`](https://github.com/hustcer/deepseek-review), but the `CHAT_TOKEN` hasn't " +
"been configured in your repo's **Variables/Secrets**. Please ensure this token is set for proper functionality. For step-by-step guidance, refer " +
"to the **CHAT_TOKEN Config** section of [README](https://github.com/hustcer/deepseek-review/blob/main/README.md#code-review-with-github-action).")

View File

@@ -28,15 +28,15 @@ def check-prompts [options: record] {
# Check if the specified type of prompt key exists in the config.yml file
def check-prompt [options: record, type: string] {
let prompt_key = $options.settings | get -i $'($type)-prompt' | default ''
let prompt_key = $options.settings | get -o $'($type)-prompt' | default ''
if ($prompt_key | is-empty) {
print $'(ansi r)The ($type) prompt key is missing in `settings.($type)-prompt` config.yml file.(ansi reset)'
exit $ECODE.INVALID_PARAMETER
}
let prompt = $options.prompts | get -i $type
let prompt = $options.prompts | get -o $type
| default []
| where name == $prompt_key
| get -i 0.prompt
| get -o 0.prompt
if ($prompt | is-empty) {
print $'The ($type) prompt (ansi r)($prompt_key)(ansi reset) is missing in `prompts.($type)` of config.yml file.'
exit $ECODE.INVALID_PARAMETER
@@ -59,11 +59,11 @@ def check-providers [options: record] {
exit $ECODE.INVALID_PARAMETER
}
# Each provider should have name, token and models field
$options.providers | each {|it|
let empties = [name token models] | filter { |field| $it | get -i $field | is-empty }
$options.providers | each {|p|
let empties = [name token models] | where { |field| $p | get -o $field | is-empty }
if ($empties | is-not-empty) {
print $'Field (ansi r)`($empties | str join ,)`(ansi reset) should not be empty for provider:'
$it | table -e -t psql | print
$p | table -e -t psql | print
exit $ECODE.INVALID_PARAMETER
}
}
@@ -105,7 +105,7 @@ def get-model-envs [settings: record, model?: string = ''] {
let provider = $settings.providers
| default []
| where name == $name
| get -i 0
| get -o 0
| default {}
let model_name = $provider.models
| default []
@@ -114,7 +114,7 @@ def get-model-envs [settings: record, model?: string = ''] {
} else {
$it.name == $model or $it.alias? == $model }
}
| get -i 0.name
| get -o 0.name
| default $model
{ CHAT_TOKEN: $provider.token?, BASE_URL: $provider.base-url?, CHAT_URL: $provider.chat-url?, CHAT_MODEL: $model_name }
@@ -132,12 +132,12 @@ export def --env config-load [
let user_prompt = $all_settings.prompts?.user?
| default []
| where name == ($settings.user-prompt? | default '')
| get -i 0.prompt
| get -o 0.prompt
let system_prompt = $all_settings.prompts?.system?
| default []
| where name == ($settings.system-prompt? | default '')
| get -i 0.prompt
| get -o 0.prompt
let model_envs = get-model-envs $all_settings $model

View File

@@ -4,7 +4,7 @@
# Description: Diff command for DeepSeek-Review
use common.nu [GITHUB_API_BASE, ECODE, git-check, has-ref]
use util.nu [generate-include-regex, generate-exclude-regex, prepare-awk]
use util.nu [generate-include-regex, generate-exclude-regex, prepare-awk, is-safe-git]
# If the PR title or body contains any of these keywords, skip the review
const IGNORE_REVIEW_KEYWORDS = ['skip review' 'skip cr']

View File

@@ -44,7 +44,7 @@ const HTTP_HEADERS = [User-Agent curl/8.9]
const DEFAULT_OPTIONS = {
MODEL: 'deepseek-chat',
TEMPERATURE: 1.0,
TEMPERATURE: 0.3,
BASE_URL: 'https://api.deepseek.com',
USER_PROMPT: 'Please review the following code changes:',
SYS_PROMPT: 'You are a professional code review assistant responsible for analyzing code changes in GitHub Pull Requests. Identify potential issues such as code style violations, logical errors, security vulnerabilities, and provide improvement suggestions. Clearly list the problems and recommendations in a concise manner.',
@@ -55,10 +55,11 @@ export def --env deepseek-review [
token?: string, # Your DeepSeek API token, fallback to CHAT_TOKEN env var
--debug(-d), # Debug mode
--repo(-r): string, # GitHub repo name, e.g. hustcer/deepseek-review, or local repo path / alias
--output(-o): string, # Output file path
--pr-number(-n): string, # GitHub PR number
--gh-token(-k): string, # Your GitHub token, fallback to GITHUB_TOKEN env var
--diff-to(-t): string, # Diff to git REF
--diff-from(-f): string, # Diff from git REF
--diff-to(-t): string, # Git diff ending commit SHA
--diff-from(-f): string, # Git diff starting commit SHA
--patch-cmd(-c): string, # The `git show` or `git diff` command to get the diff content, for local CR only
--max-length(-l): int, # Maximum length of the content for review, 0 means no limit.
--model(-m): string, # Model name, or read from CHAT_MODEL env var, `deepseek-chat` by default
@@ -68,21 +69,25 @@ export def --env deepseek-review [
--user-prompt(-u): string # Default to $DEFAULT_OPTIONS.USER_PROMPT,
--include(-i): string, # Comma separated file patterns to include in the code review
--exclude(-x): string, # Comma separated file patterns to exclude in the code review
--temperature(-T): float, # Temperature for the model, between `0` and `2`, default value `1.0`
--temperature(-T): float, # Temperature for the model, between `0` and `2`, default value `0.3`
]: nothing -> nothing {
$env.config.table.mode = 'psql'
let local_repo = $env.PWD
let write_file = ($output | is-not-empty)
let is_action = ($env.GITHUB_ACTIONS? == 'true')
let stream = if $is_action { false } else { true }
let token = $token | default $env.CHAT_TOKEN?
let repo = $repo | default $env.DEFAULT_GITHUB_REPO?
let CHAT_HEADER = [Authorization $'Bearer ($token)']
let stream = if $is_action or $write_file { false } else { true }
let model = $model | default $env.CHAT_MODEL? | default $DEFAULT_OPTIONS.MODEL
let base_url = $base_url | default $env.BASE_URL? | default $DEFAULT_OPTIONS.BASE_URL
let url = $chat_url | default $env.CHAT_URL? | default $'($base_url)/chat/completions'
let max_length = try { $max_length | default ($env.MAX_LENGTH? | default 0 | into int) } catch { 0 }
let temperature = try { $temperature | default $env.TEMPERATURE? | default $DEFAULT_OPTIONS.TEMPERATURE | into float } catch { $DEFAULT_OPTIONS.TEMPERATURE }
# Determine output mode
let output_mode = if $is_action { 'action' } else if ($output | is-not-empty) { 'file' } else { 'console' }
validate-temperature $temperature
let setting = {
repo: $repo,
@@ -109,7 +114,7 @@ export def --env deepseek-review [
print $hint; print -n (char nl)
if ($pr_number | is-empty) {
print 'Current Settings:'; hr-line
$setting | compact-record | reject -i repo | print; print -n (char nl)
$setting | compact-record | reject -o repo | print; print -n (char nl)
}
let content = (
@@ -146,27 +151,59 @@ export def --env deepseek-review [
print $'✖️ Code review failedError: '; hr-line; print $response
exit $ECODE.SERVER_ERROR
}
let message = $response | get -i choices.0.message
let message = $response | get -o choices.0.message
let reason = $message | coalesce-reasoning
let review = $message.content? | default ($response | get -i message.content)
let review = $message.content? | default ($response | get -o message.content)
let result = ['<details>' '<summary> Reasoning Details</summary>' $reason "</details>\n" $review] | str join "\n"
if ($review | is-empty) {
print $'✖️ Code review failedNo review result returned from ($base_url) ...'
exit $ECODE.SERVER_ERROR
}
let result = if ($reason | is-empty) { $review } else { $result }
if not $is_action {
print $'Code Review Result:'; hr-line; print $result
} else {
post-comments-to-pr $repo $pr_number $result
print $'✅ Code review finishedPR (ansi g)#($pr_number)(ansi reset) review result was posted as a comment.'
match $output_mode {
'action' => {
post-comments-to-pr $repo $pr_number $result
print $'✅ Code review finishedPR (ansi g)#($pr_number)(ansi reset) review result was posted as a comment.'
}
'file' => { write-review-to-file $output $setting $result $response }
_ => { print $'Code Review Result:'; hr-line; print $result }
}
if ($response.usage? | is-not-empty) {
print $'(char nl)Token Usage:'; hr-line
$response.usage? | table -e | print
}
}
# Write the code review result to a file
def write-review-to-file [
file: string, # Output file path
setting: record, # Review settings
result: string, # Review result
response: record, # DeepSeek API response
] {
let file = (if not ($file | str ends-with '.md') { $'($file).md' } else { $file })
let token_usage = if ($response.usage? | is-empty) { [] } else {
['## Token Usage', '', ($response.usage? | transpose key val | to md --pretty)]
}
# Generate content sections
let content_sections = [
'# DeepSeek Code Review Result', ''
$"Generated at: (date now | format date '%Y/%m/%d %H:%M:%S')", ''
'## Code Review Settings', ''
($setting | compact-record | reject -o repo | transpose key val | to md --pretty)
'', '## Review Detail', '', $result, '', ...$token_usage
]
try {
$content_sections | str join (char nl) | save --force $file
print $'Code Review Result saved to (ansi g)($file)(ansi reset)'
} catch {|err|
print $'(ansi r)Failed to save review result: (ansi reset)'
$err | table -e | print
}
}
# Validate the DeepSeek API token
def validate-token [token?: string, --pr-number: string, --repo: string] {
if ($token | is-empty) {
@@ -227,10 +264,10 @@ def streaming-output [
| try { lines } catch { print $'(ansi r)Error Happened ...(ansi reset)'; exit $ECODE.SERVER_ERROR }
| each {|line|
if ($line | is-empty) { return }
if ($IGNORED_MESSAGES | get -i $line | default false) { return }
if ($IGNORED_MESSAGES | get -o $line | default false) { return }
let $last = $line | parse-line
if $debug { $last | to json | kv set last-reply }
$last | get -i choices.0.delta | default ($last | get -i message) | if ($in | is-not-empty) {
$last | get -o choices.0.delta | default ($last | get -o message) | if ($in | is-not-empty) {
let delta = $in
if ($delta | coalesce-reasoning | is-not-empty) { kv set reasoning ((kv get reasoning) + 1) }
if (kv get reasoning) == 1 { print $'(char nl)Reasoning Details:'; hr-line }
@@ -242,7 +279,7 @@ def streaming-output [
if $debug and (kv get last-reply | is-not-empty) {
print $'(char nl)(char nl)Model & Token Usage:'; hr-line
kv get last-reply | from json | select -i model usage | table -e | print
kv get last-reply | from json | select -o model usage | table -e | print
}
}

View File

@@ -114,7 +114,7 @@ export def is-safe-git [cmd: string] {
let normalized_cmd = ($cmd | str trim | str downcase)
# Define allowed command patterns with named capture groups for better validation
let git_cmd_pattern = '^git\s+(show|diff)(?:\s+(?:[a-zA-Z0-9_\-\.~/]+)){0,3}(?:\s+(?::[!]?)?[a-zA-Z0-9_\-\.\*\/]+){0,2}$'
let git_cmd_pattern = '^git\s+(show|diff)(?:\s+(?:[a-zA-Z0-9_\-\.~/]+(?::[a-zA-Z0-9_\-\.\*\/]+)?)){0,3}(?:\s+(?::[!]?)?[a-zA-Z0-9_\-\.\*\/]+){0,2}$'
if ($normalized_cmd | find -r $git_cmd_pattern | is-empty) {
print $'(ansi r)Invalid git command format. (ansi g)Only simple `git show` or `git diff` commands are allowed.(ansi reset)'

View File

@@ -1,39 +1,40 @@
use std/assert
use std/testing *
use ../nu/common.nu [
compare-ver, 'from env', is-installed, has-ref,
git-check, compact-record, is-repo, windows?, mac?,
]
#[test]
@test
def 'compare-verv1.0.0 is greater than v0.999.0' [] {
assert equal (compare-ver 1.0.0 0.999.0) 1
assert equal (compare-ver v1.0.0 v0.999.0) 1
}
#[test]
@test
def 'compare-verv1.0.1 is equal to v1.0.1' [] {
assert equal (compare-ver 1.0.1 1.0.1) 0
}
#[test]
@test
def 'compare-verv1.0.0 is equal to v1' [] {
assert equal (compare-ver v1.0.0 v1) 0
}
#[test]
@test
def 'compare-verv1.0.1 is greater than v1' [] {
assert equal (compare-ver v1.0.1 v1) 1
}
#[test]
@test
def 'compare-verv1.0.1 is lower than v1.1.0' [] {
assert less (compare-ver 1.0.1 v1.1) 0
assert equal (compare-ver 1.0.1 1.1.0) (-1)
}
#[test]
@test
def 'from-env.env load should work' [] {
open tests/resources/.env.test | from env | load-env
assert equal $env.CHAT_MODEL deepseek-chat
@@ -43,35 +44,35 @@ def 'from-env.env load should work' [] {
assert equal $env.USER_PROMPT 'Please review the following code changes'
}
#[test]
@test
def 'is-installedbinary install check should work' [] {
assert equal (is-installed git) true
assert equal (is-installed abc) false
}
#[test]
@test
def 'has-refgit repo should has HEAD ref' [] {
assert equal (has-ref HEAD) true
assert equal (has-ref 0000) false
}
#[test]
@test
def 'is-repocurrent dir is a git repo' [] {
assert equal (is-repo) true
}
#[test]
@test
def 'git-checkcurrent dir is a git repo' [] {
assert equal (git-check (pwd) --check-repo=1) true
}
#[test]
@test
def 'compact-recordshould work as expected' [] {
assert equal ({a: null, b: '', c: 'abc' } | compact-record) { c: 'abc' }
assert equal ({a: null, b: 0, c: 1, e: { f: 'g' } } | compact-record) { b: 0, c: 1, e: { f: 'g' } }
}
#[test]
@test
def 'OS check should work as expected' [] {
# `$env.RUNNER_OS` Possible values are Linux, Windows, or macOS in GitHub Actions
match $nu.os-info.name {

View File

@@ -1,12 +1,13 @@
use std/assert
use std/testing *
use ../nu/diff.nu [get-diff]
use ../nu/util.nu [is-safe-git, prepare-awk, generate-include-regex, generate-exclude-regex]
# Get the unicode width of the input string
def get-uw [] { $in | str stats | get unicode-width }
#[before-all]
@before-all
def setup [] {
let awk_bin = (prepare-awk)
let patch = open -r tests/resources/diff.patch
@@ -14,7 +15,7 @@ def setup [] {
{ patch: $patch, awk: $awk_bin, SHA: 22e7b71 }
}
#[test]
@test
def 'is-safe-gitshould work as expected' [] {
assert equal (is-safe-git 'git diff') true
assert equal (is-safe-git 'git show') true
@@ -41,9 +42,11 @@ def 'is-safe-gitshould work as expected' [] {
assert equal (is-safe-git 'git diff f536acc 0dd0eb5 :!nu/* >> out.txt') false
assert equal (is-safe-git 'git diff f536acc 0dd0eb5 :!nu/* < in.txt') false
assert equal (is-safe-git 'git diff f536acc 0dd0eb5 :!nu/* << in.txt') false
assert equal (is-safe-git 'git show head:nu/common.nu') true
assert equal (is-safe-git 'git show HEAD:nu/common.nu') true
}
#[test]
@test
def 'generate-include-regexshould work as expected' [] {
let patch = $in.patch
let awk_bin = $in.awk
@@ -53,7 +56,7 @@ def 'generate-include-regexshould work as expected' [] {
assert equal ($patch | ^$awk_bin (generate-include-regex [.env*, *.md, nu/*]) | get-uw) 6871
}
#[test]
@test
def 'generate-exclude-regexshould work as expected' [] {
let patch = $in.patch
let awk_bin = $in.awk
@@ -61,7 +64,7 @@ def 'generate-exclude-regexshould work as expected' [] {
assert equal ($patch | ^$awk_bin (generate-exclude-regex [.env*, *.md, nu/*]) | get-uw) (1350 + 99)
}
#[test]
@test
def 'both include and exclude should work as expected' [] {
let patch = $in.patch
let awk_bin = $in.awk
@@ -71,7 +74,7 @@ def 'both include and exclude should work as expected' [] {
| get-uw) 2576
}
#[test]
@test
def 'both exclude and include should work as expected' [] {
let patch = $in.patch
let awk_bin = $in.awk
@@ -81,7 +84,7 @@ def 'both exclude and include should work as expected' [] {
| get-uw) 2576
}
#[test]
@test
def 'get-diffget patch from remote PR should work' [] {
$env.GH_TOKEN = $env.GITHUB_TOKEN?
const repo = 'hustcer/deepseek-review'
@@ -91,7 +94,7 @@ def 'get-diffget patch from remote PR should work' [] {
| str join "\n" | get-uw) 7923
}
#[test]
@test
def 'get-diffget patch from remote PR with include should work' [] {
$env.GH_TOKEN = $env.GITHUB_TOKEN?
const repo = 'hustcer/deepseek-review'
@@ -100,7 +103,7 @@ def 'get-diffget patch from remote PR with include should work' [] {
assert equal ($patch | get-uw) 2576
}
#[test]
@test
def 'get-diffget patch from remote PR with exclude should work' [] {
$env.GH_TOKEN = $env.GITHUB_TOKEN?
const repo = 'hustcer/deepseek-review'
@@ -109,7 +112,7 @@ def 'get-diffget patch from remote PR with exclude should work' [] {
assert equal ($patch | get-uw) 555
}
#[test]
@test
def 'get-diffget patch from remote PR with exclude & include should work' [] {
$env.GH_TOKEN = $env.GITHUB_TOKEN?
const repo = 'hustcer/deepseek-review'