nx 21.0.0-beta.11 → 21.0.0-beta.12

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 (45) hide show
  1. package/migrations.json +10 -5
  2. package/package.json +11 -11
  3. package/release/changelog-renderer/index.d.ts +7 -7
  4. package/release/changelog-renderer/index.js +12 -31
  5. package/schemas/nx-schema.json +3 -3
  6. package/src/command-line/migrate/migrate-ui-api.d.ts +2 -1
  7. package/src/command-line/migrate/migrate-ui-api.js +4 -3
  8. package/src/command-line/release/changelog.d.ts +3 -2
  9. package/src/command-line/release/changelog.js +57 -70
  10. package/src/command-line/release/command-object.d.ts +1 -1
  11. package/src/command-line/release/config/config.d.ts +8 -1
  12. package/src/command-line/release/config/config.js +18 -11
  13. package/src/command-line/release/release.js +30 -18
  14. package/src/command-line/release/utils/git.d.ts +1 -0
  15. package/src/command-line/release/utils/git.js +27 -8
  16. package/src/command-line/release/utils/remote-release-clients/github.d.ts +57 -0
  17. package/src/command-line/release/utils/remote-release-clients/github.js +309 -0
  18. package/src/command-line/release/utils/remote-release-clients/gitlab.d.ts +62 -0
  19. package/src/command-line/release/utils/remote-release-clients/gitlab.js +271 -0
  20. package/src/command-line/release/utils/remote-release-clients/remote-release-client.d.ts +111 -0
  21. package/src/command-line/release/utils/remote-release-clients/remote-release-client.js +136 -0
  22. package/src/command-line/yargs-utils/shared-options.d.ts +1 -1
  23. package/src/command-line/yargs-utils/shared-options.js +22 -3
  24. package/src/config/nx-json.d.ts +8 -1
  25. package/src/core/graph/main.js +1 -1
  26. package/src/core/graph/styles.css +1 -1
  27. package/src/migrations/update-21-0-0/release-changelog-config-changes.d.ts +2 -0
  28. package/src/migrations/update-21-0-0/release-changelog-config-changes.js +38 -0
  29. package/src/native/index.d.ts +6 -1
  30. package/src/native/native-bindings.js +1 -0
  31. package/src/native/native-file-cache-location.js +2 -1
  32. package/src/native/nx.wasm32-wasi.wasm +0 -0
  33. package/src/project-graph/plugins/get-plugins.js +19 -14
  34. package/src/tasks-runner/is-tui-enabled.d.ts +16 -1
  35. package/src/tasks-runner/is-tui-enabled.js +40 -28
  36. package/src/tasks-runner/run-command.js +3 -3
  37. package/src/tasks-runner/running-tasks/node-child-process.d.ts +1 -0
  38. package/src/tasks-runner/running-tasks/node-child-process.js +7 -0
  39. package/src/tasks-runner/task-orchestrator.js +6 -3
  40. package/src/utils/is-ci.d.ts +1 -1
  41. package/src/utils/is-ci.js +4 -1
  42. package/src/utils/package-manager.d.ts +1 -0
  43. package/src/utils/package-manager.js +29 -16
  44. package/src/command-line/release/utils/github.d.ts +0 -32
  45. package/src/command-line/release/utils/github.js +0 -326
