madp-cli1
Version:
A simple CLI for scaffolding madp projects, we provide madp-template to quickly build small and medium sized app.
612 lines (584 loc) • 19.3 kB
JavaScript
const jsonfile = require('jsonfile');
const fs = require('fs-extra');
const path = require('path');
const ora = require('ora');
const decompress = require('decompress');
const tmp = require('tmp');
const request = require('request').defaults({
headers: {
'User-Agent': 'request',
},
});
const utils = require('./lib/utils');
const logger = require('./lib/utils/logger');
class TemplateRelease {
/**
* 构造函数,必须传入以下参数
* @param {String} name 项目的名称,将用于在家目录创建形如 .eeui 的缓存目录
* @param {String} releaseUrl 形如 https://api.github.com/repos/kuaifan/eeui-template/releases
* @return {TemplateRelease}
*/
constructor(name, releaseUrl, templateUrl, methodsUrl) {
if (!name) {
throw new Error('Invalid argument');
}
// if (!name || !releaseUrl) {
// throw new Error('Invalid argument');
// }
this.name = name;
this.releaseUrl = releaseUrl;
this.templateUrl = templateUrl;
this.methodsUrl = methodsUrl;
this.CACHE_DIR_NAME = '.' + name;
this.CACHE_DIR_PATH = path.join(require('os').homedir(), this.CACHE_DIR_NAME);
this.CACHE_TEMPLATE_PATH = path.join(this.CACHE_DIR_PATH, 'template');
this.CACHE_METHODS_PATH = path.join(this.CACHE_DIR_PATH, 'methods');
this.RELEASES_JSON_PATH = path.join(this.CACHE_TEMPLATE_PATH, 'release.json');
}
/**
* 获取所有 release 的版本。只获取版本,不会下载到缓存区。
* @param {Function} cb 接受参数 error 以及无错时的版本数组 []string
*/
fetchReleaseVersions(cb) {
// this.releaseUrl=https://api.github.com/repos/kuaifan/eeui-template/releases
request.get(this.releaseUrl, function (err, res, body) {
if (err) {
cb && cb(err);
return;
}
if (res.statusCode !== 200) {
cb && cb(`获取信息失败 - ${res.statusCode}: ${res.body}`);
return;
}
let tags = JSON.parse(body).map(function (e) {
return e;
});
console('zoulezheli***********')
cb && cb(null, tags);
});
}
fetchTemplateReleaseVersions(cb) {
// this.releaseUrl=https://api.github.com/repos/kuaifan/eeui-template/releases
request.get(this.templateUrl, function (err, res, body) {
if (err) {
cb && cb(err);
return;
}
if (res.statusCode !== 200) {
cb && cb(`获取信息失败 - ${res.statusCode}: ${res.body}`);
return;
}
let tags = JSON.parse(body).map(function (e) {
return e;
});
cb && cb(null, tags);
});
}
/**
* 获取指定版本的 release,首先尝试缓存(CACHE_TEMPLATE_PATH),如果未缓存,再尝试下载并缓存
* @param {string} release 指定版本,如果为空,表示最新版本
* @param {string} location 下载服务器
* @param {Function} cb 通过该回调返回错误 error,以及无错时的 release 的路径,一般形如 ~/.eeui/template/0.1.0
*/
fetchRelease(release, location, obj, cb) {
let type = release.split('|')[0];
let releaseVersion = release.split('|')[1];
let releasesInfo = this._readReleaseJSON();
console.log(releasesInfo, 'relaseinfo')
// console.log(release)
// if (release) {
// let info = releasesInfo[release];
// if (info) {
// cb(null, path.join(this.CACHE_TEMPLATE_PATH, info.path));
// return;
// }
// }
// let url = this._getReleaseUrl(release);
// let url = this.releaseUrl;
// console.log(url)
let spinText = `正在下载模板版本: ${releaseVersion ? releaseVersion : 'latest'}...`;
let spinDown = ora(spinText);
// spinDown.start();
// https://api.github.com/repos/kuaifan/eeui-template/releases/tags/2.4.2
// {
// "url": "https://api.github.com/repos/kuaifan/eeui-template/releases/35346901",
// "assets_url": "https://api.github.com/repos/kuaifan/eeui-template/releases/35346901/assets",
// "upload_url": "https://uploads.github.com/repos/kuaifan/eeui-template/releases/35346901/assets{?name,label}",
// "html_url": "https://github.com/kuaifan/eeui-template/releases/tag/2.4.2",
// "id": 35346901,
// "author": {
// "login": "kuaifan",
// "id": 36875852,
// "node_id": "MDQ6VXNlcjM2ODc1ODUy",
// "avatar_url": "https://avatars2.githubusercontent.com/u/36875852?v=4",
// "gravatar_id": "",
// "url": "https://api.github.com/users/kuaifan",
// "html_url": "https://github.com/kuaifan",
// "followers_url": "https://api.github.com/users/kuaifan/followers",
// "following_url": "https://api.github.com/users/kuaifan/following{/other_user}",
// "gists_url": "https://api.github.com/users/kuaifan/gists{/gist_id}",
// "starred_url": "https://api.github.com/users/kuaifan/starred{/owner}{/repo}",
// "subscriptions_url": "https://api.github.com/users/kuaifan/subscriptions",
// "organizations_url": "https://api.github.com/users/kuaifan/orgs",
// "repos_url": "https://api.github.com/users/kuaifan/repos",
// "events_url": "https://api.github.com/users/kuaifan/events{/privacy}",
// "received_events_url": "https://api.github.com/users/kuaifan/received_events",
// "type": "User",
// "site_admin": false
// },
// "node_id": "MDc6UmVsZWFzZTM1MzQ2OTAx",
// "tag_name": "2.4.2",
// "target_commitish": "master",
// "name": "",
// "draft": false,
// "prerelease": false,
// "created_at": "2020-12-16T11:45:25Z",
// "published_at": "2020-12-16T12:10:29Z",
// "assets": [
// ],
// "tarball_url": "https://api.github.com/repos/kuaifan/eeui-template/tarball/2.4.2",
// "zipball_url": "https://api.github.com/repos/kuaifan/eeui-template/zipball/2.4.2",
// "body": "升级:WeexSDK至0.29.0-RC1\r\n修复:iphone7 iOS14.2系统环境下,获取导航栏高度闪退bug(@xztl 提供)\r\n修复:获取导航栏部分设备和系统主线程卡死问题(@xztl 提供)"
// }
// request(url, (err, res, body) => {
// let body1 = JSON.parse(body);
// console.log("=====")
// console.log(type+"||"+releaseVersion)
console.log("=====155")
let info = obj;
// let info = {};
// body1.some((t) => {
// if (t.type == type && t.tag_version == releaseVersion) {
// info = t;
// }
// });
// console.log(body1,'162')
// let info={
// "tag_name": release,
// "published_at":"2020-12-16T12:10:29Z",
// "zipball_url":'http://localhost:5000/madp-seed-h5.zip'
// }
// spinDown.stop();
let newInfo = {};
let tag = (newInfo.tag = info['tag_name'] + info['tag_version']);
// newInfo.time = info["published_at"];
newInfo.path = newInfo.tag;
// console.log("target:"+this.CACHE_TEMPLATE_PATH+":"+newInfo.path)
console.log(this.CACHE_TEMPLATE_PATH)
let targetPath = path.join(this.CACHE_TEMPLATE_PATH, newInfo.path);
console.log("=====177")
console.log('info', info)
console.log("=====179")
console.log('newInfo', newInfo)
console.log("=====")
console.log('targetPath', targetPath)
if (fs.pathExistsSync(targetPath)) {
logger.info(`已经缓存的版本。`);
cb(null, targetPath);
return;
}
spinDown.start();
console.log('zipball_url:' + info['zipball_url']);
// this._downloadAndUnzip('http://192.168.20.24:5000/madp-seed.zip', targetPath, (err) => {
this._downloadAndUnzip(
info['zipball_url'],
targetPath,
(err) => {
console.log('targetPath cb:' + targetPath);
console.log('err:' + err);
spinDown.stop();
if (err) {
cb && cb(err);
return;
}
releasesInfo[tag] = newInfo;
console.log(releasesInfo, 'releasesInfo----!!!!!');
console.log("=====")
console.log(this.RELEASES_JSON_PATH, 'this.RELEASES_JSON_PATH');
jsonfile.writeFileSync(this.RELEASES_JSON_PATH, releasesInfo, { spaces: 2 });
cb(null, targetPath);
},
(res) => {
spinDown.text = spinText + `(${res.progress}, ${res.speed})`;
},
);
// });
}
/**
* 返回缓存里的 release 信息
* @param {string} [release] 指定版本,不指定则返回最新
* @return {Object} release 信息
*/
getCachedReleaseInfo(release) {
let releasesInfo = this._readReleaseJSON();
if (release) {
return releasesInfo[release];
}
let latestRleaseInfo = null;
for (let tag in releasesInfo) {
let info = releasesInfo[tag];
if (!latestRleaseInfo) {
latestRleaseInfo = info;
} else {
if (Date.parse(info.time) > Date.parse(latestRleaseInfo.time)) latestRleaseInfo = info;
}
}
return latestRleaseInfo;
}
_readReleaseJSON() {
fs.ensureFileSync(this.RELEASES_JSON_PATH);
try {
return jsonfile.readFileSync(this.RELEASES_JSON_PATH);
} catch (e) {
return {};
}
}
_getReleaseUrl(tag) {
return this.releaseUrl + '/' + (tag ? `tags/${tag}` : 'latest');
}
/**
* 把 url (zipball_url) 的内容下载并解压到 savePath
* @param {string} url
* @param {string} savePath
* @param {Function} cb 接收参数 error
* @param {Function} progressCall 接收进度
*/
_downloadAndUnzip(url, savePath, cb, progressCall) {
let TMP_DOWNLOAD_PATH = tmp.tmpNameSync({ dir: require('os').tmpdir() }) + '.zip';
console.log('TMP_DOWNLOAD_PATH.', TMP_DOWNLOAD_PATH)
let file = fs.createWriteStream(TMP_DOWNLOAD_PATH);
file.on('close', () => {
console.log(TMP_DOWNLOAD_PATH + "||||" + this.CACHE_TEMPLATE_PATH)
decompress(TMP_DOWNLOAD_PATH, this.CACHE_TEMPLATE_PATH, {
map: (file) => {
if (file.data.length == 0) {
file.type = 'directory';
}
return file;
},
})
.then(() => {
console.log('===1');
let origPath = this._getLastReleasePath(savePath);
console.log('===2' + origPath + '||' + savePath);
if (origPath) {
fs.moveSync(origPath, savePath); // 重命名为指定名
}
fs.unlinkSync(TMP_DOWNLOAD_PATH); // 删除下载的压缩包
console.log('===3');
cb && cb();
})
.catch((err) => {
cb && cb(`下载版本失败: ${err}`);
});
}).on('error', (err) => {
cb && cb(err);
});
//
let receivedBytes = 0;
let totalBytes = 0;
let speedBytes = 0;
let speedPer = '0B/S';
let speedInt = setInterval(() => {
speedPer = utils.renderSize(Math.max(0, receivedBytes - speedBytes)) + '/S';
speedBytes = receivedBytes;
}, 1000);
console.log('=====url', url)
request
.get(url)
.on('error', function (err) {
cb && cb(`下载版本错误: ${err}`);
})
.on('response', function (res) {
console.log(url, 'url')
if (res.statusCode !== 200) {
cb && cb('Get zipUrl return a non-200 response.');
}
totalBytes = parseInt(res.headers['content-length'], 10);
if (isNaN(totalBytes)) totalBytes = 0;
})
.on('data', (chunk) => {
receivedBytes += chunk.length;
let progress = '0%';
if (totalBytes > 0) {
progress = parseFloat(Math.max(0, (receivedBytes / totalBytes) * 100).toFixed(2)) + '%';
} else {
progress = utils.renderSize(receivedBytes);
}
progressCall &&
progressCall({
received: receivedBytes,
total: totalBytes,
speed: speedPer,
progress: progress,
});
})
.on('end', function () {
clearInterval(speedInt);
})
.pipe(file);
}
/**
* 获取刚下载解压的 release 的路径
* TODO: 目前无法准确获取 release 解压之后的目录名称,只能根据某种模式推断
* templateReleaseUrl: "https://api.github.com/repos/kuaifan/eeui-template/releases",
* [ 'madp', 'release.json' ]
[
'https:',
'',
'api.github.com',
'repos',
'kuaifan',
'eeui-template',
'releases'
]
*/
_getLastReleasePath(savePath) {
let files = fs.readdirSync(this.CACHE_TEMPLATE_PATH);
console.log('||||||' + this.CACHE_TEMPLATE_PATH);
console.log(files);
console.log(savePath, '222222@@@');
for (let f of files) {
if (savePath.indexOf(f) !== -1) {
let ss = path.join(this.CACHE_TEMPLATE_PATH, f);
console.log(ss);
return ss;
}
}
return null;
}
/**
* 获取所有 方法集release 的版本。只获取版本,不会下载到缓存区。
* @param {Function} cb 接受参数 error 以及无错时的版本数组 []string
*/
fetchMethodsRelease(cb) {
// this.releaseUrl=https://api.github.com/repos/kuaifan/eeui-template/releases
request.get(this.methodsUrl, function (err, res, body) {
if (err) {
cb && cb(err);
return;
}
if (res.statusCode !== 200) {
cb && cb(`获取信息失败 - ${res.statusCode}: ${res.body}`);
return;
}
console.log('fetchMethodsRelease 获取方法集列表===', res);
let methods = JSON.parse(body).map(function (e) {
return e;
});
cb && cb(null, methods);
});
}
/**
* 拉取方法集
* @param {*} cb
*/
fetchMothods(info, cb) {
console.log('fetchMothods====', info);
let loadText = `正在下载方法集包...`;
let loading = ora(loadText).start();
let rundir = path.resolve(process.cwd());
console.log('rundir=====', rundir);
let srcDir = rundir + '/src/utils';
let savePath = srcDir;
console.log(savePath);
this._downloadAndUnzipMthods(
info,
savePath,
(err) => {
console.log('targetPath cb:' + savePath);
console.log('err:' + err);
loading.stop();
if (err) {
cb && cb(err);
return;
}
cb(null, savePath);
},
(res) => {
loading.text = loadText + `(${res.progress}, ${res.speed})`;
},
);
}
_downloadAndUnzipMthods(info, savePath, cb, progressCall) {
let TMP_DOWNLOAD_PATH = tmp.tmpNameSync({ dir: require('os').tmpdir() }) + '.zip';;
let rundir = path.resolve(process.cwd());
console.log("rundir====",rundir);
// console.log(TMP_DOWNLOAD_PATH)
let file = fs.createWriteStream(TMP_DOWNLOAD_PATH);
file.on('close', () => {
// console.log(TMP_DOWNLOAD_PATH+"||||"+this.CACHE_METHODS_PATH)
decompress(TMP_DOWNLOAD_PATH, path.resolve(rundir + '/src/utils'), {
map: (file) => {
if (file.data.length == 0) {
file.type = 'directory';
}
return file;
},
})
.then(() => {
console.log('===1');
console.log(savePath + '/' + info['tag_name']);
fs.copySync(path.resolve(savePath + '/' + info['tag_name']), path.resolve(rundir + '/src/utils'));
fs.remove(path.resolve(savePath + '/' + info['tag_name'])); // 删除解压后的文件夹
console.log('===3');
cb && cb();
})
.catch((err) => {
cb && cb(`下载版本失败: ${err}`);
});
}).on('error', (err) => {
cb && cb(err);
});
//
let receivedBytes = 0;
let totalBytes = 0;
let speedBytes = 0;
let speedPer = '0B/S';
let speedInt = setInterval(() => {
speedPer = utils.renderSize(Math.max(0, receivedBytes - speedBytes)) + '/S';
speedBytes = receivedBytes;
}, 1000);
request
.get(info['zipball_url'])
.on('error', function (err) {
cb && cb(`下载版本错误: ${err}`);
})
.on('response', function (res) {
if (res.statusCode !== 200) {
cb && cb('Get zipUrl return a non-200 response.');
}
totalBytes = parseInt(res.headers['content-length'], 10);
if (isNaN(totalBytes)) totalBytes = 0;
})
.on('data', (chunk) => {
receivedBytes += chunk.length;
let progress = '0%';
if (totalBytes > 0) {
progress = parseFloat(Math.max(0, (receivedBytes / totalBytes) * 100).toFixed(2)) + '%';
} else {
progress = utils.renderSize(receivedBytes);
}
progressCall &&
progressCall({
received: receivedBytes,
total: totalBytes,
speed: speedPer,
progress: progress,
});
})
.on('end', function () {
clearInterval(speedInt);
})
.pipe(file);
}
/**
* 拉取方法集
* @param {*} cb
*/
_fetchMothods(info, cb) {
// console.log('fetchMothods====', info);
let loadText = `正在下载...`;
let loading = ora(loadText).start();
let savePath = path.resolve(process.cwd());
savePath=savePath+'/'+info['name']
// console.log(savePath,'savePath402');
this._downloadAndUnzipMthodsNew(
info,
savePath,
(err) => {
// console.log('targetPath cb:' + savePath);
// console.log('err:' + err);
loading.stop();
if (err) {
cb && cb(err,savePath);
return;
}
cb('',savePath);
},
(res) => {
loading.text = loadText + `(${res.progress}, ${res.speed})`;
},
);
}
_downloadAndUnzipMthodsNew(info, savePath, cb, progressCall) {
let TMP_DOWNLOAD_PATH = tmp.tmpNameSync({ dir: require('os').tmpdir() }) + '.zip';;
let rundir = path.resolve(process.cwd());
// console.log("rundir====", rundir);
// console.log('TMP_DOWNLOAD_PATH',TMP_DOWNLOAD_PATH)
fs.readFile(rundir+'/src' , function(err, data) {
if(err){
fs.mkdir(rundir+'/src')
}
// })
let file = fs.createWriteStream(TMP_DOWNLOAD_PATH);
file.on('close', () => {
// console.log(TMP_DOWNLOAD_PATH+"||||"+this.CACHE_METHODS_PATH)
decompress(TMP_DOWNLOAD_PATH, path.resolve(rundir+'/src'), {
map: (file) => {
if (file.data.length == 0) {
file.type = 'directory';
}
return file;
},
})
.then(() => {
// console.log('===11');
// console.log(savePath ,'savePath-222');
fs.copySync(path.resolve(rundir+'/src'),path.resolve(savePath));
fs.remove(path.resolve(rundir+'/src')); // 删除解压后的文件夹
// console.log('===33');
cb && cb();
})
.catch((err) => {
cb && cb(`下载版本失败: ${err}`);
});
}).on('error', (err) => {
cb && cb(err);
});
//
let receivedBytes = 0;
let totalBytes = 0;
let speedBytes = 0;
let speedPer = '0B/S';
let speedInt = setInterval(() => {
speedPer = utils.renderSize(Math.max(0, receivedBytes - speedBytes)) + '/S';
speedBytes = receivedBytes;
}, 1000);
request
.get(info['zipball_url'])
.on('error', function (err) {
cb && cb(`下载版本错误: ${err}`);
})
.on('response', function (res) {
// console.log('res',res.statusCode)
if (res.statusCode !== 200) {
cb && cb('Get zipUrl return a non-200 response.');
}
totalBytes = parseInt(res.headers['content-length'], 10);
if (isNaN(totalBytes)) totalBytes = 0;
})
.on('data', (chunk) => {
receivedBytes += chunk.length;
let progress = '0%';
if (totalBytes > 0) {
progress = parseFloat(Math.max(0, (receivedBytes / totalBytes) * 100).toFixed(2)) + '%';
} else {
progress = utils.renderSize(receivedBytes);
}
progressCall &&
progressCall({
received: receivedBytes,
total: totalBytes,
speed: speedPer,
progress: progress,
});
})
.on('end', function () {
clearInterval(speedInt);
})
.pipe(file);
})
}
}
module.exports = TemplateRelease;