abapgit-agent 1.13.4 → 1.13.6
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/bin/abapgit-agent +6 -0
- package/package.json +2 -1
- package/src/commands/help.js +3 -1
- package/src/commands/inspect.js +133 -2
- package/src/commands/pull.js +6 -1
- package/src/commands/unit.js +122 -6
- package/src/utils/version-check.js +2 -1
package/bin/abapgit-agent
CHANGED
|
@@ -36,6 +36,12 @@ async function main() {
|
|
|
36
36
|
|
|
37
37
|
const commandModules = require('../src/commands/index');
|
|
38
38
|
|
|
39
|
+
// Handle --version / -v
|
|
40
|
+
if (command === '--version' || command === '-v') {
|
|
41
|
+
console.log(versionCheck.getCliVersion());
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
39
45
|
// Check if this is a modular command
|
|
40
46
|
if (commandModules[command] || command === '--help' || command === '-h') {
|
|
41
47
|
const cmd = command === '--help' || command === '-h' ? commandModules.help : commandModules[command];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "abapgit-agent",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.6",
|
|
4
4
|
"description": "ABAP Git Agent - Pull and activate ABAP code via abapGit from any git repository",
|
|
5
5
|
"files": [
|
|
6
6
|
"bin/",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"test:cmd:pull": "node tests/run-all.js --cmd --command=pull",
|
|
30
30
|
"test:sync-xml": "node tests/run-all.js --sync-xml",
|
|
31
31
|
"test:xml-only": "node tests/run-all.js --xml-only",
|
|
32
|
+
"test:junit": "node tests/run-all.js --junit",
|
|
32
33
|
"test:cmd:inspect": "node tests/run-all.js --cmd --command=inspect",
|
|
33
34
|
"test:cmd:unit": "node tests/run-all.js --cmd --command=unit",
|
|
34
35
|
"test:cmd:view": "node tests/run-all.js --cmd --command=view",
|
package/src/commands/help.js
CHANGED
|
@@ -9,8 +9,10 @@ module.exports = {
|
|
|
9
9
|
requiresVersionCheck: false,
|
|
10
10
|
|
|
11
11
|
async execute(args, context) {
|
|
12
|
+
const { versionCheck } = context;
|
|
13
|
+
const version = versionCheck.getCliVersion();
|
|
12
14
|
console.log(`
|
|
13
|
-
ABAP Git Agent
|
|
15
|
+
ABAP Git Agent v${version}
|
|
14
16
|
|
|
15
17
|
Usage:
|
|
16
18
|
abapgit-agent <command> [options]
|
package/src/commands/inspect.js
CHANGED
|
@@ -3,8 +3,109 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const pathModule = require('path');
|
|
6
|
+
const fs = require('fs');
|
|
6
7
|
const { printHttpError } = require('../utils/format-error');
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Escape a string for safe embedding in XML text/attribute content
|
|
11
|
+
*/
|
|
12
|
+
function escapeXml(str) {
|
|
13
|
+
return String(str)
|
|
14
|
+
.replace(/&/g, '&')
|
|
15
|
+
.replace(/</g, '<')
|
|
16
|
+
.replace(/>/g, '>')
|
|
17
|
+
.replace(/"/g, '"')
|
|
18
|
+
.replace(/'/g, ''');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Build JUnit XML from inspect results array.
|
|
23
|
+
*
|
|
24
|
+
* Maps to JUnit schema:
|
|
25
|
+
* <testsuites>
|
|
26
|
+
* <testsuite name="CLAS ZCL_MY_CLASS" tests="N" failures="F" errors="0">
|
|
27
|
+
* <testcase name="Syntax check" classname="ZCL_MY_CLASS">
|
|
28
|
+
* <failure type="SyntaxError" message="...">line/col/method detail</failure>
|
|
29
|
+
* </testcase>
|
|
30
|
+
* </testsuite>
|
|
31
|
+
* </testsuites>
|
|
32
|
+
*
|
|
33
|
+
* One testsuite per object. Each error becomes a <failure>. Warnings become
|
|
34
|
+
* a single <failure type="Warning"> so they are visible but don't fail the build
|
|
35
|
+
* unless there are also hard errors (Jenkins distinguishes failure vs unstable).
|
|
36
|
+
*/
|
|
37
|
+
function buildInspectJUnit(results) {
|
|
38
|
+
const suites = results.map(res => {
|
|
39
|
+
const objectType = res.OBJECT_TYPE !== undefined ? res.OBJECT_TYPE : (res.object_type || 'UNKNOWN');
|
|
40
|
+
const objectName = res.OBJECT_NAME !== undefined ? res.OBJECT_NAME : (res.object_name || 'UNKNOWN');
|
|
41
|
+
const errors = res.ERRORS !== undefined ? res.ERRORS : (res.errors || []);
|
|
42
|
+
const warnings = res.WARNINGS !== undefined ? res.WARNINGS : (res.warnings || []);
|
|
43
|
+
const errorCount = errors.length;
|
|
44
|
+
const warnCount = warnings.length;
|
|
45
|
+
// One testcase per error/warning; at least one testcase for a clean object
|
|
46
|
+
const testCount = Math.max(1, errorCount + warnCount);
|
|
47
|
+
|
|
48
|
+
const testcases = [];
|
|
49
|
+
|
|
50
|
+
if (errorCount === 0 && warnCount === 0) {
|
|
51
|
+
testcases.push(` <testcase name="Syntax check" classname="${escapeXml(objectName)}"/>`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
for (const err of errors) {
|
|
55
|
+
const line = err.LINE || err.line || '?';
|
|
56
|
+
const column = err.COLUMN || err.column || '?';
|
|
57
|
+
const text = err.TEXT || err.text || 'Unknown error';
|
|
58
|
+
const methodName = err.METHOD_NAME || err.method_name;
|
|
59
|
+
const sobjname = err.SOBJNAME || err.sobjname || '';
|
|
60
|
+
const detail = [
|
|
61
|
+
methodName ? `Method: ${methodName}` : null,
|
|
62
|
+
`Line ${line}, Column ${column}`,
|
|
63
|
+
sobjname ? `Include: ${sobjname}` : null,
|
|
64
|
+
text
|
|
65
|
+
].filter(Boolean).join('\n');
|
|
66
|
+
const caseName = methodName ? `${methodName} line ${line}` : `Line ${line}`;
|
|
67
|
+
testcases.push(
|
|
68
|
+
` <testcase name="${escapeXml(caseName)}" classname="${escapeXml(objectName)}">\n` +
|
|
69
|
+
` <failure type="SyntaxError" message="${escapeXml(text)}">${escapeXml(detail)}</failure>\n` +
|
|
70
|
+
` </testcase>`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for (const warn of warnings) {
|
|
75
|
+
const line = warn.LINE || warn.line || '?';
|
|
76
|
+
const text = warn.MESSAGE || warn.message || warn.TEXT || warn.text || 'Warning';
|
|
77
|
+
const methodName = warn.METHOD_NAME || warn.method_name;
|
|
78
|
+
const sobjname = warn.SOBJNAME || warn.sobjname || '';
|
|
79
|
+
const detail = [
|
|
80
|
+
methodName ? `Method: ${methodName}` : null,
|
|
81
|
+
`Line ${line}`,
|
|
82
|
+
sobjname ? `Include: ${sobjname}` : null,
|
|
83
|
+
text
|
|
84
|
+
].filter(Boolean).join('\n');
|
|
85
|
+
const caseName = methodName ? `${methodName} line ${line} (warning)` : `Line ${line} (warning)`;
|
|
86
|
+
testcases.push(
|
|
87
|
+
` <testcase name="${escapeXml(caseName)}" classname="${escapeXml(objectName)}">\n` +
|
|
88
|
+
` <failure type="Warning" message="${escapeXml(text)}">${escapeXml(detail)}</failure>\n` +
|
|
89
|
+
` </testcase>`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
` <testsuite name="${escapeXml(objectType + ' ' + objectName)}" ` +
|
|
95
|
+
`tests="${testCount}" failures="${errorCount + warnCount}" errors="0">\n` +
|
|
96
|
+
testcases.join('\n') + '\n' +
|
|
97
|
+
` </testsuite>`
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
'<?xml version="1.0" encoding="UTF-8"?>\n' +
|
|
103
|
+
'<testsuites>\n' +
|
|
104
|
+
suites.join('\n') + '\n' +
|
|
105
|
+
'</testsuites>\n'
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
8
109
|
/**
|
|
9
110
|
* Inspect all files in one request
|
|
10
111
|
*/
|
|
@@ -154,10 +255,10 @@ module.exports = {
|
|
|
154
255
|
const filesArgIndex = args.indexOf('--files');
|
|
155
256
|
if (filesArgIndex === -1 || filesArgIndex + 1 >= args.length) {
|
|
156
257
|
console.error('Error: --files parameter required');
|
|
157
|
-
console.error('Usage: abapgit-agent inspect --files <file1>,<file2>,... [--variant <check-variant>] [--json]');
|
|
258
|
+
console.error('Usage: abapgit-agent inspect --files <file1>,<file2>,... [--variant <check-variant>] [--junit-output <file>] [--json]');
|
|
158
259
|
console.error('Example: abapgit-agent inspect --files src/zcl_my_class.clas.abap');
|
|
159
260
|
console.error('Example: abapgit-agent inspect --files src/zcl_my_class.clas.abap --variant ALL_CHECKS');
|
|
160
|
-
console.error('Example: abapgit-agent inspect --files src/zcl_my_class.clas.abap --
|
|
261
|
+
console.error('Example: abapgit-agent inspect --files src/zcl_my_class.clas.abap --junit-output reports/inspect.xml');
|
|
161
262
|
process.exit(1);
|
|
162
263
|
}
|
|
163
264
|
|
|
@@ -167,11 +268,18 @@ module.exports = {
|
|
|
167
268
|
const variantArgIndex = args.indexOf('--variant');
|
|
168
269
|
const variant = variantArgIndex !== -1 ? args[variantArgIndex + 1] : null;
|
|
169
270
|
|
|
271
|
+
// Parse optional --junit-output parameter
|
|
272
|
+
const junitArgIndex = args.indexOf('--junit-output');
|
|
273
|
+
const junitOutput = junitArgIndex !== -1 ? args[junitArgIndex + 1] : null;
|
|
274
|
+
|
|
170
275
|
if (!jsonOutput) {
|
|
171
276
|
console.log(`\n Inspect for ${filesSyntaxCheck.length} file(s)`);
|
|
172
277
|
if (variant) {
|
|
173
278
|
console.log(` Using variant: ${variant}`);
|
|
174
279
|
}
|
|
280
|
+
if (junitOutput) {
|
|
281
|
+
console.log(` JUnit output: ${junitOutput}`);
|
|
282
|
+
}
|
|
175
283
|
console.log('');
|
|
176
284
|
}
|
|
177
285
|
|
|
@@ -182,6 +290,22 @@ module.exports = {
|
|
|
182
290
|
// Send all files in one request
|
|
183
291
|
const results = await inspectAllFiles(filesSyntaxCheck, csrfToken, config, variant, http, verbose);
|
|
184
292
|
|
|
293
|
+
// JUnit output mode — write XML file, then continue to normal output
|
|
294
|
+
if (junitOutput) {
|
|
295
|
+
const xml = buildInspectJUnit(results);
|
|
296
|
+
const outputPath = pathModule.isAbsolute(junitOutput)
|
|
297
|
+
? junitOutput
|
|
298
|
+
: pathModule.join(process.cwd(), junitOutput);
|
|
299
|
+
const dir = pathModule.dirname(outputPath);
|
|
300
|
+
if (!fs.existsSync(dir)) {
|
|
301
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
302
|
+
}
|
|
303
|
+
fs.writeFileSync(outputPath, xml, 'utf8');
|
|
304
|
+
if (!jsonOutput) {
|
|
305
|
+
console.log(` JUnit report written to: ${outputPath}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
185
309
|
// JSON output mode
|
|
186
310
|
if (jsonOutput) {
|
|
187
311
|
console.log(JSON.stringify(results, null, 2));
|
|
@@ -189,8 +313,15 @@ module.exports = {
|
|
|
189
313
|
}
|
|
190
314
|
|
|
191
315
|
// Process results
|
|
316
|
+
let hasErrors = false;
|
|
192
317
|
for (const result of results) {
|
|
193
318
|
await processInspectResult(result);
|
|
319
|
+
const errorCount = result.ERROR_COUNT !== undefined ? result.ERROR_COUNT : (result.error_count || 0);
|
|
320
|
+
if (errorCount > 0) hasErrors = true;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (hasErrors) {
|
|
324
|
+
process.exit(1);
|
|
194
325
|
}
|
|
195
326
|
}
|
|
196
327
|
};
|
package/src/commands/pull.js
CHANGED
|
@@ -168,8 +168,13 @@ module.exports = {
|
|
|
168
168
|
const csrfToken = await http.fetchCsrfToken();
|
|
169
169
|
statusResult = await http.post('/sap/bc/z_abapgit_agent/status', { url: gitUrl }, { csrfToken });
|
|
170
170
|
} catch (e) {
|
|
171
|
+
const isNetworkError = e.code && ['ENOTFOUND', 'ECONNREFUSED', 'ETIMEDOUT', 'ECONNRESET'].includes(e.code);
|
|
171
172
|
console.error(`❌ Repository status check failed: ${e.message}`);
|
|
172
|
-
|
|
173
|
+
if (isNetworkError) {
|
|
174
|
+
console.error(' Cannot reach the ABAP system. Check your network connection and the host in .abapGitAgent.');
|
|
175
|
+
} else {
|
|
176
|
+
console.error(' Make sure the repository is registered with abapgit-agent (run "abapgit-agent create").');
|
|
177
|
+
}
|
|
173
178
|
process.exit(1);
|
|
174
179
|
}
|
|
175
180
|
|
package/src/commands/unit.js
CHANGED
|
@@ -6,6 +6,92 @@ const pathModule = require('path');
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const { formatHttpError } = require('../utils/format-error');
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Escape a string for safe embedding in XML text/attribute content
|
|
11
|
+
*/
|
|
12
|
+
function escapeXml(str) {
|
|
13
|
+
return String(str)
|
|
14
|
+
.replace(/&/g, '&')
|
|
15
|
+
.replace(/</g, '<')
|
|
16
|
+
.replace(/>/g, '>')
|
|
17
|
+
.replace(/"/g, '"')
|
|
18
|
+
.replace(/'/g, ''');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Build JUnit XML from unit test results array.
|
|
23
|
+
*
|
|
24
|
+
* Maps to JUnit schema:
|
|
25
|
+
* <testsuites>
|
|
26
|
+
* <testsuite name="ZCL_MY_TEST" tests="10" failures="2" errors="0">
|
|
27
|
+
* <testcase name="TEST_METHOD_1" classname="ZCL_MY_TEST"/>
|
|
28
|
+
* <testcase name="TEST_METHOD_2" classname="ZCL_MY_TEST">
|
|
29
|
+
* <failure type="FAILURE" message="...">detail</failure>
|
|
30
|
+
* </testcase>
|
|
31
|
+
* </testsuite>
|
|
32
|
+
* </testsuites>
|
|
33
|
+
*
|
|
34
|
+
* One testsuite per test class file. Each failed test method becomes a <failure>.
|
|
35
|
+
* Passing methods are listed as empty <testcase> elements (Jenkins counts them).
|
|
36
|
+
*/
|
|
37
|
+
function buildUnitJUnit(results) {
|
|
38
|
+
const suites = results.map(res => {
|
|
39
|
+
const success = res.SUCCESS || res.success;
|
|
40
|
+
const testCount = res.TEST_COUNT || res.test_count || 0;
|
|
41
|
+
const passedCount = res.PASSED_COUNT || res.passed_count || 0;
|
|
42
|
+
const failedCount = res.FAILED_COUNT || res.failed_count || 0;
|
|
43
|
+
const errors = res.ERRORS || res.errors || [];
|
|
44
|
+
const className = res._className || 'UNKNOWN'; // injected by caller
|
|
45
|
+
|
|
46
|
+
// Build a set of failed method names for quick lookup
|
|
47
|
+
const failedMethods = new Set(
|
|
48
|
+
errors.map(e => (e.CLASS_NAME || e.class_name || '') + '=>' + (e.METHOD_NAME || e.method_name || ''))
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const testcases = [];
|
|
52
|
+
|
|
53
|
+
// Emit one <testcase> per failed test
|
|
54
|
+
for (const err of errors) {
|
|
55
|
+
const errClassName = err.CLASS_NAME || err.class_name || className;
|
|
56
|
+
const methodName = err.METHOD_NAME || err.method_name || '?';
|
|
57
|
+
const errorKind = err.ERROR_KIND || err.error_kind || 'FAILURE';
|
|
58
|
+
const errorText = err.ERROR_TEXT || err.error_text || 'Test failed';
|
|
59
|
+
testcases.push(
|
|
60
|
+
` <testcase name="${escapeXml(methodName)}" classname="${escapeXml(errClassName)}">\n` +
|
|
61
|
+
` <failure type="${escapeXml(errorKind)}" message="${escapeXml(errorText)}">${escapeXml(errorText)}</failure>\n` +
|
|
62
|
+
` </testcase>`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Emit empty <testcase> elements for passing tests (Jenkins shows total count)
|
|
67
|
+
// We can't enumerate them individually (ABAP doesn't return passing method names),
|
|
68
|
+
// so emit one aggregate passing testcase when passedCount > 0
|
|
69
|
+
if (passedCount > 0) {
|
|
70
|
+
testcases.push(
|
|
71
|
+
` <testcase name="(${passedCount} passing test(s))" classname="${escapeXml(className)}"/>`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (testCount === 0) {
|
|
76
|
+
testcases.push(` <testcase name="(no tests)" classname="${escapeXml(className)}"/>`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
` <testsuite name="${escapeXml(className)}" ` +
|
|
81
|
+
`tests="${Math.max(testCount, 1)}" failures="${failedCount}" errors="0">\n` +
|
|
82
|
+
testcases.join('\n') + '\n' +
|
|
83
|
+
` </testsuite>`
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
'<?xml version="1.0" encoding="UTF-8"?>\n' +
|
|
89
|
+
'<testsuites>\n' +
|
|
90
|
+
suites.join('\n') + '\n' +
|
|
91
|
+
'</testsuites>\n'
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
9
95
|
/**
|
|
10
96
|
* Run unit test for a single file
|
|
11
97
|
*/
|
|
@@ -151,10 +237,10 @@ module.exports = {
|
|
|
151
237
|
const filesArgIndex = args.indexOf('--files');
|
|
152
238
|
if (filesArgIndex === -1 || filesArgIndex + 1 >= args.length) {
|
|
153
239
|
console.error('Error: --files parameter required');
|
|
154
|
-
console.error('Usage: abapgit-agent unit --files <file1>,<file2>,... [--coverage] [--json]');
|
|
155
|
-
console.error('Example: abapgit-agent unit --files src/zcl_my_test.clas.abap');
|
|
156
|
-
console.error('Example: abapgit-agent unit --files src/zcl_my_test.clas.abap --coverage');
|
|
157
|
-
console.error('Example: abapgit-agent unit --files src/zcl_my_test.clas.abap --
|
|
240
|
+
console.error('Usage: abapgit-agent unit --files <file1>,<file2>,... [--coverage] [--junit-output <file>] [--json]');
|
|
241
|
+
console.error('Example: abapgit-agent unit --files src/zcl_my_test.clas.testclasses.abap');
|
|
242
|
+
console.error('Example: abapgit-agent unit --files src/zcl_my_test.clas.testclasses.abap --coverage');
|
|
243
|
+
console.error('Example: abapgit-agent unit --files src/zcl_my_test.clas.testclasses.abap --junit-output reports/unit.xml');
|
|
158
244
|
process.exit(1);
|
|
159
245
|
}
|
|
160
246
|
|
|
@@ -163,8 +249,15 @@ module.exports = {
|
|
|
163
249
|
// Check for coverage option
|
|
164
250
|
const coverage = args.includes('--coverage');
|
|
165
251
|
|
|
252
|
+
// Parse optional --junit-output parameter
|
|
253
|
+
const junitArgIndex = args.indexOf('--junit-output');
|
|
254
|
+
const junitOutput = junitArgIndex !== -1 ? args[junitArgIndex + 1] : null;
|
|
255
|
+
|
|
166
256
|
if (!jsonOutput) {
|
|
167
257
|
console.log(`\n Running unit tests for ${files.length} file(s)${coverage ? ' (with coverage)' : ''}`);
|
|
258
|
+
if (junitOutput) {
|
|
259
|
+
console.log(` JUnit output: ${junitOutput}`);
|
|
260
|
+
}
|
|
168
261
|
console.log('');
|
|
169
262
|
}
|
|
170
263
|
|
|
@@ -172,19 +265,42 @@ module.exports = {
|
|
|
172
265
|
const http = new AbapHttp(config);
|
|
173
266
|
const csrfToken = await http.fetchCsrfToken();
|
|
174
267
|
|
|
175
|
-
// Collect results for JSON output
|
|
268
|
+
// Collect results for JSON / JUnit output
|
|
176
269
|
const results = [];
|
|
177
270
|
let hasErrors = false;
|
|
178
271
|
|
|
179
272
|
for (const sourceFile of files) {
|
|
180
273
|
const result = await runUnitTestForFile(sourceFile, csrfToken, config, coverage, http, jsonOutput, verbose);
|
|
181
274
|
if (result) {
|
|
275
|
+
// Inject class name derived from file path for JUnit builder
|
|
276
|
+
const fileName = pathModule.basename(sourceFile).toUpperCase();
|
|
277
|
+
result._className = fileName.split('.')[0];
|
|
182
278
|
results.push(result);
|
|
183
279
|
|
|
184
|
-
// Check if this result contains an error
|
|
185
280
|
if (result.error || result.statusCode >= 400) {
|
|
186
281
|
hasErrors = true;
|
|
187
282
|
}
|
|
283
|
+
// Also treat failed tests as an error for exit code
|
|
284
|
+
const failedCount = result.FAILED_COUNT || result.failed_count || 0;
|
|
285
|
+
if (failedCount > 0) {
|
|
286
|
+
hasErrors = true;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// JUnit output mode — write XML, then continue to normal output
|
|
292
|
+
if (junitOutput) {
|
|
293
|
+
const xml = buildUnitJUnit(results);
|
|
294
|
+
const outputPath = pathModule.isAbsolute(junitOutput)
|
|
295
|
+
? junitOutput
|
|
296
|
+
: pathModule.join(process.cwd(), junitOutput);
|
|
297
|
+
const dir = pathModule.dirname(outputPath);
|
|
298
|
+
if (!fs.existsSync(dir)) {
|
|
299
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
300
|
+
}
|
|
301
|
+
fs.writeFileSync(outputPath, xml, 'utf8');
|
|
302
|
+
if (!jsonOutput) {
|
|
303
|
+
console.log(` JUnit report written to: ${outputPath}`);
|
|
188
304
|
}
|
|
189
305
|
}
|
|
190
306
|
|
|
@@ -135,7 +135,8 @@ async function getLatestNpmVersion() {
|
|
|
135
135
|
const baseUrl = registry.endsWith('/') ? registry : registry + '/';
|
|
136
136
|
const url = `${baseUrl}abapgit-agent/latest`;
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
const client = url.startsWith('http://') ? http : https;
|
|
139
|
+
client.get(url, (res) => {
|
|
139
140
|
let body = '';
|
|
140
141
|
res.on('data', chunk => body += chunk);
|
|
141
142
|
res.on('end', () => {
|