nx 17.2.8 → 17.3.0-beta.2

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 CHANGED
@@ -1,4 +1,9 @@
1
- <p style="text-align: center;"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx.png" width="600" alt="Nx - Smart, Fast and Extensible Build System"></p>
1
+ <p style="text-align: center;">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-dark.svg">
4
+ <img alt="Nx - Smart Monorepos · Fast CI" src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-light.svg" width="100%">
5
+ </picture>
6
+ </p>
2
7
 
3
8
  <div style="text-align: center;">
4
9
 
@@ -15,9 +20,9 @@
15
20
 
16
21
  <hr>
17
22
 
18
- # Nx: Smart, Fast and Extensible Build System
23
+ # Nx: Smart Monorepos · Fast CI
19
24
 
20
- Nx is a next generation build system with first class monorepo support and powerful integrations.
25
+ Nx is a build system with built-in tooling and advanced CI capabilities. It helps you maintain and scale monorepos, both locally and on CI.
21
26
 
22
27
  ## Getting Started
23
28
 
@@ -57,5 +62,5 @@ npx nx@latest init
57
62
  - [Blog Posts About Nx](https://blog.nrwl.io/nx/home)
58
63
 
59
64
  <p style="text-align: center;"><a href="https://nx.dev/#learning-materials" target="_blank" rel="noreferrer"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-courses-and-videos.svg"
60
- width="100%" alt="Nx - Smart, Fast and Extensible Build System"></a></p>
65
+ width="100%" alt="Nx - Smart Monorepos · Fast CI"></a></p>
61
66
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nx",
3
- "version": "17.2.8",
3
+ "version": "17.3.0-beta.2",
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": {
@@ -66,7 +66,7 @@
66
66
  "yargs": "^17.6.2",
67
67
  "yargs-parser": "21.1.1",
68
68
  "node-machine-id": "1.1.12",
69
- "@nrwl/tao": "17.2.8"
69
+ "@nrwl/tao": "17.3.0-beta.2"
70
70
  },
71
71
  "peerDependencies": {
72
72
  "@swc-node/register": "^1.6.7",
@@ -81,16 +81,16 @@
81
81
  }
82
82
  },
83
83
  "optionalDependencies": {
84
- "@nx/nx-darwin-x64": "17.2.8",
85
- "@nx/nx-darwin-arm64": "17.2.8",
86
- "@nx/nx-linux-x64-gnu": "17.2.8",
87
- "@nx/nx-linux-x64-musl": "17.2.8",
88
- "@nx/nx-win32-x64-msvc": "17.2.8",
89
- "@nx/nx-linux-arm64-gnu": "17.2.8",
90
- "@nx/nx-linux-arm64-musl": "17.2.8",
91
- "@nx/nx-linux-arm-gnueabihf": "17.2.8",
92
- "@nx/nx-win32-arm64-msvc": "17.2.8",
93
- "@nx/nx-freebsd-x64": "17.2.8"
84
+ "@nx/nx-darwin-x64": "17.3.0-beta.2",
85
+ "@nx/nx-darwin-arm64": "17.3.0-beta.2",
86
+ "@nx/nx-linux-x64-gnu": "17.3.0-beta.2",
87
+ "@nx/nx-linux-x64-musl": "17.3.0-beta.2",
88
+ "@nx/nx-win32-x64-msvc": "17.3.0-beta.2",
89
+ "@nx/nx-linux-arm64-gnu": "17.3.0-beta.2",
90
+ "@nx/nx-linux-arm64-musl": "17.3.0-beta.2",
91
+ "@nx/nx-linux-arm-gnueabihf": "17.3.0-beta.2",
92
+ "@nx/nx-win32-arm64-msvc": "17.3.0-beta.2",
93
+ "@nx/nx-freebsd-x64": "17.3.0-beta.2"
94
94
  },
