@redocly/cli 1.25.2 → 1.25.4
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.
- package/CHANGELOG.md +13 -0
- package/lib/cms/api/__tests__/api.client.test.js +125 -11
- package/lib/cms/api/api-client.d.ts +20 -12
- package/lib/cms/api/api-client.js +78 -22
- package/lib/cms/commands/__tests__/push-status.test.js +2 -1
- package/lib/cms/commands/__tests__/push.test.js +3 -2
- package/lib/cms/commands/push-status.js +2 -1
- package/lib/cms/commands/push.js +5 -3
- package/package.json +2 -2
- package/src/cms/api/__tests__/api.client.test.ts +179 -16
- package/src/cms/api/api-client.ts +122 -31
- package/src/cms/commands/__tests__/push-status.test.ts +2 -1
- package/src/cms/commands/__tests__/push.test.ts +4 -3
- package/src/cms/commands/push-status.ts +4 -2
- package/src/cms/commands/push.ts +7 -4
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @redocly/cli
|
|
2
2
|
|
|
3
|
+
## 1.25.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Added a warning message to the `push` and `push-status` commands to notify users about upcoming or ongoing resource deprecation.
|
|
8
|
+
- Updated @redocly/openapi-core to v1.25.4.
|
|
9
|
+
|
|
10
|
+
## 1.25.3
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- Updated @redocly/openapi-core to v1.25.3.
|
|
15
|
+
|
|
3
16
|
## 1.25.2
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const node_fetch_1 = require("node-fetch");
|
|
4
4
|
const FormData = require("form-data");
|
|
5
|
+
const colorette_1 = require("colorette");
|
|
5
6
|
const api_client_1 = require("../api-client");
|
|
6
7
|
jest.mock('node-fetch', () => ({
|
|
7
8
|
default: jest.fn(),
|
|
@@ -20,7 +21,7 @@ describe('ApiClient', () => {
|
|
|
20
21
|
describe('getDefaultBranch()', () => {
|
|
21
22
|
let apiClient;
|
|
22
23
|
beforeEach(() => {
|
|
23
|
-
apiClient = new api_client_1.
|
|
24
|
+
apiClient = new api_client_1.ReuniteApi({ domain: testDomain, apiKey: testToken, version, command });
|
|
24
25
|
});
|
|
25
26
|
it('should get default project branch', async () => {
|
|
26
27
|
mockFetchResponse({
|
|
@@ -70,19 +71,19 @@ describe('ApiClient', () => {
|
|
|
70
71
|
mountBranchName: 'remote-mount-branch-name',
|
|
71
72
|
mountPath: 'remote-mount-path',
|
|
72
73
|
};
|
|
74
|
+
const responseMock = {
|
|
75
|
+
id: 'remote-id',
|
|
76
|
+
type: 'CICD',
|
|
77
|
+
mountPath: 'remote-mount-path',
|
|
78
|
+
mountBranchName: 'remote-mount-branch-name',
|
|
79
|
+
organizationId: testOrg,
|
|
80
|
+
projectId: testProject,
|
|
81
|
+
};
|
|
73
82
|
let apiClient;
|
|
74
83
|
beforeEach(() => {
|
|
75
|
-
apiClient = new api_client_1.
|
|
84
|
+
apiClient = new api_client_1.ReuniteApi({ domain: testDomain, apiKey: testToken, version, command });
|
|
76
85
|
});
|
|
77
86
|
it('should upsert remote', async () => {
|
|
78
|
-
const responseMock = {
|
|
79
|
-
id: 'remote-id',
|
|
80
|
-
type: 'CICD',
|
|
81
|
-
mountPath: 'remote-mount-path',
|
|
82
|
-
mountBranchName: 'remote-mount-branch-name',
|
|
83
|
-
organizationId: testOrg,
|
|
84
|
-
projectId: testProject,
|
|
85
|
-
};
|
|
86
87
|
mockFetchResponse({
|
|
87
88
|
ok: true,
|
|
88
89
|
json: jest.fn().mockResolvedValue(responseMock),
|
|
@@ -162,7 +163,7 @@ describe('ApiClient', () => {
|
|
|
162
163
|
};
|
|
163
164
|
let apiClient;
|
|
164
165
|
beforeEach(() => {
|
|
165
|
-
apiClient = new api_client_1.
|
|
166
|
+
apiClient = new api_client_1.ReuniteApi({ domain: testDomain, apiKey: testToken, version, command });
|
|
166
167
|
});
|
|
167
168
|
it('should push to remote', async () => {
|
|
168
169
|
let passedFormData = new FormData();
|
|
@@ -216,4 +217,117 @@ describe('ApiClient', () => {
|
|
|
216
217
|
await expect(apiClient.remotes.push(testOrg, testProject, pushPayload, filesMock)).rejects.toThrow(new api_client_1.ReuniteApiError('Failed to push. Not found.', 404));
|
|
217
218
|
});
|
|
218
219
|
});
|
|
220
|
+
describe('Sunset header', () => {
|
|
221
|
+
const upsertRemoteMock = {
|
|
222
|
+
requestFn: () => apiClient.remotes.upsert(testOrg, testProject, {
|
|
223
|
+
mountBranchName: 'remote-mount-branch-name',
|
|
224
|
+
mountPath: 'remote-mount-path',
|
|
225
|
+
}),
|
|
226
|
+
responseBody: {
|
|
227
|
+
id: 'remote-id',
|
|
228
|
+
type: 'CICD',
|
|
229
|
+
mountPath: 'remote-mount-path',
|
|
230
|
+
mountBranchName: 'remote-mount-branch-name',
|
|
231
|
+
organizationId: testOrg,
|
|
232
|
+
projectId: testProject,
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
const getDefaultBranchMock = {
|
|
236
|
+
requestFn: () => apiClient.remotes.getDefaultBranch(testOrg, testProject),
|
|
237
|
+
responseBody: {
|
|
238
|
+
branchName: 'test-branch',
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
const pushMock = {
|
|
242
|
+
requestFn: () => apiClient.remotes.push(testOrg, testProject, {
|
|
243
|
+
remoteId: 'test-remote-id',
|
|
244
|
+
commit: {
|
|
245
|
+
message: 'test-message',
|
|
246
|
+
author: {
|
|
247
|
+
name: 'test-name',
|
|
248
|
+
email: 'test-email',
|
|
249
|
+
},
|
|
250
|
+
branchName: 'test-branch-name',
|
|
251
|
+
},
|
|
252
|
+
}, [{ path: 'some-file.yaml', stream: Buffer.from('text content') }]),
|
|
253
|
+
responseBody: {
|
|
254
|
+
branchName: 'rem/cicd/rem_01he7sr6ys2agb7w0g9t7978fn-main',
|
|
255
|
+
hasChanges: true,
|
|
256
|
+
files: [
|
|
257
|
+
{
|
|
258
|
+
type: 'file',
|
|
259
|
+
name: 'some-file.yaml',
|
|
260
|
+
path: 'docs/remotes/some-file.yaml',
|
|
261
|
+
lastModified: 1698925132394.2993,
|
|
262
|
+
mimeType: 'text/yaml',
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
commitSha: 'bb23a2f8e012ac0b7b9961b57fb40d8686b21b43',
|
|
266
|
+
outdated: false,
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
const endpointMocks = [upsertRemoteMock, getDefaultBranchMock, pushMock];
|
|
270
|
+
let apiClient;
|
|
271
|
+
beforeEach(() => {
|
|
272
|
+
apiClient = new api_client_1.ReuniteApi({ domain: testDomain, apiKey: testToken, version, command });
|
|
273
|
+
});
|
|
274
|
+
it.each(endpointMocks)('should report endpoint sunset in the past', async ({ responseBody, requestFn }) => {
|
|
275
|
+
jest.spyOn(process.stdout, 'write').mockImplementationOnce(() => true);
|
|
276
|
+
const sunsetDate = new Date('2024-09-06T12:30:32.456Z');
|
|
277
|
+
mockFetchResponse({
|
|
278
|
+
ok: true,
|
|
279
|
+
json: jest.fn().mockResolvedValue(responseBody),
|
|
280
|
+
headers: new Headers({
|
|
281
|
+
Sunset: sunsetDate.toISOString(),
|
|
282
|
+
}),
|
|
283
|
+
});
|
|
284
|
+
await requestFn();
|
|
285
|
+
apiClient.reportSunsetWarnings();
|
|
286
|
+
expect(process.stdout.write).toHaveBeenCalledWith((0, colorette_1.red)(`The "push" command is not compatible with your version of Redocly CLI. Update to the latest version by running "npm install @redocly/cli@latest".\n\n`));
|
|
287
|
+
});
|
|
288
|
+
it.each(endpointMocks)('should report endpoint sunset in the future', async ({ responseBody, requestFn }) => {
|
|
289
|
+
jest.spyOn(process.stdout, 'write').mockImplementationOnce(() => true);
|
|
290
|
+
const sunsetDate = new Date(Date.now() + 1000 * 60 * 60 * 24);
|
|
291
|
+
mockFetchResponse({
|
|
292
|
+
ok: true,
|
|
293
|
+
json: jest.fn().mockResolvedValue(responseBody),
|
|
294
|
+
headers: new Headers({
|
|
295
|
+
Sunset: sunsetDate.toISOString(),
|
|
296
|
+
}),
|
|
297
|
+
});
|
|
298
|
+
await requestFn();
|
|
299
|
+
apiClient.reportSunsetWarnings();
|
|
300
|
+
expect(process.stdout.write).toHaveBeenCalledWith((0, colorette_1.yellow)(`The "push" command will be incompatible with your version of Redocly CLI after ${sunsetDate.toLocaleString()}. Update to the latest version by running "npm install @redocly/cli@latest".\n\n`));
|
|
301
|
+
});
|
|
302
|
+
it('should report only expired resource', async () => {
|
|
303
|
+
jest.spyOn(process.stdout, 'write').mockImplementationOnce(() => true);
|
|
304
|
+
mockFetchResponse({
|
|
305
|
+
ok: true,
|
|
306
|
+
json: jest.fn().mockResolvedValue(upsertRemoteMock.responseBody),
|
|
307
|
+
headers: new Headers({
|
|
308
|
+
Sunset: new Date('2024-08-06T12:30:32.456Z').toISOString(),
|
|
309
|
+
}),
|
|
310
|
+
});
|
|
311
|
+
await upsertRemoteMock.requestFn();
|
|
312
|
+
mockFetchResponse({
|
|
313
|
+
ok: true,
|
|
314
|
+
json: jest.fn().mockResolvedValue(getDefaultBranchMock.responseBody),
|
|
315
|
+
headers: new Headers({
|
|
316
|
+
Sunset: new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString(),
|
|
317
|
+
}),
|
|
318
|
+
});
|
|
319
|
+
await getDefaultBranchMock.requestFn();
|
|
320
|
+
mockFetchResponse({
|
|
321
|
+
ok: true,
|
|
322
|
+
json: jest.fn().mockResolvedValue(pushMock.responseBody),
|
|
323
|
+
headers: new Headers({
|
|
324
|
+
Sunset: new Date('2024-08-06T12:30:32.456Z').toISOString(),
|
|
325
|
+
}),
|
|
326
|
+
});
|
|
327
|
+
await pushMock.requestFn();
|
|
328
|
+
apiClient.reportSunsetWarnings();
|
|
329
|
+
expect(process.stdout.write).toHaveBeenCalledTimes(1);
|
|
330
|
+
expect(process.stdout.write).toHaveBeenCalledWith((0, colorette_1.red)(`The "push" command is not compatible with your version of Redocly CLI. Update to the latest version by running "npm install @redocly/cli@latest".\n\n`));
|
|
331
|
+
});
|
|
332
|
+
});
|
|
219
333
|
});
|
|
@@ -2,21 +2,25 @@ import { type FetchWithTimeoutOptions } from '../../utils/fetch-with-timeout';
|
|
|
2
2
|
import type { Response } from 'node-fetch';
|
|
3
3
|
import type { ReadStream } from 'fs';
|
|
4
4
|
import type { ListRemotesResponse, PushResponse, UpsertRemoteResponse } from './types';
|
|
5
|
+
interface BaseApiClient {
|
|
6
|
+
request(url: string, options: FetchWithTimeoutOptions): Promise<Response>;
|
|
7
|
+
}
|
|
8
|
+
type CommandOption = 'push' | 'push-status';
|
|
9
|
+
export type SunsetWarning = {
|
|
10
|
+
sunsetDate: Date;
|
|
11
|
+
isSunsetExpired: boolean;
|
|
12
|
+
};
|
|
13
|
+
export type SunsetWarningsBuffer = SunsetWarning[];
|
|
5
14
|
export declare class ReuniteApiError extends Error {
|
|
6
15
|
status: number;
|
|
7
16
|
constructor(message: string, status: number);
|
|
8
17
|
}
|
|
9
|
-
declare class
|
|
10
|
-
|
|
11
|
-
protected command: string;
|
|
12
|
-
constructor(version: string, command: string);
|
|
13
|
-
protected getParsedResponse<T>(response: Response): Promise<T>;
|
|
14
|
-
protected request(url: string, options: FetchWithTimeoutOptions): Promise<Response>;
|
|
15
|
-
}
|
|
16
|
-
declare class RemotesApiClient extends ReuniteBaseApiClient {
|
|
18
|
+
declare class RemotesApi {
|
|
19
|
+
private client;
|
|
17
20
|
private readonly domain;
|
|
18
21
|
private readonly apiKey;
|
|
19
|
-
constructor(
|
|
22
|
+
constructor(client: BaseApiClient, domain: string, apiKey: string);
|
|
23
|
+
protected getParsedResponse<T>(response: Response): Promise<T>;
|
|
20
24
|
getDefaultBranch(organizationId: string, projectId: string): Promise<string>;
|
|
21
25
|
upsert(organizationId: string, projectId: string, remote: {
|
|
22
26
|
mountPath: string;
|
|
@@ -37,14 +41,18 @@ declare class RemotesApiClient extends ReuniteBaseApiClient {
|
|
|
37
41
|
pushId: string;
|
|
38
42
|
}): Promise<PushResponse>;
|
|
39
43
|
}
|
|
40
|
-
export declare class
|
|
41
|
-
|
|
44
|
+
export declare class ReuniteApi {
|
|
45
|
+
private apiClient;
|
|
46
|
+
private version;
|
|
47
|
+
private command;
|
|
48
|
+
remotes: RemotesApi;
|
|
42
49
|
constructor({ domain, apiKey, version, command, }: {
|
|
43
50
|
domain: string;
|
|
44
51
|
apiKey: string;
|
|
45
52
|
version: string;
|
|
46
|
-
command:
|
|
53
|
+
command: CommandOption;
|
|
47
54
|
});
|
|
55
|
+
reportSunsetWarnings(): void;
|
|
48
56
|
}
|
|
49
57
|
export type PushPayload = {
|
|
50
58
|
remoteId: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.ReuniteApi = exports.ReuniteApiError = void 0;
|
|
4
|
+
const colorette_1 = require("colorette");
|
|
4
5
|
const FormData = require("form-data");
|
|
5
6
|
const fetch_with_timeout_1 = require("../../utils/fetch-with-timeout");
|
|
6
7
|
class ReuniteApiError extends Error {
|
|
@@ -10,38 +11,70 @@ class ReuniteApiError extends Error {
|
|
|
10
11
|
}
|
|
11
12
|
}
|
|
12
13
|
exports.ReuniteApiError = ReuniteApiError;
|
|
13
|
-
class
|
|
14
|
+
class ReuniteApiClient {
|
|
14
15
|
constructor(version, command) {
|
|
15
16
|
this.version = version;
|
|
16
17
|
this.command = command;
|
|
18
|
+
this.sunsetWarnings = [];
|
|
17
19
|
}
|
|
18
|
-
async
|
|
19
|
-
const responseBody = await response.json();
|
|
20
|
-
if (response.ok) {
|
|
21
|
-
return responseBody;
|
|
22
|
-
}
|
|
23
|
-
throw new ReuniteApiError(`${responseBody.title || response.statusText || 'Unknown error'}.`, response.status);
|
|
24
|
-
}
|
|
25
|
-
request(url, options) {
|
|
20
|
+
async request(url, options) {
|
|
26
21
|
const headers = {
|
|
27
22
|
...options.headers,
|
|
28
23
|
'user-agent': `redocly-cli/${this.version.trim()} ${this.command}`,
|
|
29
24
|
};
|
|
30
|
-
|
|
25
|
+
const response = await (0, fetch_with_timeout_1.default)(url, {
|
|
31
26
|
...options,
|
|
32
27
|
headers,
|
|
33
28
|
});
|
|
29
|
+
this.collectSunsetWarning(response);
|
|
30
|
+
return response;
|
|
31
|
+
}
|
|
32
|
+
collectSunsetWarning(response) {
|
|
33
|
+
const sunsetTime = this.getSunsetDate(response);
|
|
34
|
+
if (!sunsetTime)
|
|
35
|
+
return;
|
|
36
|
+
const sunsetDate = new Date(sunsetTime);
|
|
37
|
+
if (sunsetTime > Date.now()) {
|
|
38
|
+
this.sunsetWarnings.push({
|
|
39
|
+
sunsetDate,
|
|
40
|
+
isSunsetExpired: false,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
this.sunsetWarnings.push({
|
|
45
|
+
sunsetDate,
|
|
46
|
+
isSunsetExpired: true,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
getSunsetDate(response) {
|
|
51
|
+
const { headers } = response;
|
|
52
|
+
if (!headers) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const sunsetDate = headers.get('sunset') || headers.get('Sunset');
|
|
56
|
+
if (!sunsetDate) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
return Date.parse(sunsetDate);
|
|
34
60
|
}
|
|
35
61
|
}
|
|
36
|
-
class
|
|
37
|
-
constructor(domain, apiKey
|
|
38
|
-
|
|
62
|
+
class RemotesApi {
|
|
63
|
+
constructor(client, domain, apiKey) {
|
|
64
|
+
this.client = client;
|
|
39
65
|
this.domain = domain;
|
|
40
66
|
this.apiKey = apiKey;
|
|
41
67
|
}
|
|
68
|
+
async getParsedResponse(response) {
|
|
69
|
+
const responseBody = await response.json();
|
|
70
|
+
if (response.ok) {
|
|
71
|
+
return responseBody;
|
|
72
|
+
}
|
|
73
|
+
throw new ReuniteApiError(`${responseBody.title || response.statusText || 'Unknown error'}.`, response.status);
|
|
74
|
+
}
|
|
42
75
|
async getDefaultBranch(organizationId, projectId) {
|
|
43
76
|
try {
|
|
44
|
-
const response = await this.request(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/source`, {
|
|
77
|
+
const response = await this.client.request(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/source`, {
|
|
45
78
|
timeout: fetch_with_timeout_1.DEFAULT_FETCH_TIMEOUT,
|
|
46
79
|
method: 'GET',
|
|
47
80
|
headers: {
|
|
@@ -62,7 +95,7 @@ class RemotesApiClient extends ReuniteBaseApiClient {
|
|
|
62
95
|
}
|
|
63
96
|
async upsert(organizationId, projectId, remote) {
|
|
64
97
|
try {
|
|
65
|
-
const response = await this.request(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/remotes`, {
|
|
98
|
+
const response = await this.client.request(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/remotes`, {
|
|
66
99
|
timeout: fetch_with_timeout_1.DEFAULT_FETCH_TIMEOUT,
|
|
67
100
|
method: 'POST',
|
|
68
101
|
headers: {
|
|
@@ -103,7 +136,7 @@ class RemotesApiClient extends ReuniteBaseApiClient {
|
|
|
103
136
|
}
|
|
104
137
|
payload.isMainBranch && formData.append('isMainBranch', 'true');
|
|
105
138
|
try {
|
|
106
|
-
const response = await this.request(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/pushes`, {
|
|
139
|
+
const response = await this.client.request(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/pushes`, {
|
|
107
140
|
method: 'POST',
|
|
108
141
|
headers: {
|
|
109
142
|
Authorization: `Bearer ${this.apiKey}`,
|
|
@@ -122,7 +155,7 @@ class RemotesApiClient extends ReuniteBaseApiClient {
|
|
|
122
155
|
}
|
|
123
156
|
async getRemotesList({ organizationId, projectId, mountPath, }) {
|
|
124
157
|
try {
|
|
125
|
-
const response = await this.request(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/remotes?filter=mountPath:/${mountPath}/`, {
|
|
158
|
+
const response = await this.client.request(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/remotes?filter=mountPath:/${mountPath}/`, {
|
|
126
159
|
timeout: fetch_with_timeout_1.DEFAULT_FETCH_TIMEOUT,
|
|
127
160
|
method: 'GET',
|
|
128
161
|
headers: {
|
|
@@ -142,7 +175,7 @@ class RemotesApiClient extends ReuniteBaseApiClient {
|
|
|
142
175
|
}
|
|
143
176
|
async getPush({ organizationId, projectId, pushId, }) {
|
|
144
177
|
try {
|
|
145
|
-
const response = await this.request(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/pushes/${pushId}`, {
|
|
178
|
+
const response = await this.client.request(`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/pushes/${pushId}`, {
|
|
146
179
|
timeout: fetch_with_timeout_1.DEFAULT_FETCH_TIMEOUT,
|
|
147
180
|
method: 'GET',
|
|
148
181
|
headers: {
|
|
@@ -161,9 +194,32 @@ class RemotesApiClient extends ReuniteBaseApiClient {
|
|
|
161
194
|
}
|
|
162
195
|
}
|
|
163
196
|
}
|
|
164
|
-
class
|
|
197
|
+
class ReuniteApi {
|
|
165
198
|
constructor({ domain, apiKey, version, command, }) {
|
|
166
|
-
this.
|
|
199
|
+
this.command = command;
|
|
200
|
+
this.version = version;
|
|
201
|
+
this.apiClient = new ReuniteApiClient(this.version, this.command);
|
|
202
|
+
this.remotes = new RemotesApi(this.apiClient, domain, apiKey);
|
|
203
|
+
}
|
|
204
|
+
reportSunsetWarnings() {
|
|
205
|
+
const sunsetWarnings = this.apiClient.sunsetWarnings;
|
|
206
|
+
if (sunsetWarnings.length) {
|
|
207
|
+
const [{ isSunsetExpired, sunsetDate }] = sunsetWarnings.sort((a, b) => {
|
|
208
|
+
// First, prioritize by expiration status
|
|
209
|
+
if (a.isSunsetExpired !== b.isSunsetExpired) {
|
|
210
|
+
return a.isSunsetExpired ? -1 : 1;
|
|
211
|
+
}
|
|
212
|
+
// If both are either expired or not, sort by sunset date
|
|
213
|
+
return a.sunsetDate > b.sunsetDate ? 1 : -1;
|
|
214
|
+
});
|
|
215
|
+
const updateVersionMessage = `Update to the latest version by running "npm install @redocly/cli@latest".`;
|
|
216
|
+
if (isSunsetExpired) {
|
|
217
|
+
process.stdout.write((0, colorette_1.red)(`The "${this.command}" command is not compatible with your version of Redocly CLI. ${updateVersionMessage}\n\n`));
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
process.stdout.write((0, colorette_1.yellow)(`The "${this.command}" command will be incompatible with your version of Redocly CLI after ${sunsetDate.toLocaleString()}. ${updateVersionMessage}\n\n`));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
167
223
|
}
|
|
168
224
|
}
|
|
169
|
-
exports.
|
|
225
|
+
exports.ReuniteApi = ReuniteApi;
|
|
@@ -15,8 +15,9 @@ jest.mock('colorette', () => ({
|
|
|
15
15
|
}));
|
|
16
16
|
jest.mock('../../api', () => ({
|
|
17
17
|
...jest.requireActual('../../api'),
|
|
18
|
-
|
|
18
|
+
ReuniteApi: jest.fn().mockImplementation(function (...args) {
|
|
19
19
|
this.remotes = remotes;
|
|
20
|
+
this.reportSunsetWarnings = jest.fn();
|
|
20
21
|
}),
|
|
21
22
|
}));
|
|
22
23
|
jest.mock('@redocly/openapi-core', () => ({
|
|
@@ -14,8 +14,9 @@ jest.mock('@redocly/openapi-core', () => ({
|
|
|
14
14
|
}));
|
|
15
15
|
jest.mock('../../api', () => ({
|
|
16
16
|
...jest.requireActual('../../api'),
|
|
17
|
-
|
|
17
|
+
ReuniteApi: jest.fn().mockImplementation(function (...args) {
|
|
18
18
|
this.remotes = remotes;
|
|
19
|
+
this.reportSunsetWarnings = jest.fn();
|
|
19
20
|
}),
|
|
20
21
|
}));
|
|
21
22
|
describe('handlePush()', () => {
|
|
@@ -269,7 +270,7 @@ describe('handlePush()', () => {
|
|
|
269
270
|
config: mockConfig,
|
|
270
271
|
version: 'cli-version',
|
|
271
272
|
});
|
|
272
|
-
expect(api_1.
|
|
273
|
+
expect(api_1.ReuniteApi).toBeCalledWith({
|
|
273
274
|
domain: 'test-domain-from-env',
|
|
274
275
|
apiKey: 'test-api-key',
|
|
275
276
|
version: 'cli-version',
|
|
@@ -28,7 +28,7 @@ async function handlePushStatus({ argv, config, version, }) {
|
|
|
28
28
|
const continueOnDeployFailures = argv['continue-on-deploy-failures'] || false;
|
|
29
29
|
try {
|
|
30
30
|
const apiKey = (0, api_1.getApiKeys)(domain);
|
|
31
|
-
const client = new api_1.
|
|
31
|
+
const client = new api_1.ReuniteApi({ domain, apiKey, version, command: 'push-status' });
|
|
32
32
|
let pushResponse;
|
|
33
33
|
pushResponse = await (0, utils_2.retryUntilConditionMet)({
|
|
34
34
|
operation: () => client.remotes.getPush({
|
|
@@ -119,6 +119,7 @@ async function handlePushStatus({ argv, config, version, }) {
|
|
|
119
119
|
printScorecard(pushResponse.status.production.scorecard);
|
|
120
120
|
}
|
|
121
121
|
printPushStatusInfo({ orgId, projectId, pushId, startedAt });
|
|
122
|
+
client.reportSunsetWarnings();
|
|
122
123
|
const summary = {
|
|
123
124
|
preview: pushResponse.status.preview,
|
|
124
125
|
production: pushResponse.isMainBranch ? pushResponse.status.production : null,
|
package/lib/cms/commands/push.js
CHANGED
|
@@ -30,10 +30,11 @@ async function handlePush({ argv, config, version, }) {
|
|
|
30
30
|
const author = parseCommitAuthor(argv.author);
|
|
31
31
|
const apiKey = (0, api_1.getApiKeys)(domain);
|
|
32
32
|
const filesToUpload = collectFilesToPush(argv.files || argv.apis);
|
|
33
|
+
const commandName = 'push';
|
|
33
34
|
if (!filesToUpload.length) {
|
|
34
|
-
return (0, miscellaneous_1.printExecutionTime)(
|
|
35
|
+
return (0, miscellaneous_1.printExecutionTime)(commandName, startedAt, `No files to upload`);
|
|
35
36
|
}
|
|
36
|
-
const client = new api_1.
|
|
37
|
+
const client = new api_1.ReuniteApi({ domain, apiKey, version, command: commandName });
|
|
37
38
|
const projectDefaultBranch = await client.remotes.getDefaultBranch(orgId, projectId);
|
|
38
39
|
const remote = await client.remotes.upsert(orgId, projectId, {
|
|
39
40
|
mountBranchName: projectDefaultBranch,
|
|
@@ -77,7 +78,8 @@ async function handlePush({ argv, config, version, }) {
|
|
|
77
78
|
});
|
|
78
79
|
}
|
|
79
80
|
verbose &&
|
|
80
|
-
(0, miscellaneous_1.printExecutionTime)(
|
|
81
|
+
(0, miscellaneous_1.printExecutionTime)(commandName, startedAt, `${(0, utils_1.pluralize)('file', filesToUpload.length)} uploaded to organization ${orgId}, project ${projectId}. Push ID: ${id}.`);
|
|
82
|
+
client.reportSunsetWarnings();
|
|
81
83
|
return {
|
|
82
84
|
pushId: id,
|
|
83
85
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/cli",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.4",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"Roman Hotsiy <roman@redoc.ly> (https://redoc.ly/)"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@redocly/openapi-core": "1.25.
|
|
39
|
+
"@redocly/openapi-core": "1.25.4",
|
|
40
40
|
"abort-controller": "^3.0.0",
|
|
41
41
|
"chokidar": "^3.5.1",
|
|
42
42
|
"colorette": "^1.2.0",
|