abapgit-agent 1.8.1 → 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 CHANGED
@@ -73,30 +73,38 @@ The folder is configured in `.abapGitAgent` (property: `folder`):
73
73
 
74
74
  **Each ABAP object requires an XML metadata file for abapGit to understand how to handle it.**
75
75
 
76
- | Object Type | ABAP File (if folder=/src/) | XML File |
77
- |-------------|------------------------------|----------|
78
- | Class | `src/zcl_*.clas.abap` | `src/zcl_*.clas.xml` |
79
- | Interface | `src/zif_*.intf.abap` | `src/zif_*.intf.xml` |
80
- | Program | `src/z*.prog.abap` | `src/z*.prog.xml` |
81
- | Table | `src/z*.tabl.abap` | `src/z*.tabl.xml` |
82
- | CDS View | `src/zc_*.ddls.asddls` | `src/zc_*.ddls.xml` |
76
+ | Object Type | ABAP File (if folder=/src/) | XML File | Details |
77
+ |-------------|------------------------------|----------|---------|
78
+ | Class | `src/zcl_*.clas.abap` | `src/zcl_*.clas.xml` | See `guidelines/08_abapgit.md` |
79
+ | Interface | `src/zif_*.intf.abap` | `src/zif_*.intf.xml` | See `guidelines/08_abapgit.md` |
80
+ | Program | `src/z*.prog.abap` | `src/z*.prog.xml` | See `guidelines/08_abapgit.md` |
81
+ | Table | `src/z*.tabl.abap` | `src/z*.tabl.xml` | See `guidelines/08_abapgit.md` |
82
+ | **CDS View Entity** | `src/zc_*.ddls.asddls` | `src/zc_*.ddls.xml` | **Use by default** - See `guidelines/04_cds.md` |
83
+ | CDS View (legacy) | `src/zc_*.ddls.asddls` | `src/zc_*.ddls.xml` | Only if explicitly requested - See `guidelines/04_cds.md` |
83
84
 
84
- **Use `ref --topic abapgit` for complete XML templates.**
85
+ **IMPORTANT: When user says "create CDS view", create CDS View Entity by default.**
86
+
87
+ **Why:** Modern S/4HANA standard, simpler (no SQL view), no namespace conflicts.
88
+
89
+ **For complete XML templates, DDL examples, and detailed comparison:**
90
+ - **CDS Views**: `guidelines/04_cds.md`
91
+ - **XML templates**: `guidelines/08_abapgit.md`
85
92
 
86
93
  ---
87
94
 
88
- ### 4. Use Syntax Command Before Commit (for CLAS, INTF, PROG)
95
+ ### 4. Use Syntax Command Before Commit (for CLAS, INTF, PROG, DDLS)
89
96
 
90
97
  ```
91
98
  ❌ WRONG: Make changes → Commit → Push → Pull → Find errors → Fix → Repeat
92
99
  ✅ CORRECT: Make changes → Run syntax → Fix locally → Commit → Push → Pull → Done
93
100
  ```
94
101
 
95
- **For CLAS, INTF, PROG files**: Run `syntax` command BEFORE commit to catch errors early.
102
+ **For CLAS, INTF, PROG, DDLS files**: Run `syntax` command BEFORE commit to catch errors early.
96
103
 
97
104
  ```bash
98
105
  # Check syntax of local code (no commit/push needed)
99
106
  abapgit-agent syntax --files src/zcl_my_class.clas.abap
107
+ abapgit-agent syntax --files src/zc_my_view.ddls.asddls
100
108
 
101
109
  # Check multiple INDEPENDENT files
102
110
  abapgit-agent syntax --files src/zcl_utils.clas.abap,src/zcl_logger.clas.abap
@@ -250,8 +258,189 @@ abapgit-agent unit --files src/zcl_test1.clas.testclasses.abap,src/zcl_test2.cla
250
258
 
251
259
  ## Development Workflow
252
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
253
305
  ```
