|
| 1 | +const Promise = require('bluebird'); |
| 2 | +const _ = require('lodash'); |
| 3 | +const decompress = require('decompress'); |
| 4 | +const decompressTargz = require('decompress-targz'); |
| 5 | +const decompressUnzip = require('decompress-unzip'); |
| 6 | +const rp = require('request-promise'); |
| 7 | +const request = require('request'); |
| 8 | +const compareVersions = require('compare-versions'); |
| 9 | +const { |
| 10 | + resolve, join, |
| 11 | +} = require('path'); |
| 12 | +const { |
| 13 | + homedir, arch, |
| 14 | +} = require('os'); |
| 15 | +const { |
| 16 | + existsSync, mkdirSync, readFileSync, createWriteStream, writeFile, |
| 17 | +} = require('fs'); |
| 18 | +const { to } = require('./../logic/cli-config/errors/awaitTo'); |
| 19 | + |
| 20 | +const CODEFRESH_PATH = resolve(homedir(), '.Codefresh'); |
| 21 | + |
| 22 | +function _ensureDirectory(location) { |
| 23 | + if (existsSync(location)) { |
| 24 | + return Promise.resolve(); |
| 25 | + } |
| 26 | + mkdirSync(location); |
| 27 | + return Promise.resolve(); |
| 28 | +} |
| 29 | + |
| 30 | +async function _getRemoteVersion({ |
| 31 | + name, branch, path, file, |
| 32 | +}) { |
| 33 | + const final = branch ? `${name}/${branch}/${path}` : `${name}/${path}`; |
| 34 | + const url = `https://raw.githubusercontent.com/codefresh-io/${final}/${file}`; |
| 35 | + const req = await rp({ |
| 36 | + url, |
| 37 | + method: 'GET', |
| 38 | + headers: { 'User-Agent': 'codefresh' }, |
| 39 | + }); |
| 40 | + return req; |
| 41 | +} |
| 42 | + |
| 43 | +function _buildDownloadURL({ name, version, binary }) { |
| 44 | + return `https://github.com/codefresh-io/${name}/releases/download/${version}/${binary}`; |
| 45 | +} |
| 46 | + |
| 47 | +function _getLocalVersion(location) { |
| 48 | + if (!existsSync(location)) { |
| 49 | + return '0'; |
| 50 | + } |
| 51 | + const content = readFileSync(location, { encoding: 'UTF8' }).trim(); |
| 52 | + if (content === '') { |
| 53 | + return '0'; |
| 54 | + } |
| 55 | + return content; |
| 56 | +} |
| 57 | + |
| 58 | +function _buildLocalOSProperties() { |
| 59 | + let osType; |
| 60 | + let osSuffix; |
| 61 | + const { platform } = process; |
| 62 | + if (_.isEqual(platform, 'darwin')) { |
| 63 | + osType = _.isEqual(arch(), 'x32') ? 'Darwin_i386.tar.gz' : 'Darwin_x86_64.tar.gz'; |
| 64 | + osSuffix = 'tar.gz'; |
| 65 | + } else if (_.isEqual(platform, 'linux')) { |
| 66 | + osType = _.isEqual(arch(), 'x32') ? 'Linux_i386.tar.gz' : 'Linux_x86_64.tar.gz'; |
| 67 | + osSuffix = 'tar.gz'; |
| 68 | + } else if (_.isEqual(platform, 'win32')) { |
| 69 | + osType = _.isEqual(arch(), 'x32') ? 'Windows_i386.zip' : 'Windows_x86_64.zip'; |
| 70 | + osSuffix = 'zip'; |
| 71 | + } |
| 72 | + |
| 73 | + return { |
| 74 | + osType, |
| 75 | + osSuffix, |
| 76 | + }; |
| 77 | +} |
| 78 | + |
| 79 | +async function _writeFiles({ |
| 80 | + zipPath, location, version, versionPath, |
| 81 | +}) { |
| 82 | + await to(decompress(zipPath, location, { |
| 83 | + plugins: [ |
| 84 | + decompressTargz(), |
| 85 | + decompressUnzip(), |
| 86 | + ], |
| 87 | + })); |
| 88 | + return Promise.fromCallback(cb => writeFile(versionPath, version, cb)); |
| 89 | +} |
| 90 | + |
| 91 | +class Downloader { |
| 92 | + constructor(options = {}) { |
| 93 | + this.location = options.location || CODEFRESH_PATH; |
| 94 | + this.logger = options.logger || console; |
| 95 | + this.progress = options.progress; |
| 96 | + } |
| 97 | + |
| 98 | + async download(component) { |
| 99 | + const { location, logger } = this; |
| 100 | + _ensureDirectory(location); |
| 101 | + const dir = join(location, component.local.dir); |
| 102 | + _ensureDirectory(dir); |
| 103 | + const compressedBinaryLocation = join(dir, 'data'); |
| 104 | + |
| 105 | + const { osType, osSuffix } = _buildLocalOSProperties(); |
| 106 | + |
| 107 | + let localVersion = _getLocalVersion(join(dir, component.local.versionFile)); |
| 108 | + const { |
| 109 | + repo: name, branch, versionPath: path, versionFile: file, |
| 110 | + } = component.remote; |
| 111 | + let remoteVersion = await _getRemoteVersion({ |
| 112 | + name, branch, path, file, |
| 113 | + }); |
| 114 | + remoteVersion = remoteVersion.trim(); |
| 115 | + localVersion = localVersion.trim(); |
| 116 | + |
| 117 | + if (compareVersions(localVersion, remoteVersion) >= 0) { |
| 118 | + // logger.debug(`Download is not required latest-version=${remoteVersion} local-version=${localVersion}`); |
| 119 | + return Promise.resolve(); |
| 120 | + } |
| 121 | + logger.debug(`${component.name} component upgrade is required, downloading.`); |
| 122 | + |
| 123 | + |
| 124 | + const binary = `${name}_${remoteVersion}_${osType}`; |
| 125 | + const version = component.version.prefix ? `${component.version.prefix}${remoteVersion}` : remoteVersion; |
| 126 | + const url = _buildDownloadURL({ name, version, binary }); |
| 127 | + const resp = await request(url); |
| 128 | + |
| 129 | + if (this.progress) { |
| 130 | + let size = 0; |
| 131 | + resp.on('response', (res) => { |
| 132 | + size = parseInt(res.headers['content-length'], 10); |
| 133 | + this.progress.start(size, 0); |
| 134 | + }); |
| 135 | + |
| 136 | + resp.on('data', (chunk) => { |
| 137 | + size += chunk.length; |
| 138 | + this.progress.update(this.progress.value + chunk.length); |
| 139 | + if (this.progress.value + chunk.length >= size) { |
| 140 | + logger.log('\n'); |
| 141 | + } |
| 142 | + }); |
| 143 | + } |
| 144 | + |
| 145 | + const zipLocation = `${compressedBinaryLocation}.${osSuffix}`; |
| 146 | + resp.pipe(createWriteStream(zipLocation)); |
| 147 | + |
| 148 | + return new Promise((resolveFn, rejectFn) => { |
| 149 | + resp.on('end', async () => { |
| 150 | + const [err] = await to(_writeFiles({ |
| 151 | + zipPath: zipLocation, location: dir, version: remoteVersion, versionPath: resolve(dir, component.local.versionFile), |
| 152 | + })); |
| 153 | + if (err) { |
| 154 | + rejectFn(err); |
| 155 | + return; |
| 156 | + } |
| 157 | + resolveFn(); |
| 158 | + }); |
| 159 | + }); |
| 160 | + } |
| 161 | +} |
| 162 | + |
| 163 | +module.exports = { |
| 164 | + CommonProgressFormat: 'downloading [{bar}] {percentage}% | {value}/{total}', |
| 165 | + Downloader, |
| 166 | +}; |
0 commit comments