Skip to content

feat: abapGit roundtrip - export, deploy, and structure support#89

Draft
ThePlenkov wants to merge 19 commits intomainfrom
feat/abapgit-roundtrip
Draft

feat: abapGit roundtrip - export, deploy, and structure support#89
ThePlenkov wants to merge 19 commits intomainfrom
feat/abapgit-roundtrip

Conversation

@ThePlenkov
Copy link
Member

@ThePlenkov ThePlenkov commented Mar 17, 2026

Summary

End-to-end abapGit roundtrip capability: export ABAP objects from SAP to abapGit-format XML/ABAP files, then deploy them back to the system. This PR adds new DDIC object handlers, structure (TABL/DS) support, and the roundtrip/activate CLI commands.

ADK (packages/adk)

  • Registry: resolveType() with full-type-first fallback — TABL/DS resolves to AdkStructure, CLAS/OC falls back to CLAS → AdkClass
  • Registry: getMainType() helper and getObjectUri() using resolveType()
  • Model: crudContract-based load()/save(), lock()/unlock(), activate() lifecycle
  • ObjectSet: deploy() with two-phase save+activate, BulkSaveResult tracking (success/failed/unchanged)
  • Save lifecycle: Pre-lock comparison phase — checkPendingSourcesUnchanged() skips lock+PUT if sources are identical on server
  • TABL: AdkTable (endpoint /ddic/tables/) and AdkStructure (endpoint /ddic/structures/) models with source support
  • DOMA/DTEL/TTYP: ADK models with CRUD contract endpoints
  • DEVC: Preserve full ADT type string (TABL/DS) instead of stripping subtypes — fixes structure import via wrong endpoint
  • CLAS/INTF/PROG/FUGR: savePendingSources() and checkPendingSourcesUnchanged() overrides for skip-unchanged support

abapGit Plugin (packages/adt-plugin-abapgit)

  • XSD schemas: Reorder DD02V/DD03P elements to match standard abapGit field ordering (builder uses XSD definition order)
  • XSD schemas: Add DD04V fields (DDLANGUAGE, DDTEXT, DOMNAME, etc.) for data elements
  • Codegen: Regenerate all schemas and TypeScript types from updated XSDs
  • DOMA handler: Domain value serialization with DD01V and DD07V_TAB (fixed values)
  • DTEL handler: Data element metadata serialization with DD04V
  • TABL handler: Table handler (TABL/DT) and structure handler (TABL/DS) with DD02V + DD03P_TABLE serialization
  • TTYP handler: Table type serialization with DD40V
  • CDS converter (cds-to-abapgit.ts): Transform CDS-sourced table metadata to classic abapGit DD02V/DD03P format
  • Base handler: Use getMainType() for file extension so TABL/DS → .tabl.xml (not .tabl/ds.xml)
  • Deserializer: Use payload.type when available for correct subtype resolution during deploy
  • Tests: DTEL handler unit tests and TABL handler roundtrip tests

ABAP CDS Parser (packages/acds) — NEW PACKAGE

  • New package: Chevrotain-based ABAP CDS DDL source parser
  • Tokenizer, parser, AST, and visitor pattern for CDS source types
  • Used by cds-to-abapgit.ts converter to parse CDS-sourced table definitions

MCP (packages/adt-mcp)

  • tsconfig.json: Add project references to adt-contracts and adt-client

CLI (packages/adt-cli)

  • Commands: Add check, unlock, import object commands
  • Import service: Single-object import by name (search → load → format)
  • Import service: Object type filter matches both full type (TABL/DS) and main type (TABL)
  • Plugin loader: Support format plugins with import/export capabilities

Export Plugin (packages/adt-export)

  • Roundtrip command: Export → deploy cycle with activation support
  • Activate command: Bulk activation of objects in a package
  • FileTree: Enhanced with directory walking and file operations for deploy

Verified Roundtrip Scenarios