95
95
  "nx-migrations": {
96
96
  "migrations": "./migrations.json",
@@ -24,15 +24,26 @@ export declare class NxScopedHost extends virtualFs.ScopedHost<any> {
24
24
  private root;
25
25
  constructor(root: string);
26
26
  read(path: Path): Observable<FileBuffer>;
27
- private readMergedWorkspaceConfiguration;
27
+ protected readMergedWorkspaceConfiguration(): Observable<any>;
28
28
  write(path: Path, content: FileBuffer): Observable<void>;
29
29
  isFile(path: Path): Observable<boolean>;
30
30
  exists(path: Path): Observable<boolean>;
31
31
  mergeProjectConfiguration(existing: AngularProjectConfiguration, updated: AngularProjectConfiguration, projectName: string): AngularProjectConfiguration;
32
32
  readExistingAngularJson(): Observable<any>;
33
- private readJson;
33
+ protected readJson<T = any>(path: string): Observable<T>;
34
+ }
35
+ /**
36
+ * Host used by Angular CLI builders. It reads the project configurations from
37
+ * the project graph to access the expanded targets.
38
+ */
39
+ export declare class NxScopedHostForBuilders extends NxScopedHost {
40
+ protected readMergedWorkspaceConfiguration(): Observable<any>;
34
41
  }
35
42
  export declare function arrayBufferToString(buffer: any): string;
43
+ /**
44
+ * Host used by Angular CLI schematics. It reads the project configurations from
45
+ * the project configuration files.
46
+ */
36
47
  export declare class NxScopeHostUsedForWrappedSchematics extends NxScopedHost {
37
48
  private readonly host;
38
49
  constructor(root: string, host: Tree);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getLogger = exports.wrapAngularDevkitSchematic = exports.mockSchematicsForTesting = exports.runMigration = exports.generate = exports.NxScopeHostUsedForWrappedSchematics = exports.arrayBufferToString = exports.NxScopedHost = exports.scheduleTarget = exports.createBuilderContext = void 0;
3
+ exports.getLogger = exports.wrapAngularDevkitSchematic = exports.mockSchematicsForTesting = exports.runMigration = exports.generate = exports.NxScopeHostUsedForWrappedSchematics = exports.arrayBufferToString = exports.NxScopedHostForBuilders = exports.NxScopedHost = exports.scheduleTarget = exports.createBuilderContext = void 0;
4
4
  const core_1 = require("@angular-devkit/core");
5
5
  const node_1 = require("@angular-devkit/core/node");
6
6
  const chalk = require("chalk");
@@ -23,7 +23,7 @@ const nx_plugin_1 = require("../utils/nx-plugin");
23
23
  const schema_utils_1 = require("../config/schema-utils");
24
24
  async function createBuilderContext(builderInfo, context) {
25
25
  require('./compat');
26
- const fsHost = new NxScopedHost(context.root);
26
+ const fsHost = new NxScopedHostForBuilders(context.root);
27
27
  // the top level import is not patched because it is imported before the
28
28
  // patching happens so we require it here to use the patched version below
29
29
  const { workspaces } = require('@angular-devkit/core');
@@ -108,7 +108,7 @@ exports.createBuilderContext = createBuilderContext;
108
108
  async function scheduleTarget(root, opts, verbose) {
109
109
  const { Architect } = require('@angular-devkit/architect');
110
110
  const logger = (0, exports.getLogger)(verbose);
111
- const fsHost = new NxScopedHost(root);
111
+ const fsHost = new NxScopedHostForBuilders(root);
112
112
  const { workspace } = await core_1.workspaces.readWorkspace('angular.json', core_1.workspaces.createWorkspaceHost(fsHost));
113
113
  const registry = new core_1.schema.CoreSchemaRegistry();
114
114
  registry.addPostTransform(core_1.schema.transforms.addUndefinedDefaults);
@@ -225,7 +225,7 @@ async function runSchematic(host, root, workflow, logger, opts, schematic, print
225
225
  .toPromise();
226
226
  }
227
227
  catch (e) {
228
- console.log(e);
228
+ console.error(e);
229
229
  throw e;
230
230
  }
231
231
  if (!record.error) {
@@ -273,8 +273,8 @@ class NxScopedHost extends core_1.virtualFs.ScopedHost {
273
273
  return workspaceConfig;
274
274
  }));
275
275
  }), (0, operators_1.catchError)((err) => {
276
- console.log('Unable to read angular.json');
277
- console.log(err);
276
+ console.error('Unable to read angular.json');
277
+ console.error(err);
278
278
  process.exit(1);
279
279
  }));
280
280
  }
@@ -378,6 +378,30 @@ class NxScopedHost extends core_1.virtualFs.ScopedHost {
378
378
  }
379
379
  }
380
380
  exports.NxScopedHost = NxScopedHost;
