From ab734ba84e0de2854e6715965c0a6437b4c45568 Mon Sep 17 00:00:00 2001 From: kaje94 Date: Wed, 25 Feb 2026 15:14:07 +0530 Subject: [PATCH] avoid compressing cli files into zip --- .../config/rush-project.json | 3 +- .../wso2-platform-extension/package.json | 2 +- .../scripts/download-choreo-cli.js | 310 ++++++++++-------- .../src/choreo-rpc/cli-install.ts | 116 +------ 4 files changed, 185 insertions(+), 246 deletions(-) diff --git a/workspaces/wso2-platform/wso2-platform-extension/config/rush-project.json b/workspaces/wso2-platform/wso2-platform-extension/config/rush-project.json index 948a711bb6a..a931cb59f15 100644 --- a/workspaces/wso2-platform/wso2-platform-extension/config/rush-project.json +++ b/workspaces/wso2-platform/wso2-platform-extension/config/rush-project.json @@ -2,7 +2,8 @@ "extends": "../../../rush-config.json", "operationSettings": [ { - "operationName": "build" + "operationName": "build", + "outputFolderNames": ["resources/choreo-cli"] // TODO: uncomment when adding back cell diagram to choreo // "dependsOnAdditionalFiles": [ // "../cell-diagram/build/*", diff --git a/workspaces/wso2-platform/wso2-platform-extension/package.json b/workspaces/wso2-platform/wso2-platform-extension/package.json index a0606ed7387..ffb03f16e58 100644 --- a/workspaces/wso2-platform/wso2-platform-extension/package.json +++ b/workspaces/wso2-platform/wso2-platform-extension/package.json @@ -3,7 +3,7 @@ "displayName": "WSO2 Platform", "description": "Manage WSO2 Choreo and Devant projects in VS Code.", "license": "Apache-2.0", - "version": "1.0.21", + "version": "1.0.22", "cliVersion": "v1.2.212602161800", "publisher": "wso2", "bugs": { diff --git a/workspaces/wso2-platform/wso2-platform-extension/scripts/download-choreo-cli.js b/workspaces/wso2-platform/wso2-platform-extension/scripts/download-choreo-cli.js index bce51a0ee6e..bc3ff5e535f 100644 --- a/workspaces/wso2-platform/wso2-platform-extension/scripts/download-choreo-cli.js +++ b/workspaces/wso2-platform/wso2-platform-extension/scripts/download-choreo-cli.js @@ -15,13 +15,13 @@ const CLI_CACHE_DIR = path.join(REPO_ROOT, 'common', 'temp', 'choreo-cli'); const PACKAGE_JSON_PATH = path.join(PROJECT_ROOT, 'package.json'); const GITHUB_REPO_URL = 'https://api.github.com/repos/wso2/choreo-cli'; -// Platform-specific file patterns for CLI downloads -const CLI_ASSET_PATTERNS = [ - 'choreo-cli-{version}-darwin-amd64.zip', - 'choreo-cli-{version}-darwin-arm64.zip', - 'choreo-cli-{version}-linux-amd64.tar.gz', - 'choreo-cli-{version}-linux-arm64.tar.gz', - 'choreo-cli-{version}-windows-amd64.zip' +// Platform/arch mappings: assetSuffix -> { os, arch, ext } +const CLI_PLATFORMS = [ + { assetSuffix: 'darwin-amd64', os: 'darwin', arch: 'amd64', ext: '.zip', binary: 'choreo' }, + { assetSuffix: 'darwin-arm64', os: 'darwin', arch: 'arm64', ext: '.zip', binary: 'choreo' }, + { assetSuffix: 'linux-amd64', os: 'linux', arch: 'amd64', ext: '.tar.gz', binary: 'choreo' }, + { assetSuffix: 'linux-arm64', os: 'linux', arch: 'arm64', ext: '.tar.gz', binary: 'choreo' }, + { assetSuffix: 'windows-amd64', os: 'win32', arch: 'amd64', ext: '.zip', binary: 'choreo.exe' }, ]; // ============================================================================ @@ -40,24 +40,18 @@ function getCliVersion() { return cliVersion; } -function getCombinedZipFileName(version) { - return `choreo-cli-${version}.zip`; +function getAssetName(version, platform) { + return `choreo-cli-${version}-${platform.assetSuffix}${platform.ext}`; } -function getCombinedZipPath(version, baseDir) { - return path.join(baseDir, getCombinedZipFileName(version)); +// resources/choreo-cli/{version}/{os}/{arch}/choreo +function getResourcesBinaryPath(version, platform) { + return path.join(CLI_RESOURCES_DIR, version, platform.os, platform.arch, platform.binary); } -function getResourcesZipPath(version) { - return getCombinedZipPath(version, CLI_RESOURCES_DIR); -} - -function getCacheZipPath(version) { - return getCombinedZipPath(version, CLI_CACHE_DIR); -} - -function getExpectedAssetNames(version) { - return CLI_ASSET_PATTERNS.map(pattern => pattern.replace('{version}', version)); +// common/temp/choreo-cli/{version}/{os}/{arch}/choreo +function getCacheBinaryPath(version, platform) { + return path.join(CLI_CACHE_DIR, version, platform.os, platform.arch, platform.binary); } // ============================================================================ @@ -108,72 +102,93 @@ function deleteDirectory(dirPath) { // ============================================================================ function checkExistingCLI(version) { - const resourcesZipPath = getResourcesZipPath(version); - const cacheZipPath = getCacheZipPath(version); - - const resourcesExists = fs.existsSync(resourcesZipPath); - const cacheExists = fs.existsSync(cacheZipPath); - - // Both exist - we're good - if (resourcesExists && cacheExists) { - console.log(`✓ Choreo CLI for version ${version} exists`); - return true; + const missingFromResources = []; + const missingFromCache = []; + + for (const platform of CLI_PLATFORMS) { + const resourcesPath = getResourcesBinaryPath(version, platform); + const cachePath = getCacheBinaryPath(version, platform); + + if (!fs.existsSync(resourcesPath)) missingFromResources.push(platform); + if (!fs.existsSync(cachePath)) missingFromCache.push(platform); } - - // Resources exists but cache doesn't (e.g., after rush purge) - if (resourcesExists && !cacheExists) { - console.log(`✓ CLI zip exists in resources/choreo-cli`); - console.log(`Restoring cache (common/temp) from resources...`); - ensureDirectoryExists(CLI_CACHE_DIR); - fs.copyFileSync(resourcesZipPath, cacheZipPath); - console.log(`✓ Restored cache from resources/choreo-cli`); + + // All present in both locations + if (missingFromResources.length === 0 && missingFromCache.length === 0) { + console.log(`✓ Choreo CLI binaries for version ${version} exist in both resources and cache`); return true; } - - // Cache exists but resources doesn't - if (!resourcesExists && cacheExists) { - console.log(`Found CLI zip in cache (common/temp), copying to resources/choreo-cli...`); - ensureDirectoryExists(CLI_RESOURCES_DIR); - fs.copyFileSync(cacheZipPath, resourcesZipPath); - console.log(`✓ Copied CLI zip to resources/choreo-cli`); + + // Resources complete, restore missing cache entries + if (missingFromResources.length === 0 && missingFromCache.length > 0) { + console.log(`✓ CLI binaries exist in resources. Restoring ${missingFromCache.length} missing cache entries...`); + for (const platform of missingFromCache) { + const src = getResourcesBinaryPath(version, platform); + const dest = getCacheBinaryPath(version, platform); + ensureDirectoryExists(path.dirname(dest)); + fs.copyFileSync(src, dest); + console.log(` ✓ Restored cache: ${platform.os}/${platform.arch}`); + } return true; } - - // Neither exists - console.log(`CLI zip for version ${version} not found in resources or cache`); - return false; -} -function cleanupOldFilesInDirectory(directory, currentVersion) { - if (!fs.existsSync(directory)) { - return; + // Cache complete, restore missing resources entries + if (missingFromCache.length === 0 && missingFromResources.length > 0) { + console.log(`✓ CLI binaries exist in cache. Restoring ${missingFromResources.length} missing resources entries...`); + for (const platform of missingFromResources) { + const src = getCacheBinaryPath(version, platform); + const dest = getResourcesBinaryPath(version, platform); + ensureDirectoryExists(path.dirname(dest)); + fs.copyFileSync(src, dest); + console.log(` ✓ Restored resources: ${platform.os}/${platform.arch}`); + } + return true; } - const currentZipName = getCombinedZipFileName(currentVersion); - const entries = fs.readdirSync(directory); - - for (const entry of entries) { - if (entry === currentZipName) { - continue; // Skip the current version + // Partial: some platforms have binaries in at least one location — sync what we can + const syncedPlatforms = []; + for (const platform of CLI_PLATFORMS) { + const resourcesPath = getResourcesBinaryPath(version, platform); + const cachePath = getCacheBinaryPath(version, platform); + const resourcesExists = fs.existsSync(resourcesPath); + const cacheExists = fs.existsSync(cachePath); + + if (resourcesExists && !cacheExists) { + ensureDirectoryExists(path.dirname(cachePath)); + fs.copyFileSync(resourcesPath, cachePath); + syncedPlatforms.push(platform); + } else if (!resourcesExists && cacheExists) { + ensureDirectoryExists(path.dirname(resourcesPath)); + fs.copyFileSync(cachePath, resourcesPath); + syncedPlatforms.push(platform); } + } - const entryPath = path.join(directory, entry); - const stats = fs.statSync(entryPath); - - console.log(`Removing old ${stats.isDirectory() ? 'directory' : 'file'}: ${entry} from ${path.basename(directory)}`); - - if (stats.isDirectory()) { - fs.rmSync(entryPath, { recursive: true, force: true }); - } else { - fs.unlinkSync(entryPath); - } + // Re-check after sync + const stillMissing = CLI_PLATFORMS.filter(platform => { + return !fs.existsSync(getResourcesBinaryPath(version, platform)); + }); + + if (stillMissing.length === 0) { + console.log(`✓ All CLI binaries synced for version ${version}`); + return true; } + + console.log(`CLI binaries for version ${version} not found for: ${stillMissing.map(p => `${p.os}/${p.arch}`).join(', ')}`); + return false; } function cleanupOldFiles(currentVersion) { - // Clean up old files from both locations - cleanupOldFilesInDirectory(CLI_RESOURCES_DIR, currentVersion); - cleanupOldFilesInDirectory(CLI_CACHE_DIR, currentVersion); + for (const baseDir of [CLI_RESOURCES_DIR, CLI_CACHE_DIR]) { + if (!fs.existsSync(baseDir)) continue; + const entries = fs.readdirSync(baseDir); + for (const entry of entries) { + if (entry === currentVersion) continue; + const entryPath = path.join(baseDir, entry); + console.log(`Removing old entry: ${entry} from ${path.basename(baseDir)}`); + fs.rmSync(entryPath, { recursive: true, force: true }); + } + } } // ============================================================================ @@ -312,83 +327,92 @@ async function downloadAsset(asset, tempDir) { } } -function getZipCommand(files, outputZipPath, tempDir) { - const isWindows = os.platform() === 'win32'; - - if (isWindows) { - const filesArg = files.map(f => `'${f}'`).join(','); - return { - command: `powershell.exe -Command "Compress-Archive -Path ${filesArg} -DestinationPath '${outputZipPath}' -Force"`, - cwd: tempDir - }; +// ============================================================================ +// Extraction +// ============================================================================ + +function extractBinary(archivePath, platform, destDir) { + ensureDirectoryExists(destDir); + const tmpExtractDir = fs.mkdtempSync(path.join(os.tmpdir(), `choreo-extract-`)); + + try { + if (platform.ext === '.tar.gz') { + execSync(`tar -xzf '${archivePath}' -C '${tmpExtractDir}'`, { stdio: 'inherit' }); + } else if (platform.ext === '.zip') { + if (os.platform() === 'win32') { + execSync(`powershell.exe -Command "Expand-Archive -Path '${archivePath}' -DestinationPath '${tmpExtractDir}' -Force"`, { stdio: 'inherit' }); + } else { + execSync(`unzip -q '${archivePath}' -d '${tmpExtractDir}'`); + } + } + + // Find the binary recursively (it may be inside a subdirectory) + const binaryPath = findFile(tmpExtractDir, platform.binary); + if (!binaryPath) { + throw new Error(`Binary '${platform.binary}' not found after extracting ${archivePath}`); + } + + const destPath = path.join(destDir, platform.binary); + fs.copyFileSync(binaryPath, destPath); + if (platform.binary !== 'choreo.exe') { + fs.chmodSync(destPath, 0o755); + } + console.log(` ✓ Extracted ${platform.os}/${platform.arch} binary`); + } finally { + fs.rmSync(tmpExtractDir, { recursive: true, force: true }); } - - // macOS/Linux - const filesArg = files.map(f => `'${f}'`).join(' '); - return { - command: `zip -q '${outputZipPath}' ${filesArg}`, - cwd: tempDir - }; } -function createCombinedZip(tempDir, outputZipPath) { - console.log('\nCreating Choreo CLI zip file...'); - const files = fs.readdirSync(tempDir).filter(f => !f.startsWith('.')); - const { command, cwd } = getZipCommand(files, outputZipPath, tempDir); - - try { - execSync(command, { cwd, stdio: 'inherit' }); - - const zipSize = getFileSize(outputZipPath); - const relativePath = path.relative(PROJECT_ROOT, outputZipPath); - console.log(`✓ Created Choreo CLI combined zip: ${relativePath} (${zipSize} bytes)`); - } catch (error) { - throw new Error(`Failed to create zip file: ${error.message}`); +function findFile(dir, filename) { + const entries = fs.readdirSync(dir, { withFileTypes: true }); + for (const entry of entries) { + const fullPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + const found = findFile(fullPath, filename); + if (found) return found; + } else if (entry.name === filename) { + return fullPath; + } } + return null; } // ============================================================================ // Main Download Logic // ============================================================================ -async function downloadAllAssets(releaseData, expectedAssetNames, tempDir) { - const downloadPromises = expectedAssetNames.map(assetName => { - const asset = releaseData.assets?.find(a => a.name === assetName); - - if (!asset) { - console.warn(`Warning: Choreo CLI Asset not found: ${assetName}`); - return Promise.resolve(); - } +async function downloadAndExtractCLI(version) { + const tempDir = createTempDirectory(`choreo-cli-${version}-`); - return downloadAsset(asset, tempDir); - }); + try { + const releaseData = await getReleaseByTag(version); - await Promise.all(downloadPromises); -} + for (const platform of CLI_PLATFORMS) { + const assetName = getAssetName(version, platform); + const asset = releaseData.assets?.find(a => a.name === assetName); -async function downloadAndCombineCLI(version) { - const tempDir = createTempDirectory(`choreo-cli-${version}-`); - - try { - // Ensure both directories exist - ensureDirectoryExists(CLI_RESOURCES_DIR); - ensureDirectoryExists(CLI_CACHE_DIR); + if (!asset) { + console.warn(`Warning: Asset not found: ${assetName}`); + continue; + } - const releaseData = await getReleaseByTag(version); - const expectedAssetNames = getExpectedAssetNames(version); + const archivePath = path.join(tempDir, assetName); + await downloadAsset(asset, tempDir); - await downloadAllAssets(releaseData, expectedAssetNames, tempDir); + const resourcesDir = path.dirname(getResourcesBinaryPath(version, platform)); + const cacheDir = path.dirname(getCacheBinaryPath(version, platform)); - // Create zip in cache directory first - const cacheZipPath = getCacheZipPath(version); - createCombinedZip(tempDir, cacheZipPath); - - // Copy to resources directory - const resourcesZipPath = getResourcesZipPath(version); - console.log('Copying CLI zip to resources/choreo-cli...'); - fs.copyFileSync(cacheZipPath, resourcesZipPath); - console.log('✓ Copied CLI zip to resources/choreo-cli'); - + console.log(`Extracting ${platform.os}/${platform.arch}...`); + extractBinary(archivePath, platform, resourcesDir); + + // Copy binary to cache + ensureDirectoryExists(cacheDir); + fs.copyFileSync( + path.join(resourcesDir, platform.binary), + path.join(cacheDir, platform.binary) + ); + console.log(` ✓ Cached ${platform.os}/${platform.arch} binary`); + } } finally { console.log('Cleaning up temporary directory...'); deleteDirectory(tempDir); @@ -402,22 +426,22 @@ async function downloadAndCombineCLI(version) { async function main() { try { const cliVersion = getCliVersion(); - - // Check if combined CLI zip already exists + + // Always clean up old versions first, regardless of whether we need to download + cleanupOldFiles(cliVersion); + + // Check if binaries already exist if (checkExistingCLI(cliVersion)) { - console.log('✓ Combined CLI zip is already present'); + console.log('✓ Choreo CLI binaries are already present'); process.exit(0); } console.log(`\nDownloading Choreo CLI version ${cliVersion}...`); - // Clean up old files before downloading new one - cleanupOldFiles(cliVersion); - - // Download all CLI assets and combine into single zip - await downloadAndCombineCLI(cliVersion); + // Download, extract and place binaries + await downloadAndExtractCLI(cliVersion); - console.log(`\n✓ Successfully created Choreo CLI zip for version ${cliVersion}`); + console.log(`\n✓ Successfully extracted Choreo CLI binaries for version ${cliVersion}`); } catch (error) { console.error('\n✗ Error:', error.message); diff --git a/workspaces/wso2-platform/wso2-platform-extension/src/choreo-rpc/cli-install.ts b/workspaces/wso2-platform/wso2-platform-extension/src/choreo-rpc/cli-install.ts index 315d67d6117..f4b69bdfd6f 100644 --- a/workspaces/wso2-platform/wso2-platform-extension/src/choreo-rpc/cli-install.ts +++ b/workspaces/wso2-platform/wso2-platform-extension/src/choreo-rpc/cli-install.ts @@ -16,7 +16,6 @@ * under the License. */ -import { execSync } from "child_process"; import * as fs from "fs"; import * as os from "os"; import * as path from "path"; @@ -54,118 +53,33 @@ export const getChoreoEnv = (): string => { const getChoreoBinPath = () => { const OS = os.platform(); const ARCH = getArchitecture(); - return path.join(ext.context.extensionPath, "resources", "choreo-cli", getCliVersion(), OS, ARCH, "bin"); + return path.join(ext.context.extensionPath, "resources", "choreo-cli", getCliVersion(), OS, ARCH); }; export const installCLI = async () => { const OS = os.platform(); - const ARCH = getArchitecture(); - const CHOREO_BIN_DIR = getChoreoBinPath(); const CHOREO_CLI_EXEC = getChoreoExecPath(); - const CLI_VERSION = getCliVersion(); - - // Path to the combined zip file in resources - const COMBINED_ZIP_PATH = path.join(ext.context.extensionPath, "resources", "choreo-cli", `choreo-cli-${CLI_VERSION}.zip`); - - if (!fs.existsSync(COMBINED_ZIP_PATH)) { - throw new Error(`Combined CLI zip not found at: ${COMBINED_ZIP_PATH}\nPlease run 'pnpm run download-choreo-cli' to download the CLI.`); - } - - getLogger().trace(`Extracting Choreo CLI from: ${COMBINED_ZIP_PATH}`); - - const CHOREO_TMP_DIR = await fs.promises.mkdtemp(path.join(os.tmpdir(), `choreo-cli-rpc-${CLI_VERSION}-`)); - - try { - fs.mkdirSync(CHOREO_BIN_DIR, { recursive: true }); - - // Extract the combined zip to temp directory - getLogger().trace(`Extracting combined zip to temp dir: ${CHOREO_TMP_DIR}`); - try { - if (OS === "win32") { - execSync(`powershell.exe -Command "Expand-Archive -Path '${COMBINED_ZIP_PATH}' -DestinationPath '${CHOREO_TMP_DIR}' -Force"`); - } else { - execSync(`unzip -q '${COMBINED_ZIP_PATH}' -d '${CHOREO_TMP_DIR}'`); - } - } catch (error) { - throw new Error(`Failed to extract combined zip: ${error instanceof Error ? error.message : String(error)}`); - } - - // Determine the specific file to extract based on OS and architecture - const FILE_NAME = `choreo-cli-${CLI_VERSION}-${OS === "win32" ? "windows" : OS}-${ARCH}`; - let FILE_TYPE = ""; - - if (OS === "linux") { - FILE_TYPE = ".tar.gz"; - } else if (OS === "darwin") { - FILE_TYPE = ".zip"; - } else if (OS === "win32") { - FILE_TYPE = ".zip"; - } else { - throw new Error(`Unsupported OS: ${OS}`); - } - const PLATFORM_ARCHIVE = path.join(CHOREO_TMP_DIR, `${FILE_NAME}${FILE_TYPE}`); - - if (!fs.existsSync(PLATFORM_ARCHIVE)) { - throw new Error(`Platform-specific archive not found: ${FILE_NAME}${FILE_TYPE}`); - } - - getLogger().trace(`Extracting platform-specific archive: ${FILE_NAME}${FILE_TYPE}`); - const PLATFORM_TMP_DIR = path.join(CHOREO_TMP_DIR, "platform-extract"); - fs.mkdirSync(PLATFORM_TMP_DIR, { recursive: true }); - - // Extract the platform-specific archive - try { - if (FILE_TYPE === ".tar.gz") { - execSync(`tar -xzf '${PLATFORM_ARCHIVE}' -C '${PLATFORM_TMP_DIR}'`); - } else if (FILE_TYPE === ".zip") { - if (OS === "darwin") { - execSync(`unzip -q '${PLATFORM_ARCHIVE}' -d '${PLATFORM_TMP_DIR}'`); - } else if (OS === "win32") { - execSync(`powershell.exe -Command "Expand-Archive -Path '${PLATFORM_ARCHIVE}' -DestinationPath '${PLATFORM_TMP_DIR}' -Force"`); - } - } - } catch (error) { - throw new Error(`Failed to extract platform-specific archive: ${error instanceof Error ? error.message : String(error)}`); - } - - // Copy the executable to the bin directory - const executableName = OS === "win32" ? "choreo.exe" : "choreo"; - const extractedExecutable = path.join(PLATFORM_TMP_DIR, executableName); - - if (!fs.existsSync(extractedExecutable)) { - throw new Error(`Executable not found after extraction: ${extractedExecutable}`); - } + if (!fs.existsSync(CHOREO_CLI_EXEC)) { + const CLI_VERSION = getCliVersion(); + const ARCH = getArchitecture(); + throw new Error( + `Choreo CLI binary not found at: ${CHOREO_CLI_EXEC}\n` + + `Expected path: resources/choreo-cli/${CLI_VERSION}/${OS}/${ARCH}/${OS === "win32" ? "choreo.exe" : "choreo"}\n` + + `Please run 'pnpm run download-choreo-cli' to download the CLI.` + ); + } - getLogger().trace(`Copying executable to ${CHOREO_BIN_DIR}`); + // Ensure executable permissions on Unix systems (may be lost after git checkout or copy) + if (OS !== "win32") { try { - await fs.promises.copyFile(extractedExecutable, CHOREO_CLI_EXEC); + await fs.promises.chmod(CHOREO_CLI_EXEC, 0o755); } catch (error) { - throw new Error(`Failed to copy executable: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to set executable permissions on ${CHOREO_CLI_EXEC}: ${error instanceof Error ? error.message : String(error)}`); } - - // Set executable permissions on Unix systems - if (OS !== "win32") { - try { - await fs.promises.chmod(CHOREO_CLI_EXEC, 0o755); - } catch (error) { - throw new Error(`Failed to set executable permissions: ${error instanceof Error ? error.message : String(error)}`); - } - } - - getLogger().trace("WSO2 Platform RPC server was installed successfully 🎉"); - } catch (error) { - // Clean up temp directory on error and re-throw - getLogger().error("Error during CLI installation:", error); - await fs.promises.rm(CHOREO_TMP_DIR, { recursive: true, force: true }).catch(() => { - // Ignore cleanup errors - }); - throw error; } - // Clean up temp directory on success - getLogger().trace("Cleaning up temporary files..."); - await fs.promises.rm(CHOREO_TMP_DIR, { recursive: true, force: true }); + getLogger().trace("WSO2 Platform RPC server is ready 🎉"); }; function getArchitecture() {