master
parent
8d246e2c1b
commit
14777853d3
@ -1,122 +0,0 @@
|
||||
import { IDiff, IFileList, Record } from "./types";
|
||||
import fs from "fs";
|
||||
import crypto from "crypto";
|
||||
|
||||
export async function fileHash(filename: string, algorithm: "md5" | "sha1" | "sha256" | "sha512"): Promise<string> {
|
||||
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: any) {
|
||||
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");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export class HashDiff implements IDiff {
|
||||
getDiffs(localFiles: IFileList, serverFiles: IFileList) {
|
||||
const uploadList: Record[] = [];
|
||||
const deleteList: Record[] = [];
|
||||
const replaceList: Record[] = [];
|
||||
|
||||
const sameList: Record[] = [];
|
||||
|
||||
let sizeUpload = 0;
|
||||
let sizeDelete = 0;
|
||||
let sizeReplace = 0;
|
||||
|
||||
// alphabetize each list based off path
|
||||
const localFilesSorted = localFiles.data.sort((first, second) => first.name.localeCompare(second.name));
|
||||
const serverFilesSorted = serverFiles.data.sort((first, second) => first.name.localeCompare(second.name));
|
||||
|
||||
let localPosition = 0;
|
||||
let serverPosition = 0;
|
||||
while (localPosition + serverPosition < localFilesSorted.length + serverFilesSorted.length) {
|
||||
let localFile: Record | undefined = localFilesSorted[localPosition];
|
||||
let serverFile: Record | undefined = serverFilesSorted[serverPosition];
|
||||
|
||||
let fileNameCompare = 0;
|
||||
if (localFile === undefined) {
|
||||
fileNameCompare = 1;
|
||||
}
|
||||
if (serverFile === undefined) {
|
||||
fileNameCompare = -1;
|
||||
}
|
||||
if (localFile !== undefined && serverFile !== undefined) {
|
||||
fileNameCompare = localFile.name.localeCompare(serverFile.name);
|
||||
}
|
||||
|
||||
if (fileNameCompare < 0) {
|
||||
uploadList.push(localFile);
|
||||
sizeUpload += localFile.size ?? 0;
|
||||
localPosition += 1;
|
||||
}
|
||||
else if (fileNameCompare > 0) {
|
||||
deleteList.push(serverFile);
|
||||
sizeDelete += serverFile.size ?? 0;
|
||||
serverPosition += 1;
|
||||
}
|
||||
else if (fileNameCompare === 0) {
|
||||
// paths are a match
|
||||
if (localFile.type === "file" && serverFile.type === "file") {
|
||||
if (localFile.hash === serverFile.hash) {
|
||||
sameList.push(localFile);
|
||||
}
|
||||
else {
|
||||
sizeReplace += localFile.size ?? 0;
|
||||
replaceList.push(localFile);
|
||||
}
|
||||
}
|
||||
|
||||
localPosition += 1;
|
||||
serverPosition += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// optimize modifications
|
||||
let foldersToDelete = deleteList.filter((item) => item.type === "folder");
|
||||
|
||||
// remove files/folders that have a nested parent folder we plan on deleting
|
||||
const optimizedDeleteList = deleteList.filter((itemToDelete) => {
|
||||
const parentFolderIsBeingDeleted = foldersToDelete.find((folder) => {
|
||||
const isSameFile = itemToDelete.name === folder.name;
|
||||
const parentFolderExists = itemToDelete.name.startsWith(folder.name);
|
||||
|
||||
return parentFolderExists && !isSameFile;
|
||||
}) !== undefined;
|
||||
|
||||
if (parentFolderIsBeingDeleted) {
|
||||
// a parent folder is being deleted, no need to delete this one
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return {
|
||||
upload: uploadList,
|
||||
delete: optimizedDeleteList,
|
||||
replace: replaceList,
|
||||
same: sameList,
|
||||
sizeDelete,
|
||||
sizeReplace,
|
||||
sizeUpload
|
||||
};
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
## local file hash
|
||||
|
||||
用于辅助Github actions: FTP Deploy的差异比较
|
||||
用于辅助Github actions: FTP Deploy的差异比较\
|
||||
FTP Deploy: 文件夹深度较深或文件较多时, 上传耗时很久, 因为每个文件计算差异之后打开ftp 连接上传
|
||||
先打包上传后, 生成文件的hash, 用于后续action的差异比较
|
||||
|
@ -1,43 +0,0 @@
|
||||
import readdir from "@jsdevtools/readdir-enhanced";
|
||||
import { Record, IFileList, syncFileDescription, currentSyncFileVersion, IFtpDeployArgumentsWithDefaults } from "./types";
|
||||
import { fileHash } from "./HashDiff";
|
||||
import { applyExcludeFilter } from "./utilities";
|
||||
|
||||
export async function getLocalFiles(args: IFtpDeployArgumentsWithDefaults): Promise<IFileList> {
|
||||
const files = await readdir.async(args["local-dir"], { deep: true, stats: true, sep: "/", filter: (stat) => applyExcludeFilter(stat, args.exclude) });
|
||||
const records: Record[] = [];
|
||||
|
||||
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: syncFileDescription,
|
||||
version: currentSyncFileVersion,
|
||||
generatedTime: new Date().getTime(),
|
||||
data: records
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue