mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
fix: improve install scripts robustness and edge cases
install.sh: - Fix NodeSource GPG key overwrite issue (rm before dearmor) - Fix systemd ExecStart missing node binary prefix - Add SUSE/openSUSE support (zypper-based) - Add local changes check before git reset --hard - Add curl/wget detection with fallback support - Use shallow clone (--depth 1) for faster install - Update Node.js target to v22 (current LTS) - Add CLAUDEMAN_NODE_VERSION env var for customization - Add run_as_root/ensure_sudo helpers for consistent privilege handling - Add cleanup trap with helpful failure message - Add Windows detection with WSL guidance - Fix PATH substring check (was using grep -qx incorrectly) - Add stdin TTY check for non-interactive piped input - Update Claude CLI install to official curl installer - Add armv7 architecture support (Raspberry Pi) - Quieter apt/dnf output with -qq flags scripts/postinstall.js: - Fix command detection for Windows (where vs command -v) - Add Fedora/Arch/Alpine install instructions for screen - Add Windows/WSL guidance - Show Claude CLI path when found - Refactor with clear sections and helper functions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+161
-52
@@ -1,87 +1,196 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Claudeman postinstall verification script
|
||||
* Runs after `npm install` to check environment readiness
|
||||
*/
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import { existsSync } from 'fs';
|
||||
import { homedir } from 'os';
|
||||
import { homedir, platform } from 'os';
|
||||
import { join } from 'path';
|
||||
|
||||
// Colors (with fallback for no-color environments)
|
||||
const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
||||
const green = (s) => useColor ? `\x1b[32m${s}\x1b[0m` : s;
|
||||
const yellow = (s) => useColor ? `\x1b[33m${s}\x1b[0m` : s;
|
||||
const red = (s) => useColor ? `\x1b[31m${s}\x1b[0m` : s;
|
||||
const bold = (s) => useColor ? `\x1b[1m${s}\x1b[0m` : s;
|
||||
// ============================================================================
|
||||
// Configuration
|
||||
// ============================================================================
|
||||
|
||||
console.log(bold('Claudeman postinstall check...'));
|
||||
const MIN_NODE_VERSION = 18;
|
||||
|
||||
// Claude CLI search paths (must match src/session.ts)
|
||||
const home = homedir();
|
||||
const CLAUDE_SEARCH_PATHS = [
|
||||
join(home, '.local/bin/claude'),
|
||||
join(home, '.claude/local/claude'),
|
||||
'/usr/local/bin/claude',
|
||||
join(home, '.npm-global/bin/claude'),
|
||||
join(home, 'bin/claude'),
|
||||
];
|
||||
|
||||
// ============================================================================
|
||||
// Colors (with fallback for no-color environments)
|
||||
// ============================================================================
|
||||
|
||||
const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
||||
|
||||
const colors = {
|
||||
green: (s) => useColor ? `\x1b[32m${s}\x1b[0m` : s,
|
||||
yellow: (s) => useColor ? `\x1b[33m${s}\x1b[0m` : s,
|
||||
red: (s) => useColor ? `\x1b[31m${s}\x1b[0m` : s,
|
||||
cyan: (s) => useColor ? `\x1b[36m${s}\x1b[0m` : s,
|
||||
bold: (s) => useColor ? `\x1b[1m${s}\x1b[0m` : s,
|
||||
dim: (s) => useColor ? `\x1b[2m${s}\x1b[0m` : s,
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Utility Functions
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Check if a command exists in PATH
|
||||
* Works on Unix and Windows
|
||||
*/
|
||||
function commandExists(cmd) {
|
||||
try {
|
||||
const checkCmd = platform() === 'win32' ? `where ${cmd}` : `command -v ${cmd}`;
|
||||
execSync(checkCmd, { stdio: 'pipe' });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get install instructions for screen based on platform
|
||||
*/
|
||||
function getScreenInstallInstructions() {
|
||||
const os = platform();
|
||||
|
||||
if (os === 'darwin') {
|
||||
return [
|
||||
' macOS: brew install screen',
|
||||
];
|
||||
}
|
||||
|
||||
if (os === 'linux') {
|
||||
return [
|
||||
' Ubuntu/Debian: sudo apt install screen',
|
||||
' Fedora/RHEL: sudo dnf install screen',
|
||||
' Arch Linux: sudo pacman -S screen',
|
||||
' Alpine: sudo apk add screen',
|
||||
];
|
||||
}
|
||||
|
||||
if (os === 'win32') {
|
||||
return [
|
||||
' Windows: Use WSL (Windows Subsystem for Linux)',
|
||||
' GNU Screen is not available on native Windows',
|
||||
];
|
||||
}
|
||||
|
||||
return [' Please install GNU Screen for your platform'];
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Main Checks
|
||||
// ============================================================================
|
||||
|
||||
console.log(colors.bold('Claudeman postinstall check...'));
|
||||
console.log('');
|
||||
|
||||
let hasWarnings = false;
|
||||
let hasErrors = false;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// 1. Check Node.js version >= 18
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const nodeVersion = process.versions.node;
|
||||
const majorVersion = parseInt(nodeVersion.split('.')[0], 10);
|
||||
|
||||
if (majorVersion < 18) {
|
||||
console.log(red(`✗ Node.js v${nodeVersion} is too old (requires >=18)`));
|
||||
process.exit(1);
|
||||
if (majorVersion < MIN_NODE_VERSION) {
|
||||
console.log(colors.red(`✗ Node.js v${nodeVersion} is too old`));
|
||||
console.log(colors.dim(` Minimum required: v${MIN_NODE_VERSION}`));
|
||||
console.log('');
|
||||
hasErrors = true;
|
||||
} else {
|
||||
console.log(green(`✓ Node.js v${nodeVersion}`) + ' (meets >=18 requirement)');
|
||||
console.log(colors.green(`✓ Node.js v${nodeVersion}`) + colors.dim(` (meets >=v${MIN_NODE_VERSION} requirement)`));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// 2. Check if GNU Screen is installed
|
||||
let screenFound = false;
|
||||
try {
|
||||
execSync('which screen', { stdio: 'pipe' });
|
||||
screenFound = true;
|
||||
console.log(green('✓ GNU Screen found'));
|
||||
} catch {
|
||||
hasWarnings = true;
|
||||
console.log(yellow('⚠ GNU Screen not found'));
|
||||
console.log(' Install:');
|
||||
console.log(' Ubuntu/Debian: sudo apt install screen');
|
||||
console.log(' macOS: brew install screen');
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
if (commandExists('screen')) {
|
||||
console.log(colors.green('✓ GNU Screen found'));
|
||||
} else {
|
||||
hasWarnings = true;
|
||||
console.log(colors.yellow('⚠ GNU Screen not found'));
|
||||
console.log(colors.dim(' Screen is required for session persistence.'));
|
||||
console.log(colors.dim(' Install:'));
|
||||
for (const instruction of getScreenInstallInstructions()) {
|
||||
console.log(colors.dim(instruction));
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// 3. Check if Claude CLI is found
|
||||
const home = homedir();
|
||||
const claudePaths = [
|
||||
join(home, '.local/bin/claude'),
|
||||
join(home, '.claude/local/claude'),
|
||||
'/usr/local/bin/claude',
|
||||
join(home, '.npm-global/bin/claude'),
|
||||
join(home, 'bin/claude'),
|
||||
];
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
let claudeFound = false;
|
||||
let claudePath = null;
|
||||
|
||||
// First try `which claude`
|
||||
try {
|
||||
execSync('which claude', { stdio: 'pipe' });
|
||||
claudeFound = true;
|
||||
} catch {
|
||||
// Check known paths
|
||||
for (const p of claudePaths) {
|
||||
if (existsSync(p)) {
|
||||
claudeFound = true;
|
||||
break;
|
||||
// First try PATH lookup
|
||||
if (commandExists('claude')) {
|
||||
claudeFound = true;
|
||||
try {
|
||||
const checkCmd = platform() === 'win32' ? 'where claude' : 'command -v claude';
|
||||
claudePath = execSync(checkCmd, { stdio: 'pipe', encoding: 'utf-8' }).trim().split('\n')[0];
|
||||
} catch {
|
||||
// Ignore, we know it exists
|
||||
}
|
||||
}
|
||||
|
||||
// Check known paths if not found in PATH
|
||||
if (!claudeFound) {
|
||||
for (const p of CLAUDE_SEARCH_PATHS) {
|
||||
if (existsSync(p)) {
|
||||
claudeFound = true;
|
||||
claudePath = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (claudeFound) {
|
||||
console.log(green('✓ Claude CLI found'));
|
||||
const pathInfo = claudePath ? colors.dim(` (${claudePath})`) : '';
|
||||
console.log(colors.green('✓ Claude CLI found') + pathInfo);
|
||||
} else {
|
||||
hasWarnings = true;
|
||||
console.log(yellow('⚠ Claude CLI not found'));
|
||||
console.log(' Install: curl -fsSL https://claude.ai/install.sh | bash');
|
||||
hasWarnings = true;
|
||||
console.log(colors.yellow('⚠ Claude CLI not found'));
|
||||
console.log(colors.dim(' Claude CLI is required to run AI sessions.'));
|
||||
console.log(colors.dim(' Install:'));
|
||||
console.log(colors.cyan(' curl -fsSL https://claude.ai/install.sh | bash'));
|
||||
}
|
||||
|
||||
// Print next steps
|
||||
// ----------------------------------------------------------------------------
|
||||
// Print Summary and Next Steps
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
console.log('');
|
||||
console.log(bold('Next steps:'));
|
||||
console.log(' npm run build');
|
||||
console.log(' claudeman web');
|
||||
|
||||
if (hasErrors) {
|
||||
console.log(colors.red(colors.bold('Installation cannot proceed due to errors above.')));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(colors.bold('Next steps:'));
|
||||
console.log(colors.dim(' 1. Build: ') + colors.cyan('npm run build'));
|
||||
console.log(colors.dim(' 2. Start: ') + colors.cyan('claudeman web --https'));
|
||||
console.log(colors.dim(' 3. Open: ') + colors.cyan('https://localhost:3000'));
|
||||
|
||||
if (hasWarnings) {
|
||||
console.log('');
|
||||
console.log(yellow('Note: Resolve warnings above for full functionality.'));
|
||||
console.log('');
|
||||
console.log(colors.yellow('Note: Resolve warnings above for full functionality.'));
|
||||
}
|
||||
|
||||
console.log('');
|
||||
|
||||
Reference in New Issue
Block a user