From 2bff252e6a31504950f6734992b0f6c3eed156a2 Mon Sep 17 00:00:00 2001 From: weishu Date: Tue, 23 Dec 2025 18:07:31 +0800 Subject: [PATCH] refactor: migrate unpack-tools script from CommonJS to TypeScript --- cli/package.json | 1 + cli/scripts/unpack-tools.cjs | 162 ----------------------------------- cli/scripts/unpack-tools.ts | 115 +++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 162 deletions(-) delete mode 100644 cli/scripts/unpack-tools.cjs create mode 100644 cli/scripts/unpack-tools.ts diff --git a/cli/package.json b/cli/package.json index 6d504edf..2a4d6854 100644 --- a/cli/package.json +++ b/cli/package.json @@ -27,6 +27,7 @@ "build:exe:all": "bun run scripts/build-executable.ts --all", "build:exe:allinone": "bun run scripts/build-executable.ts --with-web-assets", "build:exe:allinone:all": "bun run scripts/build-executable.ts --with-web-assets --all", + "tools:unpack": "bun run scripts/unpack-tools.ts", "test": "tsx --env-file .env.integration-test node_modules/.bin/vitest run", "test:win": "vitest run", "dev": "tsx src/index.ts", diff --git a/cli/scripts/unpack-tools.cjs b/cli/scripts/unpack-tools.cjs deleted file mode 100644 index 8e820c6a..00000000 --- a/cli/scripts/unpack-tools.cjs +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/env node - -/** - * Unpacks platform-specific binaries from compressed archives - * This script extracts the necessary tools for the current platform - */ - -const fs = require('fs'); -const path = require('path'); -const zlib = require('zlib'); -const tar = require('tar'); -const os = require('os'); - -/** - * Get the platform-specific directory name - */ -function getPlatformDir() { - const platform = os.platform(); - const arch = os.arch(); - - if (platform === 'darwin') { - if (arch === 'arm64') return 'arm64-darwin'; - if (arch === 'x64') return 'x64-darwin'; - } else if (platform === 'linux') { - if (arch === 'arm64') return 'arm64-linux'; - if (arch === 'x64') return 'x64-linux'; - } else if (platform === 'win32') { - if (arch === 'x64') return 'x64-win32'; - } - - throw new Error(`Unsupported platform: ${arch}-${platform}`); -} - -/** - * Get the root tools directory - */ -function getToolsDir() { - // Handle both direct execution and require() calls - const scriptDir = __dirname; - return path.resolve(scriptDir, '..', 'tools'); -} - -/** - * Check if tools are already unpacked for current platform - */ -function areToolsUnpacked(toolsDir) { - const unpackedPath = path.join(toolsDir, 'unpacked'); - - if (!fs.existsSync(unpackedPath)) { - return false; - } - - // Check for expected binaries - const isWin = os.platform() === 'win32'; - const difftBinary = isWin ? 'difft.exe' : 'difft'; - const rgBinary = isWin ? 'rg.exe' : 'rg'; - - const expectedFiles = [ - path.join(unpackedPath, difftBinary), - path.join(unpackedPath, rgBinary) - ]; - - return expectedFiles.every(file => fs.existsSync(file)); -} - -/** - * Unpack a tar.gz archive to a destination directory - */ -async function unpackArchive(archivePath, destDir) { - return new Promise((resolve, reject) => { - // Ensure destination directory exists - if (!fs.existsSync(destDir)) { - fs.mkdirSync(destDir, { recursive: true }); - } - - // Create read stream and extract - fs.createReadStream(archivePath) - .pipe(zlib.createGunzip()) - .pipe(tar.extract({ - cwd: destDir, - preserveMode: true, - preserveOwner: false - })) - .on('finish', () => { - // Set executable permissions for Unix systems - if (os.platform() !== 'win32') { - const files = fs.readdirSync(destDir); - files.forEach(file => { - const filePath = path.join(destDir, file); - const stats = fs.statSync(filePath); - if (stats.isFile() && !file.endsWith('.node')) { - // Make binary files executable - fs.chmodSync(filePath, 0o755); - } - }); - } - resolve(); - }) - .on('error', reject); - }); -} - -/** - * Main unpacking function - */ -async function unpackTools() { - try { - const platformDir = getPlatformDir(); - const toolsDir = getToolsDir(); - const archivesDir = path.join(toolsDir, 'archives'); - const unpackedPath = path.join(toolsDir, 'unpacked'); - - // Check if already unpacked - if (areToolsUnpacked(toolsDir)) { - console.log(`Tools already unpacked for ${platformDir}`); - return { success: true, alreadyUnpacked: true }; - } - - console.log(`Unpacking tools for ${platformDir}...`); - - // Create unpacked directory - if (!fs.existsSync(unpackedPath)) { - fs.mkdirSync(unpackedPath, { recursive: true }); - } - - // Unpack difftastic - const difftasticArchive = path.join(archivesDir, `difftastic-${platformDir}.tar.gz`); - if (!fs.existsSync(difftasticArchive)) { - throw new Error(`Archive not found: ${difftasticArchive}`); - } - await unpackArchive(difftasticArchive, unpackedPath); - - // Unpack ripgrep - const ripgrepArchive = path.join(archivesDir, `ripgrep-${platformDir}.tar.gz`); - if (!fs.existsSync(ripgrepArchive)) { - throw new Error(`Archive not found: ${ripgrepArchive}`); - } - await unpackArchive(ripgrepArchive, unpackedPath); - - console.log(`Tools unpacked successfully to ${unpackedPath}`); - return { success: true, alreadyUnpacked: false }; - - } catch (error) { - console.error('Failed to unpack tools:', error.message); - throw error; - } -} - -// Export for use as module -module.exports = { unpackTools, getPlatformDir, getToolsDir }; - -// Run if executed directly -if (require.main === module) { - unpackTools() - .then(result => { - process.exit(0); - }) - .catch(error => { - console.error('Error:', error); - process.exit(1); - }); -} diff --git a/cli/scripts/unpack-tools.ts b/cli/scripts/unpack-tools.ts new file mode 100644 index 00000000..fe8e9b7a --- /dev/null +++ b/cli/scripts/unpack-tools.ts @@ -0,0 +1,115 @@ +import { chmodSync, existsSync, mkdirSync, readdirSync, statSync } from 'node:fs'; +import { dirname, join, resolve } from 'node:path'; +import { arch, platform } from 'node:os'; +import { fileURLToPath } from 'node:url'; +import * as tar from 'tar'; + +export function getPlatformDir(): string { + const platformName = platform(); + const archName = arch(); + + if (platformName === 'darwin') { + if (archName === 'arm64') return 'arm64-darwin'; + if (archName === 'x64') return 'x64-darwin'; + } else if (platformName === 'linux') { + if (archName === 'arm64') return 'arm64-linux'; + if (archName === 'x64') return 'x64-linux'; + } else if (platformName === 'win32') { + if (archName === 'x64') return 'x64-win32'; + } + + throw new Error(`Unsupported platform: ${archName}-${platformName}`); +} + +export function getToolsDir(): string { + const scriptDir = dirname(fileURLToPath(import.meta.url)); + return resolve(scriptDir, '..', 'tools'); +} + +function areToolsUnpacked(toolsDir: string): boolean { + const unpackedPath = join(toolsDir, 'unpacked'); + if (!existsSync(unpackedPath)) { + return false; + } + + const isWin = platform() === 'win32'; + const difftBinary = isWin ? 'difft.exe' : 'difft'; + const rgBinary = isWin ? 'rg.exe' : 'rg'; + + const expectedFiles = [ + join(unpackedPath, difftBinary), + join(unpackedPath, rgBinary) + ]; + + return expectedFiles.every((file) => existsSync(file)); +} + +function unpackArchive(archivePath: string, destDir: string): void { + if (!existsSync(destDir)) { + mkdirSync(destDir, { recursive: true }); + } + + tar.extract({ + file: archivePath, + cwd: destDir, + sync: true, + gzip: true, + preserveMode: true, + preserveOwner: false + }); +} + +export function unpackTools(): { success: true; alreadyUnpacked: boolean } { + const platformDir = getPlatformDir(); + const toolsDir = getToolsDir(); + const archivesDir = join(toolsDir, 'archives'); + const unpackedPath = join(toolsDir, 'unpacked'); + + if (areToolsUnpacked(toolsDir)) { + console.log(`Tools already unpacked for ${platformDir}`); + return { success: true, alreadyUnpacked: true }; + } + + console.log(`Unpacking tools for ${platformDir}...`); + if (!existsSync(unpackedPath)) { + mkdirSync(unpackedPath, { recursive: true }); + } + + const archives = [ + `difftastic-${platformDir}.tar.gz`, + `ripgrep-${platformDir}.tar.gz` + ]; + + for (const archiveName of archives) { + const archivePath = join(archivesDir, archiveName); + if (!existsSync(archivePath)) { + throw new Error(`Archive not found: ${archivePath}`); + } + unpackArchive(archivePath, unpackedPath); + } + + if (platform() !== 'win32') { + const files = readdirSync(unpackedPath); + for (const file of files) { + const filePath = join(unpackedPath, file); + const stats = statSync(filePath); + if (stats.isFile() && !file.endsWith('.node')) { + chmodSync(filePath, 0o755); + } + } + } + + console.log(`Tools unpacked successfully to ${unpackedPath}`); + return { success: true, alreadyUnpacked: false }; +} + +if (import.meta.main) { + try { + unpackTools(); + process.exit(0); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + console.error('Failed to unpack tools:', message); + process.exit(1); + } +}