Object Type Export Deploy Status
INTF (Interface) OK OK Fully working
CLAS (Class) OK OK Fully working
PROG (Program) OK OK Fully working
FUGR (Function Group) OK OK Fully working
DOMA (Domain) OK OK Fully working
DTEL (Data Element) OK OK Fully working
TABL/DT (Table) OK OK DD02V + DD03P roundtrip working
TABL/DS (Structure) OK OK Uses correct /ddic/structures/ endpoint
TTYP (Table Type) OK OK Fully working
DEVC (Package) OK N/A Export only (metadata)

Key Bug Fixes

  • Structure endpoint resolution: devc.model.ts was stripping subtypes (TABL/DS → TABL) causing structures to hit /ddic/tables/ instead of /ddic/structures/. Fixed by preserving full ADT type string.
  • DD02V/DD03P field ordering: XML builder uses XSD definition order, not JS property order. Reordered XSD elements to match standard abapGit output.
  • Skip-unchanged optimization: Source-based objects compare remote content before locking to avoid unnecessary lock/PUT cycles.

Remaining Work

  • DD09L (technical settings) serialization for tables
  • TABL_EXTRAS serialization for tables
  • MSAG (message class) support
  • ENHO (enhancement) support
  • Additional DDIC types (SHLP, TRAN, etc.)

Test plan

  • Build passes: bunx nx run-many -t build -p adk adt-plugin-abapgit adt-export adt-cli
  • DTEL handler unit tests pass
  • TABL handler roundtrip tests pass
  • Manual roundtrip verified on BTP system for all listed object types
  • Full bunx nx test suite (some pre-existing failures unrelated to this PR)

Submodules

  • git_modules/abapgit-examples: Updated to include DDIC example objects (ZAGE_STRUCTURE, ZAGE_TABL, etc.)

Generated with Devin

@coderabbitai
Copy link

coderabbitai bot commented Mar 17, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6498fac1-0e95-4c60-8605-7a3d388c3d82

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/abapgit-roundtrip
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Add end-to-end abapGit roundtrip capability: export ABAP objects from SAP
to abapGit-format files, then deploy them back. Includes new DDIC object
handlers (DOMA, DTEL, TABL, TTYP), structure (TABL/DS) support, and the
roundtrip/activate CLI commands.

Key changes:

ADK (adk):
- Registry: add resolveType() with full-type-first fallback for subtypes
- Registry: add getMainType() helper and getObjectUri() using resolveType
- Model: add crudContract-based load/save, lock/unlock, activate lifecycle
- ObjectSet: add deploy() with two-phase save+activate, BulkSaveResult
- TABL: add AdkTable and AdkStructure models with source support
- DOMA/DTEL/TTYP: add ADK models with CRUD contract endpoints
- DEVC: preserve full ADT type (TABL/DS) instead of stripping subtypes
- CLAS/INTF/PROG/FUGR: add save lifecycle (savePendingSources, checkUnchanged)
- Export getMainType, resolveType, getObjectUri from index

abapGit plugin (adt-plugin-abapgit):
- XSD: reorder DD02V/DD03P fields to match abapGit output ordering
- XSD: add DD04V fields (DDLANGUAGE, DDTEXT, DOMNAME, etc.)
- Codegen: regenerate all schemas and types from updated XSDs
- Handlers: add DOMA handler with domain-specific value serialization
- Handlers: add DTEL handler with data element metadata serialization
- Handlers: add TABL handler (tables) and structure handler (TABL/DS)
- Handlers: add TTYP handler for table types
- Handlers: add cds-to-abapgit.ts converter for CDS-sourced tables
- Base: use getMainType() for file extension (TABL/DS -> .tabl)
- Deserializer: use payload.type for correct subtype resolution
- Tests: add DTEL and TABL handler tests

CLI (adt-cli):
- Commands: add check, unlock, import object commands
- Import service: add single-object import by name
- Import service: preserve full ADT type in object type filter
- Plugin loader: support format plugins with import/export capabilities

Export plugin (adt-export):
- Commands: add roundtrip command (export -> deploy cycle)
- Commands: add activate command for bulk activation
- Export: enhanced FileTree with directory walking and file operations
- Package.json: add @abapify/adk dependency

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
@ThePlenkov ThePlenkov force-pushed the feat/abapgit-roundtrip branch from 0760591 to 0da189a Compare March 17, 2026 19:46
@nx-cloud
Copy link
Contributor

