abapgit-agent 1.17.1 → 1.17.3
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/.abapGitAgent.example +8 -1
- package/.abapgit-agent.example.json +19 -0
- package/README.md +21 -8
- package/abap/guidelines/run-probe-classes.md +11 -2
- package/package.json +4 -2
- package/src/commands/init.js +24 -3
- package/src/commands/lint.js +64 -35
- package/src/utils/backgroundJobPoller.README.md +1 -1
package/.abapGitAgent.example
CHANGED
|
@@ -8,5 +8,12 @@
|
|
|
8
8
|
"protocol": "https",
|
|
9
9
|
"gitUsername": "github-username",
|
|
10
10
|
"gitPassword": "github-token",
|
|
11
|
-
"referenceFolder": "~/abap-reference"
|
|
11
|
+
"referenceFolder": "~/abap-reference",
|
|
12
|
+
|
|
13
|
+
"testRepos": {
|
|
14
|
+
"pull": "https://github.com/your-org/abgagt-pull-test.git",
|
|
15
|
+
"drop": "https://github.com/your-org/abgagt-drop-test.git",
|
|
16
|
+
"customize": "https://github.com/your-org/abgagt-customize-test.git",
|
|
17
|
+
"lifecycle": "https://github.com/your-org/abgagt-lifecycle-test.git"
|
|
18
|
+
}
|
|
12
19
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"project": {
|
|
3
|
+
"name": "MY_PACKAGE",
|
|
4
|
+
"description": ""
|
|
5
|
+
},
|
|
6
|
+
|
|
7
|
+
"safeguards": {
|
|
8
|
+
"requireFilesForPull": false,
|
|
9
|
+
"disablePull": false,
|
|
10
|
+
"disableRun": false,
|
|
11
|
+
"disableImport": false,
|
|
12
|
+
"requireImportMessage": false,
|
|
13
|
+
"disableProbeClasses": false
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
"conflictDetection": {
|
|
17
|
+
"mode": "abort"
|
|
18
|
+
}
|
|
19
|
+
}
|
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ abapgit-agent import
|
|
|
45
45
|
abapgit-agent pull
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
See [Creating New ABAP Projects](
|
|
48
|
+
See [Creating New ABAP Projects](docs/install.md#creating-new-abap-projects) to set up a new ABAP repository with Claude Code integration.
|
|
49
49
|
|
|
50
50
|
## CLI Commands
|
|
51
51
|
|
|
@@ -99,25 +99,38 @@ abapgit-agent health # Verify ABAP connection
|
|
|
99
99
|
# Install dependencies
|
|
100
100
|
npm install
|
|
101
101
|
|
|
102
|
-
# Run unit tests (no ABAP system needed)
|
|
103
|
-
npm test
|
|
104
|
-
|
|
105
102
|
# Test a command manually
|
|
106
103
|
node bin/abapgit-agent --help
|
|
107
104
|
node bin/abapgit-agent syntax --files src/zcl_my_class.clas.abap
|
|
105
|
+
```
|
|
108
106
|
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
## Running Tests
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Unit tests — no ABAP system needed (fast)
|
|
111
|
+
npm test
|
|
112
|
+
|
|
113
|
+
# Integration tests — requires a configured .abapGitAgent
|
|
114
|
+
npm run test:setup # one-time setup: clone test repos + activate ABAP objects
|
|
115
|
+
npm run test:all # full suite (setup runs automatically on first run)
|
|
116
|
+
|
|
117
|
+
# Run a single suite
|
|
118
|
+
npm run test:cmd # CLI command tests
|
|
119
|
+
npm run test:drop # drop command tests
|
|
120
|
+
npm run test:customize # customize command tests
|
|
121
|
+
npm run test:aunit # ABAP unit tests
|
|
111
122
|
```
|
|
112
123
|
|
|
124
|
+
> **Full integration test guide:** [docs/integration-tests.md](docs/integration-tests.md)
|
|
125
|
+
|
|
113
126
|
## Documentation
|
|
114
127
|
|
|
115
128
|
| Topic | File |
|
|
116
129
|
|-------|------|
|
|
117
130
|
| Full Documentation | [https://sylvoscai.github.io/abapgit-agent/](https://sylvoscai.github.io/abapgit-agent/) |
|
|
118
|
-
| Installation & Setup | [
|
|
131
|
+
| Installation & Setup | [docs/install.md](docs/install.md) |
|
|
119
132
|
| All Commands Overview | [docs/commands.md](docs/commands.md) |
|
|
120
|
-
| REST API Reference | [
|
|
133
|
+
| REST API Reference | [docs/api.md](docs/api.md) |
|
|
121
134
|
|
|
122
135
|
## Dependent Projects
|
|
123
136
|
|
|
@@ -63,5 +63,14 @@ User asks to create a probe class
|
|
|
63
63
|
|
|
64
64
|
### When `disableProbeClasses = true` and `scratchWorkspace` is NOT configured
|
|
65
65
|
|
|
66
|
-
Refuse and tell the user to configure `scratchWorkspace` in `.abapGitAgent
|
|
67
|
-
|
|
66
|
+
Refuse and tell the user to configure `scratchWorkspace` in `.abapGitAgent`:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"scratchWorkspace": {
|
|
71
|
+
"path": "/absolute/path/to/scratch-repo"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The path must point to a separate git repo initialized with `abapgit-agent init --package <SCRATCH_PACKAGE>`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "abapgit-agent",
|
|
3
|
-
"version": "1.17.
|
|
3
|
+
"version": "1.17.3",
|
|
4
4
|
"description": "ABAP Git Agent - Pull and activate ABAP code via abapGit from any git repository",
|
|
5
5
|
"files": [
|
|
6
6
|
"bin/",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"abap/CLAUDE.slim.md",
|
|
11
11
|
"abap/.github/copilot-instructions.md",
|
|
12
12
|
"abap/.github/copilot-instructions.slim.md",
|
|
13
|
-
".abapGitAgent.example"
|
|
13
|
+
".abapGitAgent.example",
|
|
14
|
+
".abapgit-agent.example.json"
|
|
14
15
|
],
|
|
15
16
|
"bin": {
|
|
16
17
|
"abapgit-agent": "bin/abapgit-agent",
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
"scripts": {
|
|
20
21
|
"test": "jest",
|
|
21
22
|
"test:all": "node tests/run-all.js",
|
|
23
|
+
"test:setup": "node tests/run-all.js --setup",
|
|
22
24
|
"test:unit": "jest",
|
|
23
25
|
"test:jest": "jest",
|
|
24
26
|
"test:integration": "node tests/run-all.js --cmd",
|
package/src/commands/init.js
CHANGED
|
@@ -129,7 +129,7 @@ Usage:
|
|
|
129
129
|
|
|
130
130
|
Description:
|
|
131
131
|
Initialize local repository configuration.
|
|
132
|
-
Creates .abapGitAgent, .abapgit.xml, .gitignore, CLAUDE.md, and guidelines folder.
|
|
132
|
+
Creates .abapGitAgent, .abapgit-agent.json, .abapgit.xml, .gitignore, CLAUDE.md, and guidelines folder.
|
|
133
133
|
|
|
134
134
|
Options:
|
|
135
135
|
--package <PACKAGE> ABAP package name (required)
|
|
@@ -562,11 +562,32 @@ Uncomment and edit the rows that differ from the defaults in \`guidelines/object
|
|
|
562
562
|
console.log(`✅ .abapgit.xml already exists, skipped`);
|
|
563
563
|
}
|
|
564
564
|
|
|
565
|
+
// Create .abapgit-agent.json with default values (team config, checked into git)
|
|
566
|
+
const projectConfigPath = pathModule.join(process.cwd(), '.abapgit-agent.json');
|
|
567
|
+
if (!fs.existsSync(projectConfigPath)) {
|
|
568
|
+
const projectConfigSamplePath = pathModule.join(__dirname, '..', '..', '.abapgit-agent.example.json');
|
|
569
|
+
if (!fs.existsSync(projectConfigSamplePath)) {
|
|
570
|
+
console.error('Error: .abapgit-agent.example.json not found.');
|
|
571
|
+
process.exit(1);
|
|
572
|
+
}
|
|
573
|
+
try {
|
|
574
|
+
const projectConfig = JSON.parse(fs.readFileSync(projectConfigSamplePath, 'utf8'));
|
|
575
|
+
projectConfig.project.name = packageName;
|
|
576
|
+
fs.writeFileSync(projectConfigPath, JSON.stringify(projectConfig, null, 2) + '\n');
|
|
577
|
+
console.log(`✅ Created .abapgit-agent.json (team config — commit this to git)`);
|
|
578
|
+
} catch (error) {
|
|
579
|
+
console.error(`Error creating .abapgit-agent.json: ${error.message}`);
|
|
580
|
+
}
|
|
581
|
+
} else {
|
|
582
|
+
console.log(`✅ .abapgit-agent.json already exists, skipped`);
|
|
583
|
+
}
|
|
584
|
+
|
|
565
585
|
console.log(`
|
|
566
586
|
📋 Next steps:
|
|
567
587
|
1. Edit .abapGitAgent with your ABAP credentials (host, user, password)
|
|
568
|
-
2.
|
|
569
|
-
3. Run 'abapgit-agent
|
|
588
|
+
2. Review .abapgit-agent.json and commit it to git (team settings)
|
|
589
|
+
3. Run 'abapgit-agent create --import' to create online repository
|
|
590
|
+
4. Run 'abapgit-agent pull' to activate objects
|
|
570
591
|
|
|
571
592
|
💡 Tips:
|
|
572
593
|
• Only guidelines/objects.local.md needs to live in your repo.
|
package/src/commands/lint.js
CHANGED
|
@@ -59,13 +59,13 @@ Examples:
|
|
|
59
59
|
// ── Resolve changed files ─────────────────────────────────────────────────
|
|
60
60
|
let abapFiles;
|
|
61
61
|
if (filesArg) {
|
|
62
|
-
abapFiles = filesArg.split(',').map(f => f.trim()).filter(f => f.
|
|
62
|
+
abapFiles = filesArg.split(',').map(f => f.trim()).filter(f => isLintable(f) && fs.existsSync(f));
|
|
63
63
|
} else {
|
|
64
64
|
abapFiles = detectChangedAbapFiles(baseBranch);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
if (abapFiles.length === 0) {
|
|
68
|
-
console.log('No changed .abap files found — nothing to lint.');
|
|
68
|
+
console.log('No changed .abap/.asddls files found — nothing to lint.');
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -102,34 +102,29 @@ Examples:
|
|
|
102
102
|
// keep <file> blocks for the originally changed files — suppressing any
|
|
103
103
|
// pre-existing issues in dependency files that were not part of this change.
|
|
104
104
|
try {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const filtered = filterCheckstyleToFiles(raw, abapFilesSet);
|
|
105
|
+
const tempOut = '.abaplint-raw.xml';
|
|
106
|
+
const abapFilesSet = new Set(abapFiles.map(f => path.resolve(f)));
|
|
107
|
+
try {
|
|
108
|
+
spawnSync(
|
|
109
|
+
`npx @abaplint/cli@latest ${scopedConfig} --outformat checkstyle --outfile ${tempOut}`,
|
|
110
|
+
{ stdio: 'pipe', shell: true }
|
|
111
|
+
);
|
|
112
|
+
const raw = fs.existsSync(tempOut) ? fs.readFileSync(tempOut, 'utf8') : '<checkstyle version="8.0"/>';
|
|
113
|
+
const filtered = filterCheckstyleToFiles(raw, abapFilesSet);
|
|
114
|
+
if (outformat === 'checkstyle') {
|
|
116
115
|
if (outfile) {
|
|
117
116
|
fs.writeFileSync(outfile, filtered);
|
|
118
117
|
} else {
|
|
119
118
|
process.stdout.write(filtered);
|
|
120
119
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (fs.existsSync(tempOut)) fs.unlinkSync(tempOut);
|
|
120
|
+
} else {
|
|
121
|
+
// Interactive: print issues as human-readable text, scoped to changed files only.
|
|
122
|
+
printCheckstyleAsText(filtered);
|
|
125
123
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
{ stdio: 'inherit', shell: true }
|
|
131
|
-
);
|
|
132
|
-
if (result.status !== 0) process.exitCode = result.status;
|
|
124
|
+
const issueCount = (filtered.match(/<error /g) || []).length;
|
|
125
|
+
if (issueCount > 0) process.exitCode = 1;
|
|
126
|
+
} finally {
|
|
127
|
+
if (fs.existsSync(tempOut)) fs.unlinkSync(tempOut);
|
|
133
128
|
}
|
|
134
129
|
} finally {
|
|
135
130
|
fs.unlinkSync(scopedConfig);
|
|
@@ -156,12 +151,12 @@ function detectChangedAbapFiles(baseBranch) {
|
|
|
156
151
|
|
|
157
152
|
let diffCmd;
|
|
158
153
|
if (base) {
|
|
159
|
-
diffCmd = `git diff --name-only ${base}...HEAD -- '*.abap'`;
|
|
154
|
+
diffCmd = `git diff --name-only ${base}...HEAD -- '*.abap' '*.asddls'`;
|
|
160
155
|
} else {
|
|
161
156
|
// Fall back to uncommitted changes first, then last commit
|
|
162
|
-
const uncommitted = runGit(
|
|
157
|
+
const uncommitted = runGit(`git diff --name-only HEAD -- '*.abap' '*.asddls'`).filter(Boolean);
|
|
163
158
|
if (uncommitted.length > 0) return filterAbapFiles(uncommitted);
|
|
164
|
-
diffCmd = `git diff --name-only HEAD~1 HEAD -- '*.abap'`;
|
|
159
|
+
diffCmd = `git diff --name-only HEAD~1 HEAD -- '*.abap' '*.asddls'`;
|
|
165
160
|
}
|
|
166
161
|
|
|
167
162
|
return filterAbapFiles(runGit(diffCmd));
|
|
@@ -190,7 +185,7 @@ function buildFileIndex(abapDir) {
|
|
|
190
185
|
const full = path.join(dir, entry.name);
|
|
191
186
|
if (entry.isDirectory()) {
|
|
192
187
|
walk(full);
|
|
193
|
-
} else if (entry.name.endsWith('.abap') || entry.name.endsWith('.xml')) {
|
|
188
|
+
} else if (entry.name.endsWith('.abap') || entry.name.endsWith('.xml') || entry.name.endsWith('.asddls')) {
|
|
194
189
|
index.set(entry.name.toLowerCase(), full);
|
|
195
190
|
}
|
|
196
191
|
}
|
|
@@ -304,13 +299,47 @@ function filterCheckstyleToFiles(xml, abapFilesSet) {
|
|
|
304
299
|
}
|
|
305
300
|
|
|
306
301
|
/**
|
|
307
|
-
*
|
|
308
|
-
*
|
|
302
|
+
* Print checkstyle XML as human-readable text, mirroring abaplint's default output format:
|
|
303
|
+
* path/to/file.clas.abap:line - severity - message (rule)
|
|
304
|
+
*/
|
|
305
|
+
function printCheckstyleAsText(xml) {
|
|
306
|
+
const fileRe = /<file\s+name="([^"]*)"([\s\S]*?)<\/file>/g;
|
|
307
|
+
const errorRe = /<error\s+[^>]*line="(\d+)"[^>]*severity="([^"]*)"[^>]*message="([^"]*)"[^>]*source="([^"]*)"/g;
|
|
308
|
+
let fileMatch;
|
|
309
|
+
let issueCount = 0;
|
|
310
|
+
while ((fileMatch = fileRe.exec(xml)) !== null) {
|
|
311
|
+
const filePath = fileMatch[1];
|
|
312
|
+
const block = fileMatch[2];
|
|
313
|
+
errorRe.lastIndex = 0;
|
|
314
|
+
let errMatch;
|
|
315
|
+
while ((errMatch = errorRe.exec(block)) !== null) {
|
|
316
|
+
const [, line, severity, message, source] = errMatch;
|
|
317
|
+
const rule = source.split('.').pop();
|
|
318
|
+
const text = message.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>').replace(/'/g, "'");
|
|
319
|
+
console.log(`${filePath}:${line} - ${severity} - ${text} (${rule})`);
|
|
320
|
+
issueCount++;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (issueCount === 0) {
|
|
324
|
+
console.log('No issues found.');
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Returns true if the file is a type abaplint can analyse.
|
|
330
|
+
* - .abap — all ABAP source files (CLAS, INTF, PROG, FUGR, ENHO, etc.)
|
|
331
|
+
* - .asddls — CDS view / view entity sources (DDLS)
|
|
332
|
+
*/
|
|
333
|
+
function isLintable(f) {
|
|
334
|
+
const lower = f.toLowerCase();
|
|
335
|
+
return lower.endsWith('.abap') || lower.endsWith('.asddls');
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Keep only lintable files (name.type.abap / name.type.subtype.abap / name.ddls.asddls)
|
|
340
|
+
* that still exist on disk. Deleted files appear in git diff output but must not
|
|
341
|
+
* be passed to abaplint.
|
|
309
342
|
*/
|
|
310
343
|
function filterAbapFiles(files) {
|
|
311
|
-
return files.filter(f =>
|
|
312
|
-
const parts = path.basename(f).split('.');
|
|
313
|
-
return (parts.length === 3 || parts.length === 4) &&
|
|
314
|
-
parts[parts.length - 1].toLowerCase() === 'abap';
|
|
315
|
-
});
|
|
344
|
+
return files.filter(f => isLintable(f) && fs.existsSync(f));
|
|
316
345
|
}
|
|
@@ -215,4 +215,4 @@ npm test -- tests/unit/backgroundJobPoller.test.js
|
|
|
215
215
|
|
|
216
216
|
- [Background Job Architecture](../../docs/architecture/README.md) - Complete architecture documentation
|
|
217
217
|
- [Import Command](../commands/import.js) - Reference implementation
|
|
218
|
-
- [API Documentation](../../
|
|
218
|
+
- [API Documentation](../../docs/api.md) - REST API for async commands
|