const https = require('https');
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const version = require('../package.json').version;
const binDir = path.join(__dirname, '..', 'bin');
function getPlatformInfo() {
const platform = process.platform;
const arch = process.arch;
if (platform === 'win32') {
return { platformName: 'win32', binaryName: 'ghgrab-win32.exe', outName: 'ghgrab.exe' };
}
if (platform === 'darwin' && arch === 'arm64') {
return { platformName: 'darwin-arm64', binaryName: 'ghgrab-darwin-arm64', outName: 'ghgrab' };
}
if (platform === 'darwin') {
return { platformName: 'darwin', binaryName: 'ghgrab-darwin', outName: 'ghgrab' };
}
if (platform === 'linux' && arch === 'arm64') {
return { platformName: 'linux-arm64', binaryName: 'ghgrab-linux-arm64', outName: 'ghgrab' };
}
if (platform === 'linux') {
return { platformName: 'linux', binaryName: 'ghgrab-linux', outName: 'ghgrab' };
}
throw new Error(`Unsupported platform: ${platform}-${arch}`);
}
function download(url, dest, redirectCount = 0) {
if (redirectCount > 10) return Promise.reject(new Error('Too many redirects'));
return new Promise((resolve, reject) => {
const file = fs.createWriteStream(dest);
https.get(url, { headers: { 'User-Agent': 'ghgrab-npm-installer' } }, (response) => {
if (response.statusCode === 301 || response.statusCode === 302 || response.statusCode === 307 || response.statusCode === 308) {
file.close();
fs.unlink(dest, () => {});
return download(response.headers.location, dest, redirectCount + 1).then(resolve).catch(reject);
}
if (response.statusCode !== 200) {
file.close();
fs.unlink(dest, () => {});
return reject(new Error(`HTTP ${response.statusCode} downloading from ${url}`));
}
response.pipe(file);
file.on('finish', () => {
file.close(() => resolve());
});
}).on('error', (err) => {
fs.unlink(dest, () => {});
reject(err);
});
});
}
async function install() {
const { platformName, binaryName, outName } = getPlatformInfo();
const downloadUrl = `https://github.com/abhixdd/ghgrab/releases/download/v${version}/${binaryName}`;
const binPath = path.join(binDir, outName);
if (!fs.existsSync(binDir)) {
fs.mkdirSync(binDir, { recursive: true });
}
console.log(`Downloading ghgrab v${version} for ${platformName}...`);
console.log(` URL: ${downloadUrl}`);
try {
await download(downloadUrl, binPath);
const stat = fs.statSync(binPath);
if (stat.size < 100000) {
throw new Error(`Downloaded file is too small (${stat.size} bytes) — likely not a valid binary`);
}
if (process.platform !== 'win32') {
fs.chmodSync(binPath, 0o755);
}
console.log(`✓ ghgrab installed successfully to ${binPath}`);
} catch (downloadErr) {
console.error(`\nDownload failed: ${downloadErr.message}`);
console.log('\nFalling back to building from source (requires Rust/cargo)...');
try {
execSync('cargo build --release', {
cwd: path.join(__dirname, '..'),
stdio: 'inherit'
});
const builtBin = process.platform === 'win32' ? 'ghgrab.exe' : 'ghgrab';
const sourceBin = path.join(__dirname, '..', 'target', 'release', builtBin);
fs.copyFileSync(sourceBin, binPath);
if (process.platform !== 'win32') {
fs.chmodSync(binPath, 0o755);
}
console.log(`✓ Built from source successfully!`);
} catch (buildErr) {
console.error(`\nCould not build from source: ${buildErr.message}`);
console.error('Please install Rust from https://rustup.rs and try again, or download manually from:');
console.error(` https://github.com/abhixdd/ghgrab/releases`);
process.exit(1);
}
}
}
install();