@sitecore-jss/sitecore-jss-dev-tools 22.4.0-canary.14 → 22.4.0-canary.15
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/dist/cjs/package-deploy.js +166 -107
- package/dist/esm/package-deploy.js +164 -106
- package/package.json +3 -4
- package/types/package-deploy.d.ts +17 -21
|
@@ -17,17 +17,17 @@ exports.normalizeFingerprint = normalizeFingerprint;
|
|
|
17
17
|
exports.doFingerprintsMatch = doFingerprintsMatch;
|
|
18
18
|
exports.finishWatchJobStatusTask = finishWatchJobStatusTask;
|
|
19
19
|
exports.logJobStatus = logJobStatus;
|
|
20
|
+
exports.watchJobStatus = watchJobStatus;
|
|
20
21
|
exports.packageDeploy = packageDeploy;
|
|
21
|
-
exports.
|
|
22
|
-
exports.
|
|
22
|
+
exports.setProxy = setProxy;
|
|
23
|
+
exports.attachFormDataHandlers = attachFormDataHandlers;
|
|
23
24
|
const chalk_1 = __importDefault(require("chalk"));
|
|
24
25
|
const fs_1 = __importDefault(require("fs"));
|
|
25
26
|
const https_1 = __importDefault(require("https"));
|
|
27
|
+
const http_1 = __importDefault(require("http"));
|
|
26
28
|
const path_1 = __importDefault(require("path"));
|
|
27
29
|
const form_data_1 = __importDefault(require("form-data"));
|
|
28
30
|
const digest_1 = require("./digest");
|
|
29
|
-
const sitecore_jss_1 = require("@sitecore-jss/sitecore-jss");
|
|
30
|
-
const undici_1 = require("undici");
|
|
31
31
|
// Node does not use system level trusted CAs. This causes issues because SIF likes to install
|
|
32
32
|
// using a Windows trusted CA - so SSL connections to Sitecore will fail from Node.
|
|
33
33
|
// If the options.acceptCertificate is passed, we disable normal SSL validation and use this function
|
|
@@ -139,79 +139,102 @@ function watchJobStatus(options, taskName) {
|
|
|
139
139
|
const warnings = [];
|
|
140
140
|
const factors = [options.appName, taskName, `${options.importServiceUrl}/status`];
|
|
141
141
|
const mac = (0, digest_1.hmac)(factors, options.secret);
|
|
142
|
-
const
|
|
142
|
+
const url = new URL(`${options.importServiceUrl}/status?appName=${options.appName}&jobName=${taskName}&after=${logOffset}`);
|
|
143
|
+
const isHttps = options.importServiceUrl.startsWith('https');
|
|
144
|
+
const client = isHttps ? https_1.default : http_1.default;
|
|
145
|
+
const reqOptions = {
|
|
143
146
|
headers: {
|
|
144
147
|
'User-Agent': 'Sitecore/JSS-Import',
|
|
145
148
|
'Cache-Control': 'no-cache',
|
|
146
149
|
'X-JSS-Auth': mac,
|
|
147
150
|
},
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
151
|
+
method: 'GET',
|
|
152
|
+
hostname: url.hostname,
|
|
153
|
+
protocol: url.protocol,
|
|
154
|
+
path: url.pathname + url.search,
|
|
155
|
+
agent: isHttps
|
|
156
|
+
? new https_1.default.Agent({
|
|
157
|
+
// we turn off normal CA cert validation when we are whitelisting a single cert thumbprint
|
|
152
158
|
rejectUnauthorized: options.acceptCertificate ? false : true,
|
|
159
|
+
// needed to allow whitelisting a cert thumbprint if a connection is reused
|
|
153
160
|
maxCachedSessions: options.acceptCertificate ? 0 : undefined,
|
|
154
|
-
}
|
|
155
|
-
|
|
161
|
+
})
|
|
162
|
+
: undefined,
|
|
156
163
|
};
|
|
157
164
|
if (options.debugSecurity) {
|
|
158
165
|
console.log(`Deployment status security factors: ${factors}`);
|
|
159
166
|
console.log(`Deployment status HMAC: ${mac}`);
|
|
160
167
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
let message = entry;
|
|
177
|
-
if (entryBits && entryBits[2]) {
|
|
178
|
-
entryLevel = entryBits[2];
|
|
179
|
-
// 3 = '[] ' in say [INFO] My log message
|
|
180
|
-
// we're not using the capture group as the message might be multi-line
|
|
181
|
-
message = entry.substring(entryLevel.length + 3);
|
|
182
|
-
}
|
|
183
|
-
if (message.startsWith('[JSS] - ')) {
|
|
184
|
-
message = message.substring(8);
|
|
185
|
-
}
|
|
186
|
-
logJobStatus({ message, entryLevel, warnings, errors });
|
|
187
|
-
});
|
|
188
|
-
if (state === 'Finished') {
|
|
189
|
-
finishWatchJobStatusTask({ warnings, errors, resolve, reject });
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
setTimeout(sendJobStatusRequest, 1000);
|
|
193
|
-
}
|
|
194
|
-
catch (error) {
|
|
195
|
-
console.error(chalk_1.default.red(`Unexpected error processing reply from import status service: ${error}`));
|
|
196
|
-
console.error(chalk_1.default.red(`Response: ${body}`));
|
|
197
|
-
console.error(chalk_1.default.red('Consult the Sitecore logs for details.'));
|
|
198
|
-
reject(error);
|
|
199
|
-
}
|
|
200
|
-
})
|
|
201
|
-
.catch((error) => {
|
|
168
|
+
if (options.proxy) {
|
|
169
|
+
setProxy(reqOptions, options.proxy, options.importServiceUrl);
|
|
170
|
+
}
|
|
171
|
+
const req = client.request(reqOptions);
|
|
172
|
+
if (isHttps) {
|
|
173
|
+
applyCertPinning(req, options);
|
|
174
|
+
}
|
|
175
|
+
let responseData = '';
|
|
176
|
+
req.on('response', (res) => {
|
|
177
|
+
if (res.statusCode !== 200) {
|
|
178
|
+
let errorData = '';
|
|
179
|
+
res.on('data', (chunk) => {
|
|
180
|
+
errorData += chunk;
|
|
181
|
+
});
|
|
182
|
+
res.on('end', () => {
|
|
202
183
|
console.error(chalk_1.default.red('Unexpected response from import status service. The import task is probably still running; check the Sitecore logs for details.'));
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
else {
|
|
208
|
-
console.error(chalk_1.default.red(error.message));
|
|
209
|
-
}
|
|
210
|
-
reject();
|
|
184
|
+
console.error(chalk_1.default.red(`Status message: ${res.statusMessage}`));
|
|
185
|
+
console.error(chalk_1.default.red(`Status: ${res.statusCode}`));
|
|
186
|
+
process.exit(1);
|
|
211
187
|
});
|
|
188
|
+
return;
|
|
212
189
|
}
|
|
213
|
-
|
|
190
|
+
res.on('error', (err) => {
|
|
191
|
+
console.error(chalk_1.default.red('Unexpected response from import status service. The import task is probably still running; check the Sitecore logs for details.'));
|
|
192
|
+
console.error(chalk_1.default.red(`Status message: ${err.message}`));
|
|
193
|
+
console.error(chalk_1.default.red(`Status: ${res.statusCode}`));
|
|
194
|
+
process.exit(1);
|
|
195
|
+
});
|
|
196
|
+
res.on('data', (chunk) => {
|
|
197
|
+
responseData += chunk;
|
|
198
|
+
});
|
|
199
|
+
res.on('end', () => {
|
|
200
|
+
try {
|
|
201
|
+
const body = JSON.parse(responseData);
|
|
202
|
+
const { state, messages } = body;
|
|
203
|
+
messages.forEach((entry) => {
|
|
204
|
+
logOffset++;
|
|
205
|
+
const entryBits = /^(\[([A-Z]+)\] )?(.+)/.exec(entry);
|
|
206
|
+
let entryLevel = 'INFO';
|
|
207
|
+
let message = entry;
|
|
208
|
+
if (entryBits && entryBits[2]) {
|
|
209
|
+
entryLevel = entryBits[2];
|
|
210
|
+
// 3 = '[] ' in say [INFO] My log message
|
|
211
|
+
// we're not using the capture group as the message might be multi-line
|
|
212
|
+
message = entry.substring(entryLevel.length + 3);
|
|
213
|
+
}
|
|
214
|
+
if (message.startsWith('[JSS] - ')) {
|
|
215
|
+
message = message.substring(8);
|
|
216
|
+
}
|
|
217
|
+
logJobStatus({ message, entryLevel, warnings, errors });
|
|
218
|
+
});
|
|
219
|
+
if (state === 'Finished') {
|
|
220
|
+
finishWatchJobStatusTask({ warnings, errors, resolve: () => { }, reject: () => { } });
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
setTimeout(() => watchJobStatus(options, taskName), 1000);
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
console.error(chalk_1.default.red(`Unexpected error processing reply from import status service: ${error}`));
|
|
227
|
+
console.error(chalk_1.default.red(`Response: ${responseData}`));
|
|
228
|
+
console.error(chalk_1.default.red('Consult the Sitecore logs for details.'));
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
214
232
|
});
|
|
233
|
+
req.on('error', (err) => {
|
|
234
|
+
console.error(chalk_1.default.red(`Request error: ${err.message}`));
|
|
235
|
+
process.exit(1);
|
|
236
|
+
});
|
|
237
|
+
req.end();
|
|
215
238
|
});
|
|
216
239
|
}
|
|
217
240
|
/**
|
|
@@ -225,7 +248,7 @@ function packageDeploy(options) {
|
|
|
225
248
|
if (options.secret.length < 32) {
|
|
226
249
|
throw new Error('Deployment secret was too short. Use a RANDOM (not words or phrases) secret at least 32 characters long.');
|
|
227
250
|
}
|
|
228
|
-
let packageFile =
|
|
251
|
+
let packageFile = '';
|
|
229
252
|
fs_1.default.readdirSync(options.packagePath).forEach((file) => {
|
|
230
253
|
if (file.startsWith(options.appName) && file.endsWith('.manifest.zip')) {
|
|
231
254
|
packageFile = path_1.default.join(options.packagePath, file);
|
|
@@ -242,58 +265,83 @@ function packageDeploy(options) {
|
|
|
242
265
|
console.log(`Deployment HMAC: ${(0, digest_1.hmac)(factors, options.secret)}`);
|
|
243
266
|
}
|
|
244
267
|
const formData = new form_data_1.default();
|
|
245
|
-
|
|
268
|
+
const fileStream = fs_1.default.createReadStream(packageFile);
|
|
269
|
+
const url = new URL(options.importServiceUrl);
|
|
246
270
|
formData.append('appName', options.appName);
|
|
247
|
-
|
|
271
|
+
formData.append('path', fileStream);
|
|
272
|
+
const isHttps = options.importServiceUrl.startsWith('https');
|
|
273
|
+
const client = isHttps ? https_1.default : http_1.default;
|
|
274
|
+
const reqOptions = {
|
|
248
275
|
headers: Object.assign({ 'User-Agent': 'Sitecore/JSS-Import', 'Cache-Control': 'no-cache', 'X-JSS-Auth': (0, digest_1.hmac)(factors, options.secret) }, formData.getHeaders()),
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
276
|
+
method: 'POST',
|
|
277
|
+
hostname: url.hostname,
|
|
278
|
+
protocol: url.protocol,
|
|
279
|
+
path: url.pathname,
|
|
280
|
+
agent: isHttps
|
|
281
|
+
? new https_1.default.Agent({
|
|
253
282
|
// we turn off normal CA cert validation when we are whitelisting a single cert thumbprint
|
|
254
283
|
rejectUnauthorized: options.acceptCertificate ? false : true,
|
|
255
284
|
// needed to allow whitelisting a cert thumbprint if a connection is reused
|
|
256
285
|
maxCachedSessions: options.acceptCertificate ? 0 : undefined,
|
|
257
|
-
}
|
|
258
|
-
|
|
286
|
+
})
|
|
287
|
+
: undefined,
|
|
259
288
|
};
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
console.error(chalk_1.default.red(
|
|
277
|
-
|
|
278
|
-
|
|
289
|
+
if (options.proxy) {
|
|
290
|
+
setProxy(reqOptions, options.proxy, options.importServiceUrl);
|
|
291
|
+
}
|
|
292
|
+
const req = client.request(reqOptions);
|
|
293
|
+
if (https_1.default) {
|
|
294
|
+
applyCertPinning(req, options);
|
|
295
|
+
}
|
|
296
|
+
attachFormDataHandlers(req, formData);
|
|
297
|
+
formData.pipe(req);
|
|
298
|
+
req.on('response', (res) => {
|
|
299
|
+
if (res.statusCode !== 200) {
|
|
300
|
+
let errorData = '';
|
|
301
|
+
res.on('data', (chunk) => {
|
|
302
|
+
errorData += chunk;
|
|
303
|
+
});
|
|
304
|
+
res.on('end', () => {
|
|
305
|
+
console.error(chalk_1.default.red(`Error while uploading package: ${res.statusCode} ${res.statusMessage}`));
|
|
306
|
+
console.error(chalk_1.default.red(errorData));
|
|
307
|
+
process.exit(1);
|
|
308
|
+
});
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
let responseData = '';
|
|
312
|
+
res.on('error', (err) => {
|
|
313
|
+
console.error(chalk_1.default.red(`Response error when uploading package: ${err.message}`));
|
|
314
|
+
process.exit(1);
|
|
315
|
+
});
|
|
316
|
+
res.on('data', (chunk) => {
|
|
317
|
+
responseData += chunk;
|
|
279
318
|
});
|
|
280
|
-
|
|
319
|
+
res.on('end', () => {
|
|
320
|
+
const taskName = responseData;
|
|
321
|
+
console.log('we are in packageDeploy', responseData);
|
|
322
|
+
console.log(chalk_1.default.green(`Package uploaded. Import task name: ${taskName}`));
|
|
323
|
+
watchJobStatus(options, taskName);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
req.on('error', (err) => {
|
|
327
|
+
console.error(chalk_1.default.red(`Error while uploading package: ${err.message}`));
|
|
328
|
+
process.exit(1);
|
|
329
|
+
});
|
|
281
330
|
});
|
|
282
331
|
}
|
|
283
332
|
/**
|
|
284
333
|
* Creates valid proxy object
|
|
285
|
-
* @param {
|
|
334
|
+
* @param {RequestOptions} reqOptions
|
|
335
|
+
* @param {string} proxy proxy url
|
|
336
|
+
* @param {string} targetUrl target url
|
|
286
337
|
*/
|
|
287
|
-
function
|
|
288
|
-
if (!proxy)
|
|
289
|
-
return undefined;
|
|
338
|
+
function setProxy(reqOptions, proxy, targetUrl) {
|
|
290
339
|
try {
|
|
291
340
|
const proxyUrl = new URL(proxy);
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
};
|
|
341
|
+
reqOptions.hostname = proxyUrl.hostname;
|
|
342
|
+
reqOptions.port = proxyUrl.port || (proxyUrl.protocol === 'https:' ? '443' : '80');
|
|
343
|
+
reqOptions.protocol = proxyUrl.protocol;
|
|
344
|
+
reqOptions.path = targetUrl;
|
|
297
345
|
}
|
|
298
346
|
catch (error) {
|
|
299
347
|
console.error(chalk_1.default.red(`Invalid proxy url provided ${proxy}`));
|
|
@@ -301,13 +349,24 @@ function extractProxy(proxy) {
|
|
|
301
349
|
}
|
|
302
350
|
}
|
|
303
351
|
/**
|
|
304
|
-
*
|
|
305
|
-
* @param {
|
|
352
|
+
* Attach form data handlers to handle errors and close events
|
|
353
|
+
* @param {ClientRequest} req request object
|
|
354
|
+
* @param {FormData} formData FormData object
|
|
306
355
|
*/
|
|
307
|
-
function
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
356
|
+
function attachFormDataHandlers(req, formData) {
|
|
357
|
+
let ended = false;
|
|
358
|
+
let errored = false;
|
|
359
|
+
formData.on('end', () => {
|
|
360
|
+
ended = true;
|
|
361
|
+
});
|
|
362
|
+
formData.once('error', (err) => {
|
|
363
|
+
errored = true;
|
|
364
|
+
console.log('Error when uploading package:', err);
|
|
365
|
+
req.destroy(err);
|
|
366
|
+
});
|
|
367
|
+
formData.on('close', () => {
|
|
368
|
+
if (!ended && !errored) {
|
|
369
|
+
new Error('Request stream has been aborted');
|
|
370
|
+
}
|
|
371
|
+
});
|
|
313
372
|
}
|
|
@@ -10,11 +10,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import chalk from 'chalk';
|
|
11
11
|
import fs from 'fs';
|
|
12
12
|
import https from 'https';
|
|
13
|
+
import http from 'http';
|
|
13
14
|
import path from 'path';
|
|
14
15
|
import FormData from 'form-data';
|
|
15
16
|
import { digest, hmac } from './digest';
|
|
16
|
-
import { NativeDataFetcher } from '@sitecore-jss/sitecore-jss';
|
|
17
|
-
import { ProxyAgent } from 'undici';
|
|
18
17
|
// Node does not use system level trusted CAs. This causes issues because SIF likes to install
|
|
19
18
|
// using a Windows trusted CA - so SSL connections to Sitecore will fail from Node.
|
|
20
19
|
// If the options.acceptCertificate is passed, we disable normal SSL validation and use this function
|
|
@@ -119,86 +118,109 @@ export function logJobStatus({ message, entryLevel, warnings, errors, }) {
|
|
|
119
118
|
* @param {PackageDeployOptions} options
|
|
120
119
|
* @param {string} taskName
|
|
121
120
|
*/
|
|
122
|
-
function watchJobStatus(options, taskName) {
|
|
121
|
+
export function watchJobStatus(options, taskName) {
|
|
123
122
|
return __awaiter(this, void 0, void 0, function* () {
|
|
124
123
|
let logOffset = 0;
|
|
125
124
|
const errors = [];
|
|
126
125
|
const warnings = [];
|
|
127
126
|
const factors = [options.appName, taskName, `${options.importServiceUrl}/status`];
|
|
128
127
|
const mac = hmac(factors, options.secret);
|
|
129
|
-
const
|
|
128
|
+
const url = new URL(`${options.importServiceUrl}/status?appName=${options.appName}&jobName=${taskName}&after=${logOffset}`);
|
|
129
|
+
const isHttps = options.importServiceUrl.startsWith('https');
|
|
130
|
+
const client = isHttps ? https : http;
|
|
131
|
+
const reqOptions = {
|
|
130
132
|
headers: {
|
|
131
133
|
'User-Agent': 'Sitecore/JSS-Import',
|
|
132
134
|
'Cache-Control': 'no-cache',
|
|
133
135
|
'X-JSS-Auth': mac,
|
|
134
136
|
},
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
method: 'GET',
|
|
138
|
+
hostname: url.hostname,
|
|
139
|
+
protocol: url.protocol,
|
|
140
|
+
path: url.pathname + url.search,
|
|
141
|
+
agent: isHttps
|
|
142
|
+
? new https.Agent({
|
|
143
|
+
// we turn off normal CA cert validation when we are whitelisting a single cert thumbprint
|
|
139
144
|
rejectUnauthorized: options.acceptCertificate ? false : true,
|
|
145
|
+
// needed to allow whitelisting a cert thumbprint if a connection is reused
|
|
140
146
|
maxCachedSessions: options.acceptCertificate ? 0 : undefined,
|
|
141
|
-
}
|
|
142
|
-
|
|
147
|
+
})
|
|
148
|
+
: undefined,
|
|
143
149
|
};
|
|
144
150
|
if (options.debugSecurity) {
|
|
145
151
|
console.log(`Deployment status security factors: ${factors}`);
|
|
146
152
|
console.log(`Deployment status HMAC: ${mac}`);
|
|
147
153
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
let message = entry;
|
|
164
|
-
if (entryBits && entryBits[2]) {
|
|
165
|
-
entryLevel = entryBits[2];
|
|
166
|
-
// 3 = '[] ' in say [INFO] My log message
|
|
167
|
-
// we're not using the capture group as the message might be multi-line
|
|
168
|
-
message = entry.substring(entryLevel.length + 3);
|
|
169
|
-
}
|
|
170
|
-
if (message.startsWith('[JSS] - ')) {
|
|
171
|
-
message = message.substring(8);
|
|
172
|
-
}
|
|
173
|
-
logJobStatus({ message, entryLevel, warnings, errors });
|
|
174
|
-
});
|
|
175
|
-
if (state === 'Finished') {
|
|
176
|
-
finishWatchJobStatusTask({ warnings, errors, resolve, reject });
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
setTimeout(sendJobStatusRequest, 1000);
|
|
180
|
-
}
|
|
181
|
-
catch (error) {
|
|
182
|
-
console.error(chalk.red(`Unexpected error processing reply from import status service: ${error}`));
|
|
183
|
-
console.error(chalk.red(`Response: ${body}`));
|
|
184
|
-
console.error(chalk.red('Consult the Sitecore logs for details.'));
|
|
185
|
-
reject(error);
|
|
186
|
-
}
|
|
187
|
-
})
|
|
188
|
-
.catch((error) => {
|
|
154
|
+
if (options.proxy) {
|
|
155
|
+
setProxy(reqOptions, options.proxy, options.importServiceUrl);
|
|
156
|
+
}
|
|
157
|
+
const req = client.request(reqOptions);
|
|
158
|
+
if (isHttps) {
|
|
159
|
+
applyCertPinning(req, options);
|
|
160
|
+
}
|
|
161
|
+
let responseData = '';
|
|
162
|
+
req.on('response', (res) => {
|
|
163
|
+
if (res.statusCode !== 200) {
|
|
164
|
+
let errorData = '';
|
|
165
|
+
res.on('data', (chunk) => {
|
|
166
|
+
errorData += chunk;
|
|
167
|
+
});
|
|
168
|
+
res.on('end', () => {
|
|
189
169
|
console.error(chalk.red('Unexpected response from import status service. The import task is probably still running; check the Sitecore logs for details.'));
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
else {
|
|
195
|
-
console.error(chalk.red(error.message));
|
|
196
|
-
}
|
|
197
|
-
reject();
|
|
170
|
+
console.error(chalk.red(`Status message: ${res.statusMessage}`));
|
|
171
|
+
console.error(chalk.red(`Status: ${res.statusCode}`));
|
|
172
|
+
process.exit(1);
|
|
198
173
|
});
|
|
174
|
+
return;
|
|
199
175
|
}
|
|
200
|
-
|
|
176
|
+
res.on('error', (err) => {
|
|
177
|
+
console.error(chalk.red('Unexpected response from import status service. The import task is probably still running; check the Sitecore logs for details.'));
|
|
178
|
+
console.error(chalk.red(`Status message: ${err.message}`));
|
|
179
|
+
console.error(chalk.red(`Status: ${res.statusCode}`));
|
|
180
|
+
process.exit(1);
|
|
181
|
+
});
|
|
182
|
+
res.on('data', (chunk) => {
|
|
183
|
+
responseData += chunk;
|
|
184
|
+
});
|
|
185
|
+
res.on('end', () => {
|
|
186
|
+
try {
|
|
187
|
+
const body = JSON.parse(responseData);
|
|
188
|
+
const { state, messages } = body;
|
|
189
|
+
messages.forEach((entry) => {
|
|
190
|
+
logOffset++;
|
|
191
|
+
const entryBits = /^(\[([A-Z]+)\] )?(.+)/.exec(entry);
|
|
192
|
+
let entryLevel = 'INFO';
|
|
193
|
+
let message = entry;
|
|
194
|
+
if (entryBits && entryBits[2]) {
|
|
195
|
+
entryLevel = entryBits[2];
|
|
196
|
+
// 3 = '[] ' in say [INFO] My log message
|
|
197
|
+
// we're not using the capture group as the message might be multi-line
|
|
198
|
+
message = entry.substring(entryLevel.length + 3);
|
|
199
|
+
}
|
|
200
|
+
if (message.startsWith('[JSS] - ')) {
|
|
201
|
+
message = message.substring(8);
|
|
202
|
+
}
|
|
203
|
+
logJobStatus({ message, entryLevel, warnings, errors });
|
|
204
|
+
});
|
|
205
|
+
if (state === 'Finished') {
|
|
206
|
+
finishWatchJobStatusTask({ warnings, errors, resolve: () => { }, reject: () => { } });
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
setTimeout(() => watchJobStatus(options, taskName), 1000);
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
console.error(chalk.red(`Unexpected error processing reply from import status service: ${error}`));
|
|
213
|
+
console.error(chalk.red(`Response: ${responseData}`));
|
|
214
|
+
console.error(chalk.red('Consult the Sitecore logs for details.'));
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
201
218
|
});
|
|
219
|
+
req.on('error', (err) => {
|
|
220
|
+
console.error(chalk.red(`Request error: ${err.message}`));
|
|
221
|
+
process.exit(1);
|
|
222
|
+
});
|
|
223
|
+
req.end();
|
|
202
224
|
});
|
|
203
225
|
}
|
|
204
226
|
/**
|
|
@@ -212,7 +234,7 @@ export function packageDeploy(options) {
|
|
|
212
234
|
if (options.secret.length < 32) {
|
|
213
235
|
throw new Error('Deployment secret was too short. Use a RANDOM (not words or phrases) secret at least 32 characters long.');
|
|
214
236
|
}
|
|
215
|
-
let packageFile =
|
|
237
|
+
let packageFile = '';
|
|
216
238
|
fs.readdirSync(options.packagePath).forEach((file) => {
|
|
217
239
|
if (file.startsWith(options.appName) && file.endsWith('.manifest.zip')) {
|
|
218
240
|
packageFile = path.join(options.packagePath, file);
|
|
@@ -229,58 +251,83 @@ export function packageDeploy(options) {
|
|
|
229
251
|
console.log(`Deployment HMAC: ${hmac(factors, options.secret)}`);
|
|
230
252
|
}
|
|
231
253
|
const formData = new FormData();
|
|
232
|
-
|
|
254
|
+
const fileStream = fs.createReadStream(packageFile);
|
|
255
|
+
const url = new URL(options.importServiceUrl);
|
|
233
256
|
formData.append('appName', options.appName);
|
|
234
|
-
|
|
257
|
+
formData.append('path', fileStream);
|
|
258
|
+
const isHttps = options.importServiceUrl.startsWith('https');
|
|
259
|
+
const client = isHttps ? https : http;
|
|
260
|
+
const reqOptions = {
|
|
235
261
|
headers: Object.assign({ 'User-Agent': 'Sitecore/JSS-Import', 'Cache-Control': 'no-cache', 'X-JSS-Auth': hmac(factors, options.secret) }, formData.getHeaders()),
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
262
|
+
method: 'POST',
|
|
263
|
+
hostname: url.hostname,
|
|
264
|
+
protocol: url.protocol,
|
|
265
|
+
path: url.pathname,
|
|
266
|
+
agent: isHttps
|
|
267
|
+
? new https.Agent({
|
|
240
268
|
// we turn off normal CA cert validation when we are whitelisting a single cert thumbprint
|
|
241
269
|
rejectUnauthorized: options.acceptCertificate ? false : true,
|
|
242
270
|
// needed to allow whitelisting a cert thumbprint if a connection is reused
|
|
243
271
|
maxCachedSessions: options.acceptCertificate ? 0 : undefined,
|
|
244
|
-
}
|
|
245
|
-
|
|
272
|
+
})
|
|
273
|
+
: undefined,
|
|
246
274
|
};
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
console.error(chalk.red(
|
|
264
|
-
|
|
265
|
-
|
|
275
|
+
if (options.proxy) {
|
|
276
|
+
setProxy(reqOptions, options.proxy, options.importServiceUrl);
|
|
277
|
+
}
|
|
278
|
+
const req = client.request(reqOptions);
|
|
279
|
+
if (https) {
|
|
280
|
+
applyCertPinning(req, options);
|
|
281
|
+
}
|
|
282
|
+
attachFormDataHandlers(req, formData);
|
|
283
|
+
formData.pipe(req);
|
|
284
|
+
req.on('response', (res) => {
|
|
285
|
+
if (res.statusCode !== 200) {
|
|
286
|
+
let errorData = '';
|
|
287
|
+
res.on('data', (chunk) => {
|
|
288
|
+
errorData += chunk;
|
|
289
|
+
});
|
|
290
|
+
res.on('end', () => {
|
|
291
|
+
console.error(chalk.red(`Error while uploading package: ${res.statusCode} ${res.statusMessage}`));
|
|
292
|
+
console.error(chalk.red(errorData));
|
|
293
|
+
process.exit(1);
|
|
294
|
+
});
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
let responseData = '';
|
|
298
|
+
res.on('error', (err) => {
|
|
299
|
+
console.error(chalk.red(`Response error when uploading package: ${err.message}`));
|
|
300
|
+
process.exit(1);
|
|
301
|
+
});
|
|
302
|
+
res.on('data', (chunk) => {
|
|
303
|
+
responseData += chunk;
|
|
266
304
|
});
|
|
267
|
-
|
|
305
|
+
res.on('end', () => {
|
|
306
|
+
const taskName = responseData;
|
|
307
|
+
console.log('we are in packageDeploy', responseData);
|
|
308
|
+
console.log(chalk.green(`Package uploaded. Import task name: ${taskName}`));
|
|
309
|
+
watchJobStatus(options, taskName);
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
req.on('error', (err) => {
|
|
313
|
+
console.error(chalk.red(`Error while uploading package: ${err.message}`));
|
|
314
|
+
process.exit(1);
|
|
315
|
+
});
|
|
268
316
|
});
|
|
269
317
|
}
|
|
270
318
|
/**
|
|
271
319
|
* Creates valid proxy object
|
|
272
|
-
* @param {
|
|
320
|
+
* @param {RequestOptions} reqOptions
|
|
321
|
+
* @param {string} proxy proxy url
|
|
322
|
+
* @param {string} targetUrl target url
|
|
273
323
|
*/
|
|
274
|
-
export function
|
|
275
|
-
if (!proxy)
|
|
276
|
-
return undefined;
|
|
324
|
+
export function setProxy(reqOptions, proxy, targetUrl) {
|
|
277
325
|
try {
|
|
278
326
|
const proxyUrl = new URL(proxy);
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
};
|
|
327
|
+
reqOptions.hostname = proxyUrl.hostname;
|
|
328
|
+
reqOptions.port = proxyUrl.port || (proxyUrl.protocol === 'https:' ? '443' : '80');
|
|
329
|
+
reqOptions.protocol = proxyUrl.protocol;
|
|
330
|
+
reqOptions.path = targetUrl;
|
|
284
331
|
}
|
|
285
332
|
catch (error) {
|
|
286
333
|
console.error(chalk.red(`Invalid proxy url provided ${proxy}`));
|
|
@@ -288,13 +335,24 @@ export function extractProxy(proxy) {
|
|
|
288
335
|
}
|
|
289
336
|
}
|
|
290
337
|
/**
|
|
291
|
-
*
|
|
292
|
-
* @param {
|
|
338
|
+
* Attach form data handlers to handle errors and close events
|
|
339
|
+
* @param {ClientRequest} req request object
|
|
340
|
+
* @param {FormData} formData FormData object
|
|
293
341
|
*/
|
|
294
|
-
export function
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
342
|
+
export function attachFormDataHandlers(req, formData) {
|
|
343
|
+
let ended = false;
|
|
344
|
+
let errored = false;
|
|
345
|
+
formData.on('end', () => {
|
|
346
|
+
ended = true;
|
|
347
|
+
});
|
|
348
|
+
formData.once('error', (err) => {
|
|
349
|
+
errored = true;
|
|
350
|
+
console.log('Error when uploading package:', err);
|
|
351
|
+
req.destroy(err);
|
|
352
|
+
});
|
|
353
|
+
formData.on('close', () => {
|
|
354
|
+
if (!ended && !errored) {
|
|
355
|
+
new Error('Request stream has been aborted');
|
|
356
|
+
}
|
|
357
|
+
});
|
|
300
358
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sitecore-jss/sitecore-jss-dev-tools",
|
|
3
|
-
"version": "22.4.0-canary.
|
|
3
|
+
"version": "22.4.0-canary.15",
|
|
4
4
|
"description": "Utilities to assist in the development and deployment of Sitecore JSS apps.",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@babel/parser": "^7.24.0",
|
|
36
|
-
"@sitecore-jss/sitecore-jss": "^22.4.0-canary.
|
|
36
|
+
"@sitecore-jss/sitecore-jss": "^22.4.0-canary.15",
|
|
37
37
|
"chalk": "^4.1.2",
|
|
38
38
|
"chokidar": "^3.6.0",
|
|
39
39
|
"del": "^6.0.0",
|
|
@@ -50,7 +50,6 @@
|
|
|
50
50
|
"recast": "^0.23.5",
|
|
51
51
|
"resolve": "^1.22.1",
|
|
52
52
|
"ts-node": "^10.9.1",
|
|
53
|
-
"undici": "^7.2.1",
|
|
54
53
|
"url-join": "^4.0.1",
|
|
55
54
|
"uuid": "^9.0.0",
|
|
56
55
|
"yargs": "^17.6.2"
|
|
@@ -86,7 +85,7 @@
|
|
|
86
85
|
"typescript": "~5.6.3"
|
|
87
86
|
},
|
|
88
87
|
"types": "types/index.d.ts",
|
|
89
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "f1721addaf19dd4f711fbde765eee1051860916b",
|
|
90
89
|
"files": [
|
|
91
90
|
"dist",
|
|
92
91
|
"types",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { RequestOptions } from 'https';
|
|
2
|
+
import FormData from 'form-data';
|
|
3
|
+
import { ClientRequest } from 'http';
|
|
3
4
|
export interface PackageDeployOptions {
|
|
4
5
|
packagePath: string;
|
|
5
6
|
appName: string;
|
|
@@ -51,28 +52,23 @@ export declare function logJobStatus({ message, entryLevel, warnings, errors, }:
|
|
|
51
52
|
}): void;
|
|
52
53
|
/**
|
|
53
54
|
* @param {PackageDeployOptions} options
|
|
55
|
+
* @param {string} taskName
|
|
54
56
|
*/
|
|
55
|
-
export declare function
|
|
57
|
+
export declare function watchJobStatus(options: PackageDeployOptions, taskName: string): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* @param {PackageDeployOptions} options
|
|
60
|
+
*/
|
|
61
|
+
export declare function packageDeploy(options: PackageDeployOptions): Promise<void>;
|
|
56
62
|
/**
|
|
57
63
|
* Creates valid proxy object
|
|
58
|
-
* @param {
|
|
64
|
+
* @param {RequestOptions} reqOptions
|
|
65
|
+
* @param {string} proxy proxy url
|
|
66
|
+
* @param {string} targetUrl target url
|
|
59
67
|
*/
|
|
60
|
-
export declare function
|
|
61
|
-
protocol: string;
|
|
62
|
-
host: string;
|
|
63
|
-
port: number;
|
|
64
|
-
} | undefined;
|
|
68
|
+
export declare function setProxy(reqOptions: RequestOptions, proxy: string, targetUrl: string): void;
|
|
65
69
|
/**
|
|
66
|
-
*
|
|
67
|
-
* @param {
|
|
70
|
+
* Attach form data handlers to handle errors and close events
|
|
71
|
+
* @param {ClientRequest} req request object
|
|
72
|
+
* @param {FormData} formData FormData object
|
|
68
73
|
*/
|
|
69
|
-
export declare function
|
|
70
|
-
request: (reqOptions: https.RequestOptions, callback: (res: IncomingMessage) => void) => ClientRequest;
|
|
71
|
-
createServer<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof import("http").ServerResponse = typeof import("http").ServerResponse>(requestListener?: import("http").RequestListener<Request, Response>): https.Server<Request, Response>;
|
|
72
|
-
createServer<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof import("http").ServerResponse = typeof import("http").ServerResponse>(options: https.ServerOptions<Request, Response>, requestListener?: import("http").RequestListener<Request, Response>): https.Server<Request, Response>;
|
|
73
|
-
get(options: https.RequestOptions | string | import("url").URL, callback?: (res: IncomingMessage) => void): ClientRequest;
|
|
74
|
-
get(url: string | import("url").URL, options: https.RequestOptions, callback?: (res: IncomingMessage) => void): ClientRequest;
|
|
75
|
-
Agent: typeof https.Agent;
|
|
76
|
-
Server: typeof https.Server;
|
|
77
|
-
globalAgent: https.Agent;
|
|
78
|
-
};
|
|
74
|
+
export declare function attachFormDataHandlers(req: ClientRequest, formData: FormData): void;
|