@oclif/plugin-update 4.7.42 → 4.7.44
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.
- package/README.md +1 -1
- package/dist/hooks/init.js +57 -3
- package/oclif.manifest.json +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -81,7 +81,7 @@ EXAMPLES
|
|
|
81
81
|
$ oclif-example update --available
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
_See code: [src/commands/update.ts](https://github.com/oclif/plugin-update/blob/4.7.
|
|
84
|
+
_See code: [src/commands/update.ts](https://github.com/oclif/plugin-update/blob/4.7.44/src/commands/update.ts)_
|
|
85
85
|
<!-- commandsstop -->
|
|
86
86
|
|
|
87
87
|
# Contributing
|
package/dist/hooks/init.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import makeDebug from 'debug';
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
|
-
import { mkdir, open, stat, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { mkdir, open, stat, unlink, writeFile } from 'node:fs/promises';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { touch } from '../util.js';
|
|
7
7
|
const debug = makeDebug('cli:updater');
|
|
@@ -56,10 +56,23 @@ export const init = async function (opts) {
|
|
|
56
56
|
const clientDir = join(clientRoot, config.version);
|
|
57
57
|
if (existsSync(clientDir))
|
|
58
58
|
await touch(clientDir);
|
|
59
|
-
|
|
59
|
+
// Atomically claim the right to spawn an autoupdate.
|
|
60
|
+
//
|
|
61
|
+
// The original `if (!(await autoupdateNeeded())) return; await writeFile(...)`
|
|
62
|
+
// sequence had a non-atomic read-then-write window: several otto invocations
|
|
63
|
+
// starting in parallel on a machine with no marker file (e.g. a fresh
|
|
64
|
+
// laptop being set up) could all pass the autoupdateNeeded() check before
|
|
65
|
+
// any one of them wrote the marker, and each would spawn its own
|
|
66
|
+
// `<cli> update --autoupdate` child. Those children then pin in debounce()
|
|
67
|
+
// (which never exits while CLI activity continues) and accumulate until OOM.
|
|
68
|
+
//
|
|
69
|
+
// Fix: combine the check and the marker creation into a single atomic step
|
|
70
|
+
// using O_EXCL (`open(path, 'wx')`). Only one process can create the marker;
|
|
71
|
+
// others see EEXIST and bail (or, if the marker is stale, race to reclaim it
|
|
72
|
+
// — which is bounded to one race per debounce window per machine).
|
|
73
|
+
if (!(await claimAutoupdate(autoupdatefile)))
|
|
60
74
|
return;
|
|
61
75
|
debug('autoupdate running');
|
|
62
|
-
await writeFile(autoupdatefile, '');
|
|
63
76
|
debug(`spawning autoupdate on ${binPath}`);
|
|
64
77
|
const fd = await open(autoupdatelogfile, 'a');
|
|
65
78
|
await writeFile(fd, timestamp(`starting \`${binPath} update --autoupdate\` from ${process.argv.slice(1, 3).join(' ')}\n`));
|
|
@@ -73,4 +86,45 @@ export const init = async function (opts) {
|
|
|
73
86
|
.on('error', (e) => process.emitWarning(e))
|
|
74
87
|
.on('close', () => fd.close())
|
|
75
88
|
.unref();
|
|
89
|
+
async function claimAutoupdate(markerPath) {
|
|
90
|
+
// Fast path: try to atomically create the marker. Wins the race when no
|
|
91
|
+
// marker exists yet (fresh-laptop case — the catastrophic scenario).
|
|
92
|
+
try {
|
|
93
|
+
const fd = await open(markerPath, 'wx');
|
|
94
|
+
await fd.close();
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
const err = error;
|
|
99
|
+
if (err.code !== 'EEXIST')
|
|
100
|
+
throw err;
|
|
101
|
+
}
|
|
102
|
+
// Marker exists. If it's within the debounce window, nothing to do.
|
|
103
|
+
if (!(await autoupdateNeeded()))
|
|
104
|
+
return false;
|
|
105
|
+
// Marker is stale (debounce window has elapsed). Reclaim by unlinking and
|
|
106
|
+
// re-creating atomically. There remains a tiny window between unlink and
|
|
107
|
+
// open where two stale-marker processes could both win, but it's
|
|
108
|
+
// microseconds vs the multi-await window of the original bug, and
|
|
109
|
+
// bounded to one race per debounce window per machine.
|
|
110
|
+
try {
|
|
111
|
+
await unlink(markerPath);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
const err = error;
|
|
115
|
+
if (err.code !== 'ENOENT')
|
|
116
|
+
throw err;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
const fd = await open(markerPath, 'wx');
|
|
120
|
+
await fd.close();
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
const err = error;
|
|
125
|
+
if (err.code === 'EEXIST')
|
|
126
|
+
return false;
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
76
130
|
};
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oclif/plugin-update",
|
|
3
|
-
"version": "4.7.
|
|
3
|
+
"version": "4.7.44",
|
|
4
4
|
"author": "Salesforce",
|
|
5
5
|
"bugs": "https://github.com/oclif/plugin-update/issues",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@inquirer/select": "^2.5.0",
|
|
8
8
|
"@oclif/core": "^4",
|
|
9
|
-
"@oclif/table": "^0.5.
|
|
9
|
+
"@oclif/table": "^0.5.9",
|
|
10
10
|
"ansis": "^3.17.0",
|
|
11
11
|
"debug": "^4.4.1",
|
|
12
12
|
"filesize": "^6.1.0",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"chai": "^4.5.0",
|
|
32
32
|
"commitlint": "^19",
|
|
33
33
|
"eslint": "^9.39.4",
|
|
34
|
-
"eslint-config-oclif": "^6.0.
|
|
34
|
+
"eslint-config-oclif": "^6.0.165",
|
|
35
35
|
"eslint-config-prettier": "^10.1.8",
|
|
36
36
|
"husky": "^9.1.7",
|
|
37
37
|
"lint-staged": "^15",
|