254
- 1. Read .abapGitAgent → get folder value
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
+
442
+ ```
443
+ 1. Read .abapGitAgent → get folder value AND workflow.mode
255
444
 
256
445
 
257
446
  2. Research → use ref command for unfamiliar topics
@@ -260,15 +449,15 @@ abapgit-agent unit --files src/zcl_test1.clas.testclasses.abap,src/zcl_test2.cla
260
449
  3. Write code → place in correct folder (e.g., src/zcl_*.clas.abap)
261
450
 
262
451
 
263
- 4. Syntax check (for CLAS, INTF, PROG only)
452
+ 4. Syntax check (for CLAS, INTF, PROG, DDLS only)
264
453
 
265
- ├─► CLAS/INTF/PROG → abapgit-agent syntax --files <file>
454
+ ├─► CLAS/INTF/PROG/DDLS → abapgit-agent syntax --files <file>
266
455
  │ │
267
456
  │ ├─► Errors? → Fix locally (no commit needed), re-run syntax
268
457
  │ │
269
458
  │ └─► Clean ✅ → Proceed to commit
270
459
 
271
- └─► Other types (DDLS, FUGR, TABL, etc.) → Skip syntax, go to commit
460
+ └─► Other types (FUGR, TABL, etc.) → Skip syntax, go to commit
272
461
 
273
462
 
274
463
  5. Commit and push → git add . && git commit && git push
@@ -301,34 +490,88 @@ abapgit-agent unit --files src/zcl_test1.clas.testclasses.abap,src/zcl_test2.cla
301
490
 
302
491
  **IMPORTANT**:
303
492
  - **Use `syntax` BEFORE commit** for CLAS/INTF/PROG/DDLS - catches errors early, no git pollution
304
- - **Syntax checks files INDEPENDENTLY** - no cross-file dependency support (e.g., interface definition not available when checking implementing class)
305
- - **For dependent files** (interface + class, class + using class): Skip `syntax`, use `pull` directly
493
+ - **Syntax checks files INDEPENDENTLY** - syntax checker doesn't have access to uncommitted files
494
+ - **For dependent files** (interface + class): Create/activate underlying object FIRST, then dependent object (see workflow below)
306
495
  - **DDLS requires proper annotations** - CDS views need `@AbapCatalog.sqlViewName`, view entities don't
307
496
  - **ALWAYS push to git BEFORE running pull** - abapGit reads from git
308
497
  - **Use `inspect` AFTER pull** for unsupported types or if pull fails
309
498
 
499
+ **Working with dependent objects (RECOMMENDED APPROACH):**
500
+
501
+ When creating objects with dependencies (e.g., interface → class), create and activate the underlying object FIRST:
502
+
503
+ ```bash
504
+ # Step 1: Create interface, syntax check, commit, activate
505
+ vim src/zif_my_interface.intf.abap
506
+ abapgit-agent syntax --files src/zif_my_interface.intf.abap # ✅ Works (no dependencies)
507
+ git add src/zif_my_interface.intf.abap src/zif_my_interface.intf.xml
508
+ git commit -m "feat: add interface"
509
+ git push
510
+ abapgit-agent pull --files src/zif_my_interface.intf.abap # Interface now activated
511
+
512
+ # Step 2: Create class, syntax check, commit, activate
513
+ vim src/zcl_my_class.clas.abap
514
+ abapgit-agent syntax --files src/zcl_my_class.clas.abap # ✅ Works (interface already activated)
515
+ git add src/zcl_my_class.clas.abap src/zcl_my_class.clas.xml
516
+ git commit -m "feat: add class implementing interface"
517
+ git push
518
+ abapgit-agent pull --files src/zcl_my_class.clas.abap
519
+ ```
520
+
521
+ **Benefits:**
522
+ - ✅ Syntax checking works for both objects
523
+ - ✅ Each step is validated independently
524
+ - ✅ Easier to debug if something fails
525
+ - ✅ Cleaner workflow
526
+
527
+ **Alternative approach (when interface design is uncertain):**
528
+
529
+ If the interface might need changes while implementing the class, commit both together:
530
+
531
+ ```bash
532
+ # Create both files
533
+ vim src/zif_my_interface.intf.abap
534
+ vim src/zcl_my_class.clas.abap
535
+
536
+ # Skip syntax (files depend on each other), commit together
537
+ git add src/zif_my_interface.intf.abap src/zif_my_interface.intf.xml
538
+ git add src/zcl_my_class.clas.abap src/zcl_my_class.clas.xml
539
+ git commit -m "feat: add interface and implementing class"
540
+ git push
541
+
542
+ # Pull both together
543
+ abapgit-agent pull --files src/zif_my_interface.intf.abap,src/zcl_my_class.clas.abap
544
+
545
+ # Use inspect if errors occur
546
+ abapgit-agent inspect --files src/zcl_my_class.clas.abap
547
+ ```
548
+
549
+ **Use this approach when:**
550
+ - ❌ Interface design is still evolving
551
+ - ❌ Multiple iterations expected
552
+
310
553
  **Working with mixed file types:**
311
554
  When modifying multiple files of different types (e.g., 1 class + 1 CDS view):
312
- 1. Run `syntax` on supported files only (CLAS, INTF, PROG) - **only if they're independent**
555
+ 1. Run `syntax` on independent supported files (CLAS, INTF, PROG, DDLS)
313
556
  2. Commit ALL files together (including unsupported types)
314
557
  3. Push and pull ALL files together
315
558
 
316
559
  Example:
317
560
  ```bash
