This repository has been archived by the owner on Jul 26, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfingerprinter.ts
190 lines (176 loc) · 6.16 KB
/
fingerprinter.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
const FS = require("fs")
const Path = require("path")
const OS = require('os')
const debug = require('debug')('fingerprinter')
const md5File = require("md5-file") // To capture the fingerprint of each file
const fcrypto = require('crypto')
// Processed data
var files: any[] = []
// List of directories to exclude
let EXCLUDE_DIR = ['.git', 'node_modules']
// List of files to exclude
let EXCLUDE_FILE = []
let delta: any[] = []
let priorChanges: any[] = []
/**
* Should the directory be processed?
* @param {string} dirName Relative path name
* @returns {boolean} false if the directory is in the EXCLUDE list; true otherwise
*/
function includeDirectory(dirName: string): boolean {
for (let i = 0; i < EXCLUDE_DIR.length; i++) {
if (dirName.includes(EXCLUDE_DIR[i])) {
return false // Escape as soon as possible
}
}
return true
}
/**
* Read the contents of the selected folder
* @param {string} dir Directory starting point
* @returns Array of files with relative paths
*/
function processDirectory(dir: string): any {
return new Promise<any[]>((resolve, reject) => {
FS.readdirSync(dir).forEach((file: string) => {
const absPath: string = Path.join(dir, file);
if (FS.statSync(absPath).isDirectory()) {
if (includeDirectory(absPath)) {
return processDirectory(absPath)
}
} else {
if (includeDirectory(absPath)) {
const hash: string = md5File.sync(absPath)
files.push({ name: absPath, md5: hash })
}
}
})
resolve(files)
})
}
/**
* Store a snapshot in a standard location (temp folder)
* @param {string} dir Full path of the directory
*/
function takeSnapshot(dir: string): void {
const absPath = Path.resolve(dir)
console.error(`adding ${dir}/${absPath}`)
try {
debug(`processing ${dir}`)
process.chdir(dir)
processDirectory('.')
.then((fileList: string[]) => {
debug(`saving snapshot with ${fileList.length} files`)
saveSnapshot({ path: absPath, processDate: new Date(), files: fileList })
console.log(`Saved snapshot!`)
})
.catch((err: any) => {
console.error(`ERROR: ${err}`)
})
} catch (err) {
console.error(`ERROR: ${err.message}`)
}
}
// Snapshot filename == sha256(dir)
function getSnapshotFilename(dir: string): any {
debug(`getSnapshotFilename(${dir}) called`)
const hash = fcrypto.createHash('sha256')
const hashVal = hash.update(dir).digest('hex')
const filename = OS.tmpdir() + Path.sep + hashVal
debug(`...result: ${filename}, ${hashVal}`)
return ({ filename: filename, hash: hashVal })
}
function saveSnapshot(data: any) {
debug(`saveSnapshot(${data}) called`)
const tmpFileInfo = getSnapshotFilename(data.path)
data.tmpFilename = tmpFileInfo.filename
data.pathHash = tmpFileInfo.hash
debug(`...result: ${tmpFileInfo.filename}, data`)
FS.writeFileSync(tmpFileInfo.filename, JSON.stringify(data))
}
/**
* Has the directory changed since the prior snapshot?
* @param {string} dir
* @returns {boolean} Yes (true) or No (false)
*/
async function isChanged(dir: string, updateSnapshot: boolean = false): Promise<any> {
debug(`isChanged(${dir}, ${updateSnapshot}) called`)
const absPath = Path.resolve(dir)
const snapshotFileInfo = getSnapshotFilename(absPath)
debug(`snapshotFileInfo: `, snapshotFileInfo)
let fingerprint: string = snapshotFileInfo.filename
// Get the old results (i.e. read the local fingerprint file)
if (FS.existsSync(fingerprint)) {
let oldResults: any = JSON.parse(FS.readFileSync(fingerprint, 'utf8'))
debug(`...got the oldResults (fingerprint: ${fingerprint})`)
// Get the current values
debug(`...chdir(${dir})`)
process.chdir(dir) // Change to the processing folder so all relative paths are sane
const fileList: string[] = await processDirectory('.')
debug(`...fileList: ${fileList.length} items long`)
// .then((fileList: string[]) => {
delta = []
// Get the current results
let newResults: any = { path: absPath, processDate: new Date(), files: fileList }
debug(`...newResults: ${absPath}, ${fileList.length} files`)
// Now compare
// TODO: Process the file data by content, rather than position
if (newResults.files.length == oldResults.files.length) {
debug(`......same # of files: ${newResults.files.length}`)
for (let i = 0; i < newResults.files.length; i++) {
if ((newResults.files[i].name != oldResults.files[i].name)) {
delta.push({ new: newResults.files[i].name, old: oldResults.files[i].name })
}
else if ((newResults.files[i].md5 != oldResults.files[i].md5)) {
delta.push(newResults.files[i].name)
}
}
if (updateSnapshot) {
debug(`......saving snapshot`)
saveSnapshot(newResults)
} else {
debug(`......not saving snapshot`)
}
return (delta)
} else {
debug(`......** Different *** # of files: new:${newResults.files.length} vs old:${oldResults.files.length}`)
if (updateSnapshot) {
debug(`......saving snapshot`)
saveSnapshot(newResults)
} else {
debug(`......not saving snapshot`)
}
delta = [{ new: newResults.files, old: oldResults.files }]
}
} else { // Snapshot file doesn't exist
throw new Error('Snapshot file not found. Please take a snapshot first.')
}
}
module.exports = { isChanged, takeSnapshot }
if (require.main === module) {
debug(`in main`)
const action = process.argv.slice(2)[0]
const pathName = process.argv.slice(2)[1]
switch (action) {
case 'add':
takeSnapshot(pathName)
break;
case 'check':
isChanged(pathName, true)
.then((changes: any[]) => {
console.log(`latestChanges: `, changes.length ? changes : 'no changes')
})
.catch((err: any) => {
console.error(err.message)
})
break;
case 'snapshotFilename':
const snapshotFilename = getSnapshotFilename(Path.resolve(pathName))
debug(`snapshotFilename: `, snapshotFilename)
console.log(`snapshot info: `, snapshotFilename)
break;
default:
console.error(`Please specify either 'add' or 'check' - plus a directory name.`)
break;
}
}