From 5d49f82c25f264d1435e3839e377cf5a949adde9 Mon Sep 17 00:00:00 2001 From: Vinay Khobragade Date: Thu, 25 Dec 2025 23:35:58 +0530 Subject: [PATCH] Add lazy binary download on first CLI run Python: - Auto-download Go binary from GitHub releases when not found - Fixes pip wheel install not triggering post-install hooks - Add proper stdout flushing for download progress messages npm: - Read version from package.json instead of hardcoding - Add lazy download fallback to CLI wrapper - Export downloadBinary for reuse Both packages bumped to version 0.1.8 --- .gitignore | 2 + packages/npm/bin/ingestkit.js | 65 +++++++++++++++++ packages/npm/package.json | 2 +- packages/npm/scripts/download-binary.js | 28 ++++--- packages/python/ingestkit/cli.py | 97 +++++++++++++++++++++++-- packages/python/setup.py | 2 +- 6 files changed, 177 insertions(+), 19 deletions(-) create mode 100644 packages/npm/bin/ingestkit.js diff --git a/.gitignore b/.gitignore index 770ace2..c8c65d0 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,8 @@ # ============================================================================= # Binaries for programs and plugins bin/ +# Exception: npm package bin directory contains JS wrapper (not compiled binary) +!packages/npm/bin/ *.exe *.exe~ *.dll diff --git a/packages/npm/bin/ingestkit.js b/packages/npm/bin/ingestkit.js new file mode 100644 index 0000000..bc6245a --- /dev/null +++ b/packages/npm/bin/ingestkit.js @@ -0,0 +1,65 @@ +#!/usr/bin/env node + +/** + * IngestKit CLI wrapper for Node.js + * + * This script forwards all arguments to the IngestKit Go binary. + */ + +const { spawn } = require('child_process'); +const path = require('path'); +const fs = require('fs'); + +// Determine binary path +const platform = process.platform; +const binaryName = platform === 'win32' ? 'ingestkit.exe' : 'ingestkit'; +const binaryPath = path.join(__dirname, binaryName); + +async function ensureBinary() { + // Check if binary exists + if (fs.existsSync(binaryPath)) { + return binaryPath; + } + + // Binary not found, try to download it + console.log('IngestKit CLI binary not found. Installing...'); + + try { + const { downloadBinary } = require('../scripts/download-binary.js'); + await downloadBinary(); + console.log(); // Blank line before running command + return binaryPath; + } catch (err) { + console.error('āŒ Failed to download IngestKit CLI binary'); + console.error('\nThis might happen if:'); + console.error('1. The installation didn\'t complete successfully'); + console.error('2. You\'re using an unsupported platform'); + console.error('3. Network issues prevented the download'); + console.error('\nTry reinstalling:'); + console.error(' npm uninstall ingestkit'); + console.error(' npm install ingestkit'); + process.exit(1); + } +} + +async function main() { + await ensureBinary(); + + // Forward all arguments to the binary + const child = spawn(binaryPath, process.argv.slice(2), { + stdio: 'inherit' + }); + + // Handle exit + child.on('close', (code) => { + process.exit(code || 0); + }); + + // Handle errors + child.on('error', (err) => { + console.error('āŒ Error running ingestkit:', err.message); + process.exit(1); + }); +} + +main(); diff --git a/packages/npm/package.json b/packages/npm/package.json index 1bcdb44..ea2a098 100644 --- a/packages/npm/package.json +++ b/packages/npm/package.json @@ -1,6 +1,6 @@ { "name": "ingestkit", - "version": "0.1.0", + "version": "0.1.8", "description": "High-performance event ingestion with type-safe SDKs", "main": "index.js", "bin": { diff --git a/packages/npm/scripts/download-binary.js b/packages/npm/scripts/download-binary.js index 91d1ecc..d5a6c1b 100644 --- a/packages/npm/scripts/download-binary.js +++ b/packages/npm/scripts/download-binary.js @@ -7,9 +7,10 @@ const https = require('https'); const fs = require('fs'); const path = require('path'); -const { execSync } = require('child_process'); -const VERSION = '0.1.0'; +// Read version from package.json +const packageJson = require('../package.json'); +const VERSION = packageJson.version; const BINARY_BASE_URL = `https://github.com/feat7/ingestkit/releases/download/v${VERSION}`; function getPlatformInfo() { @@ -48,7 +49,7 @@ function downloadBinary() { const binDir = path.join(__dirname, '..', 'bin'); const binaryPath = path.join(binDir, systemName === 'windows' ? 'ingestkit.exe' : 'ingestkit'); - console.log('šŸ“„ Downloading IngestKit CLI...'); + console.log(`šŸ“„ Downloading IngestKit CLI v${VERSION}...`); console.log(` From: ${binaryUrl}`); console.log(` To: ${binaryPath}`); @@ -78,7 +79,7 @@ function downloadBinary() { }); }); }).on('error', reject); - } else { + } else if (response.statusCode === 200) { response.pipe(file); file.on('finish', () => { file.close(() => { @@ -92,6 +93,10 @@ function downloadBinary() { resolve(); }); }); + } else { + file.close(); + fs.unlink(binaryPath, () => {}); + reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`)); } }).on('error', (err) => { fs.unlink(binaryPath, () => {}); // Delete partial download @@ -105,8 +110,13 @@ function downloadBinary() { }); } -// Run download -downloadBinary().catch((err) => { - console.error('Installation failed:', err); - process.exit(1); -}); +// Export for use by CLI wrapper +module.exports = { downloadBinary, getPlatformInfo }; + +// Run download if called directly +if (require.main === module) { + downloadBinary().catch((err) => { + console.error('Installation failed:', err.message); + process.exit(1); + }); +} diff --git a/packages/python/ingestkit/cli.py b/packages/python/ingestkit/cli.py index a150cac..517ace6 100644 --- a/packages/python/ingestkit/cli.py +++ b/packages/python/ingestkit/cli.py @@ -5,9 +5,83 @@ """ import os +import platform +import stat import subprocess import sys import shutil +import urllib.request + +VERSION = "0.1.8" +BINARY_BASE_URL = "https://github.com/feat7/ingestkit/releases/download/v{version}" + + +def get_platform_info(): + """Get platform-specific binary name""" + system = platform.system().lower() + machine = platform.machine().lower() + + system_map = { + 'darwin': 'darwin', + 'linux': 'linux', + 'windows': 'windows' + } + + arch_map = { + 'x86_64': 'amd64', + 'amd64': 'amd64', + 'arm64': 'arm64', + 'aarch64': 'arm64', + } + + system_name = system_map.get(system, system) + arch_name = arch_map.get(machine, machine) + + binary_name = f"ingestkit-{system_name}-{arch_name}" + if system == 'windows': + binary_name += '.exe' + + return binary_name, system_name + + +def download_binary(): + """Download the IngestKit binary for the current platform""" + binary_name, system_name = get_platform_info() + binary_url = f"{BINARY_BASE_URL.format(version=VERSION)}/{binary_name}" + + # Install to ~/.local/bin (user-writable, commonly in PATH) + install_dir = os.path.expanduser('~/.local/bin') + os.makedirs(install_dir, exist_ok=True) + + binary_path = os.path.join(install_dir, 'ingestkit-cli') + if system_name == 'windows': + binary_path += '.exe' + + print(f"šŸ“„ Downloading IngestKit CLI v{VERSION}...") + print(f" From: {binary_url}") + print(f" To: {binary_path}") + sys.stdout.flush() + + try: + urllib.request.urlretrieve(binary_url, binary_path) + + # Make executable (Unix-like systems) + if system_name != 'windows': + os.chmod(binary_path, os.stat(binary_path).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) + + print(f"āœ… IngestKit CLI installed successfully!") + sys.stdout.flush() + return binary_path + except Exception as e: + print(f"āŒ Failed to download binary: {e}") + print(f"\nšŸ’” Manual installation:") + print(f" Visit: https://github.com/feat7/ingestkit/releases/latest") + print(f" Download: {binary_name}") + print(f" Rename to: ingestkit-cli") + print(f" Move to: {install_dir}") + print(f" Make executable: chmod +x {binary_path}") + return None + def main(): """ @@ -18,15 +92,21 @@ def main(): # Find the ingestkit-cli binary (named differently to avoid conflict with this Python entry point) binary = shutil.which('ingestkit-cli') + # If not found in PATH, check ~/.local/bin directly if not binary: - print("āŒ IngestKit CLI binary not found!") - print("\nThis might happen if:") - print("1. The installation didn't complete successfully") - print("2. The binary directory is not in your PATH") - print("\nTry reinstalling:") - print(" pip uninstall ingestkit") - print(" pip install ingestkit") - sys.exit(1) + local_bin = os.path.expanduser('~/.local/bin/ingestkit-cli') + if os.path.isfile(local_bin) and os.access(local_bin, os.X_OK): + binary = local_bin + + # Still not found? Download it + if not binary: + print("IngestKit CLI binary not found. Installing...") + sys.stdout.flush() + binary = download_binary() + if not binary: + sys.exit(1) + print() # Blank line before running command + sys.stdout.flush() # Forward all arguments to the binary args = [binary] + sys.argv[1:] @@ -57,5 +137,6 @@ def main(): print(f"āŒ Error running ingestkit: {e}") sys.exit(1) + if __name__ == '__main__': main() diff --git a/packages/python/setup.py b/packages/python/setup.py index 65af364..6ecc9cc 100644 --- a/packages/python/setup.py +++ b/packages/python/setup.py @@ -14,7 +14,7 @@ import stat import sys -VERSION = "0.1.7" +VERSION = "0.1.8" BINARY_BASE_URL = "https://github.com/feat7/ingestkit/releases/download/v{version}" def get_platform_info():