infopack-cli
Version:
Command line tool for managing infopacks
361 lines (340 loc) • 15.1 kB
JavaScript
var program = require('commander');
const fs = require('fs');
var path = require('path');
var inquirer = require('inquirer');
const { Table } = require("console-table-printer");
const _ = require('lodash');
const slugify = require('slugify');
var infopackOutputValidator = require('infopack-output-validator');
const { exit } = require('process');
var package = require('../package');
const unzipper = require('../lib/unzipper')
const { readJson } = require('../lib/json')
const MODULE_PATH = path.join(__dirname, '..');
const TEMPLATES = ['basic'];
const urls = {
basic: 'https://gitlab.com/infopack/infopack-example-basic/-/archive/master/infopack-example-basic-master.zip'
};
program
.version(package.version)
.description(package.description);
program
.command('init [template]')
.alias('i')
.description('Creates a new information package from a template. The package will be created in the current directory with the name you choose.')
.action((template, program) => {
template = template || TEMPLATES[0];
if(TEMPLATES.indexOf(template) < 0) {
console.log(`Invalid template name provided, valid values are: ${TEMPLATES.join(', ')}`);
return;
}
console.log('Initializing...')
Promise
.resolve()
.then(() => {
return inquirer
.prompt([{
type: "input",
name: "title",
message: "Package title (This will be the human readable name)",
validate: function(answer, hash) {
return (answer.length > 0);
}
}, {
type: "input",
name: "name",
default: answers => slugify(answers.title, { lower: true }),
message: "Package name (slug with alphanumeric characters)",
validate: function(answer, hash) {
if(answer.length < 1) return 'Name too short (min 1 char)'
if(answer.length > 140) return 'Name too long (max 140 chars)'
if(!/^[a-z0-9-_]*$/.test(answer)) return 'Please use small characters separated by - och _'
return true
}
}, {
type: "input",
name: "namespace",
message: "Namespace slug (slug with alphanumeric characters)",
validate: function(answer, hash) {
if(answer.length < 1) return 'Namespace name too short (min 1 char)'
if(answer.length > 140) return 'Namespace name too long (max 140 chars)'
if(!/^[a-z0-9-_]*$/.test(answer)) return 'Please use small characters separated by - och _'
return true
}
}, {
type: "input",
name: "description",
message: "Package description (describe what this will contain)",
validate: function(answer, hash) {
if(answer.length < 1) 'Description is too short (min 1 char)'
return true
}
}, {
type: "input",
name: "version",
default: "0.0.1",
message: "Package version",
validate: function(answer, hash) {
return (answer.length > 0);
}
}, {
type: "input",
name: "author",
message: "Package author",
validate: function(answer, hash) {
return (answer.length > 0);
}
}, {
type: "input",
name: "license",
message: "Package license",
validate: function(answer, hash) {
return (answer.length > 0);
}
}])
})
.then(packageData => {
console.log()
console.log('Please confirm the following:')
console.log(`A new folder will be created in: ${path.resolve(packageData.name)}`)
console.log(`Please make sure that you are in control over the namespace: ${packageData.namespace}`)
console.log('')
return Promise.all([
packageData,
inquirer
.prompt([{
type: "confirm",
name: "lastChange",
message: "Are we OK?",
}])
])
})
.then(([packageData, { lastChange }]) => {
if(!lastChange) throw new Error('user_aborted')
console.log('=== Fetching template from: ' + urls[template])
console.log('=== Writing')
return Promise.all([
packageData,
unzipper(urls[template], packageData.name)
])
})
.then(([packageData, unzipperResult]) => {
const packagePath = path.resolve(packageData.name, 'package.json')
return Promise.all([
packageData,
unzipperResult,
readJson(packagePath),
packagePath
])
})
.then(([packageData, unzipperResult, packageJson, packagePath]) => {
// prep package.json
packageJson.name = packageData.name;
packageJson.description = packageData.description;
packageJson.version = packageData.version;
packageJson.author = packageData.author;
packageJson.license = packageData.license;
fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2));
// prep index.js
var indexPath = path.resolve(packageData.name, 'index.js')
let indexString = fs.readFileSync(indexPath).toString()
indexString = indexString.replace('<PACKAGE_TITLE>', packageData.title)
indexString = indexString.replace('<PACKAGE_NAMESPACE>', packageData.namespace)
fs.writeFileSync(indexPath, indexString)
return [packageData, unzipperResult]
})
.then(([packageData, unzipperResult]) => {
console.log('')
console.log('Next steps (at the command line):')
console.log('')
console.log(`1. cd ${packageData.name}`)
console.log('2. npm install')
console.log('3. Delete the example data in input/ (rm -rf input/) and add your own')
console.log('4. npm start (to build your package)')
console.log('')
})
.catch(err => {
if (err.isTtyError) {
// Prompt couldn't be rendered in the current environment
} else if (err.message == 'user_aborted') {
console.log('')
console.log('ABORTING')
} else {
// Something else went wrong
console.log(err)
}
});
});
program
.command('create meta-input')
.alias('ci')
.description('Creates a meta input file.')
.action((name, program) => {
Promise
.resolve()
.then(() => {
return inquirer
.prompt([{
type: "list",
name: "file",
message: "Which file?",
choices: fs.readdirSync(path.resolve()).filter(str => '.meta-input.json' !== str.substring(str.length - '.meta-input.json'.length)),
validate: function(answer, hash) {
return (answer.length > 0);
}
}, {
type: "input",
name: "title",
message: "Title?",
validate: function(answer, hash) {
return (answer.length > 0);
}
}, {
type: "input",
name: "description",
message: "Description?",
validate: function(answer, hash) {
return (answer.length > 0);
}
}, {
type: "confirm",
name: "_addLabels",
message: "Want to add Labels?"
}, {
type: "input",
name: "_labels",
message: "Write key value pair(s) (eg. Key 1=Value one, Key2 = Value two)",
when: hash => hash._addLabels,
filter: (answer, hash) => answer.split(',').map(i => i.trim().split('=').map(i => i.trim()))
}, {
type: "confirm",
name: "_addOrigin",
message: "want to add an origin?"
}, {
type: "input",
name: "origin",
message: "Write path to origin (use comma separation for multiple values)",
when: hash => hash._addOrigin,
filter: (answer, hash) => answer.split(',').map(i => i.trim())
}], { $schema: 'http://schemas.infopack.io/infopack-meta-input.2.schema.json' })
})
.then(answers => {
const { _labels, _addLabels, _addOrigin, file, ...metaInput } = answers
if(_labels && _labels.length > 0) {
metaInput.labels = {}
_labels.forEach(l => metaInput.labels[l[0]] = l[1])
}
fs.writeFileSync(path.resolve(file + '.meta-input.json'), JSON.stringify(metaInput, null, 2));
})
.catch((error) => {
if (error.isTtyError) {
// Prompt couldn't be rendered in the current environment
} else {
// Something else went wrong
console.log(error)
}
});
});
program
.command('test')
.alias('t')
.description('Tests a infopack release')
.arguments('<testSuiteVersion>', 'Manifest version for package to test')
.arguments('<path>', 'Path to infopack release to test')
.option('-s, --suppressErrors', 'If provided test will exit with errorcode = 0')
.option('-l, --generate-logfile', 'creates a log file in current folder')
.action((testSuiteVersion, folderPath, option, program) => {
var validator = new infopackOutputValidator.Validator(folderPath)
validator
.addTestSuite(testSuiteVersion)
.run()
.then(results => {
if(validator.getErrorsCount() > 0) {
const p = new Table({
title: "Error Summary",
columns: [{
name: "test",
alignment: "left",
title: "Test name"
}, {
name: "folder",
alignment: "right",
title: "Folder",
maxLen: 40
}, {
name: "file",
alignment: "right",
title: "File",
maxLen: 30
}, {
name: "message",
alignment: "left",
title: "Error messages"
}, {
name: "numberOfMessages",
alignment: "right",
title: "Messages"
}, {
name: "level",
alignment: "left",
title: "Error level"
}],
});
_.forEach(validator.getErrors(), e => {
let option = {}
if(e.level === 'info') option.color = 'yellow'
if(e.level === 'critical') option.color = 'red'
const foldername = path.dirname(e.path).replace('/', ' / ')
const filename = path.basename(e.path)
p.addRow({
test: e.test,
folder: foldername.length > 40 ? foldername.slice(0,40) + ' ' + foldername.slice(40) : foldername,
file: filename.length > 30 ? filename.slice(0,30) + ' ' + filename.slice(30) : filename,
message: e.message,
numberOfMessages: e.numberOfMessages,
level: e.level
}, option)
})
console.log()
p.printTable()
}
if(!validator.isValid()) {
if(option.suppressErrors === undefined) {
process.exitCode = 1
}
} else {
console.log('')
console.log('')
console.log(' ____________________ ')
console.log(' / \\')
console.log(' | ALL OK! | ')
console.log(' | You are awesome!! | ')
console.log(' \\____________________/')
console.log(' ! ! ')
console.log(' ! ! ')
console.log(' L_ ! ')
console.log(' / _)! ')
console.log(' / /__L ')
console.log(' _____/ (____) ')
console.log(' (____) ')
console.log(' _____ (____) ')
console.log(' \\_(____) ')
console.log(' ! ! ')
console.log(' ! ! ')
console.log(' \\__/ ')
console.log('')
}
if(option.generateLogfile) {
fs.writeFileSync(process.cwd() + '/test-log.json', JSON.stringify(results, null, 2))
}
})
});
program.parse(process.argv);
// show help if no command was provided
if(program.args.length === 0) {
program.help();
}
process.on('exit', (code) => {
console.log('Exited with code: ', code)
})