381
+ /**
382
+ * Host used by Angular CLI builders. It reads the project configurations from
383
+ * the project graph to access the expanded targets.
384
+ */
385
+ class NxScopedHostForBuilders extends NxScopedHost {
386
+ readMergedWorkspaceConfiguration() {
387
+ return (0, rxjs_1.zip)((0, rxjs_1.from)((0, project_graph_1.createProjectGraphAsync)()), this.readExistingAngularJson(), this.readJson('nx.json')).pipe((0, operators_1.map)(([graph, angularJson, nxJson]) => {
388
+ const workspaceConfig = (angularJson || { projects: {} });
389
+ workspaceConfig.cli ??= nxJson.cli;
390
+ workspaceConfig.schematics ??= nxJson.generators;
391
+ for (const projectName of Object.keys(graph.nodes)) {
392
+ workspaceConfig.projects[projectName] ??= {
393
+ ...graph.nodes[projectName].data,
394
+ };
395
+ }
396
+ return workspaceConfig;
397
+ }), (0, operators_1.catchError)((err) => {
398
+ console.error('Unable to read angular.json');
399
+ console.error(err);
400
+ process.exit(1);
401
+ }));
402
+ }
403
+ }
404
+ exports.NxScopedHostForBuilders = NxScopedHostForBuilders;
381
405
  function arrayBufferToString(buffer) {
382
406
  const array = new Uint8Array(buffer);
383
407
  let result = '';
@@ -390,6 +414,10 @@ function arrayBufferToString(buffer) {
390
414
  return result;
391
415
  }
392
416
  exports.arrayBufferToString = arrayBufferToString;
417
+ /**
418
+ * Host used by Angular CLI schematics. It reads the project configurations from
419
+ * the project configuration files.
420
+ */
393
421
  class NxScopeHostUsedForWrappedSchematics extends NxScopedHost {
394
422
  constructor(root, host) {
395
423
  super(root);
@@ -1,4 +1,3 @@
1
1
  import { CommandModule } from 'yargs';
2
- import type { ConnectToNxCloudOptions } from './connect-to-nx-cloud';
3
- export declare const yargsConnectCommand: CommandModule<{}, ConnectToNxCloudOptions>;
2
+ export declare const yargsConnectCommand: CommandModule;
4
3
  export declare const yargsViewLogsCommand: CommandModule;
@@ -6,13 +6,9 @@ exports.yargsConnectCommand = {
6
6
  command: 'connect',
7
7
  aliases: ['connect-to-nx-cloud'],
8
8
  describe: `Connect workspace to Nx Cloud`,
9
- builder: (yargs) => (0, documentation_1.linkToNxDevAndExamples)(yargs.option('interactive', {
10
- type: 'boolean',
11
- description: 'Prompt for confirmation',
12
- default: true,
13
- }), 'connect-to-nx-cloud'),
14
- handler: async (options) => {
15
- await (await Promise.resolve().then(() => require('./connect-to-nx-cloud'))).connectToNxCloudCommand(options);
9
+ builder: (yargs) => (0, documentation_1.linkToNxDevAndExamples)(yargs, 'connect-to-nx-cloud'),
10
+ handler: async () => {
11
+ await (await Promise.resolve().then(() => require('./connect-to-nx-cloud'))).connectToNxCloudCommand();
16
12
  process.exit(0);
17
13
  },
18
14
  };
@@ -2,8 +2,5 @@ import { NxJsonConfiguration } from '../../config/nx-json';
2
2
  import { NxArgs } from '../../utils/command-line-utils';
3
3
  export declare function onlyDefaultRunnerIsUsed(nxJson: NxJsonConfiguration): boolean;
4
4
  export declare function connectToNxCloudIfExplicitlyAsked(opts: NxArgs): Promise<void>;
5
- export interface ConnectToNxCloudOptions {
6
- interactive: boolean;
7
- promptOverride?: string;
8
- }
9
- export declare function connectToNxCloudCommand({ promptOverride, interactive, }: ConnectToNxCloudOptions): Promise<boolean>;
5
+ export declare function connectToNxCloudCommand(): Promise<boolean>;
6
+ export declare function connectToNxCloudPrompt(prompt?: string): Promise<boolean>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.connectToNxCloudCommand = exports.connectToNxCloudIfExplicitlyAsked = exports.onlyDefaultRunnerIsUsed = void 0;
3
+ exports.connectToNxCloudPrompt = exports.connectToNxCloudCommand = exports.connectToNxCloudIfExplicitlyAsked = exports.onlyDefaultRunnerIsUsed = void 0;
4
4
  const output_1 = require("../../utils/output");
5
5
  const configuration_1 = require("../../config/configuration");
6
6
  const nx_cloud_utils_1 = require("../../utils/nx-cloud-utils");
@@ -34,7 +34,7 @@ async function connectToNxCloudIfExplicitlyAsked(opts) {
34
34
  }
35
35
  }
36
36
  exports.connectToNxCloudIfExplicitlyAsked = connectToNxCloudIfExplicitlyAsked;
37
- async function connectToNxCloudCommand({ promptOverride, interactive, }) {
37
+ async function connectToNxCloudCommand() {
38
38
  const nxJson = (0, configuration_1.readNxJson)();
39
39
  if ((0, nx_cloud_utils_1.isNxCloudUsed)(nxJson)) {
40
40
  output_1.output.log({
@@ -49,9 +49,6 @@ async function connectToNxCloudCommand({ promptOverride, interactive, }) {
49
49
  });
50
50
  return false;
51
51
  }
52
- const res = interactive ? await connectToNxCloudPrompt(promptOverride) : true;
53
- if (!res)
54
- return false;
55
52
  (0, child_process_1.runNxSync)(`g nx:connect-to-nx-cloud --quiet --no-interactive`, {
56
53
  stdio: [0, 1, 2],
57
54
  });
@@ -79,3 +76,4 @@ async function connectToNxCloudPrompt(prompt) {
79
76
  ])
80
77
  .then((a) => a.NxCloud === 'Yes');
81
78
  }
79
+ exports.connectToNxCloudPrompt = connectToNxCloudPrompt;
@@ -171,7 +171,6 @@ function createProjectJson(repoRoot, packageJson, nestCLIOptions) {
171
171
  // lint
172
172
  json.targets['lint'] = {
173
173
  executor: '@nx/eslint:lint',
174
- outputs: ['{options.outputFile}'],
175
174
  options: {
176
175
  lintFilePatterns: ['./src', './test'],
177
176
  },
@@ -106,6 +106,7 @@ function addPluginDependencies() {
106
106
  (0, fileutils_1.writeJsonFile)(packageJsonPath, packageJson);
107
107
  }
108
108
  async function setupWorkspace(cacheableOperations, isIntegratedMigration) {
109
+ (0, utils_1.updateGitIgnore)(repoRoot);
109
110
  if (isIntegratedMigration) {
110
111
  (0, integrated_workspace_1.setupIntegratedWorkspace)();
111
112
  }
@@ -751,10 +751,8 @@ async function generateMigrationsJsonAndUpdatePackageJson(root, opts) {
751
751
  (await isMigratingToNewMajor(from, opts.targetVersion)) &&
752
752
  !(0, is_ci_1.isCI)() &&
753
753
  !(0, nx_cloud_utils_1.isNxCloudUsed)(originalNxJson)) {
754
- const useCloud = await (0, connect_to_nx_cloud_1.connectToNxCloudCommand)({
755
- promptOverride: ab_testing_1.messages.getPromptMessage('nxCloudMigration'),
756
- interactive: true,
757
- });
754
+ const setNxCloud = await (0, connect_to_nx_cloud_1.connectToNxCloudPrompt)(ab_testing_1.messages.getPromptMessage('nxCloudMigration'));
755
+ const useCloud = setNxCloud ? await (0, connect_to_nx_cloud_1.connectToNxCloudCommand)() : false;
758
756
  await (0, ab_testing_1.recordStat)({
759
757
  command: 'migrate',
760
758
  nxVersion: versions_1.nxVersion,
@@ -36,7 +36,7 @@ exports.parserConfiguration = {
36
36
  */
37
37
  exports.commandsObject = yargs
38
38
  .parserConfiguration(exports.parserConfiguration)
39
- .usage(chalk.bold('Smart, Fast and Extensible Build System'))
39
+ .usage(chalk.bold('Smart Monorepos · Fast CI'))
40
40
  .demandCommand(1, '')
41
41
  .command(command_object_1.yargsAffectedBuildCommand)
42
42
  .command(command_object_1.yargsAffectedCommand)
@@ -73,24 +73,6 @@ async function releaseChangelog(args) {
73
73
  if (autoCommitEnabled && headSHA !== toSHA) {
74
74
  throw new Error(`You are attempting to recreate the changelog for an old release, but you have enabled auto-commit mode. Please disable auto-commit mode by updating your nx.json, or passing --git-commit=false`);
75
75
  }
76
- const fromRef = args.from ||
77
- (await (0, git_1.getLatestGitTagForPattern)(nxReleaseConfig.releaseTagPattern))?.tag;
78
- if (!fromRef) {
79
- throw new Error(`Unable to determine the previous git tag, please provide an explicit git reference using --from`);
80
- }
81
- // Make sure that the fromRef is actually resolvable
82
- const fromSHA = await (0, git_1.getCommitHash)(fromRef);
83
- const rawCommits = await (0, git_1.getGitDiff)(fromSHA, toSHA);
84
- // Parse as conventional commits
85
- const commits = (0, git_1.parseCommits)(rawCommits).filter((c) => {
86
- const type = c.type;
87
- // Always ignore non user-facing commits for now
88
- // TODO: allow this filter to be configurable via config in a future release
89
- if (type === 'feat' || type === 'fix' || type === 'perf') {
90
- return true;
91
- }
92
- return false;
93
- });
94
76
  const tree = new tree_1.FsTree(workspace_root_1.workspaceRoot, args.verbose);
95
77
  const userCommitMessage = args.gitCommitMessage || nxReleaseConfig.changelog.git.commitMessage;
96
78
  const commitMessageValues = (0, shared_1.createCommitMessageValues)(releaseGroups, releaseGroupToFilteredProjects, projectsVersionData, userCommitMessage);
@@ -100,23 +82,51 @@ async function releaseChangelog(args) {
100
82
  : [];
101
83
  (0, shared_1.handleDuplicateGitTags)(gitTagValues);
102
84
  const postGitTasks = [];
103
- await generateChangelogForWorkspace(tree, args, projectGraph, nxReleaseConfig, workspaceChangelogVersion, commits, postGitTasks);
104
- if (args.projects?.length) {
105
- /**
106
- * Run changelog generation for all remaining release groups and filtered projects within them
107
- */
108
- for (const releaseGroup of releaseGroups) {
109
- const projectNodes = Array.from(releaseGroupToFilteredProjects.get(releaseGroup)).map((name) => projectGraph.nodes[name]);
110
- await generateChangelogForProjects(tree, args, projectGraph, commits, projectsVersionData, postGitTasks, releaseGroup, projectNodes);
111
- }
112
- return await applyChangesAndExit(args, nxReleaseConfig, tree, toSHA, postGitTasks, commitMessageValues, gitTagValues);
85
+ const workspaceChangelogFromRef = args.from ||
86
+ (await (0, git_1.getLatestGitTagForPattern)(nxReleaseConfig.releaseTagPattern))?.tag;
87
+ if (!workspaceChangelogFromRef) {
88
+ throw new Error(`Unable to determine the previous git tag, please provide an explicit git reference using --from`);
113
89
  }
114
- /**
115
- * Run changelog generation for all remaining release groups
116
- */
90
+ // Make sure that the fromRef is actually resolvable
91
+ const workspaceChangelogFromSHA = await (0, git_1.getCommitHash)(workspaceChangelogFromRef);
92
+ const workspaceChangelogCommits = await getCommits(workspaceChangelogFromSHA, toSHA);
93
+ await generateChangelogForWorkspace(tree, args, projectGraph, nxReleaseConfig, workspaceChangelogVersion, workspaceChangelogCommits, postGitTasks, nxJson.release?.changelog?.workspaceChangelog);
117
94
  for (const releaseGroup of releaseGroups) {
118
- const projectNodes = releaseGroup.projects.map((name) => projectGraph.nodes[name]);
119
- await generateChangelogForProjects(tree, args, projectGraph, commits, projectsVersionData, postGitTasks, releaseGroup, projectNodes);
95
+ const config = releaseGroup.changelog;
96
+ // The entire feature is disabled at the release group level, exit early
97
+ if (config === false) {
98
+ continue;
99
+ }
100
+ const projects = args.projects?.length
101
+ ? // If the user has passed a list of projects, we need to use the filtered list of projects within the release group
102
+ Array.from(releaseGroupToFilteredProjects.get(releaseGroup))
103
+ : // Otherwise, we use the full list of projects within the release group
104
+ releaseGroup.projects;
105
+ const projectNodes = projects.map((name) => projectGraph.nodes[name]);
106
+ if (releaseGroup.projectsRelationship === 'independent') {
107
+ for (const project of projectNodes) {
108
+ const fromRef = args.from ||
109
+ (await (0, git_1.getLatestGitTagForPattern)(releaseGroup.releaseTagPattern, {
110
+ projectName: project.name,
111
+ }))?.tag;
112
+ if (!fromRef) {
113
+ throw new Error(`Unable to determine the previous git tag, please provide an explicit git reference using --from`);
114
+ }
115
+ const commits = await getCommits(fromRef, toSHA);
116
+ await generateChangelogForProjects(tree, args, projectGraph, commits, projectsVersionData, postGitTasks, releaseGroup, [project]);
117
+ }
118
+ }
119
+ else {
120
+ const fromRef = args.from ||
121
+ (await (0, git_1.getLatestGitTagForPattern)(releaseGroup.releaseTagPattern))?.tag;
122
+ if (!fromRef) {
123
+ throw new Error(`Unable to determine the previous git tag, please provide an explicit git reference using --from`);
124
+ }
125
+ // Make sure that the fromRef is actually resolvable
126
+ const fromSHA = await (0, git_1.getCommitHash)(fromRef);
127
+ const commits = await getCommits(fromSHA, toSHA);
128
+ await generateChangelogForProjects(tree, args, projectGraph, commits, projectsVersionData, postGitTasks, releaseGroup, projectNodes);
129
+ }
120
130
  }
121
131
  return await applyChangesAndExit(args, nxReleaseConfig, tree, toSHA, postGitTasks, commitMessageValues, gitTagValues);
122
132
  }
@@ -225,7 +235,7 @@ function resolveChangelogRenderer(changelogRendererPath) {
225
235
  }
226
236
  return changelogRenderer;
227
237
  }
228
- async function generateChangelogForWorkspace(tree, args, projectGraph, nxReleaseConfig, workspaceChangelogVersion, commits, postGitTasks) {
238
+ async function generateChangelogForWorkspace(tree, args, projectGraph, nxReleaseConfig, workspaceChangelogVersion, commits, postGitTasks, explicitWorkspaceChangelogConfig) {
229
239
  const config = nxReleaseConfig.changelog.workspaceChangelog;
230
240
  const isEnabled = args.workspaceChangelog ?? config;
231
241
  // The entire feature is disabled at the workspace level, exit early
@@ -243,6 +253,25 @@ async function generateChangelogForWorkspace(tree, args, projectGraph, nxRelease
243
253
  if (!workspaceChangelogVersion && args.workspaceChangelog) {
244
254
  throw new Error(`Workspace changelog is enabled but no overall version was provided. Please provide an explicit version using --version`);
245
255
  }
256
+ if (Object.entries(nxReleaseConfig.groups).length > 1 ||
257
+ Object.values(nxReleaseConfig.groups)[0].projectsRelationship ===
258
+ 'independent') {
259
+ if (explicitWorkspaceChangelogConfig !== undefined &&
260
+ explicitWorkspaceChangelogConfig !== false) {
261
+ // only warn the user if they explicitly enabled workspace changelog
262
+ // if they didn't, then just disable it quietly, since it was enabled by default
263
+ output_1.output.warn({
264
+ title: `Workspace changelog is enabled, but you have multiple release groups configured or have configured an independent projects relationship. This is not supported, so workspace changelog will be disabled.`,
265
+ bodyLines: [
266
+ `A single workspace version cannot be determined when defining multiple release groups because versions differ between each group.`,
267
+ `Also, a single workspace version also cannot be determined when using independent projects because versions differ between each project.`,
268
+ `If you want to generate a workspace changelog, please use a single release group.`,
269
+ `Alternatively, project level changelogs can be enabled with the "projectChangelogs" property.`,
270
+ ],
271
+ });
272
+ }
273
+ return;
274
+ }
246
275
  // Only trigger interactive mode for the workspace changelog if the user explicitly requested it via "all" or "workspace"
247
276
  const interactive = args.interactive === 'all' || args.interactive === 'workspace';
248
277
  const dryRun = !!args.dryRun;
@@ -593,3 +622,16 @@ function checkChangelogFilesEnabled(nxReleaseConfig) {
593
622
  }
594
623
  return false;
595
624
  }
625
+ async function getCommits(fromSHA, toSHA) {
626
+ const rawCommits = await (0, git_1.getGitDiff)(fromSHA, toSHA);
627
+ // Parse as conventional commits
628
+ return (0, git_1.parseCommits)(rawCommits).filter((c) => {
629
+ const type = c.type;
630
+ // Always ignore non user-facing commits for now
631
+ // TODO: allow this filter to be configurable via config in a future release
632
+ if (type === 'feat' || type === 'fix' || type === 'perf') {
633
+ return true;
634
+ }
635
+ return false;
636
+ });
637
+ }
@@ -2,9 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.formatReferences = exports.getGithubReleaseByTag = exports.resolveGithubToken = exports.createOrUpdateGithubRelease = exports.getGitHubRepoSlug = void 0;
4
4
  const chalk = require("chalk");
5
+ const enquirer_1 = require("enquirer");
5
6
  const node_child_process_1 = require("node:child_process");
6
7
  const node_fs_1 = require("node:fs");
7
8
  const node_os_1 = require("node:os");
9
+ const output_1 = require("../../../utils/output");
8
10
  const path_1 = require("../../../utils/path");
9
11
  // axios types and values don't seem to match
10
12
  const _axios = require("axios");
@@ -32,6 +34,66 @@ function getGitHubRepoSlug(remoteName = 'origin') {
32
34
  exports.getGitHubRepoSlug = getGitHubRepoSlug;
33
35
  async function createOrUpdateGithubRelease(githubRequestConfig, release, existingGithubReleaseForVersion) {
34
36
  const result = await syncGithubRelease(githubRequestConfig, release, existingGithubReleaseForVersion);
37
+ /**
38
+ * If something went wrong POSTing to Github we can still pre-populate the web form on github.com
39
+ * to allow the user to manually complete the release if they so choose.
40
+ */
41
+ if (result.status === 'manual') {
42
+ if (result.error) {
43
+ process.exitCode = 1;
44
+ if (result.error.response?.data) {
45
+ // There's a nicely formatted error from GitHub we can display to the user
46
+ output_1.output.error({
47
+ title: `A GitHub API Error occurred when creating/updating the release`,
48
+ bodyLines: [
49
+ `GitHub Error: ${JSON.stringify(result.error.response.data)}`,
50
+ `---`,
51
+ `Request Data:`,
52
+ `Repo: ${githubRequestConfig.repo}`,
53
+ `Token: ${githubRequestConfig.token}`,
54
+ `Body: ${JSON.stringify(result.requestData)}`,
55
+ ],
56
+ });
57
+ }
58
+ else {
59
+ console.log(result.error);
60
+ console.error(`An unknown error occurred while trying to create a release on GitHub, please report this on https://github.com/nrwl/nx (NOTE: make sure to redact your GitHub token from the error message!)`);
61
+ }
62
+ }
63
+ const reply = await (0, enquirer_1.prompt)([
64
+ {
65
+ name: 'open',
66
+ message: 'Do you want to finish creating the release manually in your browser?',
67
+ type: 'autocomplete',
68
+ choices: [
69
+ {
70
+ name: 'Yes',
71
+ hint: 'It will pre-populate the form for you',
72
+ },
73
+ {
74
+ name: 'No',
75
+ },
76
+ ],
77
+ initial: 'Yes',
78
+ },
79
+ ]).catch(() => {
80
+ return { open: 'No' };
81
+ });
82
+ if (reply.open === 'Yes') {
83
+ const open = require('open');
84
+ await open(result.url)
85
+ .then(() => {
86
+ console.info(`\nFollow up in the browser to manually create the release:\n\n` +
87
+ chalk.underline(chalk.cyan(result.url)) +
88
+ `\n`);
89
+ })
90
+ .catch(() => {
91
+ console.info(`Open this link to manually create a release: \n` +
92
+ chalk.underline(chalk.cyan(result.url)) +
93
+ '\n');
94
+ });
95
+ }
96
+ }
35
97
  /**
36
98
  * If something went wrong POSTing to Github we can still pre-populate the web form on github.com
37
99
  * to allow the user to manually complete the release.
@@ -79,6 +141,7 @@ async function syncGithubRelease(githubRequestConfig, release, existingGithubRel
79
141
  status: 'manual',
80
142
  error,
81
143
  url: githubNewReleaseURL(githubRequestConfig, release),
144
+ requestData: ghRelease,
82
145
  };
83
146
  }
84
147
  }
@@ -139,7 +202,7 @@ async function updateGithubRelease(config, id, body) {
139
202
  });
140
203
  }
141
204
  function githubNewReleaseURL(config, release) {
142
- return `https://github.com/${config.repo}/releases/new?tag=v${release.version}&title=v${release.version}&body=${encodeURIComponent(release.body)}`;
205
+ return `https://github.com/${config.repo}/releases/new?tag=${release.version}&title=${release.version}&body=${encodeURIComponent(release.body)}`;
143
206
  }
144
207
  const providerToRefSpec = {
145
208
  github: { 'pull-request': 'pull', hash: 'commit', issue: 'issues' },
@@ -111,12 +111,14 @@ function createCommitMessageValues(releaseGroups, releaseGroupToFilteredProjects
111
111
  if (releaseGroup.projectsRelationship === 'independent') {
112
112
  for (const project of releaseGroupProjectNames) {
113
113
  const projectVersionData = versionData[project];
114
- const releaseVersion = new ReleaseVersion({
115
- version: projectVersionData.newVersion,
116
- releaseTagPattern: releaseGroup.releaseTagPattern,
117
- projectName: project,
118
- });
119
- commitMessageValues.push(`- project: ${project} ${releaseVersion.rawVersion}`);
114
+ if (projectVersionData.newVersion !== null) {
115
+ const releaseVersion = new ReleaseVersion({
116
+ version: projectVersionData.newVersion,
117
+ releaseTagPattern: releaseGroup.releaseTagPattern,
118
+ projectName: project,
119
+ });
120
+ commitMessageValues.push(`- project: ${project} ${releaseVersion.rawVersion}`);
121
+ }
120
122
  }
121
123
  continue;
122
124
  }
@@ -150,10 +152,12 @@ function createGitTagValues(releaseGroups, releaseGroupToFilteredProjects, versi
150
152
  if (releaseGroup.projectsRelationship === 'independent') {
151
153
  for (const project of releaseGroupProjectNames) {
152
154
  const projectVersionData = versionData[project];
153
- tags.push((0, utils_1.interpolate)(releaseGroup.releaseTagPattern, {
154
- version: projectVersionData.newVersion,
155
- projectName: project,
156
- }));
155
+ if (projectVersionData.newVersion !== null) {
156
+ tags.push((0, utils_1.interpolate)(releaseGroup.releaseTagPattern, {
157
+ version: projectVersionData.newVersion,
158
+ projectName: project,
159
+ }));
160
+ }
157
161
  }
158
162
  continue;
159
163
  }
@@ -67,6 +67,23 @@ async function releaseVersion(args) {
67
67
  : [];
68
68
  (0, shared_1.handleDuplicateGitTags)(gitTagValues);
69
69
  printAndFlushChanges(tree, !!args.dryRun);
70
+ const changedFiles = tree.listChanges().map((f) => f.path);
71
+ // No further actions are necessary in this scenario (e.g. if conventional commits detected no changes)
72
+ if (!changedFiles.length) {
73
+ return {
74
+ // An overall workspace version cannot be relevant when filtering to independent projects
75
+ workspaceVersion: undefined,
76
+ projectsVersionData: versionData,
77
+ };
78
+ }
79
+ if (args.stageChanges) {
80
+ devkit_exports_1.output.logSingleLine(`Staging changed files with git because --stage-changes was set`);
81
+ await (0, git_1.gitAdd)({
82
+ changedFiles,
83
+ dryRun: args.dryRun,
84
+ verbose: args.verbose,
85
+ });
86
+ }
70
87
  if (args.gitCommit ?? nxReleaseConfig.version.git.commit) {
71
88
  await (0, shared_1.commitChanges)(tree.listChanges().map((f) => f.path), !!args.dryRun, !!args.verbose, (0, shared_1.createCommitMessageValues)(releaseGroups, releaseGroupToFilteredProjects, versionData, userCommitMessage), args.gitCommitArgs || nxReleaseConfig.version.git.commitArgs);
72
89
  }