PageSourceSearch

https://ncat-bot-docs-delta.vercel.app/assets/index.html-DFdRQIPf.js

js ncat-bot-docs-delta.vercel.app collected 2026-10-03 07:22:51 UTC 57,537 bytes, 107 lines download raw bytes

1import{_ as i,c as a,a as n,o as t}from"./app-C1L7GXRS.js";const l={};function h(e,s){return t(),a("div",null,s[0]||(s[0]=[n(`<blockquote><p>IDE 配置、调试技巧、CI/CD 本地模拟、NapCat 本地部署与常见问题排查。</p></blockquote><hr><h2 id="目录" tabindex="-1"><a class="header-anchor" href="#目录"><span>目录</span></a></h2><ul><li><a href="#ide-%E9%85%8D%E7%BD%AEvscode">IDE 配置(VSCode)</a></li><li><a href="#%E8%B0%83%E8%AF%95%E9%85%8D%E7%BD%AE">调试配置</a></li><li><a href="#pre-commit-%E8%AF%A6%E8%A7%A3">pre-commit 详解</a></li><li><a href="#cicd-%E6%9C%AC%E5%9C%B0%E6%A8%A1%E6%8B%9F">CI/CD 本地模拟</a></li><li><a href="#napcat-%E6%9C%AC%E5%9C%B0%E9%83%A8%E7%BD%B2">NapCat 本地部署</a></li><li><a href="#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5">常见问题排查</a></li></ul><hr><h2 id="ide-配置-vscode" tabindex="-1"><a class="header-anchor" href="#ide-配置-vscode"><span>IDE 配置(VSCode)</span></a></h2><h3 id="推荐扩展" tabindex="-1"><a class="header-anchor" href="#推荐扩展"><span>推荐扩展</span></a></h3><p>在 VSCode 中安装以下扩展以获得最佳开发体验:</p><table><thead><tr><th>扩展</th><th>ID</th><th>用途</th></tr></thead><tbody><tr><td>Python</td><td><code>ms-python.python</code></td><td>Python 语言支持</td></tr><tr><td>Pylance</td><td><code>ms-python.vscode-pylance</code></td><td>类型检查 + 智能补全</td></tr><tr><td>Ruff</td><td><code>charliermarsh.ruff</code></td><td>Lint + 格式化</td></tr><tr><td>Even Better TOML</td><td><code>tamasfe.even-better-toml</code></td><td>TOML 语法高亮</td></tr></tbody></table><h3 id="推荐-settings-json" tabindex="-1"><a class="header-anchor" href="#推荐-settings-json"><span>推荐 settings.json</span></a></h3><p>在项目根目录创建 <code>.vscode/settings.json</code>(如尚不存在):</p><div class="language-jsonc line-numbers-mode" data-ext="jsonc" data-title="jsonc"><button class="copy" title="复制代码" data-copied="已复制"></button><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">
1{</span></span>
2<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;">    // Python 环境</span></span>
3<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">    &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">python.defaultInterpreterPath</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">\${workspaceFolder}/.venv/Scripts/python.exe</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
4<span class="line"></span>
5<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;">    // Ruff 作为默认格式化器</span></span>
6<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">    &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">[python]</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#999999;--shiki-dark:#666666;"> {</span></span>
7<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">        &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">editor.defaultFormatter</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">charliermarsh.ruff</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
8<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">        &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">editor.formatOnSave</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375;"> true</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
9<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">        &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">editor.codeActionsOnSave</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#999999;--shiki-dark:#666666;"> {</span></span>
10<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">source.fixAll.ruff</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">explicit</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
11<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">source.organizeImports.ruff</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">explicit</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span></span>
12<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">        }</span></span>
13<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">    },</span></span>
14<span class="line"></span>
15<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;">    // Pylance 类型检查</span></span>
16<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">    &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">python.analysis.typeCheckingMode</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">basic</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
17<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">    &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">python.analysis.autoImportCompletions</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375;"> true</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
18<span class="line"></span>
19<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;">    // 文件关联</span></span>
20<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">    &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">files.associations</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#999999;--shiki-dark:#666666;"> {</span></span>
21<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">        &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">*.toml</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">toml</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span></span>
22<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">    },</span></span>
23<span class="line"></span>
24<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;">    // 行尾统一 LF</span></span>
25<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">    &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">files.eol</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076;">\\n</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span></span>
26<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote><p><strong>Linux/macOS 用户</strong>:将 <code>python.defaultInterpreterPath</code> 改为 <code>\${workspaceFolder}/.venv/bin/python</code>。</p></blockquote><hr><h2 id="调试配置" tabindex="-1"><a class="header-anchor" href="#调试配置"><span>调试配置</span></a></h2><p>在 <code>.vscode/launch.json</code> 中添加以下配置:</p><div class="language-jsonc line-numbers-mode" data-ext="jsonc" data-title="jsonc"><button class="copy" title="复制代码" data-copied="已复制"></button><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">
26{</span></span>
27<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">    &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">version</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">0.2.0</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
28<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">    &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">configurations</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#999999;--shiki-dark:#666666;"> [</span></span>
29<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">        {</span></span>
30<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">name</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">NcatBot: 运行 main.py</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
31<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">type</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">debugpy</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
32<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">request</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">launch</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
33<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">program</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">\${workspaceFolder}/main.py</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
34<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">cwd</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">\${workspaceFolder}</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
35<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">console</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">integratedTerminal</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
36<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">justMyCode</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375;"> true</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
37<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">
37env</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#999999;--shiki-dark:#666666;"> {</span></span>
38<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">                &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">PYTHONPATH</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">\${workspaceFolder}</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span></span>
39<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">            }</span></span>
40<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">        },</span></span>
41<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">        {</span></span>
42<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">name</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">NcatBot: 运行当前文件</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
43<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">type</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">debugpy</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
44<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">request</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">launch</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
45<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">program</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">\${file}</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
46<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">cwd</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">\${workspaceFolder}</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
47<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">console</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">integratedTerminal</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
48<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">justMyCode</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375;"> true</span></span>
49<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">        },</span></span>
50<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">        {</span></span>
51<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">name</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">NcatBot: pytest 当前文件</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
52<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">type</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">debugpy</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
53<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">request</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">launch</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
54<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">module</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">pytest</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
55<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">args</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#999999;--shiki-dark:#666666;"> [</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">\${file}</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">-v</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">--no-cov</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">],</span></span>
56<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">cwd</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">\${workspaceFolder}</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
57<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">console</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">integratedTerminal</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
58<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">justMyCode</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375;">
58 false</span></span>
59<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">        },</span></span>
60<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">        {</span></span>
61<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">name</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">NcatBot: pytest 全部测试</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
62<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">type</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">debugpy</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
63<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">request</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">launch</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
64<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">module</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">pytest</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
65<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">args</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#999999;--shiki-dark:#666666;"> [</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">-v</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">--no-cov</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">],</span></span>
66<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">cwd</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">\${workspaceFolder}</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
67<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">console</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">integratedTerminal</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
68<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">            &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">justMyCode</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375;">
68 false</span></span>
69<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">        }</span></span>
70<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">    ]</span></span>
71<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><strong>使用方式</strong>:按 <code>F5</code> 或在&quot;运行和调试&quot;面板选择配置启动。调试 pytest 时使用 <code>--no-cov</code> 避免覆盖率插件干扰断点。</p><hr><h2 id="pre-commit-详解" tabindex="-1"><a class="header-anchor" href="#pre-commit-详解"><span>pre-commit 详解</span></a></h2><h3 id="安装钩子" tabindex="-1"><a class="header-anchor" href="#安装钩子"><span>安装钩子</span></a></h3><div class="language-bash line-numbers-mode" data-ext="bash" data-title="bash"><button class="copy" title="复制代码" data-copied="已复制"></button><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">uv</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> run</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> pre-commit</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> install</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>安装后每次 <code>git commit</code> 会自动运行以下检查:</p><table><thead><tr><th>来源</th><th>检查内容</th></tr></thead><tbody><tr><td><strong>pre-commit-hooks</strong></td><td>尾部空白、文件末尾换行、YAML/TOML 语法、AST 合法性、大文件检测、合并冲突标记、私钥泄露检测、debug 语句、行尾统一 LF</td></tr><tr><td><strong>ruff</strong></td><td>代码 lint(自动修复)+ 格式化</td></tr><tr><td><strong>codespell</strong></td><td>拼写检查</td></tr></tbody></table><h3 id="常见场景" tabindex="-1"><a class="header-anchor" href="#常见场景"><span>常见场景</span></a></h3><div class="language-bash line-numbers-mode" data-ext="bash" data-title="bash"><button class="copy" title="复制代码" data-copied="已复制"></button><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;"># 手动对所有文件运行(推荐首次安装后执行)</span></span>
72<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">uv</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> run</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> pre-commit</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> run</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076;"> --all-files</span></span>
73<span class="line"></span>
74<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;"># 如果 pre-commit 自动修复了文件,需要重新暂存</span></span>
75<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">git</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> add</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> .</span></span>
76<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">git</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> commit</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076;"> -m</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">your message</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="hint-container tip"><p class="hint-container-title">提示</p><p>pre-commit 自动修复文件后 <code>git commit</code> 会中断,请 <code>git add</code> 修复后的文件再重新提交。不推荐使用 <code>--no-verify</code> 跳过检查。</p></div><hr><h2 id="ci-cd-本地模拟" tabindex="-1"><a class="header-anchor" href="#ci-cd-本地模拟"><span>CI/CD 本地模拟</span></a></h2><h3 id="使用-tox-多版本测试" tabindex="-1"><a class="header-anchor" href="#使用-tox-多版本测试"><span>使用 tox 多版本测试</span></a></h3><p>tox 可在本地模拟 CI 的多 Python 版本测试矩阵:</p><div class="language-bash line-numbers-mode" data-ext="bash" data-title="bash"><button class="copy" title="复制代码" data-copied="已复制"></button><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;"># 运行所有版本(Python 3.12 + 3.13)</span></span>
77<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">uv</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> run</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> tox</span></span>
78<span class="line"></span>
79<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;"># 指定版本</span></span>
80<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">uv</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> run</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> tox</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076;"> -e</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> py312</span></span>
81<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">uv</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> run</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> tox</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076;"> -e</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> py313</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>tox 配置位于 <code>tox.ini</code>:</p><div class="language-ini line-numbers-mode" data-ext="ini" data-title="ini"><button class="copy" title="复制代码" data-copied="已复制"></button><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">[</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">tox</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">]</span></span>
82<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375;">envlist</span><span style="--shiki-light:#999999;--shiki-dark:#666666;"> =</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE;"> py312, py313</span></span>
83<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375;">requires</span><span style="--shiki-light:#999999;--shiki-dark:#666666;"> =</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE;"> tox-uv</span></span>
84<span class="line"></span>
85<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">[</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">testenv</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">]</span></span>
86<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375;">extras</span><span style="--shiki-light:#999999;--shiki-dark:#666666;"> =</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE;"> dev</span></span>
87<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375;">commands</span><span style="--shiki-light:#999999;--shiki-dark:#666666;"> =</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE;"> pytest --no-cov {posargs}</span></span>
88<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375;">uv_python_preference</span><span style="--shiki-light:#999999;--shiki-dark:#666666;"> =</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE;"> managed</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote><p>tox 使用 <code>tox-uv</code>
88 插件集成 uv,会自动下载所需的 Python 版本。多版本测试不是提交 PR 的必要条件,CI 会自动处理。</p></blockquote><h3 id="模拟完整-ci-流程" tabindex="-1"><a class="header-anchor" href="#模拟完整-ci-流程"><span>模拟完整 CI 流程</span></a></h3><p>在本地按以下顺序执行,可覆盖 CI 的所有检查项:</p><div class="language-bash line-numbers-mode" data-ext="bash" data-title="bash"><button class="copy" title="复制代码" data-copied="已复制"></button><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;"># 1. 代码检查</span></span>
89<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">uv</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> run</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> ruff</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> check</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> .</span></span>
90<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">uv</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> run</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> ruff</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> format</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076;"> --check</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> .</span></span>
91<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">uv</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> run</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> codespell</span></span>
92<span class="line"></span>
93<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;"># 2. 类型检查</span></span>
94<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">uv</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> run</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> mypy</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> ncatbot/</span></span>
95<span class="line"></span>
96<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;"># 3. 单元测试</span></span>
97<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">uv</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> run</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> pytest</span></span>
98<span class="line"></span>
99<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;"># 4. 多版本测试(可选)</span></span>
100<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">uv</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> run</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> tox</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>全部通过后即可放心提交 PR。</p><hr><h2 id="napcat-本地部署" tabindex="-1"><a class="header-anchor" href="#napcat-本地部署"><span>NapCat 本地部署</span></a></h2><p>NcatBot 依赖 <a href="https://github.com/NapNeko/NapCatQQ" target="_blank" rel="noopener noreferrer">NapCat</a> 作为 OneBot v11 协议后端。项目根目录的 <code>napcat/</code> 包含预配置的 NapCat 文件。</p><h3 id="启动-napcat" tabindex="-1"><a class="header-anchor" href="#启动-napcat"><span>启动 NapCat</span></a></h3><div class="language-powershell line-numbers-mode" data-ext="powershell" data-title="powershell"><button class="copy" title="复制代码" data-copied="已复制"></button><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;"># Windows — 使用快速登录脚本</span></span>
101<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE;">
101.\\napcat\\</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">1550507358_quickLogin.bat</span></span>
102<span class="line"></span>
103<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;"># 或使用通用启动脚本</span></span>
104<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE;">.\\napcat\\</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">launcher.bat</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="配置文件" tabindex="-1"><a class="header-anchor" href="#配置文件"><span>配置文件</span></a></h3><p>NapCat 配置文件位于 <code>napcat/config/</code> 目录,关键配置项:</p><table><thead><tr><th>文件</th><th>说明</th></tr></thead><tbody><tr><td><code>napcat/qqnt.json</code></td><td>QQ 版本与协议配置</td></tr><tr><td><code>napcat/config/</code></td><td>WebSocket / HTTP 连接配置</td></tr></tbody></table><h3 id="连接-ncatbot" tabindex="-1"><a class="header-anchor" href="#连接-ncatbot"><span>连接 NcatBot</span></a></h3><p>确保 NapCat 与 NcatBot 的 WebSocket 端口一致(默认 <code>3001</code>),在项目的 <code>config.yaml</code> 中配置:</p><div class="language-yaml line-numbers-mode" data-ext="yaml" data-title="yaml"><button class="copy" title="复制代码" data-copied="已复制"></button><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code><span class="line"><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">ws_port</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91;"> 3001</span></span>
105<span class="line"><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">http_port</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91;"> 3000</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div></div></div><blockquote><p>开发环境可使用 <code>dev/config.yaml</code> 覆盖默认配置。</p></blockquote><hr><h2 id="常见问题排查" tabindex="-1"><a class="header-anchor" href="#常见问题排查"><span>常见问题排查</span></a></h2><h3 id="uv-sync-安装失败" tabindex="-1"><a class="header-anchor" href="#uv-sync-安装失败"><span><code>uv sync</code> 安装失败</span></a></h3><ul><li><strong>检查 Python 版本</strong>:<code>python --version</code> 确认 ≥ 3.12</li><li><strong>检查 uv 版本</strong>:<code>uv --version</code>,建议升级到最新:<code>uv self update</code></li><li><strong>网络问题</strong>:配置镜像源</li></ul><div class="language-bash line-numbers-mode" data-ext="bash" data-title="bash"><button class="copy" title="复制代码" data-copied="已复制"></button><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">uv</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> sync</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076;"> --extra</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> dev</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076;"> --index-url</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> https://pypi.tuna.tsinghua.edu.cn/simple</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><h3 id="import-ncatbot-报错-modulenotfounderror" tabindex="-1"><a class="header-anchor" href="#import-ncatbot-报错-modulenotfounderror"><span><code>import ncatbot</code> 报错 ModuleNotFoundError</span></a></h3><ul><li><strong>未激活虚拟环境</strong>:执行 <code>.venv\\Scripts\\activate.ps1</code>(Windows)或 <code>source .venv/bin/activate</code>(Linux/macOS)</li><li><strong>依赖未安装</strong>:执行 <code>uv sync --extra dev</code></li><li><strong>IDE 解释器未指向 <code>.venv</code></strong>:在 VSCode 中按 <code>Ctrl+Shift+P</code> → &quot;Python: Select Interpreter&quot; → 选择 <code>.venv</code> 中的 Python</li></ul><h3 id="pre-commit-报错" tabindex="-1"><a class="header-anchor" href="#pre-commit-报错"><span>pre-commit 报错</span></a></h3><ul><li><strong>首次运行慢</strong>
105:pre-commit 需要下载钩子环境,属正常现象</li><li><strong>自动修复后提交失败</strong>:这是预期行为,<code>git add .</code> 后重新 <code>git commit</code></li><li><strong>清除缓存重装</strong>:</li></ul><div class="language-bash line-numbers-mode" data-ext="bash" data-title="bash"><button class="copy" title="复制代码" data-copied="已复制"></button><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">uv</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> run</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> pre-commit</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> clean</span></span>
106<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">uv</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> run</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> pre-commit</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> install</span></span>
107<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">uv</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> run</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> pre-commit</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;"> run</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076;"> --all-files</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="pytest-找不到测试" tabindex="-1"><a class="header-anchor" href="#pytest-找不到测试"><span>pytest 找不到测试</span></a></h3><ul><li><strong>确认在项目根目录执行</strong>:<code>cd ncatbot</code> 项目根目录</li><li><strong>确认虚拟环境已激活</strong></li><li><strong>查看 pytest 配置</strong>:<code>pyproject.toml</code> 中 <code>[tool.pytest.ini_options]</code> 的 <code>testpaths</code> 设置</li></ul><h3 id="napcat-连接失败" tabindex="-1"><a class="header-anchor" href="#napcat-连接失败"><span>NapCat 连接失败</span></a></h3><ul><li><strong>确认 NapCat 已启动</strong>:检查 NapCat 终端是否有错误输出</li><li><strong>端口冲突</strong>:确认 <code>ws_port</code>(默认 3001)和 <code>http_port</code>(默认 3000)未被占用</li><li><strong>防火墙</strong>:确保本地防火墙未拦截 WebSocket 连接</li></ul><h3 id="tox-下载-python-失败" tabindex="-1"><a class="header-anchor" href="#tox-下载-python-失败"><span>tox 下载 Python 失败</span></a></h3><ul><li><strong>网络问题</strong>:tox-uv 会自动下载 Python,确保网络畅通</li><li><strong>跳过多版本测试</strong>:多版本测试非必需,CI 会自动处理,本地只需 <code>uv run pytest</code> 即可</li></ul>`,67)]))}const k=i(l,[["render",h],["__file","index.html.vue"]]),d=JSON.parse('{"path":"/contributing/p7czpap8/","title":"高级开发环境","lang":"zh-CN","frontmatter":{"title":"高级开发环境","createTime":"2026/03/19 17:26:45","permalink":"/contributing/p7czpap8/","description":"IDE 配置、调试技巧、CI/CD 本地模拟、NapCat 本地部署与常见问题排查。 目录 IDE 配置(VSCode) 调试配置 pre-commit 详解 CI/CD 本地模拟 NapCat 本地部署 常见问题排查 IDE 配置(VSCode) 推荐扩展 在 VSCode 中安装以下扩展以获得最佳开发体验: 推荐 settings.json 在项目...","head":[["meta",{"property":"og:url","content":"http://docs.ncatbot.xyz/contributing/p7czpap8/"}],["meta",{"property":"og:site_name","content":"NcatBot 文档"}],["meta",{"property":"og:title","content":"高级开发环境"}],["meta",{"property":"og:description","content":"IDE 配置、调试技巧、CI/CD 本地模拟、NapCat 本地部署与常见问题排查。 目录 IDE 配置(VSCode) 调试配置 pre-commit 详解 CI/CD 本地模拟 NapCat 本地部署 常见问题排查 IDE 配置(VSCode) 推荐扩展 在 VSCode 中安装以下扩展以获得最佳开发体验: 推荐 settings.json 在项目..."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}
107],["meta",{"property":"og:updated_time","content":"2026-04-30T14:41:02.000Z"}],["meta",{"property":"article:modified_time","content":"2026-04-30T14:41:02.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"高级开发环境\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2026-04-30T14:41:02.000Z\\",\\"author\\":[]}"]]},"headers":[],"readingTime":{"minutes":4.26,"words":1279},"git":{"updatedTime":1777560062000,"contributors":[{"name":"GEYUANwuqi","username":"GEYUANwuqi","email":"[email protected]","commits":1,"avatar":"https://gravatar.com/avatar/1cf26d82e91dff657f3fa4d4911cdabcf7b76396339e886e69f99b15d4c8c808?d=retro"}]},"autoDesc":true,"filePathRelative":"notes/contributing/1. 开发环境/1. 高级设置.md"}');export{k as comp,d as data};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.