@woodpeck/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +205 -0
- package/bin/wood +9 -0
- package/install.js +117 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Woodpeck 🐦 / Wood 啄木鸟
|
|
2
|
+
|
|
3
|
+
基于 Git diff 的智能代码审查 CLI 工具,使用 Rust 编写。
|
|
4
|
+
|
|
5
|
+
## ✨ 特性
|
|
6
|
+
|
|
7
|
+
- 🔴 **严重级别**: unwrap()、unsafe 代码块、硬编码密钥等
|
|
8
|
+
- 🟡 **中等级别**: TODO/FIXME、未使用变量、资源泄露等
|
|
9
|
+
- 🟢 **初级别**: 魔法数字、长函数、代码风格等
|
|
10
|
+
- 📊 **多格式输出**: Table/JSON/Markdown/SARIF
|
|
11
|
+
- 🔧 **CI/CD 集成**: 支持失败阈值控制
|
|
12
|
+
|
|
13
|
+
## 📦 安装
|
|
14
|
+
|
|
15
|
+
### 方式 1: 直接下载二进制文件
|
|
16
|
+
|
|
17
|
+
#### macOS
|
|
18
|
+
```bash
|
|
19
|
+
# Intel
|
|
20
|
+
curl -L -o wood.tar.gz https://github.com/yourusername/woodpeck/releases/latest/download/wood-macos-x64.tar.gz
|
|
21
|
+
tar xzf wood.tar.gz
|
|
22
|
+
sudo mv wood /usr/local/bin/
|
|
23
|
+
|
|
24
|
+
# Apple Silicon
|
|
25
|
+
curl -L -o wood.tar.gz https://github.com/yourusername/woodpeck/releases/latest/download/wood-macos-arm64.tar.gz
|
|
26
|
+
tar xzf wood.tar.gz
|
|
27
|
+
sudo mv wood /usr/local/bin/
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
#### Linux
|
|
31
|
+
```bash
|
|
32
|
+
curl -L -o wood.tar.gz https://github.com/yourusername/woodpeck/releases/latest/download/wood-linux-x64.tar.gz
|
|
33
|
+
tar xzf wood.tar.gz
|
|
34
|
+
sudo mv wood /usr/local/bin/
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
#### Windows
|
|
38
|
+
下载 [wood-windows-x64.exe](https://github.com/yourusername/woodpeck/releases) 并重命名为 `wood.exe`,添加到 PATH。
|
|
39
|
+
|
|
40
|
+
### 方式 2: 通过 npm 安装
|
|
41
|
+
```bash
|
|
42
|
+
npm install -g @woodpeck/cli
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 方式 3: 从源码编译
|
|
46
|
+
```bash
|
|
47
|
+
git clone https://github.com/yourusername/woodpeck.git
|
|
48
|
+
cd woodpeck
|
|
49
|
+
cargo build --release
|
|
50
|
+
sudo cp target/release/woodpeck /usr/local/bin/wood
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 🚀 使用
|
|
54
|
+
|
|
55
|
+
### 查看帮助
|
|
56
|
+
```bash
|
|
57
|
+
wood -h
|
|
58
|
+
wood compare -h
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 基础分析
|
|
62
|
+
```bash
|
|
63
|
+
# 对比两个分支
|
|
64
|
+
wood compare main feature-branch
|
|
65
|
+
|
|
66
|
+
# 指定仓库路径
|
|
67
|
+
wood compare main feature-branch --path=/path/to/repo
|
|
68
|
+
|
|
69
|
+
# 只显示严重问题
|
|
70
|
+
wood compare main feature-branch --severity=high
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 输出格式
|
|
74
|
+
```bash
|
|
75
|
+
# JSON 格式
|
|
76
|
+
wood compare main feature-branch --format=json
|
|
77
|
+
|
|
78
|
+
# Markdown 格式(适合 PR 描述)
|
|
79
|
+
wood compare main feature-branch --format=markdown
|
|
80
|
+
|
|
81
|
+
# 导出到文件
|
|
82
|
+
wood compare main feature-branch --output=report.md
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### CI/CD 集成
|
|
86
|
+
```bash
|
|
87
|
+
# 如果有严重问题则 CI 失败
|
|
88
|
+
wood compare main feature-branch --fail-on=high:0
|
|
89
|
+
|
|
90
|
+
# 允许最多 5 个中等问题
|
|
91
|
+
wood compare main feature-branch --fail-on=medium:5
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 📊 输出示例
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
Woodpeck Analysis Report
|
|
98
|
+
main → feature/test
|
|
99
|
+
|
|
100
|
+
+-----------+---------+------+---------------------+----------------------------------+
|
|
101
|
+
| Severity | File | Line | Rule | Description |
|
|
102
|
+
+=======================================================================================+
|
|
103
|
+
| 🔴 High | main.rs | 3 | RUST_UNWRAP_IN_PROD | Using unwrap() can cause panics |
|
|
104
|
+
|-----------+---------+------+---------------------+----------------------------------|
|
|
105
|
+
| 🟡 Medium | main.rs | 10 | RUST_TODO | TODO comment found |
|
|
106
|
+
|-----------+---------+------+---------------------+----------------------------------|
|
|
107
|
+
| 🟢 Low | main.rs | 13 | RUST_MAGIC_NUMBER | Unnamed numeric constant |
|
|
108
|
+
+-----------+---------+------+---------------------+----------------------------------+
|
|
109
|
+
|
|
110
|
+
Summary:
|
|
111
|
+
Total files: 1
|
|
112
|
+
Total issues: 3
|
|
113
|
+
🔴 High: 1
|
|
114
|
+
🟡 Medium: 1
|
|
115
|
+
🟢 Low: 1
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## ⚙️ 配置
|
|
119
|
+
|
|
120
|
+
创建 `woodpeck.toml` 配置文件:
|
|
121
|
+
|
|
122
|
+
```toml
|
|
123
|
+
# 默认只显示严重和中等问题
|
|
124
|
+
default_severity = "medium"
|
|
125
|
+
|
|
126
|
+
# 默认 JSON 格式
|
|
127
|
+
default_format = "json"
|
|
128
|
+
|
|
129
|
+
[output]
|
|
130
|
+
color = "auto"
|
|
131
|
+
show_summary = true
|
|
132
|
+
|
|
133
|
+
[rules.overrides.RUST_UNWRAP_IN_PROD]
|
|
134
|
+
enabled = true
|
|
135
|
+
severity = "high"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## 🔌 GitHub Actions 集成
|
|
139
|
+
|
|
140
|
+
```yaml
|
|
141
|
+
name: Code Review
|
|
142
|
+
|
|
143
|
+
on: [pull_request]
|
|
144
|
+
|
|
145
|
+
jobs:
|
|
146
|
+
woodpeck:
|
|
147
|
+
runs-on: ubuntu-latest
|
|
148
|
+
steps:
|
|
149
|
+
- uses: actions/checkout@v3
|
|
150
|
+
with:
|
|
151
|
+
fetch-depth: 0
|
|
152
|
+
|
|
153
|
+
- name: Download Woodpeck
|
|
154
|
+
run: |
|
|
155
|
+
curl -L -o wood.tar.gz https://github.com/yourusername/woodpeck/releases/latest/download/wood-linux-x64.tar.gz
|
|
156
|
+
tar xzf wood.tar.gz
|
|
157
|
+
chmod +x wood
|
|
158
|
+
sudo mv wood /usr/local/bin/
|
|
159
|
+
|
|
160
|
+
- name: Run Woodpeck
|
|
161
|
+
run: wood compare origin/main HEAD --fail-on=high:0
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## 🛠️ 开发
|
|
165
|
+
|
|
166
|
+
### 构建
|
|
167
|
+
```bash
|
|
168
|
+
cargo build --release
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 测试
|
|
172
|
+
```bash
|
|
173
|
+
cargo test
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 运行
|
|
177
|
+
```bash
|
|
178
|
+
cargo run -- compare main feature-branch
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## 📋 支持的规则
|
|
182
|
+
|
|
183
|
+
| 规则 ID | 级别 | 描述 |
|
|
184
|
+
|---------|------|------|
|
|
185
|
+
| RUST_UNWRAP_IN_PROD | 🔴 High | 生产代码中使用 unwrap() |
|
|
186
|
+
| RUST_UNSAFE_BLOCK | 🔴 High | unsafe 代码块 |
|
|
187
|
+
| JS_DANGEROUS_EVAL | 🔴 High | 使用 eval() |
|
|
188
|
+
| RUST_TODO | 🟡 Medium | TODO/FIXME/XXX 注释 |
|
|
189
|
+
| RUST_UNUSED_VARIABLE | 🟡 Medium | 未使用的变量 |
|
|
190
|
+
| RUST_MAGIC_NUMBER | 🟢 Low | 魔法数字 |
|
|
191
|
+
| RUST_LONG_FUNCTION | 🟢 Low | 过长的函数 |
|
|
192
|
+
|
|
193
|
+
## 📄 许可证
|
|
194
|
+
|
|
195
|
+
MIT
|
|
196
|
+
|
|
197
|
+
## 🤝 贡献
|
|
198
|
+
|
|
199
|
+
欢迎提交 Issue 和 PR!
|
|
200
|
+
|
|
201
|
+
## 🙏 鸣谢
|
|
202
|
+
|
|
203
|
+
- [clap](https://github.com/clap-rs/clap) - CLI 框架
|
|
204
|
+
- [git2](https://github.com/rust-lang/git2-rs) - Git 操作
|
|
205
|
+
- [regex](https://github.com/rust-lang/regex) - 正则表达式
|
package/bin/wood
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
|
|
5
|
+
const binary = path.join(__dirname, process.platform === 'win32' ? 'wood.exe' : 'wood');
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
|
|
8
|
+
const child = spawn(binary, args, { stdio: 'inherit' });
|
|
9
|
+
child.on('exit', (code) => process.exit(code));
|
package/install.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const https = require('https');
|
|
7
|
+
const { platform, arch } = process;
|
|
8
|
+
|
|
9
|
+
const VERSION = require('./package.json').version;
|
|
10
|
+
const REPO = 'yourusername/woodpeck';
|
|
11
|
+
|
|
12
|
+
function getBinaryName() {
|
|
13
|
+
const platformMap = {
|
|
14
|
+
'darwin': 'macos',
|
|
15
|
+
'linux': 'linux',
|
|
16
|
+
'win32': 'windows'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const archMap = {
|
|
20
|
+
'x64': 'x64',
|
|
21
|
+
'arm64': 'arm64'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const platformName = platformMap[platform];
|
|
25
|
+
const archName = archMap[arch] || 'x64';
|
|
26
|
+
|
|
27
|
+
if (!platformName) {
|
|
28
|
+
throw new Error(`不支持的平台: ${platform}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (platform === 'win32') {
|
|
32
|
+
return `wood-${platformName}-${archName}.exe`;
|
|
33
|
+
}
|
|
34
|
+
return `wood-${platformName}-${archName}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getDownloadUrl() {
|
|
38
|
+
const binaryName = getBinaryName();
|
|
39
|
+
return `https://github.com/${REPO}/releases/download/v${VERSION}/${binaryName}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function downloadFile(url, dest) {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
const file = fs.createWriteStream(dest);
|
|
45
|
+
https.get(url, (response) => {
|
|
46
|
+
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
47
|
+
downloadFile(response.headers.location, dest)
|
|
48
|
+
.then(resolve)
|
|
49
|
+
.catch(reject);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (response.statusCode !== 200) {
|
|
54
|
+
reject(new Error(`下载失败,状态码: ${response.statusCode}`));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
response.pipe(file);
|
|
59
|
+
file.on('finish', () => {
|
|
60
|
+
file.close();
|
|
61
|
+
resolve();
|
|
62
|
+
});
|
|
63
|
+
}).on('error', (err) => {
|
|
64
|
+
fs.unlink(dest, () => {});
|
|
65
|
+
reject(err);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function install() {
|
|
71
|
+
try {
|
|
72
|
+
const binaryName = getBinaryName();
|
|
73
|
+
const binDir = path.join(__dirname, 'bin');
|
|
74
|
+
const binaryPath = path.join(binDir, platform === 'win32' ? 'wood.exe' : 'wood');
|
|
75
|
+
|
|
76
|
+
// 创建 bin 目录
|
|
77
|
+
if (!fs.existsSync(binDir)) {
|
|
78
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 如果本地已存在二进制文件,跳过下载
|
|
82
|
+
if (fs.existsSync(binaryPath)) {
|
|
83
|
+
console.log('✓ 二进制文件已存在,跳过下载');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log(`正在下载 wood ${VERSION}...`);
|
|
88
|
+
console.log(`平台: ${platform} (${arch})`);
|
|
89
|
+
console.log(`目标: ${binaryName}`);
|
|
90
|
+
|
|
91
|
+
// 开发阶段:从本地复制
|
|
92
|
+
const localBinary = path.join(__dirname, 'target', 'release', 'wood');
|
|
93
|
+
if (fs.existsSync(localBinary)) {
|
|
94
|
+
console.log('使用本地构建的二进制文件...');
|
|
95
|
+
fs.copyFileSync(localBinary, binaryPath);
|
|
96
|
+
} else {
|
|
97
|
+
// 生产阶段:从 GitHub 下载
|
|
98
|
+
const url = getDownloadUrl();
|
|
99
|
+
console.log(`下载地址: ${url}`);
|
|
100
|
+
await downloadFile(url, binaryPath);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 设置执行权限
|
|
104
|
+
if (platform !== 'win32') {
|
|
105
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log('✓ 安装完成!');
|
|
109
|
+
console.log(`运行: wood --help`);
|
|
110
|
+
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error('安装失败:', error.message);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
install();
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@woodpeck/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "基于 Git diff 的代码审查工具 - 发现代码中的潜在问题",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"wood": "./bin/wood",
|
|
8
|
+
"woodpeck": "./bin/wood"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"postinstall": "node install.js",
|
|
12
|
+
"test": "echo \"Binary only package, no tests\""
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"code-review",
|
|
16
|
+
"git",
|
|
17
|
+
"static-analysis",
|
|
18
|
+
"rust",
|
|
19
|
+
"cli",
|
|
20
|
+
"code-quality"
|
|
21
|
+
],
|
|
22
|
+
"author": "Woodpeck Team",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/jxhhdx/Woodpeck.git"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/jxhhdx/Woodpeck/issues"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/jxhhdx/Woodpeck#readme",
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=14.0.0"
|
|
34
|
+
},
|
|
35
|
+
"os": [
|
|
36
|
+
"darwin",
|
|
37
|
+
"linux",
|
|
38
|
+
"win32"
|
|
39
|
+
],
|
|
40
|
+
"cpu": [
|
|
41
|
+
"x64",
|
|
42
|
+
"arm64"
|
|
43
|
+
],
|
|
44
|
+
"files": [
|
|
45
|
+
"bin/",
|
|
46
|
+
"install.js",
|
|
47
|
+
"README.md",
|
|
48
|
+
"LICENSE"
|
|
49
|
+
]
|
|
50
|
+
}
|