Merge pull request #2 from tparnell8/refactor

Refactor
This commit is contained in:
Tommy Parnell
2016-03-12 23:19:51 -05:00
15 changed files with 374 additions and 132 deletions

4
.gitignore vendored
View File

@@ -1 +1,5 @@
node_modules
coverage
wixToolset.zip
output/
test-tmp/

View File

@@ -1,13 +1,54 @@
//require('babel-core/register');
var gulp = require('gulp');
var unzip = require('gulp-unzip');
var request = require('request');
var fs = require('fs');
var mocha = require('gulp-mocha');
var istanbul = require('gulp-istanbul');
var isparta = require('isparta')
var tap = require('gulp-tap');
var coveralls = require('gulp-coveralls');
var babel = require('gulp-babel');
gulp.task('download', function () {
return request('http://wixtoolset.org/downloads/v3.11.0.129/wix311-binaries.zip').pipe(fs.createWriteStream('wixToolset.zip'));
});
gulp.task('getwix',['download'], function(){
gulp.task('getwix',['download', 'prepublish'], function(){
return gulp.src("wixToolset.zip")
.pipe(unzip())
.pipe(gulp.dest('./lib/wixFiles'));
.pipe(gulp.dest('./lib/wixFiles'))
.pipe(gulp.dest('./test-tmp/wixFiles'));
});
gulp.task('pre-test', function () {
return gulp.src('src/**/*.js')
// Covering files
.pipe(istanbul({Instrumenter: isparta.Instrumenter, includeUntested: true}), {read: false})
// Force `require` to return covered files
.pipe(gulp.dest('test-tmp/'))
.pipe(istanbul.hookRequire());
});
gulp.task('test', ['pre-test', 'getwix'], function () {
return gulp.src(['test/**/*.js'])
.pipe(mocha())
// Creating the reports after tests ran
.pipe(istanbul.writeReports())
// Enforce a coverage of at least 90%
.pipe(istanbul.enforceThresholds({ thresholds: { lines: 70 } }));
});
//todo use babel
gulp.task('prepublish', function(){
gulp.src('src/**/*.js')
.pipe(babel({
presets: ['es2015']
}))
.pipe(gulp.dest('lib'));
});
gulp.task('coveralls', ['test'], function(){
gulp.src('coverage/**/lcov.info')
.pipe(coveralls())
});

View File

@@ -80,6 +80,12 @@ Default: `undefined`
Sets the BUILD_VERSION environment variable to version before calling heat, candle, and light
#### suppressValidation
Type: `bool`
Default: `false`
If true this will supress ICE validation checks during the linking process.
## License

View File

@@ -1,31 +1,40 @@
{
"name": "hydrocarbon",
"description": "making windows installers great again",
"version": "0.1.2",
"version": "0.3.0",
"main": "index.js",
"author": "tparnell8",
"repository": "tparnell8/HydroCarbon",
"repository": "http://github.com/tparnell8/Hydrocarbon",
"license": "MIT",
"scripts": {
"compile": "babel src --out-dir lib",
"coveralls": "cat ./coverage/lcov.info | coveralls",
"prepublish": "npm run compile",
"test": "babel-node ./node_modules/.bin/isparta cover _mocha"
"prepublish": "./node_modules/.bin/gulp getwix",
"test": "./node_modules/.bin/gulp test"
},
"dependencies": {
"underscore": "^1.8.3"
"dependencies": {
"child-process-promise": "^1.1.0",
"lodash": "^4.6.1",
"q": "^1.4.1"
},
"devDependencies": {
"babel-cli": "*",
"babel-core": "^6.6.5",
"babel-preset-es2015": "^6.6.0",
"babel-preset-es2015-node4": "*",
"coveralls": "*",
"chai": "*",
"isparta": "*",
"mocha": "*",
"sinon": "*",
"coveralls": "*",
"gulp": "^3.9.1",
"gulp-babel": "^6.1.2",
"gulp-coveralls": "^0.1.4",
"gulp-istanbul": "^0.10.3",
"gulp-mocha": "^2.2.0",
"gulp-tap": "^0.1.3",
"gulp-unzip": "^0.1.3",
"request": "^2.69.0"
"isparta": "^4.0.0",
"jscover": "^1.0.0",
"mocha": "*",
"mocha-lcov-reporter": "^1.2.0",
"request": "^2.69.0",
"sinon": "*"
}
}

50
src/CommandBuilder.js Normal file
View File

@@ -0,0 +1,50 @@
'use strict';
var fs = require('fs'),
_ = require('lodash'),
path = require('path');
var calculateCommands = function(options){
var commands = {
heatPath: options.heatPath || path.normalize(__dirname + "/wixFiles/heat.exe"),
lightPath: options.lightPath || path.normalize(__dirname + "/wixFiles/light.exe"),
candlePath: options.candlePath || path.normalize(__dirname + "/wixFiles/candle.exe")
}
if(options.version){
process.env.BUILD_VERSION = version;
}
if(options.heatCommands && _.isArray(options.heatCommands)){
commands.heatCommands = options.heatCommands
}
else{
if(options.heatFiles && _.isArray(options.heatFiles) && options.heatFiles.length > 0 ){
commands.heatCommands = _.map(options.heatFiles, (file)=>`@${path.normalize(file)}`) //heat commands can be empty as heat is a harvester and thus optional
}
}
if(options.candleCommands && _.isArray(options.candleCommands)){
commands.candleCommands = options.candleCommands
}
else{
if(!options.candleFiles || !_.isArray(options.candleFiles) || options.candleFiles.length < 1 ){
throw "light files are required if light commands are not specified";
}
commands.candleCommands = _.map(options.candleFiles, (file)=>`@${path.normalize(file)}`)
}
if(options.lightCommands && _.isArray(options.lightCommands)){
commands.lightCommands = options.lightCommands
}
else{
if(!options.lightFiles || !_.isArray(options.lightFiles) || options.lightFiles.length < 1 ){
throw "light files are required if light commands are not specified";
}
commands.lightCommands = _.map(options.lightFiles, (file)=>`@${path.normalize(file)}`)
}
if(options.suppressValidation){
commands.lightCommands.unshift('-sval');
}
return commands;
};
module.exports = calculateCommands;

View File

@@ -3,128 +3,43 @@
/* jshint -W097 */
'use strict';
var fs = require('fs'),
_ = require('underscore'),
_ = require('lodash'),
path = require('path'),
child_process = require('child_process');
commandBuilder = require('./CommandBuilder.js'),
spawn = require('child-process-promise').spawn,
Q = require('q'),
processConsole = require('./processConsole.js');
var processResults = function (stdout, stderr) {
if(stdout && _.isArray(stdout)){
_.chain(stdout)
.filter((item)=>item && item.length > 0)
.each((item)=>console.log(item))
.value();
}
else if(stdout && _.isString(stdout) && stdout.length > 0){
console.log(stdout);
}
if(stderr && _.isString(stderr) && stderr.length > 0){
console.log(stderr);
}
var processError = function(err, cb){
if(cb && _.isFunction(cb) && err){
cb(err)
}
else if(err){
throw err.message;
}
}
};
var main = function (options, callback) {
var heatFiles = options.heatFiles;
var candleFiles = options.candleFiles;
var lightFiles = options.lightFiles;
var heatCommands = options.heatCommands || null;
var candleCommands = options.candleCommands || null;
var lightCommands = options.lightCommands || null;
var heatPath = options.heatPath || __dirname + "/wixFiles/heat.exe";
var lightPath = options.lightPath || __dirname + "/wixFiles/light.exe";
var candlePath = options.candlePath || __dirname + "/wixFiles/candle.exe";
var cb = callback;
var version = options.version;
if(version){
process.env.BUILD_VERSION = version;
}
if(!heatCommands){
if(!heatFiles || !_.isArray(heatFiles) || heatFiles.length < 1 ){
throw "heat files are required if no commands are passed";
}
checkFiles(heatFiles);
}
var commands = commandBuilder(options);
var heat = null;
if(commands.heatCommands){
console.log(commands.heatPath, commands.heatCommands);
heat = spawn(commands.heatPath, commands.heatCommands)
.progress(processConsole);
}
heat = heat || Q.Promise();
if(!candleCommands){
if(!candleFiles || !_.isArray(candleFiles) || candleFiles.length < 1 ){
throw "candle files are required";
return Q.all([heat])
.then(()=>spawn(commands.candlePath, commands.candleCommands), (err)=>processError(err, callback))
.progress(processConsole)
.then(()=>spawn(commands.lightPath, commands.lightCommands), (err)=>processError(err, callback))
.progress(processConsole)
.fail((err)=>processError(err, callback))
.then(()=>{
if(callback){
callback();
}
checkFiles(candleCommands);
}
if(!lightCommands){
if(!lightFiles || !_.isArray(lightFiles) || lightFiles.length < 1 ){
throw "light files are required";
}
checkFiles(lightFiles);
}
return child_process.execFile(path.normalize(heatPath), heatCommands? heatCommands: _.map(heatFiles, (file)=>`@${path.normalize(file)}`), (err, stdout, stderr)=>{
processResults(stdout, stderr);
if(err){
if(cb){
return cb(err);
}else{
throw err;
}
}
return child_process.execFile(path.normalize(candlePath), candleCommands? candleCommands: _.map(candleFiles, (file)=>`@${path.normalize(file)}`), (err, stdout, stderr)=>{
processResults(stdout, stderr);
if(err){
if(cb){
return cb(err);
}else{
throw err;
}
}
return child_process.execFile(path.normalize(lightPath), lightCommands? lightCommands: _.map(lightFiles, (file)=>`@${path.normalize(file)}`), (err, stdout, stderr)=>{
processResults(stdout, stderr);
if(err){
if(cb){
return cb(err);
}else{
throw err;
}
}
if(cb){
return cb();
}
});
});
});
};
var checkFiles = function(files){
_.each(files, (file)=>{
if(!checkFile(file)){
throw "error finding file" + file;
}
});
};
var checkFile = function (file) {
if (!file || file.length < 1) {
return false;
}
try {
fs.accessSync(path.normalize(file), fs.R_OK); //will error if doesnt exist
//todo async?
return true;
} catch (error) {
return false;
}
};
module.exports = main;

11
src/processConsole.js Normal file
View File

@@ -0,0 +1,11 @@
'use strict'
module.exports = function processConsole(childProcess) {
if(childProcess && childProcess.stdout){
childProcess.stdout.on('data', (data)=>console.log(data.toString()));
}
if(childProcess && childProcess.stderr){
childProcess.stderr.on('data', (data)=>console.log(data.toString()));
}
};

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension">
<Product Id="*"
Name="Tommy"
Language="1033"
Version="1.0.0"
Manufacturer="Tommy"
UpgradeCode="{aba1c34e-39c6-47cf-b50a-cae4e77f8204}">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<Media Id="1" Cabinet="product.cab" EmbedCab="yes" />
<!-- Allows our MSI to automatically uninstall any previously installed versions (makes it play nicer with puppet) -->
<Property Id="PREVIOUSVERSIONSINSTALLED" Secure="yes" />
<!-- Upgrade id has to match our upgrade code -->
<Upgrade Id="{aba1c34e-39c6-47cf-b50a-cae4e77f8204}">
<UpgradeVersion
Minimum="0.0.0.1" Maximum="99.0.0.0"
Property="PREVIOUSVERSIONINSTALLED"
IncludeMinimum="yes" IncludeMaximum="no" />
</Upgrade>
<!--
We need to be able to uninstall a newer version from an older version.
The default reinstallmode is "omus", of which the 'o' means "reinstall if missing or older"
The 'd' means "reinstall if different". This ensures that, at the individual component level, rollbacks work correctly.
See http://msdn.microsoft.com/en-us/library/windows/desktop/aa371182(v=vs.85).aspx
-->
<Property Id="REINSTALLMODE" Value="dmus" />
<Feature Id="TommysFiles" Title="TommysFiles">
<ComponentGroupRef Id="files" />
<ComponentRef Id='main' />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="Test" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<Component Directory="INSTALLFOLDER" Id="main" Guid="1330738a-9a86-41ed-a0de-68c55318612a"></Component>
</Fragment>
<Fragment>
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallInitialize" />
</InstallExecuteSequence>
</Fragment>
</Wix>

View File

@@ -0,0 +1,5 @@
-dSourceDir=test
-nologo
-out output\test\installers\
test\integration\Product.wsx
output\test\installers\testfiles.gen.wxs

View File

@@ -0,0 +1,7 @@
dir test
-nologo
-cg files
-gg -scom -sreg -sfrag -srd
-dr INSTALLFOLDER
-out output\test\installers\testfiles.gen.wxs
-var var.SourceDir

View File

@@ -0,0 +1,14 @@
var assert = require('chai').assert;
var expect = require('chai').expect;
var hydroexec = require('../../test-tmp/exec');
describe('wix', function(){
it('creates an msi', function(cb){
this.timeout(1000000);
return hydroexec({
heatFiles: ['test/integration/heat.rsp'],
candleFiles: ['test/integration/candle.rsp'],
lightFiles: ['test/integration/light.rsp']
}, cb);
})
});

View File

@@ -0,0 +1,8 @@
-dSourceDir=test
output\test\installers\testfiles.gen.wixobj
output\test\installers\Product.wixobj
-out output\test\installers\Web.msi
-nologo
-sw1076
-sice:ICE80
-sice:ICE18

View File

@@ -1,3 +1,3 @@
./test/unit/**/*.test.js
./test/unit/*.test.js
--reporter spec
--recursive

View File

@@ -0,0 +1,119 @@
var assert = require('chai').assert;
var expect = require('chai').expect;
describe('CommandBuilderWorks', function(){
var commandBuilder = require('../../test-tmp/CommandBuilder');
it('Should not throw when files are passed in', function(){
var testObject = {
heatFiles: ['tst'],
candleFiles: ['awesome'],
lightFiles: ['filesss']
};
commandBuilder(testObject)
});
it('Should not throw when commands are passed in', function(){
var testObject = {
heatCommands: ['tst'],
candleCommands: ['awesome'],
lightCommands: ['filesss']
};
commandBuilder(testObject)
});
it('Should Throw if missing light files', function(){
var testObject = {
heatFiles: ['tst'],
candleFiles: ['awesome']
};
assert.throws(()=>commandBuilder(testObject));
});
it('Should Throw if missing candle files', function(){
var testObject = {
heatFiles: ['tst'],
lightFiles: ['awesome']
};
assert.throws(()=>commandBuilder(testObject));
});
it('Should not Throw if missing heat files or commands', function(){
var testObject = {
lightFiles: ['awesome'],
candleFiles: ['awesome']
};
assert.doesNotThrow(()=>commandBuilder(testObject));
});
it('should run as expected with files', function(){
var testObject = {
lightFiles: ['lightfile'],
candleFiles: ['candlefile'],
heatFiles: ['heatfile']
};
var result = commandBuilder(testObject);
expect(result.heatCommands).to.eql(['@heatfile']);
expect(result.lightCommands).to.eql(['@lightfile']);
expect(result.candleCommands).to.eql(['@candlefile']);
});
it('should run as expected with commands', function(){
var testObject = {
lightCommands: ['lightfile'],
candleCommands: ['candlefile'],
heatCommands: ['heatfile']
};
var result = commandBuilder(testObject);
expect(result.heatCommands).to.eql(['heatfile']);
expect(result.lightCommands).to.eql(['lightfile']);
expect(result.candleCommands).to.eql(['candlefile']);
});
it('should use alternate heat location', function(){
var testObject = {
lightCommands: ['lightfile'],
candleCommands: ['candlefile'],
heatCommands: ['heatfile'],
heatPath: "../awesome"
};
var result = commandBuilder(testObject);
expect(result.heatPath).to.eql("../awesome");
});
it('should use alternate candle location', function(){
var testObject = {
lightCommands: ['lightfile'],
candleCommands: ['candlefile'],
heatCommands: ['heatfile'],
candlePath: "../awesome"
};
var result = commandBuilder(testObject);
expect(result.candlePath).to.eql("../awesome");
});
it('should use alternate light location', function(){
var testObject = {
lightCommands: ['lightfile'],
candleCommands: ['candlefile'],
heatCommands: ['heatfile'],
lightPath: "../awesome"
};
var result = commandBuilder(testObject);
expect(result.lightPath).to.eql("../awesome");
});
it('should suppress validations', function(){
var testObject = {
lightCommands: ['lightfile'],
candleCommands: ['candlefile'],
heatCommands: ['heatfile'],
suppressValidation: true
};
var result = commandBuilder(testObject);
expect(result.lightCommands).to.eql(['-sval', 'lightfile']);
});
});

View File

@@ -1 +0,0 @@
import { assert } from 'chai';