diff --git a/CliClient/app/command-sync.js b/CliClient/app/command-sync.js index be0b5943f0..9744518c90 100644 --- a/CliClient/app/command-sync.js +++ b/CliClient/app/command-sync.js @@ -4,12 +4,15 @@ import { _ } from 'lib/locale.js'; import { Setting } from 'lib/models/setting.js'; import { BaseItem } from 'lib/models/base-item.js'; import { vorpalUtils } from './vorpal-utils.js'; +const locker = require('proper-lockfile'); +const fs = require('fs-extra'); class Command extends BaseCommand { constructor() { super(); this.syncTarget_ = null; + this.releaseLockFn_ = null; } usage() { @@ -27,36 +30,80 @@ class Command extends BaseCommand { ]; } + static lockFile(filePath) { + return new Promise((resolve, reject) => { + locker.lock(filePath, (error, release) => { + if (error) { + reject(error); + return; + } + + resolve(release); + }); + }); + } + + static isLocked(filePath) { + return new Promise((resolve, reject) => { + locker.check(filePath, (error, isLocked) => { + if (error) { + reject(error); + return; + } + + resolve(isLocked); + }); + }); + } + async action(args) { - this.syncTarget_ = Setting.value('sync.target'); - if (args.options.target) this.syncTarget_ = args.options.target; + this.releaseLockFn_ = null; - let sync = await app().synchronizer(this.syncTarget_); + const lockFilePath = Setting.value('tempDir') + '/synclock'; + if (!await fs.pathExists(lockFilePath)) await fs.writeFile(lockFilePath, 'synclock'); - let options = { - onProgress: (report) => { - let lines = sync.reportToLines(report); - if (lines.length) vorpalUtils.redraw(lines.join(' ')); - }, - onMessage: (msg) => { - vorpalUtils.redrawDone(); - this.log(msg); - }, - randomFailures: args.options['random-failures'] === true, - }; + if (await Command.isLocked(lockFilePath)) throw new Error(_('Synchronisation is already in progress.')); - this.log(_('Synchronization target: %s', this.syncTarget_)); + this.releaseLockFn_ = await Command.lockFile(lockFilePath); - if (!sync) throw new Error(_('Cannot initialize synchronizer.')); + try { + this.syncTarget_ = Setting.value('sync.target'); + if (args.options.target) this.syncTarget_ = args.options.target; - this.log(_('Starting synchronization...')); + let sync = await app().synchronizer(this.syncTarget_); - await sync.start(options); - vorpalUtils.redrawDone(); + let options = { + onProgress: (report) => { + let lines = sync.reportToLines(report); + if (lines.length) vorpalUtils.redraw(lines.join(' ')); + }, + onMessage: (msg) => { + vorpalUtils.redrawDone(); + this.log(msg); + }, + randomFailures: args.options['random-failures'] === true, + }; - await app().refreshCurrentFolder(); + this.log(_('Synchronization target: %s', this.syncTarget_)); - this.log(_('Done.')); + if (!sync) throw new Error(_('Cannot initialize synchronizer.')); + + this.log(_('Starting synchronization...')); + + await sync.start(options); + vorpalUtils.redrawDone(); + + await app().refreshCurrentFolder(); + + this.log(_('Done.')); + } catch (error) { + this.releaseLockFn_(); + this.releaseLockFn_ = null; + throw error; + } + + this.releaseLockFn_(); + this.releaseLockFn_ = null; } async cancel() { diff --git a/CliClient/package.json b/CliClient/package.json index ea07e4391c..37da1c583f 100644 --- a/CliClient/package.json +++ b/CliClient/package.json @@ -25,6 +25,7 @@ "moment-timezone": "^0.5.13", "node-fetch": "^1.7.1", "promise": "^7.1.1", + "proper-lockfile": "^2.0.1", "query-string": "4.3.4", "sax": "^1.2.2", "server-destroy": "^1.0.1", diff --git a/CliClient/yarn.lock b/CliClient/yarn.lock index dbacefdf98..ace8406f71 100644 --- a/CliClient/yarn.lock +++ b/CliClient/yarn.lock @@ -1711,6 +1711,13 @@ promise@^7.1.1: dependencies: asap "~2.0.3" +proper-lockfile@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-2.0.1.tgz#159fb06193d32003f4b3691dd2ec1a634aa80d1d" + dependencies: + graceful-fs "^4.1.2" + retry "^0.10.0" + pty.js@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/pty.js/-/pty.js-0.3.1.tgz#81f5bed332d6e5e7ab685688d1ba0373410d51b5" @@ -1875,6 +1882,10 @@ restore-cursor@^1.0.1: exit-hook "^1.0.0" onetime "^1.0.0" +retry@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" + rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d"