mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-27 15:17:38 +00:00
Fix npm package installation issues (#377)
This commit is contained in:
parent
8173a0f500
commit
d69a4c1ef3
4 changed files with 180 additions and 71 deletions
112
web/docs/npm.md
112
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/build-npm.js` - Unified npm build process with multi-platform support
|
||||||
- `scripts/postinstall-npm.js` - Fallback compilation logic
|
- `scripts/postinstall-npm.js` - Fallback compilation logic
|
||||||
- `.prebuildrc` - Prebuild configuration for target platforms
|
- `.prebuildrc` - Prebuild configuration for target platforms
|
||||||
- `package.json` - Package configuration and file inclusions
|
- `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)
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "vibetunnel",
|
"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",
|
"description": "Terminal sharing server with web interface - supports macOS, Linux, and headless environments",
|
||||||
"main": "lib/cli.js",
|
"main": "lib/cli.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
@ -48,7 +48,6 @@
|
||||||
"@codemirror/theme-one-dark": "^6.1.3",
|
"@codemirror/theme-one-dark": "^6.1.3",
|
||||||
"@codemirror/view": "^6.38.0",
|
"@codemirror/view": "^6.38.0",
|
||||||
"@xterm/headless": "^5.5.0",
|
"@xterm/headless": "^5.5.0",
|
||||||
"authenticate-pam": "^1.0.5",
|
|
||||||
"bonjour-service": "^1.3.0",
|
"bonjour-service": "^1.3.0",
|
||||||
"chalk": "^5.4.1",
|
"chalk": "^5.4.1",
|
||||||
"compression": "^1.8.0",
|
"compression": "^1.8.0",
|
||||||
|
|
@ -60,13 +59,14 @@
|
||||||
"mime-types": "^3.0.1",
|
"mime-types": "^3.0.1",
|
||||||
"monaco-editor": "^0.52.2",
|
"monaco-editor": "^0.52.2",
|
||||||
"multer": "^2.0.1",
|
"multer": "^2.0.1",
|
||||||
"node-addon-api": "^7.1.0",
|
|
||||||
"node-pty": "file:node-pty",
|
|
||||||
"postject": "1.0.0-alpha.6",
|
"postject": "1.0.0-alpha.6",
|
||||||
"signal-exit": "^4.1.0",
|
"signal-exit": "^4.1.0",
|
||||||
"web-push": "^3.6.7",
|
"web-push": "^3.6.7",
|
||||||
"ws": "^8.18.3"
|
"ws": "^8.18.3"
|
||||||
},
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"authenticate-pam": "^1.0.5"
|
||||||
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"terminal",
|
"terminal",
|
||||||
"multiplexer",
|
"multiplexer",
|
||||||
|
|
|
||||||
|
|
@ -375,6 +375,60 @@ function mergePrebuilds() {
|
||||||
console.log(`✅ Merged prebuilds: ${nodePtyCount} node-pty + ${pamCount} authenticate-pam = ${allPrebuilds.length} total\n`);
|
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)
|
// Copy authenticate-pam module for Linux support (OUR LINUX FIX)
|
||||||
function copyAuthenticatePam() {
|
function copyAuthenticatePam() {
|
||||||
console.log('📦 Copying authenticate-pam module for Linux support...\n');
|
console.log('📦 Copying authenticate-pam module for Linux support...\n');
|
||||||
|
|
@ -577,11 +631,14 @@ async function main() {
|
||||||
copyRecursive(src, dest);
|
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();
|
copyAuthenticatePam();
|
||||||
|
|
||||||
// Step 5: Use package.npm.json if available, otherwise create clean package.json
|
// Step 6: Use package.npm.json if available, otherwise create clean package.json
|
||||||
console.log('\n4️⃣ Creating package.json for npm...\n');
|
console.log('\n6️⃣ Creating package.json for npm...\n');
|
||||||
|
|
||||||
const npmPackageJsonPath = path.join(ROOT_DIR, 'package.npm.json');
|
const npmPackageJsonPath = path.join(ROOT_DIR, 'package.npm.json');
|
||||||
let npmPackageJson;
|
let npmPackageJson;
|
||||||
|
|
|
||||||
|
|
@ -21,23 +21,8 @@ if (isDevelopment) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create node_modules directory if it doesn't exist
|
// For npm package, node-pty is bundled in the package root
|
||||||
const nodeModulesDir = path.join(__dirname, '..', 'node_modules');
|
// No need to create symlinks as it's accessed directly
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get Node ABI version
|
// Get Node ABI version
|
||||||
const nodeABI = process.versions.modules;
|
const nodeABI = process.versions.modules;
|
||||||
|
|
@ -114,51 +99,8 @@ const compileFromSource = (moduleName, moduleDir) => {
|
||||||
execSync('npm install -g node-gyp', { stdio: 'inherit' });
|
execSync('npm install -g node-gyp', { stdio: 'inherit' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// For node-pty, ensure node-addon-api is available
|
// For node-pty, node-addon-api is included as a dependency in its package.json
|
||||||
if (moduleName === 'node-pty') {
|
// npm should handle it automatically during source compilation
|
||||||
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...');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
execSync('node-gyp rebuild', {
|
execSync('node-gyp rebuild', {
|
||||||
cwd: moduleDir,
|
cwd: moduleDir,
|
||||||
|
|
@ -186,7 +128,7 @@ const modules = [
|
||||||
version: '1.0.5',
|
version: '1.0.5',
|
||||||
dir: path.join(__dirname, '..', 'node_modules', 'authenticate-pam'),
|
dir: path.join(__dirname, '..', 'node_modules', 'authenticate-pam'),
|
||||||
build: path.join(__dirname, '..', 'node_modules', 'authenticate-pam', 'build', 'Release', 'authenticate_pam.node'),
|
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
|
platforms: ['linux', 'darwin'] // Needed on Linux and macOS
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue