trm-core 10.0.1 → 10.1.0
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.txt +10 -0
- package/dist/actions/checkEngines/analyze.js +6 -0
- package/dist/actions/checkSapEntries/analyze.d.ts +2 -0
- package/dist/actions/checkSapEntries/analyze.js +100 -18
- package/dist/actions/commons/setSystemPackages.js +1 -1
- package/dist/actions/commons/utils/actionLocks.d.ts +2 -1
- package/dist/actions/commons/utils/actionLocks.js +8 -1
- package/dist/actions/commons/utils/index.d.ts +1 -0
- package/dist/actions/commons/utils/index.js +1 -0
- package/dist/actions/commons/utils/packageCleanup.d.ts +8 -0
- package/dist/actions/commons/utils/packageCleanup.js +59 -10
- package/dist/actions/commons/utils/releaseDeletionTransport.js +11 -4
- package/dist/actions/commons/utils/trmBackend.d.ts +20 -0
- package/dist/actions/commons/utils/trmBackend.js +64 -0
- package/dist/actions/install/checkSapEntries.js +1 -1
- package/dist/actions/install/checkTransports.js +40 -1
- package/dist/actions/install/generateLandscapeTransport.js +4 -1
- package/dist/actions/install/generateUpdateTransport.d.ts +7 -0
- package/dist/actions/install/generateUpdateTransport.js +75 -8
- package/dist/actions/install/importBatch.js +0 -0
- package/dist/actions/install/index.d.ts +10 -0
- package/dist/actions/install/index.js +6 -3
- package/dist/actions/install/init.js +12 -2
- package/dist/actions/install/prepareCust.js +9 -4
- package/dist/actions/install/prepareDevc.js +9 -4
- package/dist/actions/install/prepareLang.js +9 -4
- package/dist/actions/install/prepareTadir.js +9 -4
- package/dist/actions/install/setInstallDevclass.js +17 -3
- package/dist/actions/install/updatePackageData.d.ts +5 -0
- package/dist/actions/install/updatePackageData.js +37 -18
- package/dist/client/RESTClient.js +2 -5
- package/dist/client/RFCClient.js +3 -5
- package/dist/systemConnector/SystemConnectorBase.js +25 -3
- package/dist/transport/Transport.js +17 -3
- package/package.json +1 -1
package/changelog.txt
CHANGED
|
@@ -8,6 +8,16 @@ Legend
|
|
|
8
8
|
+ : added
|
|
9
9
|
- : removed
|
|
10
10
|
|
|
11
|
+
2026-10-12 v10.1.0
|
|
12
|
+
-------------------
|
|
13
|
+
* trm-server/trm-rest upgrade from a backend before trm-server 7.0.0/trm-rest 6.0.0
|
|
14
|
+
* trm-server/trm-rest upgrade cleanup after the install, deleting only objects not in the new release
|
|
15
|
+
* trm-server/trm-rest kept in $ packages unless installed in /ATRM/ packages
|
|
16
|
+
* deletion transport only includes objects that exist on the system
|
|
17
|
+
* release transports missing from registry metadata read from the verified artifact
|
|
18
|
+
! SAP entries check with percentage progress and batched reads
|
|
19
|
+
! existing objects check batched
|
|
20
|
+
|
|
11
21
|
2026-10-11 v10.0.1
|
|
12
22
|
-------------------
|
|
13
23
|
* rest client https agent import
|
|
@@ -208,26 +208,31 @@ function evaluate(context, engines, path, required) {
|
|
|
208
208
|
switch (key) {
|
|
209
209
|
case 'trm':
|
|
210
210
|
for (const name of Object.keys(engines.trm)) {
|
|
211
|
+
trm_commons_1.Logger.loading(`Checking engine requirement ${prefix}trm.${name}...`);
|
|
211
212
|
ok = push(`${prefix}trm.${name}`, yield checkTrm(context, name, engines.trm[name])) && ok;
|
|
212
213
|
}
|
|
213
214
|
break;
|
|
214
215
|
case 'components':
|
|
215
216
|
for (const name of Object.keys(engines.components)) {
|
|
217
|
+
trm_commons_1.Logger.loading(`Checking engine requirement ${prefix}components.${name}...`);
|
|
216
218
|
ok = push(`${prefix}components.${name}`, yield checkComponent(context, name, engines.components[name])) && ok;
|
|
217
219
|
}
|
|
218
220
|
break;
|
|
219
221
|
case 'products':
|
|
220
222
|
for (const name of Object.keys(engines.products)) {
|
|
223
|
+
trm_commons_1.Logger.loading(`Checking engine requirement ${prefix}products.${name}...`);
|
|
221
224
|
ok = push(`${prefix}products.${name}`, yield checkProduct(context, name, engines.products[name])) && ok;
|
|
222
225
|
}
|
|
223
226
|
break;
|
|
224
227
|
case 'notes':
|
|
225
228
|
for (const note of Object.keys(engines.notes)) {
|
|
229
|
+
trm_commons_1.Logger.loading(`Checking engine requirement ${prefix}notes.${note}...`);
|
|
226
230
|
ok = push(`${prefix}notes.${note}`, yield checkNote(note, engines.notes[note])) && ok;
|
|
227
231
|
}
|
|
228
232
|
break;
|
|
229
233
|
case 'tables':
|
|
230
234
|
for (var i = 0; i < engines.tables.length; i++) {
|
|
235
|
+
trm_commons_1.Logger.loading(`Checking engine requirement ${prefix}tables[${i}]...`);
|
|
231
236
|
ok = push(`${prefix}tables[${i}]`, yield checkTable(engines.tables[i])) && ok;
|
|
232
237
|
}
|
|
233
238
|
break;
|
|
@@ -235,6 +240,7 @@ function evaluate(context, engines, path, required) {
|
|
|
235
240
|
var alternatives = [];
|
|
236
241
|
var anyOk = false;
|
|
237
242
|
for (var j = 0; j < engines.anyOf.length; j++) {
|
|
243
|
+
trm_commons_1.Logger.loading(`Checking engine alternative ${prefix}anyOf[${j}]...`);
|
|
238
244
|
const alternative = yield evaluate(context, engines.anyOf[j], `${prefix}anyOf[${j}]`, false);
|
|
239
245
|
alternatives = alternatives.concat(alternative.results);
|
|
240
246
|
anyOk = anyOk || alternative.ok;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Step } from "@simonegaffurini/sammarksworkflow";
|
|
2
2
|
import { CheckSapEntriesWorkflowContext } from ".";
|
|
3
|
+
/** Entries read with one query. */
|
|
4
|
+
export declare const SAP_ENTRIES_BATCH_SIZE = 25;
|
|
3
5
|
/**
|
|
4
6
|
* Workflow step that queries required SAP tables and builds a per-entry status report.
|
|
5
7
|
*
|
|
@@ -9,9 +9,93 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.analyze = void 0;
|
|
12
|
+
exports.analyze = exports.SAP_ENTRIES_BATCH_SIZE = void 0;
|
|
13
13
|
const trm_commons_1 = require("trm-commons");
|
|
14
14
|
const systemConnector_1 = require("../../systemConnector");
|
|
15
|
+
const manifest_1 = require("../../manifest");
|
|
16
|
+
const commons_1 = require("../../commons");
|
|
17
|
+
/** Entries read with one query. */
|
|
18
|
+
exports.SAP_ENTRIES_BATCH_SIZE = 25;
|
|
19
|
+
// RFC_READ_TABLE option lines hold 72 characters: a batched condition also carries "OR ( " and " )"
|
|
20
|
+
const BATCHED_CONDITION_MAX_LENGTH = 65;
|
|
21
|
+
function entryError(table, tableEntry, e) {
|
|
22
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
23
|
+
return new Error(`Unable to check required SAP entry ${JSON.stringify(tableEntry)} in table "${table}": ${reason}`);
|
|
24
|
+
}
|
|
25
|
+
function checkEntry(table, tableEntry) {
|
|
26
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
27
|
+
try {
|
|
28
|
+
return yield systemConnector_1.SystemConnector.checkSapEntryExists(table, tableEntry);
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
throw entryError(table, tableEntry, e);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Whether each entry of a table exists. Entries with the same fields are read together, in batches of
|
|
37
|
+
* {@link SAP_ENTRIES_BATCH_SIZE}; an entry a batch doesn't return is checked on its own, as are the entries
|
|
38
|
+
* of a batch that can't be read (e.g. rows too wide).
|
|
39
|
+
*/
|
|
40
|
+
function checkTableEntries(table, entries, onChecked) {
|
|
41
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
const status = new Array(entries.length);
|
|
43
|
+
const batches = new Map();
|
|
44
|
+
const singles = [];
|
|
45
|
+
entries.forEach((tableEntry, index) => {
|
|
46
|
+
let conditions;
|
|
47
|
+
try {
|
|
48
|
+
conditions = (0, manifest_1.getSapEntryConditions)(tableEntry);
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
throw entryError(table, tableEntry, e);
|
|
52
|
+
}
|
|
53
|
+
if (conditions.some(condition => condition.length > BATCHED_CONDITION_MAX_LENGTH)) {
|
|
54
|
+
singles.push(index);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const values = new Map(Object.keys(tableEntry).map(field => [field.trim().toUpperCase(), tableEntry[field].toString()]));
|
|
58
|
+
const fieldsKey = Array.from(values.keys()).sort().join(',');
|
|
59
|
+
if (!batches.has(fieldsKey)) {
|
|
60
|
+
batches.set(fieldsKey, []);
|
|
61
|
+
}
|
|
62
|
+
batches.get(fieldsKey).push({ index, values, conditions });
|
|
63
|
+
});
|
|
64
|
+
for (const [fieldsKey, group] of batches) {
|
|
65
|
+
const fields = fieldsKey.split(',');
|
|
66
|
+
// readTable returns camel-cased field names
|
|
67
|
+
const rowKeys = fields.map(field => Object.keys((0, commons_1.normalize)({ [field]: '' }))[0]);
|
|
68
|
+
for (let i = 0; i < group.length; i += exports.SAP_ENTRIES_BATCH_SIZE) {
|
|
69
|
+
const batch = group.slice(i, i + exports.SAP_ENTRIES_BATCH_SIZE);
|
|
70
|
+
let rows;
|
|
71
|
+
try {
|
|
72
|
+
rows = yield systemConnector_1.SystemConnector.readTable(table.trim().toUpperCase(), fields.map(fieldName => ({ fieldName })), batch.map(o => `( ${o.conditions.join(' AND ')} )`).join(' OR '));
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
trm_commons_1.Logger.log(`Batch read of ${batch.length} entries in ${table} failed, checking them one by one: ${e}`, true);
|
|
76
|
+
singles.push(...batch.map(o => o.index));
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
for (const o of batch) {
|
|
80
|
+
const found = rows.some(row => fields.every((field, f) => { var _a; return `${(_a = row[rowKeys[f]]) !== null && _a !== void 0 ? _a : ''}`.trim() === o.values.get(field).trim(); }));
|
|
81
|
+
if (found) {
|
|
82
|
+
status[o.index] = true;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
// values the database matches but that read differently (e.g. numbers) are confirmed on their own
|
|
86
|
+
singles.push(o.index);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
onChecked(batch.length - batch.filter(o => !status[o.index]).length);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
for (const index of singles) {
|
|
93
|
+
status[index] = yield checkEntry(table, entries[index]);
|
|
94
|
+
onChecked(1);
|
|
95
|
+
}
|
|
96
|
+
return status;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
15
99
|
/**
|
|
16
100
|
* Workflow step that queries required SAP tables and builds a per-entry status report.
|
|
17
101
|
*
|
|
@@ -60,7 +144,11 @@ exports.analyze = {
|
|
|
60
144
|
return;
|
|
61
145
|
}
|
|
62
146
|
//2- check entries
|
|
147
|
+
let checked = 0;
|
|
148
|
+
const progress = (table) => trm_commons_1.Logger.loading(`Checking ${entriesCount} SAP entries: ${Math.floor(checked * 100 / entriesCount)}% (${table.trim()})...`);
|
|
63
149
|
for (const table of Object.keys(context.output.sapEntries)) {
|
|
150
|
+
const requiredEntries = context.output.sapEntries[table];
|
|
151
|
+
progress(table);
|
|
64
152
|
var tableExists = false;
|
|
65
153
|
try {
|
|
66
154
|
for (const object of ['TABL', 'VIEW']) {
|
|
@@ -80,30 +168,26 @@ exports.analyze = {
|
|
|
80
168
|
}
|
|
81
169
|
if (!tableExists) {
|
|
82
170
|
context.runtime.missingTables.push(table);
|
|
83
|
-
|
|
171
|
+
requiredEntries.forEach(tableEntry => {
|
|
84
172
|
context.runtime.entriesStatus.bad.push({
|
|
85
173
|
table,
|
|
86
174
|
tableEntry
|
|
87
175
|
});
|
|
88
176
|
});
|
|
89
|
-
|
|
177
|
+
checked += requiredEntries.length;
|
|
178
|
+
trm_commons_1.Logger.error(`Required ${requiredEntries.length} entries in ${table}, but table or view was not found`, { debug: !context.rawInput.printOptions.information, important: true });
|
|
90
179
|
}
|
|
91
180
|
else {
|
|
92
181
|
var printTableHead = ['Table name'];
|
|
93
182
|
var printTableData = [];
|
|
94
|
-
var tableData;
|
|
95
183
|
printTableHead = printTableHead.concat(tableFields.find(o => o.tableName === table).fields);
|
|
96
184
|
printTableHead.push('Status');
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
catch (e) {
|
|
104
|
-
const reason = e instanceof Error ? e.message : String(e);
|
|
105
|
-
throw new Error(`Unable to check required SAP entry ${JSON.stringify(tableEntry)} in table "${table}": ${reason}`);
|
|
106
|
-
}
|
|
185
|
+
const statuses = yield checkTableEntries(table, requiredEntries, count => {
|
|
186
|
+
checked += count;
|
|
187
|
+
progress(table);
|
|
188
|
+
});
|
|
189
|
+
requiredEntries.forEach((tableEntry, index) => {
|
|
190
|
+
const exists = statuses[index];
|
|
107
191
|
if (exists) {
|
|
108
192
|
context.runtime.entriesStatus.good.push({
|
|
109
193
|
table,
|
|
@@ -117,10 +201,8 @@ exports.analyze = {
|
|
|
117
201
|
});
|
|
118
202
|
}
|
|
119
203
|
const entryStatus = exists ? `OK` : `NOT FOUND`;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
printTableData.push(tableData);
|
|
123
|
-
}
|
|
204
|
+
printTableData.push([table, ...printTableHead.slice(1, -1).map(field => { var _a; return (_a = tableEntry[field]) !== null && _a !== void 0 ? _a : ''; }), entryStatus]);
|
|
205
|
+
});
|
|
124
206
|
logTable.push({
|
|
125
207
|
header: printTableHead,
|
|
126
208
|
data: printTableData
|
|
@@ -27,7 +27,7 @@ exports.setSystemPackages = {
|
|
|
27
27
|
context.rawInput.contextData = {};
|
|
28
28
|
}
|
|
29
29
|
if (context.rawInput.contextData.systemPackages === undefined) {
|
|
30
|
-
trm_commons_1.Logger.loading(`Reading system
|
|
30
|
+
trm_commons_1.Logger.loading(`Reading system packages...`);
|
|
31
31
|
context.rawInput.contextData.systemPackages = yield systemConnector_1.SystemConnector.getInstalledPackages(true, true);
|
|
32
32
|
}
|
|
33
33
|
})
|
|
@@ -15,9 +15,10 @@ export declare function objectLockResource(object: {
|
|
|
15
15
|
/** One non-expiring SAP lock owner; close only after workflow rollback has finished. */
|
|
16
16
|
export declare class ActionLockScope {
|
|
17
17
|
private readonly actionName;
|
|
18
|
+
private readonly enabled;
|
|
18
19
|
private readonly ownerToken;
|
|
19
20
|
private readonly held;
|
|
20
|
-
constructor(actionName: string);
|
|
21
|
+
constructor(actionName: string, enabled?: boolean);
|
|
21
22
|
acquire(resources: LockResource[]): Promise<void>;
|
|
22
23
|
release(): Promise<void>;
|
|
23
24
|
}
|
|
@@ -42,13 +42,17 @@ function objectLockResource(object) {
|
|
|
42
42
|
}
|
|
43
43
|
/** One non-expiring SAP lock owner; close only after workflow rollback has finished. */
|
|
44
44
|
class ActionLockScope {
|
|
45
|
-
constructor(actionName) {
|
|
45
|
+
constructor(actionName, enabled = true) {
|
|
46
46
|
this.actionName = actionName;
|
|
47
|
+
this.enabled = enabled;
|
|
47
48
|
this.ownerToken = (0, crypto_1.randomUUID)().replace(/-/g, "").toUpperCase();
|
|
48
49
|
this.held = new Map();
|
|
49
50
|
}
|
|
50
51
|
acquire(resources) {
|
|
51
52
|
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
+
if (!this.enabled) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
52
56
|
const requested = resources.map(actionLockKey);
|
|
53
57
|
const fresh = requested.filter(key => !this.held.has(`${key.resourceType}:${key.resourceHash}`));
|
|
54
58
|
if (fresh.length === 0) {
|
|
@@ -82,6 +86,9 @@ class ActionLockScope {
|
|
|
82
86
|
}
|
|
83
87
|
release() {
|
|
84
88
|
return __awaiter(this, void 0, void 0, function* () {
|
|
89
|
+
if (!this.enabled) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
85
92
|
if (this.held.size === 0) {
|
|
86
93
|
return;
|
|
87
94
|
}
|
|
@@ -24,3 +24,4 @@ __exportStar(require("./installedDependency"), exports);
|
|
|
24
24
|
__exportStar(require("./installRegistry"), exports);
|
|
25
25
|
__exportStar(require("./dirtyEntries"), exports);
|
|
26
26
|
__exportStar(require("./owningInstallation"), exports);
|
|
27
|
+
__exportStar(require("./trmBackend"), exports);
|
|
@@ -41,6 +41,8 @@ export interface PackageCleanupRevert {
|
|
|
41
41
|
/** Minimum workflow context needed to remove an installed release from the target system. */
|
|
42
42
|
export interface PackageCleanupContext {
|
|
43
43
|
lockScope?: ActionLockScope;
|
|
44
|
+
/** Only the APIs of a backend older than this trm-core can be used: no test imports. */
|
|
45
|
+
legacyBackend?: boolean;
|
|
44
46
|
rawInput: {
|
|
45
47
|
packageData: {
|
|
46
48
|
name: string;
|
|
@@ -74,6 +76,12 @@ export type PackageCleanupTarget = {
|
|
|
74
76
|
incomingObjects: TADIR[];
|
|
75
77
|
/** SAP packages that hold the incoming release. Empty when uninstalling. */
|
|
76
78
|
keptDevclasses: string[];
|
|
79
|
+
/**
|
|
80
|
+
* When `true`, the incoming release is already imported: only the installed objects it doesn't
|
|
81
|
+
* ship are deleted. Otherwise every installed object is deleted before the import, except the
|
|
82
|
+
* tables the incoming release ships again.
|
|
83
|
+
*/
|
|
84
|
+
incomingImported?: boolean;
|
|
77
85
|
/** Logger/Inquirer prefix shown while the cleanup runs. */
|
|
78
86
|
prefix: string;
|
|
79
87
|
/** Action name used in abort messages, e.g. `Update`. */
|
|
@@ -402,7 +402,8 @@ function getCustomizingSources(context, target) {
|
|
|
402
402
|
if (sources.length === 0) {
|
|
403
403
|
return [];
|
|
404
404
|
}
|
|
405
|
-
|
|
405
|
+
// After the import, deleting by key would also delete the rows the incoming release has just written.
|
|
406
|
+
if (target.keepCustomizing || target.incomingImported) {
|
|
406
407
|
trm_commons_1.Logger.warning(`Keeping customizing of ${sources.length} transports (${sources.join(', ')}).`, { important: true });
|
|
407
408
|
return [];
|
|
408
409
|
}
|
|
@@ -513,7 +514,10 @@ function createStagingPackage(context, namespace) {
|
|
|
513
514
|
ctext: 'TRM upgrade cleanup',
|
|
514
515
|
as4user: systemConnector_1.SystemConnector.getLogonUser(),
|
|
515
516
|
dlvunit: 'HOME',
|
|
516
|
-
pdevclass: yield systemConnector_1.SystemConnector.getDefaultTransportLayer()
|
|
517
|
+
pdevclass: yield systemConnector_1.SystemConnector.getDefaultTransportLayer(),
|
|
518
|
+
// The package immediately receives transported objects. Some systems do not infer
|
|
519
|
+
// change recording from the transport layer when CREATE_PACKAGE is called directly.
|
|
520
|
+
korrflag: 'X'
|
|
517
521
|
});
|
|
518
522
|
yield systemConnector_1.SystemConnector.tadirInterface({
|
|
519
523
|
pgmid: 'R3TR', object: 'DEVC', objName: stagingDevclass,
|
|
@@ -544,18 +548,46 @@ function cleanupInstalledPackage(context, target) {
|
|
|
544
548
|
target: systemConnector_1.SystemConnector.getDest()
|
|
545
549
|
});
|
|
546
550
|
context.revert.updateCleanupTransport = dummy;
|
|
547
|
-
const
|
|
551
|
+
const installedTransport = yield readInstalledTransportObjects(installed);
|
|
552
|
+
const previousNamespaces = installedTransport.previousNamespaces;
|
|
553
|
+
const previousTransportObjects = installedTransport.previousTransportObjects;
|
|
548
554
|
const incomingObjects = target.incomingObjects;
|
|
555
|
+
const currentDevclasses = new Set(target.keptDevclasses.map(normalize));
|
|
556
|
+
const deletedKeys = new Set((context.runtime.deletedObjects || []).map(objectKey));
|
|
557
|
+
const { previousDevclasses, installedDevclasses, packageParents, otherInstallations, isOtherInstallation } = yield readInstallationTree(installed, context.runtime.previousInstallPackages, previousTransportObjects, context.runtime.deletedObjects || [], ((_a = context.rawInput.contextData) === null || _a === void 0 ? void 0 : _a.systemPackages) || []);
|
|
558
|
+
// After the import, the previous release is also what its SAP packages still contain beyond the
|
|
559
|
+
// incoming release: an installation without transport (e.g. trm-server/trm-rest installed with
|
|
560
|
+
// abapGit) or leftovers of an earlier upgrade.
|
|
561
|
+
if (target.incomingImported) {
|
|
562
|
+
const previousKeys = new Set(previousTransportObjects.map(objectKey));
|
|
563
|
+
let added = 0;
|
|
564
|
+
for (const devclass of previousDevclasses.values()) {
|
|
565
|
+
if (isOtherInstallation(devclass)) {
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
for (const object of yield systemConnector_1.SystemConnector.getDevclassObjects(devclass, false)) {
|
|
569
|
+
if (!isDevclass(object) && !isNamespace(object) && !deletedKeys.has(objectKey(object)) && !previousKeys.has(objectKey(object))) {
|
|
570
|
+
previousKeys.add(objectKey(object));
|
|
571
|
+
previousTransportObjects.push({ pgmid: object.pgmid, object: object.object, objName: object.objName });
|
|
572
|
+
added++;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
trm_commons_1.Logger.log(`${added} objects of the previous release found in its SAP packages only`, true);
|
|
577
|
+
}
|
|
549
578
|
// Tables shipped again by the new release are adjusted by its import instead of
|
|
550
579
|
// being dropped and re-created, which would lose their data.
|
|
551
580
|
const incomingKeys = new Set(incomingObjects.map(objectKey));
|
|
581
|
+
// Already imported, the objects of the incoming release are simply kept.
|
|
582
|
+
const keptIncoming = target.incomingImported ? previousTransportObjects.filter(object => incomingKeys.has(objectKey(object))) : [];
|
|
583
|
+
const keptIncomingKeys = new Set(keptIncoming.map(objectKey));
|
|
584
|
+
if (keptIncoming.length > 0) {
|
|
585
|
+
trm_commons_1.Logger.log(`Keeping ${keptIncoming.length} objects shipped again by the imported release`, true);
|
|
586
|
+
}
|
|
552
587
|
const retainedTables = previousTransportObjects.filter(object => normalize(object.pgmid) === 'R3TR' && normalize(object.object) === 'TABL'
|
|
553
|
-
&& incomingKeys.has(objectKey(object)));
|
|
588
|
+
&& incomingKeys.has(objectKey(object)) && !keptIncomingKeys.has(objectKey(object)));
|
|
554
589
|
const retainedKeys = new Set(retainedTables.map(objectKey));
|
|
555
590
|
retainedTables.forEach(object => trm_commons_1.Logger.log(`Keeping table ${object.objName} (present in new release)`, true));
|
|
556
|
-
const currentDevclasses = new Set(target.keptDevclasses.map(normalize));
|
|
557
|
-
const deletedKeys = new Set((context.runtime.deletedObjects || []).map(objectKey));
|
|
558
|
-
const { previousDevclasses, installedDevclasses, packageParents, otherInstallations, isOtherInstallation } = yield readInstallationTree(installed, context.runtime.previousInstallPackages, previousTransportObjects, context.runtime.deletedObjects || [], ((_a = context.rawInput.contextData) === null || _a === void 0 ? void 0 : _a.systemPackages) || []);
|
|
559
591
|
const generatedDevclasses = new Set(context.revert.sapPackages.map(normalize));
|
|
560
592
|
otherInstallations.forEach(pkg => trm_commons_1.Logger.log(`Ignoring SAP package ${pkg.getDevclass()}: TRM package ${pkg.packageName} is installed there`, true));
|
|
561
593
|
// Local additions remain cleanup candidates even when reused as incoming targets.
|
|
@@ -687,7 +719,7 @@ function cleanupInstalledPackage(context, target) {
|
|
|
687
719
|
}
|
|
688
720
|
// Installed objects moved to a package outside this installation (customer development)
|
|
689
721
|
// are kept unless confirmed; those now in another TRM package belong to it and are kept.
|
|
690
|
-
const relocatedObjects = yield readRelocatedObjects(previousTransportObjects.filter(object => !retainedKeys.has(objectKey(object))), { previousDevclasses, isOtherInstallation });
|
|
722
|
+
const relocatedObjects = yield readRelocatedObjects(previousTransportObjects.filter(object => !retainedKeys.has(objectKey(object)) && !keptIncomingKeys.has(objectKey(object))), { previousDevclasses, isOtherInstallation });
|
|
691
723
|
const movedKeys = new Set();
|
|
692
724
|
const movedObjects = [];
|
|
693
725
|
for (const object of relocatedObjects) {
|
|
@@ -743,13 +775,30 @@ function cleanupInstalledPackage(context, target) {
|
|
|
743
775
|
// SAP packages the incoming release keeps are never deleted: a renamed installation has already
|
|
744
776
|
// generated them (generate-devclass), and the import of the others updates them in place.
|
|
745
777
|
const deletionObjects = new Map([...previousTransportObjects, ...additionalObjects]
|
|
746
|
-
.filter(object => !retainedKeys.has(objectKey(object)) && !movedKeys.has(objectKey(object)))
|
|
778
|
+
.filter(object => !retainedKeys.has(objectKey(object)) && !movedKeys.has(objectKey(object)) && !keptIncomingKeys.has(objectKey(object)))
|
|
747
779
|
.filter(object => !(isDevclass(object) && currentDevclasses.has(normalize(object.objName))))
|
|
748
780
|
.map(object => [objectKey(object), object]));
|
|
781
|
+
// Only objects that exist can be transported: the installed transport may list objects this system
|
|
782
|
+
// never had or already lost (e.g. a system copy).
|
|
783
|
+
const checkedObjects = Array.from(deletionObjects.values()).filter(object => normalize(object.pgmid) === 'R3TR' && !isNamespace(object));
|
|
784
|
+
if (checkedObjects.length > 0) {
|
|
785
|
+
const existing = new Set((yield systemConnector_1.SystemConnector.getExistingObjects(checkedObjects.map(object => ({
|
|
786
|
+
pgmid: object.pgmid, object: object.object, objName: object.objName, devclass: ''
|
|
787
|
+
})))).map(objectKey));
|
|
788
|
+
checkedObjects.filter(object => !existing.has(objectKey(object))).forEach(object => {
|
|
789
|
+
trm_commons_1.Logger.log(`Skipping ${object.pgmid} ${object.object} ${object.objName}: not on ${systemConnector_1.SystemConnector.getDest()}, nothing to delete`, true);
|
|
790
|
+
deletionObjects.delete(objectKey(object));
|
|
791
|
+
});
|
|
792
|
+
}
|
|
749
793
|
// TRM comment rows of the installed transport are not objects to delete.
|
|
750
794
|
const hasObjectsToDelete = Array.from(deletionObjects.values()).some(object => normalize(object.pgmid) !== '*');
|
|
751
795
|
if (!hasObjectsToDelete && retainedTables.length === 0 && customizingSources.length === 0) {
|
|
752
|
-
|
|
796
|
+
if (target.incomingImported) {
|
|
797
|
+
trm_commons_1.Logger.log(`The imported release ships every object of the previous one: nothing to delete`, true);
|
|
798
|
+
}
|
|
799
|
+
else {
|
|
800
|
+
trm_commons_1.Logger.warning(`Nothing to delete for package ${context.rawInput.packageData.name}: deletion transport was not generated. Manual cleanup of previous release install might be necessary.`, { important: true });
|
|
801
|
+
}
|
|
753
802
|
try {
|
|
754
803
|
if (yield dummy.canBeDeleted()) {
|
|
755
804
|
yield dummy.delete();
|
|
@@ -50,10 +50,17 @@ function releaseDeletionTransport(deletionTransport_1, registry_2, context_1) {
|
|
|
50
50
|
trTarget: systemConnector_1.SystemConnector.getDest()
|
|
51
51
|
});
|
|
52
52
|
//3- import transport
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (
|
|
56
|
-
|
|
53
|
+
// A legacy backend has no test import; in the same process the RFC metadata of its import
|
|
54
|
+
// function stays cached even after the upgrade is imported.
|
|
55
|
+
if (context.legacyBackend) {
|
|
56
|
+
trm_commons_1.Logger.log(`Skipping test import of ${deletionTransport.trkorr}: legacy TRM backend`, true);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
trm_commons_1.Logger.loading(`Testing import...`);
|
|
60
|
+
const testRc = yield context.runtime.dele.import(true);
|
|
61
|
+
if (testRc < 0 || testRc > 8) {
|
|
62
|
+
throw new Error(`Test import of deletion transport failed: check logs.`);
|
|
63
|
+
}
|
|
57
64
|
}
|
|
58
65
|
trm_commons_1.Logger.loading(`Importing ${deletionTransport.trkorr}`, true);
|
|
59
66
|
// Mark before the mutating await: the import may change objects and still fail.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AbstractRegistry } from "../../../registry";
|
|
2
|
+
/**
|
|
3
|
+
* First trm-server and trm-rest releases with the backend APIs this trm-core relies on
|
|
4
|
+
* (action locks, batch import, install transports, test imports, transport object locks).
|
|
5
|
+
*/
|
|
6
|
+
export declare const TRM_SERVER_API_VERSION = "7.0.0";
|
|
7
|
+
export declare const TRM_REST_API_VERSION = "6.0.0";
|
|
8
|
+
/**
|
|
9
|
+
* Permanent TADIR transport of the public trm-server and trm-rest: the same request in every release.
|
|
10
|
+
* The registry doesn't deliver it through its transport download: it's read from the release artifact.
|
|
11
|
+
*/
|
|
12
|
+
export declare const TRM_FIXED_TADIR_TRANSPORTS: Readonly<Record<string, string>>;
|
|
13
|
+
/** Whether a package is the public trm-server or trm-rest: the TRM backend itself. */
|
|
14
|
+
export declare function isTrmBackendPackage(registry: AbstractRegistry, name: string): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Whether the connected TRM backend predates {@link TRM_SERVER_API_VERSION} / {@link TRM_REST_API_VERSION}.
|
|
17
|
+
* Such a backend can only upgrade itself, with the APIs of its own generation: the newer APIs exist
|
|
18
|
+
* only once the upgrade is imported.
|
|
19
|
+
*/
|
|
20
|
+
export declare function isLegacyTrmBackend(): Promise<boolean>;
|
|
@@ -0,0 +1,64 @@
|
|
|
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.TRM_FIXED_TADIR_TRANSPORTS = exports.TRM_REST_API_VERSION = exports.TRM_SERVER_API_VERSION = void 0;
|
|
13
|
+
exports.isTrmBackendPackage = isTrmBackendPackage;
|
|
14
|
+
exports.isLegacyTrmBackend = isLegacyTrmBackend;
|
|
15
|
+
const semver_1 = require("semver");
|
|
16
|
+
const trm_commons_1 = require("trm-commons");
|
|
17
|
+
const registry_1 = require("../../../registry");
|
|
18
|
+
const systemConnector_1 = require("../../../systemConnector");
|
|
19
|
+
/**
|
|
20
|
+
* First trm-server and trm-rest releases with the backend APIs this trm-core relies on
|
|
21
|
+
* (action locks, batch import, install transports, test imports, transport object locks).
|
|
22
|
+
*/
|
|
23
|
+
exports.TRM_SERVER_API_VERSION = '7.0.0';
|
|
24
|
+
exports.TRM_REST_API_VERSION = '6.0.0';
|
|
25
|
+
/**
|
|
26
|
+
* Permanent TADIR transport of the public trm-server and trm-rest: the same request in every release.
|
|
27
|
+
* The registry doesn't deliver it through its transport download: it's read from the release artifact.
|
|
28
|
+
*/
|
|
29
|
+
exports.TRM_FIXED_TADIR_TRANSPORTS = {
|
|
30
|
+
'trm-server': 'TRMK900000',
|
|
31
|
+
'trm-rest': 'TRMK900001'
|
|
32
|
+
};
|
|
33
|
+
/** Whether a package is the public trm-server or trm-rest: the TRM backend itself. */
|
|
34
|
+
function isTrmBackendPackage(registry, name) {
|
|
35
|
+
return registry.getRegistryType() === registry_1.RegistryType.PUBLIC
|
|
36
|
+
&& [systemConnector_1.TRM_SERVER_PACKAGE_NAME, systemConnector_1.TRM_REST_PACKAGE_NAME].includes(name.trim().toLowerCase());
|
|
37
|
+
}
|
|
38
|
+
function installedVersion(read) {
|
|
39
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
40
|
+
var _a;
|
|
41
|
+
try {
|
|
42
|
+
return (_a = (yield read()).manifest) === null || _a === void 0 ? void 0 : _a.get().version;
|
|
43
|
+
}
|
|
44
|
+
catch (_b) {
|
|
45
|
+
// not installed
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Whether the connected TRM backend predates {@link TRM_SERVER_API_VERSION} / {@link TRM_REST_API_VERSION}.
|
|
52
|
+
* Such a backend can only upgrade itself, with the APIs of its own generation: the newer APIs exist
|
|
53
|
+
* only once the upgrade is imported.
|
|
54
|
+
*/
|
|
55
|
+
function isLegacyTrmBackend() {
|
|
56
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
57
|
+
const isLegacy = (version, apiVersion) => !!version && (!(0, semver_1.valid)(version) || (0, semver_1.lt)(version, apiVersion));
|
|
58
|
+
const serverVersion = yield installedVersion(() => systemConnector_1.SystemConnector.getTrmServerPackage());
|
|
59
|
+
const restVersion = yield installedVersion(() => systemConnector_1.SystemConnector.getTrmRestPackage());
|
|
60
|
+
const legacy = isLegacy(serverVersion, exports.TRM_SERVER_API_VERSION) || isLegacy(restVersion, exports.TRM_REST_API_VERSION);
|
|
61
|
+
trm_commons_1.Logger.log(`TRM backend: trm-server ${serverVersion || '-'}, trm-rest ${restVersion || '-'}${legacy ? ' (legacy APIs)' : ''}`, true);
|
|
62
|
+
return legacy;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
@@ -19,6 +19,7 @@ const trm_commons_2 = require("trm-commons");
|
|
|
19
19
|
const systemConnector_1 = require("../../systemConnector");
|
|
20
20
|
const commons_1 = require("../../commons");
|
|
21
21
|
const utils_1 = require("../commons/utils");
|
|
22
|
+
const crypto_1 = require("crypto");
|
|
22
23
|
/** Throws when any of the objects is locked in a SAP transport request. */
|
|
23
24
|
function checkObjectsLocks(e071) {
|
|
24
25
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -205,6 +206,11 @@ exports.checkTransports = {
|
|
|
205
206
|
if (skippedCust.includes(transport.trkorr)) {
|
|
206
207
|
readEntries = false;
|
|
207
208
|
}
|
|
209
|
+
// the fixed TADIR transport of trm-server/trm-rest only comes with the artifact (below)
|
|
210
|
+
if ((context.runtime.isTrmServer || context.runtime.isTrmRest) && transport.type === transport_1.TrmTransportIdentifier.TADIR
|
|
211
|
+
&& utils_1.TRM_FIXED_TADIR_TRANSPORTS[context.runtime.package.data.manifest.name] === transport.trkorr) {
|
|
212
|
+
readEntries = false;
|
|
213
|
+
}
|
|
208
214
|
if (readEntries) {
|
|
209
215
|
const entries = yield context.rawInput.packageData.registry.transportEntries(context.runtime.package.data.manifest.name, context.runtime.package.data.manifest.version, transport.trkorr);
|
|
210
216
|
const binaries = {
|
|
@@ -235,6 +241,29 @@ exports.checkTransports = {
|
|
|
235
241
|
}
|
|
236
242
|
}
|
|
237
243
|
}
|
|
244
|
+
// The registry metadata can omit a transport of the release, and the fixed TADIR transport of
|
|
245
|
+
// trm-server and trm-rest is never downloaded alone: read them from the artifact, once its integrity is verified.
|
|
246
|
+
if (!context.runtime.transports.devc || !context.runtime.transports.tadir) {
|
|
247
|
+
trm_commons_1.Logger.log(`No ${!context.runtime.transports.devc ? 'DEVC' : 'TADIR'} transport to download from the registry: reading the release artifact`, true);
|
|
248
|
+
trm_commons_1.Logger.loading(`Reading package transports from the release artifact...`);
|
|
249
|
+
const artifact = yield context.rawInput.packageData.registry.downloadArtifact(context.runtime.package.data.manifest.name, context.runtime.package.data.manifest.version);
|
|
250
|
+
const expectedChecksum = context.runtime.package.data.checksum;
|
|
251
|
+
const checksum = (0, crypto_1.createHash)('sha512').update(artifact.binary).digest('base64');
|
|
252
|
+
if (!expectedChecksum || checksum !== expectedChecksum) {
|
|
253
|
+
trm_commons_1.Logger.error(`SECURITY ISSUE! Release artifact integrity does NOT match the registry SHA ${expectedChecksum}: received ${checksum}`, { important: true });
|
|
254
|
+
throw new Error(`Cannot continue due to security issues.`);
|
|
255
|
+
}
|
|
256
|
+
for (const binary of yield artifact.getTransportBinaries()) {
|
|
257
|
+
if (binary.type === transport_1.TrmTransportIdentifier.DEVC && !context.runtime.transports.devc) {
|
|
258
|
+
context.runtime.transports.devc = { binaries: binary };
|
|
259
|
+
mergeEntries(binary.entries);
|
|
260
|
+
}
|
|
261
|
+
else if (binary.type === transport_1.TrmTransportIdentifier.TADIR && !context.runtime.transports.tadir) {
|
|
262
|
+
context.runtime.transports.tadir = { binaries: binary };
|
|
263
|
+
mergeEntries(binary.entries);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
238
267
|
}
|
|
239
268
|
//4- check devc and tadir existance
|
|
240
269
|
if (!context.runtime.transports.devc) {
|
|
@@ -272,6 +301,7 @@ exports.checkTransports = {
|
|
|
272
301
|
}
|
|
273
302
|
//8- check objects existance
|
|
274
303
|
const checkedTadir = existenceCheckObjects(mergedTADIR);
|
|
304
|
+
trm_commons_1.Logger.loading(`Checking whether ${checkedTadir.length} package object${checkedTadir.length === 1 ? '' : 's'} already exist${checkedTadir.length === 1 ? 's' : ''}...`);
|
|
275
305
|
const existingObjects = checkedTadir.length > 0 ? yield findExistingObjects(context, checkedTadir) : [];
|
|
276
306
|
// lock-resources checks again, once locked, that no other object appeared
|
|
277
307
|
context.runtime.existingObjects = existingObjects;
|
|
@@ -279,9 +309,18 @@ exports.checkTransports = {
|
|
|
279
309
|
//if updating and existing object is part of the package (devclass in hierarchy) ok, else throw error
|
|
280
310
|
if (existingObjects.length > 0) {
|
|
281
311
|
// Name the installed TRM package that contains each existing object: it can't be taken over silently.
|
|
312
|
+
trm_commons_1.Logger.loading(`Checking ownership of ${existingObjects.length} existing object${existingObjects.length === 1 ? '' : 's'}...`);
|
|
282
313
|
const owners = new Map();
|
|
314
|
+
// objects share few SAP packages: look each one up once
|
|
315
|
+
const packageOwners = new Map();
|
|
283
316
|
for (const o of existingObjects) {
|
|
284
|
-
|
|
317
|
+
if (!o.devclass) {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
if (!packageOwners.has(o.devclass)) {
|
|
321
|
+
packageOwners.set(o.devclass, (0, utils_1.getOwningInstallation)(o.devclass, context.rawInput.contextData.systemPackages || [], context.runtime.update));
|
|
322
|
+
}
|
|
323
|
+
const owner = yield packageOwners.get(o.devclass);
|
|
285
324
|
if (owner) {
|
|
286
325
|
owners.set(o, owner.packageName);
|
|
287
326
|
}
|
|
@@ -125,7 +125,10 @@ exports.generateLandscapeTransport = {
|
|
|
125
125
|
//8- lock objects
|
|
126
126
|
//what is lockable gets locked here
|
|
127
127
|
//all workbench should be lockable here
|
|
128
|
-
|
|
128
|
+
//a legacy backend has no API for it
|
|
129
|
+
if (!context.legacyBackend) {
|
|
130
|
+
yield context.output.transport.lock();
|
|
131
|
+
}
|
|
129
132
|
}),
|
|
130
133
|
revert: (context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
131
134
|
if (context.output.transport && (yield context.output.transport.canBeDeleted())) {
|
|
@@ -18,3 +18,10 @@ export declare function keepsInstalledCustomizing(context: InstallWorkflowContex
|
|
|
18
18
|
* release without an incoming one.
|
|
19
19
|
*/
|
|
20
20
|
export declare const generateUpdateTransport: Step<InstallWorkflowContext>;
|
|
21
|
+
/**
|
|
22
|
+
* Workflow step that cleans up an upgrade of trm-server or trm-rest, last in the install (see
|
|
23
|
+
* {@link generateUpdateTransport}); every other package is cleaned up before its import. The installed
|
|
24
|
+
* release runs the install: deleting its objects first would remove the APIs the install still calls.
|
|
25
|
+
* Once the upgrade is complete, only the objects the new release doesn't ship are deleted, with its APIs.
|
|
26
|
+
*/
|
|
27
|
+
export declare const generatePostImportUpdateTransport: Step<InstallWorkflowContext>;
|
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.generateUpdateTransport = exports.deleteTemporaryCleanupPackages = void 0;
|
|
12
|
+
exports.generatePostImportUpdateTransport = exports.generateUpdateTransport = exports.deleteTemporaryCleanupPackages = void 0;
|
|
13
13
|
exports.keepsInstalledCustomizing = keepsInstalledCustomizing;
|
|
14
14
|
const trm_commons_1 = require("trm-commons");
|
|
15
15
|
const stopWarning_1 = require("../stopWarning");
|
|
@@ -60,6 +60,11 @@ function keepsInstalledCustomizing(context) {
|
|
|
60
60
|
exports.generateUpdateTransport = {
|
|
61
61
|
name: 'generate-update-transport',
|
|
62
62
|
filter: (context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
63
|
+
if (context.runtime.selfUpdate) {
|
|
64
|
+
// The installed release runs this install: its objects are cleaned up after the import
|
|
65
|
+
trm_commons_1.Logger.log(`Upgrade cleanup of ${context.rawInput.packageData.name} runs after the import`, true);
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
63
68
|
//a local (.trm) upgrade generates its deletion transport through the registry the artifact was published to
|
|
64
69
|
if (context.runtime.update) {
|
|
65
70
|
return true;
|
|
@@ -70,19 +75,78 @@ exports.generateUpdateTransport = {
|
|
|
70
75
|
}
|
|
71
76
|
}),
|
|
72
77
|
run: (context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
73
|
-
|
|
78
|
+
yield cleanupPreviousRelease(context, false);
|
|
79
|
+
}),
|
|
80
|
+
revert: revertUpdateCleanup
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Workflow step that cleans up an upgrade of trm-server or trm-rest, last in the install (see
|
|
84
|
+
* {@link generateUpdateTransport}); every other package is cleaned up before its import. The installed
|
|
85
|
+
* release runs the install: deleting its objects first would remove the APIs the install still calls.
|
|
86
|
+
* Once the upgrade is complete, only the objects the new release doesn't ship are deleted, with its APIs.
|
|
87
|
+
*/
|
|
88
|
+
exports.generatePostImportUpdateTransport = {
|
|
89
|
+
name: 'generate-post-import-update-transport',
|
|
90
|
+
filter: (context) => __awaiter(void 0, void 0, void 0, function* () { return !!context.runtime.selfUpdate; }),
|
|
91
|
+
run: (context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
92
|
+
var _a, _b, _c;
|
|
93
|
+
// The new release is installed: a failed cleanup leaves objects of the previous one, it doesn't undo the install
|
|
94
|
+
try {
|
|
95
|
+
yield cleanupPreviousRelease(context, true);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
let revertError;
|
|
99
|
+
if (!context.revert.deleImportStarted) {
|
|
100
|
+
try {
|
|
101
|
+
yield revertUpdateCleanup(context);
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
revertError = e;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const version = context.runtime.update.manifest.get().version;
|
|
108
|
+
trm_commons_1.Logger.warning(`Cleanup of ${context.rawInput.packageData.name} ${version} failed: ${error instanceof Error ? error.message : String(error)}`, { important: true });
|
|
109
|
+
if (revertError) {
|
|
110
|
+
trm_commons_1.Logger.warning(`Cleanup could not be undone either: ${revertError instanceof Error ? revertError.message : String(revertError)}`, { important: true });
|
|
111
|
+
}
|
|
112
|
+
trm_commons_1.Logger.warning(`${context.rawInput.packageData.name} ${context.runtime.package.data.manifest.version} is installed. Objects of ${version} it doesn't ship may be left on ${systemConnector_1.SystemConnector.getDest()}: delete them manually.`, { important: true });
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// The landscape transport is already released: the deletion follows it to the target
|
|
116
|
+
const targetSystem = (_b = (_a = context.rawInput.installData) === null || _a === void 0 ? void 0 : _a.landscapeTransport) === null || _b === void 0 ? void 0 : _b.targetSystem;
|
|
117
|
+
if (targetSystem && ((_c = context.output) === null || _c === void 0 ? void 0 : _c.transport) && (0, utils_1.isDeletionForwardable)(context)) {
|
|
118
|
+
try {
|
|
119
|
+
yield (0, utils_1.forwardDeletionTransport)(context, targetSystem);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
trm_commons_1.Logger.warning(`Deletion transport ${context.revert.dele.trkorr} could not be added to the ${targetSystem} import queue: ${error instanceof Error ? error.message : String(error)}. Add it in STMS after ${context.output.transport.trkorr}.`, { important: true });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}),
|
|
126
|
+
revert: (context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
127
|
+
// The imported release stays (see import-batch): objects it doesn't ship stay deleted too.
|
|
128
|
+
// A cleanup that stopped before its deletion transport is undone.
|
|
129
|
+
if (context.revert.deleImportStarted) {
|
|
130
|
+
trm_commons_1.Logger.log(`Keeping the cleanup of ${context.rawInput.packageData.name}: its imported release is not rolled back`, true);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
yield revertUpdateCleanup(context);
|
|
134
|
+
})
|
|
135
|
+
};
|
|
136
|
+
function cleanupPreviousRelease(context, incomingImported) {
|
|
137
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
138
|
+
var _a, _b, _c;
|
|
74
139
|
if (!context.runtime.stopWarningShown) {
|
|
75
140
|
context.runtime.stopWarningShown = true;
|
|
76
141
|
(0, stopWarning_1.stopWarning)('install');
|
|
77
142
|
}
|
|
78
143
|
// The cleanup deletes and re-imports the root package: read it now, so the import can put it back
|
|
79
144
|
// under the superpackage it has on this system (prepare-devc reads it after the cleanup).
|
|
80
|
-
if (((_b = (_a = context.rawInput.installData) === null || _a === void 0 ? void 0 : _a.installDevclass) === null || _b === void 0 ? void 0 : _b.keepOriginal) && !context.runtime.rootDevclassBeforeImport) {
|
|
145
|
+
if (!incomingImported && ((_b = (_a = context.rawInput.installData) === null || _a === void 0 ? void 0 : _a.installDevclass) === null || _b === void 0 ? void 0 : _b.keepOriginal) && !context.runtime.rootDevclassBeforeImport) {
|
|
81
146
|
context.runtime.rootDevclassBeforeImport = yield systemConnector_1.SystemConnector.getDevclass(context.runtime.package.hierarchy.devclass);
|
|
82
147
|
}
|
|
83
148
|
// Read the target lazily: a failure reading the previous release must still be tracked first.
|
|
84
149
|
const installDevclass = (_c = context.rawInput.installData) === null || _c === void 0 ? void 0 : _c.installDevclass;
|
|
85
|
-
const importData = (_d = context.rawInput.installData) === null || _d === void 0 ? void 0 : _d.import;
|
|
86
150
|
const keepCustomizing = keepsInstalledCustomizing(context);
|
|
87
151
|
if (keepCustomizing) {
|
|
88
152
|
const skipped = context.runtime.skippedCust || [];
|
|
@@ -97,6 +161,7 @@ exports.generateUpdateTransport = {
|
|
|
97
161
|
? flattenDevclasses(context.runtime.package.hierarchy)
|
|
98
162
|
: installDevclass.replacements.map(replacement => replacement.installDevclass);
|
|
99
163
|
},
|
|
164
|
+
incomingImported,
|
|
100
165
|
prefix: `(${transport_1.Transport.getTransportIcon()} Upgrade cleanup) `,
|
|
101
166
|
actionName: 'Update',
|
|
102
167
|
requireDeletion: false,
|
|
@@ -107,8 +172,10 @@ exports.generateUpdateTransport = {
|
|
|
107
172
|
return incomingCustomizingRows(context);
|
|
108
173
|
}
|
|
109
174
|
});
|
|
110
|
-
})
|
|
111
|
-
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
function revertUpdateCleanup(context) {
|
|
178
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
112
179
|
// Rollback of the imported objects ran first: when it failed, they are still on the system
|
|
113
180
|
// and the previous release must not be restored over them.
|
|
114
181
|
const restore = !(context.revert.cleanupImported && !context.revert.cleanupSucceeded);
|
|
@@ -118,5 +185,5 @@ exports.generateUpdateTransport = {
|
|
|
118
185
|
if (restore) {
|
|
119
186
|
yield (0, utils_1.deleteCleanupStagingPackages)(context);
|
|
120
187
|
}
|
|
121
|
-
})
|
|
122
|
-
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
Binary file
|
|
@@ -222,6 +222,11 @@ type WorkflowRuntime = {
|
|
|
222
222
|
lockedNamespaces?: string[];
|
|
223
223
|
/** Required SAP entries missing on the system: the install is aborted by check-engines, with the unmet engines. */
|
|
224
224
|
missingSapEntries?: number;
|
|
225
|
+
/**
|
|
226
|
+
* Upgrade of trm-server or trm-rest: the installed release runs the install itself. Its objects are
|
|
227
|
+
* only cleaned up after the incoming release is imported, and the import can't be rolled back.
|
|
228
|
+
*/
|
|
229
|
+
selfUpdate?: boolean;
|
|
225
230
|
};
|
|
226
231
|
/** Dependency queued for install: missing from the system, or installed in an incompatible version. */
|
|
227
232
|
export type InstallDependencyEntry = {
|
|
@@ -300,6 +305,11 @@ export type InstallActionOutput = {
|
|
|
300
305
|
/** Internal state shared by package-install workflow steps and rollback handlers. */
|
|
301
306
|
export interface InstallWorkflowContext extends IActionContext {
|
|
302
307
|
lockScope?: ActionLockScope;
|
|
308
|
+
/**
|
|
309
|
+
* trm-server/trm-rest upgrade on a backend older than the APIs this trm-core needs (see isLegacyTrmBackend):
|
|
310
|
+
* only the APIs of the installed generation are used.
|
|
311
|
+
*/
|
|
312
|
+
legacyBackend?: boolean;
|
|
303
313
|
/** Custom namespaces already locked by the parent installs of a dependency install. */
|
|
304
314
|
inheritedNamespaceLocks?: string[];
|
|
305
315
|
/** Original action input; optional groups are normalized during initialization. */
|
|
@@ -87,15 +87,18 @@ const installWorkflow = [
|
|
|
87
87
|
generateLandscapeTransport_1.generateLandscapeTransport,
|
|
88
88
|
executePostActivities_1.executePostActivities,
|
|
89
89
|
releaseLandscapeTransport_1.releaseLandscapeTransport,
|
|
90
|
-
updatePackageData_1.updatePackageData
|
|
90
|
+
updatePackageData_1.updatePackageData,
|
|
91
|
+
generateUpdateTransport_1.generatePostImportUpdateTransport
|
|
91
92
|
];
|
|
92
93
|
function runInstall(inputData_1, retainRollback_1) {
|
|
93
94
|
return __awaiter(this, arguments, void 0, function* (inputData, retainRollback, inheritedNamespaceLocks = []) {
|
|
94
95
|
var _a;
|
|
95
|
-
const lockScope = new utils_2.ActionLockScope(WORKFLOW_NAME);
|
|
96
|
-
const context = { rawInput: inputData, lockScope, inheritedNamespaceLocks };
|
|
97
96
|
//a local artifact is locked under the package it installs, as a remote install of it would be
|
|
98
97
|
const lockPackage = yield (0, utils_2.resolveInstallPackage)(inputData.packageData.registry, inputData.packageData.name);
|
|
98
|
+
// A legacy backend has no action locks: only trm-server and trm-rest can install there, to upgrade it.
|
|
99
|
+
const legacyBackend = (0, utils_2.isTrmBackendPackage)(lockPackage.registry, lockPackage.name) && (yield (0, utils_2.isLegacyTrmBackend)());
|
|
100
|
+
const lockScope = new utils_2.ActionLockScope(WORKFLOW_NAME, !legacyBackend);
|
|
101
|
+
const context = { rawInput: inputData, lockScope, inheritedNamespaceLocks, legacyBackend };
|
|
99
102
|
yield lockScope.acquire([(0, utils_2.packageLockResource)(lockPackage.registry, lockPackage.name)]);
|
|
100
103
|
const release = () => __awaiter(this, void 0, void 0, function* () {
|
|
101
104
|
var _a;
|
|
@@ -225,10 +225,16 @@ exports.init = {
|
|
|
225
225
|
//5- check if already installed
|
|
226
226
|
context.runtime.update = context.rawInput.contextData.systemPackages.find(o => manifest_1.Manifest.compare(o.manifest, new manifest_1.Manifest(context.runtime.package.data.manifest), false));
|
|
227
227
|
if (context.runtime.update) {
|
|
228
|
+
const installedVersion = context.runtime.update.manifest.get().version;
|
|
229
|
+
// trm-server and trm-rest run this install: see the selfUpdate steps
|
|
230
|
+
context.runtime.selfUpdate = context.runtime.isTrmServer || context.runtime.isTrmRest;
|
|
228
231
|
context.runtime.previousInstallPackages = yield systemConnector_1.SystemConnector.getInstallPackages(context.rawInput.packageData.name, context.runtime.installRegistry);
|
|
229
|
-
|
|
232
|
+
// A legacy backend has no install transports table: trm-server and trm-rest ship
|
|
233
|
+
// no customizing or translations, so there is nothing recorded to read.
|
|
234
|
+
if (!context.legacyBackend) {
|
|
235
|
+
context.runtime.previousInstallTransports = yield systemConnector_1.SystemConnector.getInstallTransports(context.rawInput.packageData.name, context.runtime.installRegistry);
|
|
236
|
+
}
|
|
230
237
|
const installVersion = context.runtime.package.data.manifest.version;
|
|
231
|
-
const installedVersion = context.runtime.update.manifest.get().version;
|
|
232
238
|
//transports TRM generated for the package (e.g. of a rolled back release) aren't changes made on the system
|
|
233
239
|
const dirtyEntries = yield (0, utils_1.withoutOwnTrmTransports)(context.runtime.update.getDirtyEntries(), context.runtime.update.packageName);
|
|
234
240
|
if ((0, semver_1.eq)(installVersion, installedVersion)) {
|
|
@@ -266,6 +272,10 @@ exports.init = {
|
|
|
266
272
|
trm_commons_1.Logger.warning(`${context.rawInput.packageData.name} has changes made on ${systemConnector_1.SystemConnector.getDest()} that will be overwritten!`, { important: true });
|
|
267
273
|
(0, utils_1.logDirtyEntries)(dirtyEntries);
|
|
268
274
|
}
|
|
275
|
+
if (context.runtime.isTrmServer && (0, semver_1.valid)(installedVersion) && (0, semver_1.lt)(installedVersion, utils_1.TRM_SERVER_API_VERSION) && !(0, semver_1.lt)(installVersion, utils_1.TRM_SERVER_API_VERSION)) {
|
|
276
|
+
// the install continues with the imported release: an unauthorized user can't complete it
|
|
277
|
+
trm_commons_1.Logger.warning(`trm-server ${utils_1.TRM_SERVER_API_VERSION} authorizes users with authorization object ZTRM_AUTH (ACTVT 16) instead of table /ATRM/USERS: ${systemConnector_1.SystemConnector.getLogonUser()} and the other TRM users need it.`, { important: true });
|
|
278
|
+
}
|
|
269
279
|
}
|
|
270
280
|
}
|
|
271
281
|
else {
|
|
@@ -94,10 +94,15 @@ exports.prepareCust = {
|
|
|
94
94
|
trTarget: systemConnector_1.SystemConnector.getDest()
|
|
95
95
|
});
|
|
96
96
|
//3- test import transport
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
if (context.legacyBackend) {
|
|
98
|
+
trm_commons_1.Logger.log(`Skipping test import: legacy TRM backend`, true);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
trm_commons_1.Logger.loading(`Testing import...`);
|
|
102
|
+
const testRc = yield cust.instance.import(true);
|
|
103
|
+
if (testRc < 0 || testRc > 8) {
|
|
104
|
+
throw new Error(`Test import of customizing failed: check logs.`);
|
|
105
|
+
}
|
|
101
106
|
}
|
|
102
107
|
}));
|
|
103
108
|
}
|
|
@@ -93,10 +93,15 @@ exports.prepareDevc = {
|
|
|
93
93
|
trTarget: systemConnector_1.SystemConnector.getDest()
|
|
94
94
|
});
|
|
95
95
|
//4- test import transport
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
if (context.legacyBackend) {
|
|
97
|
+
trm_commons_1.Logger.log(`Skipping test import: legacy TRM backend`, true);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
trm_commons_1.Logger.loading(`Testing import...`);
|
|
101
|
+
const testRc = yield context.runtime.transports.devc.instance.import(true);
|
|
102
|
+
if (testRc < 0 || testRc > 8) {
|
|
103
|
+
throw new Error(`Test import of SAP packages failed: check logs.`);
|
|
104
|
+
}
|
|
100
105
|
}
|
|
101
106
|
}));
|
|
102
107
|
}),
|
|
@@ -90,10 +90,15 @@ exports.prepareLang = {
|
|
|
90
90
|
trTarget: systemConnector_1.SystemConnector.getDest()
|
|
91
91
|
});
|
|
92
92
|
//3- test import transport
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
if (context.legacyBackend) {
|
|
94
|
+
trm_commons_1.Logger.log(`Skipping test import: legacy TRM backend`, true);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
trm_commons_1.Logger.loading(`Testing import...`);
|
|
98
|
+
const testRc = yield context.runtime.transports.lang.instance.import(true);
|
|
99
|
+
if (testRc < 0 || testRc > 8) {
|
|
100
|
+
throw new Error(`Test import of translations failed: check logs.`);
|
|
101
|
+
}
|
|
97
102
|
}
|
|
98
103
|
}));
|
|
99
104
|
}),
|
|
@@ -75,10 +75,15 @@ exports.prepareTadir = {
|
|
|
75
75
|
trTarget: systemConnector_1.SystemConnector.getDest()
|
|
76
76
|
});
|
|
77
77
|
//3- test import transport
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
if (context.legacyBackend) {
|
|
79
|
+
trm_commons_1.Logger.log(`Skipping test import: legacy TRM backend`, true);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
trm_commons_1.Logger.loading(`Testing import...`);
|
|
83
|
+
const testRc = yield context.runtime.transports.tadir.instance.import(true);
|
|
84
|
+
if (testRc < 0 || testRc > 8) {
|
|
85
|
+
throw new Error(`Test import of workbench failed: check logs.`);
|
|
86
|
+
}
|
|
82
87
|
}
|
|
83
88
|
}));
|
|
84
89
|
}),
|
|
@@ -58,7 +58,21 @@ exports.setInstallDevclass = {
|
|
|
58
58
|
}
|
|
59
59
|
}),
|
|
60
60
|
run: (context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
61
|
-
var _a, _b, _c;
|
|
61
|
+
var _a, _b, _c, _d;
|
|
62
|
+
// trm-server and trm-rest are installed in `$` packages (`$TRM...`), unless they are already in their
|
|
63
|
+
// original `/ATRM/` packages: then they stay there. They are never renamed otherwise.
|
|
64
|
+
if (context.runtime.isTrmServer || context.runtime.isTrmRest) {
|
|
65
|
+
const original = (((_a = context.runtime.update) === null || _a === void 0 ? void 0 : _a.getDevclass()) || '').trim().toUpperCase().startsWith('/ATRM/');
|
|
66
|
+
context.rawInput.installData.installDevclass.replacements = (0, addNamespace_1.flattenDevclasses)(context.runtime.package.hierarchy).map(originalDevclass => ({
|
|
67
|
+
originalDevclass,
|
|
68
|
+
installDevclass: original ? originalDevclass : (0, commons_1.adjustTrmServerRestDevclass)(originalDevclass)
|
|
69
|
+
}));
|
|
70
|
+
context.rawInput.installData.installDevclass.keepOriginal = context.rawInput.installData.installDevclass.replacements
|
|
71
|
+
.every(o => o.installDevclass === o.originalDevclass);
|
|
72
|
+
(0, addNamespace_1.getInstallNamespace)(context);
|
|
73
|
+
trm_commons_1.Logger.log(`${context.rawInput.packageData.name} SAP packages: ${context.rawInput.installData.installDevclass.replacements.map(o => `${o.originalDevclass} -> ${o.installDevclass}`).join(', ')}`, true);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
62
76
|
//1- find already defined replacements in system
|
|
63
77
|
if (context.rawInput.installData.installDevclass.replacements.length <= 0) {
|
|
64
78
|
//no input replacements = get from the trm table devclass replacements the corresponding name
|
|
@@ -78,7 +92,7 @@ exports.setInstallDevclass = {
|
|
|
78
92
|
//the release root can have a new original name (e.g. moved to a namespace): a renamed installation keeps its root
|
|
79
93
|
//package, matched by position, so the installation stays where the customer put it
|
|
80
94
|
const releaseRoot = context.runtime.package.hierarchy.devclass;
|
|
81
|
-
const installedRoot = (
|
|
95
|
+
const installedRoot = (_b = context.runtime.update) === null || _b === void 0 ? void 0 : _b.getDevclass();
|
|
82
96
|
if (installedRoot && !context.rawInput.installData.installDevclass.replacements.some(o => o.originalDevclass === releaseRoot)) {
|
|
83
97
|
const storedRoot = context.rawInput.installData.installDevclass.replacements.find(o => o.installDevclass.trim().toUpperCase() === installedRoot.trim().toUpperCase());
|
|
84
98
|
if (storedRoot && storedRoot.installDevclass !== storedRoot.originalDevclass) {
|
|
@@ -133,7 +147,7 @@ exports.setInstallDevclass = {
|
|
|
133
147
|
if (context.runtime.update && hasCustomization) {
|
|
134
148
|
//the installed root devclass can be unknown: fall back to the stored root replacement
|
|
135
149
|
const installedRootDevclass = context.runtime.update.getDevclass()
|
|
136
|
-
|| ((
|
|
150
|
+
|| ((_d = (_c = context.runtime.previousInstallPackages) === null || _c === void 0 ? void 0 : _c.find(o => o.originalDevclass === context.runtime.package.hierarchy.devclass)) === null || _d === void 0 ? void 0 : _d.installDevclass);
|
|
137
151
|
if (installedRootDevclass) {
|
|
138
152
|
updateNamespace = (0, commons_1.getPackageNamespace)(installedRootDevclass);
|
|
139
153
|
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { Step } from "@simonegaffurini/sammarksworkflow";
|
|
2
2
|
import { InstallWorkflowContext } from ".";
|
|
3
|
+
/**
|
|
4
|
+
* Records the installed release: TRM packages table row, install packages and install transports.
|
|
5
|
+
* A trm-server/trm-rest upgrade also records it right after its import, which is never rolled back.
|
|
6
|
+
*/
|
|
7
|
+
export declare function recordInstalledRelease(context: InstallWorkflowContext): Promise<void>;
|
|
3
8
|
/**
|
|
4
9
|
* Workflow step that records the installed release in the target system's TRM package table.
|
|
5
10
|
*
|
|
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.updatePackageData = void 0;
|
|
13
|
+
exports.recordInstalledRelease = recordInstalledRelease;
|
|
13
14
|
const trm_commons_1 = require("trm-commons");
|
|
14
15
|
const systemConnector_1 = require("../../systemConnector");
|
|
15
16
|
const utils_1 = require("../commons/utils");
|
|
@@ -33,20 +34,12 @@ function installDevcRows(packageName, packageRegistry, replacements) {
|
|
|
33
34
|
}));
|
|
34
35
|
}
|
|
35
36
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
*
|
|
41
|
-
* 1- commit new values
|
|
42
|
-
*
|
|
43
|
-
*/
|
|
44
|
-
exports.updatePackageData = {
|
|
45
|
-
name: 'update-package-data',
|
|
46
|
-
run: (context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
37
|
+
* Records the installed release: TRM packages table row, install packages and install transports.
|
|
38
|
+
* A trm-server/trm-rest upgrade also records it right after its import, which is never rolled back.
|
|
39
|
+
*/
|
|
40
|
+
function recordInstalledRelease(context) {
|
|
41
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
47
42
|
var _a, _b;
|
|
48
|
-
trm_commons_1.Logger.loading(`Finalizing install...`);
|
|
49
|
-
//1- commit new values
|
|
50
43
|
trm_commons_1.Logger.loading(`Updating TRM data...`);
|
|
51
44
|
const originalTransport = context.runtime.transports.tadir.instance.trkorr;
|
|
52
45
|
const installTransport = (_a = context.output.transport) === null || _a === void 0 ? void 0 : _a.trkorr;
|
|
@@ -79,7 +72,8 @@ exports.updatePackageData = {
|
|
|
79
72
|
: [])
|
|
80
73
|
]);
|
|
81
74
|
context.revert.metadataPackageRegistry = packageRegistry;
|
|
82
|
-
|
|
75
|
+
// A trm-server/trm-rest upgrade is not rolled back after its import: nothing to restore
|
|
76
|
+
if (context.runtime.update && !context.runtime.selfUpdate) {
|
|
83
77
|
// The installed packages may have been read without their rows: read the stored row
|
|
84
78
|
// before writing, so the revert restores it exactly, or removes the row it wrote.
|
|
85
79
|
// A failed read aborts before any write.
|
|
@@ -101,18 +95,43 @@ exports.updatePackageData = {
|
|
|
101
95
|
// fail while returning the response.
|
|
102
96
|
context.revert.metadataWriteStarted = true;
|
|
103
97
|
yield systemConnector_1.SystemConnector.setInstallDevc(installDevc);
|
|
104
|
-
|
|
105
|
-
|
|
98
|
+
// A legacy backend has neither API: trm-server and trm-rest have no stale mappings nor install transports
|
|
99
|
+
if (!context.legacyBackend) {
|
|
100
|
+
if (staleInstallDevc.length > 0) {
|
|
101
|
+
yield systemConnector_1.SystemConnector.deleteInstallDevc(staleInstallDevc);
|
|
102
|
+
}
|
|
103
|
+
context.revert.metadataTransportsWriteStarted = true;
|
|
104
|
+
yield systemConnector_1.SystemConnector.setInstallTransports(context.rawInput.packageData.name, packageRegistry, installTr);
|
|
106
105
|
}
|
|
107
|
-
context.revert.metadataTransportsWriteStarted = true;
|
|
108
|
-
yield systemConnector_1.SystemConnector.setInstallTransports(context.rawInput.packageData.name, packageRegistry, installTr);
|
|
109
106
|
yield systemConnector_1.SystemConnector.updateTrmPackageData(context.revert.metadataPackageRow);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Workflow step that records the installed release in the target system's TRM package table.
|
|
111
|
+
*
|
|
112
|
+
* Creates/update record in TRM packages table, with its install packages and the
|
|
113
|
+
* customizing and translation transports imported on this system
|
|
114
|
+
*
|
|
115
|
+
* 1- commit new values
|
|
116
|
+
*
|
|
117
|
+
*/
|
|
118
|
+
exports.updatePackageData = {
|
|
119
|
+
name: 'update-package-data',
|
|
120
|
+
run: (context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
121
|
+
trm_commons_1.Logger.loading(`Finalizing install...`);
|
|
122
|
+
//1- commit new values
|
|
123
|
+
yield recordInstalledRelease(context);
|
|
110
124
|
}),
|
|
111
125
|
revert: (context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
112
126
|
// First installs remove the newly written row through the same atomic SAP operation.
|
|
113
127
|
if (!context.revert.metadataWriteStarted) {
|
|
114
128
|
return;
|
|
115
129
|
}
|
|
130
|
+
if (context.runtime.selfUpdate) {
|
|
131
|
+
// The imported release stays installed (see import-batch): its record is kept
|
|
132
|
+
trm_commons_1.Logger.log(`Keeping the TRM data of the imported ${context.rawInput.packageData.name}`, true);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
116
135
|
if (!context.runtime.update) {
|
|
117
136
|
if (context.revert.metadataPackageRow) {
|
|
118
137
|
yield systemConnector_1.SystemConnector.restoreInstallMetadata({
|
|
@@ -510,11 +510,8 @@ class RESTClient {
|
|
|
510
510
|
}
|
|
511
511
|
importTransport(trkorr, system, test) {
|
|
512
512
|
return __awaiter(this, void 0, void 0, function* () {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
trkorr: trkorr.trim().toUpperCase(),
|
|
516
|
-
test: test ? 'X' : ' '
|
|
517
|
-
})).data;
|
|
513
|
+
// as RFC: TEST is only sent for a test import
|
|
514
|
+
const result = (yield this._axiosInstance.post('/import_tr', Object.assign({ system: system.trim().toUpperCase(), trkorr: trkorr.trim().toUpperCase() }, (test ? { test: 'X' } : {})))).data;
|
|
518
515
|
if (result && result.testResult) {
|
|
519
516
|
return result.testResult;
|
|
520
517
|
}
|
package/dist/client/RFCClient.js
CHANGED
|
@@ -631,11 +631,9 @@ class RFCClient {
|
|
|
631
631
|
}
|
|
632
632
|
importTransport(trkorr, system, test) {
|
|
633
633
|
return __awaiter(this, void 0, void 0, function* () {
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
test: test ? 'X' : ' '
|
|
638
|
-
});
|
|
634
|
+
// TEST is only sent for a test import: releases before trm-server 7 have no such parameter, and
|
|
635
|
+
// their RFC metadata stays cached in this process even after a newer release is imported
|
|
636
|
+
const result = yield this._call("/ATRM/IMPORT_TR", Object.assign({ system: system.trim().toUpperCase(), trkorr: trkorr.trim().toUpperCase() }, (test ? { test: 'X' } : {})));
|
|
639
637
|
if (result && result.testResult) {
|
|
640
638
|
return this.parseImportTransportResult(result['testResult'], '/ATRM/IMPORT_TR');
|
|
641
639
|
}
|
|
@@ -430,14 +430,36 @@ class SystemConnectorBase {
|
|
|
430
430
|
}
|
|
431
431
|
getExistingObjects(objects) {
|
|
432
432
|
return __awaiter(this, void 0, void 0, function* () {
|
|
433
|
-
|
|
433
|
+
// TADIR is read in batches of objects of the same type: one read per object is slow on large packages
|
|
434
|
+
const batchSize = 25;
|
|
435
|
+
const key = (pgmid, object, objName) => `${pgmid.trim().toUpperCase()} ${object.trim().toUpperCase()} ${objName.trim().toUpperCase()}`;
|
|
436
|
+
const found = new Map();
|
|
437
|
+
const singles = [];
|
|
438
|
+
const byType = new Map();
|
|
434
439
|
for (const object of objects) {
|
|
440
|
+
// an option line holds 72 characters: "OR ( OBJ_NAME EQ '...' )"
|
|
441
|
+
if (object.objName.trim().replace(/'/g, "''").length > 50 || /\s(AND|OR)\s/i.test(object.objName)) {
|
|
442
|
+
singles.push(object);
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
const type = `${object.pgmid.trim().toUpperCase()} ${object.object.trim().toUpperCase()}`;
|
|
446
|
+
byType.set(type, [...(byType.get(type) || []), object]);
|
|
447
|
+
}
|
|
448
|
+
for (const [type, typeObjects] of byType) {
|
|
449
|
+
const [pgmid, object] = type.split(' ');
|
|
450
|
+
for (let i = 0; i < typeObjects.length; i += batchSize) {
|
|
451
|
+
const names = typeObjects.slice(i, i + batchSize).map(o => o.objName.trim().toUpperCase().replace(/'/g, "''"));
|
|
452
|
+
const tadir = yield this.readTable('TADIR', [{ fieldName: 'PGMID' }, { fieldName: 'OBJECT' }, { fieldName: 'OBJ_NAME' }, { fieldName: 'DEVCLASS' }, { fieldName: 'SRCSYSTEM' }, { fieldName: 'AUTHOR' }], `PGMID EQ '${pgmid}' AND OBJECT EQ '${object}' AND ( ${names.map(name => `OBJ_NAME EQ '${name}'`).join(' OR ')} )`);
|
|
453
|
+
tadir.forEach(o => found.set(key(o.pgmid, o.object, o.objName), o));
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
for (const object of singles) {
|
|
435
457
|
const oTadir = yield this.getObject(object.pgmid, object.object, object.objName);
|
|
436
458
|
if (oTadir) {
|
|
437
|
-
|
|
459
|
+
found.set(key(object.pgmid, object.object, object.objName), oTadir);
|
|
438
460
|
}
|
|
439
461
|
}
|
|
440
|
-
return
|
|
462
|
+
return objects.map(o => found.get(key(o.pgmid, o.object, o.objName))).filter(Boolean);
|
|
441
463
|
});
|
|
442
464
|
}
|
|
443
465
|
getNamespace(namespace) {
|
|
@@ -623,6 +623,7 @@ class Transport {
|
|
|
623
623
|
var lastCheck;
|
|
624
624
|
if (this._trTarget) {
|
|
625
625
|
var inQueueAttempts = 0;
|
|
626
|
+
var queueReadFailures = 0;
|
|
626
627
|
trm_commons_1.Logger.loading(`Checking ${this.trkorr} status...`, skipLog);
|
|
627
628
|
while (!inQueue) {
|
|
628
629
|
inQueueAttempts++;
|
|
@@ -644,15 +645,28 @@ class Transport {
|
|
|
644
645
|
}
|
|
645
646
|
}
|
|
646
647
|
catch (e) {
|
|
647
|
-
trm_commons_1.Logger.loading(`Last check ${lastCheck.toISOString().split("T")[0]} ${lastCheck.toTimeString().split(" ")[0]}
|
|
648
|
-
trm_commons_1.Logger.
|
|
648
|
+
trm_commons_1.Logger.loading(`Last check ${lastCheck.toISOString().split("T")[0]} ${lastCheck.toTimeString().split(" ")[0]}...`, skipLog);
|
|
649
|
+
trm_commons_1.Logger.warning(`Unable to read status: check continues, this error can be ignored`, true);
|
|
649
650
|
trm_commons_1.Logger.error(e.toString(), true);
|
|
650
651
|
}
|
|
651
652
|
}
|
|
652
653
|
//reading the queue is outdated now and getTransportImportStatus should be used
|
|
653
654
|
//HOWEVER it doesn't work for VIRTUAL TARGETS!
|
|
654
655
|
//for now, keep it like this
|
|
655
|
-
var tmsQueue
|
|
656
|
+
var tmsQueue;
|
|
657
|
+
try {
|
|
658
|
+
tmsQueue = yield systemConnector_1.SystemConnector.readTmsQueue(this._trTarget);
|
|
659
|
+
queueReadFailures = 0;
|
|
660
|
+
}
|
|
661
|
+
catch (e) {
|
|
662
|
+
// an import can replace the TRM backend itself (trm-server upgrade): its API can
|
|
663
|
+
// fail while it's being imported and activated
|
|
664
|
+
if (++queueReadFailures > 10) {
|
|
665
|
+
throw e;
|
|
666
|
+
}
|
|
667
|
+
trm_commons_1.Logger.log(`Unable to read ${this._trTarget} import queue (attempt ${queueReadFailures}), retrying: ${e}`, true);
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
656
670
|
tmsQueue = tmsQueue.filter(o => o.trkorr === this.trkorr);
|
|
657
671
|
tmsQueue = tmsQueue.sort((a, b) => parseInt(b.bufpos) - parseInt(a.bufpos));
|
|
658
672
|
if (!checkImpSing) {
|