Skip to content

Commit b2fc70f

Browse files
authored
Merge pull request #5 from EricLLLLLL/test
chore: release v1.1.36 (ensure bunx creation)
2 parents 60a7d98 + 51b3a0b commit b2fc70f

16 files changed

Lines changed: 286 additions & 86 deletions

File tree

conductor/tracks.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This file tracks all major tracks for the project. Each track has its own detail
44

55
---
66

7-
## [ ] Track: 增强 Shim 解析逻辑与项目上下文检测
7+
## [~] Track: 增强 Shim 解析逻辑与项目上下文检测
88

99
*Link: [./conductor/tracks/shim_context_20251222/](./conductor/tracks/shim_context_20251222/)*
1010

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Track fix_windows_bunx_20260128 Context
2+
3+
- [Specification](./spec.md)
4+
- [Implementation Plan](./plan.md)
5+
- [Metadata](./metadata.json)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"track_id": "fix_windows_bunx_20260128",
3+
"type": "bug",
4+
"status": "new",
5+
"created_at": "2026-01-28T10:00:00Z",
6+
"updated_at": "2026-01-28T10:00:00Z",
7+
"description": "修复 BVM 在 Windows 环境下 bunx 命令失效的问题,主要涉及身份识别和显式路由优化。"
8+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
### Implementation Plan: fix_windows_bunx_20260128
2+
3+
#### Phase 1: Core Shim & Template Optimization
4+
*目标:在核心代码层面修复身份识别和路由逻辑。*
5+
6+
- [ ] Task: 更新 Windows JS Shim 模板 (`src/templates/win/bvm-shim.js`),实现 `bunx``bun x` 的显式指令路由。 [x]
7+
- [ ] Task: 更新 Windows CMD 模板 (`src/templates/win/bunx.cmd` & `bun.cmd`),为快速通道添加 `x` 路由支持。 [x]
8+
- [ ] Task: 优化 Unix Shim 模板 (`src/templates/unix/bvm-shim.sh`),使用 `exec -a` 增强身份识别稳定性。 [x]
9+
- [ ] Task: Conductor - User Manual Verification 'Phase 1: Core Shim & Template Optimization' (Protocol in workflow.md)
10+
11+
#### Phase 2: Installer & Self-Repair Evolution
12+
*目标:确保新安装及现有环境均能自动补全所需的物理文件。*
13+
14+
- [ ] Task: 改进 `install.ps1` (Windows),增加 `bunx.exe` 副本生成逻辑及旧版本扫描修复。 [x]
15+
- [ ] Task: 改进 `install.sh` (Unix),完善运行时下载后的 `bunx` 软链接创建。 [x]
16+
- [ ] Task: 更新 `src/commands/setup.ts`,确保运行 `bvm setup` 时能递归修复所有已安装版本的 `bunx` 文件。 [ ]
17+
- [ ] Task: Conductor - User Manual Verification 'Phase 2: Installer & Self-Repair Evolution' (Protocol in workflow.md)
18+
19+
#### Phase 3: Verification & TDD
20+
*目标:通过测试验证修复效果,并确保发布版本同步。*
21+
22+
- [ ] Task: 编写或更新 `test/bunx_consistency.test.ts`,在模拟环境下验证 Windows 路由和文件补全。 [ ]
23+
- [ ] Task: 执行 `bun run build` 同步所有安装脚本版本,并运行全量测试。 [x]
24+
- [ ] Task: Conductor - User Manual Verification 'Phase 3: Verification & TDD' (Protocol in workflow.md)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
### Track Specification: fix_windows_bunx_20260128
2+
3+
#### Overview
4+
修复 BVM 在 Windows 环境下 `bunx` 命令失效的问题。
5+
6+
**当前问题证据(用户截图/日志):**
7+
```powershell
8+
PS C:\Users\steph\OneDrive\Desktop> bunx
9+
Bun is a fast JavaScript runtime... (输出了帮助信息,未识别为 bunx)
10+
PS C:\Users\steph\OneDrive\Desktop> bunx skills add microsoft/PowerToys
11+
error: Script not found "skills" (身份识别错误导致路由到了 bun run 逻辑)
12+
```
13+
14+
#### Functional Requirements
15+
1. **显式路由逻辑**:修改 Windows 垫片(Shim)逻辑,将 `bunx <args>` 显式路由为 `bun x <args>`,确保 100% 触发 Bun 的扩展包运行逻辑。
16+
2. **物理文件补全**:在安装 Bun 运行时或执行 `bvm setup` 时,确保版本 bin 目录下存在 `bunx.exe`
17+
3. **安装脚本自愈**
18+
* 更新 `install.ps1`,在安装新版本或检测到系统 Bun 时,自动补全缺失的 `bunx.exe`
19+
* 更新 `install.sh`,同步确保 Unix 环境下的 `bunx` 软链接正确。
20+
4. **环境一键修复**:通过 `npm i . -g``bvm setup` 即可自动修复所有已安装版本的 `bunx` 坏道。
21+
5. **版本切换支持**`bunx` 必须严格遵循 `.bvmrc` 或全局默认版本的切换逻辑。
22+
23+
#### Acceptance Criteria
24+
- [ ] **身份验证**:在 Windows 中运行 `bunx --version` 返回版本号,不再返回 Bun 的通用帮助信息。
25+
- [ ] **功能验证**:运行 `bunx skills add ...` 能正确识别 `skills` 为包名并执行,不再报 "Script not found"。
26+
- [ ] **物理验证**:重新运行 `bvm setup` 后,所有已安装版本的 `bin` 目录下均出现 `bunx` 文件。
27+
- [ ] **安装验证**:运行 `install.sh``install.ps1` 完成后,新安装的版本自带可运行的 `bunx`

