taskchef 3.0.1 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/.codex-plugin/plugin.json +1 -1
  2. package/BACKLOG.md +15 -0
  3. package/README.md +28 -7
  4. package/SPEC.md +79 -33
  5. package/index.js +18 -0
  6. package/node_modules/graceful-fs/LICENSE +15 -0
  7. package/node_modules/graceful-fs/README.md +143 -0
  8. package/node_modules/graceful-fs/clone.js +23 -0
  9. package/node_modules/graceful-fs/graceful-fs.js +448 -0
  10. package/node_modules/graceful-fs/legacy-streams.js +118 -0
  11. package/node_modules/graceful-fs/package.json +53 -0
  12. package/node_modules/graceful-fs/polyfills.js +355 -0
  13. package/node_modules/proper-lockfile/CHANGELOG.md +108 -0
  14. package/node_modules/proper-lockfile/LICENSE +21 -0
  15. package/node_modules/proper-lockfile/README.md +183 -0
  16. package/node_modules/proper-lockfile/index.js +40 -0
  17. package/node_modules/proper-lockfile/lib/adapter.js +85 -0
  18. package/node_modules/proper-lockfile/lib/lockfile.js +342 -0
  19. package/node_modules/proper-lockfile/lib/mtime-precision.js +55 -0
  20. package/node_modules/proper-lockfile/package.json +71 -0
  21. package/node_modules/retry/.npmignore +3 -0
  22. package/node_modules/retry/.travis.yml +15 -0
  23. package/node_modules/retry/License +21 -0
  24. package/node_modules/retry/Makefile +18 -0
  25. package/node_modules/retry/README.md +227 -0
  26. package/node_modules/retry/equation.gif +0 -0
  27. package/node_modules/retry/example/dns.js +31 -0
  28. package/node_modules/retry/example/stop.js +40 -0
  29. package/node_modules/retry/index.js +1 -0
  30. package/node_modules/retry/lib/retry.js +100 -0
  31. package/node_modules/retry/lib/retry_operation.js +158 -0
  32. package/node_modules/retry/package.json +32 -0
  33. package/node_modules/retry/test/common.js +10 -0
  34. package/node_modules/retry/test/integration/test-forever.js +24 -0
  35. package/node_modules/retry/test/integration/test-retry-operation.js +258 -0
  36. package/node_modules/retry/test/integration/test-retry-wrap.js +101 -0
  37. package/node_modules/retry/test/integration/test-timeouts.js +69 -0
  38. package/node_modules/signal-exit/LICENSE.txt +16 -0
  39. package/node_modules/signal-exit/README.md +39 -0
  40. package/node_modules/signal-exit/index.js +202 -0
  41. package/node_modules/signal-exit/package.json +38 -0
  42. package/node_modules/signal-exit/signals.js +53 -0
  43. package/package.json +5 -2
  44. package/skills/taskchef-bootstrap/SKILL.md +2 -3
  45. package/skills/taskchef-delegate/SKILL.md +75 -11
  46. package/skills/taskchef-report/SKILL.md +20 -10
  47. package/src/cli.js +19 -1
  48. package/src/delegation.js +555 -0
  49. package/src/workspace.js +53 -172
