nx 19.5.5 → 19.6.0-beta.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. package/package.json +12 -12
  2. package/release/index.d.ts +1 -1
  3. package/release/index.js +2 -1
  4. package/schemas/nx-schema.json +3 -0
  5. package/src/adapter/compat.d.ts +1 -1
  6. package/src/adapter/compat.js +2 -0
  7. package/src/command-line/affected/affected.js +1 -1
  8. package/src/command-line/connect/connect-to-nx-cloud.d.ts +1 -1
  9. package/src/command-line/connect/connect-to-nx-cloud.js +10 -6
  10. package/src/command-line/release/changelog.d.ts +2 -7
  11. package/src/command-line/release/changelog.js +361 -347
  12. package/src/command-line/release/command-object.d.ts +1 -0
  13. package/src/command-line/release/command-object.js +14 -0
  14. package/src/command-line/release/config/deep-merge-json.d.ts +1 -0
  15. package/src/command-line/release/config/deep-merge-json.js +28 -0
  16. package/src/command-line/release/index.d.ts +16 -4
  17. package/src/command-line/release/index.js +23 -9
  18. package/src/command-line/release/plan.d.ts +2 -1
  19. package/src/command-line/release/plan.js +90 -77
  20. package/src/command-line/release/publish.d.ts +2 -6
  21. package/src/command-line/release/publish.js +67 -52
  22. package/src/command-line/release/release.d.ts +2 -1
  23. package/src/command-line/release/release.js +181 -165
  24. package/src/command-line/release/utils/print-config.d.ts +7 -0
  25. package/src/command-line/release/utils/print-config.js +36 -0
  26. package/src/command-line/release/version.d.ts +2 -6
  27. package/src/command-line/release/version.js +179 -165
  28. package/src/command-line/run/run-one.js +1 -1
  29. package/src/command-line/run-many/run-many.js +1 -1
  30. package/src/command-line/yargs-utils/shared-options.d.ts +1 -0
  31. package/src/command-line/yargs-utils/shared-options.js +5 -0
  32. package/src/commands-runner/create-command-graph.js +4 -2
  33. package/src/config/nx-json.d.ts +10 -1
  34. package/src/devkit-internals.d.ts +1 -1
  35. package/src/devkit-internals.js +2 -1
  36. package/src/generators/utils/project-configuration.js +41 -11
  37. package/src/native/nx.wasm32-wasi.wasm +0 -0
  38. package/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.d.ts +4 -2
  39. package/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.js +61 -20
  40. package/src/nx-cloud/nx-cloud-tasks-runner-shell.d.ts +1 -0
  41. package/src/nx-cloud/utilities/axios.js +9 -2
  42. package/src/nx-cloud/utilities/get-cloud-options.d.ts +1 -1
  43. package/src/nx-cloud/utilities/get-cloud-options.js +3 -2
  44. package/src/plugins/package-json/create-nodes.js +9 -1
  45. package/src/project-graph/plugins/isolation/plugin-pool.js +32 -10
  46. package/src/project-graph/utils/project-configuration-utils.d.ts +1 -0
  47. package/src/project-graph/utils/project-configuration-utils.js +1 -0
  48. package/src/tasks-runner/cache.js +1 -1
  49. package/src/tasks-runner/run-command.js +6 -1
  50. package/src/tasks-runner/utils.js +14 -10
  51. package/src/utils/command-line-utils.d.ts +1 -0
  52. package/src/utils/nx-cloud-utils.js +3 -1
  53. package/src/utils/package-json.d.ts +2 -9