install.ps1

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ function Detect-NetworkZone {
3939
$BVM_REGION = Detect-NetworkZone
4040
$REGISTRY = if ($BVM_REGION -eq "cn") { "registry.npmmirror.com" } else { "registry.npmjs.org" }
4141

42-
$DEFAULT_BVM_VER = "v1.1.35"
42+
$DEFAULT_BVM_VER = "v1.1.36"
4343
$BVM_VER = if ($env:BVM_INSTALL_VERSION) { $env:BVM_INSTALL_VERSION } else { "" }
4444
if (-not $BVM_VER) {
4545
try {
@@ -97,6 +97,7 @@ if ($SYSTEM_BUN_BIN) {
9797
$SYS_BIN_DIR = Join-Path $SYS_VER_DIR "bin"
9898
if (-not (Test-Path $SYS_BIN_DIR)) { New-Item -ItemType Directory -Path $SYS_BIN_DIR -Force | Out-Null }
9999
Copy-Item $SYSTEM_BUN_BIN (Join-Path $SYS_BIN_DIR "bun.exe") -Force
100+
Copy-Item $SYSTEM_BUN_BIN (Join-Path $SYS_BIN_DIR "bunx.exe") -Force
100101

101102
# Smoke Test
102103
$BvmIndex = Join-Path $BVM_SRC_DIR "index.js"
@@ -129,6 +130,7 @@ if ($USE_SYSTEM_AS_RUNTIME) {
129130
$BIN_DEST = Join-Path $TARGET_DIR "bin"
130131
if (-not (Test-Path $BIN_DEST)) { New-Item -ItemType Directory -Path $BIN_DEST -Force | Out-Null }
131132
Move-Item -Path $FoundBun.FullName -Destination (Join-Path $BIN_DEST "bun.exe") -Force
133+
Copy-Item (Join-Path $BIN_DEST "bun.exe") (Join-Path $BIN_DEST "bunx.exe") -Force
132134
Remove-Item $TMP -Force
133135
Remove-Item $EXT -Recurse -Force
134136
}
@@ -163,7 +165,7 @@ set "BVM_DIR=$WinBvmDir"
163165
set "BUN_INSTALL=%BVM_DIR%\current"
164166
165167
if not exist ".bvmrc" (
166-
"%BVM_DIR%\runtime\current\bin\bun.exe" %*
168+
"%BVM_DIR%\runtime\current\bin\%~n0.exe" %*
167169
exit /b %errorlevel%
168170
)
169171

install.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
set -e
44

55
# --- Configuration ---
6-
DEFAULT_BVM_VERSION="v1.1.35" # Fallback
6+
DEFAULT_BVM_VERSION="v1.1.36" # Fallback
77
FALLBACK_BUN_VERSION="1.3.6"
88
BVM_SRC_VERSION="${BVM_INSTALL_VERSION}" # If empty, will resolve dynamically
99

@@ -153,6 +153,7 @@ if [ -n "$SYSTEM_BUN_BIN" ]; then
153153
cp "$SYSTEM_BUN_BIN" "${SYS_VER_DIR}/bin/bun"
154154
fi
155155
chmod +x "${SYS_VER_DIR}/bin/bun"
156+
ln -sf "./bun" "${SYS_VER_DIR}/bin/bunx"
156157

157158
# Smoke Test
158159
if "${SYS_VER_DIR}/bin/bun" "${BVM_SRC_DIR}/index.js" --version >/dev/null 2>&1; then
@@ -206,6 +207,7 @@ else
206207
mkdir -p "${TARGET_RUNTIME_DIR}/bin"
207208
mv "$(find "$TEMP_DIR_BUN" -type f -name "bun" | head -n 1)" "${TARGET_RUNTIME_DIR}/bin/bun"
208209
chmod +x "${TARGET_RUNTIME_DIR}/bin/bun"
210+
ln -sf "./bun" "${TARGET_RUNTIME_DIR}/bin/bunx"
209211
rm -rf "$TEMP_DIR_BUN"
210212
fi
211213
fi

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "bvm-core",
3-
"version": "1.1.35",
4-
"description": "The native version manager for Bun. Cross-platform, shell-agnostic, and zero-dependency.",
3+
"version": "1.1.36",
4+
"description": "Bun Version Manager (BVM) - Native, fast, and cross-platform.",
55
"main": "dist/index.js",
66
"bin": {
77
"bvm": "bin/bvm-npm.js"

scripts/verify-e2e-npm.ts

Lines changed: 115 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,82 +2,130 @@
22
import { $ } from "bun";
33
import { join } from "path";
44
import { homedir } from "os";
5-
import { existsSync, readdirSync } from "fs";
5+
import { existsSync, readdirSync, mkdirSync, writeFileSync, chmodSync, rmSync } from "fs";
66

77
const HOME = homedir();
88
const BVM_DIR = join(HOME, ".bvm");
99
const SHIMS_DIR = join(BVM_DIR, "shims");
1010
const BIN_DIR = join(BVM_DIR, "bin");
1111

12-
console.log("\n🚀 Starting E2E NPM Verification Protocol...\n");
13-
14-
try {
15-
// 1. Cleanup
16-
console.log(`🧹 Cleaning up environment (${BVM_DIR})...`);
17-
await $`rm -rf ${BVM_DIR}`;
18-
await $`rm -f bvm-core-*.tgz`;
19-
20-
// 2. Build & Pack
21-
console.log("📦 Building and Packing...");
22-
// Using simple shell command to ensure npm run build executes correctly
23-
await $`npm run build`.quiet();
24-
await $`npm pack`.quiet();
25-
26-
// Find the tarball
27-
const files = readdirSync(process.cwd());
28-
const tarball = files.find(f => f.startsWith("bvm-core-") && f.endsWith(".tgz"));
29-
if (!tarball) throw new Error("Tarball not found after packing!");
30-
console.log(`📝 Found tarball: ${tarball}`);
31-
32-
// 3. Install
33-
console.log("💿 Installing globally via NPM (this may take a moment)...");
34-
// We capture stdout/stderr to show it only on failure, or stream it?
35-
// Let's stream it to be transparent as requested.
36-
await $`npm install -g ./${tarball} --foreground-scripts --force`;
37-
38-
// 4. Verify Physical Structure
39-
console.log("🔍 Verifying filesystem structure...");
40-
41-
if (!existsSync(SHIMS_DIR)) throw new Error(`❌ Shims directory missing: ${SHIMS_DIR}`);
42-
if (!existsSync(join(SHIMS_DIR, "bun"))) throw new Error(`❌ Bun shim missing`);
43-
if (!existsSync(join(BIN_DIR, "bvm"))) throw new Error(`❌ BVM binary missing`);
44-
45-
console.log(" ✅ Shims directory exists.");
46-
console.log(" ✅ Bun shim exists.");
47-
48-
// 5. Functional Verification
49-
console.log("🧪 Verifying BVM functionality...");
50-
const bvmExec = join(BIN_DIR, "bvm");
51-
const output = await $`${bvmExec} ls`.text();
52-
console.log(output.trim());
53-
54-
if (!output.includes("Locally installed Bun versions")) {
55-
throw new Error("❌ 'bvm ls' output is unexpected.");
12+
export class E2ESandbox {
13+
public bvmDir: string;
14+
public shimsDir: string;
15+
public binDir: string;
16+
private tempHome: string;
17+
18+
constructor() {
19+
this.tempHome = join(process.cwd(), `.tmp-npm-verify-${Date.now()}`);
20+
this.bvmDir = join(this.tempHome, ".bvm");
21+
this.shimsDir = join(this.bvmDir, "shims");
22+
this.binDir = join(this.bvmDir, "bin");
23+
if (!existsSync(this.tempHome)) mkdirSync(this.tempHome, { recursive: true });
24+
}
25+
26+
async installLocal() {
27+
// Mock implementation for testing safety/upgrade logic without full NPM network hit
28+
if (!existsSync(this.bvmDir)) mkdirSync(this.bvmDir, { recursive: true });
29+
if (!existsSync(this.shimsDir)) mkdirSync(this.shimsDir, { recursive: true });
30+
if (!existsSync(this.binDir)) mkdirSync(this.binDir, { recursive: true });
31+
32+
const bvmBin = join(this.binDir, "bvm");
33+
const bvmSrcDir = join(this.bvmDir, "src");
34+
if (!existsSync(bvmSrcDir)) mkdirSync(bvmSrcDir, { recursive: true });
35+
36+
// Marker for NPM install
37+
const marker = join(this.bvmDir, ".npm-install");
38+
writeFileSync(marker, "true");
39+
40+
// Dummy wrapper
41+
writeFileSync(bvmBin, `#!/bin/bash\nexport BVM_INSTALL_SOURCE="npm"\nexec ${process.execPath} ${join(this.bvmDir, "src", "index.js")} "$@"\n`);
42+
chmodSync(bvmBin, 0o755);
43+
44+
// Dummy source
45+
writeFileSync(join(bvmSrcDir, "index.js"), "import '...'; console.log('bvm')");
5646
}
5747

58-
// 6. Global Package Isolation Verification (NEW)
59-
console.log("📦 Verifying Global Package Isolation (Option B)...");
60-
// Ensure we are using a version
61-
await $`${bvmExec} use default`.quiet();
62-
// Simulate bun install -g
63-
console.log(" Installing dummy global package...");
64-
await $`${join(SHIMS_DIR, "bun")} install -g fake-pkg-test-bvm`.quiet().catch(() => {});
65-
66-
const currentBin = join(BVM_DIR, "current", "bin");
67-
console.log(` Checking if current bin path is correctly set up: ${currentBin}`);
68-
69-
// We check if current/bin is in the PATH reported by setup
70-
const zshrc = await $`cat ${join(HOME, ".zshrc")}`.text();
71-
if (!zshrc.includes("current/bin")) {
72-
throw new Error("❌ .zshrc does not contain 'current/bin' in PATH");
48+
cleanup() {
49+
if (existsSync(this.tempHome)) rmSync(this.tempHome, { recursive: true, force: true });
7350
}
51+
}
52+
53+
async function runProtocol() {
54+
console.log("\n🚀 Starting E2E NPM Verification Protocol...\n");
55+
56+
try {
57+
// 1. Cleanup
58+
console.log(`🧹 Cleaning up environment (${BVM_DIR})...`);
59+
await $`rm -rf ${BVM_DIR}`;
60+
await $`rm -f bvm-core-*.tgz`.nothrow(); // Use nothrow to avoid error if no tgz exists
61+
62+
// 2. Build & Pack
63+
console.log("📦 Building and Packing...");
64+
// Using simple shell command to ensure npm run build executes correctly
65+
await $`npm run build`.quiet();
66+
await $`npm pack`.quiet();
67+
68+
// Find the tarball
69+
const files = readdirSync(process.cwd());
70+
const tarball = files.find(f => f.startsWith("bvm-core-") && f.endsWith(".tgz"));
71+
if (!tarball) throw new Error("Tarball not found after packing!");
72+
console.log(`📝 Found tarball: ${tarball}`);
73+
74+
// 3. Install
75+
console.log("💿 Installing globally via NPM (this may take a moment)...");
76+
// We capture stdout/stderr to show it only on failure, or stream it?
77+
// Let's stream it to be transparent as requested.
78+
await $`npm install -g ./${tarball} --foreground-scripts --force`;
7479

75-
console.log("\n✅ \x1b[32mE2E VERIFICATION PASSED!\x1b[0m");
76-
console.log(" BVM is installed, shims exist, and CLI works.");
77-
console.log(` Run 'source ~/.zshrc' (or your shell config) to start using it.`);
80+
// 4. Verify Physical Structure
81+
console.log("🔍 Verifying filesystem structure...");
82+
83+
if (!existsSync(SHIMS_DIR)) throw new Error(`❌ Shims directory missing: ${SHIMS_DIR}`);
84+
if (!existsSync(join(SHIMS_DIR, "bun"))) throw new Error(`❌ Bun shim missing`);
85+
if (!existsSync(join(BIN_DIR, "bvm"))) throw new Error(`❌ BVM binary missing`);
86+
87+
console.log(" ✅ Shims directory exists.");
88+
console.log(" ✅ Bun shim exists.");
89+
90+
// 5. Functional Verification
91+
console.log("🧪 Verifying BVM functionality...");
92+
const bvmExec = join(BIN_DIR, "bvm");
93+
const output = await $`${bvmExec} ls`.text();
94+
console.log(output.trim());
95+
96+
if (!output.includes("Locally installed Bun versions")) {
97+
throw new Error("❌ 'bvm ls' output is unexpected.");
98+
}
99+
100+
// 6. Global Package Isolation Verification (NEW)
101+
console.log("📦 Verifying Global Package Isolation (Option B)...");
102+
// Ensure we are using a version
103+
await $`${bvmExec} use default`.quiet();
104+
// Simulate bun install -g
105+
console.log(" Installing dummy global package...");
106+
await $`${join(SHIMS_DIR, "bun")} install -g fake-pkg-test-bvm`.quiet().catch(() => {});
107+
108+
const currentBin = join(BVM_DIR, "current", "bin");
109+
console.log(` Checking if current bin path is correctly set up: ${currentBin}`);
110+
111+
// We check if current/bin is in the PATH reported by setup
112+
const zshrc = await $`cat ${join(HOME, ".zshrc")}`.text();
113+
if (!zshrc.includes("current/bin")) {
114+
throw new Error("❌ .zshrc does not contain 'current/bin' in PATH");
115+
}
116+
117+
console.log("\n✅ \x1b[32mE2E VERIFICATION PASSED!\x1b[0m");
118+
console.log(" BVM is installed, shims exist, and CLI works.");
119+
console.log(` Run 'source ~/.zshrc' (or your shell config) to start using it.`);
120+
121+
} catch (e) {
122+
console.error("\n💥 \x1b[31mVERIFICATION FAILED:\x1b[0m");
123+
console.error(e);
124+
process.exit(1);
125+
}
126+
}
78127

79-
} catch (e) {
80-
console.error("\n💥 \x1b[31mVERIFICATION FAILED:\x1b[0m");
81-
console.error(e);
82-
process.exit(1);
128+
// Check if this script is being run directly
129+
if (import.meta.main) {
130+
runProtocol();
83131
}

0 commit comments

Comments
 (0)