|
|
|
import readdir from 'readdir-enhanced';
|
|
|
|
import fs from 'fs';
|
|
|
|
import crypto from 'crypto';
|
|
|
|
import multimatch from 'multimatch';
|
|
|
|
import { cfg } from './repo-config.js';
|
|
|
|
|
|
|
|
function applyExcludeFilter(stat, excludeFilters) {
|
|
|
|
// match exclude, return immediatley
|
|
|
|
if (excludeFilters.length > 0) {
|
|
|
|
// todo this could be a performance problem...
|
|
|
|
const pathWithFolderSlash = stat.path + (stat.isDirectory() ? '/' : '');
|
|
|
|
const excludeMatch = multimatch(pathWithFolderSlash, excludeFilters, { matchBase: true, dot: true });
|
|
|
|
|
|
|
|
if (excludeMatch.length > 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param algorithm : "md5" | "sha1" | "sha256" | "sha512"
|
|
|
|
*/
|
|
|
|
async function fileHash(filename, algorithm) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
// Algorithm depends on availability of OpenSSL on platform
|
|
|
|
// Another algorithms: "sha1", "md5", "sha256", "sha512" ...
|
|
|
|
let shasum = crypto.createHash(algorithm);
|
|
|
|
try {
|
|
|
|
let s = fs.createReadStream(filename);
|
|
|
|
s.on('data', function (data) {
|
|
|
|
shasum.update(data);
|
|
|
|
});
|
|
|
|
|
|
|
|
s.on('error', function (error) {
|
|
|
|
reject(error);
|
|
|
|
});
|
|
|
|
|
|
|
|
// making digest
|
|
|
|
s.on('end', function () {
|
|
|
|
const hash = shasum.digest('hex');
|
|
|
|
return resolve(hash);
|
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
return reject('calc fail');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function createLocalState(localFiles, args) {
|
|
|
|
console.log(`Creating local state at ${args['local-dir']}${args['state-name']}`);
|
|
|
|
fs.writeFileSync(`${args['local-dir']}${args['state-name']}`, JSON.stringify(localFiles, undefined, 4), { encoding: 'utf8' });
|
|
|
|
console.log('Local state created');
|
|
|
|
}
|
|
|
|
async function getLocalFiles(args) {
|
|
|
|
const files = await readdir.async(args['local-dir'], { deep: true, stats: true, sep: '/', filter: (stat) => applyExcludeFilter(stat, args.exclude) });
|
|
|
|
let records = [];
|
|
|
|
console.log(files.length, 'items');
|
|
|
|
for (let stat of files) {
|
|
|
|
if (stat.isDirectory()) {
|
|
|
|
records.push({
|
|
|
|
type: 'folder',
|
|
|
|
name: stat.path,
|
|
|
|
size: undefined,
|
|
|
|
});
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stat.isFile()) {
|
|
|
|
records.push({
|
|
|
|
type: 'file',
|
|
|
|
name: stat.path,
|
|
|
|
size: stat.size,
|
|
|
|
hash: await fileHash(args['local-dir'] + stat.path, 'sha256'),
|
|
|
|
});
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stat.isSymbolicLink()) {
|
|
|
|
console.warn('This script is currently unable to handle symbolic links - please add a feature request if you need this');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
description: "Don't delete. Generate local",
|
|
|
|
version: '1.0.0',
|
|
|
|
generatedTime: new Date().getTime(),
|
|
|
|
generatedTimeString: new Date().toLocaleString(),
|
|
|
|
data: records,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
const [, , repoArg] = process.argv;
|
|
|
|
const options = Object.keys(cfg).join(' | ');
|
|
|
|
if (!repoArg) {
|
|
|
|
throw `error: Pls input repo: ${options}`;
|
|
|
|
}
|
|
|
|
const filename = `.ftp-deploy-sync-state.json`;
|
|
|
|
const _args = {
|
|
|
|
'local-dir': `./../${cfg[repoArg].folder}/`,
|
|
|
|
'state-name': filename,
|
|
|
|
'exclude': [filename, 'ftp-state/**'].concat(cfg[repoArg].exclude),
|
|
|
|
};
|
|
|
|
const localFiles = await getLocalFiles(_args);
|
|
|
|
createLocalState(localFiles, _args);
|
|
|
|
} catch (error) {
|
|
|
|
console.log(error);
|
|
|
|
}
|