abapgit-agent 1.8.2 → 1.8.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/abap/CLAUDE.md +182 -1
- package/abap/guidelines/03_testing.md +45 -6
- package/abap/guidelines/04_cds.md +113 -5
- package/package.json +2 -1
- package/src/commands/init.js +81 -20
- package/src/commands/ref.js +1 -1
- package/src/config.js +16 -1
- package/src/utils/git-utils.js +49 -1
package/abap/CLAUDE.md
CHANGED
|
@@ -258,8 +258,189 @@ abapgit-agent unit --files src/zcl_test1.clas.testclasses.abap,src/zcl_test2.cla
|
|
|
258
258
|
|
|
259
259
|
## Development Workflow
|
|
260
260
|
|
|
261
|
+
This project's workflow mode is configured in `.abapGitAgent` under `workflow.mode`.
|
|
262
|
+
|
|
263
|
+
### Workflow Modes
|
|
264
|
+
|
|
265
|
+
| Mode | Branch Strategy | Rebase Before Pull | Create PR |
|
|
266
|
+
|------|----------------|-------------------|-----------|
|
|
267
|
+
| `"branch"` | Feature branches | ✓ Always | ✓ Yes (squash merge) |
|
|
268
|
+
| `"trunk"` | Direct to default branch | ✗ No | ✗ No |
|
|
269
|
+
| (not set) | Direct to default branch | ✗ No | ✗ No |
|
|
270
|
+
|
|
271
|
+
**Default branch** (main/master/develop) is **auto-detected** from your git repository.
|
|
272
|
+
|
|
273
|
+
### Branch Workflow (`"mode": "branch"`)
|
|
274
|
+
|
|
275
|
+
**IMPORTANT**: Always work on feature branches, never commit directly to the default branch.
|
|
276
|
+
|
|
277
|
+
#### Starting a New Feature
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
# 1. Create and switch to feature branch from default branch
|
|
281
|
+
git checkout main # or master/develop (auto-detected)
|
|
282
|
+
git pull origin main
|
|
283
|
+
git checkout -b feature/user-authentication
|
|
284
|
+
|
|
285
|
+
# 2. Make your changes
|
|
286
|
+
edit src/zcl_auth_handler.clas.abap
|
|
287
|
+
|
|
288
|
+
# 3. Check syntax (CLAS/INTF/PROG/DDLS only, if independent)
|
|
289
|
+
abapgit-agent syntax --files src/zcl_auth_handler.clas.abap
|
|
290
|
+
|
|
291
|
+
# 4. Commit
|
|
292
|
+
git add src/zcl_auth_handler.clas.abap
|
|
293
|
+
git commit -m "feat: add authentication handler"
|
|
294
|
+
|
|
295
|
+
# 5. Push feature branch
|
|
296
|
+
git push origin feature/user-authentication
|
|
297
|
+
|
|
298
|
+
# 6. **CRITICAL**: Rebase before pull
|
|
299
|
+
git fetch origin main
|
|
300
|
+
git rebase origin/main
|
|
301
|
+
git push origin feature/user-authentication --force-with-lease
|
|
302
|
+
|
|
303
|
+
# 7. Pull to ABAP system
|
|
304
|
+
abapgit-agent pull --files src/zcl_auth_handler.clas.abap
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
#### During Development: Always Rebase Before Pull
|
|
308
|
+
|
|
309
|
+
**CRITICAL**: Before every `pull` command, rebase to default branch to avoid activating outdated code.
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
# Before EVERY pull, always do this:
|
|
313
|
+
git fetch origin main # main/master/develop (auto-detected)
|
|
314
|
+
git rebase origin/main
|
|
315
|
+
|
|
316
|
+
# If no conflicts:
|
|
317
|
+
git push origin feature/user-authentication --force-with-lease
|
|
318
|
+
abapgit-agent pull --files src/zcl_auth_handler.clas.abap
|
|
319
|
+
|
|
320
|
+
# If conflicts:
|
|
321
|
+
# 1. Fix conflicts in files
|
|
322
|
+
# 2. git add <resolved-files>
|
|
323
|
+
# 3. git rebase --continue
|
|
324
|
+
# 4. git push origin feature/user-authentication --force-with-lease
|
|
325
|
+
# 5. abapgit-agent pull --files ...
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
#### Completing the Feature
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
# 1. Final rebase and push
|
|
332
|
+
git fetch origin main
|
|
333
|
+
git rebase origin/main
|
|
334
|
+
git push origin feature/user-authentication --force-with-lease
|
|
335
|
+
|
|
336
|
+
# 2. Final activation and test
|
|
337
|
+
abapgit-agent pull --files src/zcl_auth_handler.clas.abap
|
|
338
|
+
abapgit-agent unit --files src/zcl_auth_handler.clas.testclasses.abap
|
|
339
|
+
|
|
340
|
+
# 3. Create PR (squash merge enabled on GitHub/GitLab)
|
|
341
|
+
# Go to GitHub and create PR from feature/user-authentication to main
|
|
342
|
+
# Select "Squash and merge" option to combine all commits into one
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### Why Rebase Before Pull?
|
|
346
|
+
|
|
347
|
+
ABAP is a **centralized system**. Multiple developers may modify the same files:
|
|
348
|
+
|
|
349
|
+
| Without Rebase | With Rebase |
|
|
350
|
+
|----------------|-------------|
|
|
351
|
+
| ✗ Your branch is based on old main | ✓ Your branch includes latest changes |
|
|
352
|
+
| ✗ Activate outdated code in ABAP | ✓ Activate current code |
|
|
353
|
+
| ✗ May overwrite others' work | ✓ Conflicts caught before activation |
|
|
354
|
+
| ✗ Hard to debug issues | ✓ Clear what changed |
|
|
355
|
+
|
|
356
|
+
**Example Scenario:**
|
|
357
|
+
|
|
358
|
+
```
|
|
359
|
+
Situation:
|
|
360
|
+
- You: working on feature/auth (based on main commit A)
|
|
361
|
+
- Colleague: pushed to main (now at commit B)
|
|
362
|
+
- Both modified: src/zcl_auth_handler.clas.abap
|
|
363
|
+
|
|
364
|
+
Without rebase:
|
|
365
|
+
feature/auth pull → activates version from commit A ✗
|
|
366
|
+
|
|
367
|
+
With rebase:
|
|
368
|
+
git rebase origin/main → either:
|
|
369
|
+
- No conflict: includes colleague's changes ✓
|
|
370
|
+
- Conflict: you see it and resolve ✓
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
#### Complete Example Workflow (Branch Mode)
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
# Day 1: Start feature
|
|
377
|
+
git checkout main && git pull origin main
|
|
378
|
+
git checkout -b feature/user-authentication
|
|
379
|
+
edit src/zcl_auth_handler.clas.abap
|
|
380
|
+
abapgit-agent syntax --files src/zcl_auth_handler.clas.abap
|
|
381
|
+
git add . && git commit -m "wip: add basic auth logic"
|
|
382
|
+
git push origin feature/user-authentication
|
|
383
|
+
git fetch origin main && git rebase origin/main
|
|
384
|
+
git push origin feature/user-authentication --force-with-lease
|
|
385
|
+
abapgit-agent pull --files src/zcl_auth_handler.clas.abap
|
|
386
|
+
|
|
387
|
+
# Day 2: Continue (colleague pushed to main overnight)
|
|
388
|
+
git fetch origin main && git rebase origin/main
|
|
389
|
+
# If conflicts: resolve, git add, git rebase --continue
|
|
390
|
+
git push origin feature/user-authentication --force-with-lease
|
|
391
|
+
edit src/zcl_auth_handler.clas.abap
|
|
392
|
+
git add . && git commit -m "feat: complete auth logic"
|
|
393
|
+
git push origin feature/user-authentication
|
|
394
|
+
git fetch origin main && git rebase origin/main
|
|
395
|
+
git push origin feature/user-authentication --force-with-lease
|
|
396
|
+
abapgit-agent pull --files src/zcl_auth_handler.clas.abap
|
|
397
|
+
|
|
398
|
+
# Day 3: Finish feature
|
|
399
|
+
abapgit-agent unit --files src/zcl_auth_handler.clas.testclasses.abap
|
|
400
|
+
git fetch origin main && git rebase origin/main
|
|
401
|
+
git push origin feature/user-authentication --force-with-lease
|
|
402
|
+
# Create PR on GitHub/GitLab (squash 3 commits into 1)
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Trunk Workflow (`"mode": "trunk"`)
|
|
406
|
+
|
|
407
|
+
If workflow mode is `"trunk"` or not set, commit directly to the default branch:
|
|
408
|
+
|
|
409
|
+
```bash
|
|
410
|
+
git checkout main # or master/develop (auto-detected)
|
|
411
|
+
git pull origin main
|
|
412
|
+
edit src/zcl_auth_handler.clas.abap
|
|
413
|
+
abapgit-agent syntax --files src/zcl_auth_handler.clas.abap
|
|
414
|
+
git add . && git commit -m "feat: add authentication handler"
|
|
415
|
+
git push origin main
|
|
416
|
+
abapgit-agent pull --files src/zcl_auth_handler.clas.abap
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### AI Tool Guidelines
|
|
420
|
+
|
|
421
|
+
**Read `.abapGitAgent` to determine workflow mode:**
|
|
422
|
+
|
|
423
|
+
**When `workflow.mode = "branch"`:**
|
|
424
|
+
1. ✓ Auto-detect default branch (main/master/develop)
|
|
425
|
+
2. ✓ Create feature branches (naming: `feature/description`)
|
|
426
|
+
3. ✓ Always `git fetch origin <default> && git rebase origin/<default>` before `pull` command
|
|
427
|
+
4. ✓ Use `--force-with-lease` after rebase (never `--force`)
|
|
428
|
+
5. ✓ Create PR with squash merge when feature complete
|
|
429
|
+
6. ✗ Never commit directly to default branch
|
|
430
|
+
7. ✗ Never use `git push --force` (always use `--force-with-lease`)
|
|
431
|
+
|
|
432
|
+
**When `workflow.mode = "trunk"` or not set:**
|
|
433
|
+
1. ✓ Commit directly to default branch
|
|
434
|
+
2. ✓ Keep commits clean and atomic
|
|
435
|
+
3. ✓ `git pull origin <default>` before push
|
|
436
|
+
4. ✗ Don't create feature branches
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
## Development Workflow (Detailed)
|
|
441
|
+
|
|
261
442
|
```
|
|
262
|
-
1. Read .abapGitAgent → get folder value
|
|
443
|
+
1. Read .abapGitAgent → get folder value AND workflow.mode
|
|
263
444
|
│
|
|
264
445
|
▼
|
|
265
446
|
2. Research → use ref command for unfamiliar topics
|
|
@@ -11,12 +11,13 @@ grand_parent: ABAP Development
|
|
|
11
11
|
**Searchable keywords**: unit test, AUnit, test class, cl_abap_unit_assert, FOR TESTING, setup, teardown, RISK LEVEL, DURATION, CDS test double, CL_CDS_TEST_ENVIRONMENT
|
|
12
12
|
|
|
13
13
|
## TOPICS IN THIS FILE
|
|
14
|
-
1. Local Test Classes - line
|
|
15
|
-
2. File Structure - line
|
|
16
|
-
3. Required Elements - line
|
|
17
|
-
4. Naming Conventions - line
|
|
18
|
-
5.
|
|
19
|
-
6. CDS
|
|
14
|
+
1. Local Test Classes - line 22
|
|
15
|
+
2. File Structure - line 24
|
|
16
|
+
3. Required Elements - line 35
|
|
17
|
+
4. Naming Conventions - line 67
|
|
18
|
+
5. Common Mistake: DDLS Testing - line 133
|
|
19
|
+
6. CDS Test Doubles - line 163
|
|
20
|
+
7. CDS with Aggregations - line 247
|
|
20
21
|
|
|
21
22
|
## Unit Testing with Local Test Classes
|
|
22
23
|
|
|
@@ -122,6 +123,44 @@ cl_abap_unit_assert=>assert_true( act = lv_bool msg = 'Should be true' ).
|
|
|
122
123
|
- ❌ Don't reference `<TESTCLASS>` in XML - abapGit auto-detects `.testclasses.abap`
|
|
123
124
|
- ❌ Don't use nested local classes inside the main class definition
|
|
124
125
|
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
### ⚠️ Common Mistake: CDS Views Don't Have `.testclasses.abap` Files
|
|
129
|
+
|
|
130
|
+
**WRONG - Creating test file for DDLS**:
|
|
131
|
+
```
|
|
132
|
+
zc_my_view.ddls.asddls
|
|
133
|
+
zc_my_view.ddls.testclasses.abap ❌ This doesn't work!
|
|
134
|
+
zc_my_view.ddls.xml
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Error you'll see**:
|
|
138
|
+
```
|
|
139
|
+
The REPORT/PROGRAM statement is missing, or the program type is INCLUDE.
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**CORRECT - Test CDS views using separate CLAS test classes**:
|
|
143
|
+
```
|
|
144
|
+
zc_flight_revenue.ddls.asddls ← CDS view definition
|
|
145
|
+
zc_flight_revenue.ddls.xml ← CDS metadata
|
|
146
|
+
|
|
147
|
+
zcl_test_flight_revenue.clas.abap ← Test class definition
|
|
148
|
+
zcl_test_flight_revenue.clas.testclasses.abap ← Test implementation
|
|
149
|
+
zcl_test_flight_revenue.clas.xml ← Class metadata (WITH_UNIT_TESTS=X)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Why**: Each ABAP object type has its own testing pattern:
|
|
153
|
+
- **CLAS** (classes): Use `.clas.testclasses.abap` for the same class
|
|
154
|
+
- **DDLS** (CDS views): Use separate CLAS test class with CDS Test Double Framework
|
|
155
|
+
- **FUGR** (function groups): Use `.fugr.testclasses.abap`
|
|
156
|
+
- **PROG** (programs): Use `.prog.testclasses.abap`
|
|
157
|
+
|
|
158
|
+
**Don't assume patterns from one object type apply to another!**
|
|
159
|
+
|
|
160
|
+
See "Unit Testing CDS Views" section below for the correct CDS testing approach.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
125
164
|
### Running Tests
|
|
126
165
|
|
|
127
166
|
In ABAP: SE24 → Test → Execute Unit Tests
|
|
@@ -11,11 +11,15 @@ grand_parent: ABAP Development
|
|
|
11
11
|
**Searchable keywords**: CDS, DDL, DDLS, CDS view, @AbapCatalog, @AccessControl, association, projection, consumption
|
|
12
12
|
|
|
13
13
|
## TOPICS IN THIS FILE
|
|
14
|
-
1. File Naming - line
|
|
15
|
-
2. DDL Source (.ddls.asddls) - line
|
|
16
|
-
3. Annotations - line
|
|
17
|
-
4. Associations - line
|
|
18
|
-
5. CDS
|
|
14
|
+
1. File Naming - line 96
|
|
15
|
+
2. DDL Source (.ddls.asddls) - line 107
|
|
16
|
+
3. Annotations - line 141
|
|
17
|
+
4. Associations - line 164
|
|
18
|
+
5. CDS Best Practices - line 194
|
|
19
|
+
- Key Field Ordering (STRICT RULE) - line 198
|
|
20
|
+
- Currency/Amount Field Aggregation - line 230
|
|
21
|
+
- Choosing Currency Fields for Aggregation - line 255
|
|
22
|
+
6. CDS Test Doubles - see 03_testing.md
|
|
19
23
|
|
|
20
24
|
## Creating CDS Views (DDLS)
|
|
21
25
|
|
|
@@ -188,6 +192,110 @@ where devclass not like '$%'
|
|
|
188
192
|
3. **Expose associations**: Add the association name at the end of the SELECT to expose it for OData navigation
|
|
189
193
|
4. **Activation warnings**: Search help warnings are informational and don't block activation
|
|
190
194
|
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## CDS Best Practices and Common Patterns
|
|
198
|
+
|
|
199
|
+
### Key Field Ordering (STRICT RULE)
|
|
200
|
+
|
|
201
|
+
CDS views enforce strict key field ordering that differs from regular SQL:
|
|
202
|
+
|
|
203
|
+
❌ **WRONG - Non-key field between keys**:
|
|
204
|
+
```abap
|
|
205
|
+
{
|
|
206
|
+
key Flight.carrid as Carrid,
|
|
207
|
+
Airline.carrname as Carrname, // ← breaks contiguity!
|
|
208
|
+
key Flight.connid as Connid,
|
|
209
|
+
key Flight.fldate as Fldate,
|
|
210
|
+
...
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
✅ **CORRECT - All keys first, then non-keys**:
|
|
215
|
+
```abap
|
|
216
|
+
{
|
|
217
|
+
key Flight.carrid as Carrid,
|
|
218
|
+
key Flight.connid as Connid,
|
|
219
|
+
key Flight.fldate as Fldate,
|
|
220
|
+
Airline.carrname as Carrname, // ← after all keys
|
|
221
|
+
...
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Rules:**
|
|
226
|
+
1. All key fields MUST be at the beginning of the field list
|
|
227
|
+
2. Key fields MUST be contiguous (no non-key fields in between)
|
|
228
|
+
3. Key fields must be declared before any non-key fields
|
|
229
|
+
|
|
230
|
+
**Error message**: "Key must be contiguous and start at the first position"
|
|
231
|
+
|
|
232
|
+
**Why this rule exists**: CDS views are not just SQL - they represent data models with strict structural requirements for consistency across the system.
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
### Currency/Amount Field Aggregation
|
|
237
|
+
|
|
238
|
+
When aggregating currency or amount fields in CDS views, use semantic annotations instead of complex casting:
|
|
239
|
+
|
|
240
|
+
❌ **WRONG - Complex casting (will fail)**:
|
|
241
|
+
```abap
|
|
242
|
+
cast(coalesce(sum(Booking.loccuram), 0) as abap.curr(15,2))
|
|
243
|
+
// Error: Data type CURR is not supported at this position
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
✅ **CORRECT - Semantic annotation + simple aggregation**:
|
|
247
|
+
```abap
|
|
248
|
+
@Semantics.amount.currencyCode: 'Currency'
|
|
249
|
+
sum(Booking.loccuram) as TotalRevenue,
|
|
250
|
+
currency as Currency
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Key points:**
|
|
254
|
+
- Use `@Semantics.amount.currencyCode` to link amount to currency field
|
|
255
|
+
- Let the framework handle data typing automatically
|
|
256
|
+
- Don't over-engineer with casts or type conversions
|
|
257
|
+
- Keep it simple: annotation + aggregation function
|
|
258
|
+
|
|
259
|
+
**Reference**:
|
|
260
|
+
```bash
|
|
261
|
+
abapgit-agent ref "CDS aggregation"
|
|
262
|
+
# Check: zdemo_abap_cds_ve_agg_exp.ddls.asddls for working examples
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
### Choosing Currency Fields for Aggregation
|
|
268
|
+
|
|
269
|
+
**Understand your data model before aggregating currency fields** - not all currency fields can be safely summed:
|
|
270
|
+
|
|
271
|
+
❌ **Dangerous - Foreign currency (different keys per row)**:
|
|
272
|
+
```abap
|
|
273
|
+
sum(Booking.forcuram) // FORCURAM has different FORCURKEY per booking!
|
|
274
|
+
// Problem: Can't safely sum USD + EUR + GBP without conversion
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
✅ **Safe - Local currency (shared key per group)**:
|
|
278
|
+
```abap
|
|
279
|
+
sum(Booking.loccuram) // LOCCURAM shares LOCCURKEY per airline
|
|
280
|
+
// Safe: All bookings for one airline use the same currency
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Data Model Example (SBOOK table)**:
|
|
284
|
+
```
|
|
285
|
+
FORCURAM + FORCURKEY = Payment currency (USD, EUR, GBP - different per booking)
|
|
286
|
+
LOCCURAM + LOCCURKEY = Airline currency (one currency per airline)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**Analysis Steps:**
|
|
290
|
+
1. Identify all currency fields in source tables
|
|
291
|
+
2. Check which currency code field each amount uses
|
|
292
|
+
3. Verify currency code is constant within your aggregation groups
|
|
293
|
+
4. Choose the field with consistent currency per group
|
|
294
|
+
|
|
295
|
+
**Rule**: Only aggregate amounts that share the same currency code within your grouping (GROUP BY).
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
191
299
|
## CDS Syntax Reference
|
|
192
300
|
|
|
193
301
|
When working with CDS view syntax (arithmetic, built-in functions, aggregations, etc.):
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "abapgit-agent",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.3",
|
|
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": [
|
|
@@ -34,6 +34,7 @@
|
|
|
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
36
|
"test:lifecycle": "node tests/run-all.js --lifecycle",
|
|
37
|
+
"test:pull": "node tests/run-all.js --pull",
|
|
37
38
|
"pull": "node bin/abapgit-agent",
|
|
38
39
|
"release": "node scripts/release.js",
|
|
39
40
|
"unrelease": "node scripts/unrelease.js"
|
package/src/commands/init.js
CHANGED
|
@@ -172,39 +172,100 @@ module.exports = {
|
|
|
172
172
|
}
|
|
173
173
|
console.log(`📌 Git remote: ${gitUrl}`);
|
|
174
174
|
|
|
175
|
-
// Check if .abapGitAgent already exists
|
|
175
|
+
// Check if .abapGitAgent already exists - merge if it does
|
|
176
176
|
const configPath = pathModule.join(process.cwd(), '.abapGitAgent');
|
|
177
|
+
let config = null;
|
|
178
|
+
let isUpdate = false;
|
|
179
|
+
|
|
177
180
|
if (fs.existsSync(configPath)) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
181
|
+
isUpdate = true;
|
|
182
|
+
try {
|
|
183
|
+
// Read existing configuration
|
|
184
|
+
const currentConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
185
|
+
|
|
186
|
+
console.log('📝 Updating existing .abapGitAgent configuration');
|
|
187
|
+
console.log('');
|
|
188
|
+
console.log('Current values:');
|
|
189
|
+
console.log(` Package: ${currentConfig.package || '(not set)'}`);
|
|
190
|
+
console.log(` Folder: ${currentConfig.folder || '(not set)'}`);
|
|
191
|
+
console.log(` Host: ${currentConfig.host || '(not set)'}`);
|
|
192
|
+
console.log('');
|
|
193
|
+
|
|
194
|
+
// Merge: keep existing values, update package and folder
|
|
195
|
+
config = currentConfig;
|
|
196
|
+
|
|
197
|
+
// Track what changed
|
|
198
|
+
const changes = [];
|
|
199
|
+
|
|
200
|
+
if (packageName && packageName !== currentConfig.package) {
|
|
201
|
+
const oldValue = currentConfig.package || '(not set)';
|
|
202
|
+
config.package = packageName;
|
|
203
|
+
changes.push(`package: ${oldValue} → ${packageName}`);
|
|
204
|
+
}
|
|
182
205
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
206
|
+
if (folder && folder !== currentConfig.folder) {
|
|
207
|
+
const oldValue = currentConfig.folder || '(not set)';
|
|
208
|
+
config.folder = folder;
|
|
209
|
+
changes.push(`folder: ${oldValue} → ${folder}`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (changes.length === 0) {
|
|
213
|
+
console.log('⚠️ No changes needed - package and folder are already set correctly');
|
|
214
|
+
console.log('');
|
|
215
|
+
console.log('To change other settings, edit .abapGitAgent manually');
|
|
216
|
+
// Don't exit - continue with other setup tasks (CLAUDE.md, guidelines, etc.)
|
|
217
|
+
} else {
|
|
218
|
+
console.log('Changes to be made:');
|
|
219
|
+
changes.forEach(change => console.log(` ${change}`));
|
|
220
|
+
console.log('');
|
|
221
|
+
console.log('✅ Keeping all other settings (host, credentials, workflow, etc.)');
|
|
222
|
+
console.log('');
|
|
223
|
+
}
|
|
224
|
+
} catch (error) {
|
|
225
|
+
console.error('Error: .abapGitAgent exists but could not read it:');
|
|
226
|
+
console.error(` ${error.message}`);
|
|
227
|
+
console.error('');
|
|
228
|
+
console.error('To fix:');
|
|
229
|
+
console.error(' 1. Check if .abapGitAgent contains valid JSON');
|
|
230
|
+
console.error(' 2. Or delete it: rm .abapGitAgent');
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
// Create new config from template
|
|
235
|
+
const samplePath = pathModule.join(__dirname, '..', '..', '.abapGitAgent.example');
|
|
236
|
+
if (!fs.existsSync(samplePath)) {
|
|
237
|
+
console.error('Error: .abapGitAgent.example not found.');
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
// Read sample and update with package/folder
|
|
243
|
+
const sampleContent = fs.readFileSync(samplePath, 'utf8');
|
|
244
|
+
config = JSON.parse(sampleContent);
|
|
245
|
+
config.package = packageName;
|
|
246
|
+
config.folder = folder;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error(`Error reading .abapGitAgent.example: ${error.message}`);
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
188
251
|
}
|
|
189
252
|
|
|
253
|
+
// Write the config (either new or updated)
|
|
190
254
|
try {
|
|
191
|
-
// Read sample and update with package/folder
|
|
192
|
-
const sampleContent = fs.readFileSync(samplePath, 'utf8');
|
|
193
|
-
const config = JSON.parse(sampleContent);
|
|
194
|
-
config.package = packageName;
|
|
195
|
-
config.folder = folder;
|
|
196
|
-
|
|
197
|
-
// Write updated config
|
|
198
255
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
199
|
-
|
|
256
|
+
if (isUpdate) {
|
|
257
|
+
console.log(`✅ Updated .abapGitAgent`);
|
|
258
|
+
} else {
|
|
259
|
+
console.log(`✅ Created .abapGitAgent`);
|
|
260
|
+
}
|
|
200
261
|
} catch (error) {
|
|
201
|
-
console.error(`Error
|
|
262
|
+
console.error(`Error writing .abapGitAgent: ${error.message}`);
|
|
202
263
|
process.exit(1);
|
|
203
264
|
}
|
|
204
265
|
|
|
205
266
|
// Update .gitignore
|
|
206
267
|
const gitignorePath = pathModule.join(process.cwd(), '.gitignore');
|
|
207
|
-
const ignoreEntries = ['.abapGitAgent'
|
|
268
|
+
const ignoreEntries = ['.abapGitAgent'];
|
|
208
269
|
let existingIgnore = '';
|
|
209
270
|
|
|
210
271
|
if (fs.existsSync(gitignorePath)) {
|
package/src/commands/ref.js
CHANGED
|
@@ -63,7 +63,7 @@ module.exports = {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// Pattern search (default)
|
|
66
|
-
const patternIndex = args.findIndex(
|
|
66
|
+
const patternIndex = args.findIndex(arg => !arg.startsWith('--'));
|
|
67
67
|
if (patternIndex === -1) {
|
|
68
68
|
console.error('Error: No pattern specified');
|
|
69
69
|
console.error('');
|
package/src/config.js
CHANGED
|
@@ -74,10 +74,25 @@ function isAbapIntegrationEnabled() {
|
|
|
74
74
|
return fs.existsSync(repoConfigPath);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Get workflow configuration
|
|
79
|
+
* @returns {Object} Workflow config with mode ('branch' or 'trunk') and optional defaultBranch
|
|
80
|
+
*/
|
|
81
|
+
function getWorkflowConfig() {
|
|
82
|
+
const cfg = loadConfig();
|
|
83
|
+
|
|
84
|
+
// Default to trunk mode if not specified (backward compatible)
|
|
85
|
+
return {
|
|
86
|
+
mode: cfg.workflow?.mode || 'trunk',
|
|
87
|
+
defaultBranch: cfg.workflow?.defaultBranch || null // null means auto-detect
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
77
91
|
module.exports = {
|
|
78
92
|
loadConfig,
|
|
79
93
|
getAbapConfig,
|
|
80
94
|
getAgentConfig,
|
|
81
95
|
getTransport,
|
|
82
|
-
isAbapIntegrationEnabled
|
|
96
|
+
isAbapIntegrationEnabled,
|
|
97
|
+
getWorkflowConfig
|
|
83
98
|
};
|
package/src/utils/git-utils.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
const pathModule = require('path');
|
|
5
5
|
const fs = require('fs');
|
|
6
|
+
const { execSync } = require('child_process');
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Get git remote URL from .git/config
|
|
@@ -51,8 +52,55 @@ function isGitRepo() {
|
|
|
51
52
|
return fs.existsSync(gitPath);
|
|
52
53
|
}
|
|
53
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Get the default/trunk branch name (main, master, develop, etc.)
|
|
57
|
+
* @param {string|null} configuredBranch - Branch name from config (overrides auto-detection)
|
|
58
|
+
* @returns {string} Default branch name
|
|
59
|
+
*/
|
|
60
|
+
function getDefaultBranch(configuredBranch = null) {
|
|
61
|
+
// If explicitly configured, use that
|
|
62
|
+
if (configuredBranch) {
|
|
63
|
+
return configuredBranch;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Method 1: Check remote HEAD (most accurate)
|
|
67
|
+
try {
|
|
68
|
+
const result = execSync('git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null', {
|
|
69
|
+
cwd: process.cwd(),
|
|
70
|
+
encoding: 'utf8'
|
|
71
|
+
}).trim();
|
|
72
|
+
|
|
73
|
+
// Result looks like: refs/remotes/origin/main
|
|
74
|
+
const match = result.match(/refs\/remotes\/origin\/(.+)/);
|
|
75
|
+
if (match && match[1]) {
|
|
76
|
+
return match[1];
|
|
77
|
+
}
|
|
78
|
+
} catch (e) {
|
|
79
|
+
// Ignore error, try next method
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Method 2: Check common branch names in remote
|
|
83
|
+
try {
|
|
84
|
+
const branches = execSync('git branch -r 2>/dev/null', {
|
|
85
|
+
cwd: process.cwd(),
|
|
86
|
+
encoding: 'utf8'
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Check in order of preference: main > master > develop
|
|
90
|
+
if (branches.includes('origin/main')) return 'main';
|
|
91
|
+
if (branches.includes('origin/master')) return 'master';
|
|
92
|
+
if (branches.includes('origin/develop')) return 'develop';
|
|
93
|
+
} catch (e) {
|
|
94
|
+
// Ignore error, use fallback
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Method 3: Fallback to 'main' (modern default)
|
|
98
|
+
return 'main';
|
|
99
|
+
}
|
|
100
|
+
|
|
54
101
|
module.exports = {
|
|
55
102
|
getRemoteUrl,
|
|
56
103
|
getBranch,
|
|
57
|
-
isGitRepo
|
|
104
|
+
isGitRepo,
|
|
105
|
+
getDefaultBranch
|
|
58
106
|
};
|