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
@@ -0,0 +1,62 @@
1
+ import type { PostGitTask } from '../../changelog';
2
+ import type { ResolvedCreateRemoteReleaseProvider } from '../../config/config';
3
+ import type { Reference } from '../git';
4
+ import { ReleaseVersion } from '../shared';
5
+ import { RemoteReleaseClient, RemoteReleaseOptions, RemoteReleaseResult, RemoteRepoData } from './remote-release-client';
6
+ export interface GitLabRepoData extends RemoteRepoData {
7
+ projectId: string;
8
+ }
9
+ export interface GitLabRelease {
10
+ id?: string;
11
+ name?: string;
12
+ tag_name: string;
13
+ ref: string;
14
+ assets?: {
15
+ links?: {
16
+ name: string;
17
+ url: string;
18
+ direct_asset_path?: string;
19
+ link_type?: string;
20
+ }[];
21
+ };
22
+ released_at?: string;
23
+ description?: string;
24
+ milestones?: string[];
25
+ prerelease?: boolean;
26
+ }
27
+ export declare const defaultCreateReleaseProvider: ResolvedCreateRemoteReleaseProvider;
28
+ export declare class GitLabRemoteReleaseClient extends RemoteReleaseClient<GitLabRelease> {
29
+ remoteReleaseProviderName: string;
30
+ /**
31
+ * Get GitLab repository data from git remote
32
+ */
33
+ static resolveRepoData(createReleaseConfig: false | ResolvedCreateRemoteReleaseProvider, remoteName?: string): GitLabRepoData | null;
34
+ /**
35
+ * Resolve a GitLab token from various environment variables
36
+ */
37
+ static resolveTokenData(hostname: string): Promise<{
38
+ token: string;
39
+ headerName: string;
40
+ } | null>;
41
+ createPostGitTask(releaseVersion: ReleaseVersion, changelogContents: string, dryRun: boolean): PostGitTask;
42
+ applyUsernameToAuthors(): Promise<void>;
43
+ protected getReleaseByTag(tag: string): Promise<GitLabRelease>;
44
+ protected createRelease(remoteRelease: GitLabRelease): Promise<any>;
45
+ protected updateRelease(_id: string, remoteRelease: GitLabRelease): Promise<any>;
46
+ /**
47
+ * Generate a URL for manual release creation on GitLab. Sadly, unlike GitHub, GitLab does not
48
+ * seem to respect query string parameters for setting the UI form fields, so the user has to
49
+ * start from scratch.
50
+ */
51
+ protected getManualRemoteReleaseURL(_remoteReleaseOptions: RemoteReleaseOptions): string;
52
+ protected handleAuthError(): void;
53
+ protected logReleaseAction(existingRelease: GitLabRelease | undefined, gitTag: string, dryRun: boolean): void;
54
+ protected handleError(error: any, result: RemoteReleaseResult): Promise<void>;
55
+ private promptForContinueInGitLab;
56
+ /**
57
+ * Format references for the release (e.g., MRs, issues)
58
+ */
59
+ formatReferences(references: Reference[]): string;
60
+ protected syncRelease(remoteReleaseOptions: RemoteReleaseOptions, existingRelease?: GitLabRelease): Promise<RemoteReleaseResult>;
61
+ private getRequiredRemoteRepoData;
62
+ }
@@ -0,0 +1,271 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GitLabRemoteReleaseClient = exports.defaultCreateReleaseProvider = void 0;
4
+ const chalk = require("chalk");
5
+ const enquirer_1 = require("enquirer");
6
+ const node_child_process_1 = require("node:child_process");
7
+ const output_1 = require("../../../../utils/output");
8
+ const remote_release_client_1 = require("./remote-release-client");
9
+ exports.defaultCreateReleaseProvider = {
10
+ provider: 'gitlab',
11
+ hostname: 'gitlab.com',
12
+ apiBaseUrl: 'https://gitlab.com/api/v4',
13
+ };
14
+ class GitLabRemoteReleaseClient extends remote_release_client_1.RemoteReleaseClient {
15
+ constructor() {
16
+ super(...arguments);
17
+ this.remoteReleaseProviderName = 'GitLab';
18
+ }
19
+ /**
20
+ * Get GitLab repository data from git remote
21
+ */
22
+ static resolveRepoData(createReleaseConfig, remoteName = 'origin') {
23
+ try {
24
+ const remoteUrl = (0, node_child_process_1.execSync)(`git remote get-url ${remoteName}`, {
25
+ encoding: 'utf8',
26
+ stdio: 'pipe',
27
+ }).trim();
28
+ // Use the default provider if custom one is not specified or releases are disabled
29
+ let hostname = exports.defaultCreateReleaseProvider.hostname;
30
+ let apiBaseUrl = exports.defaultCreateReleaseProvider.apiBaseUrl;
31
+ if (createReleaseConfig !== false &&
32
+ typeof createReleaseConfig !== 'string') {
33
+ hostname = createReleaseConfig.hostname || hostname;
34
+ apiBaseUrl = createReleaseConfig.apiBaseUrl || apiBaseUrl;
35
+ }
36
+ // Extract the project path from the URL
37
+ const escapedHostname = hostname.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
38
+ const regexString = `${escapedHostname}[/:]([\\w.-]+/[\\w.-]+(?:/[\\w.-]+)*)(\\.git)?`;
39
+ const regex = new RegExp(regexString);
40
+ const match = remoteUrl.match(regex);
41
+ if (match && match[1]) {
42
+ // Remove trailing .git if present
43
+ const slug = match[1].replace(/\.git$/, '');
44
+ // Encode the project path for use in API URLs
45
+ const projectId = encodeURIComponent(slug);
46
+ return {
47
+ hostname,
48
+ apiBaseUrl,
49
+ slug,
50
+ projectId,
51
+ };
52
+ }
53
+ else {
54
+ throw new Error(`Could not extract project path data from the resolved remote URL: ${remoteUrl}`);
55
+ }
56
+ }
57
+ catch (err) {
58
+ if (process.env.NX_VERBOSE_LOGGING === 'true') {
59
+ console.error(err);
60
+ }
61
+ return null;
62
+ }
63
+ }
64
+ /**
65
+ * Resolve a GitLab token from various environment variables
66
+ */
67
+ static async resolveTokenData(hostname) {
68
+ // Try and resolve from the environment
69
+ const tokenFromEnv = process.env.GITLAB_TOKEN || process.env.GL_TOKEN;
70
+ if (tokenFromEnv) {
71
+ return { token: tokenFromEnv, headerName: 'PRIVATE-TOKEN' };
72
+ }
73
+ // Try and resolve from a CI environment
74
+ if (process.env.CI_JOB_TOKEN) {
75
+ return { token: process.env.CI_JOB_TOKEN, headerName: 'JOB-TOKEN' };
76
+ }
77
+ if (hostname !== 'gitlab.com') {
78
+ console.log(`Warning: It was not possible to automatically resolve a GitLab token from your environment for hostname ${hostname}. If you set the GITLAB_TOKEN or GL_TOKEN environment variable (or you are in GitLab CI where CI_JOB_TOKEN is set automatically), that will be used for GitLab API requests.`);
79
+ }
80
+ return null;
81
+ }
82
+ createPostGitTask(releaseVersion, changelogContents, dryRun) {
83
+ return async (latestCommit) => {
84
+ output_1.output.logSingleLine(`Creating GitLab Release`);
85
+ await this.createOrUpdateRelease(releaseVersion, changelogContents, latestCommit, { dryRun });
86
+ };
87
+ }
88
+ // Not implemented for GitLab yet, the changelog renderer should not call this method
89
+ async applyUsernameToAuthors() {
90
+ throw new Error('applyUsernameToAuthors is not implemented for GitLab yet');
91
+ }
92
+ async getReleaseByTag(tag) {
93
+ const gitlabRepoData = this.getRequiredRemoteRepoData();
94
+ return await this.makeRequest(`/projects/${gitlabRepoData.projectId}/releases/${encodeURIComponent(tag)}`);
95
+ }
96
+ async createRelease(remoteRelease) {
97
+ const gitlabRepoData = this.getRequiredRemoteRepoData();
98
+ return await this.makeRequest(`/projects/${gitlabRepoData.projectId}/releases`, {
99
+ method: 'POST',
100
+ data: remoteRelease,
101
+ });
102
+ }
103
+ async updateRelease(_id, remoteRelease) {
104
+ const gitlabRepoData = this.getRequiredRemoteRepoData();
105
+ return await this.makeRequest(`/projects/${gitlabRepoData.projectId}/releases/${encodeURIComponent(remoteRelease.tag_name)}`, {
106
+ method: 'PUT',
107
+ data: remoteRelease,
108
+ });
109
+ }
110
+ /**
111
+ * Generate a URL for manual release creation on GitLab. Sadly, unlike GitHub, GitLab does not
112
+ * seem to respect query string parameters for setting the UI form fields, so the user has to
113
+ * start from scratch.
114
+ */
115
+ getManualRemoteReleaseURL(_remoteReleaseOptions) {
116
+ const gitlabRepoData = this.getRequiredRemoteRepoData();
117
+ return `https://${gitlabRepoData.hostname}/${gitlabRepoData.slug}/-/releases/new`;
118
+ }
119
+ handleAuthError() {
120
+ output_1.output.error({
121
+ title: `Unable to resolve data via the GitLab API.`,
122
+ bodyLines: [
123
+ '- Set the `GITLAB_TOKEN` or `GL_TOKEN` environment variable to a valid GitLab token with `api` scope',
124
+ '- If running in GitLab CI, the automatically provisioned CI_JOB_TOKEN can also be used',
125
+ ],
126
+ });
127
+ }
128
+ logReleaseAction(existingRelease, gitTag, dryRun) {
129
+ const gitlabRepoData = this.getRequiredRemoteRepoData();
130
+ const logTitle = `https://${gitlabRepoData.hostname}/${gitlabRepoData.slug}/-/releases/${encodeURIComponent(gitTag)}`;
131
+ if (existingRelease) {
132
+ console.error(`${chalk.white('UPDATE')} ${logTitle}${dryRun ? chalk.keyword('orange')(' [dry-run]') : ''}`);
133
+ }
134
+ else {
135
+ console.error(`${chalk.green('CREATE')} ${logTitle}${dryRun ? chalk.keyword('orange')(' [dry-run]') : ''}`);
136
+ }
137
+ }
138
+ async handleError(error, result) {
139
+ if (error) {
140
+ process.exitCode = 1;
141
+ if (error.response?.data) {
142
+ output_1.output.error({
143
+ title: `A GitLab API Error occurred when creating/updating the release`,
144
+ bodyLines: [
145
+ `GitLab Error: ${JSON.stringify(error.response.data)}`,
146
+ `---`,
147
+ `Request Data:`,
148
+ `Repo: ${this.getRemoteRepoData()?.slug}`,
149
+ `Token Header Data: ${this.tokenHeader}`,
150
+ `Body: ${JSON.stringify(result.requestData)}`,
151
+ ],
152
+ });
153
+ }
154
+ else {
155
+ console.log(error);
156
+ console.error(`An unknown error occurred while trying to create a release on GitLab, please report this on https://github.com/nrwl/nx (NOTE: make sure to redact your GitLab token from the error message!)`);
157
+ }
158
+ }
159
+ const shouldContinueInGitLab = await this.promptForContinueInGitLab();
160
+ if (!shouldContinueInGitLab) {
161
+ return;
162
+ }
163
+ const open = require('open');
164
+ await open(result.url)
165
+ .then(() => {
166
+ console.info(`\nFollow up in the browser to manually create the release:\n\n` +
167
+ chalk.underline(chalk.cyan(result.url)) +
168
+ `\n`);
169
+ })
170
+ .catch(() => {
171
+ console.info(`Open this link to manually create a release: \n` +
172
+ chalk.underline(chalk.cyan(result.url)) +
173
+ '\n');
174
+ });
175
+ }
176
+ async promptForContinueInGitLab() {
177
+ try {
178
+ const reply = await (0, enquirer_1.prompt)([
179
+ {
180
+ name: 'open',
181
+ message: 'Do you want to create the release manually in your browser?',
182
+ type: 'autocomplete',
183
+ choices: [
184
+ {
185
+ name: 'Yes',
186
+ hint: 'It will open the GitLab release page for you',
187
+ },
188
+ {
189
+ name: 'No',
190
+ },
191
+ ],
192
+ initial: 0,
193
+ },
194
+ ]);
195
+ return reply.open === 'Yes';
196
+ }
197
+ catch {
198
+ // Ensure the cursor is always restored before exiting
199
+ process.stdout.write('\u001b[?25h');
200
+ // Handle the case where the user exits the prompt with ctrl+c
201
+ process.exit(1);
202
+ }
203
+ }
204
+ /**
205
+ * Format references for the release (e.g., MRs, issues)
206
+ */
207
+ formatReferences(references) {
208
+ const gitlabRepoData = this.getRequiredRemoteRepoData();
209
+ const providerToRefSpec = {
210
+ gitlab: {
211
+ 'pull-request': 'merge_requests',
212
+ hash: 'commit',
213
+ issue: 'issues',
214
+ },
215
+ };
216
+ const refSpec = providerToRefSpec.gitlab;
217
+ const formatSingleReference = (ref) => {
218
+ return `https://${gitlabRepoData.hostname}/${gitlabRepoData.slug}/-/${refSpec[ref.type]}/${ref.value.replace(/^[#!]/, '')}`;
219
+ };
220
+ const mr = references.filter((ref) => ref.type === 'pull-request');
221
+ const issue = references.filter((ref) => ref.type === 'issue');
222
+ if (mr.length > 0 || issue.length > 0) {
223
+ return (' (' +
224
+ [...mr, ...issue].map((ref) => formatSingleReference(ref)).join(', ') +
225
+ ')');
226
+ }
227
+ if (references.length > 0) {
228
+ return ' (' + formatSingleReference(references[0]) + ')';
229
+ }
230
+ return '';
231
+ }
232
+ async syncRelease(remoteReleaseOptions, existingRelease) {
233
+ const gitlabReleaseData = {
234
+ tag_name: remoteReleaseOptions.version,
235
+ name: remoteReleaseOptions.version,
236
+ description: remoteReleaseOptions.body,
237
+ prerelease: remoteReleaseOptions.prerelease,
238
+ ref: remoteReleaseOptions.commit,
239
+ released_at: new Date().toISOString(),
240
+ assets: { links: [] },
241
+ milestones: [],
242
+ };
243
+ try {
244
+ const newGlRelease = await (existingRelease
245
+ ? this.updateRelease(existingRelease.id, gitlabReleaseData)
246
+ : this.createRelease(gitlabReleaseData));
247
+ const gitlabRepoData = this.getRequiredRemoteRepoData();
248
+ return {
249
+ status: existingRelease ? 'updated' : 'created',
250
+ id: newGlRelease.tag_name,
251
+ url: `https://${gitlabRepoData.hostname}/${gitlabRepoData.slug}/-/tags/${encodeURIComponent(remoteReleaseOptions.version)}`,
252
+ };
253
+ }
254
+ catch (error) {
255
+ return {
256
+ status: 'manual',
257
+ error,
258
+ url: this.getManualRemoteReleaseURL(remoteReleaseOptions),
259
+ requestData: gitlabReleaseData,
260
+ };
261
+ }
262
+ }
263
+ getRequiredRemoteRepoData() {
264
+ const gitlabRepoData = this.getRemoteRepoData();
265
+ if (!gitlabRepoData) {
266
+ throw new Error(`No remote repo data could be resolved for the current workspace`);
267
+ }
268
+ return gitlabRepoData;
269
+ }
270
+ }
271
+ exports.GitLabRemoteReleaseClient = GitLabRemoteReleaseClient;
@@ -0,0 +1,111 @@
1
+ import type { AxiosRequestConfig } from 'axios';
2
+ import type { PostGitTask } from '../../changelog';
3
+ import { ResolvedCreateRemoteReleaseProvider } from '../../config/config';
4
+ import type { Reference } from '../git';
5
+ import { type ReleaseVersion } from '../shared';
6
+ import type { GithubRemoteReleaseClient } from './github';
7
+ import type { GitLabRemoteReleaseClient } from './gitlab';
8
+ export type RemoteRepoSlug = `${string}/${string}`;
9
+ export interface RemoteRepoData {
10
+ hostname: string;
11
+ slug: RemoteRepoSlug;
12
+ apiBaseUrl: string;
13
+ }
14
+ export interface RemoteReleaseOptions {
15
+ version: string;
16
+ body: string;
17
+ prerelease: boolean;
18
+ commit: string;
19
+ }
20
+ export interface RemoteReleaseResult {
21
+ status: 'created' | 'updated' | 'manual';
22
+ id?: string;
23
+ url?: string;
24
+ error?: any;
25
+ requestData?: any;
26
+ }
27
+ /**
28
+ * Abstract base class for remote release clients
29
+ */
30
+ export declare abstract class RemoteReleaseClient<RemoteRelease extends Record<string, any>> {
31
+ private remoteRepoData;
32
+ protected createReleaseConfig: false | ResolvedCreateRemoteReleaseProvider;
33
+ protected tokenData: {
34
+ token: string;
35
+ headerName: string;
36
+ } | null;
37
+ /**
38
+ * Used in user-facing messaging
39
+ */
40
+ abstract remoteReleaseProviderName: string;
41
+ protected tokenHeader: Record<string, string>;
42
+ constructor(remoteRepoData: RemoteRepoData | null, createReleaseConfig: false | ResolvedCreateRemoteReleaseProvider, tokenData: {
43
+ token: string;
44
+ headerName: string;
45
+ } | null);
46
+ getRemoteRepoData<T extends RemoteRepoData>(): T | null;
47
+ /**
48
+ * Create a post git task that will be executed by nx release changelog after performing any relevant
49
+ * git operations, if the user has opted into remote release creation.
50
+ */
51
+ abstract createPostGitTask(releaseVersion: ReleaseVersion, changelogContents: string, dryRun: boolean): PostGitTask;
52
+ /**
53
+ * Apply authors' corresponding usernames, if applicable, on the remote release provider. It is designed to be
54
+ * invoked by a changelog renderer implementation.
55
+ */
56
+ abstract applyUsernameToAuthors(authors: Map<string, {
57
+ email: Set<string>;
58
+ username?: string;
59
+ }>): Promise<void>;
60
+ /**
61
+ * Make an (optionally authenticated) API request to the remote release provider
62
+ */
63
+ protected makeRequest(url: string, opts?: AxiosRequestConfig): Promise<any>;
64
+ createOrUpdateRelease(releaseVersion: ReleaseVersion, changelogContents: string, latestCommit: string, { dryRun }: {
65
+ dryRun: boolean;
66
+ }): Promise<void>;
67
+ /**
68
+ * Format references for the release (e.g., PRs, issues)
69
+ */
70
+ formatReferences(_references: Reference[]): string;
71
+ /**
72
+ * Handle non-auth related errors when creating releases
73
+ */
74
+ protected abstract handleError(error: any, result: RemoteReleaseResult): Promise<void>;
75
+ /**
76
+ * Display authentication error message
77
+ */
78
+ protected abstract handleAuthError(): void;
79
+ /**
80
+ * Log the release action (create or update)
81
+ */
82
+ protected abstract logReleaseAction(existingRelease: RemoteRelease | undefined, gitTag: string, dryRun: boolean): void;
83
+ /**
84
+ * Print changelog changes
85
+ */
86
+ protected printRemoteReleaseContents(existingBody: string, newBody: string): void;
87
+ /**
88
+ * Get a release by tag
89
+ */
90
+ protected abstract getReleaseByTag(tag: string): Promise<RemoteRelease>;
91
+ /**
92
+ * Create a manual release URL used to create/edit a release in the remote release provider's UI
93
+ */
94
+ protected abstract getManualRemoteReleaseURL(remoteReleaseOptions: RemoteReleaseOptions): string;
95
+ /**
96
+ * Create a new release
97
+ */
98
+ protected abstract createRelease(body: RemoteRelease): Promise<any>;
99
+ /**
100
+ * Update an existing release
101
+ */
102
+ protected abstract updateRelease(id: string, body: RemoteRelease): Promise<any>;
103
+ /**
104
+ * Synchronize a release with the remote release provider
105
+ */
106
+ protected abstract syncRelease(remoteReleaseOptions: RemoteReleaseOptions, existingRelease?: RemoteRelease): Promise<RemoteReleaseResult>;
107
+ }
108
+ /**
109
+ * Factory function to create a remote release client based on the given configuration
110
+ */
111
+ export declare function createRemoteReleaseClient(createReleaseConfig: false | ResolvedCreateRemoteReleaseProvider, remoteName?: string): Promise<GithubRemoteReleaseClient | GitLabRemoteReleaseClient | null>;
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RemoteReleaseClient = void 0;
4
+ exports.createRemoteReleaseClient = createRemoteReleaseClient;
5
+ const axios_1 = require("axios");
6
+ const print_changes_1 = require("../print-changes");
7
+ const shared_1 = require("../shared");
8
+ /**
9
+ * Abstract base class for remote release clients
10
+ */
11
+ class RemoteReleaseClient {
12
+ constructor(
13
+ // A workspace isn't guaranteed to have a remote
14
+ remoteRepoData, createReleaseConfig, tokenData) {
15
+ this.remoteRepoData = remoteRepoData;
16
+ this.createReleaseConfig = createReleaseConfig;
17
+ this.tokenData = tokenData;
18
+ this.tokenHeader = {};
19
+ if (tokenData) {
20
+ if (tokenData.headerName === 'Authorization') {
21
+ this.tokenHeader[tokenData.headerName] = `Bearer ${tokenData.token}`;
22
+ }
23
+ else {
24
+ this.tokenHeader[tokenData.headerName] = tokenData.token;
25
+ }
26
+ }
27
+ }
28
+ getRemoteRepoData() {
29
+ return this.remoteRepoData;
30
+ }
31
+ /**
32
+ * Make an (optionally authenticated) API request to the remote release provider
33
+ */
34
+ async makeRequest(url, opts = {}) {
35
+ const remoteRepoData = this.getRemoteRepoData();
36
+ if (!remoteRepoData) {
37
+ throw new Error(`No remote repo data could be resolved for the current workspace`);
38
+ }
39
+ const config = {
40
+ ...opts,
41
+ baseURL: remoteRepoData.apiBaseUrl,
42
+ headers: {
43
+ ...opts.headers,
44
+ ...this.tokenHeader,
45
+ },
46
+ };
47
+ return (await (0, axios_1.default)(url, config)).data;
48
+ }
49
+ async createOrUpdateRelease(releaseVersion, changelogContents, latestCommit, { dryRun }) {
50
+ let existingRelease;
51
+ try {
52
+ existingRelease = await this.getReleaseByTag(releaseVersion.gitTag);
53
+ }
54
+ catch (err) {
55
+ if (err.response?.status === 401) {
56
+ this.handleAuthError();
57
+ process.exit(1);
58
+ }
59
+ if (err.response?.status === 404) {
60
+ // No existing release found, this is fine
61
+ }
62
+ else {
63
+ // Rethrow unknown errors for now
64
+ throw err;
65
+ }
66
+ }
67
+ this.logReleaseAction(existingRelease, releaseVersion.gitTag, dryRun);
68
+ this.printRemoteReleaseContents(existingRelease
69
+ ? 'body' in existingRelease
70
+ ? existingRelease.body
71
+ : 'description' in existingRelease
72
+ ? existingRelease.description
73
+ : ''
74
+ : '', changelogContents);
75
+ if (!dryRun) {
76
+ const remoteReleaseOptions = {
77
+ version: releaseVersion.gitTag,
78
+ prerelease: releaseVersion.isPrerelease,
79
+ body: changelogContents,
80
+ commit: latestCommit,
81
+ };
82
+ const result = await this.syncRelease(remoteReleaseOptions, existingRelease);
83
+ if (result.status === 'manual') {
84
+ await this.handleError(result.error, result);
85
+ }
86
+ }
87
+ }
88
+ /**
89
+ * Format references for the release (e.g., PRs, issues)
90
+ */
91
+ formatReferences(_references) {
92
+ // Base implementation - to be overridden by specific providers
93
+ return '';
94
+ }
95
+ /**
96
+ * Print changelog changes
97
+ */
98
+ printRemoteReleaseContents(existingBody, newBody) {
99
+ console.log('');
100
+ (0, print_changes_1.printDiff)(existingBody, newBody, 3, shared_1.noDiffInChangelogMessage);
101
+ }
102
+ }
103
+ exports.RemoteReleaseClient = RemoteReleaseClient;
104
+ /**
105
+ * Factory function to create a remote release client based on the given configuration
106
+ */
107
+ async function createRemoteReleaseClient(createReleaseConfig, remoteName = 'origin') {
108
+ switch (true) {
109
+ // GitHub and GitHub Enterprise Server
110
+ case typeof createReleaseConfig === 'object' &&
111
+ (createReleaseConfig.provider === 'github-enterprise-server' ||
112
+ createReleaseConfig.provider === 'github'):
113
+ // If remote releases are disabled, assume GitHub repo data resolution (but don't attempt to resolve a token) to match existing behavior
114
+ case createReleaseConfig === false: {
115
+ const { GithubRemoteReleaseClient } = await Promise.resolve().then(() => require('./github'));
116
+ const repoData = GithubRemoteReleaseClient.resolveRepoData(createReleaseConfig, remoteName);
117
+ const token = createReleaseConfig && repoData
118
+ ? await GithubRemoteReleaseClient.resolveTokenData(repoData.hostname)
119
+ : null;
120
+ return new GithubRemoteReleaseClient(repoData, createReleaseConfig, token);
121
+ }
122
+ // GitLab
123
+ case typeof createReleaseConfig === 'object' &&
124
+ createReleaseConfig.provider === 'gitlab':
125
+ {
126
+ const { GitLabRemoteReleaseClient } = await Promise.resolve().then(() => require('./gitlab'));
127
+ const repoData = GitLabRemoteReleaseClient.resolveRepoData(createReleaseConfig, remoteName);
128
+ const tokenData = repoData
129
+ ? await GitLabRemoteReleaseClient.resolveTokenData(repoData.hostname)
130
+ : null;
131
+ return new GitLabRemoteReleaseClient(repoData, createReleaseConfig, tokenData);
132
+ }
133
+ default:
134
+ throw new Error(`Unsupported remote release configuration: ${JSON.stringify(createReleaseConfig)}`);
135
+ }
136
+ }
@@ -64,7 +64,7 @@ export declare function withOverrides<T extends {
64
64
  }>(args: T, commandLevel?: number): T & {
65
65
  __overrides_unparsed__: string[];
66
66
  };
