abapgit-agent 1.8.6 → 1.8.8
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 +10 -3
- package/package.json +2 -1
- package/src/commands/import.js +8 -2
- package/src/commands/pull.js +27 -1
- package/src/commands/upgrade.js +456 -0
- package/src/config.js +57 -1
- package/src/utils/backgroundJobPoller.js +1 -1
- package/src/utils/version-check.js +182 -1
package/bin/abapgit-agent
CHANGED
|
@@ -24,7 +24,7 @@ const gitUtils = require('../src/utils/git-utils');
|
|
|
24
24
|
const versionCheck = require('../src/utils/version-check');
|
|
25
25
|
const validators = require('../src/utils/validators');
|
|
26
26
|
const { AbapHttp } = require('../src/utils/abap-http');
|
|
27
|
-
const { loadConfig, getTransport, isAbapIntegrationEnabled } = require('../src/config');
|
|
27
|
+
const { loadConfig, getTransport, isAbapIntegrationEnabled, getSafeguards } = require('../src/config');
|
|
28
28
|
|
|
29
29
|
// Get terminal width for responsive table
|
|
30
30
|
const getTermWidth = () => process.stdout.columns || 80;
|
|
@@ -52,7 +52,8 @@ async function main() {
|
|
|
52
52
|
where: require('../src/commands/where'),
|
|
53
53
|
ref: require('../src/commands/ref'),
|
|
54
54
|
init: require('../src/commands/init'),
|
|
55
|
-
pull: require('../src/commands/pull')
|
|
55
|
+
pull: require('../src/commands/pull'),
|
|
56
|
+
upgrade: require('../src/commands/upgrade')
|
|
56
57
|
};
|
|
57
58
|
|
|
58
59
|
// Check if this is a modular command
|
|
@@ -98,11 +99,17 @@ To enable integration:
|
|
|
98
99
|
isAbapIntegrationEnabled,
|
|
99
100
|
loadConfig,
|
|
100
101
|
AbapHttp,
|
|
101
|
-
getTransport
|
|
102
|
+
getTransport,
|
|
103
|
+
getSafeguards
|
|
102
104
|
};
|
|
103
105
|
|
|
104
106
|
// Execute command
|
|
105
107
|
await cmd.execute(args.slice(1), context);
|
|
108
|
+
|
|
109
|
+
// Show new version reminder (non-blocking, cached daily)
|
|
110
|
+
if (command !== 'upgrade') {
|
|
111
|
+
await versionCheck.showNewVersionReminder();
|
|
112
|
+
}
|
|
106
113
|
return;
|
|
107
114
|
}
|
|
108
115
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "abapgit-agent",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.8",
|
|
4
4
|
"description": "ABAP Git Agent - Pull and activate ABAP code via abapGit from any git repository",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"test:cmd:view": "node tests/run-all.js --cmd --command=view",
|
|
34
34
|
"test:cmd:preview": "node tests/run-all.js --cmd --command=preview",
|
|
35
35
|
"test:cmd:tree": "node tests/run-all.js --cmd --command=tree",
|
|
36
|
+
"test:cmd:upgrade": "node tests/run-all.js --cmd --command=upgrade",
|
|
36
37
|
"test:lifecycle": "node tests/run-all.js --lifecycle",
|
|
37
38
|
"test:pull": "node tests/run-all.js --pull",
|
|
38
39
|
"pull": "node bin/abapgit-agent",
|
package/src/commands/import.js
CHANGED
|
@@ -120,9 +120,15 @@ Examples:
|
|
|
120
120
|
resultData = { filesStaged: 'unknown', commitMessage: commitMessage };
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
if (resultData.success !== 'X' && resultData.success !== true) {
|
|
124
|
+
const errorMsg = resultData.error || resultData.ERROR || 'Unknown error';
|
|
125
|
+
console.log(`❌ Import failed: ${errorMsg}`);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
|
|
123
129
|
console.log(`✅ Import completed successfully!`);
|
|
124
|
-
console.log(` Files staged: ${resultData.filesStaged || resultData.FILES_STAGED || 'unknown'}`);
|
|
125
|
-
console.log(` Commit: ${resultData.commitMessage || resultData.COMMIT_MESSAGE || commitMessage || 'Initial import'}`);
|
|
130
|
+
console.log(` Files staged: ${resultData.filesStaged || resultData.FILES_STAGED || resultData.files_staged || 'unknown'}`);
|
|
131
|
+
console.log(` Commit: ${resultData.commitMessage || resultData.COMMIT_MESSAGE || resultData.commit_message || commitMessage || 'Initial import'}`);
|
|
126
132
|
console.log(``);
|
|
127
133
|
|
|
128
134
|
// Calculate time spent
|
package/src/commands/pull.js
CHANGED
|
@@ -9,7 +9,21 @@ module.exports = {
|
|
|
9
9
|
requiresVersionCheck: true,
|
|
10
10
|
|
|
11
11
|
async execute(args, context) {
|
|
12
|
-
const { loadConfig, AbapHttp, gitUtils, getTransport } = context;
|
|
12
|
+
const { loadConfig, AbapHttp, gitUtils, getTransport, getSafeguards } = context;
|
|
13
|
+
|
|
14
|
+
// Check project-level safeguards
|
|
15
|
+
const safeguards = getSafeguards();
|
|
16
|
+
|
|
17
|
+
// SAFEGUARD 1: Check if pull is completely disabled
|
|
18
|
+
if (safeguards.disablePull) {
|
|
19
|
+
console.error('❌ Error: pull command is disabled for this project\n');
|
|
20
|
+
if (safeguards.reason) {
|
|
21
|
+
console.error(`Reason: ${safeguards.reason}\n`);
|
|
22
|
+
}
|
|
23
|
+
console.error('The pull command has been disabled in .abapgit-agent.json');
|
|
24
|
+
console.error('Please contact the project maintainer to enable it.');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
13
27
|
|
|
14
28
|
const urlArgIndex = args.indexOf('--url');
|
|
15
29
|
const branchArgIndex = args.indexOf('--branch');
|
|
@@ -36,6 +50,18 @@ module.exports = {
|
|
|
36
50
|
files = args[filesArgIndex + 1].split(',').map(f => f.trim());
|
|
37
51
|
}
|
|
38
52
|
|
|
53
|
+
// SAFEGUARD 2: Check if files are required but not provided
|
|
54
|
+
if (safeguards.requireFilesForPull && !files) {
|
|
55
|
+
console.error('❌ Error: --files parameter is required for this project\n');
|
|
56
|
+
if (safeguards.reason) {
|
|
57
|
+
console.error(`Reason: ${safeguards.reason}\n`);
|
|
58
|
+
}
|
|
59
|
+
console.error('Usage: abapgit-agent pull --files <file1>,<file2>\n');
|
|
60
|
+
console.error('This safeguard is configured in .abapgit-agent.json');
|
|
61
|
+
console.error('Contact the project maintainer if you need to change this setting.');
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
39
65
|
if (!gitUrl) {
|
|
40
66
|
gitUrl = gitUtils.getRemoteUrl();
|
|
41
67
|
if (!gitUrl) {
|
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upgrade command - Upgrade CLI and/or ABAP backend to latest or specific version
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
const readline = require('readline');
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
name: 'upgrade',
|
|
10
|
+
description: 'Upgrade CLI and/or ABAP backend to latest or specific version',
|
|
11
|
+
requiresAbapConfig: false, // Checked conditionally
|
|
12
|
+
requiresVersionCheck: false, // Does its own version checks
|
|
13
|
+
|
|
14
|
+
async execute(args, context) {
|
|
15
|
+
const { versionCheck, loadConfig, isAbapIntegrationEnabled } = context;
|
|
16
|
+
|
|
17
|
+
// Parse flags
|
|
18
|
+
const flags = this.parseFlags(args);
|
|
19
|
+
|
|
20
|
+
// Validate flag combinations
|
|
21
|
+
try {
|
|
22
|
+
this.validateFlags(flags);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error(`❌ Error: ${error.message}`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Check if ABAP config is needed
|
|
29
|
+
const needsAbapConfig = !flags.cliOnly && !flags.checkOnly;
|
|
30
|
+
if (needsAbapConfig && !isAbapIntegrationEnabled()) {
|
|
31
|
+
console.error('❌ Error: .abapGitAgent config file not found');
|
|
32
|
+
console.error(' ABAP upgrade requires configuration.');
|
|
33
|
+
console.error(' Run: abapgit-agent init');
|
|
34
|
+
console.error('');
|
|
35
|
+
console.error(' Or use --cli-only to upgrade CLI package only.');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Get current versions
|
|
40
|
+
const cliVersion = versionCheck.getCliVersion();
|
|
41
|
+
let abapVersion = null;
|
|
42
|
+
|
|
43
|
+
if (needsAbapConfig) {
|
|
44
|
+
try {
|
|
45
|
+
const config = loadConfig();
|
|
46
|
+
const { apiVersion } = await versionCheck.checkCompatibility(config);
|
|
47
|
+
abapVersion = apiVersion;
|
|
48
|
+
} catch (e) {
|
|
49
|
+
console.error(`⚠️ Could not fetch ABAP version: ${e.message}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Get latest version from npm
|
|
54
|
+
const latestVersion = await versionCheck.getLatestNpmVersion();
|
|
55
|
+
if (!latestVersion && !flags.version && !flags.match) {
|
|
56
|
+
console.error('❌ Error: Could not fetch latest version from npm registry');
|
|
57
|
+
console.error(' Please check your internet connection or specify --version X.X.X');
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Validate specified version exists in npm registry
|
|
62
|
+
if (flags.version && !flags.abapOnly) {
|
|
63
|
+
const versionExists = await this.validateVersionExists(flags.version);
|
|
64
|
+
if (!versionExists) {
|
|
65
|
+
console.error(`❌ Error: Version ${flags.version} not found in npm registry`);
|
|
66
|
+
console.error(' Please check available versions at: https://www.npmjs.com/package/abapgit-agent?activeTab=versions');
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Determine target versions
|
|
72
|
+
const targets = this.determineTargets(flags, cliVersion, abapVersion, latestVersion);
|
|
73
|
+
|
|
74
|
+
// Check-only mode
|
|
75
|
+
if (flags.checkOnly) {
|
|
76
|
+
this.showCheckReport(cliVersion, abapVersion, latestVersion);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Dry-run mode
|
|
81
|
+
if (flags.dryRun) {
|
|
82
|
+
this.showDryRunPlan(cliVersion, abapVersion, targets, flags);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Show upgrade plan
|
|
87
|
+
if (!flags.yes) {
|
|
88
|
+
const proceed = await this.confirmUpgrade(cliVersion, abapVersion, targets, flags);
|
|
89
|
+
if (!proceed) {
|
|
90
|
+
console.log('Upgrade cancelled.');
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Execute upgrade
|
|
96
|
+
await this.performUpgrade(targets, flags, context);
|
|
97
|
+
|
|
98
|
+
// Verify upgrade
|
|
99
|
+
await this.verifyUpgrade(targets, flags, context);
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Parse command-line flags
|
|
104
|
+
*/
|
|
105
|
+
parseFlags(args) {
|
|
106
|
+
return {
|
|
107
|
+
checkOnly: args.includes('--check'),
|
|
108
|
+
cliOnly: args.includes('--cli-only'),
|
|
109
|
+
abapOnly: args.includes('--abap-only'),
|
|
110
|
+
match: args.includes('--match'),
|
|
111
|
+
version: this.getArgValue(args, '--version'),
|
|
112
|
+
latest: args.includes('--latest'),
|
|
113
|
+
yes: args.includes('--yes') || args.includes('-y'),
|
|
114
|
+
dryRun: args.includes('--dry-run'),
|
|
115
|
+
transport: this.getArgValue(args, '--transport')
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get argument value following a flag
|
|
121
|
+
*/
|
|
122
|
+
getArgValue(args, flag) {
|
|
123
|
+
const index = args.indexOf(flag);
|
|
124
|
+
if (index !== -1 && index + 1 < args.length) {
|
|
125
|
+
return args[index + 1];
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Validate flag combinations
|
|
132
|
+
*/
|
|
133
|
+
validateFlags(flags) {
|
|
134
|
+
// Invalid combinations
|
|
135
|
+
if (flags.match && flags.version) {
|
|
136
|
+
throw new Error('Cannot use --match and --version together');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (flags.match && flags.cliOnly) {
|
|
140
|
+
throw new Error('Cannot use --match with --cli-only. --match upgrades ABAP to match CLI version');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (flags.cliOnly && flags.abapOnly) {
|
|
144
|
+
throw new Error('Cannot use --cli-only and --abap-only together');
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Validate that a version exists in npm registry
|
|
150
|
+
*/
|
|
151
|
+
async validateVersionExists(version) {
|
|
152
|
+
return new Promise((resolve) => {
|
|
153
|
+
const { execSync } = require('child_process');
|
|
154
|
+
try {
|
|
155
|
+
// Use npm view to check if version exists
|
|
156
|
+
const output = execSync(`npm view abapgit-agent@${version} version 2>/dev/null`, {
|
|
157
|
+
encoding: 'utf8',
|
|
158
|
+
stdio: ['pipe', 'pipe', 'ignore']
|
|
159
|
+
}).trim();
|
|
160
|
+
|
|
161
|
+
// If npm view returns the version, it exists
|
|
162
|
+
resolve(output === version);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
// Version doesn't exist or npm command failed
|
|
165
|
+
resolve(false);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Determine target versions for CLI and ABAP
|
|
172
|
+
*/
|
|
173
|
+
determineTargets(flags, cliVersion, abapVersion, latestVersion) {
|
|
174
|
+
let cliTarget = null;
|
|
175
|
+
let abapTarget = null;
|
|
176
|
+
|
|
177
|
+
if (flags.match) {
|
|
178
|
+
// Match ABAP to CLI version
|
|
179
|
+
abapTarget = cliVersion;
|
|
180
|
+
} else if (flags.version) {
|
|
181
|
+
// Specific version
|
|
182
|
+
cliTarget = flags.cliOnly ? flags.version : (flags.abapOnly ? null : flags.version);
|
|
183
|
+
abapTarget = flags.abapOnly ? flags.version : (flags.cliOnly ? null : flags.version);
|
|
184
|
+
} else {
|
|
185
|
+
// Latest version (default)
|
|
186
|
+
cliTarget = flags.abapOnly ? null : latestVersion;
|
|
187
|
+
abapTarget = flags.cliOnly ? null : latestVersion;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return { cliTarget, abapTarget };
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Show check-only report
|
|
195
|
+
*/
|
|
196
|
+
showCheckReport(cliVersion, abapVersion, latestVersion) {
|
|
197
|
+
console.log('');
|
|
198
|
+
console.log('Current versions:');
|
|
199
|
+
console.log(` CLI: v${cliVersion}`);
|
|
200
|
+
if (abapVersion) {
|
|
201
|
+
console.log(` ABAP: v${abapVersion}`);
|
|
202
|
+
}
|
|
203
|
+
console.log('');
|
|
204
|
+
|
|
205
|
+
if (latestVersion) {
|
|
206
|
+
console.log(`Latest available: v${latestVersion}`);
|
|
207
|
+
console.log('');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const needsCliUpgrade = latestVersion && cliVersion !== latestVersion;
|
|
211
|
+
const needsAbapUpgrade = abapVersion && latestVersion && abapVersion !== latestVersion;
|
|
212
|
+
const versionMismatch = abapVersion && cliVersion !== abapVersion;
|
|
213
|
+
|
|
214
|
+
if (versionMismatch) {
|
|
215
|
+
console.log('⚠️ Version mismatch detected');
|
|
216
|
+
console.log('');
|
|
217
|
+
} else if (!needsCliUpgrade && !needsAbapUpgrade) {
|
|
218
|
+
console.log('✅ All components are up to date');
|
|
219
|
+
console.log('');
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log('To upgrade:');
|
|
224
|
+
console.log(' Both: abapgit-agent upgrade');
|
|
225
|
+
console.log(' CLI only: abapgit-agent upgrade --cli-only');
|
|
226
|
+
if (abapVersion) {
|
|
227
|
+
console.log(' ABAP only: abapgit-agent upgrade --abap-only');
|
|
228
|
+
console.log(' Match: abapgit-agent upgrade --match');
|
|
229
|
+
}
|
|
230
|
+
console.log('');
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Show dry-run plan
|
|
235
|
+
*/
|
|
236
|
+
showDryRunPlan(cliVersion, abapVersion, targets, flags) {
|
|
237
|
+
console.log('');
|
|
238
|
+
console.log('🔹 DRY RUN - No changes will be made');
|
|
239
|
+
console.log('');
|
|
240
|
+
console.log('Current versions:');
|
|
241
|
+
console.log(` CLI: v${cliVersion}`);
|
|
242
|
+
if (abapVersion) {
|
|
243
|
+
console.log(` ABAP: v${abapVersion}`);
|
|
244
|
+
}
|
|
245
|
+
console.log('');
|
|
246
|
+
|
|
247
|
+
console.log('Target versions:');
|
|
248
|
+
if (targets.cliTarget) {
|
|
249
|
+
console.log(` CLI: v${targets.cliTarget}`);
|
|
250
|
+
}
|
|
251
|
+
if (targets.abapTarget) {
|
|
252
|
+
console.log(` ABAP: v${targets.abapTarget}`);
|
|
253
|
+
}
|
|
254
|
+
console.log('');
|
|
255
|
+
|
|
256
|
+
console.log('Would execute:');
|
|
257
|
+
let step = 1;
|
|
258
|
+
if (targets.cliTarget) {
|
|
259
|
+
console.log(` ${step}. npm install -g abapgit-agent@${targets.cliTarget}`);
|
|
260
|
+
step++;
|
|
261
|
+
}
|
|
262
|
+
if (targets.abapTarget) {
|
|
263
|
+
console.log(` ${step}. abapgit-agent pull --branch v${targets.abapTarget}`);
|
|
264
|
+
console.log(' (via existing abapGit repository)');
|
|
265
|
+
step++;
|
|
266
|
+
}
|
|
267
|
+
console.log(` ${step}. Verify versions match`);
|
|
268
|
+
console.log('');
|
|
269
|
+
console.log('No changes made.');
|
|
270
|
+
console.log('');
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Confirm upgrade with user
|
|
275
|
+
*/
|
|
276
|
+
async confirmUpgrade(cliVersion, abapVersion, targets, flags) {
|
|
277
|
+
console.log('');
|
|
278
|
+
console.log('📦 Upgrade Plan:');
|
|
279
|
+
console.log('');
|
|
280
|
+
console.log('Current versions:');
|
|
281
|
+
console.log(` CLI: v${cliVersion}`);
|
|
282
|
+
if (abapVersion) {
|
|
283
|
+
console.log(` ABAP: v${abapVersion}`);
|
|
284
|
+
}
|
|
285
|
+
console.log('');
|
|
286
|
+
|
|
287
|
+
console.log('Target versions:');
|
|
288
|
+
if (targets.cliTarget) {
|
|
289
|
+
console.log(` CLI: v${targets.cliTarget}`);
|
|
290
|
+
}
|
|
291
|
+
if (targets.abapTarget) {
|
|
292
|
+
console.log(` ABAP: v${targets.abapTarget}`);
|
|
293
|
+
}
|
|
294
|
+
console.log('');
|
|
295
|
+
|
|
296
|
+
console.log('This will:');
|
|
297
|
+
let step = 1;
|
|
298
|
+
if (targets.cliTarget) {
|
|
299
|
+
console.log(` ${step}. Upgrade npm package: abapgit-agent@${targets.cliTarget}`);
|
|
300
|
+
step++;
|
|
301
|
+
}
|
|
302
|
+
if (targets.abapTarget) {
|
|
303
|
+
console.log(` ${step}. Pull ABAP code from git tag v${targets.abapTarget}`);
|
|
304
|
+
step++;
|
|
305
|
+
console.log(` ${step}. Activate all backend components`);
|
|
306
|
+
step++;
|
|
307
|
+
}
|
|
308
|
+
console.log('');
|
|
309
|
+
|
|
310
|
+
return new Promise((resolve) => {
|
|
311
|
+
const rl = readline.createInterface({
|
|
312
|
+
input: process.stdin,
|
|
313
|
+
output: process.stdout
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
rl.question('Do you want to continue? [Y/n] ', (answer) => {
|
|
317
|
+
rl.close();
|
|
318
|
+
const normalized = answer.trim().toLowerCase();
|
|
319
|
+
resolve(normalized === '' || normalized === 'y' || normalized === 'yes');
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Perform upgrade
|
|
326
|
+
*/
|
|
327
|
+
async performUpgrade(targets, flags, context) {
|
|
328
|
+
console.log('');
|
|
329
|
+
console.log('🚀 Starting upgrade...');
|
|
330
|
+
console.log('');
|
|
331
|
+
|
|
332
|
+
// Upgrade CLI
|
|
333
|
+
if (targets.cliTarget) {
|
|
334
|
+
await this.upgradeCliPackage(targets.cliTarget);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Upgrade ABAP
|
|
338
|
+
if (targets.abapTarget) {
|
|
339
|
+
await this.upgradeAbapBackend(targets.abapTarget, flags.transport, context);
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Upgrade CLI package via npm
|
|
345
|
+
*/
|
|
346
|
+
async upgradeCliPackage(version) {
|
|
347
|
+
console.log(`📦 Upgrading CLI to v${version}...`);
|
|
348
|
+
|
|
349
|
+
try {
|
|
350
|
+
// Check if npm is available
|
|
351
|
+
try {
|
|
352
|
+
execSync('npm --version', { stdio: 'ignore' });
|
|
353
|
+
} catch (e) {
|
|
354
|
+
console.error('❌ Error: npm is not installed or not in PATH');
|
|
355
|
+
console.error(' Please install Node.js and npm: https://nodejs.org/');
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Run npm install
|
|
360
|
+
const command = `npm install -g abapgit-agent@${version}`;
|
|
361
|
+
console.log(` Running: ${command}`);
|
|
362
|
+
|
|
363
|
+
execSync(command, {
|
|
364
|
+
stdio: 'inherit',
|
|
365
|
+
encoding: 'utf8'
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
console.log(`✅ CLI upgraded to v${version}`);
|
|
369
|
+
console.log('');
|
|
370
|
+
} catch (error) {
|
|
371
|
+
console.error(`❌ Failed to upgrade CLI: ${error.message}`);
|
|
372
|
+
console.error('');
|
|
373
|
+
console.error('This may be due to:');
|
|
374
|
+
console.error(' - Version not found in npm registry');
|
|
375
|
+
console.error(' - Permission issues (try with sudo)');
|
|
376
|
+
console.error(' - Network connectivity issues');
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Upgrade ABAP backend via pull command
|
|
383
|
+
*/
|
|
384
|
+
async upgradeAbapBackend(version, transport, context) {
|
|
385
|
+
console.log(`📦 Upgrading ABAP backend to v${version}...`);
|
|
386
|
+
console.log(` Using git tag: v${version}`);
|
|
387
|
+
console.log('');
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
// Build pull command args
|
|
391
|
+
const pullArgs = ['--branch', `v${version}`];
|
|
392
|
+
if (transport) {
|
|
393
|
+
pullArgs.push('--transport', transport);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Execute pull command
|
|
397
|
+
const pullCommand = require('./pull');
|
|
398
|
+
await pullCommand.execute(pullArgs, context);
|
|
399
|
+
|
|
400
|
+
console.log('');
|
|
401
|
+
console.log(`✅ ABAP backend upgraded to v${version}`);
|
|
402
|
+
console.log('');
|
|
403
|
+
} catch (error) {
|
|
404
|
+
console.error(`❌ Failed to upgrade ABAP backend: ${error.message}`);
|
|
405
|
+
console.error('');
|
|
406
|
+
console.error('This may be due to:');
|
|
407
|
+
console.error(' - Git tag not found in repository');
|
|
408
|
+
console.error(' - ABAP activation errors');
|
|
409
|
+
console.error(' - Connection issues with ABAP system');
|
|
410
|
+
process.exit(1);
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Verify upgrade success
|
|
416
|
+
*/
|
|
417
|
+
async verifyUpgrade(targets, flags, context) {
|
|
418
|
+
console.log('🔍 Verifying upgrade...');
|
|
419
|
+
console.log('');
|
|
420
|
+
|
|
421
|
+
const { versionCheck, loadConfig } = context;
|
|
422
|
+
|
|
423
|
+
// Check CLI version
|
|
424
|
+
let cliVersion = null;
|
|
425
|
+
if (targets.cliTarget) {
|
|
426
|
+
cliVersion = versionCheck.getCliVersion();
|
|
427
|
+
if (cliVersion === targets.cliTarget) {
|
|
428
|
+
console.log(`✅ CLI version verified: v${cliVersion}`);
|
|
429
|
+
} else {
|
|
430
|
+
console.log(`⚠️ CLI version mismatch: expected v${targets.cliTarget}, got v${cliVersion}`);
|
|
431
|
+
console.log(' You may need to restart your terminal or reinstall globally');
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Check ABAP version
|
|
436
|
+
if (targets.abapTarget && !flags.cliOnly) {
|
|
437
|
+
try {
|
|
438
|
+
const config = loadConfig();
|
|
439
|
+
const { apiVersion: abapVersion } = await versionCheck.checkCompatibility(config);
|
|
440
|
+
|
|
441
|
+
if (abapVersion === targets.abapTarget) {
|
|
442
|
+
console.log(`✅ ABAP version verified: v${abapVersion}`);
|
|
443
|
+
} else {
|
|
444
|
+
console.log(`⚠️ ABAP version mismatch: expected v${targets.abapTarget}, got v${abapVersion}`);
|
|
445
|
+
console.log(' Some components may have failed to activate');
|
|
446
|
+
}
|
|
447
|
+
} catch (e) {
|
|
448
|
+
console.log(`⚠️ Could not verify ABAP version: ${e.message}`);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
console.log('');
|
|
453
|
+
console.log('✅ Upgrade complete!');
|
|
454
|
+
console.log('');
|
|
455
|
+
}
|
|
456
|
+
};
|
package/src/config.js
CHANGED
|
@@ -88,11 +88,67 @@ function getWorkflowConfig() {
|
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Load project-level configuration (.abapgit-agent.json)
|
|
93
|
+
* This file is checked into version control and contains project settings
|
|
94
|
+
* @returns {Object|null} Project config or null if not found
|
|
95
|
+
*/
|
|
96
|
+
function loadProjectConfig() {
|
|
97
|
+
const projectConfigPath = path.join(process.cwd(), '.abapgit-agent.json');
|
|
98
|
+
|
|
99
|
+
if (fs.existsSync(projectConfigPath)) {
|
|
100
|
+
try {
|
|
101
|
+
return JSON.parse(fs.readFileSync(projectConfigPath, 'utf8'));
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.warn(`⚠️ Warning: Failed to parse .abapgit-agent.json: ${error.message}`);
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get safeguard configuration from project-level config
|
|
113
|
+
* Project-level safeguards CANNOT be overridden by user config
|
|
114
|
+
* @returns {Object} Safeguards config with requireFilesForPull, disablePull, and reason
|
|
115
|
+
*/
|
|
116
|
+
function getSafeguards() {
|
|
117
|
+
const projectConfig = loadProjectConfig();
|
|
118
|
+
|
|
119
|
+
if (projectConfig?.safeguards) {
|
|
120
|
+
return {
|
|
121
|
+
requireFilesForPull: projectConfig.safeguards.requireFilesForPull === true,
|
|
122
|
+
disablePull: projectConfig.safeguards.disablePull === true,
|
|
123
|
+
reason: projectConfig.safeguards.reason || null
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Default: no safeguards (backward compatible)
|
|
128
|
+
return {
|
|
129
|
+
requireFilesForPull: false,
|
|
130
|
+
disablePull: false,
|
|
131
|
+
reason: null
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Get project information from project-level config
|
|
137
|
+
* @returns {Object|null} Project info (name, description) or null
|
|
138
|
+
*/
|
|
139
|
+
function getProjectInfo() {
|
|
140
|
+
const projectConfig = loadProjectConfig();
|
|
141
|
+
return projectConfig?.project || null;
|
|
142
|
+
}
|
|
143
|
+
|
|
91
144
|
module.exports = {
|
|
92
145
|
loadConfig,
|
|
93
146
|
getAbapConfig,
|
|
94
147
|
getAgentConfig,
|
|
95
148
|
getTransport,
|
|
96
149
|
isAbapIntegrationEnabled,
|
|
97
|
-
getWorkflowConfig
|
|
150
|
+
getWorkflowConfig,
|
|
151
|
+
getSafeguards,
|
|
152
|
+
getProjectInfo,
|
|
153
|
+
loadProjectConfig
|
|
98
154
|
};
|
|
@@ -58,7 +58,7 @@ async function pollForCompletion(http, endpoint, jobNumber, options = {}) {
|
|
|
58
58
|
let lastProgress = -1;
|
|
59
59
|
let pollCount = 0;
|
|
60
60
|
|
|
61
|
-
while (status === 'running' || status === 'scheduled') {
|
|
61
|
+
while (status === 'running' || status === 'scheduled' || status === 'STARTING') {
|
|
62
62
|
// Wait before polling (except first time)
|
|
63
63
|
if (pollCount > 0) {
|
|
64
64
|
await sleep(pollInterval);
|
|
@@ -3,8 +3,30 @@
|
|
|
3
3
|
*/
|
|
4
4
|
const pathModule = require('path');
|
|
5
5
|
const fs = require('fs');
|
|
6
|
+
const os = require('os');
|
|
6
7
|
const https = require('https');
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Get cache directory for abapgit-agent
|
|
11
|
+
* Follows OS-specific conventions:
|
|
12
|
+
* - Windows: %LOCALAPPDATA%\abapgit-agent\cache
|
|
13
|
+
* - Linux/macOS: ~/.cache/abapgit-agent (respects XDG_CACHE_HOME)
|
|
14
|
+
* @returns {string} Cache directory path
|
|
15
|
+
*/
|
|
16
|
+
function getCacheDir() {
|
|
17
|
+
const homeDir = os.homedir();
|
|
18
|
+
|
|
19
|
+
// Windows: %LOCALAPPDATA%\abapgit-agent\cache
|
|
20
|
+
if (process.platform === 'win32') {
|
|
21
|
+
const localAppData = process.env.LOCALAPPDATA || pathModule.join(homeDir, 'AppData', 'Local');
|
|
22
|
+
return pathModule.join(localAppData, 'abapgit-agent', 'cache');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Linux/macOS: respect XDG_CACHE_HOME or use ~/.cache
|
|
26
|
+
const xdgCache = process.env.XDG_CACHE_HOME || pathModule.join(homeDir, '.cache');
|
|
27
|
+
return pathModule.join(xdgCache, 'abapgit-agent');
|
|
28
|
+
}
|
|
29
|
+
|
|
8
30
|
/**
|
|
9
31
|
* Get CLI version from package.json
|
|
10
32
|
* @returns {string} CLI version or '1.0.0' as default
|
|
@@ -74,7 +96,166 @@ async function checkCompatibility(config) {
|
|
|
74
96
|
}
|
|
75
97
|
}
|
|
76
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Get configured npm registry URL
|
|
101
|
+
* Respects user's npm config (e.g., corporate mirrors, regional mirrors)
|
|
102
|
+
* @returns {string} Registry URL
|
|
103
|
+
*/
|
|
104
|
+
function getNpmRegistry() {
|
|
105
|
+
try {
|
|
106
|
+
const { execSync } = require('child_process');
|
|
107
|
+
const registry = execSync('npm config get registry', {
|
|
108
|
+
encoding: 'utf8',
|
|
109
|
+
stdio: ['pipe', 'pipe', 'ignore'] // Suppress stderr
|
|
110
|
+
}).trim();
|
|
111
|
+
|
|
112
|
+
// Validate registry URL
|
|
113
|
+
if (registry && registry.startsWith('http')) {
|
|
114
|
+
return registry;
|
|
115
|
+
}
|
|
116
|
+
} catch (e) {
|
|
117
|
+
// Fall back to default if npm config fails
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Default npm registry
|
|
121
|
+
return 'https://registry.npmjs.org/';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get latest version from npm registry
|
|
126
|
+
* Respects user's configured npm registry (mirrors, corporate proxies, etc.)
|
|
127
|
+
* @returns {Promise<string|null>} Latest version or null on error
|
|
128
|
+
*/
|
|
129
|
+
async function getLatestNpmVersion() {
|
|
130
|
+
return new Promise((resolve) => {
|
|
131
|
+
try {
|
|
132
|
+
const registry = getNpmRegistry();
|
|
133
|
+
// Ensure registry ends with /
|
|
134
|
+
const baseUrl = registry.endsWith('/') ? registry : registry + '/';
|
|
135
|
+
const url = `${baseUrl}abapgit-agent/latest`;
|
|
136
|
+
|
|
137
|
+
https.get(url, (res) => {
|
|
138
|
+
let body = '';
|
|
139
|
+
res.on('data', chunk => body += chunk);
|
|
140
|
+
res.on('end', () => {
|
|
141
|
+
try {
|
|
142
|
+
const data = JSON.parse(body);
|
|
143
|
+
resolve(data.version || null);
|
|
144
|
+
} catch (e) {
|
|
145
|
+
resolve(null);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}).on('error', () => resolve(null));
|
|
149
|
+
} catch (e) {
|
|
150
|
+
resolve(null);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Compare two semver versions
|
|
157
|
+
* @param {string} a - Version A (e.g., "1.9.0")
|
|
158
|
+
* @param {string} b - Version B (e.g., "1.8.6")
|
|
159
|
+
* @returns {number} 1 if a > b, -1 if a < b, 0 if equal
|
|
160
|
+
*/
|
|
161
|
+
function compareVersions(a, b) {
|
|
162
|
+
const aParts = a.split('.').map(Number);
|
|
163
|
+
const bParts = b.split('.').map(Number);
|
|
164
|
+
|
|
165
|
+
for (let i = 0; i < 3; i++) {
|
|
166
|
+
if (aParts[i] > bParts[i]) return 1;
|
|
167
|
+
if (aParts[i] < bParts[i]) return -1;
|
|
168
|
+
}
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Check for new version availability with daily caching
|
|
174
|
+
* Cache file location (OS-specific):
|
|
175
|
+
* - Linux/macOS: ~/.cache/abapgit-agent/version-check.json
|
|
176
|
+
* - Windows: %LOCALAPPDATA%\abapgit-agent\cache\version-check.json
|
|
177
|
+
* Format: { lastCheck: timestamp, latestVersion: "1.9.0" }
|
|
178
|
+
* @returns {Promise<object>} { hasNewVersion, latestVersion, currentVersion }
|
|
179
|
+
*/
|
|
180
|
+
async function checkForNewVersion() {
|
|
181
|
+
const cliVersion = getCliVersion();
|
|
182
|
+
const cacheFile = pathModule.join(getCacheDir(), 'version-check.json');
|
|
183
|
+
const now = Date.now();
|
|
184
|
+
const ONE_DAY = 24 * 60 * 60 * 1000;
|
|
185
|
+
|
|
186
|
+
// Read cache if exists and less than 24 hours old
|
|
187
|
+
if (fs.existsSync(cacheFile)) {
|
|
188
|
+
try {
|
|
189
|
+
const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
|
|
190
|
+
if (now - cache.lastCheck < ONE_DAY && cache.latestVersion) {
|
|
191
|
+
// Use cached result
|
|
192
|
+
return {
|
|
193
|
+
hasNewVersion: compareVersions(cache.latestVersion, cliVersion) > 0,
|
|
194
|
+
latestVersion: cache.latestVersion,
|
|
195
|
+
currentVersion: cliVersion
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
} catch (e) {
|
|
199
|
+
// Invalid cache, continue to fetch
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Fetch latest version from npm
|
|
204
|
+
const latestVersion = await getLatestNpmVersion();
|
|
205
|
+
|
|
206
|
+
if (latestVersion) {
|
|
207
|
+
// Write cache
|
|
208
|
+
try {
|
|
209
|
+
const cacheDir = pathModule.dirname(cacheFile);
|
|
210
|
+
if (!fs.existsSync(cacheDir)) {
|
|
211
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
212
|
+
}
|
|
213
|
+
fs.writeFileSync(cacheFile, JSON.stringify({
|
|
214
|
+
lastCheck: now,
|
|
215
|
+
latestVersion: latestVersion
|
|
216
|
+
}));
|
|
217
|
+
} catch (e) {
|
|
218
|
+
// Ignore cache write errors
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
hasNewVersion: compareVersions(latestVersion, cliVersion) > 0,
|
|
223
|
+
latestVersion: latestVersion,
|
|
224
|
+
currentVersion: cliVersion
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Failed to fetch, return no update
|
|
229
|
+
return {
|
|
230
|
+
hasNewVersion: false,
|
|
231
|
+
latestVersion: null,
|
|
232
|
+
currentVersion: cliVersion
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Show new version reminder at end of command output
|
|
238
|
+
* Non-blocking, silent on error
|
|
239
|
+
*/
|
|
240
|
+
async function showNewVersionReminder() {
|
|
241
|
+
try {
|
|
242
|
+
const { hasNewVersion, latestVersion, currentVersion } = await checkForNewVersion();
|
|
243
|
+
|
|
244
|
+
if (hasNewVersion) {
|
|
245
|
+
console.log('');
|
|
246
|
+
console.log(`💡 New version available: ${latestVersion} (current: ${currentVersion})`);
|
|
247
|
+
console.log(` Run: abapgit-agent upgrade`);
|
|
248
|
+
}
|
|
249
|
+
} catch (e) {
|
|
250
|
+
// Silent - don't interrupt user's workflow
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
77
254
|
module.exports = {
|
|
78
255
|
getCliVersion,
|
|
79
|
-
checkCompatibility
|
|
256
|
+
checkCompatibility,
|
|
257
|
+
getLatestNpmVersion,
|
|
258
|
+
compareVersions,
|
|
259
|
+
checkForNewVersion,
|
|
260
|
+
showNewVersionReminder
|
|
80
261
|
};
|