From 4074b763dc44f4ab1341c4a7d4acab66356989e9 Mon Sep 17 00:00:00 2001 From: Mikael Lindqvist Date: Thu, 23 Feb 2023 12:03:15 +0800 Subject: [PATCH] added newFilesOnly --- src/ftp-deploy.js | 102 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 4 deletions(-) diff --git a/src/ftp-deploy.js b/src/ftp-deploy.js index 0b23da6..7a06f23 100644 --- a/src/ftp-deploy.js +++ b/src/ftp-deploy.js @@ -10,6 +10,37 @@ var PromiseFtp = require("promise-ftp"); var PromiseSftp = require("ssh2-sftp-client"); const lib = require("./lib"); +// This is a fixed version of the lastMod function to be applied to node-ftp +// It is applied in the connect function, see the comment containing "node-ftp hack fix" +// The fix entails adding 'Z' to the date time before creating the Date object, so +// that the Date object understands that it is being created from a GMT time. +// A pull request has been sent here: https://github.com/icetee/node-ftp/pull/24 +// that fixes the problem upstream. Remove this hack once the PR is merged. +const XRegExp = require('xregexp').XRegExp; +const REX_TIMEVAL = XRegExp.cache('^(?\\d{4})(?\\d{2})(?\\d{2})(?\\d{2})(?\\d{2})(?\\d+)(?:.\\d+)?$') +const fixedLastMod= function(path, cb) { + var self = this; + this._send('MDTM ' + path, function(err, text, code) { + if (code === 502) { + return self.list(path, function(err, list) { + if (err) + return cb(err); + if (list.length === 1) + cb(undefined, list[0].date); + else + cb(new Error('File not found')); + }, true); + } else if (err) + return cb(err); + var val = XRegExp.exec(text, REX_TIMEVAL), ret; + if (!val) + return cb(new Error('Invalid date/time format from server')); + ret = new Date(val.year + '-' + val.month + '-' + val.date + 'T' + val.hour + + ':' + val.minute + ':' + val.second + 'Z'); + cb(undefined, ret); + }); +}; + /* interim structure { '/': ['test-inside-root.txt'], @@ -30,6 +61,39 @@ const FtpDeployer = function () { filename: "", }; + // If the file doesn't exist, this function resolves to undefined. + this.lastMod = function (path) { + if (this.ftp.stat) { + return this.ftp + .stat(path) + .then((stats)=>{ + return stats.modifyTime; + }) + .catch((e)=>{ + if (e.code=="ENOENT") + return Promise.resolve(undefined); + + return Promise.reject(e); + }); + } + + else if (this.ftp.lastMod) { + return this.ftp + .lastMod(path) + .catch((e)=>{ + // Error code 550 means the file doesn't exist. + if (e.code==550) + return Promise.resolve(undefined); + + return Promise.reject(e); + }); + } + + else { + return Promise.reject(new Error("Unable to check modification time.")); + } + } + this.makeAllAndUpload = function (remoteDir, filemap) { let keys = Object.keys(filemap); return Promise.mapSeries(keys, (key) => { @@ -45,6 +109,7 @@ const FtpDeployer = function () { return this.ftp.mkdir(newDirectory, true); } }; + // Creates a remote directory and uploads all of the files in it // Resolves a confirmation message on success this.makeAndUpload = (config, relDir, fnames) => { @@ -56,11 +121,34 @@ const FtpDeployer = function () { let tmp = fs.readFileSync(tmpFileName); this.eventObject["filename"] = upath.join(relDir, fname); - this.emit("uploading", this.eventObject); + let checkModTime=()=>{ + if (!config.newFilesOnly) + return Promise.resolve(true); + + return this.lastMod(upath.join(config.remoteRoot, relDir, fname)) + .then((remoteModDate)=>{ + let tmpStats=fs.statSync(tmpFileName); + if (remoteModDate && remoteModDate>=tmpStats.mtime) { + return false; + } + + return true; + }); + } + + return checkModTime() + .then((shouldUpload)=>{ + if (shouldUpload) { + this.emit("uploading", this.eventObject); + return this.ftp.put(tmp, upath.join(config.remoteRoot, relDir, fname)) + } - return this.ftp - .put(tmp, upath.join(config.remoteRoot, relDir, fname)) - .then(() => { + else { + this.emit("skipping", this.eventObject); + return Promise.resolve(); + } + }) + .then(()=>{ this.eventObject.transferredFileCount++; this.emit("uploaded", this.eventObject); return Promise.resolve("uploaded " + tmpFileName); @@ -93,6 +181,12 @@ const FtpDeployer = function () { this.emit("log", "Connected to: " + config.host); this.emit("log", "Connected: Server message: " + serverMessage); + // node-ftp hack fix: apply the fixed function to the + // underlying node-ftp instance. + if (this.ftp.lastMod) { + this.ftp.rawClient.lastMod = fixedLastMod; + } + // sftp does not provide a connection status // so instead provide one ourself if (config.sftp) {