nx 19.5.7 → 19.6.0-beta.1

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.
Files changed (77) hide show
  1. package/package.json +12 -12
  2. package/release/changelog-renderer/index.js +16 -1
  3. package/release/index.d.ts +1 -1
  4. package/release/index.js +2 -1
  5. package/schemas/nx-schema.json +3 -0
  6. package/src/adapter/compat.d.ts +1 -1
  7. package/src/adapter/compat.js +1 -0
  8. package/src/command-line/affected/affected.js +1 -1
  9. package/src/command-line/connect/connect-to-nx-cloud.js +2 -1
  10. package/src/command-line/init/init-v2.js +1 -1
  11. package/src/command-line/release/changelog.d.ts +2 -7
  12. package/src/command-line/release/changelog.js +415 -363
  13. package/src/command-line/release/command-object.d.ts +1 -0
  14. package/src/command-line/release/command-object.js +14 -0
  15. package/src/command-line/release/config/deep-merge-json.d.ts +1 -0
  16. package/src/command-line/release/config/deep-merge-json.js +28 -0
  17. package/src/command-line/release/config/version-plans.d.ts +5 -0
  18. package/src/command-line/release/config/version-plans.js +9 -5
  19. package/src/command-line/release/index.d.ts +16 -4
  20. package/src/command-line/release/index.js +23 -9
  21. package/src/command-line/release/plan.d.ts +2 -1
  22. package/src/command-line/release/plan.js +93 -100
  23. package/src/command-line/release/publish.d.ts +2 -6
  24. package/src/command-line/release/publish.js +67 -52
  25. package/src/command-line/release/release.d.ts +2 -1
  26. package/src/command-line/release/release.js +181 -165
  27. package/src/command-line/release/utils/generate-version-plan-content.js +2 -3
  28. package/src/command-line/release/utils/print-config.d.ts +7 -0
  29. package/src/command-line/release/utils/print-config.js +36 -0
  30. package/src/command-line/release/version.d.ts +7 -6
  31. package/src/command-line/release/version.js +179 -165
  32. package/src/command-line/run/run-one.js +1 -1
  33. package/src/command-line/run-many/run-many.js +1 -1
  34. package/src/command-line/yargs-utils/shared-options.d.ts +1 -0
  35. package/src/command-line/yargs-utils/shared-options.js +5 -0
  36. package/src/commands-runner/create-command-graph.js +4 -2
  37. package/src/config/nx-json.d.ts +6 -1
  38. package/src/core/graph/main.js +1 -1
  39. package/src/core/graph/styles.css +1 -1
  40. package/src/daemon/cache.d.ts +1 -0
  41. package/src/daemon/cache.js +25 -18
  42. package/src/daemon/client/client.js +9 -1
  43. package/src/daemon/message-types/force-shutdown.d.ts +5 -0
  44. package/src/daemon/message-types/force-shutdown.js +11 -0
  45. package/src/daemon/server/handle-force-shutdown.d.ts +5 -0
  46. package/src/daemon/server/handle-force-shutdown.js +18 -0
  47. package/src/daemon/server/handle-request-shutdown.js +2 -0
  48. package/src/daemon/server/server.d.ts +1 -0
  49. package/src/daemon/server/server.js +14 -0
  50. package/src/daemon/server/shutdown-utils.d.ts +2 -1
  51. package/src/daemon/server/shutdown-utils.js +11 -4
  52. package/src/daemon/server/watcher.js +3 -0
  53. package/src/devkit-internals.d.ts +1 -1
  54. package/src/devkit-internals.js +2 -1
  55. package/src/generators/utils/project-configuration.js +41 -11
  56. package/src/native/nx.wasm32-wasi.wasm +0 -0
  57. package/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.js +45 -11
  58. package/src/nx-cloud/models/onboarding-status.d.ts +1 -0
  59. package/src/nx-cloud/models/onboarding-status.js +2 -0
  60. package/src/nx-cloud/nx-cloud-tasks-runner-shell.d.ts +1 -0
  61. package/src/nx-cloud/utilities/axios.js +9 -2
  62. package/src/nx-cloud/utilities/is-workspace-claimed.d.ts +1 -0
  63. package/src/nx-cloud/utilities/is-workspace-claimed.js +24 -0
  64. package/src/nx-cloud/utilities/onboarding.d.ts +5 -0
  65. package/src/nx-cloud/utilities/onboarding.js +28 -0
  66. package/src/project-graph/plugins/internal-api.js +16 -5
  67. package/src/project-graph/plugins/isolation/messaging.d.ts +5 -1
  68. package/src/project-graph/plugins/isolation/messaging.js +1 -0
  69. package/src/project-graph/plugins/isolation/plugin-pool.js +4 -6
  70. package/src/project-graph/plugins/isolation/plugin-worker.js +15 -0
  71. package/src/project-graph/utils/project-configuration-utils.d.ts +1 -0
  72. package/src/project-graph/utils/project-configuration-utils.js +6 -2
  73. package/src/tasks-runner/run-command.js +6 -1
  74. package/src/utils/command-line-utils.d.ts +1 -0
  75. package/src/utils/nx-cloud-utils.js +3 -1
  76. package/src/utils/package-json.d.ts +2 -9
  77. package/src/utils/package-manager.js +12 -3
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.releaseChangelogCLIHandler = void 0;
4
- exports.releaseChangelog = releaseChangelog;
4
+ exports.createAPI = createAPI;
5
5
  exports.shouldCreateGitHubRelease = shouldCreateGitHubRelease;
6
6
  const chalk = require("chalk");
7
7
  const enquirer_1 = require("enquirer");
@@ -20,6 +20,7 @@ const params_1 = require("../../utils/params");
20
20
  const path_1 = require("../../utils/path");
21
21
  const workspace_root_1 = require("../../utils/workspace-root");
22
22
  const config_1 = require("./config/config");
23
+ const deep_merge_json_1 = require("./config/deep-merge-json");
23
24
  const filter_release_groups_1 = require("./config/filter-release-groups");
24
25
  const version_plans_1 = require("./config/version-plans");
25
26
  const git_1 = require("./utils/git");
