abapgit-agent 1.13.7 → 1.14.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/README.md +8 -5
- package/abap/CLAUDE.md +18 -2
- package/abap/guidelines/abaplint.md +52 -1
- package/abap/guidelines/branch-workflow.md +4 -0
- package/package.json +1 -1
- package/src/commands/index.js +2 -1
- package/src/commands/lint.js +130 -0
package/README.md
CHANGED
|
@@ -97,14 +97,17 @@ abapgit-agent health # Verify ABAP connection
|
|
|
97
97
|
|
|
98
98
|
```bash
|
|
99
99
|
# Install dependencies
|
|
100
|
-
cd abapgit-agent
|
|
101
100
|
npm install
|
|
102
101
|
|
|
103
|
-
# Run
|
|
104
|
-
|
|
102
|
+
# Run unit tests (no ABAP system needed)
|
|
103
|
+
npm test
|
|
105
104
|
|
|
106
|
-
#
|
|
107
|
-
|
|
105
|
+
# Test a command manually
|
|
106
|
+
node bin/abapgit-agent --help
|
|
107
|
+
node bin/abapgit-agent syntax --files src/zcl_my_class.clas.abap
|
|
108
|
+
|
|
109
|
+
# Run integration tests against a real ABAP system (requires .abapGitAgent)
|
|
110
|
+
npm run test:integration
|
|
108
111
|
```
|
|
109
112
|
|
|
110
113
|
## Documentation
|
package/abap/CLAUDE.md
CHANGED
|
@@ -471,7 +471,7 @@ Run abaplint as step 4b — after `syntax`, before `git commit`:
|
|
|
471
471
|
|
|
472
472
|
```bash
|
|
473
473
|
# Only if .abaplint.json exists
|
|
474
|
-
|
|
474
|
+
abapgit-agent lint
|
|
475
475
|
```
|
|
476
476
|
|
|
477
477
|
Fix any reported issues, then commit.
|
|
@@ -539,6 +539,21 @@ See **AI Tool Guidelines** below for how to react to each setting.
|
|
|
539
539
|
### Branch Workflow (`"mode": "branch"`)
|
|
540
540
|
|
|
541
541
|
Always work on feature branches. Before every `pull`: rebase to default branch. On completion: create PR with squash merge.
|
|
542
|
+
|
|
543
|
+
```bash
|
|
544
|
+
git checkout main # or master/develop (auto-detected)
|
|
545
|
+
git pull origin main
|
|
546
|
+
git checkout -b feature/my-change
|
|
547
|
+
# edit your ABAP file (name from objects.local.md)
|
|
548
|
+
abapgit-agent syntax --files src/<name>.clas.abap
|
|
549
|
+
ls .abaplint.json 2>/dev/null && abapgit-agent lint # abaplint (if configured)
|
|
550
|
+
git add . && git commit -m "feat: description"
|
|
551
|
+
git push origin feature/my-change
|
|
552
|
+
git fetch origin main && git rebase origin/main
|
|
553
|
+
git push origin feature/my-change --force-with-lease
|
|
554
|
+
abapgit-agent pull --files src/<name>.clas.abap --sync-xml
|
|
555
|
+
```
|
|
556
|
+
|
|
542
557
|
→ See `guidelines/branch-workflow.md` — run: `abapgit-agent ref --topic branch-workflow`
|
|
543
558
|
|
|
544
559
|
### Trunk Workflow (`"mode": "trunk"`)
|
|
@@ -550,6 +565,7 @@ git checkout main # or master/develop (auto-detected)
|
|
|
550
565
|
git pull origin main
|
|
551
566
|
# edit your ABAP file (name from objects.local.md)
|
|
552
567
|
abapgit-agent syntax --files src/<name>.clas.abap
|
|
568
|
+
ls .abaplint.json 2>/dev/null && abapgit-agent lint # abaplint (if configured)
|
|
553
569
|
git add . && git commit -m "feat: description"
|
|
554
570
|
git push origin main
|
|
555
571
|
abapgit-agent pull --files src/<name>.clas.abap --sync-xml
|
|
@@ -649,7 +665,7 @@ Modified ABAP files?
|
|
|
649
665
|
└─ FUGR and other complex objects?
|
|
650
666
|
└─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --sync-xml → (if errors: inspect)
|
|
651
667
|
|
|
652
|
-
[abaplint] = run
|
|
668
|
+
[abaplint] = run abapgit-agent lint only if .abaplint.json exists in repo root
|
|
653
669
|
before applying any quickfix: run abapgit-agent ref --topic abaplint
|
|
654
670
|
```
|
|
655
671
|
|
|
@@ -9,7 +9,9 @@ grand_parent: ABAP Development
|
|
|
9
9
|
# abaplint Rule Guidelines
|
|
10
10
|
|
|
11
11
|
**Searchable keywords**: abaplint, prefer_inline, inline declaration, char literal, string truncation,
|
|
12
|
-
no_inline_in_optional_branches, fully_type_constants, linting, static analysis
|
|
12
|
+
no_inline_in_optional_branches, fully_type_constants, linting, static analysis,
|
|
13
|
+
run abaplint locally, check changed file, abapgit-agent lint,
|
|
14
|
+
keyword_case, sequential_blank, double_space, use_new, local_variable_names
|
|
13
15
|
|
|
14
16
|
This file covers rules that have **non-obvious or dangerous implications** — cases where applying
|
|
15
17
|
a rule mechanically (or accepting its quickfix) can introduce subtle bugs.
|
|
@@ -166,8 +168,57 @@ on rv_result — no intermediate lv_response variable at all.
|
|
|
166
168
|
|
|
167
169
|
---
|
|
168
170
|
|
|
171
|
+
## Running abaplint Locally Against Changed Files
|
|
172
|
+
|
|
173
|
+
Run this before pushing to catch issues early, matching what CI does.
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
abapgit-agent lint
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
This automatically detects changed `.abap` files (via `git diff`), creates a scoped
|
|
180
|
+
abaplint config for just those files, runs the check, and cleans up.
|
|
181
|
+
|
|
182
|
+
### Options
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# Diff against a specific base branch (useful on a feature branch)
|
|
186
|
+
abapgit-agent lint --base main
|
|
187
|
+
|
|
188
|
+
# Check specific files explicitly
|
|
189
|
+
abapgit-agent lint --files src/zcl_foo.clas.abap,src/zcl_foo.clas.testclasses.abap
|
|
190
|
+
|
|
191
|
+
# Use a different abaplint config (default: .abaplint.json)
|
|
192
|
+
abapgit-agent lint --config .abaplint.json
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Run repeatedly after each fix until you see:
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
abaplint: 0 issue(s) found, 1 file(s) analyzed
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
### Common Issues and Fixes
|
|
204
|
+
|
|
205
|
+
| Rule | Error message | Fix |
|
|
206
|
+
|------|--------------|-----|
|
|
207
|
+
| `keyword_case` | `Keyword should be upper case: "class"` | Uppercase the keyword: `CLASS` |
|
|
208
|
+
| `sequential_blank` | `Remove sequential blank lines` | Max 1 blank line between blocks |
|
|
209
|
+
| `local_variable_names` | `<fs_data> does not match pattern` | Use `l`-prefixed name: `<ls_data>` |
|
|
210
|
+
| `double_space` | `Remove double space` | Single space around `=` in parameters |
|
|
211
|
+
| `use_new` | `Use NEW #() to instantiate` | Replace `CREATE OBJECT mo_foo` → `mo_foo = NEW #( )` |
|
|
212
|
+
| `method_parameter_names` | `Parameter name does not match pattern` | Use `iv_`, `it_`, `is_`, `io_` etc. prefixes |
|
|
213
|
+
|
|
214
|
+
See **abaplint-local.md** for the full naming convention prefix reference.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
|
|
169
219
|
## See Also
|
|
170
220
|
|
|
171
221
|
- **common-errors.md** — char-literal truncation listed as a known error pattern
|
|
172
222
|
- **json.md** — safe patterns for building JSON strings in ABAP
|
|
173
223
|
- **workflow-detailed.md** — where abaplint fits in the development workflow
|
|
224
|
+
- **abaplint-local.md** — naming convention reference (prefixes for variables, parameters, field-symbols)
|
|
@@ -21,6 +21,7 @@ edit src/zcl_auth_handler.clas.abap
|
|
|
21
21
|
|
|
22
22
|
# 3. Check syntax (CLAS/INTF/PROG/DDLS only, if independent)
|
|
23
23
|
abapgit-agent syntax --files src/zcl_auth_handler.clas.abap
|
|
24
|
+
ls .abaplint.json 2>/dev/null && abapgit-agent lint # abaplint (if configured)
|
|
24
25
|
|
|
25
26
|
# 4. Commit
|
|
26
27
|
git add src/zcl_auth_handler.clas.abap
|
|
@@ -112,6 +113,7 @@ git checkout main && git pull origin main
|
|
|
112
113
|
git checkout -b feature/user-authentication
|
|
113
114
|
edit src/zcl_auth_handler.clas.abap
|
|
114
115
|
abapgit-agent syntax --files src/zcl_auth_handler.clas.abap
|
|
116
|
+
ls .abaplint.json 2>/dev/null && abapgit-agent lint # abaplint (if configured)
|
|
115
117
|
git add . && git commit -m "wip: add basic auth logic"
|
|
116
118
|
git push origin feature/user-authentication
|
|
117
119
|
git fetch origin main && git rebase origin/main
|
|
@@ -123,6 +125,8 @@ git fetch origin main && git rebase origin/main
|
|
|
123
125
|
# If conflicts: resolve, git add, git rebase --continue
|
|
124
126
|
git push origin feature/user-authentication --force-with-lease
|
|
125
127
|
edit src/zcl_auth_handler.clas.abap
|
|
128
|
+
abapgit-agent syntax --files src/zcl_auth_handler.clas.abap
|
|
129
|
+
ls .abaplint.json 2>/dev/null && abapgit-agent lint # abaplint (if configured)
|
|
126
130
|
git add . && git commit -m "feat: complete auth logic"
|
|
127
131
|
git push origin feature/user-authentication
|
|
128
132
|
git fetch origin main && git rebase origin/main
|
package/package.json
CHANGED
package/src/commands/index.js
CHANGED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* lint command - Run abaplint on changed ABAP files
|
|
5
|
+
*
|
|
6
|
+
* Detects files changed relative to a base branch (or HEAD~1),
|
|
7
|
+
* creates a scoped abaplint config for just those files, and runs the check.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* abapgit-agent lint
|
|
11
|
+
* abapgit-agent lint --config .abaplint.json
|
|
12
|
+
* abapgit-agent lint --base main
|
|
13
|
+
* abapgit-agent lint --files src/foo.clas.abap,src/foo.clas.testclasses.abap
|
|
14
|
+
* abapgit-agent lint --outformat checkstyle --outfile reports/abaplint-results.xml
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const { execSync, spawnSync } = require('child_process');
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
name: 'lint',
|
|
23
|
+
description: 'Run abaplint on changed ABAP files',
|
|
24
|
+
requiresAbapConfig: false,
|
|
25
|
+
|
|
26
|
+
execute(args) {
|
|
27
|
+
const configPath = argValue(args, '--config') || '.abaplint.json';
|
|
28
|
+
const baseBranch = argValue(args, '--base');
|
|
29
|
+
const filesArg = argValue(args, '--files');
|
|
30
|
+
const outformat = argValue(args, '--outformat');
|
|
31
|
+
const outfile = argValue(args, '--outfile');
|
|
32
|
+
|
|
33
|
+
// ── Resolve changed files ─────────────────────────────────────────────────
|
|
34
|
+
let abapFiles;
|
|
35
|
+
if (filesArg) {
|
|
36
|
+
abapFiles = filesArg.split(',').map(f => f.trim()).filter(f => f.endsWith('.abap'));
|
|
37
|
+
} else {
|
|
38
|
+
abapFiles = detectChangedAbapFiles(baseBranch);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (abapFiles.length === 0) {
|
|
42
|
+
console.log('No changed .abap files found — nothing to lint.');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!outfile) {
|
|
47
|
+
console.log(`\nLinting ${abapFiles.length} file(s):`);
|
|
48
|
+
abapFiles.forEach(f => console.log(` ${f}`));
|
|
49
|
+
console.log('');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ── Load and scope the abaplint config ────────────────────────────────────
|
|
53
|
+
if (!fs.existsSync(configPath)) {
|
|
54
|
+
console.error(`Error: abaplint config not found: ${configPath}`);
|
|
55
|
+
console.error('Run from the project root, or pass --config <path>.');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const cfg = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
60
|
+
cfg.global.files = abapFiles.map(f => `/${f}`);
|
|
61
|
+
|
|
62
|
+
const scopedConfig = '.abaplint-local.json';
|
|
63
|
+
fs.writeFileSync(scopedConfig, JSON.stringify(cfg, null, 2));
|
|
64
|
+
|
|
65
|
+
// ── Run abaplint ──────────────────────────────────────────────────────────
|
|
66
|
+
try {
|
|
67
|
+
const formatArgs = outformat ? `--outformat ${outformat}` : '';
|
|
68
|
+
const fileArgs = outfile ? `--outfile ${outfile}` : '';
|
|
69
|
+
const result = spawnSync(
|
|
70
|
+
`npx @abaplint/cli@latest ${scopedConfig} ${formatArgs} ${fileArgs}`,
|
|
71
|
+
{ stdio: 'inherit', shell: true }
|
|
72
|
+
);
|
|
73
|
+
if (result.status !== 0) {
|
|
74
|
+
process.exitCode = result.status;
|
|
75
|
+
}
|
|
76
|
+
} finally {
|
|
77
|
+
fs.unlinkSync(scopedConfig);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
function argValue(args, flag) {
|
|
85
|
+
const idx = args.indexOf(flag);
|
|
86
|
+
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Detect changed .abap files using git diff.
|
|
91
|
+
* - If on a PR branch (CHANGE_TARGET set, e.g. in CI): diffs against that target.
|
|
92
|
+
* - If --base is given: diffs against that branch.
|
|
93
|
+
* - Otherwise: diffs HEAD~1..HEAD (last commit).
|
|
94
|
+
*/
|
|
95
|
+
function detectChangedAbapFiles(baseBranch) {
|
|
96
|
+
const base = baseBranch
|
|
97
|
+
|| (process.env.CHANGE_TARGET ? `origin/${process.env.CHANGE_TARGET}` : null);
|
|
98
|
+
|
|
99
|
+
let diffCmd;
|
|
100
|
+
if (base) {
|
|
101
|
+
diffCmd = `git diff --name-only ${base}...HEAD -- '*.abap'`;
|
|
102
|
+
} else {
|
|
103
|
+
// Fall back to uncommitted changes first, then last commit
|
|
104
|
+
const uncommitted = runGit('git diff --name-only HEAD -- *.abap').filter(Boolean);
|
|
105
|
+
if (uncommitted.length > 0) return filterAbapFiles(uncommitted);
|
|
106
|
+
diffCmd = `git diff --name-only HEAD~1 HEAD -- '*.abap'`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return filterAbapFiles(runGit(diffCmd));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function runGit(cmd) {
|
|
113
|
+
try {
|
|
114
|
+
return execSync(cmd, { encoding: 'utf8' }).trim().split('\n').filter(Boolean);
|
|
115
|
+
} catch {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Keep only files that look like ABAP source files
|
|
122
|
+
* (name.type.abap or name.type.subtype.abap).
|
|
123
|
+
*/
|
|
124
|
+
function filterAbapFiles(files) {
|
|
125
|
+
return files.filter(f => {
|
|
126
|
+
const parts = path.basename(f).split('.');
|
|
127
|
+
return (parts.length === 3 || parts.length === 4) &&
|
|
128
|
+
parts[parts.length - 1].toLowerCase() === 'abap';
|
|
129
|
+
});
|
|
130
|
+
}
|