nx-cloud bot commented Mar 17, 2026

View your CI Pipeline Execution ↗ for commit d7578d3

Command Status Duration Result
nx affected -t lint test build e2e-ci --verbose... ❌ Failed 17s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-18 07:41:42 UTC

nx-cloud[bot]

This comment was marked as outdated.

nx-cloud[bot]

This comment was marked as outdated.

ThePlenkov and others added 2 commits March 18, 2026 00:06
New package @abapify/adt-diff with  command that
compares local abapGit XML files against remote SAP objects. Uses
projection-based XML normalization to eliminate false positives from
formatting and field ordering differences.

Key fixes in CDS-to-abapGit serializer:
- Fix REFFIELD: strip table prefix from annotation value
  (e.g. ZAGE_STRUCTURE.CURRENCY_CODE -> CURRENCY_CODE)
- Fix CUKY/UNIT default lengths (cuky=5, unit=3) so INTLEN and LENG
  are correctly emitted
- Fix include serialization: plain include -> .INCLUDE only,
  include with suffix -> .INCLU-<SUFFIX> only (was emitting both)
- Fix projectOnto array projection to use union of all element keys
  instead of just the first element's keys

CDS parser (acds) enhancements:
- Support 'include <name> with suffix <suffix>' syntax
- Add Suffix keyword token, AST node, and visitor support

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
Add runtime type resolution to the TABL/structure abapGit serializer.
Named type references (data elements, structures) are now resolved via
ADT REST endpoints to determine:

- COMPTYPE: 'E' (data element) vs 'S' (structure)
- SHLPORIGIN: 'D' when data element has a search help
- DATATYPE/MASK: 'STRU'/'STRUS' for structure references
- DDTEXT: description text for include entries

Implementation:
- Add fetchText() utility to AdkObject base class for generic ADT GET
- Add TypeResolver interface and wire it through buildDD03P (now async)
- Add createAdtTypeResolver() factory that probes dataelements then
  structures endpoints with caching
- Fix HTTP 406 by not sending Accept header (let server choose)

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
nx-cloud[bot]

This comment was marked as outdated.

- Add --format ddl option to adt diff command for DDL-level comparison
- Fix CUKY type to omit length (SAP convention: cuky has implicit length 5)
- Fix .INCLU-<SUFFIX> handling: emit 'include <name> with suffix <sfx>;'
- Add blank lines after opening brace and before closing brace
- Add trailing newline to generated DDL output

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
nx-cloud[bot]

This comment was marked as outdated.

ThePlenkov and others added 6 commits March 18, 2026 07:40
… source

These DD02V/DD03P fields are database values that abapGit reads from SAP.
They cannot be reliably determined from CDS DDL source alone:

- LANGDEP: language dependency flag (varies per table)
- CLIDEP: client dependency flag (varies per table)
- POSITION: field ordinal position (assigned by SAP runtime)

Removing these eliminates false diffs in roundtrip comparison,
where our serializer was emitting fields that the remote XML
may or may not contain depending on actual SAP metadata.

Generated with Devin

Co-Authored-By: Devin <noreply@cognition.ai>
SAP ABAP File Formats uses .acds for CDS source code files,
not .ddl. Updated the diff display label accordingly.

Generated with Devin

Co-Authored-By: Devin <noreply@cognition.ai>
LANGDEP=X is set by SAP DDIC when a structure/table contains a
language-typed field (data element SPRAS or builtin type abap.lang).

This is detectable from CDS source by checking if any member field
references spras (named type) or uses abap.lang (builtin type).
Fixes the roundtrip diff for zage_structure which has language : spras.

Generated with Devin

Co-Authored-By: Devin <noreply@cognition.ai>
Reorder field assignments in buildFieldDD03P to match the canonical
DD03P structure definition order used by SAP/abapGit:

  FIELDNAME, KEYFLAG, ROLLNAME, ADMINFIELD, INTTYPE, INTLEN, NOTNULL,
  DATATYPE, LENG, DECIMALS, SHLPORIGIN, MASK, COMPTYPE, REFTABLE, REFFIELD

