diff --git a/package.json b/package.json index 49e724bd..c7fce1ec 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "npx-scope-finder": "^1.2.0", "officeparser": "^4.1.1", "p-queue": "^8.1.0", + "tar": "^7.4.3", "tokenx": "^0.4.1", "undici": "^7.4.0", "webdav": "4.11.4", diff --git a/resources/scripts/install-bun.js b/resources/scripts/install-bun.js index ada5be15..ddb20772 100644 --- a/resources/scripts/install-bun.js +++ b/resources/scripts/install-bun.js @@ -3,6 +3,7 @@ const path = require('path') const os = require('os') const { execSync } = require('child_process') const https = require('https') +const AdmZip = require('adm-zip') // Base URL for downloading bun binaries const BUN_RELEASE_BASE_URL = 'https://github.com/oven-sh/bun/releases/download' @@ -72,6 +73,41 @@ async function getLatestBunVersion() { }) } +/** + * Downloads a file from a URL with redirect handling + * @param {string} url The URL to download from + * @param {string} destinationPath The path to save the file to + * @returns {Promise} + */ +async function downloadWithRedirects(url, destinationPath) { + return new Promise((resolve, reject) => { + const file = fs.createWriteStream(destinationPath) + const request = (url) => { + https + .get(url, (response) => { + if (response.statusCode === 302 || response.statusCode === 301) { + // Handle redirect + request(response.headers.location) + return + } + + if (response.statusCode !== 200) { + reject(new Error(`Failed to download: ${response.statusCode}`)) + return + } + + response.pipe(file) + file.on('finish', () => { + file.close(resolve) + }) + }) + .on('error', reject) + } + + request(url) + }) +} + /** * Downloads and extracts the bun binary for the specified platform and architecture * @param {string} platform Platform to download for (e.g., 'darwin', 'win32', 'linux') @@ -107,16 +143,36 @@ async function downloadBunBinary(platform, arch, version = DEFAULT_BUN_VERSION, console.log(`Downloading bun ${version} for ${platformKey}...`) console.log(`URL: ${downloadUrl}`) - // Download the file - execSync(`curl --fail -L -o "${localFilename}" "${downloadUrl}"`, { stdio: 'inherit' }) + // Use the new download function + await downloadWithRedirects(downloadUrl, localFilename) - // Extract the zip file + // Extract the zip file using adm-zip console.log(`Extracting ${packageName} to ${archDir}...`) - execSync(`unzip -o "${localFilename}" -d "${tempdir}"`, { stdio: 'inherit' }) - execSync(`mv ${tempdir}/${packageName.split('.')[0]}/* ${archDir}/`, { stdio: 'inherit' }) + const zip = new AdmZip(localFilename) + zip.extractAllTo(tempdir, true) - // Clean up the downloaded file + // Move files using Node.js fs + const sourceDir = path.join(tempdir, packageName.split('.')[0]) + const files = fs.readdirSync(sourceDir) + for (const file of files) { + const sourcePath = path.join(sourceDir, file) + const destPath = path.join(archDir, file) + fs.renameSync(sourcePath, destPath) + + // Set executable permissions for non-Windows platforms + if (platform !== 'win32') { + try { + // 755 permission: rwxr-xr-x + fs.chmodSync(destPath, '755') + } catch (error) { + console.warn(`Warning: Failed to set executable permissions: ${error.message}`) + } + } + } + + // Clean up fs.unlinkSync(localFilename) + fs.rmdirSync(sourceDir, { recursive: true }) console.log(`Successfully installed bun ${version} for ${platformKey}`) return true diff --git a/resources/scripts/install-uv.js b/resources/scripts/install-uv.js index 245dbe10..cca0d3e3 100644 --- a/resources/scripts/install-uv.js +++ b/resources/scripts/install-uv.js @@ -3,6 +3,7 @@ const path = require('path') const os = require('os') const { execSync } = require('child_process') const https = require('https') +const tar = require('tar') // Base URL for downloading uv binaries const UV_RELEASE_BASE_URL = 'https://github.com/astral-sh/uv/releases/download' @@ -30,6 +31,41 @@ const UV_PACKAGES = { 'linux-musl-armv7l': 'uv-armv7-unknown-linux-musleabihf.tar.gz' } +/** + * Downloads a file from a URL with redirect handling + * @param {string} url The URL to download from + * @param {string} destinationPath The path to save the file to + * @returns {Promise} + */ +async function downloadWithRedirects(url, destinationPath) { + return new Promise((resolve, reject) => { + const file = fs.createWriteStream(destinationPath) + const request = (url) => { + https + .get(url, (response) => { + if (response.statusCode === 302 || response.statusCode === 301) { + // Handle redirect + request(response.headers.location) + return + } + + if (response.statusCode !== 200) { + reject(new Error(`Failed to download: ${response.statusCode}`)) + return + } + + response.pipe(file) + file.on('finish', () => { + file.close(resolve) + }) + }) + .on('error', reject) + } + + request(url) + }) +} + /** * Fetches the latest version of uv from GitHub API * @returns {Promise} The latest version tag (without 'v' prefix) @@ -105,21 +141,40 @@ async function downloadUvBinary(platform, arch, version = DEFAULT_UV_VERSION, is console.log(`Downloading uv ${version} for ${platformKey}...`) console.log(`URL: ${downloadUrl}`) - // Download the file - execSync(`curl --fail -L -o "${localFilename}" "${downloadUrl}"`, { stdio: 'inherit' }) + // Use the new download function + await downloadWithRedirects(downloadUrl, localFilename) - // Extract based on file extension + // Extract files using tar console.log(`Extracting ${packageName} to ${archDir}...`) - if (packageName.endsWith('.tar.gz')) { - execSync(`tar -xzf "${localFilename}" -C "${tempdir}"`, { stdio: 'inherit' }) - } else if (packageName.endsWith('.zip')) { - execSync(`unzip -o "${localFilename}" -d "${tempdir}"`, { stdio: 'inherit' }) + await tar.x({ + file: localFilename, + cwd: tempdir, + // zip 文件也可以用 tar 解压 + z: packageName.endsWith('.tar.gz') // 对于 .tar.gz 文件启用 gzip 解压 + }) + + // Move files using Node.js fs + const sourceDir = path.join(tempdir, packageName.split('.')[0]) + const files = fs.readdirSync(sourceDir) + for (const file of files) { + const sourcePath = path.join(sourceDir, file) + const destPath = path.join(archDir, file) + fs.renameSync(sourcePath, destPath) + + // Set executable permissions for non-Windows platforms + if (platform !== 'win32') { + try { + // 755 permission: rwxr-xr-x + fs.chmodSync(destPath, '755') + } catch (error) { + console.warn(`Warning: Failed to set executable permissions: ${error.message}`) + } + } } - execSync(`mv ${tempdir}/${packageName.split('.')[0]}/* ${archDir}/`, { stdio: 'inherit' }) - - // Clean up the downloaded file + // Clean up fs.unlinkSync(localFilename) + fs.rmdirSync(sourceDir, { recursive: true }) console.log(`Successfully installed uv ${version} for ${platform}-${arch}`) return true diff --git a/yarn.lock b/yarn.lock index 4662b792..69954ca3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3262,6 +3262,7 @@ __metadata: shiki: "npm:^1.22.2" string-width: "npm:^7.2.0" styled-components: "npm:^6.1.11" + tar: "npm:^7.4.3" tinycolor2: "npm:^1.6.0" tokenx: "npm:^0.4.1" typescript: "npm:^5.6.2"