@@ -27,295 +28,424 @@ const github_1 = require("./utils/github");
27
28
  const launch_editor_1 = require("./utils/launch-editor");
28
29
  const markdown_1 = require("./utils/markdown");
29
30
  const print_changes_1 = require("./utils/print-changes");
31
+ const print_config_1 = require("./utils/print-config");
30
32
  const resolve_changelog_renderer_1 = require("./utils/resolve-changelog-renderer");
31
33
  const resolve_nx_json_error_message_1 = require("./utils/resolve-nx-json-error-message");
32
34
  const shared_1 = require("./utils/shared");
33
- const releaseChangelogCLIHandler = (args) => (0, params_1.handleErrors)(args.verbose, () => releaseChangelog(args));
35
+ const releaseChangelogCLIHandler = (args) => (0, params_1.handleErrors)(args.verbose, () => createAPI({})(args));
34
36
  exports.releaseChangelogCLIHandler = releaseChangelogCLIHandler;
35
- /**
36
- * NOTE: This function is also exported for programmatic usage and forms part of the public API
37
- * of Nx. We intentionally do not wrap the implementation with handleErrors because users need
38
- * to have control over their own error handling when using the API.
39
- */
40
- async function releaseChangelog(args) {
41
- const projectGraph = await (0, project_graph_1.createProjectGraphAsync)({ exitOnError: true });
42
- const nxJson = (0, nx_json_1.readNxJson)();
43
- if (args.verbose) {
44
- process.env.NX_VERBOSE_LOGGING = 'true';
45
- }
46
- // Apply default configuration to any optional user configuration
47
- const { error: configError, nxReleaseConfig } = await (0, config_1.createNxReleaseConfig)(projectGraph, await (0, file_map_utils_1.createProjectFileMapUsingProjectGraph)(projectGraph), nxJson.release);
48
- if (configError) {
49
- return await (0, config_1.handleNxReleaseConfigError)(configError);
50
- }
51
- // The nx release top level command will always override these three git args. This is how we can tell
52
- // if the top level release command was used or if the user is using the changelog subcommand.
53
- // If the user explicitly overrides these args, then it doesn't matter if the top level config is set,
54
- // as all of the git options would be overridden anyway.
55
- if ((args.gitCommit === undefined ||
56
- args.gitTag === undefined ||
57
- args.stageChanges === undefined) &&
58
- nxJson.release?.git) {
59
- const nxJsonMessage = await (0, resolve_nx_json_error_message_1.resolveNxJsonConfigErrorMessage)([
60
- 'release',
61
- 'git',
62
- ]);
63
- output_1.output.error({
64
- title: `The "release.git" property in nx.json may not be used with the "nx release changelog" subcommand or programmatic API. Instead, configure git options for subcommands directly with "release.version.git" and "release.changelog.git".`,
65
- bodyLines: [nxJsonMessage],
66
- });
67
- process.exit(1);
68
- }
69
- const { error: filterError, releaseGroups, releaseGroupToFilteredProjects, } = (0, filter_release_groups_1.filterReleaseGroups)(projectGraph, nxReleaseConfig, args.projects, args.groups);
70
- if (filterError) {
71
- output_1.output.error(filterError);
72
- process.exit(1);
73
- }
74
- const rawVersionPlans = await (0, version_plans_1.readRawVersionPlans)();
75
- (0, version_plans_1.setVersionPlansOnGroups)(rawVersionPlans, releaseGroups, Object.keys(projectGraph.nodes));
76
- if (args.deleteVersionPlans === undefined) {
77
- // default to deleting version plans in this command instead of after versioning
78
- args.deleteVersionPlans = true;
79
- }
80
- const changelogGenerationEnabled = !!nxReleaseConfig.changelog.workspaceChangelog ||
81
- Object.values(nxReleaseConfig.groups).some((g) => g.changelog);
82
- if (!changelogGenerationEnabled) {
83
- output_1.output.warn({
84
- title: `Changelogs are disabled. No changelog entries will be generated`,
85
- bodyLines: [
86
- `To explicitly enable changelog generation, configure "release.changelog.workspaceChangelog" or "release.changelog.projectChangelogs" in nx.json.`,
87
- ],
88
- });
89
- return {};
90
- }
91
- const tree = new tree_1.FsTree(workspace_root_1.workspaceRoot, args.verbose);
92
- const useAutomaticFromRef = nxReleaseConfig.changelog?.automaticFromRef || args.firstRelease;
37
+ function createAPI(overrideReleaseConfig) {
93
38
  /**
94
- * For determining the versions to use within changelog files, there are a few different possibilities:
95
- * - the user is using the nx CLI, and therefore passes a single --version argument which represents the version for any and all changelog
96
- * files which will be generated (i.e. both the workspace changelog, and all project changelogs, depending on which of those has been enabled)
97
- * - the user is using the nxReleaseChangelog API programmatically, and:
98
- * - passes only a version property
99
- * - this works in the same way as described above for the CLI
100
- * - passes only a versionData object
101
- * - this is a special case where the user is providing a version for each project, and therefore the version argument is not needed
102
- * - NOTE: it is not possible to generate a workspace level changelog with only a versionData object, and this will produce an error
103
- * - passes both a version and a versionData object
104
- * - in this case, the version property will be used as the reference for the workspace changelog, and the versionData object will be used
105
- * to generate project changelogs
39
+ * NOTE: This function is also exported for programmatic usage and forms part of the public API
40
+ * of Nx. We intentionally do not wrap the implementation with handleErrors because users need
41
+ * to have control over their own error handling when using the API.
106
42
  */
107
- const { workspaceChangelogVersion, projectsVersionData } = resolveChangelogVersions(args, releaseGroups, releaseGroupToFilteredProjects);
108
- const to = args.to || 'HEAD';
109
- const toSHA = await (0, git_1.getCommitHash)(to);
110
- const headSHA = to === 'HEAD' ? toSHA : await (0, git_1.getCommitHash)('HEAD');
111
- /**
112
- * Protect the user against attempting to create a new commit when recreating an old release changelog,
113
- * this seems like it would always be unintentional.
114
- */
115
- const autoCommitEnabled = args.gitCommit ?? nxReleaseConfig.changelog.git.commit;
116
- if (autoCommitEnabled && headSHA !== toSHA) {
117
- 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`);
118
- }
119
- const commitMessage = args.gitCommitMessage || nxReleaseConfig.changelog.git.commitMessage;
120
- const commitMessageValues = (0, shared_1.createCommitMessageValues)(releaseGroups, releaseGroupToFilteredProjects, projectsVersionData, commitMessage);
121
- // Resolve any git tags as early as possible so that we can hard error in case of any duplicates before reaching the actual git command
122
- const gitTagValues = args.gitTag ?? nxReleaseConfig.changelog.git.tag
123
- ? (0, shared_1.createGitTagValues)(releaseGroups, releaseGroupToFilteredProjects, projectsVersionData)
124
- : [];
125
- (0, shared_1.handleDuplicateGitTags)(gitTagValues);
126
- const postGitTasks = [];
127
- let workspaceChangelogChanges = [];
128
- // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
129
- let workspaceChangelogCommits = [];
130
- // If there are multiple release groups, we'll just skip the workspace changelog anyway.
131
- const versionPlansEnabledForWorkspaceChangelog = releaseGroups[0].versionPlans;
132
- if (versionPlansEnabledForWorkspaceChangelog) {
133
- if (releaseGroups.length === 1) {
134
- const releaseGroup = releaseGroups[0];
135
- if (releaseGroup.projectsRelationship === 'fixed') {
136
- const versionPlans = releaseGroup.versionPlans;
137
- workspaceChangelogChanges = filterHiddenChanges(versionPlans
138
- .map((vp) => {
139
- const parsedMessage = (0, git_1.parseConventionalCommitsMessage)(vp.message);
140
- // only properly formatted conventional commits messages will be included in the changelog
141
- if (!parsedMessage) {
142
- return null;
143
- }
144
- return {
145
- type: parsedMessage.type,
146
- scope: parsedMessage.scope,
147
- description: parsedMessage.description,
148
- body: '',
149
- isBreaking: parsedMessage.breaking,
150
- githubReferences: [],
151
- };
152
- })
153
- .filter(Boolean), nxReleaseConfig.conventionalCommits);
154
- }
43
+ return async function releaseChangelog(args) {
44
+ const projectGraph = await (0, project_graph_1.createProjectGraphAsync)({ exitOnError: true });
45
+ const nxJson = (0, nx_json_1.readNxJson)();
46
+ const userProvidedReleaseConfig = (0, deep_merge_json_1.deepMergeJson)(nxJson.release ?? {}, overrideReleaseConfig ?? {});
47
+ if (args.verbose) {
48
+ process.env.NX_VERBOSE_LOGGING = 'true';
155
49
  }
156
- }
157
- else {
158
- let workspaceChangelogFromRef = args.from ||
159
- (await (0, git_1.getLatestGitTagForPattern)(nxReleaseConfig.releaseTagPattern))?.tag;
160
- if (!workspaceChangelogFromRef) {
161
- if (useAutomaticFromRef) {
162
- workspaceChangelogFromRef = await (0, git_1.getFirstGitCommit)();
163
- if (args.verbose) {
164
- console.log(`Determined workspace --from ref from the first commit in the workspace: ${workspaceChangelogFromRef}`);
165
- }
166
- }
167
- else {
168
- throw new Error(`Unable to determine the previous git tag. If this is the first release of your workspace, use the --first-release option or set the "release.changelog.automaticFromRef" config property in nx.json to generate a changelog from the first commit. Otherwise, be sure to configure the "release.releaseTagPattern" property in nx.json to match the structure of your repository's git tags.`);
169
- }
50
+ // Apply default configuration to any optional user configuration
51
+ const { error: configError, nxReleaseConfig } = await (0, config_1.createNxReleaseConfig)(projectGraph, await (0, file_map_utils_1.createProjectFileMapUsingProjectGraph)(projectGraph), userProvidedReleaseConfig);
52
+ if (configError) {
53
+ return await (0, config_1.handleNxReleaseConfigError)(configError);
170
54
  }
171
- // Make sure that the fromRef is actually resolvable
172
- const workspaceChangelogFromSHA = await (0, git_1.getCommitHash)(workspaceChangelogFromRef);
173
- workspaceChangelogCommits = await getCommits(workspaceChangelogFromSHA, toSHA);
174
- workspaceChangelogChanges = filterHiddenChanges(workspaceChangelogCommits.map((c) => {
175
- return {
176
- type: c.type,
177
- scope: c.scope,
178
- description: c.description,
179
- body: c.body,
180
- isBreaking: c.isBreaking,
181
- githubReferences: c.references,
182
- author: c.author,
183
- shortHash: c.shortHash,
184
- revertedHashes: c.revertedHashes,
185
- affectedProjects: '*',
186
- };
187
- }), nxReleaseConfig.conventionalCommits);
188
- }
189
- const workspaceChangelog = await generateChangelogForWorkspace({
190
- tree,
191
- args,
192
- projectGraph,
193
- nxReleaseConfig,
194
- workspaceChangelogVersion,
195
- changes: workspaceChangelogChanges,
55
+ // --print-config exits directly as it is not designed to be combined with any other programmatic operations
56
+ if (args.printConfig) {
57
+ return (0, print_config_1.printConfigAndExit)({
58
+ userProvidedReleaseConfig,
59
+ nxReleaseConfig,
60
+ isDebug: args.printConfig === 'debug',
61
+ });
62
+ }
63
+ // The nx release top level command will always override these three git args. This is how we can tell
64
+ // if the top level release command was used or if the user is using the changelog subcommand.
65
+ // If the user explicitly overrides these args, then it doesn't matter if the top level config is set,
66
+ // as all of the git options would be overridden anyway.
67
+ if ((args.gitCommit === undefined ||
68
+ args.gitTag === undefined ||
69
+ args.stageChanges === undefined) &&
70
+ userProvidedReleaseConfig.git) {
71
+ const nxJsonMessage = await (0, resolve_nx_json_error_message_1.resolveNxJsonConfigErrorMessage)([
72
+ 'release',
73
+ 'git',
74
+ ]);
75
+ output_1.output.error({
76
+ title: `The "release.git" property in nx.json may not be used with the "nx release changelog" subcommand or programmatic API. Instead, configure git options for subcommands directly with "release.version.git" and "release.changelog.git".`,
77
+ bodyLines: [nxJsonMessage],
78
+ });
79
+ process.exit(1);
80
+ }
81
+ const { error: filterError, releaseGroups, releaseGroupToFilteredProjects, } = (0, filter_release_groups_1.filterReleaseGroups)(projectGraph, nxReleaseConfig, args.projects, args.groups);
82
+ if (filterError) {
83
+ output_1.output.error(filterError);
84
+ process.exit(1);
85
+ }
86
+ const rawVersionPlans = await (0, version_plans_1.readRawVersionPlans)();
87
+ (0, version_plans_1.setVersionPlansOnGroups)(rawVersionPlans, releaseGroups, Object.keys(projectGraph.nodes));
88
+ if (args.deleteVersionPlans === undefined) {
89
+ // default to deleting version plans in this command instead of after versioning
90
+ args.deleteVersionPlans = true;
91
+ }
92
+ const changelogGenerationEnabled = !!nxReleaseConfig.changelog.workspaceChangelog ||
93
+ Object.values(nxReleaseConfig.groups).some((g) => g.changelog);
94
+ if (!changelogGenerationEnabled) {
95
+ output_1.output.warn({
96
+ title: `Changelogs are disabled. No changelog entries will be generated`,
97
+ bodyLines: [
98
+ `To explicitly enable changelog generation, configure "release.changelog.workspaceChangelog" or "release.changelog.projectChangelogs" in nx.json.`,
99
+ ],
100
+ });
101
+ return {};
102
+ }
103
+ const tree = new tree_1.FsTree(workspace_root_1.workspaceRoot, args.verbose);
104
+ const useAutomaticFromRef = nxReleaseConfig.changelog?.automaticFromRef || args.firstRelease;
105
+ /**
106
+ * For determining the versions to use within changelog files, there are a few different possibilities:
107
+ * - the user is using the nx CLI, and therefore passes a single --version argument which represents the version for any and all changelog
108
+ * files which will be generated (i.e. both the workspace changelog, and all project changelogs, depending on which of those has been enabled)
109
+ * - the user is using the nxReleaseChangelog API programmatically, and:
110
+ * - passes only a version property
111
+ * - this works in the same way as described above for the CLI
112
+ * - passes only a versionData object
113
+ * - this is a special case where the user is providing a version for each project, and therefore the version argument is not needed
114
+ * - NOTE: it is not possible to generate a workspace level changelog with only a versionData object, and this will produce an error
115
+ * - passes both a version and a versionData object
116
+ * - in this case, the version property will be used as the reference for the workspace changelog, and the versionData object will be used
117
+ * to generate project changelogs
118
+ */
119
+ const { workspaceChangelogVersion, projectsVersionData } = resolveChangelogVersions(args, releaseGroups, releaseGroupToFilteredProjects);
120
+ const to = args.to || 'HEAD';
121
+ const toSHA = await (0, git_1.getCommitHash)(to);
122
+ const headSHA = to === 'HEAD' ? toSHA : await (0, git_1.getCommitHash)('HEAD');
123
+ /**
124
+ * Protect the user against attempting to create a new commit when recreating an old release changelog,
125
+ * this seems like it would always be unintentional.
126
+ */
127
+ const autoCommitEnabled = args.gitCommit ?? nxReleaseConfig.changelog.git.commit;
128
+ if (autoCommitEnabled && headSHA !== toSHA) {
129
+ 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`);
130
+ }
131
+ const commitMessage = args.gitCommitMessage || nxReleaseConfig.changelog.git.commitMessage;
132
+ const commitMessageValues = (0, shared_1.createCommitMessageValues)(releaseGroups, releaseGroupToFilteredProjects, projectsVersionData, commitMessage);
133
+ // Resolve any git tags as early as possible so that we can hard error in case of any duplicates before reaching the actual git command
134
+ const gitTagValues = args.gitTag ?? nxReleaseConfig.changelog.git.tag
135
+ ? (0, shared_1.createGitTagValues)(releaseGroups, releaseGroupToFilteredProjects, projectsVersionData)
136
+ : [];
137
+ (0, shared_1.handleDuplicateGitTags)(gitTagValues);
138
+ const postGitTasks = [];
139
+ let workspaceChangelogChanges = [];
196
140
  // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
197
- commits: filterHiddenCommits(workspaceChangelogCommits, nxReleaseConfig.conventionalCommits),
198
- });
199
- if (workspaceChangelog &&
200
- shouldCreateGitHubRelease(nxReleaseConfig.changelog.workspaceChangelog, args.createRelease)) {
201
- let hasPushed = false;
202
- postGitTasks.push(async (latestCommit) => {
203
- if (!hasPushed) {
204
- output_1.output.logSingleLine(`Pushing to git remote`);
205
- // Before we can create/update the release we need to ensure the commit exists on the remote
206
- await (0, git_1.gitPush)({
207
- gitRemote: args.gitRemote,
208
- dryRun: args.dryRun,
209
- verbose: args.verbose,
210
- });
211
- hasPushed = true;
141
+ let workspaceChangelogCommits = [];
142
+ // If there are multiple release groups, we'll just skip the workspace changelog anyway.
143
+ const versionPlansEnabledForWorkspaceChangelog = releaseGroups[0].versionPlans;
144
+ if (versionPlansEnabledForWorkspaceChangelog) {
145
+ if (releaseGroups.length === 1) {
146
+ const releaseGroup = releaseGroups[0];
147
+ if (releaseGroup.projectsRelationship === 'fixed') {
148
+ const versionPlans = releaseGroup.versionPlans;
149
+ workspaceChangelogChanges = versionPlans
150
+ .flatMap((vp) => {
151
+ const releaseType = versionPlanSemverReleaseTypeToChangelogType(vp.groupVersionBump);
152
+ const changes = !vp.triggeredByProjects
153
+ ? {
154
+ type: releaseType.type,
155
+ scope: '',
156
+ description: vp.message,
157
+ body: '',
158
+ isBreaking: releaseType.isBreaking,
159
+ githubReferences: [],
160
+ affectedProjects: '*',
161
+ }
162
+ : vp.triggeredByProjects.map((project) => {
163
+ return {
164
+ type: releaseType.type,
165
+ scope: project,
166
+ description: vp.message,
167
+ body: '',
168
+ // TODO: what about github references?
169
+ isBreaking: releaseType.isBreaking,
170
+ githubReferences: [],
171
+ affectedProjects: [project],
172
+ };
173
+ });
174
+ return changes;
175
+ })
176
+ .filter(Boolean);
177
+ }
212
178
  }
213
- output_1.output.logSingleLine(`Creating GitHub Release`);
214
- await (0, github_1.createOrUpdateGithubRelease)(workspaceChangelog.releaseVersion, workspaceChangelog.contents, latestCommit, { dryRun: args.dryRun });
215
- });
216
- }
217
- /**
218
- * Compute any additional dependency bumps up front because there could be cases of circular dependencies,
219
- * and figuring them out during the main iteration would be too late.
220
- */
221
- const projectToAdditionalDependencyBumps = new Map();
222
- for (const releaseGroup of releaseGroups) {
223
- if (releaseGroup.projectsRelationship !== 'independent') {
224
- continue;
225
179
  }
226
- for (const project of releaseGroup.projects) {
227
- // If the project does not have any changes, do not process its dependents
228
- if (!projectsVersionData[project] ||
229
- projectsVersionData[project].newVersion === null) {
230
- continue;
180
+ else {
181
+ let workspaceChangelogFromRef = args.from ||
182
+ (await (0, git_1.getLatestGitTagForPattern)(nxReleaseConfig.releaseTagPattern))
183
+ ?.tag;
184
+ if (!workspaceChangelogFromRef) {
185
+ if (useAutomaticFromRef) {
186
+ workspaceChangelogFromRef = await (0, git_1.getFirstGitCommit)();
187
+ if (args.verbose) {
188
+ console.log(`Determined workspace --from ref from the first commit in the workspace: ${workspaceChangelogFromRef}`);
189
+ }
190
+ }
191
+ else {
192
+ throw new Error(`Unable to determine the previous git tag. If this is the first release of your workspace, use the --first-release option or set the "release.changelog.automaticFromRef" config property in nx.json to generate a changelog from the first commit. Otherwise, be sure to configure the "release.releaseTagPattern" property in nx.json to match the structure of your repository's git tags.`);
193
+ }
231
194
  }
232
- const dependentProjects = (projectsVersionData[project].dependentProjects || [])
233
- .map((dep) => {
195
+ // Make sure that the fromRef is actually resolvable
196
+ const workspaceChangelogFromSHA = await (0, git_1.getCommitHash)(workspaceChangelogFromRef);
197
+ workspaceChangelogCommits = await getCommits(workspaceChangelogFromSHA, toSHA);
198
+ workspaceChangelogChanges = filterHiddenChanges(workspaceChangelogCommits.map((c) => {
234
199
  return {
235
- dependencyName: dep.source,
236
- newVersion: projectsVersionData[dep.source].newVersion,
200
+ type: c.type,
201
+ scope: c.scope,
202
+ description: c.description,
203
+ body: c.body,
204
+ isBreaking: c.isBreaking,
205
+ githubReferences: c.references,
206
+ author: c.author,
207
+ shortHash: c.shortHash,
208
+ revertedHashes: c.revertedHashes,
209
+ affectedProjects: '*',
237
210
  };
238
- })
239
- .filter((b) => b.newVersion !== null);
240
- for (const dependent of dependentProjects) {
241
- const additionalDependencyBumpsForProject = projectToAdditionalDependencyBumps.has(dependent.dependencyName)
242
- ? projectToAdditionalDependencyBumps.get(dependent.dependencyName)
243
- : [];
244
- additionalDependencyBumpsForProject.push({
245
- dependencyName: project,
246
- newVersion: projectsVersionData[project].newVersion,
247
- });
248
- projectToAdditionalDependencyBumps.set(dependent.dependencyName, additionalDependencyBumpsForProject);
249
- }
211
+ }), nxReleaseConfig.conventionalCommits);
250
212
  }
251
- }
252
- const allProjectChangelogs = {};
253
- for (const releaseGroup of releaseGroups) {
254
- const config = releaseGroup.changelog;
255
- // The entire feature is disabled at the release group level, exit early
256
- if (config === false) {
257
- continue;
213
+ const workspaceChangelog = await generateChangelogForWorkspace({
214
+ tree,
215
+ args,
216
+ projectGraph,
217
+ nxReleaseConfig,
218
+ workspaceChangelogVersion,
219
+ changes: workspaceChangelogChanges,
220
+ // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
221
+ commits: filterHiddenCommits(workspaceChangelogCommits, nxReleaseConfig.conventionalCommits),
222
+ });
223
+ if (workspaceChangelog &&
224
+ shouldCreateGitHubRelease(nxReleaseConfig.changelog.workspaceChangelog, args.createRelease)) {
225
+ let hasPushed = false;
226
+ postGitTasks.push(async (latestCommit) => {
227
+ if (!hasPushed) {
228
+ output_1.output.logSingleLine(`Pushing to git remote`);
229
+ // Before we can create/update the release we need to ensure the commit exists on the remote
230
+ await (0, git_1.gitPush)({
231
+ gitRemote: args.gitRemote,
232
+ dryRun: args.dryRun,
233
+ verbose: args.verbose,
234
+ });
235
+ hasPushed = true;
236
+ }
237
+ output_1.output.logSingleLine(`Creating GitHub Release`);
238
+ await (0, github_1.createOrUpdateGithubRelease)(workspaceChangelog.releaseVersion, workspaceChangelog.contents, latestCommit, { dryRun: args.dryRun });
239
+ });
258
240
  }
259
- const projects = args.projects?.length
260
- ? // If the user has passed a list of projects, we need to use the filtered list of projects within the release group, plus any dependents
261
- Array.from(releaseGroupToFilteredProjects.get(releaseGroup)).flatMap((project) => {
262
- return [
263
- project,
264
- ...(projectsVersionData[project]?.dependentProjects.map((dep) => dep.source) || []),
265
- ];
241
+ /**
242
+ * Compute any additional dependency bumps up front because there could be cases of circular dependencies,
243
+ * and figuring them out during the main iteration would be too late.
244
+ */
245
+ const projectToAdditionalDependencyBumps = new Map();
246
+ for (const releaseGroup of releaseGroups) {
247
+ if (releaseGroup.projectsRelationship !== 'independent') {
248
+ continue;
249
+ }
250
+ for (const project of releaseGroup.projects) {
251
+ // If the project does not have any changes, do not process its dependents
252
+ if (!projectsVersionData[project] ||
253
+ projectsVersionData[project].newVersion === null) {
254
+ continue;
255
+ }
256
+ const dependentProjects = (projectsVersionData[project].dependentProjects || [])
257
+ .map((dep) => {
258
+ return {
259
+ dependencyName: dep.source,
260
+ newVersion: projectsVersionData[dep.source].newVersion,
261
+ };
266
262
  })
267
- : // Otherwise, we use the full list of projects within the release group
268
- releaseGroup.projects;
269
- const projectNodes = projects.map((name) => projectGraph.nodes[name]);
270
- if (releaseGroup.projectsRelationship === 'independent') {
271
- for (const project of projectNodes) {
272
- let changes = null;
263
+ .filter((b) => b.newVersion !== null);
264
+ for (const dependent of dependentProjects) {
265
+ const additionalDependencyBumpsForProject = projectToAdditionalDependencyBumps.has(dependent.dependencyName)
266
+ ? projectToAdditionalDependencyBumps.get(dependent.dependencyName)
267
+ : [];
268
+ additionalDependencyBumpsForProject.push({
269
+ dependencyName: project,
270
+ newVersion: projectsVersionData[project].newVersion,
271
+ });
272
+ projectToAdditionalDependencyBumps.set(dependent.dependencyName, additionalDependencyBumpsForProject);
273
+ }
274
+ }
275
+ }
276
+ const allProjectChangelogs = {};
277
+ for (const releaseGroup of releaseGroups) {
278
+ const config = releaseGroup.changelog;
279
+ // The entire feature is disabled at the release group level, exit early
280
+ if (config === false) {
281
+ continue;
282
+ }
283
+ const projects = args.projects?.length
284
+ ? // If the user has passed a list of projects, we need to use the filtered list of projects within the release group, plus any dependents
285
+ Array.from(releaseGroupToFilteredProjects.get(releaseGroup)).flatMap((project) => {
286
+ return [
287
+ project,
288
+ ...(projectsVersionData[project]?.dependentProjects.map((dep) => dep.source) || []),
289
+ ];
290
+ })
291
+ : // Otherwise, we use the full list of projects within the release group
292
+ releaseGroup.projects;
293
+ const projectNodes = projects.map((name) => projectGraph.nodes[name]);
294
+ if (releaseGroup.projectsRelationship === 'independent') {
295
+ for (const project of projectNodes) {
296
+ let changes = null;
297
+ // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
298
+ let commits;
299
+ if (releaseGroup.versionPlans) {
300
+ changes = releaseGroup.versionPlans
301
+ .map((vp) => {
302
+ const bumpForProject = vp.projectVersionBumps[project.name];
303
+ if (!bumpForProject) {
304
+ return null;
305
+ }
306
+ const releaseType = versionPlanSemverReleaseTypeToChangelogType(bumpForProject);
307
+ return {
308
+ type: releaseType.type,
309
+ scope: project.name,
310
+ description: vp.message,
311
+ body: '',
312
+ isBreaking: releaseType.isBreaking,
313
+ affectedProjects: Object.keys(vp.projectVersionBumps),
314
+ // TODO: can we include github references when using version plans?
315
+ githubReferences: [],
316
+ };
317
+ })
318
+ .filter(Boolean);
319
+ }
320
+ else {
321
+ let fromRef = args.from ||
322
+ (await (0, git_1.getLatestGitTagForPattern)(releaseGroup.releaseTagPattern, {
323
+ projectName: project.name,
324
+ releaseGroupName: releaseGroup.name,
325
+ }))?.tag;
326
+ if (!fromRef && useAutomaticFromRef) {
327
+ const firstCommit = await (0, git_1.getFirstGitCommit)();
328
+ const allCommits = await getCommits(firstCommit, toSHA);
329
+ const commitsForProject = allCommits.filter((c) => c.affectedFiles.find((f) => f.startsWith(project.data.root)));
330
+ fromRef = commitsForProject[0]?.shortHash;
331
+ if (args.verbose) {
332
+ console.log(`Determined --from ref for ${project.name} from the first commit in which it exists: ${fromRef}`);
333
+ }
334
+ commits = commitsForProject;
335
+ }
336
+ if (!fromRef && !commits) {
337
+ throw new Error(`Unable to determine the previous git tag. If this is the first release of your workspace, use the --first-release option or set the "release.changelog.automaticFromRef" config property in nx.json to generate a changelog from the first commit. Otherwise, be sure to configure the "release.releaseTagPattern" property in nx.json to match the structure of your repository's git tags.`);
338
+ }
339
+ if (!commits) {
340
+ commits = await getCommits(fromRef, toSHA);
341
+ }
342
+ const { fileMap } = await (0, file_map_utils_1.createFileMapUsingProjectGraph)(projectGraph);
343
+ const fileToProjectMap = createFileToProjectMap(fileMap.projectFileMap);
344
+ changes = filterHiddenChanges(commits.map((c) => ({
345
+ type: c.type,
346
+ scope: c.scope,
347
+ description: c.description,
348
+ body: c.body,
349
+ isBreaking: c.isBreaking,
350
+ githubReferences: c.references,
351
+ author: c.author,
352
+ shortHash: c.shortHash,
353
+ revertedHashes: c.revertedHashes,
354
+ affectedProjects: commitChangesNonProjectFiles(c, fileMap.nonProjectFiles)
355
+ ? '*'
356
+ : getProjectsAffectedByCommit(c, fileToProjectMap),
357
+ })), nxReleaseConfig.conventionalCommits);
358
+ }
359
+ const projectChangelogs = await generateChangelogForProjects({
360
+ tree,
361
+ args,
362
+ projectGraph,
363
+ changes,
364
+ projectsVersionData,
365
+ releaseGroup,
366
+ projects: [project],
367
+ nxReleaseConfig,
368
+ projectToAdditionalDependencyBumps,
369
+ // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
370
+ commits: filterHiddenCommits(commits, nxReleaseConfig.conventionalCommits),
371
+ });
372
+ let hasPushed = false;
373
+ for (const [projectName, projectChangelog] of Object.entries(projectChangelogs)) {
374
+ if (projectChangelogs &&
375
+ shouldCreateGitHubRelease(releaseGroup.changelog, args.createRelease)) {
376
+ postGitTasks.push(async (latestCommit) => {
377
+ if (!hasPushed) {
378
+ output_1.output.logSingleLine(`Pushing to git remote`);
379
+ // Before we can create/update the release we need to ensure the commit exists on the remote
380
+ await (0, git_1.gitPush)({
381
+ gitRemote: args.gitRemote,
382
+ dryRun: args.dryRun,
383
+ verbose: args.verbose,
384
+ });
385
+ hasPushed = true;
386
+ }
387
+ output_1.output.logSingleLine(`Creating GitHub Release`);
388
+ await (0, github_1.createOrUpdateGithubRelease)(projectChangelog.releaseVersion, projectChangelog.contents, latestCommit, { dryRun: args.dryRun });
389
+ });
390
+ }
391
+ allProjectChangelogs[projectName] = projectChangelog;
392
+ }
393
+ }
394
+ }
395
+ else {
396
+ let changes = [];
273
397
  // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
274
- let commits;
398
+ let commits = [];
275
399
  if (releaseGroup.versionPlans) {
276
- changes = filterHiddenChanges(releaseGroup.versionPlans
277
- .map((vp) => {
278
- const parsedMessage = (0, git_1.parseConventionalCommitsMessage)(vp.message);
279
- // only properly formatted conventional commits messages will be included in the changelog
280
- if (!parsedMessage) {
281
- return null;
282
- }
283
- return {
284
- type: parsedMessage.type,
285
- scope: parsedMessage.scope,
286
- description: parsedMessage.description,
287
- body: '',
288
- isBreaking: parsedMessage.breaking,
289
- affectedProjects: Object.keys(vp.projectVersionBumps),
290
- githubReferences: [],
291
- };
400
+ changes = releaseGroup.versionPlans
401
+ .flatMap((vp) => {
402
+ const releaseType = versionPlanSemverReleaseTypeToChangelogType(vp.groupVersionBump);
403
+ const changes = !vp.triggeredByProjects
404
+ ? {
405
+ type: releaseType.type,
406
+ scope: '',
407
+ description: vp.message,
408
+ body: '',
409
+ isBreaking: releaseType.isBreaking,
410
+ githubReferences: [],
411
+ affectedProjects: '*',
412
+ }
413
+ : vp.triggeredByProjects.map((project) => {
414
+ return {
415
+ type: releaseType.type,
416
+ scope: project,
417
+ description: vp.message,
418
+ body: '',
419
+ // TODO: what about github references?
420
+ isBreaking: releaseType.isBreaking,
421
+ githubReferences: [],
422
+ affectedProjects: [project],
423
+ };
424
+ });
425
+ return changes;
292
426
  })
293
- .filter(Boolean), nxReleaseConfig.conventionalCommits);
427
+ .filter(Boolean);
294
428
  }
295
429
  else {
296
430
  let fromRef = args.from ||
297
- (await (0, git_1.getLatestGitTagForPattern)(releaseGroup.releaseTagPattern, {
298
- projectName: project.name,
299
- releaseGroupName: releaseGroup.name,
300
- }))?.tag;
301
- if (!fromRef && useAutomaticFromRef) {
302
- const firstCommit = await (0, git_1.getFirstGitCommit)();
303
- const allCommits = await getCommits(firstCommit, toSHA);
304
- const commitsForProject = allCommits.filter((c) => c.affectedFiles.find((f) => f.startsWith(project.data.root)));
305
- fromRef = commitsForProject[0]?.shortHash;
306
- if (args.verbose) {
307
- console.log(`Determined --from ref for ${project.name} from the first commit in which it exists: ${fromRef}`);
431
+ (await (0, git_1.getLatestGitTagForPattern)(releaseGroup.releaseTagPattern))
432
+ ?.tag;
433
+ if (!fromRef) {
434
+ if (useAutomaticFromRef) {
435
+ fromRef = await (0, git_1.getFirstGitCommit)();
436
+ if (args.verbose) {
437
+ console.log(`Determined release group --from ref from the first commit in the workspace: ${fromRef}`);
438
+ }
439
+ }
440
+ else {
441
+ throw new Error(`Unable to determine the previous git tag. If this is the first release of your release group, use the --first-release option or set the "release.changelog.automaticFromRef" config property in nx.json to generate a changelog from the first commit. Otherwise, be sure to configure the "release.releaseTagPattern" property in nx.json to match the structure of your repository's git tags.`);
308
442
  }
309
- commits = commitsForProject;
310
- }
311
- if (!fromRef && !commits) {
312
- throw new Error(`Unable to determine the previous git tag. If this is the first release of your workspace, use the --first-release option or set the "release.changelog.automaticFromRef" config property in nx.json to generate a changelog from the first commit. Otherwise, be sure to configure the "release.releaseTagPattern" property in nx.json to match the structure of your repository's git tags.`);
313
- }
314
- if (!commits) {
315
- commits = await getCommits(fromRef, toSHA);
316
443
  }
444
+ // Make sure that the fromRef is actually resolvable
445
+ const fromSHA = await (0, git_1.getCommitHash)(fromRef);
317
446
  const { fileMap } = await (0, file_map_utils_1.createFileMapUsingProjectGraph)(projectGraph);
318
447
  const fileToProjectMap = createFileToProjectMap(fileMap.projectFileMap);
448
+ commits = await getCommits(fromSHA, toSHA);
319
449
  changes = filterHiddenChanges(commits.map((c) => ({
320
450
  type: c.type,
321
451
  scope: c.scope,
@@ -338,7 +468,7 @@ async function releaseChangelog(args) {
338
468
  changes,
339
469
  projectsVersionData,
340
470
  releaseGroup,
341
- projects: [project],
471
+ projects: projectNodes,
342
472
  nxReleaseConfig,
343
473
  projectToAdditionalDependencyBumps,
344
474
  // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
@@ -367,105 +497,11 @@ async function releaseChangelog(args) {
367
497
  }
368
498
  }
369
499
  }
370
- else {
371
- let changes = [];
372
- // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
373
- let commits = [];
374
- if (releaseGroup.versionPlans) {
375
- changes = filterHiddenChanges(releaseGroup.versionPlans
376
- .map((vp) => {
377
- const parsedMessage = (0, git_1.parseConventionalCommitsMessage)(vp.message);
378
- // only properly formatted conventional commits messages will be included in the changelog
379
- if (!parsedMessage) {
380
- return null;
381
- }
382
- return {
383
- type: parsedMessage.type,
384
- scope: parsedMessage.scope,
385
- description: parsedMessage.description,
386
- body: '',
387
- isBreaking: parsedMessage.breaking,
388
- githubReferences: [],
389
- affectedProjects: '*',
390
- };
391
- })
392
- .filter(Boolean), nxReleaseConfig.conventionalCommits);
393
- }
394
- else {
395
- let fromRef = args.from ||
396
- (await (0, git_1.getLatestGitTagForPattern)(releaseGroup.releaseTagPattern))
397
- ?.tag;
398
- if (!fromRef) {
399
- if (useAutomaticFromRef) {
400
- fromRef = await (0, git_1.getFirstGitCommit)();
401
- if (args.verbose) {
402
- console.log(`Determined release group --from ref from the first commit in the workspace: ${fromRef}`);
403
- }
404
- }
405
- else {
406
- throw new Error(`Unable to determine the previous git tag. If this is the first release of your release group, use the --first-release option or set the "release.changelog.automaticFromRef" config property in nx.json to generate a changelog from the first commit. Otherwise, be sure to configure the "release.releaseTagPattern" property in nx.json to match the structure of your repository's git tags.`);
407
- }
408
- }
409
- // Make sure that the fromRef is actually resolvable
410
- const fromSHA = await (0, git_1.getCommitHash)(fromRef);
411
- const { fileMap } = await (0, file_map_utils_1.createFileMapUsingProjectGraph)(projectGraph);
412
- const fileToProjectMap = createFileToProjectMap(fileMap.projectFileMap);
413
- commits = await getCommits(fromSHA, toSHA);
414
- changes = filterHiddenChanges(commits.map((c) => ({
415
- type: c.type,
416
- scope: c.scope,
417
- description: c.description,
418
- body: c.body,
419
- isBreaking: c.isBreaking,
420
- githubReferences: c.references,
421
- author: c.author,
422
- shortHash: c.shortHash,
423
- revertedHashes: c.revertedHashes,
424
- affectedProjects: commitChangesNonProjectFiles(c, fileMap.nonProjectFiles)
425
- ? '*'
426
- : getProjectsAffectedByCommit(c, fileToProjectMap),
427
- })), nxReleaseConfig.conventionalCommits);
428
- }
429
- const projectChangelogs = await generateChangelogForProjects({
430
- tree,
431
- args,
432
- projectGraph,
433
- changes,
434
- projectsVersionData,
435
- releaseGroup,
436
- projects: projectNodes,
437
- nxReleaseConfig,
438
- projectToAdditionalDependencyBumps,
439
- // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
440
- commits: filterHiddenCommits(commits, nxReleaseConfig.conventionalCommits),
441
- });
442
- let hasPushed = false;
443
- for (const [projectName, projectChangelog] of Object.entries(projectChangelogs)) {
444
- if (projectChangelogs &&
445
- shouldCreateGitHubRelease(releaseGroup.changelog, args.createRelease)) {
446
- postGitTasks.push(async (latestCommit) => {
447
- if (!hasPushed) {
448
- output_1.output.logSingleLine(`Pushing to git remote`);
449
- // Before we can create/update the release we need to ensure the commit exists on the remote
450
- await (0, git_1.gitPush)({
451
- gitRemote: args.gitRemote,
452
- dryRun: args.dryRun,
453
- verbose: args.verbose,
454
- });
455
- hasPushed = true;
456
- }
457
- output_1.output.logSingleLine(`Creating GitHub Release`);
458
- await (0, github_1.createOrUpdateGithubRelease)(projectChangelog.releaseVersion, projectChangelog.contents, latestCommit, { dryRun: args.dryRun });
459
- });
460
- }
461
- allProjectChangelogs[projectName] = projectChangelog;
462
- }
463
- }
464
- }
465
- await applyChangesAndExit(args, nxReleaseConfig, tree, toSHA, postGitTasks, commitMessageValues, gitTagValues, releaseGroups);
466
- return {
467
- workspaceChangelog,
468
- projectChangelogs: allProjectChangelogs,
500
+ await applyChangesAndExit(args, nxReleaseConfig, tree, toSHA, postGitTasks, commitMessageValues, gitTagValues, releaseGroups);
501
+ return {
502
+ workspaceChangelog,
503
+ projectChangelogs: allProjectChangelogs,
504
+ };
469
505
  };
470
506
  }
471
507
  function resolveChangelogVersions(args, releaseGroups, releaseGroupToFilteredProjects) {
@@ -900,3 +936,19 @@ function createFileToProjectMap(projectFileMap) {
900
936
  }
901
937
  return fileToProjectMap;
902
938
  }
939
+ function versionPlanSemverReleaseTypeToChangelogType(bump) {
940
+ switch (bump) {
941
+ case 'premajor':
942
+ case 'major':
943
+ return { type: 'feat', isBreaking: true };
944
+ case 'preminor':
945
+ case 'minor':
946
+ return { type: 'feat', isBreaking: false };
947
+ case 'prerelease':
948
+ case 'prepatch':
949
+ case 'patch':
950
+ return { type: 'fix', isBreaking: false };
951
+ default:
952
+ throw new Error(`Invalid semver bump type: ${bump}`);
953
+ }
954
+ }