Merge pull request #3278 from mikiher/revert-to-ffbinaries

Go back to downloading binaries from ffbinaries.com
This commit is contained in:
advplyr 2024-08-14 16:43:01 -05:00 committed by GitHub
commit 603823d6ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,69 +1,71 @@
const child_process = require('child_process') const child_process = require('child_process')
const { promisify } = require('util') const { promisify } = require('util')
const exec = promisify(child_process.exec) const exec = promisify(child_process.exec)
const path = require('path') const os = require('os')
const axios = require('axios') const axios = require('axios')
const path = require('path')
const which = require('../libs/which') const which = require('../libs/which')
const fs = require('../libs/fsExtra') const fs = require('../libs/fsExtra')
const Logger = require('../Logger') const Logger = require('../Logger')
const fileUtils = require('../utils/fileUtils') const fileUtils = require('../utils/fileUtils')
const StreamZip = require('../libs/nodeStreamZip') const StreamZip = require('../libs/nodeStreamZip')
class GithubAssetDownloader { class ZippedAssetDownloader {
constructor(owner, repo) { constructor() {
this.owner = owner
this.repo = repo
this.assetCache = {} this.assetCache = {}
} }
getReleaseUrl(releaseTag) {
throw new Error('Not implemented')
}
extractAssetUrl(assets, assetName) {
throw new Error('Not implemented')
}
getAssetName(binaryName, releaseTag) {
throw new Error('Not implemented')
}
getAssetFileName(binaryName) {
throw new Error('Not implemented')
}
async getAssetUrl(releaseTag, assetName) { async getAssetUrl(releaseTag, assetName) {
// Check if the assets information is already cached for the release tag // Check if the assets information is already cached for the release tag
if (this.assetCache[releaseTag]) { if (this.assetCache[releaseTag]) {
Logger.debug(`[GithubAssetDownloader] Repo ${this.repo} release ${releaseTag}: assets found in cache.`) Logger.debug(`[ZippedAssetDownloader] release ${releaseTag}: assets found in cache.`)
} else { } else {
// Get the release information // Get the release information
const releaseUrl = `https://api.github.com/repos/${this.owner}/${this.repo}/releases/tags/${releaseTag}` const releaseUrl = this.getReleaseUrl(releaseTag)
const releaseResponse = await axios.get(releaseUrl, { const releaseResponse = await axios.get(releaseUrl, { headers: { 'User-Agent': 'axios' } })
headers: {
Accept: 'application/vnd.github.v3+json',
'User-Agent': 'axios'
}
})
// Cache the assets information for the release tag // Cache the assets information for the release tag
this.assetCache[releaseTag] = releaseResponse.data.assets this.assetCache[releaseTag] = releaseResponse.data
Logger.debug(`[GithubAssetDownloader] Repo ${this.repo} release ${releaseTag}: assets fetched from API.`) Logger.debug(`[ZippedAssetDownloader] release ${releaseTag}: assets fetched from API.`)
} }
// Find the asset URL
const assets = this.assetCache[releaseTag] const assets = this.assetCache[releaseTag]
const asset = assets.find((asset) => asset.name === assetName) const assetUrl = this.extractAssetUrl(assets, assetName)
if (!asset) {
throw new Error(`[GithubAssetDownloader] Repo ${this.repo} release ${releaseTag}: asset ${assetName} not found`)
}
return asset.browser_download_url return assetUrl
} }
async downloadAsset(assetUrl, destDir) { async downloadAsset(assetUrl, destDir) {
const zipPath = path.join(destDir, 'temp.zip') const zipPath = path.join(destDir, 'temp.zip')
const writer = fs.createWriteStream(zipPath) const writer = fs.createWriteStream(zipPath)
const assetResponse = await axios({ const assetResponse = await axios({ url: assetUrl, responseType: 'stream' })
url: assetUrl,
method: 'GET',
responseType: 'stream'
})
assetResponse.data.pipe(writer) assetResponse.data.pipe(writer)
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
writer.on('finish', () => { writer.on('finish', () => {
Logger.debug(`[GithubAssetDownloader] Downloaded asset ${assetUrl} to ${zipPath}`) Logger.debug(`[ZippedAssetDownloader] Downloaded asset ${assetUrl} to ${zipPath}`)
resolve() resolve()
}) })
writer.on('error', (err) => { writer.on('error', (err) => {
Logger.error(`[GithubAssetDownloader] Error downloading asset ${assetUrl}: ${err.message}`) Logger.error(`[ZippedAssetDownloader] Error downloading asset ${assetUrl}: ${err.message}`)
reject(err) reject(err)
}) })
}) })
@ -77,7 +79,7 @@ class GithubAssetDownloader {
for (const file of filesToExtract) { for (const file of filesToExtract) {
const outputPath = path.join(destDir, file.outputFileName) const outputPath = path.join(destDir, file.outputFileName)
await zip.extract(file.pathInsideZip, outputPath) await zip.extract(file.pathInsideZip, outputPath)
Logger.debug(`[GithubAssetDownloader] Extracted file ${file.pathInsideZip} to ${outputPath}`) Logger.debug(`[ZippedAssetDownloader] Extracted file ${file.pathInsideZip} to ${outputPath}`)
// Set executable permission for Linux // Set executable permission for Linux
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
@ -96,101 +98,69 @@ class GithubAssetDownloader {
zipPath = await this.downloadAsset(assetUrl, destDir) zipPath = await this.downloadAsset(assetUrl, destDir)
await this.extractFiles(zipPath, filesToExtract, destDir) await this.extractFiles(zipPath, filesToExtract, destDir)
} catch (error) { } catch (error) {
Logger.error(`[GithubAssetDownloader] Error downloading or extracting files: ${error.message}`) Logger.error(`[ZippedAssetDownloader] Error downloading or extracting files: ${error.message}`)
throw error throw error
} finally { } finally {
if (zipPath) await fs.remove(zipPath) if (zipPath) await fs.remove(zipPath)
} }
} }
}
class FFBinariesDownloader extends GithubAssetDownloader {
constructor() {
super('ffbinaries', 'ffbinaries-prebuilt')
}
getPlatformSuffix() {
const platform = process.platform
const arch = process.arch
switch (platform) {
case 'win32':
return 'win-64'
case 'darwin':
return 'macos-64'
case 'linux':
switch (arch) {
case 'x64':
return 'linux-64'
case 'x32':
case 'ia32':
return 'linux-32'
case 'arm64':
return 'linux-arm-64'
case 'arm':
return 'linux-armhf-32'
default:
throw new Error(`Unsupported architecture: ${arch}`)
}
default:
throw new Error(`Unsupported platform: ${platform}`)
}
}
async downloadBinary(binaryName, releaseTag, destDir) { async downloadBinary(binaryName, releaseTag, destDir) {
const platformSuffix = this.getPlatformSuffix() const assetName = this.getAssetName(binaryName, releaseTag)
const assetName = `${binaryName}-${releaseTag}-${platformSuffix}.zip` const fileName = this.getAssetFileName(binaryName)
const fileName = process.platform === 'win32' ? `${binaryName}.exe` : binaryName
const filesToExtract = [{ pathInsideZip: fileName, outputFileName: fileName }] const filesToExtract = [{ pathInsideZip: fileName, outputFileName: fileName }]
releaseTag = `v${releaseTag}`
await this.downloadAndExtractFiles(releaseTag, assetName, filesToExtract, destDir) await this.downloadAndExtractFiles(releaseTag, assetName, filesToExtract, destDir)
} }
} }
class SQLeanDownloader extends GithubAssetDownloader { class FFBinariesDownloader extends ZippedAssetDownloader {
constructor() { constructor() {
super('nalgeon', 'sqlean') super()
this.platformSuffix = this.getPlatformSuffix()
} }
getPlatformSuffix() { getPlatformSuffix() {
const platform = process.platform var type = os.type().toLowerCase()
const arch = process.arch var arch = os.arch().toLowerCase()
switch (platform) { if (type === 'darwin') {
case 'win32': return 'osx-64'
return arch === 'x64' ? 'win-x64' : 'win-x86'
case 'darwin':
return arch === 'arm64' ? 'macos-arm64' : 'macos-x86'
case 'linux':
return arch === 'arm64' ? 'linux-arm64' : 'linux-x86'
default:
throw new Error(`Unsupported platform or architecture: ${platform}, ${arch}`)
} }
if (type === 'windows_nt') {
return arch === 'x64' ? 'windows-64' : 'windows-32'
}
if (type === 'linux') {
if (arch === 'arm') return 'linux-armel'
if (arch === 'arm64') return 'linux-arm64'
return arch === 'x64' ? 'linux-64' : 'linux-32'
}
return null
} }
getLibraryName(binaryName) { getReleaseUrl(releaseTag) {
const platform = process.platform return `https://ffbinaries.com/api/v1/version/${releaseTag}`
switch (platform) {
case 'win32':
return `${binaryName}.dll`
case 'darwin':
return `${binaryName}.dylib`
case 'linux':
return `${binaryName}.so`
default:
throw new Error(`Unsupported platform: ${platform}`)
}
} }
async downloadBinary(binaryName, releaseTag, destDir) { extractAssetUrl(assets, assetName) {
const platformSuffix = this.getPlatformSuffix() const assetUrl = assets?.bin?.[this.platformSuffix]?.[assetName]
const assetName = `sqlean-${platformSuffix}.zip`
const fileName = this.getLibraryName(binaryName)
const filesToExtract = [{ pathInsideZip: fileName, outputFileName: fileName }]
await this.downloadAndExtractFiles(releaseTag, assetName, filesToExtract, destDir) if (!assetUrl) {
throw new Error(`[FFBinariesDownloader] Asset ${assetName} not found for platform ${this.platformSuffix}`)
}
return assetUrl
}
getAssetName(binaryName, releaseTag) {
return binaryName
}
getAssetFileName(binaryName) {
return process.platform === 'win32' ? `${binaryName}.exe` : binaryName
} }
} }
@ -257,8 +227,8 @@ class Binary {
const ffbinaries = new FFBinariesDownloader() const ffbinaries = new FFBinariesDownloader()
module.exports.ffbinaries = ffbinaries // for testing module.exports.ffbinaries = ffbinaries // for testing
const sqlean = new SQLeanDownloader() //const sqlean = new SQLeanDownloader()
module.exports.sqlean = sqlean // for testing //module.exports.sqlean = sqlean // for testing
class BinaryManager { class BinaryManager {
defaultRequiredBinaries = [ defaultRequiredBinaries = [