From 9f2a935ec29de99bf5e4f19b2effc92513df8dd3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:26:42 +0000 Subject: [PATCH 1/6] Initial plan From e372a5069c5d831074d562c39c6c4d4be8e0cfb9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:34:15 +0000 Subject: [PATCH 2/6] Add pnpm version enforcement and lockfile consistency checks - Add scripts to check pnpm version and lockfile consistency - Update CI workflows to enable corepack and verify versions - Update pre-commit hook to warn about version mismatches - Add comprehensive documentation in CONTRIBUTING.md - Update README files with pnpm version requirements - Create detailed PNPM_LOCKFILE_GUIDE.md for reference Co-authored-by: longsizhuo <114939201+longsizhuo@users.noreply.github.com> --- .github/workflows/content-check.yml | 21 +++ .github/workflows/deploy.yml | 20 +++ .github/workflows/sync-uuid.yml | 8 + .husky/pre-commit | 6 +- CONTRIBUTING.md | 140 +++++++++++++++- PNPM_LOCKFILE_GUIDE.md | 239 ++++++++++++++++++++++++++++ README.en.md | 14 +- README.md | 11 +- package.json | 4 +- scripts/check-lockfile.mjs | 64 ++++++++ scripts/check-pnpm-version.mjs | 73 +++++++++ 11 files changed, 587 insertions(+), 13 deletions(-) create mode 100644 PNPM_LOCKFILE_GUIDE.md create mode 100755 scripts/check-lockfile.mjs create mode 100755 scripts/check-pnpm-version.mjs diff --git a/.github/workflows/content-check.yml b/.github/workflows/content-check.yml index a5985a0..f273390 100644 --- a/.github/workflows/content-check.yml +++ b/.github/workflows/content-check.yml @@ -31,6 +31,10 @@ jobs: steps: - uses: actions/checkout@v4 + # Enable corepack to ensure the exact pnpm version from package.json is used + - name: Enable Corepack + run: corepack enable + - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 @@ -38,7 +42,24 @@ jobs: node-version: 20 cache: "pnpm" + # Verify pnpm version matches package.json packageManager field + - name: Check pnpm version + run: node scripts/check-pnpm-version.mjs + - run: pnpm install --frozen-lockfile + + # Verify lockfile wasn't modified by install + - name: Check lockfile consistency + run: | + if ! git diff --exit-code pnpm-lock.yaml; then + echo "❌ Error: pnpm-lock.yaml was modified after install" + echo "This indicates a pnpm version mismatch or corrupted lockfile" + echo "Expected pnpm version: $(node -p 'require(\"./package.json\").packageManager')" + echo "Actual pnpm version: $(pnpm --version)" + exit 1 + fi + echo "✅ Lockfile is consistent" + - name: Run tests run: pnpm test # Non-blocking image migration + lint (visibility only) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f0c5339..dc38bb9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -20,6 +20,10 @@ jobs: steps: - uses: actions/checkout@v4 + # Enable corepack to ensure the exact pnpm version from package.json is used + - name: Enable Corepack + run: corepack enable + - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 @@ -27,7 +31,23 @@ jobs: node-version: 20 cache: pnpm + # Verify pnpm version matches package.json packageManager field + - name: Check pnpm version + run: node scripts/check-pnpm-version.mjs + - run: pnpm install --frozen-lockfile + + # Verify lockfile wasn't modified by install + - name: Check lockfile consistency + run: | + if ! git diff --exit-code pnpm-lock.yaml; then + echo "❌ Error: pnpm-lock.yaml was modified after install" + echo "This indicates a pnpm version mismatch or corrupted lockfile" + echo "Expected pnpm version: $(node -p 'require(\"./package.json\").packageManager')" + echo "Actual pnpm version: $(pnpm --version)" + exit 1 + fi + echo "✅ Lockfile is consistent" - run: pnpm run lint - run: pnpm run lint:images - run: pnpm run typecheck diff --git a/.github/workflows/sync-uuid.yml b/.github/workflows/sync-uuid.yml index 7bdd3be..9899b61 100644 --- a/.github/workflows/sync-uuid.yml +++ b/.github/workflows/sync-uuid.yml @@ -36,6 +36,10 @@ jobs: steps: - uses: actions/checkout@v4 + # Enable corepack to ensure the exact pnpm version from package.json is used + - name: Enable Corepack + run: corepack enable + - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 @@ -43,6 +47,10 @@ jobs: node-version: 20 cache: "pnpm" # 顺便启用 pnpm 缓存,加速 + # Verify pnpm version matches package.json packageManager field + - name: Check pnpm version + run: node scripts/check-pnpm-version.mjs + - name: Install deps run: pnpm install --frozen-lockfile diff --git a/.husky/pre-commit b/.husky/pre-commit index 56a93d6..6cf9623 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -10,5 +10,9 @@ pnpm lint:images || exit 1 # 3) 运行 Vitest,确保配置校验通过 pnpm test || exit 1 -# 4) 其余按 lint-staged 处理(如 Prettier) +# 4) 检查 pnpm 版本和 lockfile 一致性(警告但不阻止提交) +pnpm check:pnpm-version || true +pnpm check:lockfile || true + +# 5) 其余按 lint-staged 处理(如 Prettier) pnpm exec lint-staged diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d8d9a43..0e2613f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -173,6 +173,83 @@ git push origin doc_raven | `disable` | 在任何模式下都跳过远程图片尺寸请求,仅使用文档内手动声明的宽高 | | `force` | 强制启用远程图片尺寸请求,并在出现错误时抛出异常以暴露问题 | +### pnpm 版本和 lockfile 相关问题 + +#### 问题:pnpm-lock.yaml 频繁出现格式变化(单双引号切换) + +**原因**:不同版本的 pnpm 使用不同的 YAML 序列化格式,即使依赖关系相同,锁文件的格式也可能不同。 + +**解决方案**: + +1. **确保使用正确的 pnpm 版本**: + ```bash + # 检查当前版本 + pnpm --version + + # 应该显示: 10.20.0 + # 如果不是,请使用以下命令之一: + + # 方法 1: 使用 corepack(推荐) + corepack enable + corepack prepare pnpm@10.20.0 --activate + + # 方法 2: 全局安装 + npm install -g pnpm@10.20.0 + ``` + +2. **验证版本一致性**: + ```bash + pnpm check:pnpm-version + ``` + +3. **如果 lockfile 已经被错误修改**: + ```bash + # 丢弃 lockfile 的修改 + git checkout pnpm-lock.yaml + + # 确认使用正确的 pnpm 版本后重新安装 + pnpm install + ``` + +4. **在提交前检查**: + 项目的 pre-commit hook 会自动检查 pnpm 版本和 lockfile 变更,如果发现版本不匹配会给出警告。 + +#### 问题:CI 检查失败,提示 lockfile 不一致 + +这通常意味着: +1. 你本地使用的 pnpm 版本与项目要求的不一致 +2. lockfile 被错误修改或损坏 + +**解决方案**: +```bash +# 1. 确认并切换到正确的 pnpm 版本 +corepack enable +pnpm --version # 确认是 10.20.0 + +# 2. 删除 node_modules 和重新安装 +rm -rf node_modules +pnpm install + +# 3. 检查 lockfile 是否有变更 +git status + +# 4. 如果没有变更,说明已修复;如果有变更,说明之前的 lockfile 确实有问题 +``` + +#### 问题:为什么要使用 corepack 而不是全局安装? + +**Corepack 的优势**: +- 自动读取 `package.json` 的 `packageManager` 字段 +- 每个项目可以使用不同的 pnpm 版本而不冲突 +- 新贡献者克隆项目后自动使用正确的版本 +- 减少版本不匹配导致的问题 + +**如何为团队启用 corepack**: +```bash +# 一次性设置,之后所有项目都会受益 +corepack enable +``` + ## 🚀 开发环境 ### 1. 克隆仓库 @@ -184,10 +261,57 @@ cd involutionhell ### 2. 安装依赖 +**重要:为了避免 pnpm-lock.yaml 格式不一致的问题,请务必使用项目指定的 pnpm 版本!** + +本项目已在 `package.json` 中锁定了 pnpm 版本(`"packageManager": "pnpm@10.20.0"`),推荐使用 **corepack** 来自动管理正确的版本: + +#### 方式一:使用 Corepack(推荐) + +Corepack 是 Node.js 16.9+ 自带的工具,能自动使用 `package.json` 中指定的包管理器版本。 + +```bash +# 启用 corepack +corepack enable + +# 安装依赖(corepack 会自动使用 pnpm@10.20.0) +pnpm install +``` + +#### 方式二:全局安装指定版本 + +如果不想使用 corepack,可以手动安装项目指定的 pnpm 版本: + +```bash +# 安装 pnpm 10.20.0 +npm install -g pnpm@10.20.0 + +# 安装依赖 +pnpm install +``` + +#### 验证 pnpm 版本 + +安装完成后,请务必验证你使用的是正确的版本: + ```bash -pnpm install # 推荐;如需可改用 npm install +# 检查 pnpm 版本 +pnpm --version +# 应该输出: 10.20.0 + +# 或使用项目提供的检查脚本 +pnpm check:pnpm-version ``` +#### ⚠️ 为什么版本一致性很重要? + +不同版本的 pnpm 在序列化 `pnpm-lock.yaml` 时可能使用不同的格式(如单引号 vs 双引号),导致: +- PR 中产生大量无意义的 diff +- 增加代码审查负担 +- 可能导致 CI 检查失败 + +我们的 CI 工作流会自动检查版本一致性,如果检测到版本不匹配会导致构建失败。 + + ### 3. 本地开发 运行开发服务器: @@ -207,12 +331,14 @@ pnpm dev 常用脚本集中在 `package.json` 中,以下是最常用的命令: ```bash -pnpm dev # 启动开发服务器 -pnpm build # 构建生产版本 -pnpm start # 启动生产服务器 -pnpm lint:images # 检查图片是否符合规范 -pnpm migrate:images # 自动迁移图片到对应 assets 目录 -pnpm postinstall # 同步必要的 Husky/Fumadocs 配置 +pnpm dev # 启动开发服务器 +pnpm build # 构建生产版本 +pnpm start # 启动生产服务器 +pnpm lint:images # 检查图片是否符合规范 +pnpm migrate:images # 自动迁移图片到对应 assets 目录 +pnpm check:pnpm-version # 检查 pnpm 版本是否与 package.json 一致 +pnpm check:lockfile # 检查 lockfile 是否有未预期的修改 +pnpm postinstall # 同步必要的 Husky/Fumadocs 配置 ``` --- diff --git a/PNPM_LOCKFILE_GUIDE.md b/PNPM_LOCKFILE_GUIDE.md new file mode 100644 index 0000000..20e5703 --- /dev/null +++ b/PNPM_LOCKFILE_GUIDE.md @@ -0,0 +1,239 @@ +# pnpm 版本管理与 lockfile 一致性指南 + +## 问题背景 + +在多人协作的开源项目中,`pnpm-lock.yaml` 文件频繁出现格式变化(如单引号与双引号切换)是一个常见但令人困扰的问题。这会导致: + +- PR 中出现大量无意义的 diff +- 增加代码审查的复杂度 +- 可能引发 CI 检查失败 +- 浪费开发者时间去处理格式差异 + +## 根本原因 + +不同版本的 pnpm 使用不同的 YAML 序列化库或配置,即使依赖关系完全相同,生成的 `pnpm-lock.yaml` 格式也可能不同。主要体现在: + +1. **引号风格**:某些版本使用单引号,某些版本使用双引号 +2. **空格和缩进**:格式化规则可能有细微差异 +3. **字段顺序**:对象属性的排序可能不同 + +## 我们的解决方案 + +### 1. 锁定 pnpm 版本 + +项目在 `package.json` 中明确指定了 pnpm 版本: + +```json +{ + "packageManager": "pnpm@10.20.0" +} +``` + +这是 Node.js 官方支持的 `packageManager` 字段,与 corepack 配合使用可以实现自动版本管理。 + +### 2. 使用 Corepack + +Corepack 是 Node.js 16.9+ 自带的工具,能够: + +- 自动识别并使用 `package.json` 中指定的包管理器版本 +- 无需手动安装或切换版本 +- 确保团队成员使用一致的工具链 + +**启用方式**: + +```bash +# 一次性设置 +corepack enable + +# 之后在项目目录下运行 pnpm 命令,会自动使用 10.20.0 +pnpm install +``` + +### 3. CI 工作流验证 + +我们在所有 CI 工作流中添加了以下检查: + +1. **版本验证**:运行 `check-pnpm-version.mjs` 脚本确保 pnpm 版本正确 +2. **Lockfile 一致性检查**:在 `pnpm install` 后验证 lockfile 没有被修改 +3. **显式启用 corepack**:在 CI 环境中确保使用正确的版本 + +### 4. 开发者工具 + +我们提供了便捷的检查脚本: + +```bash +# 检查 pnpm 版本是否正确 +pnpm check:pnpm-version + +# 检查 lockfile 是否有未预期的修改 +pnpm check:lockfile +``` + +### 5. Pre-commit Hook + +项目的 pre-commit hook 会在提交前自动运行版本检查,如果发现版本不匹配会给出警告(但不会阻止提交,以免影响正常工作流)。 + +## 开发者指南 + +### 首次设置 + +```bash +# 1. 克隆项目 +git clone https://github.com/involutionhell/involutionhell.git +cd involutionhell + +# 2. 启用 corepack(如果还没启用) +corepack enable + +# 3. 验证 pnpm 版本 +pnpm --version +# 应该显示: 10.20.0 + +# 4. 安装依赖 +pnpm install +``` + +### 日常开发 + +每次拉取新代码后: + +```bash +# 更新依赖 +pnpm install + +# 如果 lockfile 有变化但 package.json 没变化,可能是版本问题 +pnpm check:pnpm-version +``` + +### 遇到版本不匹配问题 + +如果你的 pnpm 版本不正确: + +```bash +# 方法 1:使用 corepack(推荐) +corepack enable +corepack prepare pnpm@10.20.0 --activate + +# 方法 2:全局安装指定版本 +npm install -g pnpm@10.20.0 + +# 重新安装依赖 +rm -rf node_modules +pnpm install +``` + +### 处理意外的 lockfile 变更 + +如果发现 lockfile 被意外修改(只有格式变化,没有实际依赖变化): + +```bash +# 1. 丢弃 lockfile 的修改 +git checkout pnpm-lock.yaml + +# 2. 确认使用正确的 pnpm 版本 +pnpm check:pnpm-version + +# 3. 重新安装 +pnpm install + +# 4. 验证没有变更 +git status +``` + +## CI 配置说明 + +### 工作流变更 + +所有 GitHub Actions 工作流现在包含以下步骤: + +```yaml +steps: + - uses: actions/checkout@v4 + + # 显式启用 corepack + - name: Enable Corepack + run: corepack enable + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + # 验证版本 + - name: Check pnpm version + run: node scripts/check-pnpm-version.mjs + + - run: pnpm install --frozen-lockfile + + # 验证 lockfile 没有被修改 + - name: Check lockfile consistency + run: | + if ! git diff --exit-code pnpm-lock.yaml; then + echo "❌ Error: pnpm-lock.yaml was modified after install" + exit 1 + fi +``` + +### 为什么需要这些检查 + +1. **Enable Corepack**:确保 GitHub Actions 环境使用 `packageManager` 字段指定的版本 +2. **Check pnpm version**:明确验证并报告版本,便于调试 +3. **Check lockfile consistency**:如果 `pnpm install --frozen-lockfile` 后 lockfile 被修改,说明版本不匹配 + +## 常见问题 + +### Q: 为什么不直接在 `pnpm/action-setup@v4` 中指定版本? + +A: 虽然可以在 action 中指定版本,但使用 `packageManager` 字段 + corepack 的方式更符合标准,且能确保本地开发和 CI 环境使用相同的配置源。 + +### Q: 我必须使用 corepack 吗? + +A: 不是必须的。你也可以全局安装 `pnpm@10.20.0`。但 corepack 的优势是可以让不同项目使用不同的 pnpm 版本而不冲突。 + +### Q: 如果我暂时无法升级/切换 pnpm 版本怎么办? + +A: 如果你确实无法切换版本,请在 PR 中说明情况。维护者可以在合并前重新生成 lockfile。但这会增加维护负担,建议尽量使用统一的版本。 + +### Q: 这个问题是 pnpm 的 bug 吗? + +A: 不是 bug。不同版本的工具使用不同的实现是正常的。这个问题的本质是需要团队协调统一工具版本。 + +## 技术细节 + +### pnpm-lock.yaml 格式版本 + +pnpm-lock.yaml 文件开头的 `lockfileVersion` 字段表示格式版本: + +```yaml +lockfileVersion: "9.0" +``` + +不同的 pnpm 版本可能使用不同的 lockfile 格式版本。pnpm 10.x 使用版本 9.0。 + +### YAML 序列化差异 + +不同的 YAML 库对字符串的引号处理规则不同: + +- 某些实现总是使用双引号 +- 某些实现只在必要时使用引号 +- 某些实现偏好单引号 + +pnpm 依赖的 YAML 库在不同版本之间可能有变化,导致相同数据的不同表示。 + +## 参考资源 + +- [Corepack 官方文档](https://nodejs.org/api/corepack.html) +- [pnpm packageManager 字段](https://pnpm.io/package_json#packagemanager) +- [pnpm lockfile 规范](https://pnpm.io/git#lockfiles) + +## 反馈和改进 + +如果你发现此解决方案仍然存在问题,或有改进建议,请: + +1. 在项目中创建 Issue +2. 说明具体的场景和复现步骤 +3. 附上你的环境信息(pnpm 版本、Node 版本、操作系统等) + +我们会持续优化这个流程以减少协作中的摩擦。 diff --git a/README.en.md b/README.en.md index 9a5a459..1313f85 100644 --- a/README.en.md +++ b/README.en.md @@ -44,17 +44,29 @@ A collaborative documentation platform built with modern web technologies to hel **Prerequisites** - Node.js 18+ -- pnpm recommended (npm / yarn also work) +- pnpm 10.20.0 (locked version - see installation instructions below) **Local preview** ```bash git clone https://github.com/involutionhell/involutionhell.git cd involutionhell + +# Recommended: Use corepack (comes with Node.js 16.9+) +corepack enable + +# Or install the specific pnpm version globally +npm install -g pnpm@10.20.0 + +# Install dependencies pnpm install + +# Start dev server pnpm dev ``` +⚠️ **Important**: This project locks pnpm to version `10.20.0` in `package.json`. Please use the same version to avoid `pnpm-lock.yaml` format inconsistencies. Check your version with `pnpm --version` or run `pnpm check:pnpm-version` to verify. + Visit [http://localhost:3000](http://localhost:3000) in your browser. > On Windows with VSCode(Cursor) you may hit a Husky hook issue. Run `git commit` from a terminal instead. diff --git a/README.md b/README.md index 397577b..37eff7c 100644 --- a/README.md +++ b/README.md @@ -59,14 +59,19 @@ cd involutionhell 如果你的电脑还没有安装 pnpm,可以先运行: -# 全局安装 pnpm -npm install -g pnpm +# 推荐方式:使用 corepack(Node.js 16.9+ 自带) +corepack enable -# 安装依赖 +# 或者全局安装指定版本的 pnpm +npm install -g pnpm@10.20.0 + +# 安装依赖(注意:项目锁定了 pnpm@10.20.0,请确保版本一致) pnpm install pnpm dev ``` +⚠️ **重要提示**:本项目在 `package.json` 中锁定了 `pnpm@10.20.0`,请确保使用相同版本以避免 `pnpm-lock.yaml` 格式不一致。使用 `pnpm --version` 检查版本,或运行 `pnpm check:pnpm-version` 验证。 + 打开浏览器访问 [http://localhost:3000](http://localhost:3000)。 > 本地离线调试 Fumadocs 时,如果发现界面加载需要等待远程图片尺寸请求,可以设置环境变量 `DOCS_REMOTE_IMAGE_SIZE=disable` 或直接沿用默认行为(开发模式自动禁用远程图片尺寸请求),显著加快调试速度。如需强制启用远程图片尺寸补全,可手动设置 `DOCS_REMOTE_IMAGE_SIZE=force`。 diff --git a/package.json b/package.json index 58ce485..0fc8e63 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,9 @@ "docs:sync-cuid": "node scripts/uuid.mjs", "lint": "eslint . --ext .ts,.tsx", "lint:fix": "eslint . --ext .ts,.tsx --fix", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "check:pnpm-version": "node scripts/check-pnpm-version.mjs", + "check:lockfile": "node scripts/check-lockfile.mjs" }, "dependencies": { "@ai-sdk/google": "^2.0.14", diff --git a/scripts/check-lockfile.mjs b/scripts/check-lockfile.mjs new file mode 100755 index 0000000..e389f9e --- /dev/null +++ b/scripts/check-lockfile.mjs @@ -0,0 +1,64 @@ +#!/usr/bin/env node +/** + * Script to check if pnpm-lock.yaml has been modified without corresponding package.json changes + * This helps detect unintended lockfile format changes that can occur from version mismatches + */ + +import { execSync } from 'child_process'; + +try { + // Check if we're in a git repository + try { + execSync('git rev-parse --git-dir', { stdio: 'ignore' }); + } catch { + console.log('⚠️ Not in a git repository, skipping lockfile check'); + process.exit(0); + } + + // Get list of staged files + let stagedFiles; + try { + stagedFiles = execSync('git diff --cached --name-only', { encoding: 'utf-8' }).trim(); + } catch { + // No staged files + console.log('✅ No staged files to check'); + process.exit(0); + } + + if (!stagedFiles) { + console.log('✅ No staged files to check'); + process.exit(0); + } + + const stagedFileList = stagedFiles.split('\n').filter(Boolean); + const hasLockfileChange = stagedFileList.includes('pnpm-lock.yaml'); + const hasPackageJsonChange = stagedFileList.includes('package.json'); + + if (hasLockfileChange && !hasPackageJsonChange) { + console.warn('⚠️ Warning: pnpm-lock.yaml is being committed without package.json changes'); + console.warn(''); + console.warn('This might indicate:'); + console.warn('1. A pnpm version mismatch causing lockfile format changes'); + console.warn('2. An unintended dependency resolution change'); + console.warn(''); + console.warn('Please verify:'); + console.warn('- Run: node scripts/check-pnpm-version.mjs'); + console.warn('- Ensure you are using the correct pnpm version specified in package.json'); + console.warn('- Review the lockfile changes carefully'); + console.warn(''); + console.warn('If this is intentional (e.g., fixing a corrupted lockfile), you can proceed.'); + console.warn(''); + // Don't exit with error, just warn + } else if (hasLockfileChange && hasPackageJsonChange) { + console.log('✅ Both package.json and pnpm-lock.yaml are being updated together'); + } else { + console.log('✅ Lockfile check passed'); + } + + process.exit(0); + +} catch (error) { + console.error('❌ Error checking lockfile:', error.message); + // Don't fail the commit, just warn + process.exit(0); +} diff --git a/scripts/check-pnpm-version.mjs b/scripts/check-pnpm-version.mjs new file mode 100755 index 0000000..696c6bd --- /dev/null +++ b/scripts/check-pnpm-version.mjs @@ -0,0 +1,73 @@ +#!/usr/bin/env node +/** + * Script to check if the installed pnpm version matches the version specified in package.json + * This helps ensure consistency in pnpm-lock.yaml formatting across different environments + */ + +import { readFileSync } from 'fs'; +import { execSync } from 'child_process'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +const rootDir = join(__dirname, '..'); + +try { + // Read package.json + const packageJsonPath = join(rootDir, 'package.json'); + const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')); + + // Get expected pnpm version from packageManager field + const packageManager = packageJson.packageManager; + if (!packageManager) { + console.error('❌ Error: No packageManager field found in package.json'); + process.exit(1); + } + + if (!packageManager.startsWith('pnpm@')) { + console.error(`❌ Error: packageManager is not pnpm: ${packageManager}`); + process.exit(1); + } + + const expectedVersion = packageManager.replace('pnpm@', ''); + + // Get actual pnpm version + let actualVersion; + try { + actualVersion = execSync('pnpm --version', { encoding: 'utf-8' }).trim(); + } catch (error) { + console.error('❌ Error: pnpm is not installed or not found in PATH'); + console.error('\nTo fix this issue:'); + console.error('1. Enable corepack: corepack enable'); + console.error('2. Or install pnpm globally: npm install -g pnpm@' + expectedVersion); + process.exit(1); + } + + // Compare versions + if (actualVersion !== expectedVersion) { + console.error(`❌ pnpm version mismatch!`); + console.error(` Expected: ${expectedVersion} (from package.json packageManager field)`); + console.error(` Actual: ${actualVersion}`); + console.error(''); + console.error('This mismatch can cause pnpm-lock.yaml format inconsistencies.'); + console.error(''); + console.error('To fix this issue:'); + console.error(''); + console.error('Option 1 - Use corepack (recommended):'); + console.error(' corepack enable'); + console.error(' corepack prepare pnpm@' + expectedVersion + ' --activate'); + console.error(''); + console.error('Option 2 - Install the correct pnpm version globally:'); + console.error(' npm install -g pnpm@' + expectedVersion); + console.error(''); + process.exit(1); + } + + console.log(`✅ pnpm version check passed: ${actualVersion}`); + process.exit(0); + +} catch (error) { + console.error('❌ Error checking pnpm version:', error.message); + process.exit(1); +} From b38713e01c1eb9888e111ec73d420633bb528c14 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:36:54 +0000 Subject: [PATCH 3/6] Improve error handling and robustness of validation scripts - Use split() instead of replace() for version parsing - Add better error handling with specific error types - Improve error messages in CI workflows with fallbacks Co-authored-by: longsizhuo <114939201+longsizhuo@users.noreply.github.com> --- .github/workflows/content-check.yml | 8 ++++++-- .github/workflows/deploy.yml | 8 ++++++-- scripts/check-lockfile.mjs | 10 ++++++++-- scripts/check-pnpm-version.mjs | 7 ++++++- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/.github/workflows/content-check.yml b/.github/workflows/content-check.yml index f273390..480f23f 100644 --- a/.github/workflows/content-check.yml +++ b/.github/workflows/content-check.yml @@ -54,8 +54,12 @@ jobs: if ! git diff --exit-code pnpm-lock.yaml; then echo "❌ Error: pnpm-lock.yaml was modified after install" echo "This indicates a pnpm version mismatch or corrupted lockfile" - echo "Expected pnpm version: $(node -p 'require(\"./package.json\").packageManager')" - echo "Actual pnpm version: $(pnpm --version)" + echo "" + echo "Expected pnpm version from package.json:" + node -p "require('./package.json').packageManager || 'not specified'" || echo "Could not read package.json" + echo "" + echo "Actual pnpm version:" + pnpm --version || echo "pnpm not found" exit 1 fi echo "✅ Lockfile is consistent" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index dc38bb9..473c766 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -43,8 +43,12 @@ jobs: if ! git diff --exit-code pnpm-lock.yaml; then echo "❌ Error: pnpm-lock.yaml was modified after install" echo "This indicates a pnpm version mismatch or corrupted lockfile" - echo "Expected pnpm version: $(node -p 'require(\"./package.json\").packageManager')" - echo "Actual pnpm version: $(pnpm --version)" + echo "" + echo "Expected pnpm version from package.json:" + node -p "require('./package.json').packageManager || 'not specified'" || echo "Could not read package.json" + echo "" + echo "Actual pnpm version:" + pnpm --version || echo "pnpm not found" exit 1 fi echo "✅ Lockfile is consistent" diff --git a/scripts/check-lockfile.mjs b/scripts/check-lockfile.mjs index e389f9e..3d4f721 100755 --- a/scripts/check-lockfile.mjs +++ b/scripts/check-lockfile.mjs @@ -58,7 +58,13 @@ try { process.exit(0); } catch (error) { - console.error('❌ Error checking lockfile:', error.message); - // Don't fail the commit, just warn + if (error.code === 'ENOENT') { + console.error('❌ Error: Git repository or command not found'); + } else if (error.message.includes('not a git repository')) { + console.log('⚠️ Not in a git repository, skipping lockfile check'); + } else { + console.error('❌ Error checking lockfile:', error.message); + } + // Don't fail the commit, just provide information process.exit(0); } diff --git a/scripts/check-pnpm-version.mjs b/scripts/check-pnpm-version.mjs index 696c6bd..a129b03 100755 --- a/scripts/check-pnpm-version.mjs +++ b/scripts/check-pnpm-version.mjs @@ -30,7 +30,12 @@ try { process.exit(1); } - const expectedVersion = packageManager.replace('pnpm@', ''); + const expectedVersion = packageManager.split('@')[1]; + + if (!expectedVersion) { + console.error('❌ Error: Could not parse pnpm version from packageManager field'); + process.exit(1); + } // Get actual pnpm version let actualVersion; From a812e36973a484762eb3ecd395b75dcc04d85a7a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:38:36 +0000 Subject: [PATCH 4/6] Further improve robustness of version parsing and error messages - Use substring() instead of split() to handle edge cases in version parsing - Replace Node.js inline require with grep for better portability in CI - Add validation for empty version strings Co-authored-by: longsizhuo <114939201+longsizhuo@users.noreply.github.com> --- .github/workflows/content-check.yml | 2 +- .github/workflows/deploy.yml | 2 +- scripts/check-pnpm-version.mjs | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/content-check.yml b/.github/workflows/content-check.yml index 480f23f..1cef06f 100644 --- a/.github/workflows/content-check.yml +++ b/.github/workflows/content-check.yml @@ -56,7 +56,7 @@ jobs: echo "This indicates a pnpm version mismatch or corrupted lockfile" echo "" echo "Expected pnpm version from package.json:" - node -p "require('./package.json').packageManager || 'not specified'" || echo "Could not read package.json" + grep -o 'pnpm@[^"]*' package.json | head -1 || echo "Could not extract version" echo "" echo "Actual pnpm version:" pnpm --version || echo "pnpm not found" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 473c766..058e25d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -45,7 +45,7 @@ jobs: echo "This indicates a pnpm version mismatch or corrupted lockfile" echo "" echo "Expected pnpm version from package.json:" - node -p "require('./package.json').packageManager || 'not specified'" || echo "Could not read package.json" + grep -o 'pnpm@[^"]*' package.json | head -1 || echo "Could not extract version" echo "" echo "Actual pnpm version:" pnpm --version || echo "pnpm not found" diff --git a/scripts/check-pnpm-version.mjs b/scripts/check-pnpm-version.mjs index a129b03..0894316 100755 --- a/scripts/check-pnpm-version.mjs +++ b/scripts/check-pnpm-version.mjs @@ -30,10 +30,12 @@ try { process.exit(1); } - const expectedVersion = packageManager.split('@')[1]; + // Extract version: "pnpm@10.20.0" -> "10.20.0" + const expectedVersion = packageManager.substring(packageManager.indexOf('@') + 1); - if (!expectedVersion) { + if (!expectedVersion || expectedVersion.trim() === '') { console.error('❌ Error: Could not parse pnpm version from packageManager field'); + console.error(` packageManager value: ${packageManager}`); process.exit(1); } From 83fc037e8c76212b0645794ad88fd4af8bc5a496 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:42:00 +0000 Subject: [PATCH 5/6] Final robustness improvements based on code review - Validate packageManager format by ensuring exactly 2 parts after split - Make error handling in check-lockfile.mjs consistent (all warnings) - Add fallback methods for version extraction in CI workflows - Improve error messages and validation logic Co-authored-by: longsizhuo <114939201+longsizhuo@users.noreply.github.com> --- .github/workflows/content-check.yml | 5 ++++- .github/workflows/deploy.yml | 5 ++++- scripts/check-lockfile.mjs | 7 ++++--- scripts/check-pnpm-version.mjs | 13 +++++++++++-- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/.github/workflows/content-check.yml b/.github/workflows/content-check.yml index 1cef06f..f0286ba 100644 --- a/.github/workflows/content-check.yml +++ b/.github/workflows/content-check.yml @@ -56,7 +56,10 @@ jobs: echo "This indicates a pnpm version mismatch or corrupted lockfile" echo "" echo "Expected pnpm version from package.json:" - grep -o 'pnpm@[^"]*' package.json | head -1 || echo "Could not extract version" + # Use multiple fallback methods to extract version + grep '"packageManager"' package.json | grep -o 'pnpm@[^"]*' || \ + node -e "try { console.log(require('./package.json').packageManager || 'not specified') } catch(e) { console.log('Could not read') }" || \ + echo "Could not extract version" echo "" echo "Actual pnpm version:" pnpm --version || echo "pnpm not found" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 058e25d..ae218a4 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -45,7 +45,10 @@ jobs: echo "This indicates a pnpm version mismatch or corrupted lockfile" echo "" echo "Expected pnpm version from package.json:" - grep -o 'pnpm@[^"]*' package.json | head -1 || echo "Could not extract version" + # Use multiple fallback methods to extract version + grep '"packageManager"' package.json | grep -o 'pnpm@[^"]*' || \ + node -e "try { console.log(require('./package.json').packageManager || 'not specified') } catch(e) { console.log('Could not read') }" || \ + echo "Could not extract version" echo "" echo "Actual pnpm version:" pnpm --version || echo "pnpm not found" diff --git a/scripts/check-lockfile.mjs b/scripts/check-lockfile.mjs index 3d4f721..3206f4b 100755 --- a/scripts/check-lockfile.mjs +++ b/scripts/check-lockfile.mjs @@ -58,12 +58,13 @@ try { process.exit(0); } catch (error) { + // All errors are treated as non-fatal - provide info but don't block the commit if (error.code === 'ENOENT') { - console.error('❌ Error: Git repository or command not found'); - } else if (error.message.includes('not a git repository')) { + console.log('⚠️ Git command not found, skipping lockfile check'); + } else if (error.message && error.message.includes('not a git repository')) { console.log('⚠️ Not in a git repository, skipping lockfile check'); } else { - console.error('❌ Error checking lockfile:', error.message); + console.log('⚠️ Could not check lockfile:', error.message); } // Don't fail the commit, just provide information process.exit(0); diff --git a/scripts/check-pnpm-version.mjs b/scripts/check-pnpm-version.mjs index 0894316..f88ace4 100755 --- a/scripts/check-pnpm-version.mjs +++ b/scripts/check-pnpm-version.mjs @@ -31,9 +31,18 @@ try { } // Extract version: "pnpm@10.20.0" -> "10.20.0" - const expectedVersion = packageManager.substring(packageManager.indexOf('@') + 1); + // Split and validate to handle edge cases properly + const parts = packageManager.split('@'); + if (parts.length !== 2) { + console.error('❌ Error: Invalid packageManager format'); + console.error(` Expected format: pnpm@x.y.z`); + console.error(` Actual value: ${packageManager}`); + process.exit(1); + } + + const expectedVersion = parts[1].trim(); - if (!expectedVersion || expectedVersion.trim() === '') { + if (!expectedVersion) { console.error('❌ Error: Could not parse pnpm version from packageManager field'); console.error(` packageManager value: ${packageManager}`); process.exit(1); From b2987dd80436ad8ddf4f0f30e5ec8c3a79dc7bb4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:44:23 +0000 Subject: [PATCH 6/6] Polish validation scripts and add clarifying comments - Add comment explaining packageManager format specification - Simplify error handling in check-lockfile.mjs to avoid redundancy - Improve error messages with format examples Co-authored-by: longsizhuo <114939201+longsizhuo@users.noreply.github.com> --- scripts/check-lockfile.mjs | 11 ++--------- scripts/check-pnpm-version.mjs | 6 ++++-- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/scripts/check-lockfile.mjs b/scripts/check-lockfile.mjs index 3206f4b..6137732 100755 --- a/scripts/check-lockfile.mjs +++ b/scripts/check-lockfile.mjs @@ -58,14 +58,7 @@ try { process.exit(0); } catch (error) { - // All errors are treated as non-fatal - provide info but don't block the commit - if (error.code === 'ENOENT') { - console.log('⚠️ Git command not found, skipping lockfile check'); - } else if (error.message && error.message.includes('not a git repository')) { - console.log('⚠️ Not in a git repository, skipping lockfile check'); - } else { - console.log('⚠️ Could not check lockfile:', error.message); - } - // Don't fail the commit, just provide information + // All errors are treated as non-fatal warnings + console.log('⚠️ Could not check lockfile:', error.message); process.exit(0); } diff --git a/scripts/check-pnpm-version.mjs b/scripts/check-pnpm-version.mjs index f88ace4..693ffc8 100755 --- a/scripts/check-pnpm-version.mjs +++ b/scripts/check-pnpm-version.mjs @@ -31,12 +31,14 @@ try { } // Extract version: "pnpm@10.20.0" -> "10.20.0" - // Split and validate to handle edge cases properly + // The packageManager field format is strictly "package@version" + // Additional @ symbols are not part of the standard spec const parts = packageManager.split('@'); if (parts.length !== 2) { console.error('❌ Error: Invalid packageManager format'); - console.error(` Expected format: pnpm@x.y.z`); + console.error(` Expected format: pnpm@x.y.z (e.g., pnpm@10.20.0)`); console.error(` Actual value: ${packageManager}`); + console.error(` Note: The packageManager field should contain exactly one @ separator`); process.exit(1); }