nx 23.2.0-beta.7 → 23.2.0-beta.8
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/dist/bin/init-local.js +3 -5
- package/dist/release/changelog-renderer/index.js +4 -5
- package/dist/src/adapter/ngcli-adapter.js +60 -1
- package/dist/src/command-line/completion/command-object.js +7 -12
- package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +17 -23
- package/dist/src/command-line/generate/generate.js +35 -35
- package/dist/src/command-line/import/import.js +18 -40
- package/dist/src/command-line/init/ai-agent-prompts.js +5 -11
- package/dist/src/command-line/init/command-object.js +4 -2
- package/dist/src/command-line/init/implementation/add-nx-to-monorepo.js +12 -32
- package/dist/src/command-line/init/implementation/add-nx-to-nest.js +8 -21
- package/dist/src/command-line/init/implementation/add-nx-to-npm-repo.js +8 -21
- package/dist/src/command-line/init/implementation/angular/index.js +6 -15
- package/dist/src/command-line/init/init-v1.js +8 -18
- package/dist/src/command-line/init/init-v2.js +26 -56
- package/dist/src/command-line/migrate/agentic/definitions.js +35 -8
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +22 -3
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +13 -10
- package/dist/src/command-line/migrate/agentic/handoff.d.ts +3 -1
- package/dist/src/command-line/migrate/agentic/handoff.js +4 -2
- package/dist/src/command-line/migrate/agentic/run-step.js +1 -0
- package/dist/src/command-line/migrate/agentic/runner.js +24 -24
- package/dist/src/command-line/migrate/agentic/select.js +20 -30
- package/dist/src/command-line/migrate/agentic/types.d.ts +19 -3
- package/dist/src/command-line/migrate/agentic/types.js +13 -4
- package/dist/src/command-line/migrate/command-object.d.ts +3 -0
- package/dist/src/command-line/migrate/command-object.js +13 -0
- package/dist/src/command-line/migrate/execute-migration.d.ts +14 -0
- package/dist/src/command-line/migrate/execute-migration.js +30 -9
- package/dist/src/command-line/migrate/migrate-analytics.d.ts +28 -0
- package/dist/src/command-line/migrate/migrate-analytics.js +51 -1
- package/dist/src/command-line/migrate/migrate-commits.d.ts +8 -2
- package/dist/src/command-line/migrate/migrate-commits.js +66 -19
- package/dist/src/command-line/migrate/migrate-config.d.ts +4 -2
- package/dist/src/command-line/migrate/migrate-config.js +12 -4
- package/dist/src/command-line/migrate/migrate.d.ts +9 -2
- package/dist/src/command-line/migrate/migrate.js +179 -56
- package/dist/src/command-line/migrate/multi-major.js +7 -10
- package/dist/src/command-line/migrate/resolve-package-version.js +4 -8
- package/dist/src/command-line/migrate/run/agent-output.d.ts +20 -0
- package/dist/src/command-line/migrate/run/agent-output.js +65 -0
- package/dist/src/command-line/migrate/run/index.d.ts +7 -0
- package/dist/src/command-line/migrate/run/index.js +19 -1
- package/dist/src/command-line/migrate/run/orchestrator.d.ts +20 -0
- package/dist/src/command-line/migrate/run/orchestrator.js +1200 -0
- package/dist/src/command-line/migrate/run/run-id.d.ts +16 -0
- package/dist/src/command-line/migrate/run/run-id.js +56 -0
- package/dist/src/command-line/migrate/run/run-state.d.ts +157 -0
- package/dist/src/command-line/migrate/run/run-state.js +438 -0
- package/dist/src/command-line/migrate/run/state-lock.d.ts +25 -0
- package/dist/src/command-line/migrate/run/state-lock.js +76 -0
- package/dist/src/command-line/migrate/run/state-machine.d.ts +84 -0
- package/dist/src/command-line/migrate/run/state-machine.js +315 -0
- package/dist/src/command-line/migrate/run/util.d.ts +58 -0
- package/dist/src/command-line/migrate/run/util.js +139 -7
- package/dist/src/command-line/migrate/run/worker.d.ts +1 -0
- package/dist/src/command-line/migrate/run/worker.js +308 -45
- package/dist/src/command-line/migrate/safe-prompt.d.ts +14 -26
- package/dist/src/command-line/migrate/safe-prompt.js +32 -43
- package/dist/src/command-line/migrate/step-actions.d.ts +5 -0
- package/dist/src/command-line/migrate/step-actions.js +12 -0
- package/dist/src/command-line/migrate/text.d.ts +18 -0
- package/dist/src/command-line/migrate/text.js +26 -0
- package/dist/src/command-line/migrate/version-skew-guard.d.ts +6 -4
- package/dist/src/command-line/migrate/version-skew-guard.js +34 -14
- package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +16 -20
- package/dist/src/command-line/release/changelog.js +6 -17
- package/dist/src/command-line/release/plan.js +15 -28
- package/dist/src/command-line/release/release.js +6 -17
- package/dist/src/command-line/release/utils/remote-release-clients/github.js +16 -22
- package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +13 -22
- package/dist/src/command-line/release/utils/resolve-semver-specifier.js +19 -41
- package/dist/src/command-line/release/version/resolve-current-version.js +7 -10
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/devkit-internals.d.ts +2 -0
- package/dist/src/devkit-internals.js +12 -4
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.d.ts +12 -0
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.js +250 -0
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.md +50 -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/plugins/js/lock-file/bun-parser.js +43 -10
- package/dist/src/project-graph/utils/project-configuration/target-normalization.js +128 -2
- package/dist/src/tasks-runner/run-command.js +17 -32
- package/dist/src/tasks-runner/task-env.js +5 -2
- package/dist/src/tasks-runner/utils.js +2 -6
- package/dist/src/utils/analytics-prompt.js +4 -7
- package/dist/src/utils/exit-codes.d.ts +9 -0
- package/dist/src/utils/exit-codes.js +19 -0
- package/dist/src/utils/fileutils.d.ts +5 -0
- package/dist/src/utils/fileutils.js +7 -2
- package/dist/src/utils/git-utils.d.ts +33 -2
- package/dist/src/utils/git-utils.js +156 -8
- package/dist/src/utils/long-running-target.d.ts +10 -0
- package/dist/src/utils/long-running-target.js +19 -0
- package/dist/src/utils/min-release-age/behavior/bun.js +7 -11
- package/dist/src/utils/min-release-age/behavior/npm.js +5 -2
- package/dist/src/utils/min-release-age/packument.js +1 -1
- package/dist/src/utils/nx-console-prompt.js +3 -6
- package/dist/src/utils/package-manager-config/bunfig.d.ts +14 -0
- package/dist/src/utils/package-manager-config/bunfig.js +49 -0
- package/dist/src/utils/package-manager-config/npmrc.d.ts +23 -0
- package/dist/src/utils/package-manager-config/npmrc.js +124 -0
- package/dist/src/utils/package-manager-config/pnpm-config.d.ts +17 -0
- package/dist/src/utils/package-manager-config/pnpm-config.js +60 -0
- package/dist/src/utils/package-manager.d.ts +21 -2
- package/dist/src/utils/package-manager.js +244 -47
- package/dist/src/utils/params.d.ts +7 -1
- package/dist/src/utils/params.js +64 -6
- package/dist/src/utils/prompt-helpers.d.ts +53 -0
- package/dist/src/utils/prompt-helpers.js +111 -0
- package/dist/src/utils/provenance.js +6 -14
- package/dist/src/utils/registry-config/bun.d.ts +2 -0
- package/dist/src/utils/registry-config/bun.js +257 -0
- package/dist/src/utils/registry-config/index.d.ts +13 -0
- package/dist/src/utils/registry-config/index.js +102 -0
- package/dist/src/utils/registry-config/pnpm.d.ts +2 -0
- package/dist/src/utils/registry-config/pnpm.js +1551 -0
- package/dist/src/utils/registry-config/utils.d.ts +195 -0
- package/dist/src/utils/registry-config/utils.js +496 -0
- package/dist/src/utils/registry-config/yarn-berry.d.ts +2 -0
- package/dist/src/utils/registry-config/yarn-berry.js +612 -0
- package/dist/src/utils/registry-config/yarn-classic.d.ts +2 -0
- package/dist/src/utils/registry-config/yarn-classic.js +1001 -0
- package/dist/src/utils/safe-spawn.d.ts +24 -0
- package/dist/src/utils/safe-spawn.js +104 -0
- package/migrations.json +6 -0
- package/package.json +17 -13
- package/dist/src/utils/min-release-age/npmrc.d.ts +0 -15
- package/dist/src/utils/min-release-age/npmrc.js +0 -45
|
@@ -5,6 +5,8 @@ exports.normalizeTarget = normalizeTarget;
|
|
|
5
5
|
exports.validateAndNormalizeProjectRootMap = validateAndNormalizeProjectRootMap;
|
|
6
6
|
const executor_utils_1 = require("../../../command-line/run/executor-utils");
|
|
7
7
|
const fileutils_1 = require("../../../utils/fileutils");
|
|
8
|
+
const long_running_target_1 = require("../../../utils/long-running-target");
|
|
9
|
+
const output_1 = require("../../../utils/output");
|
|
8
10
|
const to_project_name_1 = require("../../../config/to-project-name");
|
|
9
11
|
const error_types_1 = require("../../error-types");
|
|
10
12
|
const target_merging_1 = require("./target-merging");
|
|
@@ -65,15 +67,137 @@ function normalizeTarget(target, project, workspaceRoot, projectsMap, errorMsgKe
|
|
|
65
67
|
}
|
|
66
68
|
return target;
|
|
67
69
|
}
|
|
70
|
+
// TODO(v24): remove the legacy target-name cache fallback and its warning.
|
|
71
|
+
// Removal needs a second mechanism for plugin-inferred targets, which the
|
|
72
|
+
// accompanying migration cannot reach.
|
|
73
|
+
/**
|
|
74
|
+
* Whether `target` is cacheable only by way of the legacy name-based fallback:
|
|
75
|
+
* the exact target-name key of `targetDefaults` declares `cache: true`, but an
|
|
76
|
+
* executor key won target-default resolution instead, so the merged target never
|
|
77
|
+
* received it.
|
|
78
|
+
*
|
|
79
|
+
* A `true` result means the user's `cache: true` silently lost, so this doubles
|
|
80
|
+
* as the condition for warning them that the name key is being shadowed.
|
|
81
|
+
*/
|
|
82
|
+
function isLegacyCachedTarget(targetName, targetDefaults, target) {
|
|
83
|
+
// Resolution already decided `cache`, so the name key isn't shadowed.
|
|
84
|
+
if (target.cache !== undefined) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
if (isLongRunningTarget(targetName, target)) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
// Restricted to shadowing, which is narrower than what pre-23 restored: that
|
|
91
|
+
// derivation matched on target name alone, so a name key dropped as
|
|
92
|
+
// incompatible (its entry declared a foreign executor) was cacheable too.
|
|
93
|
+
// Restoring that as well would mean writing `cache` with no key to name in
|
|
94
|
+
// the warning, and no migration able to retire it. Deliberately not covered.
|
|
95
|
+
if (!findShadowingTargetDefaultKey(targetDefaults, target)) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return declaresCacheTrue(targetDefaults?.[targetName]);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Whether the name key declares `cache: true` on an entry that always applies.
|
|
102
|
+
*
|
|
103
|
+
* Filters are deliberately not evaluated. They cannot express a pre-23 config
|
|
104
|
+
* (the filtered array shape postdates the behavior being restored), and a
|
|
105
|
+
* filtered entry declaring `cache` may or may not apply to this project — so
|
|
106
|
+
* rather than guess, a filtered `cache` declares the value unknowable and
|
|
107
|
+
* nothing is restored. Among unfiltered entries the last wins, matching the
|
|
108
|
+
* in-key merge order.
|
|
109
|
+
*/
|
|
110
|
+
function declaresCacheTrue(value) {
|
|
111
|
+
if (!value) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
const entries = Array.isArray(value) ? value : [value];
|
|
115
|
+
let declared;
|
|
116
|
+
for (const entry of entries) {
|
|
117
|
+
// `nx.json` is hand-edited; a null or scalar entry would throw here.
|
|
118
|
+
if (!entry || typeof entry !== 'object')
|
|
119
|
+
continue;
|
|
120
|
+
if (entry.cache === undefined)
|
|
121
|
+
continue;
|
|
122
|
+
if (entry.filter)
|
|
123
|
+
return false;
|
|
124
|
+
declared = entry.cache;
|
|
125
|
+
}
|
|
126
|
+
return declared === true;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The normalization-time half of the pre-23 `longRunningTask` guard, which kept
|
|
130
|
+
* `cacheableOperations` from ever making these cacheable. Its remaining clause
|
|
131
|
+
* — `task.overrides['watch']` — is a runtime invocation override with no target
|
|
132
|
+
* equivalent, so it has no counterpart here.
|
|
133
|
+
*/
|
|
134
|
+
function isLongRunningTarget(targetName, target) {
|
|
135
|
+
return !!target.continuous || (0, long_running_target_1.isLongRunningTargetName)(targetName);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The `targetDefaults` key that beat the target-name key for `target`. Only an
|
|
139
|
+
* executor key can: key precedence puts the exact target name ahead of every
|
|
140
|
+
* glob, so nothing else outranks it. Undefined when the name key lost for
|
|
141
|
+
* another reason (e.g. its entry declared a foreign executor and was dropped as
|
|
142
|
+
* incompatible) — see {@link isLegacyCachedTarget} for why that case is left
|
|
143
|
+
* alone even though pre-23 restored it.
|
|
144
|
+
*
|
|
145
|
+
* `hasOwnProperty` rather than a lookup: an executor named `__proto__` resolves
|
|
146
|
+
* through the prototype chain to a truthy object, which would report a key the
|
|
147
|
+
* user never wrote.
|
|
148
|
+
*/
|
|
149
|
+
function findShadowingTargetDefaultKey(targetDefaults, target) {
|
|
150
|
+
return target.executor &&
|
|
151
|
+
targetDefaults &&
|
|
152
|
+
Object.prototype.hasOwnProperty.call(targetDefaults, target.executor)
|
|
153
|
+
? target.executor
|
|
154
|
+
: undefined;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Emits a single grouped warning for every (shadowing key, target-name key)
|
|
158
|
+
* pair that relied on the deprecated fallback. Grouping matters because the
|
|
159
|
+
* same pair recurs in every affected project — a per-target warning would
|
|
160
|
+
* print hundreds of identical lines in a large workspace.
|
|
161
|
+
*/
|
|
162
|
+
function warnAboutLegacyCachedTargets(legacyCacheReads) {
|
|
163
|
+
if (legacyCacheReads.size === 0) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const bodyLines = [];
|
|
167
|
+
for (const [shadowingKey, targetKeys] of legacyCacheReads) {
|
|
168
|
+
for (const targetKey of targetKeys) {
|
|
169
|
+
bodyLines.push(` - "${shadowingKey}" does not set "cache", so it was read from "${targetKey}"`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
bodyLines.push('', 'An executor key applies to every target that resolves through it, so exclude any continuous target before setting "cache" on one — a target that is both cacheable and continuous is rejected.', 'Target defaults resolve to a single key rather than merging, so an executor key hides the target name key entirely.', 'Set "cache" on the executor key to keep these targets cacheable — reading it from the target name key is deprecated and will be removed in Nx 24.');
|
|
173
|
+
output_1.output.warn({
|
|
174
|
+
title: 'Some targets are only cacheable through a deprecated fallback.',
|
|
175
|
+
bodyLines,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
68
178
|
function normalizeTargets(project, sourceMaps, nxJsonConfiguration, workspaceRoot,
|
|
69
179
|
/**
|
|
70
180
|
* Project configurations keyed by project name
|
|
71
181
|
*/
|
|
72
|
-
projects
|
|
182
|
+
projects,
|
|
183
|
+
/**
|
|
184
|
+
* Shadowing `targetDefaults` key -> target name keys its `cache` was read
|
|
185
|
+
* from. Accumulated across projects so the deprecation warns once per pair.
|
|
186
|
+
*/
|
|
187
|
+
legacyCacheReads) {
|
|
73
188
|
const targetErrorMessage = [];
|
|
74
189
|
for (const targetName in project.targets) {
|
|
75
190
|
project.targets[targetName] = normalizeTarget(project.targets[targetName], project, workspaceRoot, projects, [project.root, targetName].join(':'));
|
|
76
191
|
const target = project.targets[targetName];
|
|
192
|
+
const targetDefaults = nxJsonConfiguration.targetDefaults;
|
|
193
|
+
if (isLegacyCachedTarget(targetName, targetDefaults, target)) {
|
|
194
|
+
target.cache = true;
|
|
195
|
+
// Always defined: `isLegacyCachedTarget` returns false without it.
|
|
196
|
+
const shadowingKey = findShadowingTargetDefaultKey(targetDefaults, target);
|
|
197
|
+
const targetKeys = legacyCacheReads.get(shadowingKey) ?? new Set();
|
|
198
|
+
targetKeys.add(targetName);
|
|
199
|
+
legacyCacheReads.set(shadowingKey, targetKeys);
|
|
200
|
+
}
|
|
77
201
|
if (
|
|
78
202
|
// If the target has no executor or command, it doesn't do anything
|
|
79
203
|
!target.executor &&
|
|
@@ -106,6 +230,7 @@ function validateAndNormalizeProjectRootMap(workspaceRoot, projectRootMap, nxJso
|
|
|
106
230
|
const conflicts = new Map();
|
|
107
231
|
const projectRootsWithNoName = [];
|
|
108
232
|
const validityErrors = [];
|
|
233
|
+
const legacyCacheReads = new Map();
|
|
109
234
|
for (const root in projectRootMap) {
|
|
110
235
|
const project = projectRootMap[root];
|
|
111
236
|
// We're setting `// targets` as a comment `targets` is empty due to Project Crystal.
|
|
@@ -155,7 +280,7 @@ function validateAndNormalizeProjectRootMap(workspaceRoot, projectRootMap, nxJso
|
|
|
155
280
|
for (const root in projectRootMap) {
|
|
156
281
|
const project = projectRootMap[root];
|
|
157
282
|
try {
|
|
158
|
-
normalizeTargets(project, sourceMaps, nxJsonConfiguration, workspaceRoot, projects);
|
|
283
|
+
normalizeTargets(project, sourceMaps, nxJsonConfiguration, workspaceRoot, projects, legacyCacheReads);
|
|
159
284
|
}
|
|
160
285
|
catch (e) {
|
|
161
286
|
if (e instanceof error_types_1.WorkspaceValidityError) {
|
|
@@ -166,6 +291,7 @@ function validateAndNormalizeProjectRootMap(workspaceRoot, projectRootMap, nxJso
|
|
|
166
291
|
}
|
|
167
292
|
}
|
|
168
293
|
}
|
|
294
|
+
warnAboutLegacyCachedTargets(legacyCacheReads);
|
|
169
295
|
const errors = [];
|
|
170
296
|
if (conflicts.size > 0) {
|
|
171
297
|
errors.push(new error_types_1.MultipleProjectsWithSameNameError(conflicts, projects));
|
|
@@ -8,7 +8,7 @@ exports.constructLifeCycles = constructLifeCycles;
|
|
|
8
8
|
exports.getRunner = getRunner;
|
|
9
9
|
exports.getRunnerOptions = getRunnerOptions;
|
|
10
10
|
const tslib_1 = require("tslib");
|
|
11
|
-
const
|
|
11
|
+
const prompt_helpers_1 = require("../utils/prompt-helpers");
|
|
12
12
|
const node_util_1 = require("node:util");
|
|
13
13
|
const nx_json_1 = require("../config/nx-json");
|
|
14
14
|
const client_1 = require("../daemon/client/client");
|
|
@@ -599,23 +599,19 @@ async function ensureWorkspaceIsInSyncAndGetGraphs(projectGraph, nxJson, project
|
|
|
599
599
|
}
|
|
600
600
|
async function promptForApplyingSyncGeneratorChanges() {
|
|
601
601
|
try {
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
message: 'Would you like to sync the identified changes to get your workspace up to date?',
|
|
602
|
+
// No footer slot, so the opt-out note is part of the message.
|
|
603
|
+
const applyChanges = await (0, prompt_helpers_1.selectPrompt)({
|
|
604
|
+
message: `Would you like to sync the identified changes to get your workspace up to date?${pc.dim('\nYou can skip this prompt by setting the `sync.applyChanges` option to `true` in your `nx.json`.\nFor more information, refer to the docs: https://nx.dev/concepts/sync-generators.')}`,
|
|
606
605
|
choices: [
|
|
606
|
+
{ value: 'yes', label: 'Yes, sync the changes and run the tasks' },
|
|
607
607
|
{
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
},
|
|
611
|
-
{
|
|
612
|
-
name: 'no',
|
|
613
|
-
message: 'No, run the tasks without syncing the changes',
|
|
608
|
+
value: 'no',
|
|
609
|
+
label: 'No, run the tasks without syncing the changes',
|
|
614
610
|
},
|
|
615
611
|
],
|
|
616
|
-
|
|
617
|
-
};
|
|
618
|
-
return
|
|
612
|
+
onCancel: () => process.exit(1),
|
|
613
|
+
});
|
|
614
|
+
return applyChanges === 'yes';
|
|
619
615
|
}
|
|
620
616
|
catch {
|
|
621
617
|
process.exit(1);
|
|
@@ -623,26 +619,15 @@ async function promptForApplyingSyncGeneratorChanges() {
|
|
|
623
619
|
}
|
|
624
620
|
async function confirmRunningTasksWithSyncFailures() {
|
|
625
621
|
try {
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
type: 'autocomplete',
|
|
629
|
-
message: 'Would you like to ignore the sync failures and continue running the tasks?',
|
|
622
|
+
const runTasks = await (0, prompt_helpers_1.selectPrompt)({
|
|
623
|
+
message: `Would you like to ignore the sync failures and continue running the tasks?${pc.dim(`\nWhen running in CI and there are sync failures, the tasks won't run. Addressing the errors above is highly recommended to prevent failures in CI.`)}`,
|
|
630
624
|
choices: [
|
|
631
|
-
{
|
|
632
|
-
|
|
633
|
-
message: 'Yes, ignore the failures and run the tasks',
|
|
634
|
-
},
|
|
635
|
-
{
|
|
636
|
-
name: 'no',
|
|
637
|
-
message: `No, don't run the tasks`,
|
|
638
|
-
},
|
|
625
|
+
{ value: 'yes', label: 'Yes, ignore the failures and run the tasks' },
|
|
626
|
+
{ value: 'no', label: `No, don't run the tasks` },
|
|
639
627
|
],
|
|
640
|
-
|
|
641
|
-
};
|
|
642
|
-
|
|
643
|
-
promptConfig,
|
|
644
|
-
]).then(({ runTasks }) => runTasks === 'yes');
|
|
645
|
-
if (!runTasks) {
|
|
628
|
+
onCancel: () => process.exit(1),
|
|
629
|
+
});
|
|
630
|
+
if (runTasks !== 'yes') {
|
|
646
631
|
process.exit(1);
|
|
647
632
|
}
|
|
648
633
|
}
|
|
@@ -166,10 +166,13 @@ function getOwnerTargetForTask(task, graph) {
|
|
|
166
166
|
for (const targets of Object.values(project.data.metadata.targetGroups)) {
|
|
167
167
|
if (targets.includes(task.target.target)) {
|
|
168
168
|
for (const target of targets) {
|
|
169
|
-
|
|
169
|
+
// a target group can name a target the project does not define —
|
|
170
|
+
// inference plugins build the group and the targets separately, so
|
|
171
|
+
// the two can disagree. skip the phantom rather than dereferencing it.
|
|
172
|
+
if (project.data.targets[target]?.metadata?.nonAtomizedTarget) {
|
|
170
173
|
return [
|
|
171
174
|
target,
|
|
172
|
-
project.data.targets[target]
|
|
175
|
+
project.data.targets[target]?.metadata?.nonAtomizedTarget,
|
|
173
176
|
];
|
|
174
177
|
}
|
|
175
178
|
}
|
|
@@ -36,6 +36,7 @@ const fileutils_1 = require("../utils/fileutils");
|
|
|
36
36
|
const find_matching_projects_1 = require("../utils/find-matching-projects");
|
|
37
37
|
const legacy_depends_on_warning_1 = require("./legacy-depends-on-warning");
|
|
38
38
|
const globs_1 = require("../utils/globs");
|
|
39
|
+
const long_running_target_1 = require("../utils/long-running-target");
|
|
39
40
|
const path_1 = require("../utils/path");
|
|
40
41
|
const serialize_overrides_into_command_line_1 = require("../utils/serialize-overrides-into-command-line");
|
|
41
42
|
const split_target_1 = require("../utils/split-target");
|
|
@@ -429,14 +430,9 @@ function isCacheableTask(task) {
|
|
|
429
430
|
return task.cache;
|
|
430
431
|
}
|
|
431
432
|
function longRunningTask(task) {
|
|
432
|
-
const t = task.target.target;
|
|
433
433
|
return (task.continuous ||
|
|
434
434
|
(!!task.overrides['watch'] && task.overrides['watch'] !== 'false') ||
|
|
435
|
-
|
|
436
|
-
t.endsWith('-watch') ||
|
|
437
|
-
t === 'serve' ||
|
|
438
|
-
t === 'dev' ||
|
|
439
|
-
t === 'start');
|
|
435
|
+
(0, long_running_target_1.isLongRunningTargetName)(task.target.target));
|
|
440
436
|
}
|
|
441
437
|
// TODO: vsavkin remove when nx-cloud doesn't depend on it
|
|
442
438
|
function unparse(options) {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ensureAnalyticsPreferenceSet = ensureAnalyticsPreferenceSet;
|
|
4
4
|
exports.promptForAnalyticsPreference = promptForAnalyticsPreference;
|
|
5
5
|
const fs_1 = require("fs");
|
|
6
|
-
const
|
|
6
|
+
const prompt_helpers_1 = require("./prompt-helpers");
|
|
7
7
|
const path_1 = require("path");
|
|
8
8
|
const output_1 = require("./output");
|
|
9
9
|
const is_ci_1 = require("./is-ci");
|
|
@@ -47,16 +47,13 @@ async function promptForAnalyticsPreference() {
|
|
|
47
47
|
'Learn more: https://cloud.nx.app/privacy',
|
|
48
48
|
],
|
|
49
49
|
});
|
|
50
|
-
|
|
51
|
-
type: 'confirm',
|
|
52
|
-
name: 'enableAnalytics',
|
|
50
|
+
return await (0, prompt_helpers_1.confirmationPrompt)({
|
|
53
51
|
message: 'Share usage data with the Nx team?',
|
|
54
|
-
|
|
52
|
+
// Cancelling is a decline, not a failure.
|
|
53
|
+
onCancel: () => false,
|
|
55
54
|
});
|
|
56
|
-
return enableAnalytics;
|
|
57
55
|
}
|
|
58
56
|
catch {
|
|
59
|
-
// User cancelled - default to false
|
|
60
57
|
return false;
|
|
61
58
|
}
|
|
62
59
|
}
|
|
@@ -13,3 +13,12 @@ export declare const EXPECTED_TERMINATION_SIGNALS: Set<number>;
|
|
|
13
13
|
* Handles both Linux exact-match and macOS strsignal formats (e.g. "Terminated by Hangup: 1").
|
|
14
14
|
*/
|
|
15
15
|
export declare function messageToCode(message: string): number;
|
|
16
|
+
/**
|
|
17
|
+
* Ends the process the way an interrupted process ends: by SIGINT, not by
|
|
18
|
+
* exiting with its conventional code.
|
|
19
|
+
*
|
|
20
|
+
* A shell reports 130 either way, but a parent using waitpid sees
|
|
21
|
+
* `signal: 'SIGINT'` rather than `code: 130`, which is what tools wrapping nx
|
|
22
|
+
* check to tell "the user pressed Ctrl+C" from "the command failed".
|
|
23
|
+
*/
|
|
24
|
+
export declare function exitAsInterrupted(): never;
|
|
@@ -4,6 +4,7 @@ exports.EXPECTED_TERMINATION_SIGNALS = void 0;
|
|
|
4
4
|
exports.signalToCode = signalToCode;
|
|
5
5
|
exports.codeToSignal = codeToSignal;
|
|
6
6
|
exports.messageToCode = messageToCode;
|
|
7
|
+
exports.exitAsInterrupted = exitAsInterrupted;
|
|
7
8
|
/**
|
|
8
9
|
* Translates NodeJS signals to numeric exit code
|
|
9
10
|
* @param signal
|
|
@@ -73,3 +74,21 @@ function messageToCode(message) {
|
|
|
73
74
|
return 1;
|
|
74
75
|
}
|
|
75
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Ends the process the way an interrupted process ends: by SIGINT, not by
|
|
79
|
+
* exiting with its conventional code.
|
|
80
|
+
*
|
|
81
|
+
* A shell reports 130 either way, but a parent using waitpid sees
|
|
82
|
+
* `signal: 'SIGINT'` rather than `code: 130`, which is what tools wrapping nx
|
|
83
|
+
* check to tell "the user pressed Ctrl+C" from "the command failed".
|
|
84
|
+
*/
|
|
85
|
+
function exitAsInterrupted() {
|
|
86
|
+
// Windows has no signal disposition to inherit - `process.kill` there
|
|
87
|
+
// terminates with an arbitrary code - so keep the 128+SIGINT convention.
|
|
88
|
+
if (process.platform !== 'win32') {
|
|
89
|
+
// Any listener would swallow this; the default action is what terminates.
|
|
90
|
+
process.removeAllListeners('SIGINT');
|
|
91
|
+
process.kill(process.pid, 'SIGINT');
|
|
92
|
+
}
|
|
93
|
+
process.exit(130);
|
|
94
|
+
}
|
|
@@ -27,6 +27,11 @@ interface YamlReadOptions {
|
|
|
27
27
|
* Compatibility with JSON.parse behaviour. If true, then duplicate keys in a mapping will override values rather than throwing an error.
|
|
28
28
|
*/
|
|
29
29
|
json?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Resolves only the tags every YAML parser must support, so every scalar stays
|
|
32
|
+
* a string instead of `true` or `12`.
|
|
33
|
+
*/
|
|
34
|
+
failsafe?: boolean;
|
|
30
35
|
}
|
|
31
36
|
/**
|
|
32
37
|
* Reads a YAML file and returns the object the YAML content represents.
|
|
@@ -41,8 +41,13 @@ function readJsonFile(path, options) {
|
|
|
41
41
|
*/
|
|
42
42
|
function readYamlFile(path, options) {
|
|
43
43
|
const content = (0, node_fs_1.readFileSync)(path, 'utf-8');
|
|
44
|
-
const { load } = require('@zkochan/js-yaml');
|
|
45
|
-
|
|
44
|
+
const { load, FAILSAFE_SCHEMA } = require('@zkochan/js-yaml');
|
|
45
|
+
const { failsafe, ...loadOptions } = options ?? {};
|
|
46
|
+
return load(content, {
|
|
47
|
+
...loadOptions,
|
|
48
|
+
...(failsafe ? { schema: FAILSAFE_SCHEMA } : {}),
|
|
49
|
+
filename: path,
|
|
50
|
+
});
|
|
46
51
|
}
|
|
47
52
|
/**
|
|
48
53
|
* Serializes the given data to JSON and writes it to a file.
|
|
@@ -52,8 +52,21 @@ export declare function isShallowRepository(directory?: string): boolean;
|
|
|
52
52
|
*/
|
|
53
53
|
export declare function getFirstCommitSha(directory?: string): string | null;
|
|
54
54
|
export declare function isGitRepository(directory?: string): boolean;
|
|
55
|
+
export type GitRepositoryStatus = 'git' | 'not-git' | 'unknown';
|
|
56
|
+
/**
|
|
57
|
+
* Like `isGitRepository`, but separates "this is not a git repository" from
|
|
58
|
+
* "the probe itself failed" (git not installed, permissions). Callers gating
|
|
59
|
+
* destructive or unverifiable behavior on the answer must fail closed on
|
|
60
|
+
* 'unknown' instead of reading a broken probe as a missing repository.
|
|
61
|
+
*/
|
|
62
|
+
export declare function getGitRepositoryStatus(directory?: string): GitRepositoryStatus;
|
|
55
63
|
export declare function getGitCurrentBranch(directory?: string): string | null;
|
|
56
|
-
export declare function
|
|
64
|
+
export declare function getGitRemoteNames(directory?: string): string[];
|
|
65
|
+
export type WorkingTreeStatus = 'dirty' | 'clean' | 'unknown';
|
|
66
|
+
export declare function getWorkingTreeStatus(directory?: string, excludePaths?: string[]): WorkingTreeStatus;
|
|
67
|
+
export declare function hasUncommittedChanges(directory?: string, excludePaths?: string[]): boolean;
|
|
68
|
+
export type PathCommitExposure = 'ignored' | 'tracked' | 'unignored' | 'unknown';
|
|
69
|
+
export declare function getPathCommitExposure(dirPath: string, directory?: string): PathCommitExposure;
|
|
57
70
|
export declare function getUncommittedChangesSnapshot(directory?: string): string;
|
|
58
71
|
export declare function commitChanges(commitMessage: string, directory?: string): string | null;
|
|
59
72
|
/**
|
|
@@ -66,6 +79,24 @@ export declare function commitChanges(commitMessage: string, directory?: string)
|
|
|
66
79
|
* Returns `null` (rather than throwing) when the commit itself succeeded
|
|
67
80
|
* but `git rev-parse HEAD` failed transiently — by contract the diff is
|
|
68
81
|
* no longer in the working tree, so callers must NOT report it as such.
|
|
82
|
+
*
|
|
83
|
+
* `excludePaths` are `directory`-relative paths the commit must not capture,
|
|
84
|
+
* whatever the ignore rules say. Their working-tree files are left intact;
|
|
85
|
+
* only their index entries are put back to HEAD's state. Paths come from
|
|
86
|
+
* callers' own constants, never from user input.
|
|
69
87
|
*/
|
|
70
|
-
export declare function tryCommitChanges(commitMessage: string, directory: string): string | null;
|
|
88
|
+
export declare function tryCommitChanges(commitMessage: string, directory: string, excludePaths?: string[]): string | null;
|
|
71
89
|
export declare function getLatestCommitSha(directory?: string): string | null;
|
|
90
|
+
/**
|
|
91
|
+
* The shape of a recorded `git rev-parse` output: 40 hex chars, or 64 in a
|
|
92
|
+
* sha256 repository. Anything a caller persists and later interpolates into a
|
|
93
|
+
* command line has to be checked against this first.
|
|
94
|
+
*/
|
|
95
|
+
export declare const GIT_SHA: RegExp;
|
|
96
|
+
/**
|
|
97
|
+
* Whether `ancestor` is reachable from `descendant`, i.e. resetting to
|
|
98
|
+
* `descendant` keeps `ancestor` in history. Returns false when the answer
|
|
99
|
+
* cannot be established (invalid input, not a repository, unknown commits),
|
|
100
|
+
* so callers treat an unverifiable commit as not preserved.
|
|
101
|
+
*/
|
|
102
|
+
export declare function isAncestorCommit(ancestor: string, descendant: string, directory?: string): boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GitRepository = void 0;
|
|
3
|
+
exports.GIT_SHA = exports.GitRepository = void 0;
|
|
4
4
|
exports.cloneFromUpstream = cloneFromUpstream;
|
|
5
5
|
exports.parseVcsRemoteUrl = parseVcsRemoteUrl;
|
|
6
6
|
exports.getVcsRemoteInfo = getVcsRemoteInfo;
|
|
@@ -9,12 +9,17 @@ exports.getGitRootRelativePath = getGitRootRelativePath;
|
|
|
9
9
|
exports.isShallowRepository = isShallowRepository;
|
|
10
10
|
exports.getFirstCommitSha = getFirstCommitSha;
|
|
11
11
|
exports.isGitRepository = isGitRepository;
|
|
12
|
+
exports.getGitRepositoryStatus = getGitRepositoryStatus;
|
|
12
13
|
exports.getGitCurrentBranch = getGitCurrentBranch;
|
|
14
|
+
exports.getGitRemoteNames = getGitRemoteNames;
|
|
15
|
+
exports.getWorkingTreeStatus = getWorkingTreeStatus;
|
|
13
16
|
exports.hasUncommittedChanges = hasUncommittedChanges;
|
|
17
|
+
exports.getPathCommitExposure = getPathCommitExposure;
|
|
14
18
|
exports.getUncommittedChangesSnapshot = getUncommittedChangesSnapshot;
|
|
15
19
|
exports.commitChanges = commitChanges;
|
|
16
20
|
exports.tryCommitChanges = tryCommitChanges;
|
|
17
21
|
exports.getLatestCommitSha = getLatestCommitSha;
|
|
22
|
+
exports.isAncestorCommit = isAncestorCommit;
|
|
18
23
|
const tslib_1 = require("tslib");
|
|
19
24
|
const child_process_1 = require("child_process");
|
|
20
25
|
const crypto = tslib_1.__importStar(require("crypto"));
|
|
@@ -368,6 +373,29 @@ function isGitRepository(directory) {
|
|
|
368
373
|
return false;
|
|
369
374
|
}
|
|
370
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* Like `isGitRepository`, but separates "this is not a git repository" from
|
|
378
|
+
* "the probe itself failed" (git not installed, permissions). Callers gating
|
|
379
|
+
* destructive or unverifiable behavior on the answer must fail closed on
|
|
380
|
+
* 'unknown' instead of reading a broken probe as a missing repository.
|
|
381
|
+
*/
|
|
382
|
+
function getGitRepositoryStatus(directory) {
|
|
383
|
+
try {
|
|
384
|
+
(0, child_process_1.execSync)('git rev-parse --is-inside-work-tree', {
|
|
385
|
+
stdio: 'pipe',
|
|
386
|
+
cwd: directory,
|
|
387
|
+
windowsHide: true,
|
|
388
|
+
// Force untranslated messages; the classification matches on the
|
|
389
|
+
// English "not a git repository".
|
|
390
|
+
env: { ...process.env, LC_ALL: 'C' },
|
|
391
|
+
});
|
|
392
|
+
return 'git';
|
|
393
|
+
}
|
|
394
|
+
catch (err) {
|
|
395
|
+
const stderr = err?.stderr?.toString() ?? '';
|
|
396
|
+
return /not a git repository/i.test(stderr) ? 'not-git' : 'unknown';
|
|
397
|
+
}
|
|
398
|
+
}
|
|
371
399
|
// Checked-out branch name, or null when there isn't one to act on: a detached
|
|
372
400
|
// HEAD reports the literal "HEAD" (treated as no branch), and any git error
|
|
373
401
|
// (not a repo, no commits yet) also yields null.
|
|
@@ -385,21 +413,94 @@ function getGitCurrentBranch(directory) {
|
|
|
385
413
|
return null;
|
|
386
414
|
}
|
|
387
415
|
}
|
|
416
|
+
// Names of the remotes configured in the repository, empty when there are
|
|
417
|
+
// none or the probe itself failed.
|
|
418
|
+
function getGitRemoteNames(directory) {
|
|
419
|
+
try {
|
|
420
|
+
return (0, child_process_1.execSync)('git remote', {
|
|
421
|
+
encoding: 'utf8',
|
|
422
|
+
cwd: directory,
|
|
423
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
424
|
+
windowsHide: true,
|
|
425
|
+
})
|
|
426
|
+
.split('\n')
|
|
427
|
+
.map((name) => name.trim())
|
|
428
|
+
.filter(Boolean);
|
|
429
|
+
}
|
|
430
|
+
catch {
|
|
431
|
+
return [];
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
// Tri-state working-tree probe: 'unknown' means the probe itself failed (git
|
|
435
|
+
// missing, spawn failure, permissions), not that the tree is clean. Callers
|
|
436
|
+
// that gate destructive actions on tree cleanliness must treat 'unknown' as
|
|
437
|
+
// unsafe rather than clean.
|
|
438
|
+
// `excludePaths` are left out of the probe the way `tryCommitChanges` leaves
|
|
439
|
+
// them out of the commit, so a tree dirty only under them reads as clean. An
|
|
440
|
+
// exclude-only pathspec still covers the whole tree, matching `git add -A`.
|
|
441
|
+
function getWorkingTreeStatus(directory, excludePaths = []) {
|
|
442
|
+
const pathspecs = excludePaths
|
|
443
|
+
.map((excludePath) => ` ":(exclude)${excludePath}"`)
|
|
444
|
+
.join('');
|
|
445
|
+
try {
|
|
446
|
+
const out = (0, child_process_1.execSync)(`git status --porcelain${pathspecs ? ` --${pathspecs}` : ''}`, {
|
|
447
|
+
encoding: 'utf8',
|
|
448
|
+
cwd: directory,
|
|
449
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
450
|
+
windowsHide: true,
|
|
451
|
+
});
|
|
452
|
+
return out.trim() === '' ? 'clean' : 'dirty';
|
|
453
|
+
}
|
|
454
|
+
catch {
|
|
455
|
+
return 'unknown';
|
|
456
|
+
}
|
|
457
|
+
}
|
|
388
458
|
// Sync companion to `GitRepository.hasUncommittedChanges` for callers that
|
|
389
|
-
// can't drop into the async class
|
|
390
|
-
//
|
|
391
|
-
function hasUncommittedChanges(directory) {
|
|
459
|
+
// can't drop into the async class. A failed probe reads as false; callers for
|
|
460
|
+
// whom that tolerance is unsafe use `getWorkingTreeStatus` instead.
|
|
461
|
+
function hasUncommittedChanges(directory, excludePaths = []) {
|
|
462
|
+
return getWorkingTreeStatus(directory, excludePaths) === 'dirty';
|
|
463
|
+
}
|
|
464
|
+
// Classifies whether `git add -A` commits made in `directory` can sweep in
|
|
465
|
+
// the directory at `dirPath`. Tracked files stay committable no matter what
|
|
466
|
+
// the ignore rules say (ignore rules never apply to tracked files), so
|
|
467
|
+
// `git ls-files` decides 'tracked' first; `git check-ignore` then splits the
|
|
468
|
+
// untracked remainder into 'ignored' (covered) vs 'unignored' (no
|
|
469
|
+
// coverage). 'unknown' means the probe itself failed (not a git repository,
|
|
470
|
+
// git missing); callers gating destructive behavior on the result must
|
|
471
|
+
// treat it as unsafe.
|
|
472
|
+
function getPathCommitExposure(dirPath, directory) {
|
|
392
473
|
try {
|
|
393
|
-
const
|
|
474
|
+
const tracked = (0, child_process_1.execSync)(`git ls-files -- ${dirPath}`, {
|
|
394
475
|
encoding: 'utf8',
|
|
395
476
|
cwd: directory,
|
|
396
477
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
397
478
|
windowsHide: true,
|
|
398
479
|
});
|
|
399
|
-
|
|
480
|
+
if (tracked.trim() !== '') {
|
|
481
|
+
return 'tracked';
|
|
482
|
+
}
|
|
400
483
|
}
|
|
401
484
|
catch {
|
|
402
|
-
return
|
|
485
|
+
return 'unknown';
|
|
486
|
+
}
|
|
487
|
+
// Query with a trailing slash so git treats the path as a directory even
|
|
488
|
+
// when it does not exist on disk yet: a directory-only ignore rule (a
|
|
489
|
+
// trailing-slash .gitignore entry) does not match a bare query for an
|
|
490
|
+
// absent path, which would misreport covered workspaces as unignored.
|
|
491
|
+
const asDir = dirPath.endsWith('/') ? dirPath : `${dirPath}/`;
|
|
492
|
+
try {
|
|
493
|
+
(0, child_process_1.execSync)(`git check-ignore -q -- ${asDir}`, {
|
|
494
|
+
cwd: directory,
|
|
495
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
496
|
+
windowsHide: true,
|
|
497
|
+
});
|
|
498
|
+
return 'ignored';
|
|
499
|
+
}
|
|
500
|
+
catch (e) {
|
|
501
|
+
// check-ignore exits 1 for "not ignored"; anything else is a probe
|
|
502
|
+
// failure.
|
|
503
|
+
return e?.status === 1 ? 'unignored' : 'unknown';
|
|
403
504
|
}
|
|
404
505
|
}
|
|
405
506
|
// Returns a content-sensitive sha1 snapshot of the working tree state for
|
|
@@ -503,8 +604,13 @@ function commitChanges(commitMessage, directory) {
|
|
|
503
604
|
* Returns `null` (rather than throwing) when the commit itself succeeded
|
|
504
605
|
* but `git rev-parse HEAD` failed transiently — by contract the diff is
|
|
505
606
|
* no longer in the working tree, so callers must NOT report it as such.
|
|
607
|
+
*
|
|
608
|
+
* `excludePaths` are `directory`-relative paths the commit must not capture,
|
|
609
|
+
* whatever the ignore rules say. Their working-tree files are left intact;
|
|
610
|
+
* only their index entries are put back to HEAD's state. Paths come from
|
|
611
|
+
* callers' own constants, never from user input.
|
|
506
612
|
*/
|
|
507
|
-
function tryCommitChanges(commitMessage, directory) {
|
|
613
|
+
function tryCommitChanges(commitMessage, directory, excludePaths = []) {
|
|
508
614
|
try {
|
|
509
615
|
(0, child_process_1.execSync)('git add -A', {
|
|
510
616
|
encoding: 'utf8',
|
|
@@ -512,6 +618,20 @@ function tryCommitChanges(commitMessage, directory) {
|
|
|
512
618
|
cwd: directory,
|
|
513
619
|
windowsHide: true,
|
|
514
620
|
});
|
|
621
|
+
// Exclusion happens as an unstage rather than an add-time pathspec:
|
|
622
|
+
// `git add` refuses a pathspec naming an ignored directory (exit 1) even
|
|
623
|
+
// as an exclusion, and an add-time pathspec cannot cover entries that
|
|
624
|
+
// were already staged before this call. The reset is relative to cwd, so
|
|
625
|
+
// a workspace nested inside a larger repo excludes its own path; a path
|
|
626
|
+
// with no index entry is a quiet no-op, unborn HEAD included.
|
|
627
|
+
for (const excludePath of excludePaths) {
|
|
628
|
+
(0, child_process_1.execSync)(`git reset -q -- "${excludePath}"`, {
|
|
629
|
+
encoding: 'utf8',
|
|
630
|
+
stdio: 'pipe',
|
|
631
|
+
cwd: directory,
|
|
632
|
+
windowsHide: true,
|
|
633
|
+
});
|
|
634
|
+
}
|
|
515
635
|
(0, child_process_1.execSync)('git commit --no-verify -F -', {
|
|
516
636
|
encoding: 'utf8',
|
|
517
637
|
stdio: 'pipe',
|
|
@@ -546,3 +666,31 @@ function getLatestCommitSha(directory) {
|
|
|
546
666
|
return null;
|
|
547
667
|
}
|
|
548
668
|
}
|
|
669
|
+
/**
|
|
670
|
+
* The shape of a recorded `git rev-parse` output: 40 hex chars, or 64 in a
|
|
671
|
+
* sha256 repository. Anything a caller persists and later interpolates into a
|
|
672
|
+
* command line has to be checked against this first.
|
|
673
|
+
*/
|
|
674
|
+
exports.GIT_SHA = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i;
|
|
675
|
+
/**
|
|
676
|
+
* Whether `ancestor` is reachable from `descendant`, i.e. resetting to
|
|
677
|
+
* `descendant` keeps `ancestor` in history. Returns false when the answer
|
|
678
|
+
* cannot be established (invalid input, not a repository, unknown commits),
|
|
679
|
+
* so callers treat an unverifiable commit as not preserved.
|
|
680
|
+
*/
|
|
681
|
+
function isAncestorCommit(ancestor, descendant, directory) {
|
|
682
|
+
if (!exports.GIT_SHA.test(ancestor) || !exports.GIT_SHA.test(descendant)) {
|
|
683
|
+
return false;
|
|
684
|
+
}
|
|
685
|
+
try {
|
|
686
|
+
(0, child_process_1.execSync)(`git merge-base --is-ancestor ${ancestor} ${descendant}`, {
|
|
687
|
+
stdio: 'pipe',
|
|
688
|
+
windowsHide: true,
|
|
689
|
+
cwd: directory,
|
|
690
|
+
});
|
|
691
|
+
return true;
|
|
692
|
+
}
|
|
693
|
+
catch {
|
|
694
|
+
return false;
|
|
695
|
+
}
|
|
696
|
+
}
|