@@ -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,251 +28,362 @@ 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;
93
- /**
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
106
- */
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');
37
+ function createAPI(overrideReleaseConfig) {
111
38
  /**
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.
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.
114
42
  */
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 = filterHiddenChanges(versionPlans
150
+ .map((vp) => {
151
+ const parsedMessage = (0, git_1.parseConventionalCommitsMessage)(vp.message);
152
+ // only properly formatted conventional commits messages will be included in the changelog
153
+ if (!parsedMessage) {
154
+ return null;
155
+ }
156
+ return {
157
+ type: parsedMessage.type,
158
+ scope: parsedMessage.scope,
159
+ description: parsedMessage.description,
160
+ body: '',
161
+ isBreaking: parsedMessage.breaking,
162
+ githubReferences: [],
163
+ };
164
+ })
165
+ .filter(Boolean), nxReleaseConfig.conventionalCommits);
166
+ }
212
167
  }
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
168
  }
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;
169
+ else {
170
+ let workspaceChangelogFromRef = args.from ||
171
+ (await (0, git_1.getLatestGitTagForPattern)(nxReleaseConfig.releaseTagPattern))
172
+ ?.tag;
173
+ if (!workspaceChangelogFromRef) {
174
+ if (useAutomaticFromRef) {
175
+ workspaceChangelogFromRef = await (0, git_1.getFirstGitCommit)();
176
+ if (args.verbose) {
177
+ console.log(`Determined workspace --from ref from the first commit in the workspace: ${workspaceChangelogFromRef}`);
178
+ }
179
+ }
180
+ else {
181
+ 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.`);
182
+ }
231
183
  }
232
- const dependentProjects = (projectsVersionData[project].dependentProjects || [])
233
- .map((dep) => {
184
+ // Make sure that the fromRef is actually resolvable
185
+ const workspaceChangelogFromSHA = await (0, git_1.getCommitHash)(workspaceChangelogFromRef);
186
+ workspaceChangelogCommits = await getCommits(workspaceChangelogFromSHA, toSHA);
187
+ workspaceChangelogChanges = filterHiddenChanges(workspaceChangelogCommits.map((c) => {
234
188
  return {
235
- dependencyName: dep.source,
236
- newVersion: projectsVersionData[dep.source].newVersion,
189
+ type: c.type,
190
+ scope: c.scope,
191
+ description: c.description,
192
+ body: c.body,
193
+ isBreaking: c.isBreaking,
194
+ githubReferences: c.references,
195
+ author: c.author,
196
+ shortHash: c.shortHash,
197
+ revertedHashes: c.revertedHashes,
198
+ affectedProjects: '*',
237
199
  };
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
- }
200
+ }), nxReleaseConfig.conventionalCommits);
250
201
  }
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;
202
+ const workspaceChangelog = await generateChangelogForWorkspace({
203
+ tree,
204
+ args,
205
+ projectGraph,
206
+ nxReleaseConfig,
207
+ workspaceChangelogVersion,
208
+ changes: workspaceChangelogChanges,
209
+ // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
210
+ commits: filterHiddenCommits(workspaceChangelogCommits, nxReleaseConfig.conventionalCommits),
211
+ });
212
+ if (workspaceChangelog &&
213
+ shouldCreateGitHubRelease(nxReleaseConfig.changelog.workspaceChangelog, args.createRelease)) {
214
+ let hasPushed = false;
215
+ postGitTasks.push(async (latestCommit) => {
216
+ if (!hasPushed) {
217
+ output_1.output.logSingleLine(`Pushing to git remote`);
218
+ // Before we can create/update the release we need to ensure the commit exists on the remote
219
+ await (0, git_1.gitPush)({
220
+ gitRemote: args.gitRemote,
221
+ dryRun: args.dryRun,
222
+ verbose: args.verbose,
223
+ });
224
+ hasPushed = true;
225
+ }
226
+ output_1.output.logSingleLine(`Creating GitHub Release`);
227
+ await (0, github_1.createOrUpdateGithubRelease)(workspaceChangelog.releaseVersion, workspaceChangelog.contents, latestCommit, { dryRun: args.dryRun });
228
+ });
258
229
  }
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
- ];
230
+ /**
231
+ * Compute any additional dependency bumps up front because there could be cases of circular dependencies,
232
+ * and figuring them out during the main iteration would be too late.
233
+ */
234
+ const projectToAdditionalDependencyBumps = new Map();
235
+ for (const releaseGroup of releaseGroups) {
236
+ if (releaseGroup.projectsRelationship !== 'independent') {
237
+ continue;
238
+ }
239
+ for (const project of releaseGroup.projects) {
240
+ // If the project does not have any changes, do not process its dependents
241
+ if (!projectsVersionData[project] ||
242
+ projectsVersionData[project].newVersion === null) {
243
+ continue;
244
+ }
245
+ const dependentProjects = (projectsVersionData[project].dependentProjects || [])
246
+ .map((dep) => {
247
+ return {
248
+ dependencyName: dep.source,
249
+ newVersion: projectsVersionData[dep.source].newVersion,
250
+ };
266
251
  })
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;
252
+ .filter((b) => b.newVersion !== null);
253
+ for (const dependent of dependentProjects) {
254
+ const additionalDependencyBumpsForProject = projectToAdditionalDependencyBumps.has(dependent.dependencyName)
255
+ ? projectToAdditionalDependencyBumps.get(dependent.dependencyName)
256
+ : [];
257
+ additionalDependencyBumpsForProject.push({
258
+ dependencyName: project,
259
+ newVersion: projectsVersionData[project].newVersion,
260
+ });
261
+ projectToAdditionalDependencyBumps.set(dependent.dependencyName, additionalDependencyBumpsForProject);
262
+ }
263
+ }
264
+ }
265
+ const allProjectChangelogs = {};
266
+ for (const releaseGroup of releaseGroups) {
267
+ const config = releaseGroup.changelog;
268
+ // The entire feature is disabled at the release group level, exit early
269
+ if (config === false) {
270
+ continue;
271
+ }
272
+ const projects = args.projects?.length
273
+ ? // 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
274
+ Array.from(releaseGroupToFilteredProjects.get(releaseGroup)).flatMap((project) => {
275
+ return [
276
+ project,
277
+ ...(projectsVersionData[project]?.dependentProjects.map((dep) => dep.source) || []),
278
+ ];
279
+ })
280
+ : // Otherwise, we use the full list of projects within the release group
281
+ releaseGroup.projects;
282
+ const projectNodes = projects.map((name) => projectGraph.nodes[name]);
283
+ if (releaseGroup.projectsRelationship === 'independent') {
284
+ for (const project of projectNodes) {
285
+ let changes = null;
286
+ // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
287
+ let commits;
288
+ if (releaseGroup.versionPlans) {
289
+ changes = filterHiddenChanges(releaseGroup.versionPlans
290
+ .map((vp) => {
291
+ const parsedMessage = (0, git_1.parseConventionalCommitsMessage)(vp.message);
292
+ // only properly formatted conventional commits messages will be included in the changelog
293
+ if (!parsedMessage) {
294
+ return null;
295
+ }
296
+ return {
297
+ type: parsedMessage.type,
298
+ scope: parsedMessage.scope,
299
+ description: parsedMessage.description,
300
+ body: '',
301
+ isBreaking: parsedMessage.breaking,
302
+ affectedProjects: Object.keys(vp.projectVersionBumps),
303
+ githubReferences: [],
304
+ };
305
+ })
306
+ .filter(Boolean), nxReleaseConfig.conventionalCommits);
307
+ }
308
+ else {
309
+ let fromRef = args.from ||
310
+ (await (0, git_1.getLatestGitTagForPattern)(releaseGroup.releaseTagPattern, {
311
+ projectName: project.name,
312
+ releaseGroupName: releaseGroup.name,
313
+ }))?.tag;
314
+ if (!fromRef && useAutomaticFromRef) {
315
+ const firstCommit = await (0, git_1.getFirstGitCommit)();
316
+ const allCommits = await getCommits(firstCommit, toSHA);
317
+ const commitsForProject = allCommits.filter((c) => c.affectedFiles.find((f) => f.startsWith(project.data.root)));
318
+ fromRef = commitsForProject[0]?.shortHash;
319
+ if (args.verbose) {
320
+ console.log(`Determined --from ref for ${project.name} from the first commit in which it exists: ${fromRef}`);
321
+ }
322
+ commits = commitsForProject;
323
+ }
324
+ if (!fromRef && !commits) {
325
+ 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.`);
326
+ }
327
+ if (!commits) {
328
+ commits = await getCommits(fromRef, toSHA);
329
+ }
330
+ const { fileMap } = await (0, file_map_utils_1.createFileMapUsingProjectGraph)(projectGraph);
331
+ const fileToProjectMap = createFileToProjectMap(fileMap.projectFileMap);
332
+ changes = filterHiddenChanges(commits.map((c) => ({
333
+ type: c.type,
334
+ scope: c.scope,
335
+ description: c.description,
336
+ body: c.body,
337
+ isBreaking: c.isBreaking,
338
+ githubReferences: c.references,
339
+ author: c.author,
340
+ shortHash: c.shortHash,
341
+ revertedHashes: c.revertedHashes,
342
+ affectedProjects: commitChangesNonProjectFiles(c, fileMap.nonProjectFiles)
343
+ ? '*'
344
+ : getProjectsAffectedByCommit(c, fileToProjectMap),
345
+ })), nxReleaseConfig.conventionalCommits);
346
+ }
347
+ const projectChangelogs = await generateChangelogForProjects({
348
+ tree,
349
+ args,
350
+ projectGraph,
351
+ changes,
352
+ projectsVersionData,
353
+ releaseGroup,
354
+ projects: [project],
355
+ nxReleaseConfig,
356
+ projectToAdditionalDependencyBumps,
357
+ // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
358
+ commits: filterHiddenCommits(commits, nxReleaseConfig.conventionalCommits),
359
+ });
360
+ let hasPushed = false;
361
+ for (const [projectName, projectChangelog] of Object.entries(projectChangelogs)) {
362
+ if (projectChangelogs &&
363
+ shouldCreateGitHubRelease(releaseGroup.changelog, args.createRelease)) {
364
+ postGitTasks.push(async (latestCommit) => {
365
+ if (!hasPushed) {
366
+ output_1.output.logSingleLine(`Pushing to git remote`);
367
+ // Before we can create/update the release we need to ensure the commit exists on the remote
368
+ await (0, git_1.gitPush)({
369
+ gitRemote: args.gitRemote,
370
+ dryRun: args.dryRun,
371
+ verbose: args.verbose,
372
+ });
373
+ hasPushed = true;
374
+ }
375
+ output_1.output.logSingleLine(`Creating GitHub Release`);
376
+ await (0, github_1.createOrUpdateGithubRelease)(projectChangelog.releaseVersion, projectChangelog.contents, latestCommit, { dryRun: args.dryRun });
377
+ });
378
+ }
379
+ allProjectChangelogs[projectName] = projectChangelog;
380
+ }
381
+ }
382
+ }
383
+ else {
384
+ let changes = [];
273
385
  // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
274
- let commits;
386
+ let commits = [];
275
387
  if (releaseGroup.versionPlans) {
276
388
  changes = filterHiddenChanges(releaseGroup.versionPlans
277
389
  .map((vp) => {
@@ -286,36 +398,32 @@ async function releaseChangelog(args) {
286
398
  description: parsedMessage.description,
287
399
  body: '',
288
400
  isBreaking: parsedMessage.breaking,
289
- affectedProjects: Object.keys(vp.projectVersionBumps),
290
401
  githubReferences: [],
402
+ affectedProjects: '*',
291
403
  };
292
404
  })
293
405
  .filter(Boolean), nxReleaseConfig.conventionalCommits);
294
406
  }
295
407
  else {
296
408
  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}`);
409
+ (await (0, git_1.getLatestGitTagForPattern)(releaseGroup.releaseTagPattern))
410
+ ?.tag;
411
+ if (!fromRef) {
412
+ if (useAutomaticFromRef) {
413
+ fromRef = await (0, git_1.getFirstGitCommit)();
414
+ if (args.verbose) {
415
+ console.log(`Determined release group --from ref from the first commit in the workspace: ${fromRef}`);
416
+ }
417
+ }
418
+ else {
419
+ 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
420
  }
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
421
  }
422
+ // Make sure that the fromRef is actually resolvable
423
+ const fromSHA = await (0, git_1.getCommitHash)(fromRef);
317
424
  const { fileMap } = await (0, file_map_utils_1.createFileMapUsingProjectGraph)(projectGraph);
318
425
  const fileToProjectMap = createFileToProjectMap(fileMap.projectFileMap);
426
+ commits = await getCommits(fromSHA, toSHA);
319
427
  changes = filterHiddenChanges(commits.map((c) => ({
320
428
  type: c.type,
321
429
  scope: c.scope,
@@ -338,7 +446,7 @@ async function releaseChangelog(args) {
338
446
  changes,
339
447
  projectsVersionData,
340
448
  releaseGroup,
341
- projects: [project],
449
+ projects: projectNodes,
342
450
  nxReleaseConfig,
343
451
  projectToAdditionalDependencyBumps,
344
452
  // TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
@@ -367,105 +475,11 @@ async function releaseChangelog(args) {
367
475
  }
368
476
  }
369
477
  }
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,
478
+ await applyChangesAndExit(args, nxReleaseConfig, tree, toSHA, postGitTasks, commitMessageValues, gitTagValues, releaseGroups);
479
+ return {
480
+ workspaceChangelog,
481
+ projectChangelogs: allProjectChangelogs,
482
+ };
469
483
  };
470
484
  }
471
485
  function resolveChangelogVersions(args, releaseGroups, releaseGroupToFilteredProjects) {