@redocly/cli 1.7.0 → 1.8.1
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 +17 -0
- package/lib/__tests__/commands/build-docs.test.js +3 -3
- package/lib/__tests__/commands/bundle.test.js +5 -5
- package/lib/__tests__/commands/join.test.js +11 -11
- package/lib/__tests__/commands/lint.test.js +14 -14
- package/lib/__tests__/commands/push-region.test.js +1 -1
- package/lib/__tests__/commands/push.test.js +11 -11
- package/lib/__tests__/fetch-with-timeout.test.js +4 -13
- package/lib/__tests__/spinner.test.js +43 -0
- package/lib/__tests__/utils.test.js +36 -36
- package/lib/__tests__/wrapper.test.js +8 -8
- package/lib/cms/api/__tests__/api-keys.test.d.ts +1 -0
- package/lib/cms/api/__tests__/api-keys.test.js +26 -0
- package/lib/cms/api/__tests__/api.client.test.d.ts +1 -0
- package/lib/cms/api/__tests__/api.client.test.js +217 -0
- package/lib/cms/api/__tests__/domains.test.d.ts +1 -0
- package/lib/cms/api/__tests__/domains.test.js +13 -0
- package/lib/cms/api/api-client.d.ts +50 -0
- package/lib/cms/api/api-client.js +148 -0
- package/lib/cms/api/api-keys.d.ts +1 -0
- package/lib/cms/api/api-keys.js +24 -0
- package/lib/cms/api/domains.d.ts +1 -0
- package/lib/cms/api/domains.js +12 -0
- package/lib/cms/api/index.d.ts +3 -0
- package/lib/cms/api/index.js +19 -0
- package/lib/cms/api/types.d.ts +91 -0
- package/lib/cms/api/types.js +2 -0
- package/lib/cms/commands/__tests__/push-status.test.d.ts +1 -0
- package/lib/cms/commands/__tests__/push-status.test.js +164 -0
- package/lib/cms/commands/__tests__/push.test.d.ts +1 -0
- package/lib/cms/commands/__tests__/push.test.js +226 -0
- package/lib/cms/commands/push-status.d.ts +12 -0
- package/lib/cms/commands/push-status.js +150 -0
- package/lib/cms/commands/push.d.ts +23 -0
- package/lib/cms/commands/push.js +142 -0
- package/lib/cms/utils.d.ts +2 -0
- package/lib/cms/utils.js +6 -0
- package/lib/commands/build-docs/index.js +4 -4
- package/lib/commands/build-docs/utils.js +2 -2
- package/lib/commands/bundle.js +13 -13
- package/lib/commands/join.js +25 -25
- package/lib/commands/lint.js +10 -10
- package/lib/commands/login.js +2 -2
- package/lib/commands/preview-docs/index.js +4 -4
- package/lib/commands/preview-docs/preview-server/preview-server.js +2 -2
- package/lib/commands/preview-project/types.d.ts +1 -1
- package/lib/commands/push.d.ts +5 -0
- package/lib/commands/push.js +25 -17
- package/lib/commands/split/__tests__/index.test.js +2 -2
- package/lib/commands/split/index.js +17 -17
- package/lib/commands/stats.js +4 -4
- package/lib/index.d.ts +1 -1
- package/lib/index.js +130 -17
- package/lib/types.d.ts +8 -1
- package/lib/{__mocks__/utils.js → utils/__mocks__/miscellaneous.js} +1 -1
- package/lib/utils/assert-node-version.d.ts +1 -0
- package/lib/{fetch-with-timeout.js → utils/fetch-with-timeout.js} +2 -7
- package/lib/{utils.d.ts → utils/miscellaneous.d.ts} +1 -1
- package/lib/{utils.js → utils/miscellaneous.js} +2 -2
- package/lib/utils/spinner.d.ts +10 -0
- package/lib/utils/spinner.js +42 -0
- package/lib/{update-version-notifier.js → utils/update-version-notifier.js} +4 -4
- package/lib/wrapper.js +5 -5
- package/package.json +5 -3
- package/src/__tests__/commands/build-docs.test.ts +2 -2
- package/src/__tests__/commands/bundle.test.ts +2 -2
- package/src/__tests__/commands/join.test.ts +2 -2
- package/src/__tests__/commands/lint.test.ts +3 -3
- package/src/__tests__/commands/push-region.test.ts +1 -1
- package/src/__tests__/commands/push.test.ts +2 -2
- package/src/__tests__/fetch-with-timeout.test.ts +4 -16
- package/src/__tests__/spinner.test.ts +51 -0
- package/src/__tests__/utils.test.ts +2 -5
- package/src/__tests__/wrapper.test.ts +2 -2
- package/src/cms/api/__tests__/api-keys.test.ts +37 -0
- package/src/cms/api/__tests__/api.client.test.ts +275 -0
- package/src/cms/api/__tests__/domains.test.ts +15 -0
- package/src/cms/api/api-client.ts +199 -0
- package/src/cms/api/api-keys.ts +26 -0
- package/src/cms/api/domains.ts +11 -0
- package/src/cms/api/index.ts +3 -0
- package/src/cms/api/types.ts +101 -0
- package/src/cms/commands/__tests__/push-status.test.ts +212 -0
- package/src/cms/commands/__tests__/push.test.ts +293 -0
- package/src/cms/commands/push-status.ts +203 -0
- package/src/cms/commands/push.ts +215 -0
- package/src/cms/utils.ts +1 -0
- package/src/commands/build-docs/index.ts +1 -1
- package/src/commands/build-docs/utils.ts +1 -1
- package/src/commands/bundle.ts +2 -2
- package/src/commands/join.ts +2 -2
- package/src/commands/lint.ts +1 -1
- package/src/commands/login.ts +1 -1
- package/src/commands/preview-docs/index.ts +5 -1
- package/src/commands/preview-docs/preview-server/preview-server.ts +1 -1
- package/src/commands/preview-project/types.ts +1 -1
- package/src/commands/push.ts +15 -1
- package/src/commands/split/__tests__/index.test.ts +3 -4
- package/src/commands/split/index.ts +2 -2
- package/src/commands/stats.ts +2 -2
- package/src/index.ts +138 -20
- package/src/types.ts +8 -0
- package/src/{__mocks__/utils.ts → utils/__mocks__/miscellaneous.ts} +1 -1
- package/src/{fetch-with-timeout.ts → utils/fetch-with-timeout.ts} +1 -6
- package/src/{utils.ts → utils/miscellaneous.ts} +2 -2
- package/src/utils/spinner.ts +50 -0
- package/src/{update-version-notifier.ts → utils/update-version-notifier.ts} +2 -2
- package/src/wrapper.ts +7 -2
- package/tsconfig.tsbuildinfo +1 -1
- /package/lib/{assert-node-version.d.ts → __tests__/spinner.test.d.ts} +0 -0
- /package/lib/{__mocks__/utils.d.ts → utils/__mocks__/miscellaneous.d.ts} +0 -0
- /package/lib/{assert-node-version.js → utils/assert-node-version.js} +0 -0
- /package/lib/{fetch-with-timeout.d.ts → utils/fetch-with-timeout.d.ts} +0 -0
- /package/lib/{js-utils.d.ts → utils/js-utils.d.ts} +0 -0
- /package/lib/{js-utils.js → utils/js-utils.js} +0 -0
- /package/lib/{update-version-notifier.d.ts → utils/update-version-notifier.d.ts} +0 -0
- /package/src/{assert-node-version.ts → utils/assert-node-version.ts} +0 -0
- /package/src/{js-utils.ts → utils/js-utils.ts} +0 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const fs = require("fs");
|
|
13
|
+
const path = require("path");
|
|
14
|
+
const push_1 = require("../push");
|
|
15
|
+
const api_1 = require("../../api");
|
|
16
|
+
const remotes = {
|
|
17
|
+
push: jest.fn(),
|
|
18
|
+
upsert: jest.fn(),
|
|
19
|
+
getDefaultBranch: jest.fn(),
|
|
20
|
+
};
|
|
21
|
+
jest.mock('@redocly/openapi-core', () => ({
|
|
22
|
+
slash: jest.fn().mockImplementation((p) => p),
|
|
23
|
+
}));
|
|
24
|
+
jest.mock('../../api', () => (Object.assign(Object.assign({}, jest.requireActual('../../api')), { ReuniteApiClient: jest.fn().mockImplementation(function (...args) {
|
|
25
|
+
this.remotes = remotes;
|
|
26
|
+
}) })));
|
|
27
|
+
describe('handlePush()', () => {
|
|
28
|
+
let pathResolveSpy;
|
|
29
|
+
let pathRelativeSpy;
|
|
30
|
+
let pathDirnameSpy;
|
|
31
|
+
let fsStatSyncSpy;
|
|
32
|
+
let fsReaddirSyncSpy;
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
remotes.getDefaultBranch.mockResolvedValueOnce('test-default-branch');
|
|
35
|
+
remotes.upsert.mockResolvedValueOnce({ id: 'test-remote-id' });
|
|
36
|
+
remotes.push.mockResolvedValueOnce({ branchName: 'uploaded-to-branch' });
|
|
37
|
+
jest.spyOn(fs, 'createReadStream').mockReturnValue('stream');
|
|
38
|
+
pathResolveSpy = jest.spyOn(path, 'resolve');
|
|
39
|
+
pathRelativeSpy = jest.spyOn(path, 'relative');
|
|
40
|
+
pathDirnameSpy = jest.spyOn(path, 'dirname');
|
|
41
|
+
fsStatSyncSpy = jest.spyOn(fs, 'statSync');
|
|
42
|
+
fsReaddirSyncSpy = jest.spyOn(fs, 'readdirSync');
|
|
43
|
+
});
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
pathResolveSpy.mockRestore();
|
|
46
|
+
pathRelativeSpy.mockRestore();
|
|
47
|
+
pathDirnameSpy.mockRestore();
|
|
48
|
+
fsStatSyncSpy.mockRestore();
|
|
49
|
+
fsReaddirSyncSpy.mockRestore();
|
|
50
|
+
});
|
|
51
|
+
it('should upload files', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
52
|
+
const mockConfig = { apis: {} };
|
|
53
|
+
process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
|
|
54
|
+
fsStatSyncSpy.mockReturnValueOnce({
|
|
55
|
+
isDirectory() {
|
|
56
|
+
return false;
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
pathResolveSpy.mockImplementationOnce((p) => p);
|
|
60
|
+
pathRelativeSpy.mockImplementationOnce((_, p) => p);
|
|
61
|
+
pathDirnameSpy.mockImplementation((_) => '.');
|
|
62
|
+
yield (0, push_1.handlePush)({
|
|
63
|
+
domain: 'test-domain',
|
|
64
|
+
'mount-path': 'test-mount-path',
|
|
65
|
+
organization: 'test-org',
|
|
66
|
+
project: 'test-project',
|
|
67
|
+
branch: 'test-branch',
|
|
68
|
+
namespace: 'test-namespace',
|
|
69
|
+
repository: 'test-repository',
|
|
70
|
+
'commit-sha': 'test-commit-sha',
|
|
71
|
+
'commit-url': 'test-commit-url',
|
|
72
|
+
'default-branch': 'test-branch',
|
|
73
|
+
'created-at': 'test-created-at',
|
|
74
|
+
author: 'TestAuthor <test-author@mail.com>',
|
|
75
|
+
message: 'Test message',
|
|
76
|
+
files: ['test-file'],
|
|
77
|
+
'max-execution-time': 10,
|
|
78
|
+
}, mockConfig);
|
|
79
|
+
expect(remotes.getDefaultBranch).toHaveBeenCalledWith('test-org', 'test-project');
|
|
80
|
+
expect(remotes.upsert).toHaveBeenCalledWith('test-org', 'test-project', {
|
|
81
|
+
mountBranchName: 'test-default-branch',
|
|
82
|
+
mountPath: 'test-mount-path',
|
|
83
|
+
});
|
|
84
|
+
expect(remotes.push).toHaveBeenCalledWith('test-org', 'test-project', {
|
|
85
|
+
isMainBranch: true,
|
|
86
|
+
remoteId: 'test-remote-id',
|
|
87
|
+
commit: {
|
|
88
|
+
message: 'Test message',
|
|
89
|
+
branchName: 'test-branch',
|
|
90
|
+
createdAt: 'test-created-at',
|
|
91
|
+
namespace: 'test-namespace',
|
|
92
|
+
repository: 'test-repository',
|
|
93
|
+
sha: 'test-commit-sha',
|
|
94
|
+
url: 'test-commit-url',
|
|
95
|
+
author: {
|
|
96
|
+
name: 'TestAuthor',
|
|
97
|
+
email: 'test-author@mail.com',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
}, [
|
|
101
|
+
{
|
|
102
|
+
path: 'test-file',
|
|
103
|
+
stream: 'stream',
|
|
104
|
+
},
|
|
105
|
+
]);
|
|
106
|
+
}));
|
|
107
|
+
it('should collect files from directory and preserve file structure', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
108
|
+
const mockConfig = { apis: {} };
|
|
109
|
+
process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
|
|
110
|
+
/*
|
|
111
|
+
├── app
|
|
112
|
+
│ ├── index.html
|
|
113
|
+
├── openapi.yaml
|
|
114
|
+
└── some-ref.yaml
|
|
115
|
+
*/
|
|
116
|
+
fsStatSyncSpy.mockImplementation((filePath) => ({
|
|
117
|
+
isDirectory() {
|
|
118
|
+
return filePath === 'test-folder' || filePath === 'test-folder/app';
|
|
119
|
+
},
|
|
120
|
+
}));
|
|
121
|
+
fsReaddirSyncSpy.mockImplementation((dirPath) => {
|
|
122
|
+
if (dirPath === 'test-folder') {
|
|
123
|
+
return ['app', 'another-ref.yaml', 'openapi.yaml'];
|
|
124
|
+
}
|
|
125
|
+
if (dirPath === 'test-folder/app') {
|
|
126
|
+
return ['index.html'];
|
|
127
|
+
}
|
|
128
|
+
throw new Error('Not a directory');
|
|
129
|
+
});
|
|
130
|
+
yield (0, push_1.handlePush)({
|
|
131
|
+
domain: 'test-domain',
|
|
132
|
+
'mount-path': 'test-mount-path',
|
|
133
|
+
organization: 'test-org',
|
|
134
|
+
project: 'test-project',
|
|
135
|
+
branch: 'test-branch',
|
|
136
|
+
author: 'TestAuthor <test-author@mail.com>',
|
|
137
|
+
message: 'Test message',
|
|
138
|
+
'default-branch': 'main',
|
|
139
|
+
files: ['test-folder'],
|
|
140
|
+
'max-execution-time': 10,
|
|
141
|
+
}, mockConfig);
|
|
142
|
+
expect(remotes.push).toHaveBeenCalledWith(expect.anything(), expect.anything(), expect.anything(), [
|
|
143
|
+
{
|
|
144
|
+
path: 'app/index.html',
|
|
145
|
+
stream: 'stream',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
path: 'another-ref.yaml',
|
|
149
|
+
stream: 'stream',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
path: 'openapi.yaml',
|
|
153
|
+
stream: 'stream',
|
|
154
|
+
},
|
|
155
|
+
]);
|
|
156
|
+
}));
|
|
157
|
+
it('should not upload files if no files passed', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
158
|
+
const mockConfig = { apis: {} };
|
|
159
|
+
process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
|
|
160
|
+
yield (0, push_1.handlePush)({
|
|
161
|
+
domain: 'test-domain',
|
|
162
|
+
'mount-path': 'test-mount-path',
|
|
163
|
+
organization: 'test-org',
|
|
164
|
+
project: 'test-project',
|
|
165
|
+
branch: 'test-branch',
|
|
166
|
+
author: 'TestAuthor <test-author@mail.com>',
|
|
167
|
+
message: 'Test message',
|
|
168
|
+
'default-branch': 'main',
|
|
169
|
+
files: [],
|
|
170
|
+
'max-execution-time': 10,
|
|
171
|
+
}, mockConfig);
|
|
172
|
+
expect(remotes.getDefaultBranch).not.toHaveBeenCalled();
|
|
173
|
+
expect(remotes.upsert).not.toHaveBeenCalled();
|
|
174
|
+
expect(remotes.push).not.toHaveBeenCalled();
|
|
175
|
+
}));
|
|
176
|
+
it('should get organization from config if not passed', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
177
|
+
const mockConfig = { organization: 'test-org-from-config', apis: {} };
|
|
178
|
+
process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
|
|
179
|
+
fsStatSyncSpy.mockReturnValueOnce({
|
|
180
|
+
isDirectory() {
|
|
181
|
+
return false;
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
pathResolveSpy.mockImplementationOnce((p) => p);
|
|
185
|
+
pathRelativeSpy.mockImplementationOnce((_, p) => p);
|
|
186
|
+
pathDirnameSpy.mockImplementation((_) => '.');
|
|
187
|
+
yield (0, push_1.handlePush)({
|
|
188
|
+
domain: 'test-domain',
|
|
189
|
+
'mount-path': 'test-mount-path',
|
|
190
|
+
project: 'test-project',
|
|
191
|
+
branch: 'test-branch',
|
|
192
|
+
author: 'TestAuthor <test-author@mail.com>',
|
|
193
|
+
message: 'Test message',
|
|
194
|
+
files: ['test-file'],
|
|
195
|
+
'default-branch': 'main',
|
|
196
|
+
'max-execution-time': 10,
|
|
197
|
+
}, mockConfig);
|
|
198
|
+
expect(remotes.getDefaultBranch).toHaveBeenCalledWith('test-org-from-config', expect.anything());
|
|
199
|
+
expect(remotes.upsert).toHaveBeenCalledWith('test-org-from-config', expect.anything(), expect.anything());
|
|
200
|
+
expect(remotes.push).toHaveBeenCalledWith('test-org-from-config', expect.anything(), expect.anything(), expect.anything());
|
|
201
|
+
}));
|
|
202
|
+
it('should get domain from env if not passed', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
203
|
+
const mockConfig = { organization: 'test-org-from-config', apis: {} };
|
|
204
|
+
process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
|
|
205
|
+
process.env.REDOCLY_DOMAIN = 'test-domain-from-env';
|
|
206
|
+
fsStatSyncSpy.mockReturnValueOnce({
|
|
207
|
+
isDirectory() {
|
|
208
|
+
return false;
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
pathResolveSpy.mockImplementationOnce((p) => p);
|
|
212
|
+
pathRelativeSpy.mockImplementationOnce((_, p) => p);
|
|
213
|
+
pathDirnameSpy.mockImplementation((_) => '.');
|
|
214
|
+
yield (0, push_1.handlePush)({
|
|
215
|
+
'mount-path': 'test-mount-path',
|
|
216
|
+
project: 'test-project',
|
|
217
|
+
branch: 'test-branch',
|
|
218
|
+
'default-branch': 'main',
|
|
219
|
+
author: 'TestAuthor <test-author@mail.com>',
|
|
220
|
+
message: 'Test message',
|
|
221
|
+
files: ['test-file'],
|
|
222
|
+
'max-execution-time': 10,
|
|
223
|
+
}, mockConfig);
|
|
224
|
+
expect(api_1.ReuniteApiClient).toBeCalledWith('test-domain-from-env', 'test-api-key');
|
|
225
|
+
}));
|
|
226
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Config } from '@redocly/openapi-core';
|
|
2
|
+
export type PushStatusOptions = {
|
|
3
|
+
organization: string;
|
|
4
|
+
project: string;
|
|
5
|
+
pushId: string;
|
|
6
|
+
domain?: string;
|
|
7
|
+
config?: string;
|
|
8
|
+
format?: 'stylish' | 'json';
|
|
9
|
+
wait?: boolean;
|
|
10
|
+
'max-execution-time': number;
|
|
11
|
+
};
|
|
12
|
+
export declare function handlePushStatus(argv: PushStatusOptions, config: Config): Promise<void>;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.handlePushStatus = void 0;
|
|
13
|
+
const colors = require("colorette");
|
|
14
|
+
const miscellaneous_1 = require("../../utils/miscellaneous");
|
|
15
|
+
const spinner_1 = require("../../utils/spinner");
|
|
16
|
+
const utils_1 = require("../utils");
|
|
17
|
+
const colorette_1 = require("colorette");
|
|
18
|
+
const api_1 = require("../api");
|
|
19
|
+
const INTERVAL = 5000;
|
|
20
|
+
function handlePushStatus(argv, config) {
|
|
21
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
22
|
+
const startedAt = performance.now();
|
|
23
|
+
const spinner = new spinner_1.Spinner();
|
|
24
|
+
const { organization, project: projectId, pushId, wait } = argv;
|
|
25
|
+
const orgId = organization || config.organization;
|
|
26
|
+
if (!orgId) {
|
|
27
|
+
return (0, miscellaneous_1.exitWithError)(`No organization provided, please use --organization option or specify the 'organization' field in the config file.`);
|
|
28
|
+
}
|
|
29
|
+
const domain = argv.domain || (0, api_1.getDomain)();
|
|
30
|
+
if (!domain) {
|
|
31
|
+
return (0, miscellaneous_1.exitWithError)(`No domain provided, please use --domain option or environment variable REDOCLY_DOMAIN.`);
|
|
32
|
+
}
|
|
33
|
+
const maxExecutionTime = argv['max-execution-time'] || 600;
|
|
34
|
+
try {
|
|
35
|
+
const apiKey = (0, api_1.getApiKeys)(domain);
|
|
36
|
+
const client = new api_1.ReuniteApiClient(domain, apiKey);
|
|
37
|
+
if (wait) {
|
|
38
|
+
const push = yield waitForDeployment(client, 'preview');
|
|
39
|
+
if (push.isMainBranch && push.status.preview.deploy.status === 'success') {
|
|
40
|
+
yield waitForDeployment(client, 'production');
|
|
41
|
+
}
|
|
42
|
+
printPushStatusInfo();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const pushPreview = yield getAndPrintPushStatus(client, 'preview');
|
|
46
|
+
printScorecard(pushPreview.status.preview.scorecard);
|
|
47
|
+
if (pushPreview.isMainBranch) {
|
|
48
|
+
yield getAndPrintPushStatus(client, 'production');
|
|
49
|
+
printScorecard(pushPreview.status.production.scorecard);
|
|
50
|
+
}
|
|
51
|
+
printPushStatusInfo();
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
const message = err instanceof utils_1.DeploymentError
|
|
55
|
+
? err.message
|
|
56
|
+
: `✗ Failed to get push status. Reason: ${err.message}\n`;
|
|
57
|
+
(0, miscellaneous_1.exitWithError)(message);
|
|
58
|
+
}
|
|
59
|
+
function printPushStatusInfo() {
|
|
60
|
+
process.stderr.write(`\nProcessed push-status for ${colors.yellow(orgId)}, ${colors.yellow(projectId)} and pushID ${colors.yellow(pushId)}.\n`);
|
|
61
|
+
(0, miscellaneous_1.printExecutionTime)('push-status', startedAt, 'Finished');
|
|
62
|
+
}
|
|
63
|
+
function waitForDeployment(client, buildType) {
|
|
64
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
if (performance.now() - startedAt > maxExecutionTime * 1000) {
|
|
67
|
+
spinner.stop();
|
|
68
|
+
reject(new Error(`Time limit exceeded.`));
|
|
69
|
+
}
|
|
70
|
+
getAndPrintPushStatus(client, buildType)
|
|
71
|
+
.then((push) => {
|
|
72
|
+
if (!['pending', 'running'].includes(push.status[buildType].deploy.status)) {
|
|
73
|
+
printScorecard(push.status[buildType].scorecard);
|
|
74
|
+
resolve(push);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
try {
|
|
79
|
+
const pushResponse = yield waitForDeployment(client, buildType);
|
|
80
|
+
resolve(pushResponse);
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
reject(e);
|
|
84
|
+
}
|
|
85
|
+
}), INTERVAL);
|
|
86
|
+
})
|
|
87
|
+
.catch(reject);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function getAndPrintPushStatus(client, buildType) {
|
|
92
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
const push = yield client.remotes.getPush({
|
|
94
|
+
organizationId: orgId,
|
|
95
|
+
projectId,
|
|
96
|
+
pushId,
|
|
97
|
+
});
|
|
98
|
+
if (push.isOutdated || !push.hasChanges) {
|
|
99
|
+
process.stderr.write((0, colorette_1.yellow)(`Files not uploaded. Reason: ${push.isOutdated ? 'outdated' : 'no changes'}.\n`));
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
displayDeploymentAndBuildStatus({
|
|
103
|
+
status: push.status[buildType].deploy.status,
|
|
104
|
+
previewUrl: push.status[buildType].deploy.url,
|
|
105
|
+
buildType,
|
|
106
|
+
spinner,
|
|
107
|
+
wait,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return push;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
exports.handlePushStatus = handlePushStatus;
|
|
116
|
+
function printScorecard(scorecard) {
|
|
117
|
+
if (!scorecard.length) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
process.stdout.write(`\n${colors.magenta('Scorecard')}:`);
|
|
121
|
+
for (const scorecardItem of scorecard) {
|
|
122
|
+
process.stdout.write(`
|
|
123
|
+
${colors.magenta('Name')}: ${scorecardItem.name}
|
|
124
|
+
${colors.magenta('Status')}: ${scorecardItem.status}
|
|
125
|
+
${colors.magenta('URL')}: ${colors.cyan(scorecardItem.url)}
|
|
126
|
+
${colors.magenta('Description')}: ${scorecardItem.description}\n`);
|
|
127
|
+
}
|
|
128
|
+
process.stdout.write(`\n`);
|
|
129
|
+
}
|
|
130
|
+
function displayDeploymentAndBuildStatus({ status, previewUrl, spinner, buildType, wait, }) {
|
|
131
|
+
switch (status) {
|
|
132
|
+
case 'success':
|
|
133
|
+
spinner.stop();
|
|
134
|
+
return process.stdout.write(`${colors.green(`🚀 ${buildType.toLocaleUpperCase()} deployment succeeded.`)}\n${colors.magenta('Preview URL')}: ${colors.cyan(previewUrl)}\n`);
|
|
135
|
+
case 'failed':
|
|
136
|
+
spinner.stop();
|
|
137
|
+
throw new utils_1.DeploymentError(`${colors.red(`❌ ${buildType.toLocaleUpperCase()} deployment failed.`)}\n${colors.magenta('Preview URL')}: ${colors.cyan(previewUrl)}`);
|
|
138
|
+
case 'pending':
|
|
139
|
+
return wait
|
|
140
|
+
? spinner.start(`${colors.yellow(`Pending ${buildType}`)}`)
|
|
141
|
+
: process.stdout.write(`Status: ${colors.yellow(`Pending ${buildType}`)}\n`);
|
|
142
|
+
case 'skipped':
|
|
143
|
+
spinner.stop();
|
|
144
|
+
return process.stdout.write(`${colors.yellow(`Skipped ${buildType}`)}\n`);
|
|
145
|
+
case 'running':
|
|
146
|
+
return wait
|
|
147
|
+
? spinner.start(`${colors.yellow(`Running ${buildType}`)}`)
|
|
148
|
+
: process.stdout.write(`Status: ${colors.yellow(`Running ${buildType}`)}\n`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Config } from '@redocly/openapi-core';
|
|
2
|
+
export type PushOptions = {
|
|
3
|
+
apis?: string[];
|
|
4
|
+
organization?: string;
|
|
5
|
+
project: string;
|
|
6
|
+
'mount-path': string;
|
|
7
|
+
branch: string;
|
|
8
|
+
author: string;
|
|
9
|
+
message: string;
|
|
10
|
+
'commit-sha'?: string;
|
|
11
|
+
'commit-url'?: string;
|
|
12
|
+
namespace?: string;
|
|
13
|
+
repository?: string;
|
|
14
|
+
'created-at'?: string;
|
|
15
|
+
files: string[];
|
|
16
|
+
'default-branch': string;
|
|
17
|
+
domain?: string;
|
|
18
|
+
config?: string;
|
|
19
|
+
'wait-for-deployment'?: boolean;
|
|
20
|
+
'max-execution-time': number;
|
|
21
|
+
verbose?: boolean;
|
|
22
|
+
};
|
|
23
|
+
export declare function handlePush(argv: PushOptions, config: Config): Promise<void>;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.handlePush = void 0;
|
|
13
|
+
const fs = require("fs");
|
|
14
|
+
const path = require("path");
|
|
15
|
+
const openapi_core_1 = require("@redocly/openapi-core");
|
|
16
|
+
const miscellaneous_1 = require("../../utils/miscellaneous");
|
|
17
|
+
const colorette_1 = require("colorette");
|
|
18
|
+
const pluralize = require("pluralize");
|
|
19
|
+
const push_status_1 = require("./push-status");
|
|
20
|
+
const api_1 = require("../api");
|
|
21
|
+
function handlePush(argv, config) {
|
|
22
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
const startedAt = performance.now();
|
|
24
|
+
const { organization, project: projectId, 'mount-path': mountPath, verbose } = argv;
|
|
25
|
+
const orgId = organization || config.organization;
|
|
26
|
+
if (!argv.message || !argv.author || !argv.branch) {
|
|
27
|
+
(0, miscellaneous_1.exitWithError)('Error: message, author and branch are required for push to the CMS');
|
|
28
|
+
}
|
|
29
|
+
if (!orgId) {
|
|
30
|
+
return (0, miscellaneous_1.exitWithError)(`No organization provided, please use --organization option or specify the 'organization' field in the config file.`);
|
|
31
|
+
}
|
|
32
|
+
const domain = argv.domain || (0, api_1.getDomain)();
|
|
33
|
+
if (!domain) {
|
|
34
|
+
return (0, miscellaneous_1.exitWithError)(`No domain provided, please use --domain option or environment variable REDOCLY_AUTHORIZATION.`);
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const { 'commit-sha': commitSha, 'commit-url': commitUrl, 'default-branch': defaultBranch, 'wait-for-deployment': waitForDeployment, 'max-execution-time': maxExecutionTime, } = argv;
|
|
38
|
+
const author = parseCommitAuthor(argv.author);
|
|
39
|
+
const apiKey = (0, api_1.getApiKeys)(domain);
|
|
40
|
+
const filesToUpload = collectFilesToPush(argv.files || argv.apis);
|
|
41
|
+
if (!filesToUpload.length) {
|
|
42
|
+
return (0, miscellaneous_1.printExecutionTime)('push', startedAt, `No files to upload`);
|
|
43
|
+
}
|
|
44
|
+
const client = new api_1.ReuniteApiClient(domain, apiKey);
|
|
45
|
+
const projectDefaultBranch = yield client.remotes.getDefaultBranch(orgId, projectId);
|
|
46
|
+
const remote = yield client.remotes.upsert(orgId, projectId, {
|
|
47
|
+
mountBranchName: projectDefaultBranch,
|
|
48
|
+
mountPath,
|
|
49
|
+
});
|
|
50
|
+
process.stderr.write(`Uploading to ${remote.mountPath} ${filesToUpload.length} ${pluralize('file', filesToUpload.length)}:\n`);
|
|
51
|
+
const { id } = yield client.remotes.push(orgId, projectId, {
|
|
52
|
+
remoteId: remote.id,
|
|
53
|
+
commit: {
|
|
54
|
+
message: argv.message,
|
|
55
|
+
branchName: argv.branch,
|
|
56
|
+
sha: commitSha,
|
|
57
|
+
url: commitUrl,
|
|
58
|
+
createdAt: argv['created-at'],
|
|
59
|
+
namespace: argv.namespace,
|
|
60
|
+
repository: argv.repository,
|
|
61
|
+
author,
|
|
62
|
+
},
|
|
63
|
+
isMainBranch: defaultBranch === argv.branch,
|
|
64
|
+
}, filesToUpload.map((f) => ({ path: (0, openapi_core_1.slash)(f.name), stream: fs.createReadStream(f.path) })));
|
|
65
|
+
filesToUpload.forEach((f) => {
|
|
66
|
+
process.stderr.write((0, colorette_1.green)(`✓ ${f.name}\n`));
|
|
67
|
+
});
|
|
68
|
+
process.stdout.write('\n');
|
|
69
|
+
process.stdout.write(`Push ID: ${id}\n`);
|
|
70
|
+
if (waitForDeployment) {
|
|
71
|
+
process.stdout.write('\n');
|
|
72
|
+
yield (0, push_status_1.handlePushStatus)({
|
|
73
|
+
organization: orgId,
|
|
74
|
+
project: projectId,
|
|
75
|
+
pushId: id,
|
|
76
|
+
wait: true,
|
|
77
|
+
domain,
|
|
78
|
+
'max-execution-time': maxExecutionTime,
|
|
79
|
+
}, config);
|
|
80
|
+
}
|
|
81
|
+
verbose &&
|
|
82
|
+
(0, miscellaneous_1.printExecutionTime)('push', startedAt, `${pluralize('file', filesToUpload.length)} uploaded to organization ${orgId}, project ${projectId}. Push ID: ${id}.`);
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
const message = err instanceof miscellaneous_1.HandledError ? '' : `✗ File upload failed. Reason: ${err.message}`;
|
|
86
|
+
(0, miscellaneous_1.exitWithError)(message);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
exports.handlePush = handlePush;
|
|
91
|
+
function parseCommitAuthor(author) {
|
|
92
|
+
// Author Name <author@email.com>
|
|
93
|
+
const reg = /^.+\s<[^<>]+>$/;
|
|
94
|
+
if (!reg.test(author)) {
|
|
95
|
+
throw new Error('Invalid author format. Use "Author Name <author@email.com>"');
|
|
96
|
+
}
|
|
97
|
+
const [name, email] = author.split('<');
|
|
98
|
+
return {
|
|
99
|
+
name: name.trim(),
|
|
100
|
+
email: email.replace('>', '').trim(),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function collectFilesToPush(files) {
|
|
104
|
+
const collectedFiles = {};
|
|
105
|
+
for (const file of files) {
|
|
106
|
+
if (fs.statSync(file).isDirectory()) {
|
|
107
|
+
const dir = file;
|
|
108
|
+
const fileList = getFilesList(dir, []);
|
|
109
|
+
fileList.forEach((f) => addFile(f, dir));
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
addFile(file, path.dirname(file));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function addFile(filePath, fileDir) {
|
|
116
|
+
const fileName = path.relative(fileDir, filePath);
|
|
117
|
+
if (collectedFiles[fileName]) {
|
|
118
|
+
process.stdout.write((0, colorette_1.yellow)(`File ${collectedFiles[fileName]} is overwritten by ${filePath}\n`));
|
|
119
|
+
}
|
|
120
|
+
collectedFiles[fileName] = filePath;
|
|
121
|
+
}
|
|
122
|
+
return Object.entries(collectedFiles).map(([name, filePath]) => getFileEntry(name, filePath));
|
|
123
|
+
}
|
|
124
|
+
function getFileEntry(name, filePath) {
|
|
125
|
+
return {
|
|
126
|
+
name,
|
|
127
|
+
path: path.resolve(filePath),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function getFilesList(dir, files) {
|
|
131
|
+
const filesAndDirs = fs.readdirSync(dir);
|
|
132
|
+
for (const name of filesAndDirs) {
|
|
133
|
+
const currentPath = path.join(dir, name);
|
|
134
|
+
if (fs.statSync(currentPath).isDirectory()) {
|
|
135
|
+
files = getFilesList(currentPath, files);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
files.push(currentPath);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return files;
|
|
142
|
+
}
|
package/lib/cms/utils.js
ADDED
|
@@ -16,12 +16,12 @@ const fs_1 = require("fs");
|
|
|
16
16
|
const perf_hooks_1 = require("perf_hooks");
|
|
17
17
|
const utils_1 = require("./utils");
|
|
18
18
|
const openapi_core_1 = require("@redocly/openapi-core");
|
|
19
|
-
const
|
|
19
|
+
const miscellaneous_1 = require("../../utils/miscellaneous");
|
|
20
20
|
const handlerBuildCommand = (argv, configFromFile) => __awaiter(void 0, void 0, void 0, function* () {
|
|
21
21
|
var _a;
|
|
22
22
|
const startedAt = perf_hooks_1.performance.now();
|
|
23
23
|
const config = (0, openapi_core_1.getMergedConfig)(configFromFile, argv.api);
|
|
24
|
-
const apis = yield (0,
|
|
24
|
+
const apis = yield (0, miscellaneous_1.getFallbackApisOrExit)(argv.api ? [argv.api] : [], config);
|
|
25
25
|
const { path: pathToApi } = apis[0];
|
|
26
26
|
const options = {
|
|
27
27
|
output: argv.o,
|
|
@@ -33,7 +33,7 @@ const handlerBuildCommand = (argv, configFromFile) => __awaiter(void 0, void 0,
|
|
|
33
33
|
};
|
|
34
34
|
const redocCurrentVersion = require('../../../package.json').dependencies.redoc.substring(1); // remove ~
|
|
35
35
|
try {
|
|
36
|
-
const elapsed = (0,
|
|
36
|
+
const elapsed = (0, miscellaneous_1.getExecutionTime)(startedAt);
|
|
37
37
|
const api = yield (0, redoc_1.loadAndBundleSpec)((0, openapi_core_1.isAbsoluteUrl)(pathToApi) ? pathToApi : (0, path_1.resolve)(pathToApi));
|
|
38
38
|
const pageHTML = yield (0, utils_1.getPageHTML)(api, pathToApi, Object.assign(Object.assign({}, options), { redocCurrentVersion }), argv.config);
|
|
39
39
|
(0, fs_1.mkdirSync)((0, path_1.dirname)(options.output), { recursive: true });
|
|
@@ -42,7 +42,7 @@ const handlerBuildCommand = (argv, configFromFile) => __awaiter(void 0, void 0,
|
|
|
42
42
|
process.stdout.write(`\n🎉 bundled successfully in: ${options.output} (${sizeInKiB} KiB) [⏱ ${elapsed}].\n`);
|
|
43
43
|
}
|
|
44
44
|
catch (e) {
|
|
45
|
-
(0,
|
|
45
|
+
(0, miscellaneous_1.exitWithError)(e);
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
48
|
exports.handlerBuildCommand = handlerBuildCommand;
|
|
@@ -19,7 +19,7 @@ const handlebars_1 = require("handlebars");
|
|
|
19
19
|
const path_1 = require("path");
|
|
20
20
|
const fs_1 = require("fs");
|
|
21
21
|
const colorette_1 = require("colorette");
|
|
22
|
-
const
|
|
22
|
+
const miscellaneous_1 = require("../../utils/miscellaneous");
|
|
23
23
|
function getObjectOrJSON(openapiOptions, config) {
|
|
24
24
|
switch (typeof openapiOptions) {
|
|
25
25
|
case 'object':
|
|
@@ -35,7 +35,7 @@ function getObjectOrJSON(openapiOptions, config) {
|
|
|
35
35
|
}
|
|
36
36
|
catch (e) {
|
|
37
37
|
process.stderr.write((0, colorette_1.red)(`Encountered error:\n\n${openapiOptions}\n\nis neither a file with a valid JSON object neither a stringified JSON object.`));
|
|
38
|
-
(0,
|
|
38
|
+
(0, miscellaneous_1.exitWithError)(e);
|
|
39
39
|
}
|
|
40
40
|
break;
|
|
41
41
|
default: {
|