diff --git a/.github/CLAUDE.md b/.github/CLAUDE.md index eaa3d85..8e05744 100644 --- a/.github/CLAUDE.md +++ b/.github/CLAUDE.md @@ -4,54 +4,52 @@ ## 模块职责 -管理项目的持续集成/持续部署流水线和社区 Issue 模板。三条工作流覆盖 PR 验证、正式发布和测试版发布。 +管理项目的持续集成/持续部署流水线和社区 Issue 模板。三条工作流覆盖 PR 验证、正式发布和测试版发布。发布物为 `dist/PVE-Tools.sh` 单文件 + `SHA256SUMS.txt`(不使用 shc 编译二进制)。 ## 入口与启动 | 项目 | 说明 | |---|---| -| 触发方式 | GitHub Actions,由 push/pull_request 事件触发 | +| 触发方式 | GitHub Actions,由 push(tag)/pull_request 事件触发 | | 运行环境 | `ubuntu-latest` | -| 权限 | `contents: write`(Release 工作流需要) | +| 权限 | `contents: write`(两个 Release 工作流需要) | ## 工作流清单 +### pr-validation.yml -- PR 验证工作流 + +**触发条件**: PR 到 `main` 或 `beta` 分支 + +| 检查 | 说明 | +|---|---| +| Shellcheck(入口) | `shellcheck -f gcc PVE-Tools.sh`,error/warning 均失败 | +| Shellcheck(全源码) | `find lib src/modules -name '*.sh' \| xargs shellcheck --severity=error`,error 级卡死 | +| 语法检查 | `bash -n` 入口/dev.sh,`bash build.sh` 后 `bash -n dist` | +| Shellcheck(产物) | dist 的 error/warning 均失败 | +| 构建一致性 | 源码与 dist 的函数集合双向 diff,不一致即失败 | +| 版本一致性 | `CURRENT_VERSION`(lib/config.sh)== `VERSION` 文件 | +| UPDATE 新鲜度 | `UPDATE` 首行必须包含当前版本号(防更新日志脱节) | +| 安全扫描 | dist 中禁 `eval`、未加引号变量开头的 `rm -rf`、`source` 语句,命中即失败 | + ### release.yml -- 正式发布工作流 -**触发条件**: 推送版本标签 (`v*.*.*`, `*.*.*`, `v*.*.*-stable`, `*.*.*-stable`) +**触发条件**: 推送 `v*.*.*` / `*.*.*` / `*-stable` 标签;**排除** `-beta*` / `-alpha*` / `-rc*`(预发布标签只归 beta-release.yml,避免双流水线竞争同一 tag) **步骤**: -1. `actions/checkout@v4`(fetch-depth: 0 以获取完整历史) -2. 从 tag 提取版本号 -3. **`bash build.sh`** -- 将 lib/ + src/modules/ 组装为 `dist/PVE-Tools.sh` -4. 安装 `shc`(via neurobin/ppa) -5. `shc -f dist/PVE-Tools.sh -o pve-tools` -- 编译为二进制 -6. 生成 release notes(基于 git 提交历史) -7. `softprops/action-gh-release@v2` 创建 GitHub Release,上传 `pve-tools` + `dist/PVE-Tools.sh` +1. checkout(fetch-depth: 0)-> 从 tag 提取版本号 +2. `bash build.sh` 构建 dist +3. **发布前校验**(打 tag 直发不绕过质量闸门):`bash -n dist`、`shellcheck --severity=error dist`、函数集合一致性 diff、版本三方一致(tag == config == VERSION) +4. 生成 `SHA256SUMS.txt` +5. 基于 git 提交历史生成 release notes +6. `softprops/action-gh-release@v2` 创建 Release,上传 `dist/PVE-Tools.sh` + `dist/SHA256SUMS.txt` -**构建产物**: -- `pve-tools` -- 编译后的二进制文件 -- `dist/PVE-Tools.sh` -- 拼接后的单文件脚本 +**注意**: 远程安装链路依赖 `releases/latest/download/PVE-Tools.sh`,正式发版后新用户方能获取最新版。 ### beta-release.yml -- 测试版发布工作流 -**触发条件**: 推送 beta/alpha 标签 +**触发条件**: 推送 `-beta*` / `-alpha*` 标签 -功能与 release.yml 类似,但标记为 prerelease。 - -### pr-validation.yml -- PR 验证工作流 - -**触发条件**: PR 合并到 `main` 或 `beta` 分支 - -**检查项**: - -| 检查 | 命令 | 说明 | -|---|---|---| -| Shellcheck | `shellcheck -f gcc PVE-Tools.sh` | 静态分析,有 error/warning 则失败 | -| 语法检查 | `bash -n PVE-Tools.sh`、`bash -n dev.sh`、`bash -n dist/PVE-Tools.sh` | 先运行 `bash build.sh` 构建再检查 | -| 构建验证 | `bash build.sh` | 验证构建不报错 | -| 版本一致性 | 比较 `lib/config.sh` 中的 `CURRENT_VERSION` 与 `VERSION` 文件 | 不一致则失败 | -| 安全扫描 | grep 检测 `eval`/`source` 使用 | 发现则告警 | +与 release.yml 类似但 `prerelease: true`;同样执行构建质量校验(语法/shellcheck/函数一致性),版本号断言放宽(beta 标签允许与 config 版本不同步)。 ## Issue 模板 @@ -71,42 +69,29 @@ ## 关键依赖与配置 -- **GitHub Actions**: 免费额度,`ubuntu-latest` runner -- **shc**: 来自 `ppa:neurobin/ppa`,用于 Bash 编译 -- **softprops/action-gh-release@v2**: 第三方 GitHub Action,用于创建 Release -- **shellcheck**: Ubuntu 自带或通过 apt 安装 - -## 测试与质量 - -CI/CD 本身即为项目的测试与质量保障体系: -- 每次 PR 自动执行静态分析、语法检查、版本一致性校验、安全扫描 -- Release 前自动构建并验证构建产物 +- **GitHub Actions**: `ubuntu-latest` runner(自带 shellcheck) +- **softprops/action-gh-release@v2**: 创建 Release 与上传资产 ## 常见问题 (FAQ) -**Q: 为什么 Release 要用 shc 编译?** -shc 将 Bash 脚本编译为二进制文件,提供基础的源码保护,同时便于分发。构建产物 `dist/PVE-Tools.sh` 同时发布以保持 `bash <(curl ...)` 兼容性。 +**Q: 发版时需要同步哪些版本号?** +三处:`lib/config.sh` 的 `CURRENT_VERSION`、`VERSION` 文件、`UPDATE` 首行条目,且正式发布 tag 必须与之一致——任何一处不同步都会被 CI 拦下。 -**Q: PR 验证中 build.sh 会失败怎么办?** -检查 lib/ 和 src/modules/ 中的文件是否存在、语法是否正确。`bash -n` 仅检查语法不执行代码。 +**Q: PR 验证中 build.sh 失败怎么办?** +检查 lib/ 与 src/modules/ 文件是否齐全、语法是否正确;新增 lib 文件需同步 build.sh / dev.sh / PVE-Tools.sh 三处加载列表。 **Q: 如何添加新的 Issue 模板?** -在 `ISSUE_TEMPLATE/` 目录添加 `.md` 文件并更新 `config.yml` 即可,GitHub 会自动识别。 +在 `ISSUE_TEMPLATE/` 目录添加 `.md` 文件并更新 `config.yml`。 ## 相关文件清单 ``` .github/ workflows/ - release.yml # 正式发布工作流 - beta-release.yml # 测试版发布工作流 - pr-validation.yml # PR 验证工作流 - ISSUE_TEMPLATE/ - fast-bugs-report.md # 快速 Bug 报告模板 - feature-request.md # 功能请求模板 - plugin-submit.md # 插件提交模板 - report-bugs.md # 详细 Bug 报告模板 - config.yml # Issue 模板配置 + release.yml # 正式发布(含发布前校验与 SHA256SUMS) + beta-release.yml # 测试版发布(含构建校验) + pr-validation.yml # PR 验证(8 项检查) + ISSUE_TEMPLATE/ # Issue 模板 5 件 FUNDING.yml # 赞助配置 ``` @@ -114,4 +99,5 @@ shc 将 Bash 脚本编译为二进制文件,提供基础的源码保护,同 | 日期 | 变更 | |---|---| -| 2026-07-08 | 初始化 .github 模块 CLAUDE.md。PR 验证已适配模块化(build.sh/build -n dist)。Release 已添加 build.sh 步骤。 | +| 2026-07-26 | 按现实重写:移除 shc 描述;补充发布前校验/SHA256SUMS/UPDATE 新鲜度/安全扫描细节;记录正式与预发布标签互斥规则 | +| 2026-07-08 | 初始化 .github 模块 CLAUDE.md | diff --git a/.github/workflows/beta-release.yml b/.github/workflows/beta-release.yml index 62fb14b..981c2bf 100644 --- a/.github/workflows/beta-release.yml +++ b/.github/workflows/beta-release.yml @@ -29,6 +29,19 @@ jobs: - name: Build single script from modules run: bash build.sh + - name: Validate build output before release + run: | + # beta 同样不跳过构建质量校验(版本号断言放宽,beta 标签允许与 config 不同步) + bash -n dist/PVE-Tools.sh + shellcheck --severity=error -f gcc dist/PVE-Tools.sh + diff <(grep -rhoE '^[a-zA-Z_][a-zA-Z0-9_]*\(\)' lib src/modules --include='*.sh' | sort -u) \ + <(grep -hoE '^[a-zA-Z_][a-zA-Z0-9_]*\(\)' dist/PVE-Tools.sh | sort -u) \ + || { echo "构建产物与源码函数集合不一致,中止发布"; exit 1; } + + - name: Generate checksums + run: | + cd dist && sha256sum PVE-Tools.sh > SHA256SUMS.txt && cat SHA256SUMS.txt + - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: @@ -47,6 +60,7 @@ jobs: ``` files: | dist/PVE-Tools.sh + dist/SHA256SUMS.txt draft: false prerelease: true env: diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index ef94521..cd479b9 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -18,6 +18,11 @@ jobs: exit 1 fi + - name: Shellcheck (lib and modules, error level) + run: | + # 全量源码 error 级静态检查(入口与构建产物维持 error+warning 严格档) + find lib src/modules -name '*.sh' -print0 | xargs -0 shellcheck --severity=error -f gcc + - name: Shell syntax check run: | bash -n PVE-Tools.sh @@ -34,6 +39,13 @@ jobs: exit 1 fi + - name: Build output consistency check + run: | + # 断言 lib/ 与 src/modules/ 中定义的每个函数都进入了构建产物(双向一致) + diff <(grep -rhoE '^[a-zA-Z_][a-zA-Z0-9_]*\(\)' lib src/modules --include='*.sh' | sort -u) \ + <(grep -hoE '^[a-zA-Z_][a-zA-Z0-9_]*\(\)' dist/PVE-Tools.sh | sort -u) \ + || { echo "构建产物与源码的函数集合不一致,见上方 diff(< 仅源码有 / > 仅产物有)"; exit 1; } + - name: Version consistency check run: | SCRIPT_VERSION=$(grep "CURRENT_VERSION=" lib/config.sh | cut -d'"' -f2) @@ -42,12 +54,34 @@ jobs: echo "Version inconsistency: script($SCRIPT_VERSION) != version file($VERSION_FILE_VERSION)" exit 1 fi + + - name: UPDATE changelog freshness check + run: | + # UPDATE 是 check_update 展示给线上用户的更新日志,首行必须包含当前版本号,防止再次脱节 + VERSION_FILE_VERSION=$(cat VERSION) + if ! head -1 UPDATE | grep -qF "$VERSION_FILE_VERSION"; then + echo "UPDATE 首行未包含当前版本 $VERSION_FILE_VERSION —— 更新日志已脱节,请同步 UPDATE 文件" + exit 1 + fi - name: Security scan run: | - # 检查是否有潜在的安全问题 - if grep -q "eval\|source" PVE-Tools.sh; then - echo "Potential security issues found: eval or source commands detected" - else - echo "No security issues detected" - fi \ No newline at end of file + # 对全量构建产物扫描真实危险模式,命中即失败 + # (旧版对入口 grep eval/source 永远命中且从不失败,属于空转检查) + status=0 + if grep -nE '(^|[^a-zA-Z_.])eval([^a-zA-Z_]|$)' dist/PVE-Tools.sh; then + echo "检测到 eval 使用:本项目约定禁止 eval,请改写后再合并" + status=1 + fi + if grep -nE 'rm -rf +\$' dist/PVE-Tools.sh; then + echo "检测到未加引号且以变量开头的 rm -rf 路径:变量为空或含空格时行为不可控,请加引号与前置校验" + status=1 + fi + if grep -nE '^[[:space:]]*(source|\.)[[:space:]]' dist/PVE-Tools.sh; then + echo "构建产物中不应存在 source 语句:dist 是自包含单文件,出现 source 说明拼接进了开发态加载代码" + status=1 + fi + if [ "$status" -eq 0 ]; then + echo "Security scan passed" + fi + exit $status \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 76f84db..6521403 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,6 +6,14 @@ on: - '*.*.*' - 'v*.*.*-stable' - '*.*.*-stable' + # 排除预发布标签,否则 v*.*.* 会同时命中 beta/alpha/rc, + # 导致预发布被当成正式版发布并与 beta-release.yml 竞争同一 tag + - '!v*.*.*-beta*' + - '!*.*.*-beta*' + - '!v*.*.*-alpha*' + - '!*.*.*-alpha*' + - '!v*.*.*-rc*' + - '!*.*.*-rc*' permissions: contents: write @@ -29,6 +37,33 @@ jobs: - name: Build single script from modules run: bash build.sh + - name: Validate build output before release + run: | + # 发版前置校验:打 tag 直发不再绕过 PR 的质量闸门 + bash -n dist/PVE-Tools.sh + shellcheck --severity=error -f gcc dist/PVE-Tools.sh + # 函数集合一致性(源码 vs 产物 双向) + diff <(grep -rhoE '^[a-zA-Z_][a-zA-Z0-9_]*\(\)' lib src/modules --include='*.sh' | sort -u) \ + <(grep -hoE '^[a-zA-Z_][a-zA-Z0-9_]*\(\)' dist/PVE-Tools.sh | sort -u) \ + || { echo "构建产物与源码函数集合不一致,中止发布"; exit 1; } + # 版本三方一致:tag == lib/config.sh == VERSION,防止打错标签发出版本混乱的 Release + SCRIPT_VERSION=$(grep "CURRENT_VERSION=" lib/config.sh | cut -d'"' -f2) + VERSION_FILE_VERSION=$(cat VERSION) + TAG_VERSION="${{ steps.get_version.outputs.VERSION }}" + TAG_VERSION="${TAG_VERSION%-stable}" + if [ "$SCRIPT_VERSION" != "$VERSION_FILE_VERSION" ]; then + echo "版本不一致: config($SCRIPT_VERSION) != VERSION($VERSION_FILE_VERSION),中止发布" + exit 1 + fi + if [ "$TAG_VERSION" != "$SCRIPT_VERSION" ]; then + echo "标签版本($TAG_VERSION)与脚本版本($SCRIPT_VERSION)不一致,中止发布" + exit 1 + fi + + - name: Generate checksums + run: | + cd dist && sha256sum PVE-Tools.sh > SHA256SUMS.txt && cat SHA256SUMS.txt + - name: Generate release notes id: release_notes run: | @@ -66,6 +101,7 @@ jobs: ``` files: | dist/PVE-Tools.sh + dist/SHA256SUMS.txt draft: false prerelease: false env: diff --git a/.gitignore b/.gitignore index 3d1459f..2f67937 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ # Ignore dependencies node_modules/ **/node_modules/ -bun.lockb # Ignore build outputs dist/ @@ -9,10 +8,6 @@ dist/ build/ out/ .temp/ -.vitepress/dist/ -.vitepress/cache/ -**/.vitepress/dist/ -**/.vitepress/cache/ # Ignore system files .DS_Store @@ -37,12 +32,3 @@ report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Python __pycache__/ *.pyc - -# BMad Method V6 -_bmad/ -_bmad-output/ - -test.txt - -# ignore docs -Docs/ diff --git a/AGENTS.md b/AGENTS.md index 8e5879d..969d8a1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,41 +2,39 @@ ## Project Structure & Module Organization -`PVE-Tools.sh` is the main Bash entry point for Proxmox VE 9.x operations. `Tools/` contains standalone maintenance scripts for host setup, LXC cleanup, monitoring, kernel handling, and backups. `Modules/` contains plugin-market modules and bundled binary assets such as `Modules/VGPU/*.so`; do not edit binary files unless the change is intentional and documented. `Web/` is the VitePress documentation site, with pages in Markdown and static assets under `Web/public/` and `Web/assets/`. `Docs/` stores supplemental guides, `images/` stores README and site screenshots, and `.github/` contains issue templates and CI/release workflows. +- `PVE-Tools.sh` — bootstrap entry (~330 lines). Local mode sources `lib/` + `src/modules/`; remote mode (`bash <(curl ...)`) downloads the prebuilt single file from GitHub Releases (`releases/latest/download/PVE-Tools.sh`). +- `lib/` — infrastructure layer, loaded in strict order: `config.sh` (globals) → `core.sh` (logging/UI/backup/GRUB helpers) → `menu.sh` (menu framework) → `network.sh` (region detection, mirror selection) → `runtime.sh` (guards + `main()`). +- `src/modules/` — 10 numbered feature modules matching main-menu items 1-10; each has `init.sh` (menu entry) plus feature files. +- `build.sh` concatenates lib + modules into `dist/PVE-Tools.sh` (gitignored; built by CI). `dev.sh` sources everything for local development. +- `Tools/` — standalone community maintenance scripts (not part of the build). `Modules/` — plugin-market assets incl. `Modules/VGPU/*.so` (binary asset referenced by the vGPU unlock download URL — do not edit or remove). `Docs/` — supplementary docs incl. the modularization design doc. `images/` — README screenshots. `.github/` — issue templates and CI workflows. +- The former `Web/` VitePress documentation site has been removed from this repository. ## Build, Test, and Development Commands -Run Bash checks from the repository root: - ```bash -bash -n PVE-Tools.sh -shellcheck -f gcc PVE-Tools.sh +bash dev.sh # run from source +bash build.sh # build dist/PVE-Tools.sh +bash -n PVE-Tools.sh && bash -n dist/PVE-Tools.sh # syntax check +shellcheck -f gcc PVE-Tools.sh dist/PVE-Tools.sh # strict lint (entry + artifact) +find lib src/modules -name '*.sh' -print0 | xargs -0 shellcheck --severity=error -f gcc ``` -For the documentation site: - -```bash -cd Web -bun install -bun run dev -bun run build -bun run preview -``` - -`bun run build` copies `VERSION`, `UPDATE`, and `PVE-Tools.sh` into the generated site output. +CI (`pr-validation.yml`) additionally asserts: source-vs-dist function-set consistency, `CURRENT_VERSION` ↔ `VERSION` match, `UPDATE` first line contains the current version, and a security scan (no `eval`, no unquoted `rm -rf $var`, no `source` in dist). Release workflows re-run these gates before publishing and attach `SHA256SUMS.txt`. ## Coding Style & Naming Conventions -Bash scripts use `#!/bin/bash`, 4-space indentation, and `snake_case` function names. Use `UPPER_SNAKE` for constants and global configuration, and `lower_case` for local variables. Prefer existing helpers in `PVE-Tools.sh`, especially logging, confirmation, backup, UI, and idempotent configuration functions. Web components should follow VitePress/Vue 3 conventions with `
--${keyString} -
-