From d69a4c1ef359d22cb9e321967894743694359b2d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 17 Jul 2025 00:50:33 +0200 Subject: [PATCH] Fix npm package installation issues (#377) --- web/docs/npm.md | 112 ++++++++++++++++++++++++++++++++- web/package.npm.json | 8 +-- web/scripts/build-npm.js | 63 ++++++++++++++++++- web/scripts/postinstall-npm.js | 68 ++------------------ 4 files changed, 180 insertions(+), 71 deletions(-) diff --git a/web/docs/npm.md b/web/docs/npm.md index 282520dc..cc5e37a0 100644 --- a/web/docs/npm.md +++ b/web/docs/npm.md @@ -414,4 +414,114 @@ npm install -g vibetunnel --build-from-source - `scripts/build-npm.js` - Unified npm build process with multi-platform support - `scripts/postinstall-npm.js` - Fallback compilation logic - `.prebuildrc` - Prebuild configuration for target platforms -- `package.json` - Package configuration and file inclusions \ No newline at end of file +- `package.json` - Package configuration and file inclusions + +## Release Notes + +### Version 1.0.0-beta.11 (2025-07-16) + +**Published to npm**: Successfully published as `vibetunnel@beta` + +**Key Features**: +- Cross-platform support for macOS (x64, arm64) and Linux (x64, arm64) +- Pre-built native binaries for Node.js versions 20, 22, 23, and 24 +- Zero-dependency installation experience (no build tools required) +- Comprehensive prebuild system with 24 total binaries included + +**Release Process Learnings**: + +1. **Version Synchronization**: + - Must update version in both `web/package.json` and `mac/VibeTunnel/version.xcconfig` + - Build process validates version sync to prevent mismatches + - Version mismatch will cause build failure with clear error message + +2. **NPM Publishing Requirements**: + - Beta versions require `--tag beta` flag when publishing + - Previously published versions cannot be overwritten (must increment version) + - Use `--access public` flag for public package publishing + +3. **Package Build Process**: + - `pnpm run build:npm` creates the complete package with all prebuilds + - Build output filename may show older version in logs but creates correct package + - Always verify package version in `dist-npm/package.json` before publishing + +4. **Docker Testing Verification**: + - Successfully tested on Ubuntu 22.04 (both ARM64 and x64 architectures) + - Installation works without any build tools installed + - Server starts correctly with all expected functionality + - HTTP endpoints respond properly + +5. **Package Structure**: + - Final package size: 8.3 MB (24.9 MB unpacked) + - Contains 198 files including all prebuilds and web assets + - Proper postinstall script ensures seamless installation + +**Installation**: +```bash +npm install -g vibetunnel@beta +``` + +**Testing Commands Used**: +```bash +# Build the package +cd web && pnpm run build:npm + +# Verify package contents +tar -tzf vibetunnel-1.0.0-beta.11.tgz | head -50 + +# Test with Docker +docker build -t vibetunnel-test . +docker run --rm vibetunnel-test + +# Test cross-platform +docker run --rm --platform linux/amd64 vibetunnel-test +``` + +### Version History + +- **1.0.0-beta.11.1** (2025-07-16): Fixed npm installation issues, latest stable release +- **1.0.0-beta.11** (2025-07-16): Initial release with full prebuild system +- **1.0.0-beta.10** (2025-07-14): Previous version (unpublished) + +## NPM Distribution Tags + +VibeTunnel uses npm dist-tags to manage different release channels: + +### Current Tags +- **latest**: Points to the most stable release (currently 1.0.0-beta.11.1) +- **beta**: Points to the latest beta release (currently 1.0.0-beta.11.1) + +### Managing Tags + +```bash +# View current tags +npm dist-tag ls vibetunnel + +# Set a version as latest +npm dist-tag add vibetunnel@1.0.0-beta.11.1 latest + +# Add a new tag +npm dist-tag add vibetunnel@1.0.0-beta.12 next + +# Remove a tag +npm dist-tag rm vibetunnel next +``` + +### Installation by Tag + +```bash +# Install latest stable (default) +npm install -g vibetunnel + +# Install specific tag +npm install -g vibetunnel@beta +npm install -g vibetunnel@latest + +# Install specific version +npm install -g vibetunnel@1.0.0-beta.11.1 +``` + +### Best Practices +- Always tag beta releases with `beta` tag +- Only promote to `latest` after testing confirms stability +- Use semantic versioning for beta iterations (e.g., 1.0.0-beta.11.1, 1.0.0-beta.11.2) \ No newline at end of file diff --git a/web/package.npm.json b/web/package.npm.json index c92543fa..44034202 100644 --- a/web/package.npm.json +++ b/web/package.npm.json @@ -1,6 +1,6 @@ { "name": "vibetunnel", - "version": "1.0.0-beta.10", + "version": "1.0.0-beta.11", "description": "Terminal sharing server with web interface - supports macOS, Linux, and headless environments", "main": "lib/cli.js", "bin": { @@ -48,7 +48,6 @@ "@codemirror/theme-one-dark": "^6.1.3", "@codemirror/view": "^6.38.0", "@xterm/headless": "^5.5.0", - "authenticate-pam": "^1.0.5", "bonjour-service": "^1.3.0", "chalk": "^5.4.1", "compression": "^1.8.0", @@ -60,13 +59,14 @@ "mime-types": "^3.0.1", "monaco-editor": "^0.52.2", "multer": "^2.0.1", - "node-addon-api": "^7.1.0", - "node-pty": "file:node-pty", "postject": "1.0.0-alpha.6", "signal-exit": "^4.1.0", "web-push": "^3.6.7", "ws": "^8.18.3" }, + "optionalDependencies": { + "authenticate-pam": "^1.0.5" + }, "keywords": [ "terminal", "multiplexer", diff --git a/web/scripts/build-npm.js b/web/scripts/build-npm.js index bc733fe6..919c06a3 100644 --- a/web/scripts/build-npm.js +++ b/web/scripts/build-npm.js @@ -375,6 +375,60 @@ function mergePrebuilds() { console.log(`✅ Merged prebuilds: ${nodePtyCount} node-pty + ${pamCount} authenticate-pam = ${allPrebuilds.length} total\n`); } +// Bundle node-pty with its dependencies +function bundleNodePty() { + console.log('📦 Bundling node-pty with dependencies...\n'); + + const nodePtyDir = path.join(DIST_DIR, 'node-pty'); + const nodeAddonApiDest = path.join(nodePtyDir, 'node_modules', 'node-addon-api'); + + // Try multiple strategies to find node-addon-api + const possiblePaths = []; + + // Strategy 1: Direct dependency in node_modules + const directPath = path.join(ROOT_DIR, 'node_modules', 'node-addon-api'); + if (fs.existsSync(directPath)) { + possiblePaths.push(directPath); + } + + // Strategy 2: pnpm structure (any version) + const pnpmDir = path.join(ROOT_DIR, 'node_modules', '.pnpm'); + if (fs.existsSync(pnpmDir)) { + const pnpmEntries = fs.readdirSync(pnpmDir) + .filter(dir => dir.startsWith('node-addon-api@')) + .map(dir => path.join(pnpmDir, dir, 'node_modules', 'node-addon-api')) + .filter(fs.existsSync); + possiblePaths.push(...pnpmEntries); + } + + // Strategy 3: Check if it's a dependency of node-pty + const nodePtyModules = path.join(ROOT_DIR, 'node-pty', 'node_modules', 'node-addon-api'); + if (fs.existsSync(nodePtyModules)) { + possiblePaths.push(nodePtyModules); + } + + // Strategy 4: Hoisted by npm/yarn (parent directory) + const hoistedPath = path.join(ROOT_DIR, '..', 'node_modules', 'node-addon-api'); + if (fs.existsSync(hoistedPath)) { + possiblePaths.push(hoistedPath); + } + + if (possiblePaths.length > 0) { + const nodeAddonApiSrc = possiblePaths[0]; + fs.mkdirSync(path.dirname(nodeAddonApiDest), { recursive: true }); + fs.cpSync(nodeAddonApiSrc, nodeAddonApiDest, { recursive: true }); + console.log(` ✅ Bundled node-addon-api from: ${path.relative(ROOT_DIR, nodeAddonApiSrc)}`); + } else { + console.error(' ❌ CRITICAL: node-addon-api not found - source compilation will fail!'); + console.error(' Please ensure node-addon-api is installed as a dependency.'); + console.error(' Run: pnpm add -D node-addon-api'); + // Don't exit during build - let the developer decide + console.warn(' ⚠️ Continuing build, but npm package may have issues if prebuilds are missing.'); + } + + console.log('✅ node-pty bundled with dependencies\n'); +} + // Copy authenticate-pam module for Linux support (OUR LINUX FIX) function copyAuthenticatePam() { console.log('📦 Copying authenticate-pam module for Linux support...\n'); @@ -577,11 +631,14 @@ async function main() { copyRecursive(src, dest); }); - // Step 4: Copy authenticate-pam module for Linux support (OUR ENHANCEMENT) + // Step 4: Bundle node-pty with dependencies + bundleNodePty(); + + // Step 5: Copy authenticate-pam module for Linux support (OUR ENHANCEMENT) copyAuthenticatePam(); - // Step 5: Use package.npm.json if available, otherwise create clean package.json - console.log('\n4️⃣ Creating package.json for npm...\n'); + // Step 6: Use package.npm.json if available, otherwise create clean package.json + console.log('\n6️⃣ Creating package.json for npm...\n'); const npmPackageJsonPath = path.join(ROOT_DIR, 'package.npm.json'); let npmPackageJson; diff --git a/web/scripts/postinstall-npm.js b/web/scripts/postinstall-npm.js index 9da8b0e7..157d4686 100755 --- a/web/scripts/postinstall-npm.js +++ b/web/scripts/postinstall-npm.js @@ -21,23 +21,8 @@ if (isDevelopment) { return; } -// Create node_modules directory if it doesn't exist -const nodeModulesDir = path.join(__dirname, '..', 'node_modules'); -if (!fs.existsSync(nodeModulesDir)) { - fs.mkdirSync(nodeModulesDir, { recursive: true }); -} - -// Create symlink for node-pty so it can be required normally -const nodePtySource = path.join(__dirname, '..', 'node-pty'); -const nodePtyTarget = path.join(nodeModulesDir, 'node-pty'); -if (!fs.existsSync(nodePtyTarget) && fs.existsSync(nodePtySource)) { - try { - fs.symlinkSync(nodePtySource, nodePtyTarget, 'dir'); - console.log('✓ Created node-pty symlink in node_modules'); - } catch (error) { - console.warn('Warning: Could not create node-pty symlink:', error.message); - } -} +// For npm package, node-pty is bundled in the package root +// No need to create symlinks as it's accessed directly // Get Node ABI version const nodeABI = process.versions.modules; @@ -114,51 +99,8 @@ const compileFromSource = (moduleName, moduleDir) => { execSync('npm install -g node-gyp', { stdio: 'inherit' }); } - // For node-pty, ensure node-addon-api is available - if (moduleName === 'node-pty') { - const nodeAddonApiPath = path.join(moduleDir, 'node_modules', 'node-addon-api'); - if (!fs.existsSync(nodeAddonApiPath)) { - console.log(` Setting up node-addon-api for ${moduleName}...`); - - // Create node_modules directory - const nodeModulesDir = path.join(moduleDir, 'node_modules'); - fs.mkdirSync(nodeModulesDir, { recursive: true }); - - // Try multiple locations for node-addon-api - const possiblePaths = [ - path.join(__dirname, '..', 'node_modules', 'node-addon-api'), - path.join(__dirname, '..', '..', 'node_modules', 'node-addon-api'), - path.join(__dirname, '..', '..', '..', 'node_modules', 'node-addon-api'), - '/usr/local/lib/node_modules/vibetunnel/node_modules/node-addon-api', - '/usr/lib/node_modules/vibetunnel/node_modules/node-addon-api' - ]; - - let found = false; - for (const sourcePath of possiblePaths) { - if (fs.existsSync(sourcePath)) { - console.log(` Found node-addon-api at: ${sourcePath}`); - console.log(` Copying to ${nodeAddonApiPath}...`); - fs.cpSync(sourcePath, nodeAddonApiPath, { recursive: true }); - found = true; - break; - } - } - - if (!found) { - // As a fallback, install it - console.log(` Installing node-addon-api package...`); - try { - execSync('npm install node-addon-api@^7.1.0 --no-save --no-package-lock', { - cwd: moduleDir, - stdio: 'inherit' - }); - } catch (e) { - console.error(' Failed to install node-addon-api:', e.message); - console.error(' Trying to continue anyway...'); - } - } - } - } + // For node-pty, node-addon-api is included as a dependency in its package.json + // npm should handle it automatically during source compilation execSync('node-gyp rebuild', { cwd: moduleDir, @@ -186,7 +128,7 @@ const modules = [ version: '1.0.5', dir: path.join(__dirname, '..', 'node_modules', 'authenticate-pam'), build: path.join(__dirname, '..', 'node_modules', 'authenticate-pam', 'build', 'Release', 'authenticate_pam.node'), - essential: true, // PAM is essential for server environments + essential: false, // Optional - falls back to other auth methods platforms: ['linux', 'darwin'] // Needed on Linux and macOS } ];