@@ -2,7 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isCI = isCI;
4
4
  function isCI() {
5
- return ((process.env.CI && process.env.CI !== 'false') ||
5
+ if (process.env.CI === 'false') {
6
+ return false;
7
+ }
8
+ return (process.env.CI ||
6
9
  process.env.TF_BUILD === 'true' ||
7
10
  process.env.GITHUB_ACTIONS === 'true' ||
8
11
  process.env.BUILDKITE === 'true' ||
@@ -44,6 +44,7 @@ export declare function getPackageManagerCommand(packageManager?: PackageManager
44
44
  * but it can also be passed in explicitly.
45
45
  */
46
46
  export declare function getPackageManagerVersion(packageManager?: PackageManager, cwd?: string): string;
47
+ export declare function parseVersionFromPackageManagerField(requestedPackageManager: string, packageManagerFieldValue: string | undefined): null | string;
47
48
  /**
48
49
  * Checks for a project level npmrc file by crawling up the file tree until
49
50
  * hitting a package.json file, as this is how npm finds them as well.
@@ -4,6 +4,7 @@ exports.detectPackageManager = detectPackageManager;
4
4
  exports.isWorkspacesEnabled = isWorkspacesEnabled;
5
5
  exports.getPackageManagerCommand = getPackageManagerCommand;
6
6
  exports.getPackageManagerVersion = getPackageManagerVersion;
7
+ exports.parseVersionFromPackageManagerField = parseVersionFromPackageManagerField;
7
8
  exports.findFileInPackageJsonDirectory = findFileInPackageJsonDirectory;
8
9
  exports.modifyYarnRcYmlToFitNewDirectory = modifyYarnRcYmlToFitNewDirectory;
9
10
  exports.modifyYarnRcToFitNewDirectory = modifyYarnRcToFitNewDirectory;
@@ -175,30 +176,42 @@ function getPackageManagerCommand(packageManager = detectPackageManager(), root
175
176
  */
176
177
  function getPackageManagerVersion(packageManager = detectPackageManager(), cwd = process.cwd()) {
177
178
  let version;
178
- try {
179
- version = (0, child_process_1.execSync)(`${packageManager} --version`, {
180
- cwd,
181
- encoding: 'utf-8',
182
- windowsHide: true,
183
- }).trim();
179
+ if ((0, fs_1.existsSync)((0, path_1.join)(cwd, 'package.json'))) {
180
+ const packageManagerEntry = (0, fileutils_1.readJsonFile)((0, path_1.join)(cwd, 'package.json'))?.packageManager;
181
+ version = parseVersionFromPackageManagerField(packageManager, packageManagerEntry);
184
182
  }
185
- catch {
186
- if ((0, fs_1.existsSync)((0, path_1.join)(cwd, 'package.json'))) {
187
- const packageVersion = (0, fileutils_1.readJsonFile)((0, path_1.join)(cwd, 'package.json'))?.packageManager;
188
- if (packageVersion) {
189
- const [packageManagerFromPackageJson, versionFromPackageJson] = packageVersion.split('@');
190
- if (packageManagerFromPackageJson === packageManager &&
191
- versionFromPackageJson) {
192
- version = versionFromPackageJson;
193
- }
194
- }
183
+ if (!version) {
184
+ try {
185
+ version = (0, child_process_1.execSync)(`${packageManager} --version`, {
186
+ cwd,
187
+ encoding: 'utf-8',
188
+ windowsHide: true,
189
+ }).trim();
195
190
  }
191
+ catch { }
196
192
  }
197
193
  if (!version) {
198
194
  throw new Error(`Cannot determine the version of ${packageManager}.`);
199
195
  }
200
196
  return version;
201
197
  }
198
+ function parseVersionFromPackageManagerField(requestedPackageManager, packageManagerFieldValue) {
199
+ if (!packageManagerFieldValue)
200
+ return null;
201
+ const [packageManagerFromPackageJson, versionFromPackageJson] = packageManagerFieldValue.split('@');
202
+ if (versionFromPackageJson &&
203
+ // If it's a URL, it's not a valid range by default, unless users set `COREPACK_ENABLE_UNSAFE_CUSTOM_URLS=1`.
204
+ // In the unsafe case, there's no way to reliably pare out the version since it could be anything, e.g. http://mydomain.com/bin/yarn.js.
205
+ // See: https://github.com/nodejs/corepack/blob/2b43f26/sources/corepackUtils.ts#L110-L112
206
+ !URL.canParse(versionFromPackageJson) &&
207
+ packageManagerFromPackageJson === requestedPackageManager &&
208
+ versionFromPackageJson) {
209
+ // The range could have a validation hash attached, like "3.2.3+sha224.953c8233f7a92884eee2de69a1b92d1f2ec1655e66d08071ba9a02fa".
210
+ // We just want to parse out the "<major>.<minor>.<patch>". Semver treats "+" as a build, which is not included in the resulting version.
211
+ return (0, semver_1.parse)(versionFromPackageJson)?.version ?? null;
212
+ }
213
+ return null;
214
+ }
202
215
  /**
203
216
  * Checks for a project level npmrc file by crawling up the file tree until
204
217
  * hitting a package.json file, as this is how npm finds them as well.
@@ -1,32 +0,0 @@
1
- import { NxReleaseChangelogConfiguration } from '../../../config/nx-json';
2
- import { Reference } from './git';
3
- import { ReleaseVersion } from './shared';
4
- export type RepoSlug = `${string}/${string}`;
5
- interface GithubRequestConfig {
6
- repo: string;
7
- hostname: string;
8
- apiBaseUrl: string;
9
- token: string | null;
10
- }
11
- interface GithubRelease {
12
- id?: string;
13
- tag_name: string;
14
- target_commitish?: string;
15
- name?: string;
16
- body?: string;
17
- draft?: boolean;
18
- prerelease?: boolean;
19
- make_latest?: 'legacy' | boolean;
20
- }
21
- export interface GithubRepoData {
22
- hostname: string;
23
- slug: RepoSlug;
24
- apiBaseUrl: string;
25
- }
26
- export declare function getGitHubRepoData(remoteName: string, createReleaseConfig: NxReleaseChangelogConfiguration['createRelease']): GithubRepoData | null;
27
- export declare function createOrUpdateGithubRelease(createReleaseConfig: NxReleaseChangelogConfiguration['createRelease'], releaseVersion: ReleaseVersion, changelogContents: string, latestCommit: string, { dryRun }: {
28
- dryRun: boolean;
29
- }): Promise<void>;
30
- export declare function getGithubReleaseByTag(config: GithubRequestConfig, tag: string): Promise<GithubRelease>;
31
- export declare function formatReferences(references: Reference[], repoData: GithubRepoData): string;
32
- export {};
@@ -1,326 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getGitHubRepoData = getGitHubRepoData;
4
- exports.createOrUpdateGithubRelease = createOrUpdateGithubRelease;
5
- exports.getGithubReleaseByTag = getGithubReleaseByTag;
6
- exports.formatReferences = formatReferences;
7
- const chalk = require("chalk");
8
- const enquirer_1 = require("enquirer");
9
- const node_child_process_1 = require("node:child_process");
10
- const node_fs_1 = require("node:fs");
11
- const node_os_1 = require("node:os");
12
- const output_1 = require("../../../utils/output");
13
- const path_1 = require("../../../utils/path");
14
- const config_1 = require("../config/config");
15
- const print_changes_1 = require("./print-changes");
16
- const shared_1 = require("./shared");
17
- // axios types and values don't seem to match
18
- const _axios = require("axios");
19
- const axios = _axios;
20
- function getGitHubRepoData(remoteName = 'origin', createReleaseConfig) {
21
- try {
22
- const remoteUrl = (0, node_child_process_1.execSync)(`git remote get-url ${remoteName}`, {
23
- encoding: 'utf8',
24
- stdio: 'pipe',
25
- }).trim();
26
- // Use the default provider (github.com) if custom one is not specified or releases are disabled
27
- let hostname = config_1.defaultCreateReleaseProvider.hostname;
28
- let apiBaseUrl = config_1.defaultCreateReleaseProvider.apiBaseUrl;
29
- if (createReleaseConfig !== false &&
30
- typeof createReleaseConfig !== 'string') {
31
- hostname = createReleaseConfig.hostname;
32
- apiBaseUrl = createReleaseConfig.apiBaseUrl;
33
- }
34
- // Extract the 'user/repo' part from the URL
35
- const escapedHostname = hostname.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
36
- const regexString = `${escapedHostname}[/:]([\\w.-]+/[\\w.-]+)(\\.git)?`;
37
- const regex = new RegExp(regexString);
38
- const match = remoteUrl.match(regex);
39
- if (match && match[1]) {
40
- return {
41
- hostname,
42
- apiBaseUrl,
43
- // Ensure any trailing .git is stripped
44
- slug: match[1].replace(/\.git$/, ''),
45
- };
46
- }
47
- else {
48
- throw new Error(`Could not extract "user/repo" data from the resolved remote URL: ${remoteUrl}`);
49
- }
50
- }
51
- catch (error) {
52
- return null;
53
- }
54
- }
55
- async function createOrUpdateGithubRelease(createReleaseConfig, releaseVersion, changelogContents, latestCommit, { dryRun }) {
56
- const githubRepoData = getGitHubRepoData(undefined, createReleaseConfig);
57
- if (!githubRepoData) {
58
- output_1.output.error({
59
- title: `Unable to create a GitHub release because the GitHub repo slug could not be determined.`,
60
- bodyLines: [
61
- `Please ensure you have a valid GitHub remote configured. You can run \`git remote -v\` to list your current remotes.`,
62
- ],
63
- });
64
- process.exit(1);
65
- }
66
- const token = await resolveGithubToken(githubRepoData.hostname);
67
- const githubRequestConfig = {
68
- repo: githubRepoData.slug,
69
- hostname: githubRepoData.hostname,
70
- apiBaseUrl: githubRepoData.apiBaseUrl,
71
- token,
72
- };
73
- let existingGithubReleaseForVersion;
74
- try {
75
- existingGithubReleaseForVersion = await getGithubReleaseByTag(githubRequestConfig, releaseVersion.gitTag);
76
- }
77
- catch (err) {
78
- if (err.response?.status === 401) {
79
- output_1.output.error({
80
- title: `Unable to resolve data via the GitHub API. You can use any of the following options to resolve this:`,
81
- bodyLines: [
82
- '- Set the `GITHUB_TOKEN` or `GH_TOKEN` environment variable to a valid GitHub token with `repo` scope',
83
- '- Have an active session via the official gh CLI tool (https://cli.github.com) in your current terminal',
84
- ],
85
- });
86
- process.exit(1);
87
- }
88
- if (err.response?.status === 404) {
89
- // No existing release found, this is fine
90
- }
91
- else {
92
- // Rethrow unknown errors for now
93
- throw err;
94
- }
95
- }
96
- const logTitle = `https://${githubRepoData.hostname}/${githubRepoData.slug}/releases/tag/${releaseVersion.gitTag}`;
97
- if (existingGithubReleaseForVersion) {
98
- console.error(`${chalk.white('UPDATE')} ${logTitle}${dryRun ? chalk.keyword('orange')(' [dry-run]') : ''}`);
99
- }
100
- else {
101
- console.error(`${chalk.green('CREATE')} ${logTitle}${dryRun ? chalk.keyword('orange')(' [dry-run]') : ''}`);
102
- }
103
- console.log('');
104
- (0, print_changes_1.printDiff)(existingGithubReleaseForVersion ? existingGithubReleaseForVersion.body : '', changelogContents, 3, shared_1.noDiffInChangelogMessage);
105
- if (!dryRun) {
106
- await createOrUpdateGithubReleaseInternal(githubRequestConfig, {
107
- version: releaseVersion.gitTag,
108
- prerelease: releaseVersion.isPrerelease,
109
- body: changelogContents,
110
- commit: latestCommit,
111
- }, existingGithubReleaseForVersion);
112
- }
113
- }
114
- async function createOrUpdateGithubReleaseInternal(githubRequestConfig, release, existingGithubReleaseForVersion) {
115
- const result = await syncGithubRelease(githubRequestConfig, release, existingGithubReleaseForVersion);
116
- /**
117
- * If something went wrong POSTing to Github we can still pre-populate the web form on github.com
118
- * to allow the user to manually complete the release if they so choose.
119
- */
120
- if (result.status === 'manual') {
121
- if (result.error) {
122
- process.exitCode = 1;
123
- if (result.error.response?.data) {
124
- // There's a nicely formatted error from GitHub we can display to the user
125
- output_1.output.error({
126
- title: `A GitHub API Error occurred when creating/updating the release`,
127
- bodyLines: [
128
- `GitHub Error: ${JSON.stringify(result.error.response.data)}`,
129
- `---`,
130
- `Request Data:`,
131
- `Repo: ${githubRequestConfig.repo}`,
132
- `Token: ${githubRequestConfig.token}`,
133
- `Body: ${JSON.stringify(result.requestData)}`,
134
- ],
135
- });
136
- }
137
- else {
138
- console.log(result.error);
139
- console.error(`An unknown error occurred while trying to create a release on GitHub, please report this on https://github.com/nrwl/nx (NOTE: make sure to redact your GitHub token from the error message!)`);
140
- }
141
- }
142
- const shouldContinueInGitHub = await promptForContinueInGitHub();
143
- if (!shouldContinueInGitHub) {
144
- return;
145
- }
146
- const open = require('open');
147
- await open(result.url)
148
- .then(() => {
149
- console.info(`\nFollow up in the browser to manually create the release:\n\n` +
150
- chalk.underline(chalk.cyan(result.url)) +
151
- `\n`);
152
- })
153
- .catch(() => {
154
- console.info(`Open this link to manually create a release: \n` +
155
- chalk.underline(chalk.cyan(result.url)) +
156
- '\n');
157
- });
158
- }
159
- /**
160
- * If something went wrong POSTing to Github we can still pre-populate the web form on github.com
161
- * to allow the user to manually complete the release.
162
- */
163
- if (result.status === 'manual') {
164
- if (result.error) {
165
- console.error(result.error);
166
- process.exitCode = 1;
167
- }
168
- const open = require('open');
169
- await open(result.url)
170
- .then(() => {
171
- console.info(`Follow up in the browser to manually create the release.`);
172
- })
173
- .catch(() => {
174
- console.info(`Open this link to manually create a release: \n` +
175
- chalk.underline(chalk.cyan(result.url)) +
176
- '\n');
177
- });
178
- }
179
- }
180
- async function promptForContinueInGitHub() {
181
- try {
182
- const reply = await (0, enquirer_1.prompt)([
183
- {
184
- name: 'open',
185
- message: 'Do you want to finish creating the release manually in your browser?',
186
- type: 'autocomplete',
187
- choices: [
188
- {
189
- name: 'Yes',
190
- hint: 'It will pre-populate the form for you',
191
- },
192
- {
193
- name: 'No',
194
- },
195
- ],
196
- initial: 0,
197
- },
198
- ]);
199
- return reply.open === 'Yes';
200
- }
201
- catch {
202
- // Ensure the cursor is always restored before exiting
203
- process.stdout.write('\u001b[?25h');
204
- // Handle the case where the user exits the prompt with ctrl+c
205
- process.exit(1);
206
- }
207
- }
208
- async function syncGithubRelease(githubRequestConfig, release, existingGithubReleaseForVersion) {
209
- const ghRelease = {
210
- tag_name: release.version,
211
- name: release.version,
212
- body: release.body,
213
- prerelease: release.prerelease,
214
- // legacy specifies that the latest release should be determined based on the release creation date and higher semantic version.
215
- make_latest: 'legacy',
216
- };
217
- try {
218
- const newGhRelease = await (existingGithubReleaseForVersion
219
- ? updateGithubRelease(githubRequestConfig, existingGithubReleaseForVersion.id, ghRelease)
220
- : createGithubRelease(githubRequestConfig, {
221
- ...ghRelease,
222
- target_commitish: release.commit,
223
- }));
224
- return {
225
- status: existingGithubReleaseForVersion ? 'updated' : 'created',
226
- id: newGhRelease.id,
227
- url: newGhRelease.html_url,
228
- };
229
- }
230
- catch (error) {
231
- return {
232
- status: 'manual',
233
- error,
234
- url: githubNewReleaseURL(githubRequestConfig, release),
235
- requestData: ghRelease,
236
- };
237
- }
238
- }
239
- async function resolveGithubToken(hostname) {
240
- // Try and resolve from the environment
241
- const tokenFromEnv = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
242
- if (tokenFromEnv) {
243
- return tokenFromEnv;
244
- }
245
- // Try and resolve from gh CLI installation
246
- const ghCLIPath = (0, path_1.joinPathFragments)(process.env.XDG_CONFIG_HOME || (0, path_1.joinPathFragments)((0, node_os_1.homedir)(), '.config'), 'gh', 'hosts.yml');
247
- if ((0, node_fs_1.existsSync)(ghCLIPath)) {
248
- const yamlContents = await node_fs_1.promises.readFile(ghCLIPath, 'utf8');
249
- const { load } = require('@zkochan/js-yaml');
250
- const ghCLIConfig = load(yamlContents);
251
- if (ghCLIConfig[hostname]) {
252
- // Web based session (the token is already embedded in the config)
253
- if (ghCLIConfig[hostname].oauth_token) {
254
- return ghCLIConfig[hostname].oauth_token;
255
- }
256
- // SSH based session (we need to dynamically resolve a token using the CLI)
257
- if (ghCLIConfig[hostname].user &&
258
- ghCLIConfig[hostname].git_protocol === 'ssh') {
259
- return (0, node_child_process_1.execSync)(`gh auth token`, {
260
- encoding: 'utf8',
261
- stdio: 'pipe',
262
- windowsHide: false,
263
- }).trim();
264
- }
265
- }
266
- }
267
- if (hostname !== 'github.com') {
268
- console.log(`Warning: It was not possible to automatically resolve a GitHub token from your environment for hostname ${hostname}. If you set the GITHUB_TOKEN or GH_TOKEN environment variable, that will be used for GitHub API requests.`);
269
- }
270
- return null;
271
- }
272
- async function getGithubReleaseByTag(config, tag) {
273
- return await makeGithubRequest(config, `/repos/${config.repo}/releases/tags/${tag}`, {});
274
- }
275
- async function makeGithubRequest(config, url, opts = {}) {
276
- return (await axios(url, {
277
- ...opts,
278
- baseURL: config.apiBaseUrl,
279
- headers: {
280
- ...opts.headers,
281
- Authorization: config.token ? `Bearer ${config.token}` : undefined,
282
- },
283
- })).data;
284
- }
285
- async function createGithubRelease(config, body) {
286
- return await makeGithubRequest(config, `/repos/${config.repo}/releases`, {
287
- method: 'POST',
288
- data: body,
289
- });
290
- }
291
- async function updateGithubRelease(config, id, body) {
292
- return await makeGithubRequest(config, `/repos/${config.repo}/releases/${id}`, {
293
- method: 'PATCH',
294
- data: body,
295
- });
296
- }
297
- function githubNewReleaseURL(config, release) {
298
- // Parameters taken from https://github.com/isaacs/github/issues/1410#issuecomment-442240267
299
- let url = `https://${config.hostname}/${config.repo}/releases/new?tag=${release.version}&title=${release.version}&body=${encodeURIComponent(release.body)}&target=${release.commit}`;
300
- if (release.prerelease) {
301
- url += '&prerelease=true';
302
- }
303
- return url;
304
- }
305
- const providerToRefSpec = {
306
- github: { 'pull-request': 'pull', hash: 'commit', issue: 'issues' },
307
- };
308
- function formatReference(ref, repoData) {
309
- const refSpec = providerToRefSpec['github'];
310
- return `[${ref.value}](https://${repoData.hostname}/${repoData.slug}/${refSpec[ref.type]}/${ref.value.replace(/^#/, '')})`;
311
- }
312
- function formatReferences(references, repoData) {
313
- const pr = references.filter((ref) => ref.type === 'pull-request');
314
- const issue = references.filter((ref) => ref.type === 'issue');
315
- if (pr.length > 0 || issue.length > 0) {
316
- return (' (' +
317
- [...pr, ...issue]
318
- .map((ref) => formatReference(ref, repoData))
319
- .join(', ') +
320
- ')');
321
- }
322
- if (references.length > 0) {
323
- return ' (' + formatReference(references[0], repoData) + ')';
324
- }
325
- return '';
326
- }