318
- # Check syntax on independent class and interface only (skip CDS, skip if dependent)
319
- abapgit-agent syntax --files src/zcl_my_class.clas.abap,src/zif_my_intf.intf.abap
561
+ # Check syntax on independent files only
562
+ abapgit-agent syntax --files src/zcl_my_class.clas.abap,src/zc_my_view.ddls.asddls
320
563
 
321
- # Commit and push all files including CDS
322
- git add src/zcl_my_class.clas.abap src/zif_my_intf.intf.abap src/zc_my_view.ddls.asddls
323
- git commit -m "feat: add class, interface, and CDS view"
564
+ # Commit and push all files
565
+ git add src/zcl_my_class.clas.abap src/zc_my_view.ddls.asddls
566
+ git commit -m "feat: add class and CDS view"
324
567
  git push
325
568
 
326
569
  # Pull all files together
327
- abapgit-agent pull --files src/zcl_my_class.clas.abap,src/zif_my_intf.intf.abap,src/zc_my_view.ddls.asddls
570
+ abapgit-agent pull --files src/zcl_my_class.clas.abap,src/zc_my_view.ddls.asddls
328
571
  ```
329
572
 
330
573
  **When to use syntax vs inspect vs view**:
331
- - **syntax**: Check LOCAL code BEFORE commit (CLAS, INTF, PROG only)
574
+ - **syntax**: Check LOCAL code BEFORE commit (CLAS, INTF, PROG, DDLS)
332
575
  - **inspect**: Check ACTIVATED code AFTER pull (all types, runs Code Inspector)
333
576
  - **view**: Understand object STRUCTURE (not for debugging errors)
334
577
 
@@ -338,21 +581,29 @@ abapgit-agent pull --files src/zcl_my_class.clas.abap,src/zif_my_intf.intf.abap,
338
581
 
339
582
  ```
340
583
  1. Identify file extension(s) AND dependencies
341
- ├─ .clas.abap or .clas.testclasses.abap → CLAS ✅ syntax supported (if independent)
342
- ├─ .intf.abap → INTF ✅ syntax supported (if independent)
584
+ ├─ .clas.abap or .clas.testclasses.abap → CLAS ✅ syntax supported
585
+ ├─ .intf.abap → INTF ✅ syntax supported
343
586
  ├─ .prog.abap → PROG ✅ syntax supported
344
587
  ├─ .ddls.asddls → DDLS ✅ syntax supported (requires proper annotations)
345
588
  └─ All other extensions → ❌ syntax not supported
346
589
 
347
590
  2. Check for dependencies:
348
- ├─ Interface + implementing class? → Dependencies exist
349
- ├─ Class A uses class B? → Dependencies exist
350
- ├─ New objects that don't exist in ABAP system? → Check if they depend on each other
351
- └─ Unrelated bug fixes across files? → No dependencies
591
+ ├─ Interface + implementing class? → DEPENDENT (interface is underlying)
592
+ ├─ Class A uses class B? → DEPENDENT (class B is underlying)
593
+ ├─ CDS view uses table? → INDEPENDENT (table already exists)
594
+ └─ Unrelated bug fixes across files? → INDEPENDENT
352
595
 
353
596
  3. For SUPPORTED types (CLAS/INTF/PROG/DDLS):
354
- ├─ Independent files → Run syntax → Fix errors → Commit → Push → Pull
355
- └─ Dependent files → Skip syntax → Commit → Push → Pull
597
+ ├─ INDEPENDENT files → Run syntax → Fix errors → Commit → Push → Pull
598
+
599
+ └─ DEPENDENT files (NEW objects):
600
+ ├─ RECOMMENDED: Create underlying object first (interface, base class, etc.)
601
+ │ 1. Create underlying object → Syntax → Commit → Push → Pull
602
+ │ 2. Create dependent object → Syntax (works!) → Commit → Push → Pull
603
+ │ ✅ Benefits: Both syntax checks work, cleaner workflow
604
+
605
+ └─ ALTERNATIVE: If interface design uncertain, commit both together
606
+ → Skip syntax → Commit both → Push → Pull → (if errors: inspect)
356
607
 
357
608
  4. For UNSUPPORTED types (FUGR, TABL, etc.):
358
609
  Write code → Skip syntax → Commit → Push → Pull → (if errors: inspect)
@@ -361,6 +612,32 @@ abapgit-agent pull --files src/zcl_my_class.clas.abap,src/zif_my_intf.intf.abap,
361
612
  Write all code → Run syntax on independent supported files ONLY → Commit ALL → Push → Pull ALL
