nx 19.5.6 → 19.6.0-beta.0

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 (52) 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.js +7 -3
  9. package/src/command-line/release/changelog.d.ts +2 -7
  10. package/src/command-line/release/changelog.js +361 -347
  11. package/src/command-line/release/command-object.d.ts +1 -0
  12. package/src/command-line/release/command-object.js +14 -0
  13. package/src/command-line/release/config/deep-merge-json.d.ts +1 -0
  14. package/src/command-line/release/config/deep-merge-json.js +28 -0
  15. package/src/command-line/release/index.d.ts +16 -4
  16. package/src/command-line/release/index.js +23 -9
  17. package/src/command-line/release/plan.d.ts +2 -1
  18. package/src/command-line/release/plan.js +90 -77
  19. package/src/command-line/release/publish.d.ts +2 -6
  20. package/src/command-line/release/publish.js +67 -52
  21. package/src/command-line/release/release.d.ts +2 -1
  22. package/src/command-line/release/release.js +181 -165
  23. package/src/command-line/release/utils/print-config.d.ts +7 -0
  24. package/src/command-line/release/utils/print-config.js +36 -0
  25. package/src/command-line/release/version.d.ts +2 -6
  26. package/src/command-line/release/version.js +179 -165
  27. package/src/command-line/run/run-one.js +1 -1
  28. package/src/command-line/run-many/run-many.js +1 -1
  29. package/src/command-line/yargs-utils/shared-options.d.ts +1 -0
  30. package/src/command-line/yargs-utils/shared-options.js +5 -0
  31. package/src/commands-runner/create-command-graph.js +4 -2
  32. package/src/config/nx-json.d.ts +10 -1
  33. package/src/devkit-internals.d.ts +1 -1
  34. package/src/devkit-internals.js +2 -1
  35. package/src/generators/utils/project-configuration.js +41 -11
  36. package/src/native/nx.wasm32-wasi.wasm +0 -0
  37. package/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.d.ts +4 -2
  38. package/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.js +61 -20
  39. package/src/nx-cloud/nx-cloud-tasks-runner-shell.d.ts +1 -0
  40. package/src/nx-cloud/utilities/axios.js +9 -2
  41. package/src/nx-cloud/utilities/get-cloud-options.d.ts +1 -1
  42. package/src/nx-cloud/utilities/get-cloud-options.js +3 -2
  43. package/src/plugins/package-json/create-nodes.js +9 -1
  44. package/src/project-graph/plugins/isolation/plugin-pool.js +32 -10
  45. package/src/project-graph/utils/project-configuration-utils.d.ts +1 -0
  46. package/src/project-graph/utils/project-configuration-utils.js +1 -0
  47. package/src/tasks-runner/cache.js +1 -1
  48. package/src/tasks-runner/run-command.js +6 -1
  49. package/src/tasks-runner/utils.js +14 -10
  50. package/src/utils/command-line-utils.d.ts +1 -0
  51. package/src/utils/nx-cloud-utils.js +3 -1
  52. 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) {