diff --git a/README.md b/README.md index 3c97ebf..0790256 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,16 @@ # SYNOPSIS -hash all the files recursively from a specified path, appreciate .ignore files. uses `sha1` by default. +recursively hash all the files in a specified path. Sync to ensure +hashing happens in the same order every time. # USAGE +Uses `sha1` by default, use `openssl list-message-digest-algorithms` +for a list of supported algorithms. Appreciates `.ignore` files/globs. ### Use as a lib ```js var hashd = require('hashd') -var hash = hashd('./path/to/files', { algorithm: 'md5', ignore: ['.gitignore', '.npmignore'] }) +var opts = { algorithm: 'md5', ignore: ['.gitignore', '.npmignore'] } +var hash = hashd('./path/to/files', opts) console.log(hash) ``` @@ -15,3 +19,6 @@ console.log(hash) npm install hashd -g hashd ./path/to/files --algorithm sha1 --ignore '.gitignore .npmignore' ``` + +## OPTIONS +CLI options and api options are the same. First argument is always a path. diff --git a/index.js b/index.js index 4672181..5fbef13 100644 --- a/index.js +++ b/index.js @@ -6,15 +6,17 @@ var Minimatch = require("minimatch").Minimatch function hashd(p, opts) { + var opts = opts || {} var shasum = crypto.createHash(opts.algorithm || 'sha1') var rules = [] - if (opts.ignores && !Array.isArray(opts.ignores)) { - opts.ignores = opts.ignores.split(' ') + // todo, parse cmdline files as CSV? + if (opts.files && !Array.isArray(opts.files)) { + opts.files = opts.files.split(' ') } - if (opts.ignores) { - opts.ignores.forEach(function(file) { + if (opts.files) { + opts.files.forEach(function(file) { var set @@ -38,14 +40,20 @@ function hashd(p, opts) { rules = uniq(rules) } - var mmopt = { matchBase: true, dot: true, flipNegate: true } + // todo, parse cmdline patterns as CSV? + if (opts.patterns && !Array.isArray(opts.patterns)) { + opts.patterns = opts.patterns.split(' ') + } + + if (opts.patterns) { + rules = rules.concat(opts.patterns) + rules = uniq(rules) + } + var mmopt = { matchBase: true, dot: true, flipNegate: true } var mm = {} rules.map(function (s) { - if (s[s.length] === '/') { - s = s.slice(0, -1) - } mm[s] = new Minimatch(s, mmopt) }) @@ -56,18 +64,23 @@ function hashd(p, opts) { items.forEach(function(item) { var d = path.join(p, item) + var isDirectory = fs.statSync(d).isDirectory() if (Object.keys(mm).some(function(m) { - if (m.match(item)) { + + if (isDirectory) { + item = item + '/' + } + + if (mm[m].match(item)) { return true } })) { return true } + console.log(d) - var stat = fs.statSync(d) - - if (stat.isDirectory()) { + if (isDirectory) { return read(d) } shasum.update(fs.readFileSync(d, { encoding: 'utf8' })) @@ -75,7 +88,7 @@ function hashd(p, opts) { } read(p) - return shasum.digest('hex') + return shasum.digest(opts.encoding || 'hex') } module.exports = hashd diff --git a/package.json b/package.json index d4e4266..5de74ea 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,36 @@ { "name": "hashd", - "version": "0.0.1", + "version": "1.0.0", "description": "hash all the files recursively from a specified path, appreciate .ignore files. uses `sha1` by default.", "main": "index.js", - "bin": "./bin/hashd", - "author": "", + "bin": { + "hashd": "./bin/hashd" + }, "license": "MIT", + "author": "Paolo Fragomeni (http://twitter.com/hij1nx)", + "repository": { + "type": "git", + "url": "git://github.com/hij1nx/hashd.git" + }, "dependencies": { "optimist": "~0.6.0", "minimatch": "~0.2.12", "uniq": "0.0.2" - } + }, + "bugs": { + "url": "https://github.com/hij1nx/hashd/issues" + }, + "directories": { + "test": "test" + }, + "devDependencies": {}, + "scripts": { + "test": "node ./test/test.js" + }, + "keywords": [ + "hash", + "recursive", + "file", + "tree" + ] } diff --git a/test/fixtures/.ignore-a b/test/fixtures/.ignore-a new file mode 100644 index 0000000..1b643f5 --- /dev/null +++ b/test/fixtures/.ignore-a @@ -0,0 +1 @@ +a/ \ No newline at end of file diff --git a/test/fixtures/.ignore-b b/test/fixtures/.ignore-b new file mode 100644 index 0000000..63d8dbd --- /dev/null +++ b/test/fixtures/.ignore-b @@ -0,0 +1 @@ +b \ No newline at end of file diff --git a/test/fixtures/a/a.txt b/test/fixtures/a/a.txt new file mode 100644 index 0000000..f079749 --- /dev/null +++ b/test/fixtures/a/a.txt @@ -0,0 +1 @@ +test1 \ No newline at end of file diff --git a/test/fixtures/b/b.txt b/test/fixtures/b/b.txt new file mode 100644 index 0000000..d606037 --- /dev/null +++ b/test/fixtures/b/b.txt @@ -0,0 +1 @@ +test2 \ No newline at end of file diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..aa4078a --- /dev/null +++ b/test/test.js @@ -0,0 +1,41 @@ +var assert = require('assert') +var path = require('path') +var hashd = require('../index') + +var basepath = path.join(__dirname, 'fixtures') +console.log('\nhasing %s', basepath) +var basehash = hashd(basepath) + +var pathA = path.join(basepath, 'a') +console.log('\nhasing %s', pathA) +var hashA = hashd(pathA) + +var pathB = path.join(basepath, 'b') +console.log('\nhasing %s', pathB) +var hashB = hashd(pathB) + +assert.notEqual(basehash, hashA) +assert.notEqual(basehash, hashB) +assert.notEqual(hashB, hashA) + +var ignorePattern1 = ["a", ".ignore-a", ".ignore-b"] +console.log('\nhashing %s (patterns: %j)', basepath, ignorePattern1) + +var basehashIgnorePatternA = hashd(basepath, { patterns: ignorePattern1 }) +assert.notEqual(basehash, basehashIgnorePatternA, 'The base hash should be different from the hash that excludes directory `A`.') +assert.equal(basehashIgnorePatternA, hashB, 'The hash excluding directory `A` should be the same as the hash on directory `B`.') + +var ignorePattern2 = ["b", ".ignore-a", ".ignore-b"] +console.log('\nhashing %s (patterns: %j)', basepath, ignorePattern2) + +var basehashIgnorePatternB = hashd(basepath, { patterns: ignorePattern2 }) +assert.notEqual(basehash, basehashIgnorePatternB, 'The base hash should be different from the hash that excludes directory `B`.') +assert.equal(basehashIgnorePatternB, hashA, 'The hash excluding directory `B` should be the same as the hash on directory `A`.') + +var ignoreFiles1 = ['.ignore-a'] +var ignorePattern3 = ['.ignore-b', '.ignore-a'] // use a file's globs but also ignore the file itself +console.log('\nhashing %s (patterns: %j, files: %j)', basepath, ignoreFiles1, ignorePattern3) + +var basehashIgnoreFileA = hashd(basepath, { files: ignoreFiles1, patterns: ignorePattern3 }) +assert.notEqual(basehash, basehashIgnoreFileA, 'The base hash should be different from the hash that excludes directory `A`.') +assert.equal(basehashIgnoreFileA, hashB, 'The hash excluding directory `A` should be the same as the hash on directory `B`.')