67
- declare const allOutputStyles: readonly ["dynamic", "static", "stream", "stream-without-prefixes", "compact"];
67
+ declare const allOutputStyles: readonly ["tui", "dynamic", "dynamic-legacy", "static", "stream", "stream-without-prefixes", "compact"];
68
68
  export type OutputStyle = (typeof allOutputStyles)[number];
69
69
  export declare function withOutputStyleOption(yargs: Argv, choices?: ReadonlyArray<OutputStyle>): Argv<{
70
70
  "output-style": string;
@@ -15,6 +15,8 @@ exports.withOutputStyleOption = withOutputStyleOption;
15
15
  exports.withRunOneOptions = withRunOneOptions;
16
16
  exports.parseCSV = parseCSV;
17
17
  exports.readParallelFromArgsAndEnv = readParallelFromArgsAndEnv;
18
+ const nx_json_1 = require("../../config/nx-json");
19
+ const is_tui_enabled_1 = require("../../tasks-runner/is-tui-enabled");
18
20
  exports.defaultYargsParserConfiguration = {
19
21
  'strip-dashed': true,
20
22
  'unknown-options-as-args': true,
@@ -229,23 +231,40 @@ function withOverrides(args, commandLevel = 1) {
229
231
  };
230
232
  }
231
233
  const allOutputStyles = [
234
+ 'tui',
232
235
  'dynamic',
236
+ 'dynamic-legacy',
233
237
  'static',
234
238
  'stream',
235
239
  'stream-without-prefixes',
236
240
  'compact',
237
241
  ];
238
242
  function withOutputStyleOption(yargs, choices = [
243
+ 'dynamic-legacy',
239
244
  'dynamic',
245
+ 'tui',
240
246
  'static',
241
247
  'stream',
242
248
  'stream-without-prefixes',
243
249
  ]) {
244
- return yargs.option('output-style', {
245
- describe: `Defines how Nx emits outputs tasks logs. **dynamic**: use dynamic output life cycle, previous content is overwritten or modified as new outputs are added, display minimal logs by default, always show errors. This output format is recommended on your local development environments. **static**: uses static output life cycle, no previous content is rewritten or modified as new outputs are added. This output format is recommened for CI environments. **stream**: nx by default logs output to an internal output stream, enable this option to stream logs to stdout / stderr. **stream-without-prefixes**: nx prefixes the project name the target is running on, use this option remove the project name prefix from output.`,
250
+ return yargs
251
+ .option('output-style', {
252
+ describe: `Defines how Nx emits outputs tasks logs. **tui**: enables the Nx Terminal UI, recommended for local development environments. **dynamic-legacy**: use dynamic-legacy output life cycle, previous content is overwritten or modified as new outputs are added, display minimal logs by default, always show errors. This output format is recommended for local development environments where tui is not supported. **static**: uses static output life cycle, no previous content is rewritten or modified as new outputs are added. This output format is recommened for CI environments. **stream**: nx by default logs output to an internal output stream, enable this option to stream logs to stdout / stderr. **stream-without-prefixes**: nx prefixes the project name the target is running on, use this option remove the project name prefix from output.`,
246
253
  type: 'string',
247
254
  choices,
248
- });
255
+ })
256
+ .middleware([
257
+ (args) => {
258
+ const useTui = (0, is_tui_enabled_1.shouldUseTui)((0, nx_json_1.readNxJson)(), args);
259
+ if (useTui) {
260
+ // We have to set both of these because `check` runs after the normalization that
261
+ // handles the kebab-case'd args -> camelCase'd args translation.
262
+ args['output-style'] = 'tui';
263
+ args.outputStyle = 'tui';
264
+ }
265
+ process.env.NX_TUI = useTui.toString();
266
+ },
267
+ ]);
249
268
  }
250
269
  function withRunOneOptions(yargs) {
251
270
  const executorShouldShowHelp = !(process.argv[2] === 'run' && process.argv[3] === '--help');
@@ -165,13 +165,20 @@ export interface NxReleaseChangelogConfiguration {
165
165
  * NOTE: if createRelease is set on a group of projects, it will cause the default releaseTagPattern of
166
166
  * "{projectName}@{version}" to be used for those projects, even when versioning everything together.
167
167
  */
168
- createRelease?: false | 'github' | {
168
+ createRelease?: false | 'github' | 'gitlab' | {
169
169
  provider: 'github-enterprise-server';
170
170
  hostname: string;
171
171
  /**
172
172
  * If not set, this will default to `https://${hostname}/api/v3`
173
173
  */
174
174
  apiBaseUrl?: string;
175
+ } | {
176
+ provider: 'gitlab';
177
+ hostname: string;
178
+ /**
179
+ * If not set, this will default to `https://${hostname}/api/v4`
180
+ */
181
+ apiBaseUrl?: string;
175
182
  };
176
183
  /**
177
184
  * This can either be set to a string value that will be written to the changelog file(s)