diff --git a/README.md b/README.md index 9fdceae..e966688 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,6 @@ This project follows the git-contributor [spec](https://github.com/xudafeng/git- ## Docs -- [CLI Usage](//github.com/macacajs/reliable-cli) - [Development](./docker/reliable-web#development) ## License diff --git a/README.zh-CN.md b/README.zh-CN.md index dcaef16..569e9cf 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -18,5 +18,4 @@ ## 文档 -- [命令行客户端](//github.com/macacajs/reliable-cli) - [开发 Reliable](./docker/reliable-web#development) diff --git a/cli/.eslintignore b/cli/.eslintignore new file mode 100644 index 0000000..d69058a --- /dev/null +++ b/cli/.eslintignore @@ -0,0 +1,5 @@ +**/.* +**/node_modules +**/test +coverage/ +**/docs/ diff --git a/cli/.eslintrc.js b/cli/.eslintrc.js new file mode 100644 index 0000000..d28bfe5 --- /dev/null +++ b/cli/.eslintrc.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = { + extends: 'eslint-config-antife', + rules: { + semi: [2, 'always'], + }, +}; diff --git a/cli/.gitignore b/cli/.gitignore new file mode 100644 index 0000000..77f3bf6 --- /dev/null +++ b/cli/.gitignore @@ -0,0 +1,40 @@ +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store +._* +node_modules +coverage/ +.nyc_output +*.gz +*.sw* +*.un~ +package-lock.json +.vscode diff --git a/cli/README.md b/cli/README.md new file mode 100644 index 0000000..fcc8c1e --- /dev/null +++ b/cli/README.md @@ -0,0 +1,26 @@ +# reliable-cli + +[![NPM version][npm-image]][npm-url] +[![CI][CI-image]][CI-url] +[![Test coverage][codecov-image]][codecov-url] +[![node version][node-image]][node-url] +[![npm download][download-image]][download-url] + +[npm-image]: https://img.shields.io/npm/v/reliable-cli.svg +[npm-url]: https://npmjs.org/package/reliable-cli +[CI-image]: https://github.com/macacajs/reliable-cli/actions/workflows/ci.yml/badge.svg +[CI-url]: https://github.com/macacajs/reliable-cli/actions/workflows/ci.yml +[codecov-image]: https://img.shields.io/codecov/c/github/macacajs/reliable-cli.svg +[codecov-url]: https://codecov.io/gh/macacajs/reliable-cli +[node-image]: https://img.shields.io/badge/node.js-%3E=_8-green.svg +[node-url]: http://nodejs.org/download/ +[download-image]: https://img.shields.io/npm/dm/reliable-cli.svg +[download-url]: https://npmjs.org/package/reliable-cli + +> command-line interface for Reliable + +[Reliable Offlice Site](//macacajs.github.io/reliable) + +## License + +The MIT License (MIT) diff --git a/cli/bin/reliable-cli-make.js b/cli/bin/reliable-cli-make.js new file mode 100755 index 0000000..978a801 --- /dev/null +++ b/cli/bin/reliable-cli-make.js @@ -0,0 +1,67 @@ +#!/usr/bin/env node + +'use strict'; + +const _ = require('../lib/helper'); +const childProcess = require('child_process'); + +const platformType = _.getPlatformType(); + +let cmd = ''; +const args = []; + +if (platformType === 'ios') { + console.log('executing make process for ios'); + console.log(process.argv); + + // args should be; [0]node, [1]js-file, [2]make-action....[3]configuration args + if (process.argv.length < 3) { + console.error('invalid arguments'); + process.exit(0); + } + + // check make action: 'build', 'release', 'test', 'pack' etc + if (process.argv[2].indexOf('=') > 0) { + console.error('invalid action'); + console.log(process.argv[2]); + process.exit(0); + } + + cmd = process.argv[2]; + args.push(cmd); + + // formalize extra args + if (process.argv.length > 3) { + args.push(`ARGS="--${process.argv.slice(3).join(' --')}"`); + } + + console.log(`make ${args}`); +} else if (platformType === 'android') { + +} else { + +} + +// execute cmd +const buildProcess = childProcess.spawn('make', args, { + stdio: [ + process.stdin, + process.stdout, + 2, + 'ipc', + ], +}); + +buildProcess.on('close', code => { + process.exit('process exited with code ' + code); +}); + +buildProcess.on('exit', code => { + process.exit(code); +}); + +buildProcess.on('message', e => { + if (e.signal === 'kill') { + buildProcess.kill(); + } +}); diff --git a/cli/bin/reliable-cli-report.js b/cli/bin/reliable-cli-report.js new file mode 100755 index 0000000..0635cea --- /dev/null +++ b/cli/bin/reliable-cli-report.js @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +const ReportCommand = require('../lib/report-command'); + +new ReportCommand().run(); diff --git a/cli/bin/reliable-cli.js b/cli/bin/reliable-cli.js new file mode 100755 index 0000000..c689618 --- /dev/null +++ b/cli/bin/reliable-cli.js @@ -0,0 +1,100 @@ +#!/usr/bin/env node + +'use strict'; + +const { + EOL, +} = require('os'); +const _ = require('xutil'); +const path = require('path'); +const { + spawn, +} = require('child_process'); +const program = require('commander'); + +const pkg = require('../package'); + +const buildCmds = ['build', 'release', 'pack', 'lint', 'doc', 'coverage', 'setup', 'test']; +const chalk = _.chalk; + +program + .option('-v, --versions', 'show version and exit') + .option('--verbose', 'show more debugging information') + .usage(' [options] [arguments]') + .helpInformation = function () { + return [ + '', + ' ' + chalk.white(pkg.description), + '', + ' Usage:', + '', + ' ' + this._name + ' ' + this.usage(), + '', + ' Commands:', + '', + ' report report to Reliable server', + '', + ' Options:', + '', + '' + this.optionHelp().replace(/^/gm, ' '), + '', + '', + ].join(EOL); + }; + +program.parse(process.argv); + +if (program.versions) { + console.info('%s %s%s', EOL, pkg.version, EOL); + process.exit(0); +} + +let cmd = program.args[0]; + +if (!cmd) { + return program.help(); +} + +// for cmd which belongs to development process, all navigate to reliable-cli-make.js +if (buildCmds.includes(cmd)) { + program.rawArgs.splice(3, 0, cmd); + cmd = 'make'; +} + +const file = path.join(__dirname, `${pkg.name}-${cmd}.js`); + +if (!_.isExistedFile(file)) { + console.log('%s command `%s` not found', EOL, chalk.yellow(cmd)); + program.help(); + return; +} + +const args = program.rawArgs.slice(3); +args.unshift(file); + +const bootstrap = spawn('node', args, { + stdio: [ + process.stdin, + process.stdout, + 2, + 'ipc', + ], +}); + +bootstrap.on('close', code => { + process.exit('process exited with code ' + code); +}); + +bootstrap.on('exit', code => { + process.exit(code); +}); + +bootstrap.on('message', e => { + switch (e.signal) { + case 'kill': + bootstrap.kill(); + break; + default : + break; + } +}); diff --git a/cli/index.js b/cli/index.js new file mode 100644 index 0000000..ed652e8 --- /dev/null +++ b/cli/index.js @@ -0,0 +1,4 @@ +'use strict'; + +exports.helper = require('./lib/helper'); +exports.ReportCommand = require('./lib/report-command'); diff --git a/cli/jsdoc.json b/cli/jsdoc.json new file mode 100644 index 0000000..4d6aa12 --- /dev/null +++ b/cli/jsdoc.json @@ -0,0 +1,17 @@ +{ + "source": { + "include": [ + "lib", + "README.md" + ], + "includePattern": ".js$" + }, + "opts": { + "destination": "./docs/", + "template": "node_modules/minami" + }, + "templates": { + "cleverLinks": false, + "monospaceLinks": false + } +} diff --git a/cli/lib/environment.js b/cli/lib/environment.js new file mode 100644 index 0000000..025ff9f --- /dev/null +++ b/cli/lib/environment.js @@ -0,0 +1,21 @@ +'use strict'; + +const os = require('os'); + +const _ = require('./helper'); + +module.exports = () => { + const env = process.env; + return { + platform: _.getPlatformType(), + ci: { + JOB_NAME: _.normalizeJobName(env.JOB_NAME), + BUILD_NUMBER: env.BUILD_NUMBER, + RUNNER_TYPE: 'JENKINS', // GITLAB_CI + }, + os: { + nodeVersion: process.version, + platform: os.platform(), + }, + }; +}; diff --git a/cli/lib/git-info.js b/cli/lib/git-info.js new file mode 100644 index 0000000..97ea339 --- /dev/null +++ b/cli/lib/git-info.js @@ -0,0 +1,8 @@ +'use strict'; + +const LCL = require('last-commit-log'); + +module.exports = directory => { + const lcl = new LCL(directory); + return lcl.getLastCommitSync(); +}; diff --git a/cli/lib/helper.js b/cli/lib/helper.js new file mode 100644 index 0000000..fb005ac --- /dev/null +++ b/cli/lib/helper.js @@ -0,0 +1,250 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const utils = require('xutil'); +const urllib = require('urllib'); +const iosUtils = require('ios-utils'); +const istanbul = require('macaca-istanbul'); +const g2js = require('gradle-to-js/lib/parser'); + +const iosGen = require('./ios/gen'); + +const _ = utils.merge({}, utils); + +/** + * is path a regular file + * @type Node.js + * @returns {boolean} returns true if the path is regular file, otherwise false + * @method isExistedFile + */ + +/** + * is path a file system directory + * @type Node.js + * @returns {boolean} returns true if the path is a directory, otherwise false + * @method isExistedDir + */ + +/** + * A `rm -rf` util for nodejs + * @see {@link https://github.com/isaacs/rimraf#rimrafsync} + * @method rimraf + */ + +/** + * Node.js implementation of port detector + * @see {@link https://github.com/node-modules/detect-port} + * @method detectPort + */ + +/** + * Portable Unix shell commands for Node.js + * @see {@link https://github.com/shelljs/shelljs} + * @method shelljs + */ + + +/** + * Synchronously create a new directory and any necessary subdirectories at dir with octal permission string opts.mode. If opts is a non-object, it will be treated as the opts.mode. + * @see {@link https://github.com/substack/node-mkdirp#mkdirpsyncdir-opts} + * @method mkdir + */ + +/** + * Parse, validate, manipulate, and display dates and times + * @see {@link https://momentjs.com/} + * @class moment + */ + +/** + * get reliable home path + * @type Node.js + * @returns {String} reliable home path + * @method getReliableHome + */ +_.getReliableHome = () => { + return path.join(process.env.HOME, 'reliable_home'); +}; + +/** + * get static server path + * @type Node.js + * @returns {String} static server path + * @method getStaticServerRoot + */ +_.getStaticServerRoot = () => { + const home = _.getReliableHome(); + return path.join(_.isInDocker() ? '/' : home, 'static'); +}; + + +/** + * get reliable is running in docker + * @type Node.js + * @returns {boolean} returns true, if reliable is running in docker; otherwise false + * @method isInDocker + */ +_.isInDocker = () => { + if (process.env.NOT_IN_DOCKER) { + return false; + } + return _.getPlatformType() !== 'ios'; +}; + +/** + * post data to gateway + * @type Node.js + * @param {object} options request options + * @param {string} options.remote.server remote server host + * @param {object} content the data will to post + * @returns {Promise.} promised http response + * @method postToGW + */ +_.postToGW = async function (options, content) { + const server = process.env.RELIABLE_SERVER_URL || options.remote.server; + return urllib.request(`${server}/api/gw`, { + method: 'POST', + data: content, + contentType: 'json', + dataType: 'json', + }); +}; + +/** + * get platform type + * @type Node.js + * @returns {String} 'ios' | 'android' | 'web' | 'unknown' + * @method getPlatformType + */ +_.getPlatformType = () => { + const { + ANDROID_HOME, + GRADLE_HOME, + RELIABLE_ANDROID, + RELIABLE_IOS, + RELIABLE_WEB, + ELECTRON_MIRROR, + } = process.env; + + if (RELIABLE_IOS) { + return 'ios'; + } else if (RELIABLE_ANDROID) { + return 'android'; + } else if (RELIABLE_WEB) { + return 'web'; + } + + if (ANDROID_HOME || GRADLE_HOME) { + return 'android'; + } else if (ELECTRON_MIRROR) { + return 'web'; + } + + return 'unknown'; +}; + +/** + * get dependence package version by name + * @param {String} name name of package + * @type Node.js + * @returns {String} package version + * @method getDepsPkgVersion + */ +_.getDepsPkgVersion = name => { + let version; + + try { + const pkg = path.join(process.cwd(), 'node_modules', name, 'package.json'); + version = JSON.parse(fs.readFileSync(pkg, 'utf8')).version; + } catch (_) { + } + return version; +}; + +/** + * ios utils namespace + * @namespace {object} iosUtils + */ +_.iosUtils = iosUtils; +/** + * ios file genertors namespace + * @namespace iosGen + * @memberof iosUtils + */ +iosUtils.iosGen = iosGen; + +/** + * generate the ios plist.info file content + * @type Node.js + * @returns {String} plist.info file string + * @method genPlist + * @memberof iosUtils.iosGen + * @param {Object} options + * @param {string} options.url assets url + * @param {string} options.bundleIdentifier metadata bundle-identifier + * @param {string} options.bundleVersion meatadata bundle-version + * @param {string} options.title meatadata bundle-title + */ + +/** + * generate the download HTML file content string + * @type Node.js + * @returns {String} HTML doc string containing a download link + * @method genDownloadHtml + * @memberof iosUtils.iosGen + * @param {Object} options + * @param {string} options.url download url + */ + +/** + * android utils + * @namespace + */ +_.androidUtils = { + /** + * parse gradle config + * @param {String} content gradle config text content + * @type Node.js + * @returns {Object} gradle config js object + */ + parseGradle: content => { + return new Promise((resolve) => { + g2js.parseText(content) + .then(representation => { + resolve(representation); + }); + }); + }, +}; + +/** + * web utils + * @namespace + */ +_.webUtils = { + /** + * add a coverage object to the collector, and summary coverage + * @param {Object} coverageResult coverage object + * @type Node.js + * @returns {Object} the coverage summary for a single coverage object + */ + summarizedCoverage: coverageResult => { + const collector = new istanbul.Collector(); + collector.add(coverageResult); + return istanbul + .utils + .summarizeCoverage(collector.getFinalCoverage()); + }, +}; + +/** + * normalize job name, replace / to _ + * @type Node.js + * @param {String} jobName the job name contains / + * @returns {String} normal job name, replace / to _ + * @method normalizeJobName + */ +_.normalizeJobName = (jobName = '') => jobName.replace(/\//g, '_'); + +module.exports = _; diff --git a/cli/lib/ios/download.template.html b/cli/lib/ios/download.template.html new file mode 100644 index 0000000..1e9eb67 --- /dev/null +++ b/cli/lib/ios/download.template.html @@ -0,0 +1,24 @@ + + + + + + + + + +

click to download

+
+

+ built time: <#= time #> +

+

+ url: <#= plistUrl #> +

+ + diff --git a/cli/lib/ios/gen.js b/cli/lib/ios/gen.js new file mode 100644 index 0000000..1171247 --- /dev/null +++ b/cli/lib/ios/gen.js @@ -0,0 +1,28 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const moment = require('moment'); + +const Render = require('microtemplate').render; + +exports.genDownloadHtml = (options) => { + const template = options.customTemplatePath || path.join(__dirname, 'download.template.html'); + const content = fs.readFileSync(template, 'utf8'); + options.time = moment().format('YYYY-MM-DD HH:mm:ss'); + const output = Render(content, options, { + tagOpen: '<#', + tagClose: '#>', + }); + return output; +}; + +exports.genPlist = (options) => { + const template = path.join(__dirname, 'plist.template'); + const content = fs.readFileSync(template, 'utf8'); + const output = Render(content, options, { + tagOpen: '<#', + tagClose: '#>', + }); + return output; +}; diff --git a/cli/lib/ios/plist.template b/cli/lib/ios/plist.template new file mode 100644 index 0000000..a72b6ce --- /dev/null +++ b/cli/lib/ios/plist.template @@ -0,0 +1,31 @@ + + + + + items + + + assets + + + kind + software-package + url + <#= url #> + + + metadata + + bundle-identifier + <#= bundleIdentifier #> + bundle-version + <#= bundleVersion #> + kind + software + title + <#= title #> + + + + + diff --git a/cli/lib/migrate.js b/cli/lib/migrate.js new file mode 100644 index 0000000..bf4e73a --- /dev/null +++ b/cli/lib/migrate.js @@ -0,0 +1,25 @@ +'use strict'; + +const path = require('path'); + +const _ = require('./helper'); + +const { + shelljs, + mkdir, +} = _; + +const { + JOB_NAME, + BUILD_NUMBER, +} = process.env; + +exports.migrateToDockerStaticSync = (relativePath, ruleType = 'jenkins') => { + const distDir = `${_.getStaticServerRoot()}/${ruleType}/${_.normalizeJobName(JOB_NAME)}/${BUILD_NUMBER}`; + mkdir(distDir); + const originPath = path.resolve(relativePath); + const distPath = path.join(distDir, relativePath); + mkdir(path.dirname(distPath)); + console.log(`file copy: ${originPath} >> ${distPath}`); + shelljs.cp(originPath, distPath); +}; diff --git a/cli/lib/move-files.js b/cli/lib/move-files.js new file mode 100644 index 0000000..c8a4e5f --- /dev/null +++ b/cli/lib/move-files.js @@ -0,0 +1,14 @@ +'use strict'; + +const globby = require('globby'); + +const { + migrateToDockerStaticSync, +} = require('./migrate'); + +module.exports = async function (options) { + const patterns = options.files; + const paths = await globby(patterns); + paths.map(i => migrateToDockerStaticSync(i)); + return paths; +}; diff --git a/cli/lib/package-info.js b/cli/lib/package-info.js new file mode 100644 index 0000000..8e48eb2 --- /dev/null +++ b/cli/lib/package-info.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = options => { + const { + packages, + } = options; + return packages; +}; diff --git a/cli/lib/report-command.js b/cli/lib/report-command.js new file mode 100644 index 0000000..6c9c564 --- /dev/null +++ b/cli/lib/report-command.js @@ -0,0 +1,152 @@ +'use strict'; + +const path = require('path'); +const { EOL } = require('os'); +const program = require('commander'); + +const _ = require('./helper'); +const getEnv = require('./environment'); +const getCommitInfo = require('./git-info'); +const getPackages = require('./package-info'); +const postFileToStatic = require('./move-files'); + +const { chalk, postToGW } = _; + +const DEFAULT_OPTIONS = { + directory: program.directory || process.cwd(), + remote: { + server: null, + }, + files: [], + packages: [], + testInfo: {}, + extraInfo: {}, +}; + +class ReportCommand { + constructor (options = {}) { + this.options = Object.assign({}, DEFAULT_OPTIONS, options); + + this.context = { + environment: {}, + gitCommitInfo: {}, + files: [], + packages: [], + testInfo: {}, + extraInfo: {}, + }; + + this._initCommand(); + } + + _initCommand () { + this.program = program + .option('--verbose', 'show more debugging information') + .option('-d, --directory ', 'Set project directory') + .option('-c, --config ', 'set configuration file') + .option('-o, --optionstr ', 'set options string'); + this.addCommand(); + this.program.parse(process.argv); + } + + addCommand () {} + + async run () { + await this._run(); + } + + async _run () { + await this.parseOptions(); + this.context = Object.assign({}, this.context, { + environment: await this.getEnvironment(), + gitCommitInfo: await this.getGitCommitInfo(), + testInfo: await this.getTestInfo(), + extraInfo: await this.getExtraInfo(), + files: await this.getFiles(), + packages: await this.getPackages(), + args: await this.getArgs(), + }); + + try { + const response = await this.pushToWebhook(); + await this.handleWebhookResponse(response); + } catch (e) { + console.log(chalk.red('[FAILED] Upload ci result.')); + console.error('request:', JSON.stringify(this.context, null, 2)); + console.error('error:', e); + process.exit(1); + } + } + + async parseOptions () { + if (program.config) { + const configFile = path.resolve(program.config); + + if (_.isExistedFile(configFile)) { + console.log(`${EOL}configuration file: ${chalk.cyan(configFile)}`); + const mod = require(configFile); + if (typeof mod === 'function') { + this.options = await mod(this.options); + } else { + this.options = Object.assign(this.options, mod); + } + } + } + + if (program.optionstr) { + try { + this.options = Object.assign(this.options, + JSON.parse(program.optionstr)); + } catch (e) { + console.log(e); + } + } + } + + async getGitCommitInfo () { + return await getCommitInfo(this.options.directory); + } + + async getFiles () { + return await postFileToStatic(this.options); + } + + async getPackages () { + return await getPackages(this.options); + } + + async getTestInfo () { + return this.options.testInfo; + } + + async getExtraInfo () { + return this.options.extraInfo; + } + + async getArgs () { + return this.options.args || {}; + } + + async getEnvironment () { + const defaultEnv = await getEnv(); + return Object.assign(defaultEnv, this.options.environment); + } + + async pushToWebhook () { + return await postToGW(this.options, this.context); + } + + async handleWebhookResponse (response) { + if (response.status === 200 && response.data.success) { + console.log(chalk.green('[DONE] Upload ci result.')); + console.log('response data: \n', JSON.stringify(response.data, null, 2)); + } else { + console.log(chalk.red('[FAILED] Upload ci result.')); + console.log('response:', response); + console.log('request:', JSON.stringify(this.context, null, 2)); + process.exit(1); + } + } +} + +module.exports = ReportCommand; diff --git a/cli/package.json b/cli/package.json new file mode 100644 index 0000000..eb04fe1 --- /dev/null +++ b/cli/package.json @@ -0,0 +1,67 @@ +{ + "name": "reliable-cli", + "version": "1.1.5", + "description": "command-line interface for Reliable", + "keywords": [ + "reliable", + "macaca", + "cd", + "ci", + "cli" + ], + "bin": { + "reliable": "./bin/reliable-cli.js" + }, + "main": "index.js", + "files": [ + "bin/**/*.js", + "lib/**/*.js", + "lib/**/*.template", + "lib/**/*.template.html" + ], + "repository": { + "type": "git", + "url": "https://github.com/macacajs/reliable.git" + }, + "dependencies": { + "commander": "^2.15.1", + "globby": "^8.0.1", + "gradle-to-js": "^1.1.0", + "ios-utils": "^1.0.7", + "last-commit-log": "3", + "macaca-istanbul": "1", + "microtemplate": "1", + "moment": "^2.29.1", + "urllib": "^2.28.1", + "xutil": "1" + }, + "devDependencies": { + "cheerio": "^1.0.0-rc.2", + "eslint": "^4.19.1", + "eslint-config-antife": "^1.0.2", + "git-contributor": "1", + "husky": "^1.3.1", + "intelli-espower-loader": "^1.0.1", + "jsdoc": "^3.5.5", + "minami": "^1.1.1", + "mocha": "^5.2.0", + "nyc": "^12.0.2", + "power-assert": "^1.6.1", + "sinon": "^6.0.0" + }, + "scripts": { + "ci": "npm run lint && npm run test", + "doc": "npm run clean && jsdoc -c ./jsdoc.json", + "clean": "rm -rf ./docs", + "test": "nyc --reporter=lcov --reporter=text mocha", + "test-debug": "nyc --reporter=lcov --reporter=text mocha; open coverage/lcov-report/index.html", + "lint": "eslint . --fix", + "contributor": "git-contributor" + }, + "husky": { + "hooks": { + "pre-commit": "npm run lint" + } + }, + "license": "MIT" +} diff --git a/cli/test/lib/environment.test.js b/cli/test/lib/environment.test.js new file mode 100644 index 0000000..7eea620 --- /dev/null +++ b/cli/test/lib/environment.test.js @@ -0,0 +1,32 @@ +'use strict'; + +const assert = require('power-assert'); + +const enviroment = require('../../lib/environment'); + +describe('lib/enviroment', () => { + let env; + + before(() => { + env = Object.assign({}, process.env); + }); + + afterEach(() => { + process.env = env; + }); + + it('should get env', () => { + process.env.JOB_NAME = 'task_force'; + process.env.BUILD_NUMBER = '141'; + + const envData = enviroment(); + console.log(envData.ci); + + assert(envData.ci.JOB_NAME === 'task_force'); + assert(envData.ci.BUILD_NUMBER === '141'); + assert(envData.ci.RUNNER_TYPE === 'JENKINS'); + assert(envData.platform); + assert(envData.os.nodeVersion); + assert(envData.os.platform); + }); +}); diff --git a/cli/test/lib/git-info.test.js b/cli/test/lib/git-info.test.js new file mode 100644 index 0000000..0abf586 --- /dev/null +++ b/cli/test/lib/git-info.test.js @@ -0,0 +1,26 @@ +'use strict'; + +const assert = require('power-assert'); + +const getGitInfo = require('../../lib/git-info'); + +describe('lib/git-info', () => { + it('should get git info', () => { + const data = getGitInfo(); + assert(data.gitRemote.endsWith('macacajs/reliable-cli.git')); + assert(data.gitUrl.endsWith('://github.com/macacajs/reliable-cli')); + assert(data.shortHash); + assert(data.hash); + assert(data.subject); + assert(data.sanitizedSubject); + assert(typeof data.body === 'string'); + assert(data.committer.date.match(/\d+/)); + assert(data.committer.relativeDate); + assert(data.committer.name); + assert(data.committer.email); + assert(data.author.date.match(/\d+/)); + assert(data.author.relativeDate); + assert(data.author.name); + assert(data.author.email); + }); +}); diff --git a/cli/test/lib/helper.test.js b/cli/test/lib/helper.test.js new file mode 100644 index 0000000..38d268a --- /dev/null +++ b/cli/test/lib/helper.test.js @@ -0,0 +1,84 @@ +'use strict'; + +const sinon = require('sinon'); +const urllib = require('urllib'); +const assert = require('power-assert'); + +const helper = require('../../lib/helper'); + +describe('lib/helper', () => { + it('should get reliable home', () => { + const res = helper.getReliableHome(); + assert(res.endsWith('/reliable_home')); + }); + + it('should get static server root', () => { + const _env = process.env; + + process.env.HOME = '/home/docker'; + process.env.NOT_IN_DOCKER = true; + let res = helper.getStaticServerRoot(); + assert(res === '/home/docker/reliable_home/static'); + + delete process.env.NOT_IN_DOCKER; + process.env.RELIABLE_WEB = true; + res = helper.getStaticServerRoot(); + assert(res === '/static'); + + delete process.env.NOT_IN_DOCKER; + delete process.env.RELIABLE_WEB; + process.env.RELIABLE_IOS = true; + res = helper.getStaticServerRoot(); + assert(res === '/home/docker/reliable_home/static'); + + process.env = _env; + }); + + it('should post data to server', () => { + const stub = sinon.stub(urllib, 'request').callsFake(function (...args) { + assert.deepStrictEqual(args, [ + 'http://example/api/gw', + { + method: 'POST', + data: 'new message', + contentType: 'json', + dataType: 'json', + }, + ]); + stub.restore(); + return Promise.resolve({}); + }); + return helper.postToGW({ + remote: { + server: 'http://example', + }, + }, 'new message'); + }); + + it('should get platform type', () => { + const _env = process.env; + + process.env.RELIABLE_IOS = true; + assert(helper.getPlatformType() === 'ios'); + delete process.env.RELIABLE_IOS; + + process.env.RELIABLE_ANDROID = true; + assert(helper.getPlatformType() === 'android'); + delete process.env.RELIABLE_ANDROID; + + process.env.RELIABLE_WEB = true; + assert(helper.getPlatformType() === 'web'); + delete process.env.RELIABLE_WEB; + process.env = _env; + }); + + it('should get package version', () => { + assert(helper.getDepsPkgVersion('last-commit-log').match(/^\d+.\d+\.\d+$/)); + assert(helper.getDepsPkgVersion('no-deps-last-commit-log') === undefined); + }); + + it('should normalize jobName', () => { + assert(helper.normalizeJobName('group/project') === 'group_project'); + assert(helper.normalizeJobName('project') === 'project'); + }); +}); diff --git a/cli/test/lib/ios/gen.test.js b/cli/test/lib/ios/gen.test.js new file mode 100644 index 0000000..dca0877 --- /dev/null +++ b/cli/test/lib/ios/gen.test.js @@ -0,0 +1,30 @@ +'use strict'; + +const assert = require('power-assert'); + +const gen = require('../../../lib/ios/gen'); +const cheerio = require('cheerio'); + +describe('lib/ios/gen', () => { + it('should gen download html', () => { + const html = gen.genDownloadHtml({ + plistUrl: 'http://soap-gaz', + }); + const $ = cheerio.load(html); + assert($('a').attr('href') === 'itms-services://?action=download-manifest&url=http://soap-gaz'); + assert($('a > h1').html() === 'click to download'); + }); + + it('should gen plist', () => { + const html = gen.genPlist({ + url: 'http://soap-gaz', + bundleIdentifier: 'm14', + bundleVersion: 'aug', + title: 'mw', + }); + assert(html.includes('http://soap-gaz')); + assert(html.includes('m14')); + assert(html.includes('aug')); + assert(html.includes('mw')); + }); +}); diff --git a/cli/test/mocha.opts b/cli/test/mocha.opts new file mode 100644 index 0000000..4a52320 --- /dev/null +++ b/cli/test/mocha.opts @@ -0,0 +1 @@ +--recursive diff --git a/cli/test/reliable-cli.test.js b/cli/test/reliable-cli.test.js new file mode 100644 index 0000000..dd3aa8f --- /dev/null +++ b/cli/test/reliable-cli.test.js @@ -0,0 +1,11 @@ +'use strict'; + +const assert = require('power-assert'); + +const reliableCli = require('..'); + +describe('test', () => { + it('should be ok', () => { + assert.ok(reliableCli); + }); +});