Previously REFTABLE/REFFIELD were emitted before DATATYPE, and MASK
was emitted after REFTABLE/REFFIELD. This caused field ordering diffs
in roundtrip comparison even though the data was identical.

Generated with Devin

Co-Authored-By: Devin <noreply@cognition.ai>
Switch DD02V and DD03P XSD definitions from all to sequence so
field order is preserved for TABL serialization and roundtrip
compatibility with SAP/abapGit output.

Regenerate the derived schema and type files to match the updated
XSD definitions and ignore generated schema output in Prettier.
Copy link
Contributor

@nx-cloud nx-cloud bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nx Cloud is proposing a fix for your failed CI:

We address three ESLint errors introduced by this PR across check.ts, unlock.ts, and cds-to-abapgit.ts. Redundant : string type annotations on default-valued parameters are removed to satisfy @typescript-eslint/no-inferrable-types, and the re-thrown error in unlock.ts now passes { cause: unlockError } to satisfy the preserve-caught-error rule.

Tip

We verified this fix by re-running adt-plugin-abapgit:lint.

Suggested Fix changes
diff --git a/packages/adt-cli/src/lib/commands/check.ts b/packages/adt-cli/src/lib/commands/check.ts
index 0e2577e..4d954e9 100644
--- a/packages/adt-cli/src/lib/commands/check.ts
+++ b/packages/adt-cli/src/lib/commands/check.ts
@@ -157,7 +157,7 @@ async function resolvePackageObjects(
  */
 function buildCheckObjectListXml(
   objects: Array<{ uri: string }>,
-  version: string = 'active',
+  version = 'active',
 ): string {
   const checkObjects = objects
     .map(
diff --git a/packages/adt-cli/src/lib/commands/unlock.ts b/packages/adt-cli/src/lib/commands/unlock.ts
index 8dafcb3..777ca13 100644
--- a/packages/adt-cli/src/lib/commands/unlock.ts
+++ b/packages/adt-cli/src/lib/commands/unlock.ts
@@ -102,7 +102,9 @@ async function performUnlock(
     if (msg.includes('not locked') || msg.includes('not enqueued')) {
       console.log(`ℹ️  ${objectName} is not locked`);
     } else {
-      throw new Error(`Unlock failed for ${objectName}: ${msg}`);
+      throw new Error(`Unlock failed for ${objectName}: ${msg}`, {
+        cause: unlockError,
+      });
     }
   }
 }
diff --git a/packages/adt-plugin-abapgit/src/lib/handlers/cds-to-abapgit.ts b/packages/adt-plugin-abapgit/src/lib/handlers/cds-to-abapgit.ts
index 5de3338..cc18148 100644
--- a/packages/adt-plugin-abapgit/src/lib/handlers/cds-to-abapgit.ts
+++ b/packages/adt-plugin-abapgit/src/lib/handlers/cds-to-abapgit.ts
@@ -415,7 +415,7 @@ async function buildFieldDD03P(
  */
 export async function buildDD03P(
   members: TableMember[],
-  tableName: string = '',
+  tableName = '',
   resolver?: TypeResolver,
 ): Promise<DD03PData[]> {
   const entries: DD03PData[] = [];
diff --git a/packages/adt-plugin-abapgit/tests/handlers/dtel-e2e.test.ts b/packages/adt-plugin-abapgit/tests/handlers/dtel-e2e.test.ts
index 458d390..145455a 100644
--- a/packages/adt-plugin-abapgit/tests/handlers/dtel-e2e.test.ts
+++ b/packages/adt-plugin-abapgit/tests/handlers/dtel-e2e.test.ts
@@ -13,11 +13,16 @@ import assert from 'node:assert';
 
 import { parseXml, type Schema } from '@abapify/ts-xsd';
 
-// Import the raw schema literals (these are the generated schema JS objects)
+// Import typed schemas and extract the raw schema literals via .schema
 // The dataelementWrapper imports adtcore + dataelements schemas
-import dataelementWrapperSchema from '../../../adt-schemas/src/schemas/generated/schemas/custom/dataelementWrapper.ts';
-import adtcoreSchema from '../../../adt-schemas/src/schemas/generated/schemas/sap/adtcore.ts';
-import dataelementsSchema from '../../../adt-schemas/src/schemas/generated/schemas/sap/dataelements.ts';
+import {
+  dataelementWrapper,
+  adtcore,
+  dataelements,
+} from '@abapify/adt-schemas';
+const dataelementWrapperSchema = dataelementWrapper.schema;
+const adtcoreSchema = adtcore.schema;
+const dataelementsSchema = dataelements.schema;
 
 // Import handler to trigger registration
 import '../../src/lib/handlers/objects/dtel.ts';

Apply fix via Nx Cloud  Reject fix via Nx Cloud


Or Apply changes locally with:

npx nx-cloud apply-locally wGVs-khrH

Apply fix locally with your editor ↗   View interactive diff ↗



🎓 Learn more about Self-Healing CI on nx.dev

ThePlenkov and others added 8 commits March 18, 2026 09:32
Tables with a key field of type CLNT (abap.clnt) or data element MANDT
are client-dependent. Set CLIDEP=X in DD02V when detected.

Adds 4 tests for CLIDEP detection covering:
- key field with mandt data element
- key field with abap.clnt builtin
- structures (no key fields → no CLIDEP)
- non-key mandt field (not client-dependent)

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
Change diff argument from single required <file> to variadic [files...]
with glob expansion. Enables usage like:
  adt diff *.tabl.xml
  adt diff zage_tabl.tabl.xml zage_value_table.tabl.xml

Multi-file mode shows per-object headers and aggregate summary.
Single-file mode preserves the original detailed output.
Exit code 1 if any differences found.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
Fixture only has 1 key field (CLIENT), tests expected 2 key fields
and a 'key_field' that doesn't exist in the current fixture.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
Move 5 rules from .windsurf/rules/ into .agents/rules/ organized by
domain (development/, planning/, git/). Both .windsurf/rules/ and
.cognition/rules/ are now symlinks to .agents/rules/, keeping all
AI tools (Windsurf, Devin, Claude) in sync from a single source.

Added git/no-auto-commit.md: require explicit user approval before
any git commit or push.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
- bundler-imports: fix glob *.ts -> **/*.ts (recursive)
- package-versions: add trigger: model_decision
- nx-circular-dependencies: add trigger: model_decision
- nx-monorepo-setup: add trigger: model_decision
- project-planning-memory: add description to frontmatter

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
Delete 4 agent rule files (nx-circular-dependencies, nx-monorepo-setup,
project-planning-memory, spec-first-then-code) and merge their content
into AGENTS.md as a single source of truth.

AGENTS.md now covers:
- Nx monorepo workflow (build/test/lint commands)
- Package layout and dependency graph
- Type flow architecture (XSD → schemas → contracts → client)
- Plugin system and ADK object handlers
- Save lifecycle and source comparison
Add cross-references between related rules (coding-conventions ↔
bundler-imports, project-planning-memory ↔ spec-first-then-code,
no-auto-commit ↔ monitor-ci exception). Update skills to reference
verification/after-changes checklist and relevant rules (xsd-best-practices,
file-lifecycle, adt-ddic-mapping, adk-save-logic, tmp-folder-testing).

Replace npx/pnpm with bunx in skills (add-endpoint, add-object-type,
nx-generate
… for CDS comparison

Rename `--format ddl` to `--source` flag (boolean) for clarity. When comparing CDS source, filter remote annotations to match local's annotation set — prevents spurious diffs from fields present in remote XML but absent in local (e.g. MATEFLAG).

Also add MATEFLAG support in cds-to-abapgit (maps AbapCatalog.dataMaintenance annotation) and strengthen after-changes rule to require agents verify builds/tests
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
8 Security Hotspots
9.6% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant