diff --git a/Gruntfile.js b/Gruntfile.js index fa050163..4eb78592 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -28,6 +28,7 @@ module.exports = function(grunt) { '!test/unit/fixtures/syntax-error/**/*.js', '!test/unit/fixtures/project-binary-modules/**/*', '!test/unit/fixtures/project-skip-binary/**/*', + '!test/unit/fixtures/project-ignore-binary/**/*', ] } }, @@ -40,6 +41,7 @@ module.exports = function(grunt) { '!test/unit/fixtures/syntax-error/**/*.js', '!test/unit/fixtures/project-binary-modules/**/*', '!test/unit/fixtures/project-skip-binary/**/*', + '!test/unit/fixtures/project-ignore-binary/**/*', ], options: { config: '.jscsrc' diff --git a/lib/tessel/deploy.js b/lib/tessel/deploy.js index 322bfd36..3238a441 100644 --- a/lib/tessel/deploy.js +++ b/lib/tessel/deploy.js @@ -12,6 +12,7 @@ var fs = require('fs-extra'); var fsTemp = require('fs-temp'); var glob = require('glob'); var Ignore = require('fstream-ignore'); +var minimatch = require('minimatch'); var Project = require('t2-project'); var Reader = require('fstream').Reader; var request = require('request'); @@ -366,14 +367,23 @@ actions.glob = { // Ignores empty lines and comments if (pattern && !pattern.match(/^#/)) { - patterns.push(path.relative(target, path.join(dirname, pattern))); + patterns.push({ + isExplicitDir: pattern.endsWith('/'), + pattern: path.relative(target, path.join(dirname, pattern)) + }); } return patterns; }, []); return rules.concat(patterns); - }, []).map(function(rule) { + }, []).reduce(function(rules, entry) { + + var rule = entry.pattern; + + if (!entry.isExplicitDir && !entry.pattern.includes('.')) { + rules.push(entry.pattern); + } if (rule[rule.length - 1] === '/') { rule += '**/*.*'; @@ -383,8 +393,9 @@ actions.glob = { rule += '/**/*.*'; } - return rule; - }); + rules.push(rule); + return rules; + }, []); }, /* Generate a complete list of files, from cwd down, that match all @@ -442,6 +453,7 @@ actions.resolveBinaryModules = function(opts) { var patterns = ['node_modules/**/*.node', 'node_modules/**/binding.gyp']; var binaries = actions.glob.files(globRoot, patterns).reduce((bins, globPath) => { // Gather information about each found module + var resolved = true; var modulePath = bindings.getRoot(globPath); var packageJson = require(path.join(globRoot, modulePath, 'package.json')); var binName = path.basename(globPath); @@ -513,33 +525,32 @@ actions.resolveBinaryModules = function(opts) { bindingGypJson = JSON.parse(bindingGypData); } catch (error) { // If this module's binding.gyp is missing, broken or otherwise - // unusable, log a message about and move on. There are too - // many failure modes here, no way to recover. - - logMissingBinaryModuleWarning(packageJson.name); - return bins; + // unusable. There are too many failure modes here, no way to recover. + resolved = false; } } - if (bindingGypJson && Array.isArray(bindingGypJson.targets)) { - // Anything that can't be covered by this will have to be - // dealt with as we encounter them and as they are reported. - binName = bindingGypJson.targets[0].target_name + '.node'; + if (resolved) { + if (bindingGypJson && Array.isArray(bindingGypJson.targets)) { + // Anything that can't be covered by this will have to be + // dealt with as we encounter them and as they are reported. + binName = bindingGypJson.targets[0].target_name + '.node'; - // Assume the most likely scenario first: - // - // build/Release - // build/Debug - // - buildPath = path.join('build', buildType); - - // Unless there is a specific `binary.module_path`. - // Checking this only matters when the glob patterns - // didn't turn up a .node binary. - if (packageJson.binary && packageJson.binary.module_path) { - buildPath = path.normalize(packageJson.binary.module_path); - if (buildPath[0] === '.') { - buildPath = buildPath.slice(1); + // Assume the most likely scenario first: + // + // build/Release + // build/Debug + // + buildPath = path.join('build', buildType); + + // Unless there is a specific `binary.module_path`. + // Checking this only matters when the glob patterns + // didn't turn up a .node binary. + if (packageJson.binary && packageJson.binary.module_path) { + buildPath = path.normalize(packageJson.binary.module_path); + if (buildPath[0] === '.') { + buildPath = buildPath.slice(1); + } } } } @@ -550,8 +561,10 @@ actions.resolveBinaryModules = function(opts) { buildPath: buildPath, buildType: buildType, globPath: globPath, + ignored: false, name: packageJson.name, modulePath: modulePath, + resolved: resolved, version: packageJson.version, }); @@ -607,10 +620,7 @@ actions.resolveBinaryModules = function(opts) { gunzip.on('error', function(error) { if (error.code === 'Z_DATA_ERROR') { - logMissingBinaryModuleWarning(details.name); - - // Remove from tracked binary modules - binaryModulesUsed.delete(details.name); + details.resolved = false; // Remove extraction directory fs.removeSync(details.extractPath); @@ -637,7 +647,7 @@ actions.resolveBinaryModules = function(opts) { }); // Resolve this operation once all binary module requests have resolved - return Promise.all(requests).then(resolve).catch(reject); + return Promise.all(requests).then(() => resolve(binaryModulesUsed)).catch(reject); }); }; @@ -660,19 +670,25 @@ actions.injectBinaryModules = function(globRoot, tempBundlePath) { return new Promise((resolve) => { // For every binary module in use... binaryModulesUsed.forEach(details => { - // console.log(details); - var buildDir = details.buildPath.replace(path.dirname(details.buildPath), ''); - var sourceBinary = path.join(details.extractPath, buildDir, details.binName); - var tempTargetModulePath = path.join(tempBundlePath, details.modulePath); - var tempTargetBinary = path.join(tempTargetModulePath, details.buildPath, details.binName); - - fs.copySync(sourceBinary, tempTargetBinary); - - // Also ensure that package.json was copied. - fs.copySync( - path.join(globRoot, details.modulePath, 'package.json'), - path.join(tempTargetModulePath, 'package.json') - ); + if (details.resolved) { + var buildDir = details.buildPath.replace(path.dirname(details.buildPath), ''); + var sourceBinary = path.join(details.extractPath, buildDir, details.binName); + var tempTargetModulePath = path.join(tempBundlePath, details.modulePath); + var tempTargetBinary = path.join(tempTargetModulePath, details.buildPath, details.binName); + + fs.copySync(sourceBinary, tempTargetBinary); + + // Also ensure that package.json was copied. + fs.copySync( + path.join(globRoot, details.modulePath, 'package.json'), + path.join(tempTargetModulePath, 'package.json') + ); + } else { + if (!details.ignored) { + logMissingBinaryModuleWarning(details.name); + } + // In the future we may allow users to log the ignored modules here + } }); // All binary modules have been replaced, resolve. @@ -710,6 +726,21 @@ actions.tarBundle = function(opts) { var ignoreRules = actions.glob.rules(target, '.tesselignore').concat(includeNegateRules); var ignoreFiles = actions.glob.files(globRoot, ignoreRules); + var matchOptions = { + matchBase: true, + dot: true + }; + + // Mark binary modules that should be ignored + binaryModulesUsed.forEach(details => { + ignoreRules.forEach(rule => { + if (minimatch(details.modulePath, rule, matchOptions)) { + details.ignored = true; + details.resolved = false; + } + }); + }); + // Both the --slim and --full paths will use a copy of the // project to bundle. This allows us to be destructive // with the files, but without directly tampering with the diff --git a/package.json b/package.json index 13739c3a..f7dfc7bc 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "glob": "^5.0.15", "inquirer": "^0.8.5", "mdns-js": "^0.4.0", + "minimatch": "^3.0.0", "node-rsa": "^0.2.26", "nomnom": "^1.8.1", "npm": "^2.7.1", diff --git a/test/unit/deploy.js b/test/unit/deploy.js index c0be3ace..c0efbbfb 100644 --- a/test/unit/deploy.js +++ b/test/unit/deploy.js @@ -1868,12 +1868,11 @@ exports['deploy.resolveBinaryModules'] = { }); this.exists = sandbox.stub(fs, 'existsSync', () => true); - this.logsWarn = sandbox.stub(logs, 'warn'); deploy.resolveBinaryModules({ target: this.target - }).then(() => { - test.equal(this.logsWarn.called, true); + }).then(binaryModulesUsed => { + test.equal(binaryModulesUsed.get('missing').resolved, false); test.done(); }).catch((error) => test.fail(error)); }, @@ -2027,7 +2026,7 @@ exports['deploy.injectBinaryModules'] = { }, copies: function(test) { - // test.expect(9); + test.expect(17); this.globSync.restore(); @@ -2177,6 +2176,25 @@ exports['deploy.injectBinaryModules'] = { }); }, + doesNotCopyIgnoredBinaries: function(test) { + test.expect(1); + this.target = path.normalize('test/unit/fixtures/project-ignore-binary'); + this.relative.restore(); + this.relative = sandbox.stub(path, 'relative', () => { + return path.join(__dirname, '/../../test/unit/fixtures/project-ignore-binary/'); + }); + + deploy.resolveBinaryModules({ + target: this.target + }).then(() => { + deploy.injectBinaryModules(this.globRoot, fsTemp.mkdirSync()).then(() => { + // Nothing gets copied! + test.equal(this.copySync.callCount, 0); + test.done(); + }); + }); + }, + throwError: function(test) { test.expect(1); diff --git a/test/unit/fixtures/project-ignore-binary/.tesselignore b/test/unit/fixtures/project-ignore-binary/.tesselignore new file mode 100644 index 00000000..d7025695 --- /dev/null +++ b/test/unit/fixtures/project-ignore-binary/.tesselignore @@ -0,0 +1 @@ +release diff --git a/test/unit/fixtures/project-ignore-binary/index.js b/test/unit/fixtures/project-ignore-binary/index.js new file mode 100644 index 00000000..fc2c4ac2 --- /dev/null +++ b/test/unit/fixtures/project-ignore-binary/index.js @@ -0,0 +1,3 @@ +var path = require('path'); +var fs = require('fs'); +var release = require('release'); diff --git a/test/unit/fixtures/project-ignore-binary/node_modules/release/binding.gyp b/test/unit/fixtures/project-ignore-binary/node_modules/release/binding.gyp new file mode 100644 index 00000000..00c77582 --- /dev/null +++ b/test/unit/fixtures/project-ignore-binary/node_modules/release/binding.gyp @@ -0,0 +1,5 @@ +{ + "targets": [{ + "target_name": "release" + }] +} diff --git a/test/unit/fixtures/project-ignore-binary/node_modules/release/build/Release/release.node b/test/unit/fixtures/project-ignore-binary/node_modules/release/build/Release/release.node new file mode 100755 index 00000000..1b5fdc5c Binary files /dev/null and b/test/unit/fixtures/project-ignore-binary/node_modules/release/build/Release/release.node differ diff --git a/test/unit/fixtures/project-ignore-binary/node_modules/release/index.js b/test/unit/fixtures/project-ignore-binary/node_modules/release/index.js new file mode 100644 index 00000000..fd4237cd --- /dev/null +++ b/test/unit/fixtures/project-ignore-binary/node_modules/release/index.js @@ -0,0 +1 @@ +module.exports = require('build/Release/release.node'); diff --git a/test/unit/fixtures/project-ignore-binary/node_modules/release/package.json b/test/unit/fixtures/project-ignore-binary/node_modules/release/package.json new file mode 100644 index 00000000..9557b970 --- /dev/null +++ b/test/unit/fixtures/project-ignore-binary/node_modules/release/package.json @@ -0,0 +1,9 @@ +{ + "name": "release", + "version": "1.1.1", + "description": "build/Release (see tootallnate/bindings)", + "main": "index.js", + "tessel": { + "skipBinary": true + } +} diff --git a/test/unit/fixtures/project-ignore-binary/package.json b/test/unit/fixtures/project-ignore-binary/package.json new file mode 100644 index 00000000..048149d2 --- /dev/null +++ b/test/unit/fixtures/project-ignore-binary/package.json @@ -0,0 +1,9 @@ +{ + "name": "project-ignore-binary", + "version": "0.0.1", + "description": "project-ignore-binary", + "main": "index.js", + "dependencies": { + "release": "1.1.1" + } +}