@uxf/scripts 1.5.1 → 1.5.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/package.json +1 -1
- package/src/Sitemap.js +19 -18
- package/src/Slack.js +5 -3
- package/src/uxf-sitemap-check/cli.js +6 -1
- package/src/uxf-sitemap-check/index.js +305 -53
package/package.json
CHANGED
package/src/Sitemap.js
CHANGED
|
@@ -4,31 +4,32 @@ const cheerio = require("cheerio");
|
|
|
4
4
|
const { HTTP_USERNAME, HTTP_PASSWORD } = process.env;
|
|
5
5
|
|
|
6
6
|
async function getSitemap(xml) {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const { data } = await axios.get(xml);
|
|
8
|
+
const $ = cheerio.load(data, { xmlMode: true });
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
const urls = [];
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
$("loc").each(function () {
|
|
13
|
+
urls.push($(this).text());
|
|
14
|
+
});
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
return urls;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
const axios = create({
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
auth:
|
|
21
|
+
HTTP_PASSWORD && HTTP_USERNAME
|
|
22
|
+
? {
|
|
23
|
+
username: HTTP_USERNAME,
|
|
24
|
+
password: HTTP_PASSWORD,
|
|
25
|
+
}
|
|
26
|
+
: undefined,
|
|
27
|
+
withCredentials: true,
|
|
28
|
+
maxRedirects: 0,
|
|
29
|
+
timeout: 20000,
|
|
29
30
|
});
|
|
30
31
|
|
|
31
32
|
module.exports = {
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
getSitemap,
|
|
34
|
+
axios,
|
|
34
35
|
};
|
package/src/Slack.js
CHANGED
|
@@ -15,12 +15,14 @@ const axios = create({
|
|
|
15
15
|
async function chatPostMessage(channel, data, dryRun = false) {
|
|
16
16
|
if (env.SLACK_TOKEN && !dryRun) {
|
|
17
17
|
const res = await axios.post("/chat.postMessage", { ...data, channel });
|
|
18
|
-
if (res.data.ok
|
|
18
|
+
if (res.data.ok === false) {
|
|
19
19
|
process.stdout.write("SLACK: chat.postMessage error - " + JSON.stringify(res.data));
|
|
20
|
+
return;
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
process.stdout.write("SLACK: chat.postMessage - done\n");
|
|
23
|
+
return;
|
|
23
24
|
}
|
|
25
|
+
process.stdout.write("SLACK: chat.postMessage - skipped\n");
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
module.exports = {
|
|
@@ -44,6 +44,11 @@ Environment variables:
|
|
|
44
44
|
type: "string",
|
|
45
45
|
group: "Options",
|
|
46
46
|
})
|
|
47
|
+
.option("test-nested", {
|
|
48
|
+
describe: "If nested urls should be tested.",
|
|
49
|
+
type: "boolean",
|
|
50
|
+
group: "Options",
|
|
51
|
+
})
|
|
47
52
|
.option("h", { alias: "help", group: "Options" })
|
|
48
53
|
.strict(false)
|
|
49
54
|
.exitProcess(false);
|
|
@@ -62,7 +67,7 @@ Environment variables:
|
|
|
62
67
|
env.HTTP_PASSWORD = options["http-password"];
|
|
63
68
|
}
|
|
64
69
|
|
|
65
|
-
await require("./index")(url, webUrl, options["slack-channel"], skip);
|
|
70
|
+
await require("./index")(url, webUrl, options["slack-channel"], skip, options["test-nested"]);
|
|
66
71
|
} catch (e) {
|
|
67
72
|
console.error(e);
|
|
68
73
|
return 1;
|
|
@@ -2,99 +2,351 @@ const Slack = require("../Slack");
|
|
|
2
2
|
const Sitemap = require("../Sitemap");
|
|
3
3
|
const { performance } = require("perf_hooks");
|
|
4
4
|
const { env, stdout } = require("process");
|
|
5
|
+
const { axios } = require("../Sitemap");
|
|
6
|
+
const cheerio = require("cheerio");
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
* @typedef {{parentUrl: (string | undefined), isImg: boolean, time: number, ttl: number, url: string, status: number, message: (string | undefined)}} UrlCheckResponse
|
|
11
|
+
*/
|
|
5
12
|
|
|
6
13
|
const MAX_TTL = 3;
|
|
14
|
+
const TESTED_URLS = [];
|
|
15
|
+
const IMAGES_LABEL = "🏞 Images:";
|
|
16
|
+
const URLS_LABEL = "🔗 Links:";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @param length {number}
|
|
21
|
+
* @return {string}
|
|
22
|
+
*/
|
|
23
|
+
function createTabSpace(length = 1) {
|
|
24
|
+
return "".padStart(2 * length);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
*
|
|
29
|
+
* @param url {string}
|
|
30
|
+
* @return {boolean}
|
|
31
|
+
*/
|
|
32
|
+
function isImageUrl(url) {
|
|
33
|
+
return new RegExp("[a-z].*(\\.jpg|\\.png|\\.webp|\\.avif|\\.gif)$", "gim").test(url);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
*
|
|
38
|
+
* @param errors {UrlCheckResponse[]}
|
|
39
|
+
* @return {string}
|
|
40
|
+
*/
|
|
41
|
+
function createErrorList(errors) {
|
|
42
|
+
return errors.map(err => `${createTabSpace(3)}${err.url}${createTabSpace()}${err.status}${err.message ? ` – ${err.message}` : ""}`).join("\n");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
*
|
|
47
|
+
* @param errors {UrlCheckResponse[]}
|
|
48
|
+
* @return {string}
|
|
49
|
+
*/
|
|
50
|
+
function createErrorResult(errors) {
|
|
51
|
+
let parentPages = "";
|
|
52
|
+
let nestedPages = "";
|
|
53
|
+
|
|
54
|
+
const parentPagesErrors = errors.filter(url => url.parentUrl === undefined);
|
|
55
|
+
if (parentPagesErrors.length > 0) {
|
|
56
|
+
parentPages = `${createTabSpace()}Pages from sitemap:\n${createErrorList(parentPagesErrors)}\n`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const nestedPagesErrors = errors
|
|
60
|
+
.filter(url => url.parentUrl !== undefined)
|
|
61
|
+
.sort((prev, curr) => prev.parentUrl.localeCompare(curr.parentUrl));
|
|
62
|
+
for (let i = 0; i < nestedPagesErrors.length; i++) {
|
|
63
|
+
if (i === 0) {
|
|
64
|
+
nestedPages = `${createTabSpace()}Nested pages:\n`;
|
|
65
|
+
nestedPages += `${createTabSpace(1)}Page: ${nestedPagesErrors[i].parentUrl}\n`;
|
|
66
|
+
} else {
|
|
67
|
+
if (nestedPagesErrors[i].parentUrl === nestedPagesErrors[i - 1].parentUrl) {
|
|
68
|
+
continue;
|
|
69
|
+
} else {
|
|
70
|
+
nestedPages += `${createTabSpace(1)}Page: ${nestedPagesErrors[i].parentUrl}\n`;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const images = nestedPagesErrors.filter(err => err.parentUrl === nestedPagesErrors[i].parentUrl && err.isImg);
|
|
74
|
+
const links = nestedPagesErrors.filter(err => err.parentUrl === nestedPagesErrors[i].parentUrl && !err.isImg);
|
|
75
|
+
if (images.length > 0) {
|
|
76
|
+
nestedPages += `${createTabSpace(2)}${IMAGES_LABEL}\n${createErrorList(images)}\n`;
|
|
77
|
+
}
|
|
78
|
+
if (links.length > 0) {
|
|
79
|
+
nestedPages += `${createTabSpace(2)}${URLS_LABEL}\n${createErrorList(links)}\n`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return parentPages + nestedPages;
|
|
84
|
+
}
|
|
7
85
|
|
|
8
|
-
|
|
86
|
+
/**
|
|
87
|
+
*
|
|
88
|
+
* @param incorrectLinks {string[]}
|
|
89
|
+
* @param webUrl {string}
|
|
90
|
+
* @return {string[]}
|
|
91
|
+
*/
|
|
92
|
+
function createCorrectLinks(incorrectLinks, webUrl) {
|
|
93
|
+
return [
|
|
94
|
+
...new Set(
|
|
95
|
+
incorrectLinks
|
|
96
|
+
.filter((i, url) => url && new RegExp("^(\\/|http)").test(url))
|
|
97
|
+
.map((i, url) => (url.startsWith("http") ? url : webUrl + url)),
|
|
98
|
+
),
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
*
|
|
104
|
+
* @param url {string}
|
|
105
|
+
* @param parentUrl {string | undefined}
|
|
106
|
+
* @param ttl {number}
|
|
107
|
+
* @return {Promise<UrlCheckResponse>}
|
|
108
|
+
*/
|
|
109
|
+
async function fetchUrl(url, parentUrl = undefined, ttl = 1) {
|
|
9
110
|
try {
|
|
111
|
+
Sitemap.axios.defaults.maxRedirects = parentUrl ? 1 : 0;
|
|
112
|
+
|
|
10
113
|
const t0 = performance.now();
|
|
11
114
|
const { status } = await Sitemap.axios.get(url);
|
|
12
115
|
const t1 = performance.now();
|
|
13
116
|
|
|
14
|
-
if (status !== 200) {
|
|
15
|
-
|
|
117
|
+
if (status !== 200 && ttl < MAX_TTL) {
|
|
118
|
+
return await fetchUrl(url, parentUrl, ttl + 1);
|
|
16
119
|
}
|
|
17
120
|
|
|
18
121
|
return {
|
|
19
122
|
url,
|
|
20
|
-
|
|
123
|
+
parentUrl,
|
|
124
|
+
isImg: isImageUrl(url),
|
|
21
125
|
ttl,
|
|
126
|
+
status,
|
|
22
127
|
time: Math.ceil(t1 - t0),
|
|
23
128
|
};
|
|
24
129
|
} catch (e) {
|
|
25
|
-
if (ttl < MAX_TTL) {
|
|
26
|
-
return tryUrl(url, ttl + 1);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
130
|
return {
|
|
30
131
|
url,
|
|
132
|
+
parentUrl,
|
|
133
|
+
isImg: isImageUrl(url),
|
|
31
134
|
ttl,
|
|
32
|
-
status: Number.parseInt((e && e.response && e.response.status) ||
|
|
135
|
+
status: Number.parseInt((e && e.response && e.response.status) || -1, 10),
|
|
33
136
|
time: 0,
|
|
137
|
+
message: e.message,
|
|
34
138
|
};
|
|
35
139
|
}
|
|
36
140
|
}
|
|
37
141
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
142
|
+
/**
|
|
143
|
+
*
|
|
144
|
+
* @param url {string}
|
|
145
|
+
* @param parentUrl {string | undefined}
|
|
146
|
+
* @return {UrlCheckResponse}
|
|
147
|
+
*/
|
|
148
|
+
async function testUrl(url, parentUrl = undefined) {
|
|
149
|
+
const indexInChecked = TESTED_URLS.findIndex(result => result.url === url);
|
|
150
|
+
if (indexInChecked === -1) {
|
|
151
|
+
const result = await fetchUrl(url, parentUrl);
|
|
152
|
+
TESTED_URLS.push(result);
|
|
153
|
+
return result;
|
|
42
154
|
}
|
|
155
|
+
return TESTED_URLS[indexInChecked];
|
|
156
|
+
}
|
|
43
157
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
158
|
+
/**
|
|
159
|
+
*
|
|
160
|
+
* @param urls {string[]}
|
|
161
|
+
* @param webUrl {string}
|
|
162
|
+
* @param sitemapUrl {string}
|
|
163
|
+
* @param skip {number}
|
|
164
|
+
* @param testNested {boolean}
|
|
165
|
+
* @return {Promise<void>}
|
|
166
|
+
*/
|
|
167
|
+
async function testSitemapUrls(urls, webUrl, sitemapUrl, skip, testNested) {
|
|
168
|
+
for (let i = skip || 0; i < urls.length; i++) {
|
|
169
|
+
const url = urls[i];
|
|
170
|
+
const changedUrl = webUrl ? `${webUrl}${new URL(url).pathname}` : null;
|
|
171
|
+
|
|
172
|
+
printUrlInfo(changedUrl ?? url, i, urls.length);
|
|
173
|
+
printUrlResult(await testUrl(changedUrl ?? url));
|
|
174
|
+
|
|
175
|
+
if (testNested) {
|
|
176
|
+
await testAllNestedUrls(changedUrl ?? url, i, webUrl ?? sitemapUrl.split("/").slice(0, 3).join("/"));
|
|
177
|
+
}
|
|
47
178
|
}
|
|
179
|
+
}
|
|
48
180
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
181
|
+
/**
|
|
182
|
+
*
|
|
183
|
+
* @param parentUrl {string}
|
|
184
|
+
* @param parentIndex {number}
|
|
185
|
+
* @param webUrl {string}
|
|
186
|
+
* @return {Promise<void>}
|
|
187
|
+
*/
|
|
188
|
+
async function testAllNestedUrls(parentUrl, parentIndex, webUrl) {
|
|
189
|
+
const { data } = await axios.get(parentUrl);
|
|
190
|
+
const $ = cheerio.load(data);
|
|
191
|
+
const urls = createCorrectLinks(
|
|
192
|
+
$("a").map((i, node) => $(node).attr("href")),
|
|
193
|
+
webUrl,
|
|
194
|
+
);
|
|
195
|
+
const images = createCorrectLinks(
|
|
196
|
+
$("img").map((i, node) => $(node).attr("src")),
|
|
197
|
+
webUrl,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
await testNested(images, parentIndex, parentUrl, createTabSpace() + IMAGES_LABEL);
|
|
201
|
+
await testNested(urls, parentIndex, parentUrl, createTabSpace() + URLS_LABEL);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
*
|
|
206
|
+
* @param urls {string[]}
|
|
207
|
+
* @param parentIndex {number}
|
|
208
|
+
* @param parentUrl {string}
|
|
209
|
+
* @param label {string}
|
|
210
|
+
* @return {Promise<void>}
|
|
211
|
+
*/
|
|
212
|
+
async function testNested(urls, parentIndex, parentUrl, label) {
|
|
213
|
+
if (urls.length === 0) {
|
|
214
|
+
return;
|
|
52
215
|
}
|
|
53
216
|
|
|
54
|
-
|
|
217
|
+
stdout.write(label + "\n");
|
|
218
|
+
for (let i = 0; i < urls.length; i++) {
|
|
219
|
+
if (TESTED_URLS.findIndex(result => result.url === urls[i]) !== -1) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
printUrlInfo(urls[i], i, urls.length, `${createTabSpace(2)}(${parentIndex + 1}) `);
|
|
223
|
+
const result = await testUrl(urls[i], parentUrl);
|
|
224
|
+
printUrlResult(result);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
55
227
|
|
|
56
|
-
|
|
228
|
+
/**
|
|
229
|
+
*
|
|
230
|
+
* @param url {string}
|
|
231
|
+
* @param urlIndex {number}
|
|
232
|
+
* @param allUrlsCount {number}
|
|
233
|
+
* @param prefix {string}
|
|
234
|
+
*/
|
|
235
|
+
function printUrlInfo(url, urlIndex, allUrlsCount, prefix = "") {
|
|
236
|
+
stdout.write(`${prefix}${urlIndex + 1} / ${allUrlsCount}${createTabSpace()}${url}`);
|
|
237
|
+
}
|
|
57
238
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
239
|
+
/**
|
|
240
|
+
*
|
|
241
|
+
* @param result {UrlCheckResponse}
|
|
242
|
+
*/
|
|
243
|
+
function printUrlResult(result) {
|
|
244
|
+
const { ttl, status, time, message } = result;
|
|
245
|
+
stdout.write(`${createTabSpace()}${status}${message ? " – " + message : ""} (${time}ms) ttl=${ttl} ${status === 200 ? "✅ " : "❌ "}\n`);
|
|
246
|
+
}
|
|
61
247
|
|
|
62
|
-
|
|
248
|
+
/**
|
|
249
|
+
*
|
|
250
|
+
* @param errorText {string}
|
|
251
|
+
*/
|
|
252
|
+
function logErrors(errorText) {
|
|
253
|
+
stdout.write("\nErrors:\n");
|
|
254
|
+
stdout.write(`${errorText}\n\n`);
|
|
255
|
+
}
|
|
63
256
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
257
|
+
/**
|
|
258
|
+
*
|
|
259
|
+
* @param millis {number}
|
|
260
|
+
* @return {string}
|
|
261
|
+
*/
|
|
262
|
+
function convertTime(millis) {
|
|
263
|
+
let minutes = Math.floor(millis / 60000);
|
|
264
|
+
let seconds = ((millis % 60000) / 1000).toFixed(0);
|
|
265
|
+
return `${minutes} min ${seconds < 10 ? "0" : ""}${seconds} sec`;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
*
|
|
270
|
+
* @param okResults {UrlCheckResponse[]}
|
|
271
|
+
* @param time {number}
|
|
272
|
+
*/
|
|
273
|
+
function logStatistics(okResults, time) {
|
|
274
|
+
const avgTime = Math.round(okResults.reduce((prev, curr) => prev + curr.time, 0) / TESTED_URLS.length);
|
|
275
|
+
const maxTime = okResults.reduce((prev, curr) => (curr.time > prev.time ? curr : prev));
|
|
276
|
+
const minTime = okResults.reduce((prev, curr) => (curr.time < prev.time ? curr : prev));
|
|
277
|
+
|
|
278
|
+
stdout.write("\nSummary:\n");
|
|
279
|
+
stdout.write(createTabSpace() + `Time ${convertTime(time)}\n`);
|
|
280
|
+
stdout.write(
|
|
281
|
+
createTabSpace() + "Images tested:" + createTabSpace() + TESTED_URLS.filter(url => url.isImg).length + "\n",
|
|
282
|
+
);
|
|
283
|
+
stdout.write(
|
|
284
|
+
createTabSpace() + "Links tested:" + createTabSpace() + TESTED_URLS.filter(url => !url.isImg).length + "\n",
|
|
285
|
+
);
|
|
286
|
+
stdout.write(createTabSpace() + "Avg time:" + createTabSpace() + avgTime + "ms\n");
|
|
287
|
+
stdout.write(
|
|
288
|
+
createTabSpace() + "Min time:" + createTabSpace() + minTime.time + "ms" + createTabSpace() + minTime.url + "\n",
|
|
289
|
+
);
|
|
290
|
+
stdout.write(
|
|
291
|
+
createTabSpace() + "Max time" + createTabSpace() + maxTime.time + "ms" + createTabSpace() + maxTime.url + "\n",
|
|
292
|
+
);
|
|
293
|
+
}
|
|
67
294
|
|
|
68
|
-
|
|
295
|
+
/**
|
|
296
|
+
*
|
|
297
|
+
* @param errorText {string}
|
|
298
|
+
* @param slackChannel {string}
|
|
299
|
+
* @return {Promise<void>}
|
|
300
|
+
*/
|
|
301
|
+
async function sendSlackMessage(errorText, slackChannel) {
|
|
302
|
+
await Slack.chatPostMessage(slackChannel, {
|
|
303
|
+
text: ":warning: Odkazy uvedené v sitemap.xml nejsou dostupné",
|
|
304
|
+
attachments: [
|
|
305
|
+
{
|
|
306
|
+
text: errorText,
|
|
307
|
+
},
|
|
308
|
+
],
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
*
|
|
314
|
+
* @param sitemapUrl {string}
|
|
315
|
+
* @param webUrl {string}
|
|
316
|
+
* @param slackChannel {string}
|
|
317
|
+
* @param skip {number}
|
|
318
|
+
* @param testNested {boolean}
|
|
319
|
+
* @return {Promise<*>}
|
|
320
|
+
*/
|
|
321
|
+
module.exports = async function run(sitemapUrl, webUrl, slackChannel, skip, testNested) {
|
|
322
|
+
if (!sitemapUrl) {
|
|
323
|
+
stdout.write("⛔ Required parameter --url is empty.\n");
|
|
324
|
+
return process.exit(1);
|
|
69
325
|
}
|
|
70
326
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
327
|
+
if (slackChannel && !env.SLACK_TOKEN) {
|
|
328
|
+
stdout.write("⛔ Environment variable SLACK_TOKEN is empty.\n");
|
|
329
|
+
return process.exit(1);
|
|
330
|
+
}
|
|
75
331
|
|
|
76
|
-
if (
|
|
77
|
-
stdout.write(
|
|
78
|
-
stdout.write(
|
|
79
|
-
stdout.write("\n");
|
|
332
|
+
if (webUrl) {
|
|
333
|
+
stdout.write(`${createTabSpace()}Sitemap url: ${sitemapUrl}\n`);
|
|
334
|
+
stdout.write(`❗${createTabSpace()}Web url is defined: ${webUrl}\n\n`);
|
|
80
335
|
}
|
|
81
336
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
text: resultErrors.map(r => `${r.url} ${r.status}`).join("\n"),
|
|
94
|
-
},
|
|
95
|
-
],
|
|
96
|
-
});
|
|
337
|
+
const startTime = performance.now();
|
|
338
|
+
await testSitemapUrls(await Sitemap.getSitemap(sitemapUrl), webUrl, sitemapUrl, skip, testNested);
|
|
339
|
+
const finishTime = performance.now();
|
|
340
|
+
|
|
341
|
+
const errors = TESTED_URLS.filter(r => r.status !== 200);
|
|
342
|
+
const ok = TESTED_URLS.filter(r => r.status === 200);
|
|
343
|
+
|
|
344
|
+
if (errors.length > 0) {
|
|
345
|
+
const errorText = createErrorResult(errors);
|
|
346
|
+
logErrors(errorText);
|
|
347
|
+
await sendSlackMessage(errorText, slackChannel);
|
|
97
348
|
}
|
|
349
|
+
logStatistics(ok, Math.ceil(finishTime - startTime));
|
|
98
350
|
|
|
99
|
-
process.exit(
|
|
351
|
+
process.exit(errors.length > 0 ? 1 : 0);
|
|
100
352
|
};
|