abapgit-agent 1.14.0 → 1.14.2
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.
|
@@ -73,10 +73,47 @@ abapgit-agent pull --files src/zcl_auth_handler.clas.abap
|
|
|
73
73
|
abapgit-agent unit --files src/zcl_auth_handler.clas.testclasses.abap
|
|
74
74
|
|
|
75
75
|
# 3. Create PR (squash merge enabled on GitHub/GitLab)
|
|
76
|
-
#
|
|
77
|
-
# Select "Squash and merge" option to combine all commits into one
|
|
76
|
+
# See "Creating a PR" section below for transport handling
|
|
78
77
|
```
|
|
79
78
|
|
|
79
|
+
#### Creating a PR
|
|
80
|
+
|
|
81
|
+
When creating a PR with `gh pr create`:
|
|
82
|
+
|
|
83
|
+
**Check `.abapgit-agent.json` for `transports.hook`.**
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
transports.hook present?
|
|
87
|
+
├─ NO → create PR normally, no transport line needed. STOP here.
|
|
88
|
+
└─ YES → continue below
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**If hook is configured — resolve which transport to include:**
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
abapgit-agent transport list --scope task --json
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
- **One result** → use it directly, no need to ask the user
|
|
98
|
+
- **Multiple results** → show the list to the user and ask which one to use for this PR
|
|
99
|
+
- **No results** → ask the user to provide a transport number manually, or confirm to skip
|
|
100
|
+
|
|
101
|
+
**Append `transport: <number>` as the last line of the PR body:**
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
## Summary
|
|
105
|
+
- Implemented user authentication handler
|
|
106
|
+
|
|
107
|
+
## Changed Objects
|
|
108
|
+
- ZCL_AUTH_HANDLER (CLAS)
|
|
109
|
+
|
|
110
|
+
transport: DEVK900001
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Why:** The CI pipeline reads this line from the PR description and uses it for the
|
|
114
|
+
Activate stage, overriding the automatic hook selection. This lets the user control
|
|
115
|
+
which transport request receives the activated changes.
|
|
116
|
+
|
|
80
117
|
#### Why Rebase Before Pull?
|
|
81
118
|
|
|
82
119
|
ABAP is a **centralized system**. Multiple developers may modify the same files:
|
|
@@ -137,5 +174,6 @@ abapgit-agent pull --files src/zcl_auth_handler.clas.abap
|
|
|
137
174
|
abapgit-agent unit --files src/zcl_auth_handler.clas.testclasses.abap
|
|
138
175
|
git fetch origin main && git rebase origin/main
|
|
139
176
|
git push origin feature/user-authentication --force-with-lease
|
|
140
|
-
# Create PR
|
|
177
|
+
# Create PR: check transport hook, resolve transport, include in PR body
|
|
178
|
+
# abapgit-agent transport list --scope task --json → pick transport → append to PR body
|
|
141
179
|
```
|
package/package.json
CHANGED
package/src/commands/lint.js
CHANGED
|
@@ -57,7 +57,13 @@ module.exports = {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
const cfg = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
60
|
-
|
|
60
|
+
|
|
61
|
+
// Scope to changed files + their direct dependencies (interfaces, superclasses)
|
|
62
|
+
// so abaplint can resolve cross-references without including the whole repo.
|
|
63
|
+
const abapDir = cfg.global.files.replace(/\/\*\*.*$/, '').replace(/^\//, '') || 'abap';
|
|
64
|
+
const depFiles = resolveDependencies(abapFiles, abapDir);
|
|
65
|
+
const allFiles = [...new Set([...abapFiles, ...depFiles])];
|
|
66
|
+
cfg.global.files = allFiles.map(f => `/${f}`);
|
|
61
67
|
|
|
62
68
|
const scopedConfig = '.abaplint-local.json';
|
|
63
69
|
fs.writeFileSync(scopedConfig, JSON.stringify(cfg, null, 2));
|
|
@@ -117,6 +123,84 @@ function runGit(cmd) {
|
|
|
117
123
|
}
|
|
118
124
|
}
|
|
119
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Resolve direct dependencies of the given ABAP files by scanning their source
|
|
128
|
+
* for interface/superclass/type references and mapping them to local files.
|
|
129
|
+
*
|
|
130
|
+
* Handles:
|
|
131
|
+
* INTERFACES <name>. → <name>.intf.abap + <name>.intf.xml
|
|
132
|
+
* INHERITING FROM <name> → <name>.clas.abap + <name>.clas.xml
|
|
133
|
+
* TYPE REF TO <name> → <name>.intf.abap or <name>.clas.abap (whichever exists)
|
|
134
|
+
*
|
|
135
|
+
* Only resolves one level deep — enough for abaplint to check the changed files.
|
|
136
|
+
* XML companion files are always included alongside their .abap counterpart
|
|
137
|
+
* so xml_consistency checks can run.
|
|
138
|
+
*/
|
|
139
|
+
function resolveDependencies(abapFiles, abapDir) {
|
|
140
|
+
const deps = new Set();
|
|
141
|
+
const visited = new Set(abapFiles); // don't re-scan changed files as deps
|
|
142
|
+
|
|
143
|
+
// Patterns to extract referenced object names from ABAP source
|
|
144
|
+
const patterns = [
|
|
145
|
+
/^\s*INTERFACES\s+(\w+)\s*\./gim,
|
|
146
|
+
/INHERITING\s+FROM\s+(\w+)/gim,
|
|
147
|
+
/TYPE\s+REF\s+TO\s+(\w+)/gim,
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
// BFS queue — seed with the changed files, then expand transitively
|
|
151
|
+
const queue = [...abapFiles];
|
|
152
|
+
|
|
153
|
+
while (queue.length > 0) {
|
|
154
|
+
const file = queue.shift();
|
|
155
|
+
|
|
156
|
+
let source;
|
|
157
|
+
try { source = fs.readFileSync(file, 'utf8'); } catch { continue; }
|
|
158
|
+
|
|
159
|
+
for (const pattern of patterns) {
|
|
160
|
+
pattern.lastIndex = 0;
|
|
161
|
+
let match;
|
|
162
|
+
while ((match = pattern.exec(source)) !== null) {
|
|
163
|
+
const name = match[1].toLowerCase();
|
|
164
|
+
for (const suffix of [`${name}.intf`, `${name}.clas`]) {
|
|
165
|
+
const abapFile = path.join(abapDir, `${suffix}.abap`);
|
|
166
|
+
const xmlFile = path.join(abapDir, `${suffix}.xml`);
|
|
167
|
+
if (fs.existsSync(abapFile)) {
|
|
168
|
+
deps.add(abapFile);
|
|
169
|
+
if (fs.existsSync(xmlFile)) deps.add(xmlFile);
|
|
170
|
+
// Recurse into this dep if not yet visited
|
|
171
|
+
if (!visited.has(abapFile)) {
|
|
172
|
+
visited.add(abapFile);
|
|
173
|
+
queue.push(abapFile);
|
|
174
|
+
}
|
|
175
|
+
// For interfaces, also include the canonical concrete implementation
|
|
176
|
+
// (zif_foo → zcl_foo) so rules like unused_variables can fully type-check.
|
|
177
|
+
if (suffix.endsWith('.intf')) {
|
|
178
|
+
const implName = name.replace(/^zif_/, 'zcl_');
|
|
179
|
+
const implFile = path.join(abapDir, `${implName}.clas.abap`);
|
|
180
|
+
const implXml = path.join(abapDir, `${implName}.clas.xml`);
|
|
181
|
+
if (fs.existsSync(implFile)) {
|
|
182
|
+
deps.add(implFile);
|
|
183
|
+
if (fs.existsSync(implXml)) deps.add(implXml);
|
|
184
|
+
if (!visited.has(implFile)) {
|
|
185
|
+
visited.add(implFile);
|
|
186
|
+
queue.push(implFile);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
break; // intf matched — don't also try clas
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Always include the XML companion of each scanned file
|
|
197
|
+
const xmlCompanion = file.replace(/\.abap$/, '.xml');
|
|
198
|
+
if (fs.existsSync(xmlCompanion)) deps.add(xmlCompanion);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return [...deps];
|
|
202
|
+
}
|
|
203
|
+
|
|
120
204
|
/**
|
|
121
205
|
* Keep only files that look like ABAP source files
|
|
122
206
|
* (name.type.abap or name.type.subtype.abap).
|