nx 23.3.0 → 24.0.0-beta.0
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 +3 -6
- package/dist/src/adapter/ngcli-adapter.js +1 -0
- package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +0 -12
- package/dist/src/command-line/graph/graph.js +3 -5
- package/dist/src/command-line/init/command-object.js +0 -12
- package/dist/src/command-line/init/init-v2.js +0 -2
- package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +17 -12
- package/dist/src/command-line/release/utils/remote-release-clients/github.js +5 -6
- package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +5 -6
- package/dist/src/command-line/run/executor-utils.js +1 -0
- package/dist/src/command-line/run/run-one.d.ts +20 -1
- package/dist/src/command-line/run/run-one.js +153 -5
- package/dist/src/command-line/show/show-target/utils.js +1 -0
- package/dist/src/command-line/watch/command-object.js +3 -22
- package/dist/src/command-line/watch/watch.d.ts +0 -7
- package/dist/src/command-line/watch/watch.js +1 -1
- package/dist/src/config/misc-interfaces.d.ts +12 -0
- package/dist/src/config/workspace-json-project-json.d.ts +5 -0
- package/dist/src/daemon/client/client.d.ts +9 -0
- package/dist/src/daemon/client/client.js +89 -37
- package/dist/src/daemon/server/shutdown-utils.js +0 -3
- package/dist/src/daemon/socket-utils.d.ts +9 -4
- package/dist/src/daemon/socket-utils.js +12 -49
- package/dist/src/daemon/tmp-dir.d.ts +19 -14
- package/dist/src/daemon/tmp-dir.js +142 -354
- package/dist/src/devkit-internals.d.ts +1 -1
- package/dist/src/devkit-internals.js +1 -0
- package/dist/src/hasher/file-hasher.js +9 -7
- package/dist/src/hasher/hash-task.js +4 -0
- package/dist/src/hasher/native-task-hasher-impl.js +1 -1
- package/dist/src/migrations/update-24-0-0/find-custom-hashers.d.ts +3 -0
- package/dist/src/migrations/update-24-0-0/find-custom-hashers.js +100 -0
- package/dist/src/migrations/update-24-0-0/find-custom-hashers.md +63 -0
- package/dist/src/migrations/update-24-0-0/move-custom-hashers-to-inputs.md +145 -0
- package/dist/src/native/index.d.ts +127 -8
- package/dist/src/native/native-bindings.js +5 -0
- package/dist/src/native/nx.wasi-browser.js +1 -0
- package/dist/src/native/nx.wasi.cjs +1 -0
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/nx-cloud/update-manager.d.ts +7 -0
- package/dist/src/nx-cloud/update-manager.js +237 -50
- package/dist/src/project-graph/affected/affected-tasks.js +1 -0
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +7 -0
- package/dist/src/tasks-runner/create-task-graph.d.ts +10 -0
- package/dist/src/tasks-runner/create-task-graph.js +37 -2
- package/dist/src/tasks-runner/task-env.d.ts +10 -0
- package/dist/src/tasks-runner/task-env.js +26 -5
- package/dist/src/tasks-runner/task-orchestrator.d.ts +10 -4
- package/dist/src/tasks-runner/task-orchestrator.js +87 -53
- package/dist/src/tasks-runner/utils.d.ts +20 -5
- package/dist/src/tasks-runner/utils.js +80 -29
- package/dist/src/ultracache/overrides.js +4 -0
- package/dist/src/utils/owned-private-dir.d.ts +1 -17
- package/dist/src/utils/owned-private-dir.js +10 -62
- package/dist/src/utils/perf-hooks.d.ts +1 -0
- package/dist/src/utils/plugins/core-plugins.js +1 -1
- package/dist/src/utils/string-similarity.d.ts +38 -0
- package/dist/src/utils/string-similarity.js +108 -0
- package/migrations.json +7 -0
- package/package.json +11 -20
- package/readme-template.md +3 -3
- package/schemas/nx-schema.json +5 -0
- package/schemas/project-schema.json +5 -0
- package/dist/src/tasks-runner/legacy-depends-on-warning.d.ts +0 -18
- package/dist/src/tasks-runner/legacy-depends-on-warning.js +0 -109
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.DAEMON_OUTPUT_LOG_FILE = exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE = exports.InvalidSocketDirConfigured = void 0;
|
|
4
4
|
exports.writeDaemonLogs = writeDaemonLogs;
|
|
5
5
|
exports.markDaemonAsDisabled = markDaemonAsDisabled;
|
|
6
6
|
exports.disableDaemonForThisProcess = disableDaemonForThisProcess;
|
|
7
7
|
exports.isDaemonDisabled = isDaemonDisabled;
|
|
8
|
-
exports.getSocketDir = getSocketDir;
|
|
9
|
-
exports.getPluginSocketDir = getPluginSocketDir;
|
|
10
8
|
exports.resetSocketDirWarningsForTesting = resetSocketDirWarningsForTesting;
|
|
11
|
-
exports.
|
|
12
|
-
exports.
|
|
9
|
+
exports.getDaemonSocketPath = getDaemonSocketPath;
|
|
10
|
+
exports.getForkedProcessSocketPath = getForkedProcessSocketPath;
|
|
11
|
+
exports.getPluginSocketPath = getPluginSocketPath;
|
|
12
|
+
exports.getNxConsoleSocketPath = getNxConsoleSocketPath;
|
|
13
13
|
exports.removeSocketDir = removeSocketDir;
|
|
14
14
|
/**
|
|
15
|
-
* Where the daemon, forked task processes
|
|
15
|
+
* Where the daemon, forked task processes, plugin workers and Nx Console put
|
|
16
|
+
* their sockets.
|
|
16
17
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* process cache rather than deriving it.
|
|
18
|
+
* Resolution itself lives in the native crate, in `native/utils/socket_path.rs`.
|
|
19
|
+
* It has to: the root is picked at runtime from an ordered chain that can
|
|
20
|
+
* demote, so a second derivation cannot track it, and Nx Console binds the
|
|
21
|
+
* socket Nx dials. What stays here is the wording and the once-per-process
|
|
22
|
+
* latches, because the logger is here.
|
|
23
23
|
*
|
|
24
24
|
* Daemon logs are not here — they live in the workspace data dir alongside the
|
|
25
25
|
* `disabled` marker, which is why that path survives a socket-root change.
|
|
@@ -27,13 +27,7 @@ exports.removeSocketDir = removeSocketDir;
|
|
|
27
27
|
const node_fs_1 = require("node:fs");
|
|
28
28
|
const path_1 = require("path");
|
|
29
29
|
const cache_directory_1 = require("../utils/cache-directory");
|
|
30
|
-
const
|
|
31
|
-
const crypto_1 = require("crypto");
|
|
32
|
-
// Refused as a socket *directory* (see InvalidSocketDirConfigured), and also
|
|
33
|
-
// the socket root itself on Windows, where named pipes are not filesystem
|
|
34
|
-
// objects and there is nothing to lock down beneath it.
|
|
35
|
-
const tmp_1 = require("tmp");
|
|
36
|
-
const native_file_cache_location_1 = require("../native/native-file-cache-location");
|
|
30
|
+
const native_1 = require("../native");
|
|
37
31
|
const nx_tmp_dir_1 = require("../utils/nx-tmp-dir");
|
|
38
32
|
const is_sandbox_1 = require("../utils/is-sandbox");
|
|
39
33
|
const workspace_root_1 = require("../utils/workspace-root");
|
|
@@ -59,8 +53,8 @@ class InvalidSocketDirConfigured extends Error {
|
|
|
59
53
|
// root is *not* peer-writable, so what it holds is this account's
|
|
60
54
|
// own. And "Nx already puts its sockets in a subdirectory of this
|
|
61
55
|
// root" is false on POSIX, where the default root is a literal
|
|
62
|
-
// /tmp/.nx and this one is
|
|
63
|
-
// two differ.
|
|
56
|
+
// /tmp/.nx and this one is the OS temp dir — which is exactly when
|
|
57
|
+
// the two differ.
|
|
64
58
|
`The configured Nx socket directory ${dir} is the operating system temp directory. Nx deletes the socket directory and everything in it when the daemon stops, which here would take everything else using this temp directory with it. Point NX_SOCKET_DIR at a directory of your own instead — one nested beneath this root is fine.`
|
|
65
59
|
: `The configured Nx socket directory ${dir} is a directory Nx keeps its own runtime state in, and Nx creates and removes socket directories beneath it. Point NX_SOCKET_DIR at a directory of your own instead — one nested beneath this root is fine.`);
|
|
66
60
|
this.dir = dir;
|
|
@@ -71,11 +65,6 @@ class InvalidSocketDirConfigured extends Error {
|
|
|
71
65
|
exports.InvalidSocketDirConfigured = InvalidSocketDirConfigured;
|
|
72
66
|
exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE = (0, path_1.join)(cache_directory_1.workspaceDataDirectory, 'd');
|
|
73
67
|
exports.DAEMON_OUTPUT_LOG_FILE = (0, path_1.join)(exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE, 'daemon.log');
|
|
74
|
-
const getDaemonSocketDir = () => (0, path_1.join)(getSocketDir(),
|
|
75
|
-
// Kept intentionally short to stay under the socket/named pipe path length
|
|
76
|
-
// limit enforced by `assertValidSocketPath` in socket-utils.ts.
|
|
77
|
-
'd.sock');
|
|
78
|
-
exports.getDaemonSocketDir = getDaemonSocketDir;
|
|
79
68
|
function writeDaemonLogs(error) {
|
|
80
69
|
const file = (0, path_1.join)(exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE, 'daemon-error.log');
|
|
81
70
|
(0, node_fs_1.writeFileSync)(file, error);
|
|
@@ -109,225 +98,6 @@ function isDaemonDisabled() {
|
|
|
109
98
|
return false;
|
|
110
99
|
}
|
|
111
100
|
}
|
|
112
|
-
/**
|
|
113
|
-
* One short root beneath the current user's owner-only runtime directory. No
|
|
114
|
-
* extra segment on Windows: `%TMP%` is already per-user and already contains the
|
|
115
|
-
* username, so `\.nx\sockets` would spend 12 characters of the 95-char budget
|
|
116
|
-
* `assertValidSocketPath` enforces and newly overrun it for long account names.
|
|
117
|
-
*/
|
|
118
|
-
function defaultSocketRoot() {
|
|
119
|
-
return process.platform === 'win32'
|
|
120
|
-
? tmp_1.tmpdir
|
|
121
|
-
: (0, path_1.join)(nx_tmp_dir_1.NX_USER_TMP_DIR, 'sockets');
|
|
122
|
-
}
|
|
123
|
-
function homeSocketRoot() {
|
|
124
|
-
return nx_tmp_dir_1.NX_HOME_TMP_DIR ? (0, path_1.join)(nx_tmp_dir_1.NX_HOME_TMP_DIR, 'sockets') : undefined;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Whether `~/.nx` is somewhere other than the shared container. With
|
|
128
|
-
* `HOME=/tmp` they are the same path, and offering it as a second tier would
|
|
129
|
-
* point `ensureOwnedPrivateDir` at `/tmp/.nx` itself — which, when the container
|
|
130
|
-
* is already ours (or Nx runs as root), takes a `1777` container to `0700` and
|
|
131
|
-
* undoes the documented provisioning, with nothing to put it back.
|
|
132
|
-
*/
|
|
133
|
-
function homeTierIsDistinct() {
|
|
134
|
-
if (!nx_tmp_dir_1.NX_HOME_TMP_DIR) {
|
|
135
|
-
return false;
|
|
136
|
-
}
|
|
137
|
-
// Hoisted: the callback runs once per root, and this operand does not change.
|
|
138
|
-
const home = (0, owned_private_dir_1.canonicalDir)(nx_tmp_dir_1.NX_HOME_TMP_DIR);
|
|
139
|
-
return ![
|
|
140
|
-
tmp_1.tmpdir,
|
|
141
|
-
nx_tmp_dir_1.NX_TMP_DIR,
|
|
142
|
-
nx_tmp_dir_1.NX_USER_TMP_DIR,
|
|
143
|
-
defaultSocketRoot(),
|
|
144
|
-
native_file_cache_location_1.NATIVE_CACHE_ROOT,
|
|
145
|
-
].some((shared) => (0, owned_private_dir_1.canonicalDir)(shared) === home);
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Socket roots to try, best first. Each entry establishes its own containment
|
|
149
|
-
* before it can be used; the first that succeeds wins, and the workspace data
|
|
150
|
-
* dir is the last resort when none does.
|
|
151
|
-
*
|
|
152
|
-
* `/tmp` first because it is the shortest path — the socket budget is 95
|
|
153
|
-
* characters. Home second because it needs no administrator. Windows has one
|
|
154
|
-
* tier: named pipes are not filesystem objects, so there is nothing to establish.
|
|
155
|
-
*/
|
|
156
|
-
function socketRootTiers() {
|
|
157
|
-
if (process.platform === 'win32') {
|
|
158
|
-
return [{ root: tmp_1.tmpdir, establish: () => true }];
|
|
159
|
-
}
|
|
160
|
-
// Stops at the first refusal and records it: once the shared container is
|
|
161
|
-
// unusable, whether the directories beneath it would also have failed says
|
|
162
|
-
// nothing the user can act on.
|
|
163
|
-
const establishAll = (dirs, refusals, guard) => dirs.every((d) => {
|
|
164
|
-
const result = guard(d);
|
|
165
|
-
if (result.status === 'refused') {
|
|
166
|
-
refusals.push(result.refusal);
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
return true;
|
|
170
|
-
});
|
|
171
|
-
return [
|
|
172
|
-
{
|
|
173
|
-
root: defaultSocketRoot(),
|
|
174
|
-
establish: (refusals) => establishAll([nx_tmp_dir_1.NX_TMP_DIR], refusals, owned_private_dir_1.ensureSafeSharedRoot) &&
|
|
175
|
-
establishAll([nx_tmp_dir_1.NX_USER_TMP_DIR, defaultSocketRoot()], refusals, owned_private_dir_1.ensureOwnedPrivateDir),
|
|
176
|
-
},
|
|
177
|
-
// Omitted entirely when there is no home directory to use, or when it is
|
|
178
|
-
// the shared container under another name, rather than offered and then
|
|
179
|
-
// damaging what it lands on.
|
|
180
|
-
...(homeTierIsDistinct() && homeSocketRoot()
|
|
181
|
-
? [
|
|
182
|
-
{
|
|
183
|
-
root: homeSocketRoot(),
|
|
184
|
-
// No shared level to verify: the home directory is the user's own,
|
|
185
|
-
// so there is no container another user could have created first.
|
|
186
|
-
establish: (refusals) => establishAll([nx_tmp_dir_1.NX_HOME_TMP_DIR, homeSocketRoot()], refusals, owned_private_dir_1.ensureOwnedPrivateDir),
|
|
187
|
-
},
|
|
188
|
-
]
|
|
189
|
-
: []),
|
|
190
|
-
];
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* The first socket root whose containment could be established, or `undefined`
|
|
194
|
-
* when none could and the caller should fall back to the workspace.
|
|
195
|
-
*/
|
|
196
|
-
function establishSocketRoot(refusals) {
|
|
197
|
-
const tiers = socketRootTiers();
|
|
198
|
-
for (const [index, tier] of tiers.entries()) {
|
|
199
|
-
if (tier.establish(refusals)) {
|
|
200
|
-
// `preferred` is set only on a demotion, and names the tier that was
|
|
201
|
-
// skipped — the caller records it so a later length failure can say the
|
|
202
|
-
// path was not the one Nx wanted.
|
|
203
|
-
return index === 0
|
|
204
|
-
? { root: tier.root }
|
|
205
|
-
: { root: tier.root, preferred: tiers[0].root };
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return undefined;
|
|
209
|
-
}
|
|
210
|
-
/** Directories that may not *be* the socket directory, and why. */
|
|
211
|
-
function dirsUnusableAsSocketDir() {
|
|
212
|
-
const onWindows = process.platform === 'win32';
|
|
213
|
-
return [
|
|
214
|
-
// Refused even when no peer can reach it: a configured directory becomes the
|
|
215
|
-
// socket directory itself, and `removeSocketDir` deletes that recursively —
|
|
216
|
-
// here, the user's whole temp directory.
|
|
217
|
-
{
|
|
218
|
-
dir: tmp_1.tmpdir,
|
|
219
|
-
reason: (0, owned_private_dir_1.isPeerWritable)(tmp_1.tmpdir)
|
|
220
|
-
? 'shared-with-other-users'
|
|
221
|
-
: 'os-temp-root',
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
dir: nx_tmp_dir_1.NX_TMP_DIR,
|
|
225
|
-
reason: (0, owned_private_dir_1.isPeerWritable)(nx_tmp_dir_1.NX_TMP_DIR)
|
|
226
|
-
? 'shared-with-other-users'
|
|
227
|
-
: 'nx-managed',
|
|
228
|
-
},
|
|
229
|
-
{ dir: nx_tmp_dir_1.NX_USER_TMP_DIR, reason: 'nx-managed' },
|
|
230
|
-
{ dir: defaultSocketRoot(), reason: 'nx-managed' },
|
|
231
|
-
// Skipped on Windows, where the home tier is never offered.
|
|
232
|
-
...(!onWindows && nx_tmp_dir_1.NX_HOME_TMP_DIR
|
|
233
|
-
? [
|
|
234
|
-
{ dir: nx_tmp_dir_1.NX_HOME_TMP_DIR, reason: 'nx-managed' },
|
|
235
|
-
{ dir: homeSocketRoot(), reason: 'nx-managed' },
|
|
236
|
-
]
|
|
237
|
-
: []),
|
|
238
|
-
{ dir: native_file_cache_location_1.NATIVE_CACHE_ROOT, reason: 'nx-managed' },
|
|
239
|
-
];
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* The configured socket dir, normalized. `resolve` strips a trailing slash,
|
|
243
|
-
* which would otherwise defeat the `O_NOFOLLOW` guard downstream — this is the
|
|
244
|
-
* one socket path built from user input rather than by `join`.
|
|
245
|
-
*
|
|
246
|
-
* `||` rather than `??`: an empty value means unset. An empty string survives
|
|
247
|
-
* `??`, and `resolve('')` is the working directory — which `removeSocketDir`
|
|
248
|
-
* then deletes recursively. `NX_SOCKET_DIR=` with no value is ordinary in a
|
|
249
|
-
* .env file or a compose environment list.
|
|
250
|
-
*/
|
|
251
|
-
function configuredSocketDir() {
|
|
252
|
-
const dir = process.env.NX_SOCKET_DIR || process.env.NX_DAEMON_SOCKET_DIR;
|
|
253
|
-
return dir ? (0, path_1.resolve)(dir) : undefined;
|
|
254
|
-
}
|
|
255
|
-
function socketDirName(root) {
|
|
256
|
-
const hasher = (0, crypto_1.createHash)('sha256');
|
|
257
|
-
hasher.update(workspace_root_1.workspaceRoot.toLowerCase());
|
|
258
|
-
hasher.update(String(process.pid));
|
|
259
|
-
const unique = hasher.digest('hex').substring(0, 20);
|
|
260
|
-
return (0, path_1.join)(root, unique);
|
|
261
|
-
}
|
|
262
|
-
function pluginSocketDirName(root) {
|
|
263
|
-
// Short so the socket file name still fits under assertValidSocketPath's limit.
|
|
264
|
-
const hash = (0, crypto_1.createHash)('sha256')
|
|
265
|
-
.update(workspace_root_1.workspaceRoot.toLowerCase())
|
|
266
|
-
.digest('hex')
|
|
267
|
-
.substring(0, 8);
|
|
268
|
-
return (0, path_1.join)(root, hash);
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* A socket dir under the first usable root, falling back to the workspace data
|
|
272
|
-
* dir when no root can be established. Either way it is locked to the current
|
|
273
|
-
* user.
|
|
274
|
-
*/
|
|
275
|
-
function socketDirUnderFirstUsableRoot(leafFor) {
|
|
276
|
-
const configuredDir = configuredSocketDir();
|
|
277
|
-
if (configuredDir !== undefined) {
|
|
278
|
-
return createOwnerOnlySocketDir(configuredDir, false);
|
|
279
|
-
}
|
|
280
|
-
// Cleared here too: the no-tier exit below returns without entering
|
|
281
|
-
// createOwnerOnlySocketDir, and a stale value would have assertValidSocketPath
|
|
282
|
-
// blame an NX_SOCKET_DIR the user no longer has set.
|
|
283
|
-
socketDirFallbackCause = undefined;
|
|
284
|
-
refusedConfiguredSocketDir = undefined;
|
|
285
|
-
const refusals = [];
|
|
286
|
-
const established = establishSocketRoot(refusals);
|
|
287
|
-
if (established === undefined) {
|
|
288
|
-
return fallBackToWorkspaceSocketDir(socketRootsUnavailable(refusals), undefined, refusals);
|
|
289
|
-
}
|
|
290
|
-
const dir = createOwnerOnlySocketDir(leafFor(established.root), true, refusals);
|
|
291
|
-
// Only when the directory we actually got is the demoted tier: if
|
|
292
|
-
// createOwnerOnlySocketDir fell back to the workspace, it recorded its own,
|
|
293
|
-
// more specific cause and that one should survive.
|
|
294
|
-
if (established.preferred !== undefined &&
|
|
295
|
-
socketDirFallbackCause === undefined) {
|
|
296
|
-
noteSocketRootDemotion(established.preferred, established.root, refusals);
|
|
297
|
-
}
|
|
298
|
-
return dir;
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Record a successful demotion to a later tier. Verbose, not warn: nothing
|
|
302
|
-
* failed. The cause is still set because `assertValidSocketPath` keys its
|
|
303
|
-
* "run with --verbose" block off it, and without it a later length failure
|
|
304
|
-
* reads as though the user chose the path.
|
|
305
|
-
*
|
|
306
|
-
* `refusals` can name any of the three directories tier 0 establishes, so it is
|
|
307
|
-
* not always `preferred`.
|
|
308
|
-
*/
|
|
309
|
-
function noteSocketRootDemotion(preferred, used, refusals) {
|
|
310
|
-
socketDirFallbackCause = new Error(`Nx could not establish its preferred socket root ${preferred}, so it used ${used}.`);
|
|
311
|
-
// Lazily required for the import-cycle reason spelled out in
|
|
312
|
-
// fallBackToWorkspaceSocketDir below.
|
|
313
|
-
const { logger } = require('../utils/logger');
|
|
314
|
-
logger.verbose(`Nx could not use the default socket directory ${preferred}. Using ${used} instead.` +
|
|
315
|
-
(refusals.length
|
|
316
|
-
? ` ${refusals.map((r) => (0, owned_private_dir_1.describeRefusal)(r)).join('; ')}.`
|
|
317
|
-
: ''));
|
|
318
|
-
}
|
|
319
|
-
function getSocketDir() {
|
|
320
|
-
return socketDirUnderFirstUsableRoot(socketDirName);
|
|
321
|
-
}
|
|
322
|
-
/**
|
|
323
|
-
* Plugin worker sockets get their own workspace-scoped directory rather than
|
|
324
|
-
* sitting in the shared system temp dir, which cannot be locked down.
|
|
325
|
-
*/
|
|
326
|
-
function getPluginSocketDir() {
|
|
327
|
-
return socketDirUnderFirstUsableRoot(pluginSocketDirName);
|
|
328
|
-
}
|
|
329
|
-
let socketDirFallbackCause;
|
|
330
|
-
let refusedConfiguredSocketDir;
|
|
331
101
|
let warnedAboutWorkspaceFallback = false;
|
|
332
102
|
let warnedAboutConfiguredSocketDir = false;
|
|
333
103
|
/**
|
|
@@ -339,139 +109,157 @@ function resetSocketDirWarningsForTesting() {
|
|
|
339
109
|
warnedAboutWorkspaceFallback = false;
|
|
340
110
|
warnedAboutConfiguredSocketDir = false;
|
|
341
111
|
}
|
|
342
|
-
function getSocketDirFallbackCause() {
|
|
343
|
-
return socketDirFallbackCause;
|
|
344
|
-
}
|
|
345
112
|
/**
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
113
|
+
* Required lazily, and only where something is being reported. A static import
|
|
114
|
+
* closes a cycle — utils/logger reads `serverLogger` from daemon/logger while it
|
|
115
|
+
* is still evaluating, and daemon/logger imports this module — which throws
|
|
116
|
+
* whenever `isOnDaemon()` is true as this module loads. Production escapes it
|
|
117
|
+
* only because server.ts sets `global.NX_DAEMON` after its imports, so daemon
|
|
118
|
+
* boot rests on import order.
|
|
351
119
|
*/
|
|
352
|
-
function
|
|
353
|
-
return
|
|
120
|
+
function lazyLogger() {
|
|
121
|
+
return require('../utils/logger')
|
|
122
|
+
.logger;
|
|
354
123
|
}
|
|
355
124
|
/**
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
* case Nx verifies the stable shared container and establishes the
|
|
359
|
-
* current user's owner-only roots first.
|
|
360
|
-
* @param priorRefusals why the tiers above this one were skipped. Ignored unless
|
|
361
|
-
* `usingDefaultRoot`.
|
|
125
|
+
* Report one resolution and hand back the socket path. The native side decides
|
|
126
|
+
* where it is and why; every sentence about it is written here.
|
|
362
127
|
*/
|
|
363
|
-
function
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
// Outside the try so it is not swallowed by the fallback. Exact matches only,
|
|
367
|
-
// so the per-user directories under those roots never trip it.
|
|
368
|
-
const canonical = (0, owned_private_dir_1.canonicalDir)(dir);
|
|
369
|
-
const unusable = dirsUnusableAsSocketDir().find((d) => canonical === (0, owned_private_dir_1.canonicalDir)(d.dir));
|
|
370
|
-
if (unusable) {
|
|
371
|
-
throw new InvalidSocketDirConfigured(dir, unusable.reason);
|
|
372
|
-
}
|
|
373
|
-
try {
|
|
374
|
-
// A default root has already had its containment established by the tier it
|
|
375
|
-
// came from; a configured one is the user's to create.
|
|
376
|
-
if (!usingDefaultRoot) {
|
|
377
|
-
(0, node_fs_1.mkdirSync)((0, path_1.dirname)(dir), { recursive: true });
|
|
378
|
-
}
|
|
379
|
-
// Separately from its parents: mkdirSync does not throw on a pre-planted
|
|
380
|
-
// symlink, so creating and locking down in one step would adopt it.
|
|
381
|
-
const created = (0, owned_private_dir_1.ensureOwnedPrivateDir)(dir);
|
|
382
|
-
if (created.status === 'refused') {
|
|
383
|
-
throw new owned_private_dir_1.DirectoryRefusedError(created.refusal);
|
|
384
|
-
}
|
|
385
|
-
return dir;
|
|
128
|
+
function adopt(details) {
|
|
129
|
+
if (details.invalidReason) {
|
|
130
|
+
throw new InvalidSocketDirConfigured(details.path, details.invalidReason);
|
|
386
131
|
}
|
|
387
|
-
|
|
388
|
-
const refusals = e instanceof owned_private_dir_1.DirectoryRefusedError
|
|
389
|
-
? [...priorRefusals, e.refusal]
|
|
390
|
-
: priorRefusals;
|
|
391
|
-
// Recoverable: fall back to the owner-controlled workspace data dir.
|
|
392
|
-
if (usingDefaultRoot) {
|
|
393
|
-
return fallBackToWorkspaceSocketDir(refusals.length ? socketRootsUnavailable(refusals) : e, dir, refusals);
|
|
394
|
-
}
|
|
132
|
+
if (details.refusedConfiguredDir !== undefined) {
|
|
395
133
|
// Never swap out a configured directory silently — the substitute is longer
|
|
396
134
|
// and would resurface as a length complaint about a path the user never set.
|
|
397
135
|
// Latched: a task-per-PseudoTerminal command resolves this once per task.
|
|
398
136
|
if (!warnedAboutConfiguredSocketDir) {
|
|
399
137
|
warnedAboutConfiguredSocketDir = true;
|
|
400
|
-
console.warn(`Nx could not use the configured socket directory ${
|
|
138
|
+
console.warn(`Nx could not use the configured socket directory ${details.refusedConfiguredDir}: ${details.refusalError}\nFalling back to ${details.path}.`);
|
|
139
|
+
}
|
|
140
|
+
return assertWithinBudget(details);
|
|
141
|
+
}
|
|
142
|
+
if (details.usedWorkspaceFallback) {
|
|
143
|
+
const logger = lazyLogger();
|
|
144
|
+
logger.verbose(`Nx could not use the default socket ${details.attemptedDir
|
|
145
|
+
? `directory ${details.attemptedDir}`
|
|
146
|
+
: 'directories'}. Falling back to ${details.path}.`, fallbackCause(details));
|
|
147
|
+
// Warned rather than verbose: the workspace path grows with checkout depth,
|
|
148
|
+
// so this is where the 95-character budget is most likely to trip, and an
|
|
149
|
+
// allowlist scoped to Nx's usual roots no longer covers it.
|
|
150
|
+
//
|
|
151
|
+
// Latched once per process — neither socket-dir accessor is memoized and one
|
|
152
|
+
// CLI process resolves several, so without it a single command repeats this
|
|
153
|
+
// many times.
|
|
154
|
+
if (!warnedAboutWorkspaceFallback) {
|
|
155
|
+
warnedAboutWorkspaceFallback = true;
|
|
156
|
+
logger.warn([
|
|
157
|
+
`Nx could not use any of its usual socket directories and fell back to ${details.path}.`,
|
|
158
|
+
...details.remedies,
|
|
159
|
+
(0, is_sandbox_1.isSandbox)()
|
|
160
|
+
? // NX_HOME_TMP_DIR is undefined when there is no home directory,
|
|
161
|
+
// which is itself one reason this fallback is reached.
|
|
162
|
+
`A sandbox allowlist covering only ${[nx_tmp_dir_1.NX_TMP_DIR, nx_tmp_dir_1.NX_HOME_TMP_DIR]
|
|
163
|
+
.filter(Boolean)
|
|
164
|
+
.join(' or ')} does not cover this path.`
|
|
165
|
+
: undefined,
|
|
166
|
+
'Run with --verbose to see why the others were rejected.',
|
|
167
|
+
]
|
|
168
|
+
.filter(Boolean)
|
|
169
|
+
.join(' '));
|
|
401
170
|
}
|
|
402
|
-
|
|
403
|
-
// telling someone to shorten an NX_SOCKET_DIR that was refused for another
|
|
404
|
-
// reason.
|
|
405
|
-
refusedConfiguredSocketDir = dir;
|
|
406
|
-
return establishWorkspaceSocketDir(e);
|
|
171
|
+
return assertWithinBudget(details);
|
|
407
172
|
}
|
|
173
|
+
if (details.demotedFrom !== undefined) {
|
|
174
|
+
// Verbose, not warn: nothing failed.
|
|
175
|
+
lazyLogger().verbose(`Nx could not use the default socket directory ${details.demotedFrom}. Using ${details.path} instead.` +
|
|
176
|
+
(details.refusalDetails ? ` ${details.refusalDetails}.` : ''));
|
|
177
|
+
}
|
|
178
|
+
return assertWithinBudget(details);
|
|
408
179
|
}
|
|
409
180
|
/**
|
|
410
|
-
*
|
|
411
|
-
*
|
|
412
|
-
* rather than being flattened into the message that `--verbose` prints.
|
|
181
|
+
* Why this is not the preferred location, or `undefined` when it is. Kept as
|
|
182
|
+
* the error's `cause` so `--verbose` can show what the sentence summarizes.
|
|
413
183
|
*/
|
|
414
|
-
function
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
184
|
+
function fallbackCause(details) {
|
|
185
|
+
if (details.refusalError !== undefined) {
|
|
186
|
+
return new Error(details.refusalError);
|
|
187
|
+
}
|
|
188
|
+
if (details.usedWorkspaceFallback) {
|
|
189
|
+
return new Error(`Nx could not establish any of its default socket directories${details.refusalDetails ? `: ${details.refusalDetails}` : ''}.`);
|
|
190
|
+
}
|
|
191
|
+
if (details.demotedFrom !== undefined) {
|
|
192
|
+
return new Error(`Nx could not establish its preferred socket root ${details.demotedFrom}, so it used ${details.dir}.`);
|
|
193
|
+
}
|
|
194
|
+
return undefined;
|
|
418
195
|
}
|
|
419
196
|
/**
|
|
420
|
-
* The
|
|
421
|
-
*
|
|
197
|
+
* The native side measures; the sentence is decided here, from the same
|
|
198
|
+
* resolution that produced the path. Which advice is correct turns on *why*
|
|
199
|
+
* the path is long: a user who already set `NX_SOCKET_DIR` and had it refused
|
|
200
|
+
* for an unrelated reason is not helped by being told to set a shorter one.
|
|
422
201
|
*/
|
|
423
|
-
function
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
// import closes a cycle — utils/logger reads `serverLogger` from
|
|
427
|
-
// daemon/logger while it is still evaluating, and daemon/logger imports this
|
|
428
|
-
// module — which throws whenever `isOnDaemon()` is true as this module loads.
|
|
429
|
-
// Production escapes it only because server.ts sets `global.NX_DAEMON` after
|
|
430
|
-
// its imports, so daemon boot rests on import order.
|
|
431
|
-
const { logger } = require('../utils/logger');
|
|
432
|
-
logger.verbose(`Nx could not use the default socket ${attempted ? `directory ${attempted}` : 'directories'}. Falling back to ${exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE}.`, cause);
|
|
433
|
-
// Warned rather than verbose: the workspace path grows with checkout depth, so
|
|
434
|
-
// this is where the 95-character budget is most likely to trip, and an
|
|
435
|
-
// allowlist scoped to Nx's usual roots no longer covers it.
|
|
436
|
-
//
|
|
437
|
-
// Latched once per process — neither socket-dir accessor is memoized and one
|
|
438
|
-
// CLI process resolves several, so without it a single command repeats this
|
|
439
|
-
// many times.
|
|
440
|
-
if (!warnedAboutWorkspaceFallback) {
|
|
441
|
-
warnedAboutWorkspaceFallback = true;
|
|
442
|
-
logger.warn([
|
|
443
|
-
`Nx could not use any of its usual socket directories and fell back to ${exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE}.`,
|
|
444
|
-
...new Set(refusals.map(owned_private_dir_1.remedyFor).filter(Boolean)),
|
|
445
|
-
(0, is_sandbox_1.isSandbox)()
|
|
446
|
-
? // NX_HOME_TMP_DIR is undefined when there is no home directory,
|
|
447
|
-
// which is itself one reason this fallback is reached.
|
|
448
|
-
`A sandbox allowlist covering only ${[nx_tmp_dir_1.NX_TMP_DIR, nx_tmp_dir_1.NX_HOME_TMP_DIR]
|
|
449
|
-
.filter(Boolean)
|
|
450
|
-
.join(' or ')} does not cover this path.`
|
|
451
|
-
: undefined,
|
|
452
|
-
'Run with --verbose to see why the others were rejected.',
|
|
453
|
-
]
|
|
454
|
-
.filter(Boolean)
|
|
455
|
-
.join(' '));
|
|
202
|
+
function assertWithinBudget(details) {
|
|
203
|
+
if (!details.tooLong) {
|
|
204
|
+
return details.path;
|
|
456
205
|
}
|
|
457
|
-
|
|
206
|
+
const cause = fallbackCause(details);
|
|
207
|
+
throw new Error([
|
|
208
|
+
'Attempted to open socket that exceeds the maximum socket length.',
|
|
209
|
+
// A refused NX_SOCKET_DIR also lands in the workspace, but no default
|
|
210
|
+
// directory was tried, and the line below already names the cause.
|
|
211
|
+
...((details.usedWorkspaceFallback &&
|
|
212
|
+
details.refusedConfiguredDir === undefined) ||
|
|
213
|
+
details.demotedFrom !== undefined
|
|
214
|
+
? [
|
|
215
|
+
`Nx fell back to ${details.dir} because the default socket directory could not be used.`,
|
|
216
|
+
'Run the command with --verbose to see why the default directory was rejected.',
|
|
217
|
+
]
|
|
218
|
+
: []),
|
|
219
|
+
'',
|
|
220
|
+
details.refusedConfiguredDir === undefined
|
|
221
|
+
? `Set NX_SOCKET_DIR to a shorter path (e.g. ${process.platform === 'win32' ? '%TMP%/nx-tmp' : '/tmp/nx-tmp'}) to avoid this issue.`
|
|
222
|
+
: // Saying "set a shorter path" here would be advice they already
|
|
223
|
+
// followed: they set one, and it was refused for another reason.
|
|
224
|
+
`The directory set in NX_SOCKET_DIR (${details.refusedConfiguredDir}) could not be used — see the warning above — so Nx fell back to a longer path.\nPoint NX_SOCKET_DIR at a short directory your user owns.`,
|
|
225
|
+
].join('\n'), cause === undefined ? undefined : { cause });
|
|
458
226
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
227
|
+
/** The daemon's own socket. */
|
|
228
|
+
function getDaemonSocketPath() {
|
|
229
|
+
return adopt((0, native_1.resolveDaemonSocketPath)(workspace_root_1.workspaceRoot));
|
|
230
|
+
}
|
|
231
|
+
/** A forked task process's socket, in the daemon's per-run directory. */
|
|
232
|
+
function getForkedProcessSocketPath(id) {
|
|
233
|
+
return adopt((0, native_1.resolveForkedProcessSocketPath)(workspace_root_1.workspaceRoot, id));
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Plugin worker sockets get their own workspace-scoped directory rather than
|
|
237
|
+
* sitting in the shared system temp dir, which cannot be locked down.
|
|
238
|
+
*/
|
|
239
|
+
function getPluginSocketPath(id) {
|
|
240
|
+
return adopt((0, native_1.resolvePluginSocketPath)(workspace_root_1.workspaceRoot, id));
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Where the Nx Console socket lives, for whoever binds or connects to it.
|
|
244
|
+
*
|
|
245
|
+
* Nx Console is the server here and Nx the client, so the extension listens
|
|
246
|
+
* where this says and Nx dials it.
|
|
247
|
+
*
|
|
248
|
+
* @param workspaceRootOverride the workspace to resolve for, when the caller is
|
|
249
|
+
* not itself running inside it (Nx Console runs in the editor's extension host)
|
|
250
|
+
* @param env the environment to read `NX_SOCKET_DIR` from, for callers that
|
|
251
|
+
* load a workspace `.env` into a copy rather than into `process.env`
|
|
252
|
+
*/
|
|
253
|
+
function getNxConsoleSocketPath(workspaceRootOverride, env) {
|
|
254
|
+
return adopt((0, native_1.resolveNxConsoleSocketPath)(workspaceRootOverride ?? workspace_root_1.workspaceRoot, env));
|
|
471
255
|
}
|
|
472
256
|
function removeSocketDir() {
|
|
473
257
|
try {
|
|
474
|
-
|
|
258
|
+
// The directory, not the socket: the daemon's whole per-run directory goes.
|
|
259
|
+
(0, node_fs_1.rmSync)((0, native_1.resolveDaemonSocketPath)(workspace_root_1.workspaceRoot).dir, {
|
|
260
|
+
recursive: true,
|
|
261
|
+
force: true,
|
|
262
|
+
});
|
|
475
263
|
}
|
|
476
264
|
catch (e) { }
|
|
477
265
|
}
|
|
@@ -115,7 +115,7 @@ export type { LifeCycle, TaskMetadata } from './tasks-runner/life-cycle';
|
|
|
115
115
|
export { CompositeLifeCycle } from './tasks-runner/life-cycle';
|
|
116
116
|
export { createRunManyDynamicOutputRenderer } from './tasks-runner/life-cycles/dynamic-run-many-terminal-output-life-cycle';
|
|
117
117
|
export type { TaskStatus, TasksRunner } from './tasks-runner/tasks-runner';
|
|
118
|
-
export type {
|
|
118
|
+
export type { NormalizedTargetDependencyConfig } from './tasks-runner/utils';
|
|
119
119
|
export { calculateReverseDeps, createTaskId, expandDependencyConfigSyntaxSugar, expandInitiatingTasksThroughNoop, expandWildcardTargetConfiguration, getCliPath, getCustomHasher, getDependencyConfigs, getExecutorForTask, getExecutorNameForTask, getOutputs, getPrintableCommandArgsForTask, getSerializedArgsForTask, getTargetConfigurationForTask, getUnparsedOverrideArgs, isCacheableTask, normalizeDependencyConfigDefinition, normalizeDependencyConfigProjects, normalizeTargetDependencyWithStringProjects, readProjectAndTargetFromTargetString, removeTasksFromTaskGraph, shouldStreamOutput, transformLegacyOutputs, unparse, validateOutputs, } from './tasks-runner/utils';
|
|
120
120
|
export { getLastValueFromAsyncIterableIterator } from './utils/async-iterator';
|
|
121
121
|
export { workspaceDataDirectory } from './utils/cache-directory';
|
|
@@ -251,6 +251,7 @@ Object.defineProperty(exports, "expandDependencyConfigSyntaxSugar", { enumerable
|
|
|
251
251
|
Object.defineProperty(exports, "expandInitiatingTasksThroughNoop", { enumerable: true, get: function () { return utils_2.expandInitiatingTasksThroughNoop; } });
|
|
252
252
|
Object.defineProperty(exports, "expandWildcardTargetConfiguration", { enumerable: true, get: function () { return utils_2.expandWildcardTargetConfiguration; } });
|
|
253
253
|
Object.defineProperty(exports, "getCliPath", { enumerable: true, get: function () { return utils_2.getCliPath; } });
|
|
254
|
+
// TODO(v25): remove custom hasher support.
|
|
254
255
|
Object.defineProperty(exports, "getCustomHasher", { enumerable: true, get: function () { return utils_2.getCustomHasher; } });
|
|
255
256
|
Object.defineProperty(exports, "getDependencyConfigs", { enumerable: true, get: function () { return utils_2.getDependencyConfigs; } });
|
|
256
257
|
Object.defineProperty(exports, "getExecutorForTask", { enumerable: true, get: function () { return utils_2.getExecutorForTask; } });
|
|
@@ -3,21 +3,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.hashArray = hashArray;
|
|
4
4
|
exports.hashObject = hashObject;
|
|
5
5
|
exports.hashFile = hashFile;
|
|
6
|
+
// Loaded on first use; a top-level import breaks unit tests in the Nx repo.
|
|
7
|
+
// Cached because each `require` call re-resolves the module.
|
|
8
|
+
let native;
|
|
9
|
+
function getNative() {
|
|
10
|
+
return (native ??= require('../native'));
|
|
11
|
+
}
|
|
6
12
|
function hashArray(content) {
|
|
7
|
-
|
|
8
|
-
const { hashArray } = require('../native');
|
|
9
|
-
return hashArray(content);
|
|
13
|
+
return getNative().hashArray(content);
|
|
10
14
|
}
|
|
11
15
|
function hashObject(obj) {
|
|
12
|
-
const { hashArray } = require('../native');
|
|
13
16
|
const parts = [];
|
|
14
17
|
for (const key of Object.keys(obj ?? {}).sort()) {
|
|
15
18
|
parts.push(key);
|
|
16
19
|
parts.push(JSON.stringify(obj[key]));
|
|
17
20
|
}
|
|
18
|
-
return hashArray(parts);
|
|
21
|
+
return getNative().hashArray(parts);
|
|
19
22
|
}
|
|
20
23
|
function hashFile(filePath) {
|
|
21
|
-
|
|
22
|
-
return hashFile(filePath);
|
|
24
|
+
return getNative().hashFile(filePath);
|
|
23
25
|
}
|
|
@@ -9,6 +9,7 @@ const native_1 = require("../native");
|
|
|
9
9
|
const project_graph_1 = require("../project-graph/project-graph");
|
|
10
10
|
const task_io_service_1 = require("../tasks-runner/task-io-service");
|
|
11
11
|
const task_env_1 = require("../tasks-runner/task-env");
|
|
12
|
+
// TODO(v25): remove custom hasher support.
|
|
12
13
|
const utils_1 = require("../tasks-runner/utils");
|
|
13
14
|
const db_connection_1 = require("../utils/db-connection");
|
|
14
15
|
const task_hasher_1 = require("./task-hasher");
|
|
@@ -32,6 +33,7 @@ async function hashTasksThatDoNotDependOnOutputsOfOtherTasks(hasher, projectGrap
|
|
|
32
33
|
? new Set((0, native_1.getUltracacheDeferredTaskIds)(ultracacheConfigurations, taskGraph))
|
|
33
34
|
: null;
|
|
34
35
|
const tasks = Object.values(taskGraph.tasks);
|
|
36
|
+
// TODO(v25): remove custom hasher support.
|
|
35
37
|
const tasksWithHashers = await Promise.all(tasks.map(async (task) => {
|
|
36
38
|
const customHasher = (0, utils_1.getCustomHasher)(task, projects);
|
|
37
39
|
return { task, customHasher };
|
|
@@ -81,6 +83,7 @@ function readsDependencyOutputs(task, taskGraph, projectGraph, nxJson) {
|
|
|
81
83
|
async function hashTask(hasher, projectGraph, taskGraph, task, env, taskDetails) {
|
|
82
84
|
performance.mark('hashSingleTask:start');
|
|
83
85
|
const projectsConfigurations = (0, project_graph_1.readProjectsConfigurationFromProjectGraph)(projectGraph);
|
|
86
|
+
// TODO(v25): remove custom hasher support.
|
|
84
87
|
const customHasher = (0, utils_1.getCustomHasher)(task, projectsConfigurations.projects);
|
|
85
88
|
const { value, details, inputs } = await (customHasher
|
|
86
89
|
? customHasher(task, {
|
|
@@ -126,6 +129,7 @@ async function hashTasks(hasher, projectGraph, taskGraph, perTaskEnvs, taskDetai
|
|
|
126
129
|
const projectsConfigurations = (0, project_graph_1.readProjectsConfigurationFromProjectGraph)(projectGraph);
|
|
127
130
|
const nxJson = (0, nx_json_1.readNxJson)();
|
|
128
131
|
const tasks = (tasksToHashOverride ?? Object.values(taskGraph.tasks)).filter((task) => !task.hash);
|
|
132
|
+
// TODO(v25): remove custom hasher support.
|
|
129
133
|
// Separate tasks with custom hashers from those without
|
|
130
134
|
const tasksWithCustomHashers = [];
|
|
131
135
|
const tasksWithoutCustomHashers = [];
|