@sap-ux/deploy-tooling 0.9.15 → 0.9.17
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/base/deploy.js +13 -0
- package/dist/base/validate.d.ts +52 -0
- package/dist/base/validate.js +289 -0
- package/package.json +6 -4
package/dist/base/deploy.js
CHANGED
|
@@ -15,6 +15,7 @@ const fs_1 = require("fs");
|
|
|
15
15
|
const config_1 = require("./config");
|
|
16
16
|
const prompt_1 = require("./prompt");
|
|
17
17
|
const system_access_1 = require("@sap-ux/system-access");
|
|
18
|
+
const validate_1 = require("./validate");
|
|
18
19
|
const deploymentCommands = { tryUndeploy, tryDeploy };
|
|
19
20
|
/**
|
|
20
21
|
* Handle exceptions thrown, in some cases we to retry them.
|
|
@@ -198,6 +199,7 @@ function runCommand(command, config, logger, archive = Buffer.from('')) {
|
|
|
198
199
|
* @param archive - archive file that is to be deployed
|
|
199
200
|
*/
|
|
200
201
|
function tryDeploy(provider, config, logger, archive) {
|
|
202
|
+
var _a, _b, _c, _d, _e;
|
|
201
203
|
return __awaiter(this, void 0, void 0, function* () {
|
|
202
204
|
try {
|
|
203
205
|
if (config.createTransport) {
|
|
@@ -205,6 +207,17 @@ function tryDeploy(provider, config, logger, archive) {
|
|
|
205
207
|
// Reset as we dont want other flows kicking it off again!
|
|
206
208
|
config.createTransport = false;
|
|
207
209
|
}
|
|
210
|
+
if (config.test === true) {
|
|
211
|
+
const validateOutput = yield (0, validate_1.validateBeforeDeploy)({
|
|
212
|
+
appName: config.app.name,
|
|
213
|
+
description: (_a = config.app.description) !== null && _a !== void 0 ? _a : '',
|
|
214
|
+
package: (_b = config.app.package) !== null && _b !== void 0 ? _b : '',
|
|
215
|
+
transport: (_c = config.app.transport) !== null && _c !== void 0 ? _c : '',
|
|
216
|
+
client: (_d = config.target.client) !== null && _d !== void 0 ? _d : '',
|
|
217
|
+
url: (_e = config.target.url) !== null && _e !== void 0 ? _e : ''
|
|
218
|
+
}, provider, logger);
|
|
219
|
+
logger.info((0, validate_1.formatSummary)(validateOutput.summary));
|
|
220
|
+
}
|
|
208
221
|
const service = getUi5AbapRepositoryService(provider, config, logger);
|
|
209
222
|
yield service.deploy({ archive, bsp: config.app, testMode: config.test, safeMode: config.safe });
|
|
210
223
|
if (config.test === true) {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { AbapServiceProvider } from '@sap-ux/axios-extension';
|
|
2
|
+
import type { Logger } from '@sap-ux/logger';
|
|
3
|
+
export type ValidationInputs = {
|
|
4
|
+
appName: string;
|
|
5
|
+
description: string;
|
|
6
|
+
package: string;
|
|
7
|
+
transport: string;
|
|
8
|
+
client: string;
|
|
9
|
+
url: string;
|
|
10
|
+
};
|
|
11
|
+
export type ValidationOutput = {
|
|
12
|
+
summary: SummaryRecord[];
|
|
13
|
+
result: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type SummaryRecord = {
|
|
16
|
+
message: string;
|
|
17
|
+
status: SummaryStatus;
|
|
18
|
+
};
|
|
19
|
+
export declare enum SummaryStatus {
|
|
20
|
+
Valid = 0,
|
|
21
|
+
Invalid = 1,
|
|
22
|
+
Unknown = 2
|
|
23
|
+
}
|
|
24
|
+
export declare const summaryMessage: {
|
|
25
|
+
allClientCheckPass: string;
|
|
26
|
+
adtServiceUndefined: string;
|
|
27
|
+
packageCheckPass: string;
|
|
28
|
+
packageNotFound: string;
|
|
29
|
+
pacakgeAdtAccessError: string;
|
|
30
|
+
transportCheckPass: string;
|
|
31
|
+
transportNotFound: string;
|
|
32
|
+
transportAdtAccessError: string;
|
|
33
|
+
transportNotRequired: string;
|
|
34
|
+
atoAdtAccessError: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Validation of deploy configuration before running deploy-test.
|
|
38
|
+
*
|
|
39
|
+
* @param input Deploy configuration that needs to be validated
|
|
40
|
+
* @param provider AbapServiceProvider
|
|
41
|
+
* @param logger Logger used by deploy tooling
|
|
42
|
+
* @returns Validation result and a summary report of identified issues.
|
|
43
|
+
*/
|
|
44
|
+
export declare function validateBeforeDeploy(input: ValidationInputs, provider: AbapServiceProvider, logger: Logger): Promise<ValidationOutput>;
|
|
45
|
+
/**
|
|
46
|
+
* Format a list of summary records that is ready to be printed on the console.
|
|
47
|
+
*
|
|
48
|
+
* @param summary A list of summary records
|
|
49
|
+
* @returns Formatted summary string
|
|
50
|
+
*/
|
|
51
|
+
export declare function formatSummary(summary: SummaryRecord[]): string;
|
|
52
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1,289 @@
|
|
|
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.formatSummary = exports.validateBeforeDeploy = exports.summaryMessage = exports.SummaryStatus = void 0;
|
|
13
|
+
const axios_extension_1 = require("@sap-ux/axios-extension");
|
|
14
|
+
const chalk_1 = require("chalk");
|
|
15
|
+
const project_input_validator_1 = require("@sap-ux/project-input-validator");
|
|
16
|
+
const os_1 = require("os");
|
|
17
|
+
var SummaryStatus;
|
|
18
|
+
(function (SummaryStatus) {
|
|
19
|
+
SummaryStatus[SummaryStatus["Valid"] = 0] = "Valid";
|
|
20
|
+
SummaryStatus[SummaryStatus["Invalid"] = 1] = "Invalid";
|
|
21
|
+
SummaryStatus[SummaryStatus["Unknown"] = 2] = "Unknown";
|
|
22
|
+
})(SummaryStatus = exports.SummaryStatus || (exports.SummaryStatus = {}));
|
|
23
|
+
exports.summaryMessage = {
|
|
24
|
+
allClientCheckPass: 'SAPUI5 ABAP Repository follows the rules of creating BSP application',
|
|
25
|
+
adtServiceUndefined: 'AdtService cannot be instantiated',
|
|
26
|
+
packageCheckPass: 'Package is found on ABAP system',
|
|
27
|
+
packageNotFound: 'Package does not exist on ABAP system',
|
|
28
|
+
pacakgeAdtAccessError: 'Package could not be validated. Please check manually.',
|
|
29
|
+
transportCheckPass: 'Transport Request is found on ABAP system',
|
|
30
|
+
transportNotFound: 'Transport Request does not exist on ABAP system',
|
|
31
|
+
transportAdtAccessError: 'Transport Request could not be validated. Please check manually.',
|
|
32
|
+
transportNotRequired: 'Transport Request is not required for local package',
|
|
33
|
+
atoAdtAccessError: 'Development prefix could not be validated. Please check manually.'
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Validation of deploy configuration before running deploy-test.
|
|
37
|
+
*
|
|
38
|
+
* @param input Deploy configuration that needs to be validated
|
|
39
|
+
* @param provider AbapServiceProvider
|
|
40
|
+
* @param logger Logger used by deploy tooling
|
|
41
|
+
* @returns Validation result and a summary report of identified issues.
|
|
42
|
+
*/
|
|
43
|
+
function validateBeforeDeploy(input, provider, logger) {
|
|
44
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
45
|
+
const output = {
|
|
46
|
+
summary: [],
|
|
47
|
+
result: true
|
|
48
|
+
};
|
|
49
|
+
// output is passed by reference and status updated during the internal pipeline below.
|
|
50
|
+
yield validateInputTextFormat(input, output, provider, logger);
|
|
51
|
+
yield validatePackageWithAdt(input, output, provider, logger);
|
|
52
|
+
yield validateTransportRequestWithAdt(input, output, provider, logger);
|
|
53
|
+
return output;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
exports.validateBeforeDeploy = validateBeforeDeploy;
|
|
57
|
+
/**
|
|
58
|
+
* Format a list of summary records that is ready to be printed on the console.
|
|
59
|
+
*
|
|
60
|
+
* @param summary A list of summary records
|
|
61
|
+
* @returns Formatted summary string
|
|
62
|
+
*/
|
|
63
|
+
function formatSummary(summary) {
|
|
64
|
+
const summaryStr = summary
|
|
65
|
+
.map((next) => {
|
|
66
|
+
let statusSymbol = '';
|
|
67
|
+
switch (next.status) {
|
|
68
|
+
case SummaryStatus.Valid:
|
|
69
|
+
statusSymbol = (0, chalk_1.green)('√');
|
|
70
|
+
break;
|
|
71
|
+
case SummaryStatus.Invalid:
|
|
72
|
+
statusSymbol = (0, chalk_1.red)('×');
|
|
73
|
+
break;
|
|
74
|
+
case SummaryStatus.Unknown:
|
|
75
|
+
default:
|
|
76
|
+
statusSymbol = (0, chalk_1.yellow)('?');
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
return `${statusSymbol} ${next.message}`;
|
|
80
|
+
})
|
|
81
|
+
.reduce((aggregated, current) => {
|
|
82
|
+
return `${aggregated}${os_1.EOL}${current}`;
|
|
83
|
+
}, '');
|
|
84
|
+
return summaryStr;
|
|
85
|
+
}
|
|
86
|
+
exports.formatSummary = formatSummary;
|
|
87
|
+
/**
|
|
88
|
+
* Client-side validation on the deploy configuration based on the
|
|
89
|
+
* known input format constraints.
|
|
90
|
+
*
|
|
91
|
+
* @param input Deploy config that needs to be validated
|
|
92
|
+
* @param output Validation output
|
|
93
|
+
* @param provider AbapServiceProvider
|
|
94
|
+
* @param logger Logger from the calling context
|
|
95
|
+
*/
|
|
96
|
+
function validateInputTextFormat(input, output, provider, logger) {
|
|
97
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
98
|
+
// Prepare backend info for validation
|
|
99
|
+
const prefix = yield getSystemPrefix(output, provider, logger);
|
|
100
|
+
// A sequence of client-side validations. No early termination of detecting invalid inputs.
|
|
101
|
+
// Setting output.result to false if any of the checks is invalid.
|
|
102
|
+
// Add individual error messages into output.summary array if validation failed.
|
|
103
|
+
let result = (0, project_input_validator_1.validateAppName)(input.appName, prefix);
|
|
104
|
+
processInputValidationResult(result, output);
|
|
105
|
+
result = (0, project_input_validator_1.validateAppDescription)(input.description);
|
|
106
|
+
processInputValidationResult(result, output);
|
|
107
|
+
result = (0, project_input_validator_1.validateTransportRequestNumber)(input.transport, input.package);
|
|
108
|
+
processInputValidationResult(result, output);
|
|
109
|
+
result = (0, project_input_validator_1.validatePackage)(input.package);
|
|
110
|
+
processInputValidationResult(result, output);
|
|
111
|
+
result = (0, project_input_validator_1.validateClient)(input.client);
|
|
112
|
+
processInputValidationResult(result, output);
|
|
113
|
+
result = (0, project_input_validator_1.validateUrl)(input.url);
|
|
114
|
+
processInputValidationResult(result, output);
|
|
115
|
+
// If all the text validation passed, only show the following success message.
|
|
116
|
+
if (output.result) {
|
|
117
|
+
output.summary.push({
|
|
118
|
+
message: exports.summaryMessage.allClientCheckPass,
|
|
119
|
+
status: SummaryStatus.Valid
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Helper function that calls ADT service to retrieve system specific prefix
|
|
126
|
+
* requirement for Fiori app name.
|
|
127
|
+
*
|
|
128
|
+
* @param output Validation output
|
|
129
|
+
* @param provider AbapServiceProvider
|
|
130
|
+
* @param logger Logger from the calling context
|
|
131
|
+
* @returns System specific development prefix constraint for Fiori app name
|
|
132
|
+
*/
|
|
133
|
+
function getSystemPrefix(output, provider, logger) {
|
|
134
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
135
|
+
try {
|
|
136
|
+
const adtService = yield provider.getAdtService(axios_extension_1.AtoService);
|
|
137
|
+
if (!adtService) {
|
|
138
|
+
output.summary.push({
|
|
139
|
+
message: `${exports.summaryMessage.adtServiceUndefined} for AtoService`,
|
|
140
|
+
status: SummaryStatus.Unknown
|
|
141
|
+
});
|
|
142
|
+
output.result = false;
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
const atoSettings = yield adtService.getAtoInfo();
|
|
146
|
+
return atoSettings === null || atoSettings === void 0 ? void 0 : atoSettings.developmentPrefix;
|
|
147
|
+
}
|
|
148
|
+
catch (e) {
|
|
149
|
+
logger.error(e);
|
|
150
|
+
output.summary.push({
|
|
151
|
+
message: exports.summaryMessage.atoAdtAccessError,
|
|
152
|
+
status: SummaryStatus.Unknown
|
|
153
|
+
});
|
|
154
|
+
output.result = false;
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Helper function to proces input validation result. Avoids sonarqube warning about
|
|
161
|
+
* increasing complexity.
|
|
162
|
+
*
|
|
163
|
+
* @param validationResult Validation result is either true or error message
|
|
164
|
+
* @param output validation output
|
|
165
|
+
*/
|
|
166
|
+
function processInputValidationResult(validationResult, output) {
|
|
167
|
+
if (typeof validationResult === 'string') {
|
|
168
|
+
output.summary.push({
|
|
169
|
+
message: validationResult,
|
|
170
|
+
status: SummaryStatus.Invalid
|
|
171
|
+
});
|
|
172
|
+
output.result = false;
|
|
173
|
+
}
|
|
174
|
+
else if (validationResult !== true) {
|
|
175
|
+
// Strict check for validator functions that return false instead of error message.
|
|
176
|
+
throw new Error('Expect error message string returned from validation function instead of false');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Query ADT backend service to verify input package name is valid.
|
|
181
|
+
*
|
|
182
|
+
* @param input Inputs to query ADT service
|
|
183
|
+
* @param output Output to be updated during this function call
|
|
184
|
+
* @param provider AbapServiceProvider
|
|
185
|
+
* @param logger Logger from the calling context
|
|
186
|
+
*/
|
|
187
|
+
function validatePackageWithAdt(input, output, provider, logger) {
|
|
188
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
189
|
+
if (output.result === false) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
const adtService = yield provider.getAdtService(axios_extension_1.ListPackageService);
|
|
194
|
+
if (!adtService) {
|
|
195
|
+
output.summary.push({
|
|
196
|
+
message: `${exports.summaryMessage.adtServiceUndefined} for ListPackageService`,
|
|
197
|
+
status: SummaryStatus.Unknown
|
|
198
|
+
});
|
|
199
|
+
output.result = false;
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const packages = yield adtService.listPackages({ phrase: input.package });
|
|
203
|
+
const isValidPackage = packages.findIndex((packageName) => packageName === input.package) >= 0;
|
|
204
|
+
if (isValidPackage) {
|
|
205
|
+
output.summary.push({
|
|
206
|
+
message: exports.summaryMessage.packageCheckPass,
|
|
207
|
+
status: SummaryStatus.Valid
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
output.summary.push({
|
|
212
|
+
message: exports.summaryMessage.packageNotFound,
|
|
213
|
+
status: SummaryStatus.Invalid
|
|
214
|
+
});
|
|
215
|
+
output.result = false;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
catch (e) {
|
|
219
|
+
logger.error(e);
|
|
220
|
+
output.summary.push({
|
|
221
|
+
message: exports.summaryMessage.pacakgeAdtAccessError,
|
|
222
|
+
status: SummaryStatus.Unknown
|
|
223
|
+
});
|
|
224
|
+
output.result = false;
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Query ADT backend service to verify input transport request.
|
|
230
|
+
*
|
|
231
|
+
* @param input Inputs to query ADT service
|
|
232
|
+
* @param output Output to be updated during this function call
|
|
233
|
+
* @param provider AbapServiceProvider
|
|
234
|
+
* @param logger Logger from the calling context
|
|
235
|
+
*/
|
|
236
|
+
function validateTransportRequestWithAdt(input, output, provider, logger) {
|
|
237
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
238
|
+
if (output.result === false) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
const adtService = yield provider.getAdtService(axios_extension_1.TransportChecksService);
|
|
243
|
+
if (!adtService) {
|
|
244
|
+
output.summary.push({
|
|
245
|
+
message: `${exports.summaryMessage.adtServiceUndefined} for TransportChecksService`,
|
|
246
|
+
status: SummaryStatus.Unknown
|
|
247
|
+
});
|
|
248
|
+
output.result = false;
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const trList = yield adtService.getTransportRequests(input.package, input.appName);
|
|
252
|
+
const isValidTrList = trList.findIndex((tr) => tr.transportNumber === input.transport) >= 0;
|
|
253
|
+
if (isValidTrList) {
|
|
254
|
+
output.summary.push({
|
|
255
|
+
message: exports.summaryMessage.transportCheckPass,
|
|
256
|
+
status: SummaryStatus.Valid
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
output.summary.push({
|
|
261
|
+
message: exports.summaryMessage.transportNotFound,
|
|
262
|
+
status: SummaryStatus.Invalid
|
|
263
|
+
});
|
|
264
|
+
output.result = false;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch (e) {
|
|
268
|
+
// TransportChecksService.getTransportRequests() API is used to provide valid
|
|
269
|
+
// transport request list. If input packge is local package, no transport request
|
|
270
|
+
// is returned and LocalPackageError is thrown as exception.
|
|
271
|
+
// LocalPackageError is acceptable for validation purpose here.
|
|
272
|
+
if (e.message === axios_extension_1.TransportChecksService.LocalPackageError) {
|
|
273
|
+
output.summary.push({
|
|
274
|
+
message: exports.summaryMessage.transportNotRequired,
|
|
275
|
+
status: SummaryStatus.Valid
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
logger.error(e);
|
|
280
|
+
output.summary.push({
|
|
281
|
+
message: exports.summaryMessage.transportAdtAccessError,
|
|
282
|
+
status: SummaryStatus.Unknown
|
|
283
|
+
});
|
|
284
|
+
output.result = false;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
//# sourceMappingURL=validate.js.map
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"bugs": {
|
|
10
10
|
"url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Adeploy-tooling"
|
|
11
11
|
},
|
|
12
|
-
"version": "0.9.
|
|
12
|
+
"version": "0.9.17",
|
|
13
13
|
"license": "Apache-2.0",
|
|
14
14
|
"author": "@SAP/ux-tools-team",
|
|
15
15
|
"main": "dist/index.js",
|
|
@@ -31,11 +31,13 @@
|
|
|
31
31
|
"dotenv": "16.3.1",
|
|
32
32
|
"prompts": "2.4.2",
|
|
33
33
|
"yazl": "2.5.1",
|
|
34
|
-
"
|
|
34
|
+
"chalk": "4.1.2",
|
|
35
|
+
"@sap-ux/axios-extension": "1.5.0",
|
|
35
36
|
"@sap-ux/btp-utils": "0.11.9",
|
|
36
37
|
"@sap-ux/logger": "0.3.8",
|
|
37
|
-
"@sap-ux/system-access": "0.2.
|
|
38
|
-
"@sap-ux/ui5-config": "0.19.
|
|
38
|
+
"@sap-ux/system-access": "0.2.9",
|
|
39
|
+
"@sap-ux/ui5-config": "0.19.4",
|
|
40
|
+
"@sap-ux/project-input-validator": "0.1.0"
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
41
43
|
"@types/prompts": "2.4.4",
|