nx 17.2.5 → 17.2.7

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,9 +1,4 @@
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>
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>
7
2
 
8
3
  <div style="text-align: center;">
9
4
 
@@ -20,9 +15,9 @@
20
15
 
21
16
  <hr>
22
17
 
23
- # Nx: Smart Monorepos · Fast CI
18
+ # Nx: Smart, Fast and Extensible Build System
24
19
 
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.
20
+ Nx is a next generation build system with first class monorepo support and powerful integrations.
26
21
 
27
22
  ## Getting Started
28
23
 
@@ -62,5 +57,5 @@ npx nx@latest init
62
57
  - [Blog Posts About Nx](https://blog.nrwl.io/nx/home)
63
58
 
64
59
  <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"
65
- width="100%" alt="Nx - Smart Monorepos · Fast CI"></a></p>
60
+ width="100%" alt="Nx - Smart, Fast and Extensible Build System"></a></p>
66
61
 
@@ -36,7 +36,17 @@ export interface DefaultChangelogRenderOptions extends ChangelogRenderOptions {
36
36
  * Whether or not the commit authors should be added to the bottom of the changelog in a "Thank You"
37
37
  * section. Defaults to true.
38
38
  */
39
- includeAuthors?: boolean;
39
+ authors?: boolean;
40
+ /**
41
+ * Whether or not the commit references (such as commit and/or PR links) should be included in the changelog.
42
+ * Defaults to true.
43
+ */
44
+ commitReferences?: boolean;
45
+ /**
46
+ * Whether or not to include the date in the version title. It can be set to false to disable it, or true to enable
47
+ * with the default of (YYYY-MM-DD). Defaults to true.
48
+ */
49
+ versionTitleDate?: boolean;
40
50
  }
41
51
  /**
42
52
  * The default ChangelogRenderer implementation that nx exports for the common case of generating markdown
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const semver_1 = require("semver");
3
4
  const github_1 = require("../src/command-line/release/utils/github");
4
5
  const shared_1 = require("../src/command-line/release/utils/shared");
5
6
  // axios types and values don't seem to match
@@ -44,12 +45,12 @@ const defaultChangelogRenderer = async ({ projectGraph, commits, releaseVersion,
44
45
  // No changes for the workspace
45
46
  if (commits.length === 0) {
46
47
  if (entryWhenNoChanges) {
47
- markdownLines.push('', `## ${releaseVersion}\n\n${entryWhenNoChanges}`, '');
48
+ markdownLines.push('', `${createVersionTitle(releaseVersion, changelogRenderOptions)}\n\n${entryWhenNoChanges}`, '');
48
49
  }
49
50
  return markdownLines.join('\n').trim();
50
51
  }
51
52
  const typeGroups = groupBy(commits, 'type');
52
- markdownLines.push('', `## ${releaseVersion}`, '');
53
+ markdownLines.push('', createVersionTitle(releaseVersion, changelogRenderOptions), '');
53
54
  for (const type of Object.keys(commitTypes)) {
54
55
  const group = typeGroups[type];
55
56
  if (!group || group.length === 0) {
@@ -67,10 +68,13 @@ const defaultChangelogRenderer = async ({ projectGraph, commits, releaseVersion,
67
68
  for (const scope of scopesSortedAlphabetically) {
68
69
  const commits = commitsGroupedByScope[scope];
69
70
  for (const commit of commits) {
70
- const line = formatCommit(commit, repoSlug);
71
+ const line = formatCommit(commit, changelogRenderOptions, repoSlug);
71
72
  markdownLines.push(line);
72
73
  if (commit.isBreaking) {
73
- breakingChanges.push(line);
74
+ const breakingChangeExplanation = extractBreakingChangeExplanation(commit.body);
75
+ breakingChanges.push(breakingChangeExplanation
76
+ ? `- ${commit.scope ? `**${commit.scope.trim()}:** ` : ''}${breakingChangeExplanation}`
77
+ : line);
74
78
  }
75
79
  }
76
80
  }
@@ -82,11 +86,11 @@ const defaultChangelogRenderer = async ({ projectGraph, commits, releaseVersion,
82
86
  // Generating for a named project, but that project has no relevant changes in the current set of commits, exit early
83
87
  if (relevantCommits.length === 0) {
84
88
  if (entryWhenNoChanges) {
85
- markdownLines.push('', `## ${releaseVersion}\n\n${entryWhenNoChanges}`, '');
89
+ markdownLines.push('', `${createVersionTitle(releaseVersion, changelogRenderOptions)}\n\n${entryWhenNoChanges}`, '');
86
90
  }
87
91
  return markdownLines.join('\n').trim();
88
92
  }
89
- markdownLines.push('', `## ${releaseVersion}`, '');
93
+ markdownLines.push('', createVersionTitle(releaseVersion, changelogRenderOptions), '');
90
94
  const typeGroups = groupBy(
91
95
  // Sort the relevant commits to have the unscoped commits first, before grouping by type
92
96
  relevantCommits.sort((a, b) => (b.scope ? 1 : 0) - (a.scope ? 1 : 0)), 'type');
@@ -98,10 +102,13 @@ const defaultChangelogRenderer = async ({ projectGraph, commits, releaseVersion,
98
102
  markdownLines.push('', `### ${commitTypes[type].title}`, '');
99
103
  const commitsInChronologicalOrder = group.reverse();
100
104
  for (const commit of commitsInChronologicalOrder) {
101
- const line = formatCommit(commit, repoSlug);
105
+ const line = formatCommit(commit, changelogRenderOptions, repoSlug);
102
106
  markdownLines.push(line + '\n');
103
107
  if (commit.isBreaking) {
104
- breakingChanges.push(line);
108
+ const breakingChangeExplanation = extractBreakingChangeExplanation(commit.body);
109
+ breakingChanges.push(breakingChangeExplanation
110
+ ? `- ${commit.scope ? `**${commit.scope.trim()}:** ` : ''}${breakingChangeExplanation}`
111
+ : line);
105
112
  }
106
113
  }
107
114
  }
@@ -109,7 +116,7 @@ const defaultChangelogRenderer = async ({ projectGraph, commits, releaseVersion,
109
116
  if (breakingChanges.length > 0) {
110
117
  markdownLines.push('', '#### ⚠️ Breaking Changes', '', ...breakingChanges);
111
118
  }
112
- if (changelogRenderOptions.includeAuthors) {
119
+ if (changelogRenderOptions.authors) {
113
120
  const _authors = new Map();
114
121
  for (const commit of commits) {
115
122
  if (!commit.author) {
@@ -184,13 +191,48 @@ function groupBy(items, key) {
184
191
  }
185
192
  return groups;
186
193
  }
187
- function formatCommit(commit, repoSlug) {
194
+ function formatCommit(commit, changelogRenderOptions, repoSlug) {
188
195
  let commitLine = '- ' +
189
- (commit.scope ? `**${commit.scope.trim()}:** ` : '') +
190
196
  (commit.isBreaking ? '⚠️ ' : '') +
197
+ (commit.scope ? `**${commit.scope.trim()}:** ` : '') +
191
198
  commit.description;
192
- if (repoSlug) {
199
+ if (repoSlug && changelogRenderOptions.commitReferences) {
193
200
  commitLine += (0, github_1.formatReferences)(commit.references, repoSlug);
194
201
  }
195
202
  return commitLine;
196
203
  }
204
+ /**
205
+ * It is common to add further information about a breaking change in the commit body,
206
+ * and it is naturally that information that should be included in the BREAKING CHANGES
207
+ * section of changelog, rather than repeating the commit title/description.
208
+ */
209
+ function extractBreakingChangeExplanation(message) {
210
+ const breakingChangeIdentifier = 'BREAKING CHANGE:';
211
+ const startIndex = message.indexOf(breakingChangeIdentifier);
212
+ if (startIndex === -1) {
213
+ // "BREAKING CHANGE:" not found in the message
214
+ return null;
215
+ }
216
+ const startOfBreakingChange = startIndex + breakingChangeIdentifier.length;
217
+ const endOfBreakingChange = message.indexOf('\n', startOfBreakingChange);
218
+ if (endOfBreakingChange === -1) {
219
+ // No newline character found, extract till the end of the message
220
+ return message.substring(startOfBreakingChange).trim();
221
+ }
222
+ // Extract and return the breaking change message
223
+ return message.substring(startOfBreakingChange, endOfBreakingChange).trim();
224
+ }
225
+ function createVersionTitle(version, changelogRenderOptions) {
226
+ // Normalize by removing any leading `v` during comparison
227
+ const isMajorVersion = `${(0, semver_1.major)(version)}.0.0` === version.replace(/^v/, '');
228
+ let maybeDateStr = '';
229
+ if (changelogRenderOptions.versionTitleDate) {
230
+ // YYYY-MM-DD
231
+ const dateStr = new Date().toISOString().slice(0, 10);
232
+ maybeDateStr = ` (${dateStr})`;
233
+ }
234
+ if (isMajorVersion) {
235
+ return `# ${version}${maybeDateStr}`;
236
+ }
237
+ return `## ${version}${maybeDateStr}`;
238
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nx",
3
- "version": "v17.2.5",
3
+ "version": "17.2.7",
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": "v17.2.5"
69
+ "@nrwl/tao": "17.2.7"
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": "v17.2.5",
85
- "@nx/nx-darwin-arm64": "v17.2.5",
86
- "@nx/nx-linux-x64-gnu": "v17.2.5",
87
- "@nx/nx-linux-x64-musl": "v17.2.5",
88
- "@nx/nx-win32-x64-msvc": "v17.2.5",
89
- "@nx/nx-linux-arm64-gnu": "v17.2.5",
90
- "@nx/nx-linux-arm64-musl": "v17.2.5",
91
- "@nx/nx-linux-arm-gnueabihf": "v17.2.5",
92
- "@nx/nx-win32-arm64-msvc": "v17.2.5",
93
- "@nx/nx-freebsd-x64": "v17.2.5"
84
+ "@nx/nx-darwin-x64": "17.2.7",
85
+ "@nx/nx-darwin-arm64": "17.2.7",
86
+ "@nx/nx-linux-x64-gnu": "17.2.7",
87
+ "@nx/nx-linux-x64-musl": "17.2.7",
88
+ "@nx/nx-win32-x64-msvc": "17.2.7",
89
+ "@nx/nx-linux-arm64-gnu": "17.2.7",
90
+ "@nx/nx-linux-arm64-musl": "17.2.7",
91
+ "@nx/nx-linux-arm-gnueabihf": "17.2.7",
92
+ "@nx/nx-win32-arm64-msvc": "17.2.7",
93
+ "@nx/nx-freebsd-x64": "17.2.7"
94
94
  },
95
95
  "nx-migrations": {
96
96
  "migrations": "./migrations.json",
@@ -135,8 +135,6 @@
135
135
  "@nrwl/react-native",
136
136
  "@nx/rollup",
137
137
  "@nrwl/rollup",
138
- "@nx/remix",
139
- "@nrwl/remix",
140
138
  "@nx/storybook",
141
139
  "@nrwl/storybook",
142
140
  "@nrwl/tao",
@@ -36,7 +36,7 @@ exports.parserConfiguration = {
36
36
  */
37
37
  exports.commandsObject = yargs
38
38
  .parserConfiguration(exports.parserConfiguration)
39
- .usage(chalk.bold('Smart Monorepos · Fast CI'))
39
+ .usage(chalk.bold('Smart, Fast and Extensible Build System'))
40
40
  .demandCommand(1, '')
41
41
  .command(command_object_1.yargsAffectedBuildCommand)
42
42
  .command(command_object_1.yargsAffectedCommand)
@@ -227,15 +227,20 @@ function resolveChangelogRenderer(changelogRendererPath) {
227
227
  }
228
228
  async function generateChangelogForWorkspace(tree, args, projectGraph, nxReleaseConfig, workspaceChangelogVersion, commits, postGitTasks) {
229
229
  const config = nxReleaseConfig.changelog.workspaceChangelog;
230
+ const isEnabled = args.workspaceChangelog ?? config;
230
231
  // The entire feature is disabled at the workspace level, exit early
231
- if (config === false) {
232
+ if (isEnabled === false) {
232
233
  return;
233
234
  }
234
235
  // If explicitly null it must mean that no changes were detected (e.g. when using conventional commits), so do nothing
235
236
  if (workspaceChangelogVersion === null) {
236
237
  return;
237
238
  }
238
- if (!workspaceChangelogVersion) {
239
+ // The user explicitly passed workspaceChangelog=true but does not have a workspace changelog config in nx.json
240
+ if (!config) {
241
+ throw new Error(`Workspace changelog is enabled but no configuration was provided. Please provide a workspaceChangelog object in your nx.json`);
242
+ }
243
+ if (!workspaceChangelogVersion && args.workspaceChangelog) {
239
244
  throw new Error(`Workspace changelog is enabled but no overall version was provided. Please provide an explicit version using --version`);
240
245
  }
241
246
  // Only trigger interactive mode for the workspace changelog if the user explicitly requested it via "all" or "workspace"
@@ -270,9 +275,7 @@ async function generateChangelogForWorkspace(tree, args, projectGraph, nxRelease
270
275
  output_1.output.log({
271
276
  title: logTitle,
272
277
  });
273
- const githubRepoSlug = config.createRelease === 'github'
274
- ? (0, github_1.getGitHubRepoSlug)(gitRemote)
275
- : undefined;
278
+ const githubRepoSlug = (0, github_1.getGitHubRepoSlug)(gitRemote);
276
279
  let contents = await changelogRenderer({
277
280
  projectGraph,
278
281
  commits,
@@ -565,7 +568,7 @@ async function generateChangelogForProjects(tree, args, projectGraph, commits, p
565
568
  if (!dryRun) {
566
569
  postGitTasks.push(async (latestCommit) => {
567
570
  // Before we can create/update the release we need to ensure the commit exists on the remote
568
- await (0, git_1.gitPush)();
571
+ await (0, git_1.gitPush)(gitRemote);
569
572
  await (0, github_1.createOrUpdateGithubRelease)(githubRequestConfig, {
570
573
  version: releaseVersion.gitTag,
571
574
  prerelease: releaseVersion.isPrerelease,
@@ -27,6 +27,7 @@ export type ChangelogOptions = NxReleaseArgs & GitCommitAndTagOptions & {
27
27
  from?: string;
28
28
  interactive?: string;
29
29
  gitRemote?: string;
30
+ workspaceChangelog?: boolean;
30
31
  };
31
32
  export type PublishOptions = NxReleaseArgs & Partial<RunManyOptions> & {
32
33
  outputStyle?: OutputStyle;
@@ -35,5 +36,9 @@ export type PublishOptions = NxReleaseArgs & Partial<RunManyOptions> & {
35
36
  tag?: string;
36
37
  otp?: number;
37
38
  };
39
+ export type ReleaseOptions = NxReleaseArgs & {
40
+ yes?: boolean;
41
+ skipPublish?: boolean;
42
+ };
38
43
  export declare const yargsReleaseCommand: CommandModule<Record<string, unknown>, NxReleaseArgs>;
39
44
  export {};
@@ -8,6 +8,7 @@ exports.yargsReleaseCommand = {
8
8
  command: 'release',
9
9
  describe: '**ALPHA**: Orchestrate versioning and publishing of applications and libraries',
10
10
  builder: (yargs) => yargs
11
+ .command(releaseCommand)
11
12
  .command(versionCommand)
12
13
  .command(changelogCommand)
13
14
  .command(publishCommand)
@@ -26,7 +27,7 @@ exports.yargsReleaseCommand = {
26
27
  coerce: shared_options_1.parseCSV,
27
28
  describe: 'Projects to run. (comma/space delimited project names and/or patterns)',
28
29
  })
29
- .option('dryRun', {
30
+ .option('dry-run', {
30
31
  describe: 'Preview the changes without updating files/creating releases',
31
32
  alias: 'd',
32
33
  type: 'boolean',
@@ -55,6 +56,37 @@ exports.yargsReleaseCommand = {
55
56
  process.exit(1);
56
57
  },
57
58
  };
59
+ const releaseCommand = {
60
+ command: '$0 [specifier]',
61
+ describe: 'Create a version and release for the workspace, generate a changelog, and optionally publish the packages',
62
+ builder: (yargs) => yargs
63
+ .positional('specifier', {
64
+ type: 'string',
65
+ describe: 'Exact version or semver keyword to apply to the selected release group.',
66
+ })
67
+ .option('yes', {
68
+ type: 'boolean',
69
+ alias: 'y',
70
+ description: 'Automatically answer yes to the confirmation prompt for publishing',
71
+ })
72
+ .option('skip-publish', {
73
+ type: 'boolean',
74
+ description: 'Skip publishing by automatically answering no to the confirmation prompt for publishing',
75
+ })
76
+ .check((argv) => {
77
+ if (argv.yes !== undefined && argv.skipPublish !== undefined) {
78
+ throw new Error('The --yes and --skip-publish options are mutually exclusive, please use one or the other.');
79
+ }
80
+ return true;
81
+ }),
82
+ handler: (args) => Promise.resolve().then(() => require('./release')).then((m) => m.releaseCLIHandler(args))
83
+ .then((versionDataOrExitCode) => {
84
+ if (typeof versionDataOrExitCode === 'number') {
85
+ return process.exit(versionDataOrExitCode);
86
+ }
87
+ process.exit(0);
88
+ }),
89
+ };
58
90
  const versionCommand = {
59
91
  command: 'version [specifier]',
60
92
  aliases: ['v'],
@@ -108,7 +140,7 @@ const changelogCommand = {
108
140
  description: 'Interactively modify changelog markdown contents in your code editor before applying the changes. You can set it to be interactive for all changelogs, or only the workspace level, or only the project level',
109
141
  choices: ['all', 'workspace', 'projects'],
110
142
  })
111
- .option('gitRemote', {
143
+ .option('git-remote', {
112
144
  type: 'string',
113
145
  description: 'Alternate git remote in the form {user}/{repo} on which to create the Github release (useful for testing)',
114
146
  default: 'origin',
@@ -47,7 +47,9 @@ requiredTargetName) {
47
47
  file: '{workspaceRoot}/CHANGELOG.md',
48
48
  renderer: 'nx/changelog-renderer',
49
49
  renderOptions: {
50
- includeAuthors: true,
50
+ authors: true,
51
+ commitReferences: true,
52
+ versionTitleDate: true,
51
53
  },
52
54
  },
53
55
  // For projectChangelogs if the user has set any changelog config at all, then use one set of defaults, otherwise default to false for the whole feature
@@ -58,7 +60,9 @@ requiredTargetName) {
58
60
  entryWhenNoChanges: 'This was a version bump only for {projectName} to align it with other projects, there were no code changes.',
59
61
  renderer: 'nx/changelog-renderer',
60
62
  renderOptions: {
61
- includeAuthors: true,
63
+ authors: true,
64
+ commitReferences: true,
65
+ versionTitleDate: true,
62
66
  },
63
67
  }
64
68
  : false,
@@ -82,7 +86,9 @@ requiredTargetName) {
82
86
  file: '{projectRoot}/CHANGELOG.md',
83
87
  renderer: 'nx/changelog-renderer',
84
88
  renderOptions: {
85
- includeAuthors: true,
89
+ authors: true,
90
+ commitReferences: true,
91
+ versionTitleDate: true,
86
92
  },
87
93
  },
88
94
  releaseTagPattern:
@@ -10,3 +10,7 @@ export { releasePublish } from './publish';
10
10
  * @public
11
11
  */
12
12
  export { releaseVersion } from './version';
13
+ /**
14
+ * @public
15
+ */
16
+ export { release } from './release';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.releaseVersion = exports.releasePublish = exports.releaseChangelog = void 0;
3
+ exports.release = exports.releaseVersion = exports.releasePublish = exports.releaseChangelog = void 0;
4
4
  /**
5
5
  * @public
6
6
  */
@@ -16,3 +16,8 @@ Object.defineProperty(exports, "releasePublish", { enumerable: true, get: functi
16
16
  */
17
17
  var version_1 = require("./version");
18
18
  Object.defineProperty(exports, "releaseVersion", { enumerable: true, get: function () { return version_1.releaseVersion; } });
19
+ /**
20
+ * @public
21
+ */
22
+ var release_1 = require("./release");
23
+ Object.defineProperty(exports, "release", { enumerable: true, get: function () { return release_1.release; } });
@@ -76,6 +76,11 @@ async function runPublishOnProjects(args, projectGraph, nxJson, projectNames) {
76
76
  }
77
77
  if (args.dryRun) {
78
78
  overrides.dryRun = args.dryRun;
79
+ /**
80
+ * Ensure the env var is set too, so that any and all publish executors triggered
81
+ * indirectly via dependsOn can also pick up on the fact that this is a dry run.
82
+ */
83
+ process.env.NX_DRY_RUN = 'true';
79
84
  }
80
85
  if (args.verbose) {
81
86
  process.env.NX_VERBOSE_LOGGING = 'true';
@@ -0,0 +1,4 @@
1
+ import { ReleaseOptions, VersionOptions } from './command-object';
2
+ import { NxReleaseVersionResult } from './version';
3
+ export declare const releaseCLIHandler: (args: VersionOptions) => Promise<any>;
4
+ export declare function release(args: ReleaseOptions): Promise<NxReleaseVersionResult | number>;
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.release = exports.releaseCLIHandler = void 0;
4
+ const enquirer_1 = require("enquirer");
5
+ const nx_json_1 = require("../../config/nx-json");
6
+ const devkit_exports_1 = require("../../devkit-exports");
7
+ const project_graph_1 = require("../../project-graph/project-graph");
8
+ const params_1 = require("../../utils/params");
9
+ const changelog_1 = require("./changelog");
10
+ const config_1 = require("./config/config");
11
+ const publish_1 = require("./publish");
12
+ const resolve_nx_json_error_message_1 = require("./utils/resolve-nx-json-error-message");
13
+ const version_1 = require("./version");
14
+ const releaseCLIHandler = (args) => (0, params_1.handleErrors)(args.verbose, () => release(args));
15
+ exports.releaseCLIHandler = releaseCLIHandler;
16
+ async function release(args) {
17
+ const projectGraph = await (0, project_graph_1.createProjectGraphAsync)({ exitOnError: true });
18
+ const nxJson = (0, nx_json_1.readNxJson)();
19
+ if (args.verbose) {
20
+ process.env.NX_VERBOSE_LOGGING = 'true';
21
+ }
22
+ const hasVersionGitConfig = Object.keys(nxJson.release?.version?.git ?? {}).length > 0;
23
+ const hasChangelogGitConfig = Object.keys(nxJson.release?.changelog?.git ?? {}).length > 0;
24
+ if (hasVersionGitConfig || hasChangelogGitConfig) {
25
+ const jsonConfigErrorPath = hasVersionGitConfig
26
+ ? ['release', 'version', 'git']
27
+ : ['release', 'changelog', 'git'];
28
+ const nxJsonMessage = await (0, resolve_nx_json_error_message_1.resolveNxJsonConfigErrorMessage)(jsonConfigErrorPath);
29
+ devkit_exports_1.output.error({
30
+ title: `The 'release' top level command cannot be used with granular git configuration. Instead, configure git options in the 'release.git' property in nx.json.`,
31
+ bodyLines: [nxJsonMessage],
32
+ });
33
+ process.exit(1);
34
+ }
35
+ // Apply default configuration to any optional user configuration
36
+ const { error: configError, nxReleaseConfig } = await (0, config_1.createNxReleaseConfig)(projectGraph, nxJson.release, 'nx-release-publish');
37
+ if (configError) {
38
+ return await (0, config_1.handleNxReleaseConfigError)(configError);
39
+ }
40
+ const versionResult = await (0, version_1.releaseVersion)({
41
+ ...args,
42
+ // if enabled, committing and tagging will be handled by the changelog
43
+ // command, so we should only stage the changes in the version command
44
+ stageChanges: nxReleaseConfig.git?.commit,
45
+ gitCommit: false,
46
+ gitTag: false,
47
+ });
48
+ await (0, changelog_1.releaseChangelog)({
49
+ ...args,
50
+ versionData: versionResult.projectsVersionData,
51
+ version: versionResult.workspaceVersion,
52
+ workspaceChangelog: versionResult.workspaceVersion !== undefined,
53
+ });
54
+ let shouldPublish = !!args.yes && !args.skipPublish;
55
+ const shouldPromptPublishing = !args.yes && !args.skipPublish && !args.dryRun;
56
+ if (shouldPromptPublishing) {
57
+ shouldPublish = await promptForPublish();
58
+ }
59
+ if (shouldPublish) {
60
+ await (0, publish_1.releasePublish)(args);
61
+ }
62
+ else {
63
+ console.log('Skipped publishing packages.');
64
+ }
65
+ return versionResult;
66
+ }
67
+ exports.release = release;
68
+ async function promptForPublish() {
69
+ console.log('\n');
70
+ const reply = await (0, enquirer_1.prompt)([
71
+ {
72
+ name: 'confirmation',
73
+ message: 'Do you want to publish these versions?',
74
+ type: 'confirm',
75
+ },
76
+ ]);
77
+ console.log('\n');
78
+ return reply.confirmation;
79
+ }
@@ -48,7 +48,7 @@ export declare function gitTag({ tag, message, additionalArgs, dryRun, verbose,
48
48
  verbose?: boolean;
49
49
  logFn?: (message: string) => void;
50
50
  }): Promise<string>;
51
- export declare function gitPush(): Promise<void>;
51
+ export declare function gitPush(gitRemote?: string): Promise<void>;
52
52
  export declare function parseCommits(commits: RawGitCommit[]): GitCommit[];
53
53
  export declare function parseGitCommit(commit: RawGitCommit): GitCommit | null;
54
54
  export declare function getCommitHash(ref: string): Promise<string>;
@@ -155,7 +155,7 @@ async function gitTag({ tag, message, additionalArgs, dryRun, verbose, logFn, })
155
155
  }
156
156
  }
157
157
  exports.gitTag = gitTag;
158
- async function gitPush() {
158
+ async function gitPush(gitRemote) {
159
159
  try {
160
160
  await (0, exec_command_1.execCommand)('git', [
161
161
  'push',
@@ -163,6 +163,8 @@ async function gitPush() {
163
163
  '--follow-tags',
164
164
  '--no-verify',
165
165
  '--atomic',
166
+ // Set custom git remote if provided
167
+ ...(gitRemote ? [gitRemote] : []),
166
168
  ]);
167
169
  }
168
170
  catch (err) {
@@ -188,7 +190,7 @@ function parseGitCommit(commit) {
188
190
  return null;
189
191
  }
190
192
  const scope = match.groups.scope || '';
191
- const isBreaking = Boolean(match.groups.breaking);
193
+ const isBreaking = Boolean(match.groups.breaking) || commit.body.includes('BREAKING CHANGE:');
192
194
  let description = match.groups.description;
193
195
  // Extract references from message
194
196
  const references = [];
@@ -2,7 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseChangelogMarkdown = void 0;
4
4
  function parseChangelogMarkdown(contents) {
5
- const CHANGELOG_RELEASE_HEAD_RE = new RegExp('^#{2,}\\s+(\\d+\\.\\d+\\.\\d+)', 'gm');
5
+ /**
6
+ * The release header may include prerelease identifiers (e.g., -alpha.13),
7
+ * and major releases may use a single #, instead of the standard ## used
8
+ * for minor and patch releases. This regex matches all of these cases.
9
+ */
10
+ const CHANGELOG_RELEASE_HEAD_RE = new RegExp('^#+\\s*\\[?(\\d+\\.\\d+\\.\\d+(?:-[a-zA-Z0-9\\.]+)?)\\]?', 'gm');
6
11
  const headings = [...contents.matchAll(CHANGELOG_RELEASE_HEAD_RE)];
7
12
  const releases = [];
8
13
  for (let i = 0; i < headings.length; i++) {
@@ -15,7 +15,7 @@ export interface ReleaseVersionGeneratorSchema {
15
15
  currentVersionResolver?: 'registry' | 'disk' | 'git-tag';
16
16
  currentVersionResolverMetadata?: Record<string, unknown>;
17
17
  }
18
- interface NxReleaseVersionResult {
18
+ export interface NxReleaseVersionResult {
19
19
  /**
20
20
  * In one specific (and very common) case, an overall workspace version is relevant, for example when there is
21
21
  * only a single release group in which all projects have a fixed relationship to each other. In this case, the
@@ -47,7 +47,7 @@ export interface NxReleaseChangelogConfiguration {
47
47
  * is false by default.
48
48
  *
49
49
  * NOTE: if createRelease is set on a group of projects, it will cause the default releaseTagPattern of
50
- * "{projectName}@v{version}" to be used for those projects, even when versioning everything together.
50
+ * "{projectName}@{version}" to be used for those projects, even when versioning everything together.
51
51
  */
52
52
  createRelease?: 'github' | false;
53
53
  /**
@@ -198,13 +198,13 @@ interface NxReleaseConfiguration {
198
198
  };
199
199
  /**
200
200
  * Optionally override the git/release tag pattern to use. This field is the source of truth
201
- * for changelog generation and release tagging, as well as for conventional-commits parsing.
201
+ * for changelog generation and release tagging, as well as for conventional commits parsing.
202
202
  *
203
203
  * It supports interpolating the version as {version} and (if releasing independently or forcing
204
204
  * project level version control system releases) the project name as {projectName} within the string.
205
205
  *
206
- * The default releaseTagPattern for unified releases is: "v{version}"
207
- * The default releaseTagPattern for releases at the project level is: "{projectName}@v{version}"
206
+ * The default releaseTagPattern for fixed/unified releases is: "v{version}"
207
+ * The default releaseTagPattern for independent releases at the project level is: "{projectName}@{version}"
208
208
  */
209
209
  releaseTagPattern?: string;
210
210
  /**