nx 19.6.0-beta.2 → 19.6.0-beta.3

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nx",
3
- "version": "19.6.0-beta.2",
3
+ "version": "19.6.0-beta.3",
4
4
  "private": false,
5
5
  "description": "The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.",
6
6
  "repository": {
@@ -71,7 +71,7 @@
71
71
  "yargs-parser": "21.1.1",
72
72
  "node-machine-id": "1.1.12",
73
73
  "ora": "5.3.0",
74
- "@nrwl/tao": "19.6.0-beta.2"
74
+ "@nrwl/tao": "19.6.0-beta.3"
75
75
  },
76
76
  "peerDependencies": {
77
77
  "@swc-node/register": "^1.8.0",
@@ -86,16 +86,16 @@
86
86
  }
87
87
  },
88
88
  "optionalDependencies": {
89
- "@nx/nx-darwin-x64": "19.6.0-beta.2",
90
- "@nx/nx-darwin-arm64": "19.6.0-beta.2",
91
- "@nx/nx-linux-x64-gnu": "19.6.0-beta.2",
92
- "@nx/nx-linux-x64-musl": "19.6.0-beta.2",
93
- "@nx/nx-win32-x64-msvc": "19.6.0-beta.2",
94
- "@nx/nx-linux-arm64-gnu": "19.6.0-beta.2",
95
- "@nx/nx-linux-arm64-musl": "19.6.0-beta.2",
96
- "@nx/nx-linux-arm-gnueabihf": "19.6.0-beta.2",
97
- "@nx/nx-win32-arm64-msvc": "19.6.0-beta.2",
98
- "@nx/nx-freebsd-x64": "19.6.0-beta.2"
89
+ "@nx/nx-darwin-x64": "19.6.0-beta.3",
90
+ "@nx/nx-darwin-arm64": "19.6.0-beta.3",
91
+ "@nx/nx-linux-x64-gnu": "19.6.0-beta.3",
92
+ "@nx/nx-linux-x64-musl": "19.6.0-beta.3",
93
+ "@nx/nx-win32-x64-msvc": "19.6.0-beta.3",
94
+ "@nx/nx-linux-arm64-gnu": "19.6.0-beta.3",
95
+ "@nx/nx-linux-arm64-musl": "19.6.0-beta.3",
96
+ "@nx/nx-linux-arm-gnueabihf": "19.6.0-beta.3",
97
+ "@nx/nx-win32-arm64-msvc": "19.6.0-beta.3",
98
+ "@nx/nx-freebsd-x64": "19.6.0-beta.3"
99
99
  },
100
100
  "nx-migrations": {
101
101
  "migrations": "./migrations.json",
@@ -186,8 +186,15 @@
186
186
  "type": "string"
187
187
  },
188
188
  "versionPlans": {
189
- "type": "boolean",
190
- "description": "Enables using version plans as a specifier source for versioning and to determine changes for changelog generation."
189
+ "oneOf": [
190
+ {
191
+ "$ref": "#/definitions/NxReleaseVersionPlansConfiguration"
192
+ },
193
+ {
194
+ "type": "boolean",
195
+ "description": "Enables using version plans as a specifier source for versioning and to determine changes for changelog generation."
196
+ }
197
+ ]
191
198
  }
192
199
  },
193
200
  "required": ["projects"]
@@ -239,8 +246,15 @@
239
246
  "$ref": "#/definitions/NxReleaseVersionConfiguration"
240
247
  },
241
248
  "versionPlans": {
242
- "type": "boolean",
243
- "description": "Enables using version plans as a specifier source for versioning and to determine changes for changelog generation."
249
+ "oneOf": [
250
+ {
251
+ "$ref": "#/definitions/NxReleaseVersionPlansConfiguration"
252
+ },
253
+ {
254
+ "type": "boolean",
255
+ "description": "Enables using version plans as a specifier source for versioning and to determine changes for changelog generation."
256
+ }
257
+ ]
244
258
  },
245
259
  "releaseTagPattern": {
246
260
  "type": "string"
@@ -698,6 +712,18 @@
698
712
  }
699
713
  }
700
714
  },
715
+ "NxReleaseVersionPlansConfiguration": {
716
+ "type": "object",
717
+ "properties": {
718
+ "ignorePatternsForPlanCheck": {
719
+ "type": "array",
720
+ "items": {
721
+ "type": "string"
722
+ },
723
+ "description": "Changes to files matching any of these optional patterns will be excluded from the affected project logic within the `nx release plan:check` command. This is useful for ignoring files that are not relevant to the versioning process, such as documentation or configuration files."
724
+ }
725
+ }
726
+ },
701
727
  "ChangelogRenderOptions": {
702
728
  "type": "object",
703
729
  "additionalProperties": true
@@ -84,7 +84,7 @@ function createAPI(overrideReleaseConfig) {
84
84
  process.exit(1);
85
85
  }
86
86
  const rawVersionPlans = await (0, version_plans_1.readRawVersionPlans)();
87
- (0, version_plans_1.setVersionPlansOnGroups)(rawVersionPlans, releaseGroups, Object.keys(projectGraph.nodes));
87
+ (0, version_plans_1.setResolvedVersionPlansOnGroups)(rawVersionPlans, releaseGroups, Object.keys(projectGraph.nodes));
88
88
  if (args.deleteVersionPlans === undefined) {
89
89
  // default to deleting version plans in this command instead of after versioning
90
90
  args.deleteVersionPlans = true;
@@ -140,12 +140,12 @@ function createAPI(overrideReleaseConfig) {
140
140
  // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
141
141
  let workspaceChangelogCommits = [];
142
142
  // If there are multiple release groups, we'll just skip the workspace changelog anyway.
143
- const versionPlansEnabledForWorkspaceChangelog = releaseGroups[0].versionPlans;
143
+ const versionPlansEnabledForWorkspaceChangelog = releaseGroups[0].resolvedVersionPlans;
144
144
  if (versionPlansEnabledForWorkspaceChangelog) {
145
145
  if (releaseGroups.length === 1) {
146
146
  const releaseGroup = releaseGroups[0];
147
147
  if (releaseGroup.projectsRelationship === 'fixed') {
148
- const versionPlans = releaseGroup.versionPlans;
148
+ const versionPlans = releaseGroup.resolvedVersionPlans;
149
149
  workspaceChangelogChanges = versionPlans
150
150
  .flatMap((vp) => {
151
151
  const releaseType = versionPlanSemverReleaseTypeToChangelogType(vp.groupVersionBump);
@@ -296,8 +296,8 @@ function createAPI(overrideReleaseConfig) {
296
296
  let changes = null;
297
297
  // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
298
298
  let commits;
299
- if (releaseGroup.versionPlans) {
300
- changes = releaseGroup.versionPlans
299
+ if (releaseGroup.resolvedVersionPlans) {
300
+ changes = releaseGroup.resolvedVersionPlans
301
301
  .map((vp) => {
302
302
  const bumpForProject = vp.projectVersionBumps[project.name];
303
303
  if (!bumpForProject) {
@@ -396,8 +396,8 @@ function createAPI(overrideReleaseConfig) {
396
396
  let changes = [];
397
397
  // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
398
398
  let commits = [];
399
- if (releaseGroup.versionPlans) {
400
- changes = releaseGroup.versionPlans
399
+ if (releaseGroup.resolvedVersionPlans) {
400
+ changes = releaseGroup.resolvedVersionPlans
401
401
  .flatMap((vp) => {
402
402
  const releaseType = versionPlanSemverReleaseTypeToChangelogType(vp.groupVersionBump);
403
403
  const changes = !vp.triggeredByProjects
@@ -585,8 +585,8 @@ async function applyChangesAndExit(args, nxReleaseConfig, tree, toSHA, postGitTa
585
585
  if (args.deleteVersionPlans && !args.dryRun) {
586
586
  const planFiles = new Set();
587
587
  releaseGroups.forEach((group) => {
588
- if (group.versionPlans) {
589
- group.versionPlans.forEach((plan) => {
588
+ if (group.resolvedVersionPlans) {
589
+ group.resolvedVersionPlans.forEach((plan) => {
590
590
  (0, fs_extra_1.removeSync)(plan.absolutePath);
591
591
  planFiles.add(plan.relativePath);
592
592
  });
@@ -1,12 +1,14 @@
1
1
  import { CommandModule } from 'yargs';
2
2
  import { OutputStyle, RunManyOptions } from '../yargs-utils/shared-options';
3
3
  import { VersionData } from './utils/shared';
4
- export interface NxReleaseArgs {
4
+ export interface BaseNxReleaseArgs {
5
+ verbose?: boolean;
6
+ printConfig?: boolean | 'debug';
7
+ }
8
+ export interface NxReleaseArgs extends BaseNxReleaseArgs {
5
9
  groups?: string[];
6
10
  projects?: string[];
7
11
  dryRun?: boolean;
8
- verbose?: boolean;
9
- printConfig?: boolean | 'debug';
10
12
  }
11
13
  interface GitCommitAndTagOptions {
12
14
  stageChanges?: boolean;
@@ -43,6 +45,13 @@ export type PlanOptions = NxReleaseArgs & {
43
45
  bump?: string;
44
46
  message?: string;
45
47
  };
48
+ export type PlanCheckOptions = BaseNxReleaseArgs & {
49
+ base?: string;
50
+ head?: string;
51
+ files?: string;
52
+ uncommitted?: boolean;
53
+ untracked?: boolean;
54
+ };
46
55
  export type ReleaseOptions = NxReleaseArgs & FirstReleaseArgs & {
47
56
  yes?: boolean;
48
57
  skipPublish?: boolean;
@@ -3,9 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.yargsReleaseCommand = void 0;
4
4
  const yargs_1 = require("yargs");
5
5
  const nx_json_1 = require("../../config/nx-json");
6
+ const command_line_utils_1 = require("../../utils/command-line-utils");
6
7
  const logger_1 = require("../../utils/logger");
7
8
  const shared_options_1 = require("../yargs-utils/shared-options");
8
- const command_line_utils_1 = require("../../utils/command-line-utils");
9
9
  exports.yargsReleaseCommand = {
10
10
  command: 'release',
11
11
  describe: 'Orchestrate versioning and publishing of applications and libraries',
@@ -15,6 +15,7 @@ exports.yargsReleaseCommand = {
15
15
  .command(changelogCommand)
16
16
  .command(publishCommand)
17
17
  .command(planCommand)
18
+ .command(planCheckCommand)
18
19
  .demandCommand()
19
20
  // Error on typos/mistyped CLI args, there is no reason to support arbitrary unknown args for these commands
20
21
  .strictOptions()
@@ -206,6 +207,7 @@ const publishCommand = {
206
207
  const planCommand = {
207
208
  command: 'plan [bump]',
208
209
  aliases: ['pl'],
210
+ // TODO: Remove this when docs are added
209
211
  // Create a plan to pick a new version and generate a changelog entry.
210
212
  // Hidden for now until the feature is more stable
211
213
  describe: false,
@@ -237,6 +239,19 @@ const planCommand = {
237
239
  process.exit(result);
238
240
  },
239
241
  };
242
+ const planCheckCommand = {
243
+ command: 'plan:check',
244
+ // TODO: Remove this when docs are added
245
+ // Create a plan to pick a new version and generate a changelog entry.
246
+ // Hidden for now until the feature is more stable
247
+ describe: false,
248
+ builder: (yargs) => (0, shared_options_1.withAffectedOptions)(yargs),
249
+ handler: async (args) => {
250
+ const release = await Promise.resolve().then(() => require('./plan-check'));
251
+ const result = await release.releasePlanCheckCLIHandler(args);
252
+ process.exit(result);
253
+ },
254
+ };
240
255
  function coerceParallelOption(args) {
241
256
  return {
242
257
  ...args,
@@ -144,7 +144,8 @@ async function createNxReleaseConfig(projectGraph, projectFileMap, userConfig =
144
144
  ? defaultIndependentReleaseTagPattern
145
145
  : defaultFixedReleaseTagPattern),
146
146
  conventionalCommits: conventional_commits_1.DEFAULT_CONVENTIONAL_COMMITS_CONFIG,
147
- versionPlans: false,
147
+ versionPlans: (userConfig.versionPlans ||
148
+ false),
148
149
  };
149
150
  const groupProjectsRelationship = userConfig.projectsRelationship || WORKSPACE_DEFAULTS.projectsRelationship;
150
151
  const GROUP_DEFAULTS = {
@@ -200,7 +201,8 @@ async function createNxReleaseConfig(projectGraph, projectFileMap, userConfig =
200
201
  git: userConfig.git,
201
202
  },
202
203
  ], normalizeTrueToEmptyObject(userConfig.changelog));
203
- const rootVersionPlansConfig = userConfig.versionPlans ?? WORKSPACE_DEFAULTS.versionPlans;
204
+ const rootVersionPlansConfig = (userConfig.versionPlans ??
205
+ WORKSPACE_DEFAULTS.versionPlans);
204
206
  const rootConventionalCommitsConfig = deepMergeDefaults([WORKSPACE_DEFAULTS.conventionalCommits], fillUnspecifiedConventionalCommitsProperties(normalizeConventionalCommitsConfig(userConfig.conventionalCommits)));
205
207
  // these options are not supported at the group level, only the root/command level
206
208
  const rootVersionWithoutGlobalOptions = { ...rootVersionConfig };
@@ -1,9 +1,9 @@
1
1
  import { ProjectGraph } from '../../../config/project-graph';
2
2
  import { NxReleaseConfig } from './config';
3
3
  import { GroupVersionPlan, ProjectsVersionPlan } from './version-plans';
4
- export type ReleaseGroupWithName = Omit<NxReleaseConfig['groups'][string], 'versionPlans'> & {
4
+ export type ReleaseGroupWithName = NxReleaseConfig['groups'][string] & {
5
5
  name: string;
6
- versionPlans: (ProjectsVersionPlan | GroupVersionPlan)[] | false;
6
+ resolvedVersionPlans: (ProjectsVersionPlan | GroupVersionPlan)[] | false;
7
7
  };
8
8
  export declare function filterReleaseGroups(projectGraph: ProjectGraph, nxReleaseConfig: NxReleaseConfig, projectsFilter?: string[], groupsFilter?: string[]): {
9
9
  error: null | {
@@ -9,7 +9,7 @@ function filterReleaseGroups(projectGraph, nxReleaseConfig, projectsFilter, grou
9
9
  return {
10
10
  ...group,
11
11
  name,
12
- versionPlans: group.versionPlans ? [] : false,
12
+ resolvedVersionPlans: group.versionPlans ? [] : false,
13
13
  };
14
14
  });
15
15
  const filteredProjectToReleaseGroup = new Map();
@@ -25,5 +25,5 @@ export interface ProjectsVersionPlan extends VersionPlan {
25
25
  projectVersionBumps: Record<string, ReleaseType>;
26
26
  }
27
27
  export declare function readRawVersionPlans(): Promise<RawVersionPlan[]>;
28
- export declare function setVersionPlansOnGroups(rawVersionPlans: RawVersionPlan[], releaseGroups: ReleaseGroupWithName[], allProjectNamesInWorkspace: string[]): ReleaseGroupWithName[];
28
+ export declare function setResolvedVersionPlansOnGroups(rawVersionPlans: RawVersionPlan[], releaseGroups: ReleaseGroupWithName[], allProjectNamesInWorkspace: string[]): ReleaseGroupWithName[];
29
29
  export declare function getVersionPlansAbsolutePath(): string;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.readRawVersionPlans = readRawVersionPlans;
4
- exports.setVersionPlansOnGroups = setVersionPlansOnGroups;
4
+ exports.setResolvedVersionPlansOnGroups = setResolvedVersionPlansOnGroups;
5
5
  exports.getVersionPlansAbsolutePath = getVersionPlansAbsolutePath;
6
6
  const fs_1 = require("fs");
7
7
  const fs_extra_1 = require("fs-extra");
@@ -35,7 +35,7 @@ async function readRawVersionPlans() {
35
35
  }
36
36
  return versionPlans;
37
37
  }
38
- function setVersionPlansOnGroups(rawVersionPlans, releaseGroups, allProjectNamesInWorkspace) {
38
+ function setResolvedVersionPlansOnGroups(rawVersionPlans, releaseGroups, allProjectNamesInWorkspace) {
39
39
  const groupsByName = releaseGroups.reduce((acc, group) => acc.set(group.name, group), new Map());
40
40
  const isDefaultGroup = isDefault(releaseGroups);
41
41
  for (const rawVersionPlan of rawVersionPlans) {
@@ -45,7 +45,7 @@ function setVersionPlansOnGroups(rawVersionPlans, releaseGroups, allProjectNames
45
45
  for (const [key, value] of Object.entries(rawVersionPlan.content)) {
46
46
  if (groupsByName.has(key)) {
47
47
  const group = groupsByName.get(key);
48
- if (!group.versionPlans) {
48
+ if (!group.resolvedVersionPlans) {
49
49
  if (isDefaultGroup) {
50
50
  throw new Error(`Found a version bump in '${rawVersionPlan.fileName}' but version plans are not enabled.`);
51
51
  }
@@ -69,7 +69,7 @@ function setVersionPlansOnGroups(rawVersionPlans, releaseGroups, allProjectNames
69
69
  throw new Error(`Found a version bump for group '${key}' in '${rawVersionPlan.fileName}' with an invalid release type. Please specify one of ${semver_1.RELEASE_TYPES.join(', ')}.`);
70
70
  }
71
71
  }
72
- const existingPlan = (group.versionPlans.find((plan) => plan.fileName === rawVersionPlan.fileName));
72
+ const existingPlan = (group.resolvedVersionPlans.find((plan) => plan.fileName === rawVersionPlan.fileName));
73
73
  if (existingPlan) {
74
74
  if (existingPlan.groupVersionBump !== value) {
75
75
  if (isDefaultGroup) {
@@ -81,7 +81,7 @@ function setVersionPlansOnGroups(rawVersionPlans, releaseGroups, allProjectNames
81
81
  }
82
82
  }
83
83
  else {
84
- group.versionPlans.push({
84
+ group.resolvedVersionPlans.push({
85
85
  absolutePath: rawVersionPlan.absolutePath,
86
86
  relativePath: rawVersionPlan.relativePath,
87
87
  fileName: rawVersionPlan.fileName,
@@ -104,7 +104,7 @@ function setVersionPlansOnGroups(rawVersionPlans, releaseGroups, allProjectNames
104
104
  throw new Error(`Found a version bump for project '${key}' in '${rawVersionPlan.fileName}' but the project is not in any configured release groups.`);
105
105
  }
106
106
  }
107
- if (!groupForProject.versionPlans) {
107
+ if (!groupForProject.resolvedVersionPlans) {
108
108
  if (isDefaultGroup) {
109
109
  throw new Error(`Found a version bump for project '${key}' in '${rawVersionPlan.fileName}' but version plans are not enabled.`);
110
110
  }
@@ -116,12 +116,12 @@ function setVersionPlansOnGroups(rawVersionPlans, releaseGroups, allProjectNames
116
116
  throw new Error(`Found a version bump for project '${key}' in '${rawVersionPlan.fileName}' with an invalid release type. Please specify one of ${semver_1.RELEASE_TYPES.join(', ')}.`);
117
117
  }
118
118
  if (groupForProject.projectsRelationship === 'independent') {
119
- const existingPlan = (groupForProject.versionPlans.find((plan) => plan.fileName === rawVersionPlan.fileName));
119
+ const existingPlan = (groupForProject.resolvedVersionPlans.find((plan) => plan.fileName === rawVersionPlan.fileName));
120
120
  if (existingPlan) {
121
121
  existingPlan.projectVersionBumps[key] = value;
122
122
  }
123
123
  else {
124
- groupForProject.versionPlans.push({
124
+ groupForProject.resolvedVersionPlans.push({
125
125
  absolutePath: rawVersionPlan.absolutePath,
126
126
  relativePath: rawVersionPlan.relativePath,
127
127
  fileName: rawVersionPlan.fileName,
@@ -134,7 +134,7 @@ function setVersionPlansOnGroups(rawVersionPlans, releaseGroups, allProjectNames
134
134
  }
135
135
  }
136
136
  else {
137
- const existingPlan = (groupForProject.versionPlans.find((plan) => plan.fileName === rawVersionPlan.fileName));
137
+ const existingPlan = (groupForProject.resolvedVersionPlans.find((plan) => plan.fileName === rawVersionPlan.fileName));
138
138
  // This can occur if the same fixed release group has multiple entries for different projects within
139
139
  // the same version plan file. This will be the case when users are using the default release group.
140
140
  if (existingPlan) {
@@ -151,7 +151,7 @@ function setVersionPlansOnGroups(rawVersionPlans, releaseGroups, allProjectNames
151
151
  }
152
152
  }
153
153
  else {
154
- groupForProject.versionPlans.push({
154
+ groupForProject.resolvedVersionPlans.push({
155
155
  absolutePath: rawVersionPlan.absolutePath,
156
156
  relativePath: rawVersionPlan.relativePath,
157
157
  fileName: rawVersionPlan.fileName,
@@ -169,8 +169,8 @@ function setVersionPlansOnGroups(rawVersionPlans, releaseGroups, allProjectNames
169
169
  }
170
170
  // Order the plans from newest to oldest
171
171
  releaseGroups.forEach((group) => {
172
- if (group.versionPlans) {
173
- group.versionPlans.sort((a, b) => b.createdOnMs - a.createdOnMs);
172
+ if (group.resolvedVersionPlans) {
173
+ group.resolvedVersionPlans.sort((a, b) => b.createdOnMs - a.createdOnMs);
174
174
  }
175
175
  });
176
176
  return releaseGroups;
@@ -0,0 +1,4 @@
1
+ import { NxReleaseConfiguration } from '../../config/nx-json';
2
+ import { PlanCheckOptions, PlanOptions } from './command-object';
3
+ export declare const releasePlanCheckCLIHandler: (args: PlanCheckOptions) => Promise<number>;
4
+ export declare function createAPI(overrideReleaseConfig: NxReleaseConfiguration): (args: PlanOptions) => Promise<number>;
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.releasePlanCheckCLIHandler = void 0;
4
+ exports.createAPI = createAPI;
5
+ const nx_json_1 = require("../../config/nx-json");
6
+ const workspace_projects_1 = require("../../project-graph/affected/locators/workspace-projects");
7
+ const file_map_utils_1 = require("../../project-graph/file-map-utils");
8
+ const file_utils_1 = require("../../project-graph/file-utils");
9
+ const project_graph_1 = require("../../project-graph/project-graph");
10
+ const all_file_data_1 = require("../../utils/all-file-data");
11
+ const command_line_utils_1 = require("../../utils/command-line-utils");
12
+ const ignore_1 = require("../../utils/ignore");
13
+ const output_1 = require("../../utils/output");
14
+ const params_1 = require("../../utils/params");
15
+ const config_1 = require("./config/config");
16
+ const deep_merge_json_1 = require("./config/deep-merge-json");
17
+ const filter_release_groups_1 = require("./config/filter-release-groups");
18
+ const version_plans_1 = require("./config/version-plans");
19
+ const print_config_1 = require("./utils/print-config");
20
+ const releasePlanCheckCLIHandler = (args) => (0, params_1.handleErrors)(args.verbose, () => createAPI({})(args));
21
+ exports.releasePlanCheckCLIHandler = releasePlanCheckCLIHandler;
22
+ function createAPI(overrideReleaseConfig) {
23
+ return async function releasePlanCheck(args) {
24
+ const projectGraph = await (0, project_graph_1.createProjectGraphAsync)({ exitOnError: true });
25
+ const nxJson = (0, nx_json_1.readNxJson)();
26
+ const userProvidedReleaseConfig = (0, deep_merge_json_1.deepMergeJson)(nxJson.release ?? {}, overrideReleaseConfig ?? {});
27
+ if (args.verbose) {
28
+ process.env.NX_VERBOSE_LOGGING = 'true';
29
+ }
30
+ // Apply default configuration to any optional user configuration
31
+ const { error: configError, nxReleaseConfig } = await (0, config_1.createNxReleaseConfig)(projectGraph, await (0, file_map_utils_1.createProjectFileMapUsingProjectGraph)(projectGraph), userProvidedReleaseConfig);
32
+ if (configError) {
33
+ return await (0, config_1.handleNxReleaseConfigError)(configError);
34
+ }
35
+ // --print-config exits directly as it is not designed to be combined with any other programmatic operations
36
+ if (args.printConfig) {
37
+ return (0, print_config_1.printConfigAndExit)({
38
+ userProvidedReleaseConfig,
39
+ nxReleaseConfig,
40
+ isDebug: args.printConfig === 'debug',
41
+ });
42
+ }
43
+ const { error: filterError, releaseGroups, releaseGroupToFilteredProjects, } = (0, filter_release_groups_1.filterReleaseGroups)(projectGraph, nxReleaseConfig, args.projects, args.groups);
44
+ if (filterError) {
45
+ output_1.output.error(filterError);
46
+ process.exit(1);
47
+ }
48
+ // If no release groups have version plans enabled, provide an explicit error
49
+ if (!releaseGroups.some((group) => group.versionPlans)) {
50
+ output_1.output.error({
51
+ title: 'Version plans are not enabled',
52
+ bodyLines: [
53
+ 'Please ensure at least one release group has version plans enabled in your Nx release configuration if you want to use this command.',
54
+ // TODO: Add docs link here once it is available
55
+ ],
56
+ });
57
+ return 1;
58
+ }
59
+ const rawVersionPlans = await (0, version_plans_1.readRawVersionPlans)();
60
+ (0, version_plans_1.setResolvedVersionPlansOnGroups)(rawVersionPlans, releaseGroups, Object.keys(projectGraph.nodes));
61
+ // Resolve the final values for base, head etc to use when resolving the changes to consider
62
+ const { nxArgs } = (0, command_line_utils_1.splitArgsIntoNxArgsAndOverrides)(args, 'affected', {
63
+ printWarnings: args.verbose,
64
+ }, nxJson);
65
+ const changedFiles = (0, command_line_utils_1.parseFiles)(nxArgs).files;
66
+ if (nxArgs.verbose) {
67
+ if (changedFiles.length) {
68
+ output_1.output.log({
69
+ title: `Changed files based on resolved "base" (${nxArgs.base}) and "head" (${nxArgs.head ?? 'HEAD'})`,
70
+ bodyLines: changedFiles.map((file) => ` - ${file}`),
71
+ });
72
+ }
73
+ else {
74
+ output_1.output.warn({
75
+ title: 'No changed files found based on resolved "base" and "head"',
76
+ });
77
+ }
78
+ }
79
+ const resolvedAllFileData = await (0, all_file_data_1.allFileData)();
80
+ /**
81
+ * Create a minimal subset of touched projects based on the configured ignore patterns, we only need
82
+ * to recompute when the ignorePatternsForPlanCheck differs between release groups.
83
+ */
84
+ const serializedIgnorePatternsToTouchedProjects = new Map();
85
+ const NOTE_ABOUT_VERBOSE_LOGGING = 'Run with --verbose to see the full list of changed files used for the touched projects logic.';
86
+ let hasErrors = false;
87
+ for (const releaseGroup of releaseGroups) {
88
+ // The current release group doesn't leverage version plans
89
+ if (!releaseGroup.versionPlans) {
90
+ continue;
91
+ }
92
+ const resolvedVersionPlans = releaseGroup.resolvedVersionPlans || [];
93
+ // Check upfront if the release group as a whole is featured in any version plan files
94
+ const matchingVersionPlanFiles = resolvedVersionPlans.filter((plan) => 'groupVersionBump' in plan);
95
+ if (matchingVersionPlanFiles.length) {
96
+ output_1.output.log({
97
+ title: `${releaseGroup.name === config_1.IMPLICIT_DEFAULT_RELEASE_GROUP
98
+ ? `There are`
99
+ : `Release group "${releaseGroup.name}" has`} pending bumps in version plan(s)`,
100
+ bodyLines: [
101
+ ...matchingVersionPlanFiles.map((plan) => ` - "${plan.groupVersionBump}" in ${plan.fileName}`),
102
+ ],
103
+ });
104
+ continue;
105
+ }
106
+ // Exclude patterns from .nxignore, .gitignore and explicit version plan config
107
+ let serializedIgnorePatterns = '[]';
108
+ const ignore = (0, ignore_1.getIgnoreObject)();
109
+ if (typeof releaseGroup.versionPlans !== 'boolean' &&
110
+ Array.isArray(releaseGroup.versionPlans.ignorePatternsForPlanCheck) &&
111
+ releaseGroup.versionPlans.ignorePatternsForPlanCheck.length) {
112
+ output_1.output.note({
113
+ title: `Applying configured ignore patterns to changed files${releaseGroup.name !== config_1.IMPLICIT_DEFAULT_RELEASE_GROUP
114
+ ? ` for release group "${releaseGroup.name}"`
115
+ : ''}`,
116
+ bodyLines: [
117
+ ...releaseGroup.versionPlans.ignorePatternsForPlanCheck.map((pattern) => ` - ${pattern}`),
118
+ ],
119
+ });
120
+ ignore.add(releaseGroup.versionPlans.ignorePatternsForPlanCheck);
121
+ serializedIgnorePatterns = JSON.stringify(releaseGroup.versionPlans.ignorePatternsForPlanCheck);
122
+ }
123
+ let touchedProjects = {};
124
+ if (serializedIgnorePatternsToTouchedProjects.has(serializedIgnorePatterns)) {
125
+ touchedProjects = serializedIgnorePatternsToTouchedProjects.get(serializedIgnorePatterns);
126
+ }
127
+ else {
128
+ // We only care about directly touched projects, not implicitly affected ones etc
129
+ const touchedProjectsArr = await (0, workspace_projects_1.getTouchedProjects)((0, file_utils_1.calculateFileChanges)(changedFiles, resolvedAllFileData, nxArgs, undefined, ignore), projectGraph.nodes);
130
+ touchedProjects = touchedProjectsArr.reduce((acc, project) => ({ ...acc, [project]: true }), {});
131
+ serializedIgnorePatternsToTouchedProjects.set(serializedIgnorePatterns, touchedProjects);
132
+ }
133
+ const touchedProjectsUnderReleaseGroup = releaseGroup.projects.filter((project) => touchedProjects[project]);
134
+ if (touchedProjectsUnderReleaseGroup.length) {
135
+ output_1.output.log({
136
+ title: `Touched projects based on changed files${releaseGroup.name !== config_1.IMPLICIT_DEFAULT_RELEASE_GROUP
137
+ ? ` under release group "${releaseGroup.name}"`
138
+ : ''}`,
139
+ bodyLines: [
140
+ ...touchedProjectsUnderReleaseGroup.map((project) => ` - ${project}`),
141
+ '',
142
+ 'NOTE: You can adjust your "versionPlans.ignorePatternsForPlanCheck" config to stop certain files from resulting in projects being classed as touched for the purposes of this command.',
143
+ ],
144
+ });
145
+ }
146
+ else {
147
+ output_1.output.log({
148
+ title: `No touched projects found based on changed files${typeof releaseGroup.versionPlans !== 'boolean' &&
149
+ Array.isArray(releaseGroup.versionPlans.ignorePatternsForPlanCheck) &&
150
+ releaseGroup.versionPlans.ignorePatternsForPlanCheck.length
151
+ ? ' combined with configured ignore patterns'
152
+ : ''}${releaseGroup.name !== config_1.IMPLICIT_DEFAULT_RELEASE_GROUP
153
+ ? ` under release group "${releaseGroup.name}"`
154
+ : ''}`,
155
+ });
156
+ }
157
+ const projectsInResolvedVersionPlans = resolvedVersionPlans.reduce((acc, plan) => {
158
+ if ('projectVersionBumps' in plan) {
159
+ for (const project in plan.projectVersionBumps) {
160
+ acc[project] = acc[project] || [];
161
+ acc[project].push({
162
+ bump: plan.projectVersionBumps[project],
163
+ fileName: plan.fileName,
164
+ });
165
+ }
166
+ }
167
+ return acc;
168
+ }, {});
169
+ // Ensure each touched project under this release group features in at least one version plan file
170
+ let touchedProjectsNotFoundInVersionPlans = [];
171
+ for (const touchedProject of touchedProjectsUnderReleaseGroup) {
172
+ if (!resolvedVersionPlans.length) {
173
+ touchedProjectsNotFoundInVersionPlans.push(touchedProject);
174
+ continue;
175
+ }
176
+ const matchingVersionPlanFileEntries = projectsInResolvedVersionPlans[touchedProject];
177
+ if (!matchingVersionPlanFileEntries?.length) {
178
+ touchedProjectsNotFoundInVersionPlans.push(touchedProject);
179
+ continue;
180
+ }
181
+ }
182
+ // Log any resolved pending bumps, regardless of whether the projects were directly touched or not
183
+ for (const [projectName, entries] of Object.entries(projectsInResolvedVersionPlans)) {
184
+ output_1.output.log({
185
+ title: `Project "${projectName}" has pending bumps in version plan(s)`,
186
+ bodyLines: [
187
+ ...entries.map(({ bump, fileName }) => ` - "${bump}" in ${fileName}`),
188
+ ],
189
+ });
190
+ }
191
+ if (touchedProjectsNotFoundInVersionPlans.length) {
192
+ const bodyLines = [
193
+ `The following touched projects${releaseGroup.name !== config_1.IMPLICIT_DEFAULT_RELEASE_GROUP
194
+ ? ` under release group "${releaseGroup.name}"`
195
+ : ''} do not feature in any version plan files:`,
196
+ ...touchedProjectsNotFoundInVersionPlans.map((project) => ` - ${project}`),
197
+ '',
198
+ 'Please use `nx release plan` to generate missing version plans, or adjust your "versionPlans.ignorePatternsForPlanCheck" config stop certain files from affecting the projects for the purposes of this command.',
199
+ ];
200
+ if (!nxArgs.verbose) {
201
+ bodyLines.push('', NOTE_ABOUT_VERBOSE_LOGGING);
202
+ }
203
+ output_1.output.error({
204
+ title: 'Touched projects missing version plans',
205
+ bodyLines,
206
+ });
207
+ // At least one project in one release group has an issue
208
+ hasErrors = true;
209
+ }
210
+ }
211
+ // Do not print success message if any projects are missing version plans
212
+ if (hasErrors) {
213
+ return 1;
214
+ }
215
+ const bodyLines = [];
216
+ if (!nxArgs.verbose) {
217
+ bodyLines.push(NOTE_ABOUT_VERBOSE_LOGGING);
218
+ }
219
+ output_1.output.success({
220
+ title: 'All touched projects have, or do not require, version plans.',
221
+ bodyLines,
222
+ });
223
+ return 0;
224
+ };
225
+ }
@@ -31,7 +31,7 @@ function createAPI(overrideReleaseConfig) {
31
31
  process.env.NX_VERBOSE_LOGGING = 'true';
32
32
  }
33
33
  // Apply default configuration to any optional user configuration
34
- const { error: configError, nxReleaseConfig } = await (0, config_1.createNxReleaseConfig)(projectGraph, await (0, file_map_utils_1.createProjectFileMapUsingProjectGraph)(projectGraph), nxJson.release);
34
+ const { error: configError, nxReleaseConfig } = await (0, config_1.createNxReleaseConfig)(projectGraph, await (0, file_map_utils_1.createProjectFileMapUsingProjectGraph)(projectGraph), userProvidedReleaseConfig);
35
35
  if (configError) {
36
36
  return await (0, config_1.handleNxReleaseConfigError)(configError);
37
37
  }
@@ -88,17 +88,17 @@ function createAPI(overrideReleaseConfig) {
88
88
  process.exit(1);
89
89
  }
90
90
  const rawVersionPlans = await (0, version_plans_1.readRawVersionPlans)();
91
- (0, version_plans_1.setVersionPlansOnGroups)(rawVersionPlans, releaseGroups, Object.keys(projectGraph.nodes));
91
+ (0, version_plans_1.setResolvedVersionPlansOnGroups)(rawVersionPlans, releaseGroups, Object.keys(projectGraph.nodes));
92
92
  const planFiles = new Set();
93
93
  releaseGroups.forEach((group) => {
94
- if (group.versionPlans) {
94
+ if (group.resolvedVersionPlans) {
95
95
  if (group.name === config_1.IMPLICIT_DEFAULT_RELEASE_GROUP) {
96
96
  output_1.output.logSingleLine(`Removing version plan files`);
97
97
  }
98
98
  else {
99
99
  output_1.output.logSingleLine(`Removing version plan files for group ${group.name}`);
100
100
  }
101
- group.versionPlans.forEach((plan) => {
101
+ group.resolvedVersionPlans.forEach((plan) => {
102
102
  if (!args.dryRun) {
103
103
  (0, fs_extra_1.removeSync)(plan.absolutePath);
104
104
  if (args.verbose) {
@@ -84,7 +84,7 @@ function createAPI(overrideReleaseConfig) {
84
84
  process.exit(1);
85
85
  }
86
86
  const rawVersionPlans = await (0, version_plans_1.readRawVersionPlans)();
87
- (0, version_plans_1.setVersionPlansOnGroups)(rawVersionPlans, releaseGroups, Object.keys(projectGraph.nodes));
87
+ (0, version_plans_1.setResolvedVersionPlansOnGroups)(rawVersionPlans, releaseGroups, Object.keys(projectGraph.nodes));
88
88
  if (args.deleteVersionPlans === undefined) {
89
89
  // default to not delete version plans after versioning as they may be needed for changelog generation
90
90
  args.deleteVersionPlans = false;
@@ -145,6 +145,13 @@ export interface NxReleaseConventionalCommitsConfiguration {
145
145
  } | boolean;
146
146
  } | boolean>;
147
147
  }
148
+ export interface NxReleaseVersionPlansConfiguration {
149
+ /**
150
+ * Changes to files matching any of these optional patterns will be excluded from the affected project logic within the `nx release plan:check`
151
+ * command. This is useful for ignoring files that are not relevant to the versioning process, such as documentation or configuration files.
152
+ */
153
+ ignorePatternsForPlanCheck?: string[];
154
+ }
148
155
  export interface NxReleaseConfiguration {
149
156
  /**
150
157
  * Shorthand for amending the projects which will be included in the implicit default release group (all projects by default).
@@ -193,7 +200,7 @@ export interface NxReleaseConfiguration {
193
200
  * Enables using version plans as a specifier source for versioning and
194
201
  * to determine changes for changelog generation.
195
202
  */
196
- versionPlans?: boolean;
203
+ versionPlans?: NxReleaseVersionPlansConfiguration | boolean;
197
204
  }>;
198
205
  /**
199
206
  * Configures the default value for all groups that don't explicitly state their own projectsRelationship.
@@ -264,7 +271,7 @@ export interface NxReleaseConfiguration {
264
271
  * Enables using version plans as a specifier source for versioning and
265
272
  * to determine changes for changelog generation.
266
273
  */
267
- versionPlans?: boolean;
274
+ versionPlans?: NxReleaseVersionPlansConfiguration | boolean;
268
275
  }
269
276
  export interface NxSyncConfiguration {
270
277
  /**
Binary file
@@ -70,7 +70,7 @@ function splitArgsIntoNxArgsAndOverrides(args, mode, options = { printWarnings:
70
70
  ],
71
71
  });
72
72
  }
73
- // Allow setting base and head via environment variables (lower priority then direct command arguments)
73
+ // Allow setting base and head via environment variables (lower priority than direct command arguments)
74
74
  if (!nxArgs.base && process.env.NX_BASE) {
75
75
  nxArgs.base = process.env.NX_BASE;
76
76
  if (options.printWarnings) {