@@ -0,0 +1,355 @@
1
+ var constants = require('constants')
2
+
3
+ var origCwd = process.cwd
4
+ var cwd = null
5
+
6
+ var platform = process.env.GRACEFUL_FS_PLATFORM || process.platform
7
+
8
+ process.cwd = function() {
9
+ if (!cwd)
10
+ cwd = origCwd.call(process)
11
+ return cwd
12
+ }
13
+ try {
14
+ process.cwd()
15
+ } catch (er) {}
16
+
17
+ // This check is needed until node.js 12 is required
18
+ if (typeof process.chdir === 'function') {
19
+ var chdir = process.chdir
20
+ process.chdir = function (d) {
21
+ cwd = null
22
+ chdir.call(process, d)
23
+ }
24
+ if (Object.setPrototypeOf) Object.setPrototypeOf(process.chdir, chdir)
25
+ }
26
+
27
+ module.exports = patch
28
+
29
+ function patch (fs) {
30
+ // (re-)implement some things that are known busted or missing.
31
+
32
+ // lchmod, broken prior to 0.6.2
33
+ // back-port the fix here.
34
+ if (constants.hasOwnProperty('O_SYMLINK') &&
35
+ process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
36
+ patchLchmod(fs)
37
+ }
38
+
39
+ // lutimes implementation, or no-op
40
+ if (!fs.lutimes) {
41
+ patchLutimes(fs)
42
+ }
43
+
44
+ // https://github.com/isaacs/node-graceful-fs/issues/4
45
+ // Chown should not fail on einval or eperm if non-root.
46
+ // It should not fail on enosys ever, as this just indicates
47
+ // that a fs doesn't support the intended operation.
48
+
49
+ fs.chown = chownFix(fs.chown)
50
+ fs.fchown = chownFix(fs.fchown)
51
+ fs.lchown = chownFix(fs.lchown)
52
+
53
+ fs.chmod = chmodFix(fs.chmod)
54
+ fs.fchmod = chmodFix(fs.fchmod)
55
+ fs.lchmod = chmodFix(fs.lchmod)
56
+
57
+ fs.chownSync = chownFixSync(fs.chownSync)
58
+ fs.fchownSync = chownFixSync(fs.fchownSync)
59
+ fs.lchownSync = chownFixSync(fs.lchownSync)
60
+
61
+ fs.chmodSync = chmodFixSync(fs.chmodSync)
62
+ fs.fchmodSync = chmodFixSync(fs.fchmodSync)
63
+ fs.lchmodSync = chmodFixSync(fs.lchmodSync)
64
+
65
+ fs.stat = statFix(fs.stat)
66
+ fs.fstat = statFix(fs.fstat)
67
+ fs.lstat = statFix(fs.lstat)
68
+
69
+ fs.statSync = statFixSync(fs.statSync)
70
+ fs.fstatSync = statFixSync(fs.fstatSync)
71
+ fs.lstatSync = statFixSync(fs.lstatSync)
72
+
73
+ // if lchmod/lchown do not exist, then make them no-ops
74
+ if (fs.chmod && !fs.lchmod) {
75
+ fs.lchmod = function (path, mode, cb) {
76
+ if (cb) process.nextTick(cb)
77
+ }
78
+ fs.lchmodSync = function () {}
79
+ }
80
+ if (fs.chown && !fs.lchown) {
81
+ fs.lchown = function (path, uid, gid, cb) {
82
+ if (cb) process.nextTick(cb)
83
+ }
84
+ fs.lchownSync = function () {}
85
+ }
86
+
87
+ // on Windows, A/V software can lock the directory, causing this
88
+ // to fail with an EACCES or EPERM if the directory contains newly
89
+ // created files. Try again on failure, for up to 60 seconds.
90
+
91
+ // Set the timeout this long because some Windows Anti-Virus, such as Parity
92
+ // bit9, may lock files for up to a minute, causing npm package install
93
+ // failures. Also, take care to yield the scheduler. Windows scheduling gives
94
+ // CPU to a busy looping process, which can cause the program causing the lock
95
+ // contention to be starved of CPU by node, so the contention doesn't resolve.
96
+ if (platform === "win32") {
97
+ fs.rename = typeof fs.rename !== 'function' ? fs.rename
98
+ : (function (fs$rename) {
99
+ function rename (from, to, cb) {
100
+ var start = Date.now()
101
+ var backoff = 0;
102
+ fs$rename(from, to, function CB (er) {
103
+ if (er
104
+ && (er.code === "EACCES" || er.code === "EPERM" || er.code === "EBUSY")
105
+ && Date.now() - start < 60000) {
106
+ setTimeout(function() {
107
+ fs.stat(to, function (stater, st) {
108
+ if (stater && stater.code === "ENOENT")
109
+ fs$rename(from, to, CB);
110
+ else
111
+ cb(er)
112
+ })
113
+ }, backoff)
114
+ if (backoff < 100)
115
+ backoff += 10;
116
+ return;
117
+ }
118
+ if (cb) cb(er)
119
+ })
120
+ }
121
+ if (Object.setPrototypeOf) Object.setPrototypeOf(rename, fs$rename)
122
+ return rename
123
+ })(fs.rename)
124
+ }
125
+
126
+ // if read() returns EAGAIN, then just try it again.
127
+ fs.read = typeof fs.read !== 'function' ? fs.read
128
+ : (function (fs$read) {
129
+ function read (fd, buffer, offset, length, position, callback_) {
130
+ var callback
131
+ if (callback_ && typeof callback_ === 'function') {
132
+ var eagCounter = 0
133
+ callback = function (er, _, __) {
134
+ if (er && er.code === 'EAGAIN' && eagCounter < 10) {
135
+ eagCounter ++
136
+ return fs$read.call(fs, fd, buffer, offset, length, position, callback)
137
+ }
138
+ callback_.apply(this, arguments)
139
+ }
140
+ }
141
+ return fs$read.call(fs, fd, buffer, offset, length, position, callback)
142
+ }
143
+
144
+ // This ensures `util.promisify` works as it does for native `fs.read`.
145
+ if (Object.setPrototypeOf) Object.setPrototypeOf(read, fs$read)
146
+ return read
147
+ })(fs.read)
148
+
149
+ fs.readSync = typeof fs.readSync !== 'function' ? fs.readSync
150
+ : (function (fs$readSync) { return function (fd, buffer, offset, length, position) {
151
+ var eagCounter = 0
152
+ while (true) {
153
+ try {
154
+ return fs$readSync.call(fs, fd, buffer, offset, length, position)
155
+ } catch (er) {
156
+ if (er.code === 'EAGAIN' && eagCounter < 10) {
157
+ eagCounter ++
158
+ continue
159
+ }
160
+ throw er
161
+ }
162
+ }
163
+ }})(fs.readSync)
164
+
165
+ function patchLchmod (fs) {
166
+ fs.lchmod = function (path, mode, callback) {
167
+ fs.open( path
168
+ , constants.O_WRONLY | constants.O_SYMLINK
169
+ , mode
170
+ , function (err, fd) {
171
+ if (err) {
172
+ if (callback) callback(err)
173
+ return
174
+ }
175
+ // prefer to return the chmod error, if one occurs,
176
+ // but still try to close, and report closing errors if they occur.
177
+ fs.fchmod(fd, mode, function (err) {
178
+ fs.close(fd, function(err2) {
179
+ if (callback) callback(err || err2)
180
+ })
181
+ })
182
+ })
183
+ }
184
+
185
+ fs.lchmodSync = function (path, mode) {
186
+ var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode)
187
+
188
+ // prefer to return the chmod error, if one occurs,
189
+ // but still try to close, and report closing errors if they occur.
190
+ var threw = true
191
+ var ret
192
+ try {
193
+ ret = fs.fchmodSync(fd, mode)
194
+ threw = false
195
+ } finally {
196
+ if (threw) {
197
+ try {
198
+ fs.closeSync(fd)
199
+ } catch (er) {}
200
+ } else {
201
+ fs.closeSync(fd)
202
+ }
203
+ }
204
+ return ret
205
+ }
206
+ }
207
+
208
+ function patchLutimes (fs) {
209
+ if (constants.hasOwnProperty("O_SYMLINK") && fs.futimes) {
210
+ fs.lutimes = function (path, at, mt, cb) {
211
+ fs.open(path, constants.O_SYMLINK, function (er, fd) {
212
+ if (er) {
213
+ if (cb) cb(er)
214
+ return
215
+ }
216
+ fs.futimes(fd, at, mt, function (er) {
217
+ fs.close(fd, function (er2) {
218
+ if (cb) cb(er || er2)
219
+ })
220
+ })
221
+ })
222
+ }
223
+
224
+ fs.lutimesSync = function (path, at, mt) {
225
+ var fd = fs.openSync(path, constants.O_SYMLINK)
226
+ var ret
227
+ var threw = true
228
+ try {
229
+ ret = fs.futimesSync(fd, at, mt)
230
+ threw = false
231
+ } finally {
232
+ if (threw) {
233
+ try {
234
+ fs.closeSync(fd)
235
+ } catch (er) {}
236
+ } else {
237
+ fs.closeSync(fd)
238
+ }
239
+ }
240
+ return ret
241
+ }
242
+
243
+ } else if (fs.futimes) {
244
+ fs.lutimes = function (_a, _b, _c, cb) { if (cb) process.nextTick(cb) }
245
+ fs.lutimesSync = function () {}
246
+ }
247
+ }
248
+
249
+ function chmodFix (orig) {
250
+ if (!orig) return orig
251
+ return function (target, mode, cb) {
252
+ return orig.call(fs, target, mode, function (er) {
253
+ if (chownErOk(er)) er = null
254
+ if (cb) cb.apply(this, arguments)
255
+ })
256
+ }
257
+ }
258
+
259
+ function chmodFixSync (orig) {
260
+ if (!orig) return orig
261
+ return function (target, mode) {
262
+ try {
263
+ return orig.call(fs, target, mode)
264
+ } catch (er) {
265
+ if (!chownErOk(er)) throw er
266
+ }
267
+ }
268
+ }
269
+
270
+
271
+ function chownFix (orig) {
272
+ if (!orig) return orig
273
+ return function (target, uid, gid, cb) {
274
+ return orig.call(fs, target, uid, gid, function (er) {
275
+ if (chownErOk(er)) er = null
276
+ if (cb) cb.apply(this, arguments)
277
+ })
278
+ }
279
+ }
280
+
281
+ function chownFixSync (orig) {
282
+ if (!orig) return orig
283
+ return function (target, uid, gid) {
284
+ try {
285
+ return orig.call(fs, target, uid, gid)
286
+ } catch (er) {
287
+ if (!chownErOk(er)) throw er
288
+ }
289
+ }
290
+ }
291
+
292
+ function statFix (orig) {
293
+ if (!orig) return orig
294
+ // Older versions of Node erroneously returned signed integers for
295
+ // uid + gid.
296
+ return function (target, options, cb) {
297
+ if (typeof options === 'function') {
298
+ cb = options
299
+ options = null
300
+ }
301
+ function callback (er, stats) {
302
+ if (stats) {
303
+ if (stats.uid < 0) stats.uid += 0x100000000
304
+ if (stats.gid < 0) stats.gid += 0x100000000
305
+ }
306
+ if (cb) cb.apply(this, arguments)
307
+ }
308
+ return options ? orig.call(fs, target, options, callback)
309
+ : orig.call(fs, target, callback)
310
+ }
311
+ }
312
+
313
+ function statFixSync (orig) {
314
+ if (!orig) return orig
315
+ // Older versions of Node erroneously returned signed integers for
316
+ // uid + gid.
317
+ return function (target, options) {
318
+ var stats = options ? orig.call(fs, target, options)
319
+ : orig.call(fs, target)
320
+ if (stats) {
321
+ if (stats.uid < 0) stats.uid += 0x100000000
322
+ if (stats.gid < 0) stats.gid += 0x100000000
323
+ }
324
+ return stats;
325
+ }
326
+ }
327
+
328
+ // ENOSYS means that the fs doesn't support the op. Just ignore
329
+ // that, because it doesn't matter.
330
+ //
331
+ // if there's no getuid, or if getuid() is something other
332
+ // than 0, and the error is EINVAL or EPERM, then just ignore
333
+ // it.
334
+ //
335
+ // This specific case is a silent failure in cp, install, tar,
336
+ // and most other unix tools that manage permissions.
337
+ //
338
+ // When running as root, or if other types of errors are
339
+ // encountered, then it's strict.
340
+ function chownErOk (er) {
341
+ if (!er)
342
+ return true
343
+
344
+ if (er.code === "ENOSYS")
345
+ return true
346
+
347
+ var nonroot = !process.getuid || process.getuid() !== 0
348
+ if (nonroot) {
349
+ if (er.code === "EINVAL" || er.code === "EPERM")
350
+ return true
351
+ }
352
+
353
+ return false
354
+ }
355
+ }
@@ -0,0 +1,108 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
+
5
+ <a name="4.1.2"></a>
6
+ ## [4.1.2](https://github.com/moxystudio/node-proper-lockfile/compare/v4.1.1...v4.1.2) (2021-01-25)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * fix node 14 updating graceful-fs ([#102](https://github.com/moxystudio/node-proper-lockfile/issues/102)) ([b0d988e](https://github.com/moxystudio/node-proper-lockfile/commit/b0d988e))
12
+
13
+
14
+
15
+ <a name="4.1.1"></a>
16
+ ## [4.1.1](https://github.com/moxystudio/node-proper-lockfile/compare/v4.1.0...v4.1.1) (2019-04-03)
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * fix mtime precision on some filesystems ([#88](https://github.com/moxystudio/node-proper-lockfile/issues/88)) ([f266158](https://github.com/moxystudio/node-proper-lockfile/commit/f266158)), closes [#82](https://github.com/moxystudio/node-proper-lockfile/issues/82) [#87](https://github.com/moxystudio/node-proper-lockfile/issues/87)
22
+
23
+
24
+
25
+ <a name="4.1.0"></a>
26
+ # [4.1.0](https://github.com/moxystudio/node-proper-lockfile/compare/v4.0.0...v4.1.0) (2019-03-18)
27
+
28
+
29
+ ### Features
30
+
31
+ * allow second precision in mtime comparison ([#78](https://github.com/moxystudio/node-proper-lockfile/issues/78)) ([b2816a6](https://github.com/moxystudio/node-proper-lockfile/commit/b2816a6))
32
+
33
+
34
+
35
+ <a name="4.0.0"></a>
36
+ # [4.0.0](https://github.com/moxystudio/node-proper-lockfile/compare/v3.2.0...v4.0.0) (2019-03-12)
37
+
38
+
39
+ ### Bug Fixes
40
+
41
+ * fix typo in error message ([#68](https://github.com/moxystudio/node-proper-lockfile/issues/68)) ([b91cb55](https://github.com/moxystudio/node-proper-lockfile/commit/b91cb55))
42
+
43
+
44
+ ### Features
45
+
46
+ * make staleness check more robust ([#74](https://github.com/moxystudio/node-proper-lockfile/issues/74)) ([9cc0973](https://github.com/moxystudio/node-proper-lockfile/commit/9cc0973)), closes [#71](https://github.com/moxystudio/node-proper-lockfile/issues/71) [/github.com/ipfs/js-ipfs-repo/issues/188#issuecomment-468682971](https://github.com//github.com/ipfs/js-ipfs-repo/issues/188/issues/issuecomment-468682971)
47
+
48
+
49
+ ### BREAKING CHANGES
50
+
51
+ * We were marking the lock as compromised when system went into sleep or if the event loop was busy taking too long to run the internals timers, Now we keep track of the mtime updated by the current process, and if we lose some cycles in the update process but recover and the mtime is still ours we do not mark the lock as compromised.
52
+
53
+
54
+
55
+ <a name="3.2.0"></a>
56
+ # [3.2.0](https://github.com/moxystudio/node-proper-lockfile/compare/v3.1.0...v3.2.0) (2018-11-19)
57
+
58
+
59
+ ### Features
60
+
61
+ * add lock path option ([#66](https://github.com/moxystudio/node-proper-lockfile/issues/66)) ([32f1b8d](https://github.com/moxystudio/node-proper-lockfile/commit/32f1b8d))
62
+
63
+
64
+
65
+ <a name="3.1.0"></a>
66
+ # [3.1.0](https://github.com/moxystudio/node-proper-lockfile/compare/v3.0.2...v3.1.0) (2018-11-15)
67
+
68
+
69
+ ### Bug Fixes
70
+
71
+ * **package:** update retry to version 0.12.0 ([#50](https://github.com/moxystudio/node-proper-lockfile/issues/50)) ([d400b98](https://github.com/moxystudio/node-proper-lockfile/commit/d400b98))
72
+
73
+
74
+ ### Features
75
+
76
+ * add signal exit ([#65](https://github.com/moxystudio/node-proper-lockfile/issues/65)) ([f20bc45](https://github.com/moxystudio/node-proper-lockfile/commit/f20bc45))
77
+
78
+
79
+
80
+ <a name="3.0.2"></a>
81
+ ## [3.0.2](https://github.com/moxystudio/node-proper-lockfile/compare/v3.0.1...v3.0.2) (2018-01-30)
82
+
83
+
84
+
85
+ <a name="3.0.1"></a>
86
+ ## [3.0.1](https://github.com/moxystudio/node-proper-lockfile/compare/v3.0.0...v3.0.1) (2018-01-20)
87
+
88
+
89
+ ### Bug Fixes
90
+
91
+ * restore ability to use lockfile() directly ([0ef8fbc](https://github.com/moxystudio/node-proper-lockfile/commit/0ef8fbc))
92
+
93
+
94
+
95
+ <a name="3.0.0"></a>
96
+ # [3.0.0](https://github.com/moxystudio/node-proper-lockfile/compare/v2.0.1...v3.0.0) (2018-01-20)
97
+
98
+
99
+ ### Chores
100
+
101
+ * update project to latest node lts ([b1d43e5](https://github.com/moxystudio/node-proper-lockfile/commit/b1d43e5))
102
+
103
+
104
+ ### BREAKING CHANGES
105
+
106
+ * remove callback support
107
+ * use of node lts language features such as object spread
108
+ * compromised function in lock() has been moved to an option
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Made With MOXY Lda <hello@moxy.studio>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,183 @@
1
+ # proper-lockfile
2
+
3
+ [![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Coverage Status][codecov-image]][codecov-url] [![Dependency status][david-dm-image]][david-dm-url] [![Dev Dependency status][david-dm-dev-image]][david-dm-dev-url]
4
+
5
+ [npm-url]:https://npmjs.org/package/proper-lockfile
6
+ [downloads-image]:https://img.shields.io/npm/dm/proper-lockfile.svg
7
+ [npm-image]:https://img.shields.io/npm/v/proper-lockfile.svg
8
+ [travis-url]:https://travis-ci.org/moxystudio/node-proper-lockfile
9
+ [travis-image]:https://img.shields.io/travis/moxystudio/node-proper-lockfile/master.svg
10
+ [codecov-url]:https://codecov.io/gh/moxystudio/node-proper-lockfile
11
+ [codecov-image]:https://img.shields.io/codecov/c/github/moxystudio/node-proper-lockfile/master.svg
12
+ [david-dm-url]:https://david-dm.org/moxystudio/node-proper-lockfile
13
+ [david-dm-image]:https://img.shields.io/david/moxystudio/node-proper-lockfile.svg
14
+ [david-dm-dev-url]:https://david-dm.org/moxystudio/node-proper-lockfile?type=dev
15
+ [david-dm-dev-image]:https://img.shields.io/david/dev/moxystudio/node-proper-lockfile.svg
16
+
17
+ An inter-process and inter-machine lockfile utility that works on a local or network file system.
18
+
19
+
20
+ ## Installation
21
+
22
+ `$ npm install proper-lockfile`
23
+
24
+
25
+ ## Design
26
+
27
+ There are various ways to achieve [file locking](http://en.wikipedia.org/wiki/File_locking).
28
+
29
+ This library utilizes the `mkdir` strategy which works atomically on any kind of file system, even network based ones.
30
+ The lockfile path is based on the file path you are trying to lock by suffixing it with `.lock`.
31
+
32
+ When a lock is successfully acquired, the lockfile's `mtime` (modified time) is periodically updated to prevent staleness. This allows to effectively check if a lock is stale by checking its `mtime` against a stale threshold. If the update of the mtime fails several times, the lock might be compromised. The `mtime` is [supported](http://en.wikipedia.org/wiki/Comparison_of_file_systems) in almost every `filesystem`.
33
+
34
+
35
+ ### Comparison
36
+
37
+ This library is similar to [lockfile](https://github.com/isaacs/lockfile) but the latter has some drawbacks:
38
+
39
+ - It relies on `open` with `O_EXCL` flag which has problems in network file systems. `proper-lockfile` uses `mkdir` which doesn't have this issue.
40
+
41
+ > O_EXCL is broken on NFS file systems; programs which rely on it for performing locking tasks will contain a race condition.
42
+
43
+ - The lockfile staleness check is done via `ctime` (creation time) which is unsuitable for long running processes. `proper-lockfile` constantly updates lockfiles `mtime` to do proper staleness check.
44
+
45
+ - It does not check if the lockfile was compromised which can lead to undesirable situations. `proper-lockfile` checks the lockfile when updating the `mtime`.
46
+
47
+ - It has a default value of `0` for the stale option which isn't good because any crash or process kill that the package can't handle gracefully will leave the lock active forever.
48
+
49
+
50
+ ### Compromised
51
+
52
+ `proper-lockfile` does not detect cases in which:
53
+
54
+ - A `lockfile` is manually removed and someone else acquires the lock right after
55
+ - Different `stale`/`update` values are being used for the same file, possibly causing two locks to be acquired on the same file
56
+
57
+ `proper-lockfile` detects cases in which:
58
+
59
+ - Updates to the `lockfile` fail
60
+ - Updates take longer than expected, possibly causing the lock to become stale for a certain amount of time
61
+
62
+
63
+ As you see, the first two are a consequence of bad usage. Technically, it was possible to detect the first two but it would introduce complexity and eventual race conditions.
64
+
65
+
66
+ ## Usage
67
+
68
+ ### .lock(file, [options])
69
+
70
+ Tries to acquire a lock on `file` or rejects the promise on error.
71
+
72
+ If the lock succeeds, a `release` function is provided that should be called when you want to release the lock. The `release` function also rejects the promise on error (e.g. when the lock was already compromised).
73
+
74
+ Available options:
75
+
76
+ - `stale`: Duration in milliseconds in which the lock is considered stale, defaults to `10000` (minimum value is `5000`)
77
+ - `update`: The interval in milliseconds in which the lockfile's `mtime` will be updated, defaults to `stale/2` (minimum value is `1000`, maximum value is `stale/2`)
78
+ - `retries`: The number of retries or a [retry](https://www.npmjs.org/package/retry) options object, defaults to `0`
79
+ - `realpath`: Resolve symlinks using realpath, defaults to `true` (note that if `true`, the `file` must exist previously)
80
+ - `fs`: A custom fs to use, defaults to `graceful-fs`
81
+ - `onCompromised`: Called if the lock gets compromised, defaults to a function that simply throws the error which will probably cause the process to die
82
+ - `lockfilePath`: Custom lockfile path. e.g.: If you want to lock a directory and create the lock file inside it, you can pass `file` as `<dir path>` and `options.lockfilePath` as `<dir path>/dir.lock`
83
+
84
+
85
+ ```js
86
+ const lockfile = require('proper-lockfile');
87
+
88
+ lockfile.lock('some/file')
89
+ .then((release) => {
90
+ // Do something while the file is locked
91
+
92
+ // Call the provided release function when you're done,
93
+ // which will also return a promise
94
+ return release();
95
+ })
96
+ .catch((e) => {
97
+ // either lock could not be acquired
98
+ // or releasing it failed
99
+ console.error(e)
100
+ });
101
+
102
+ // Alternatively, you may use lockfile('some/file') directly.
103
+ ```
104
+
105
+
106
+ ### .unlock(file, [options])
107
+
108
+ Releases a previously acquired lock on `file` or rejects the promise on error.
109
+
110
+ Whenever possible you should use the `release` function instead (as exemplified above). Still there are cases in which it's hard to keep a reference to it around code. In those cases `unlock()` might be handy.
111
+
112
+ Available options:
113
+
114
+ - `realpath`: Resolve symlinks using realpath, defaults to `true` (note that if `true`, the `file` must exist previously)
115
+ - `fs`: A custom fs to use, defaults to `graceful-fs`
116
+ - `lockfilePath`: Custom lockfile path. e.g.: If you want to lock a directory and create the lock file inside it, you can pass `file` as `<dir path>` and `options.lockfilePath` as `<dir path>/dir.lock`
117
+
118
+
119
+ ```js
120
+ const lockfile = require('proper-lockfile');
121
+
122
+ lockfile.lock('some/file')
123
+ .then(() => {
124
+ // Do something while the file is locked
125
+
126
+ // Later..
127
+ return lockfile.unlock('some/file');
128
+ });
129
+ ```
130
+
131
+ ### .check(file, [options])
132
+
133
+ Check if the file is locked and its lockfile is not stale, rejects the promise on error.
134
+
135
+ Available options:
136
+
137
+ - `stale`: Duration in milliseconds in which the lock is considered stale, defaults to `10000` (minimum value is `5000`)
138
+ - `realpath`: Resolve symlinks using realpath, defaults to `true` (note that if `true`, the `file` must exist previously)
139
+ - `fs`: A custom fs to use, defaults to `graceful-fs`
140
+ - `lockfilePath`: Custom lockfile path. e.g.: If you want to lock a directory and create the lock file inside it, you can pass `file` as `<dir path>` and `options.lockfilePath` as `<dir path>/dir.lock`
141
+
142
+
143
+ ```js
144
+ const lockfile = require('proper-lockfile');
145
+
146
+ lockfile.check('some/file')
147
+ .then((isLocked) => {
148
+ // isLocked will be true if 'some/file' is locked, false otherwise
149
+ });
150
+ ```
151
+
152
+ ### .lockSync(file, [options])
153
+
154
+ Sync version of `.lock()`.
155
+ Returns the `release` function or throws on error.
156
+
157
+ ### .unlockSync(file, [options])
158
+
159
+ Sync version of `.unlock()`.
160
+ Throws on error.
161
+
162
+ ### .checkSync(file, [options])
163
+
164
+ Sync version of `.check()`.
165
+ Returns a boolean or throws on error.
166
+
167
+
168
+ ## Graceful exit
169
+
170
+ `proper-lockfile` automatically removes locks if the process exits, except if the process is killed with SIGKILL or it crashes due to a VM fatal error (e.g.: out of memory).
171
+
172
+
173
+ ## Tests
174
+
175
+ `$ npm test`
176
+ `$ npm test -- --watch` during development
177
+
178
+ The test suite is very extensive. There's even a stress test to guarantee exclusiveness of locks.
179
+
180
+
181
+ ## License
182
+
183
+ Released under the [MIT License](https://www.opensource.org/licenses/mit-license.php).