362
613
  ```
363
614
 
615
+ **Example workflows:**
616
+
617
+ **Scenario 1: Interface + Class (RECOMMENDED)**
618
+ ```bash
619
+ # Step 1: Interface first
620
+ vim src/zif_calculator.intf.abap
621
+ abapgit-agent syntax --files src/zif_calculator.intf.abap # ✅ Works
622
+ git commit -am "feat: add calculator interface" && git push
623
+ abapgit-agent pull --files src/zif_calculator.intf.abap # Interface activated
624
+
625
+ # Step 2: Class next
626
+ vim src/zcl_calculator.clas.abap
627
+ abapgit-agent syntax --files src/zcl_calculator.clas.abap # ✅ Works (interface exists!)
628
+ git commit -am "feat: implement calculator" && git push
629
+ abapgit-agent pull --files src/zcl_calculator.clas.abap
630
+ ```
631
+
632
+ **Scenario 2: Multiple independent classes**
633
+ ```bash
634
+ # All syntax checks work (no dependencies)
635
+ vim src/zcl_class1.clas.abap src/zcl_class2.clas.abap
636
+ abapgit-agent syntax --files src/zcl_class1.clas.abap,src/zcl_class2.clas.abap
637
+ git commit -am "feat: add utility classes" && git push
638
+ abapgit-agent pull --files src/zcl_class1.clas.abap,src/zcl_class2.clas.abap
639
+ ```
640
+
364
641
  **Error indicators after pull:**
365
642
  - ❌ **"Error updating where-used list"** → SYNTAX ERROR - run `inspect` for details
366
643
  - ❌ **Objects in "Failed Objects Log"** → SYNTAX ERROR - run `inspect`
@@ -370,9 +647,10 @@ abapgit-agent pull --files src/zcl_my_class.clas.abap,src/zif_my_intf.intf.abap,
370
647
  ### Commands
371
648
 
372
649
  ```bash
373
- # 1. Syntax check LOCAL code BEFORE commit (CLAS, INTF, PROG only)
650
+ # 1. Syntax check LOCAL code BEFORE commit (CLAS, INTF, PROG, DDLS)
374
651
  abapgit-agent syntax --files src/zcl_my_class.clas.abap
375
- abapgit-agent syntax --files src/zcl_class1.clas.abap,src/zif_intf1.intf.abap
652
+ abapgit-agent syntax --files src/zc_my_view.ddls.asddls
653
+ abapgit-agent syntax --files src/zcl_class1.clas.abap,src/zif_intf1.intf.abap,src/zc_view.ddls.asddls
376
654
 
377
655
  # 2. Pull/activate AFTER pushing to git
378
656
  abapgit-agent pull --files src/zcl_class1.clas.abap,src/zcl_class2.clas.abap
@@ -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 3
15
- 2. File Structure - line 5
16
- 3. Required Elements - line 16
17
- 4. Naming Conventions - line 48
18
- 5. CDS Test Doubles - line 94
19
- 6. CDS with Aggregations - line 178
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 7
15
- 2. DDL Source (.ddls.asddls) - line 18
16
- 3. Annotations - line 50
17
- 4. Associations - line 75
18
- 5. CDS Test Doubles - see 03_testing.md
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.1",
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"
@@ -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
- console.error('Error: .abapGitAgent already exists.');
179
- console.error('To reinitialize, delete the existing file first.');
180
- process.exit(1);
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
- // Copy .abapGitAgent.example to .abapGitAgent
184
- const samplePath = pathModule.join(__dirname, '..', '..', '.abapGitAgent.example');
185
- if (!fs.existsSync(samplePath)) {
186
- console.error('Error: .abapGitAgent.example not found.');
187
- process.exit(1);
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
- console.log(`✅ Created .abapGitAgent`);
256
+ if (isUpdate) {
257
+ console.log(`✅ Updated .abapGitAgent`);
258
+ } else {
259
+ console.log(`✅ Created .abapGitAgent`);
260
+ }
200
261
  } catch (error) {
201
- console.error(`Error creating .abapGitAgent: ${error.message}`);
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', '.abapgit_agent_cookies.txt'];
268
+ const ignoreEntries = ['.abapGitAgent'];
208
269
  let existingIgnore = '';
209
270
 
210
271
  if (fs.existsSync(gitignorePath)) {
@@ -63,7 +63,7 @@ module.exports = {
63
63
  }
64
64
 
65
65
  // Pattern search (default)
66
- const patternIndex = args.findIndex((arg, idx) => idx > 0 && !arg.startsWith('--'));
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
  };
@@ -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
  };