Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
31 changes: 31 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: test

# Runs the app build + the CAP app's jest suite on every pull request (and
# on pushes to main): assemble the app from src/ + the committed core mirror
# (run/input/core), install and run its tests. Make this check "Required" in
# the branch protection rules of `main` so a PR can only be merged once the
# tests pass.

on:
pull_request:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 22
- run: npm run assemble
# one install covers the app AND the vendored core (its deps are part
# of the app lock, placed under core/node_modules/)
- run: npm ci
working-directory: run/output/cap2UI5
- run: npm test
98 changes: 98 additions & 0 deletions .github/workflows/update_cap.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
name: update cap

# The one pipeline of this repo: mirror the published core package from
# builder-abap2UI5-js, assemble the full CAP app (src/ + vendored core +
# webapp overlay), gate it with the app's jest suite, commit the refreshed
# mirror here and publish the built app 1:1 to the deployable repo
# cap2UI5/cap2UI5.
#
# Runs event-driven on every push to main. That includes the trigger
# commits builder-abap2UI5-js pushes here via deploy key (its trigger_cap
# workflow updates UPSTREAM_HEAD after a core rebuild), so the app follows
# every core update — plus changes to the build tooling itself. The nightly
# cron remains as a safety net.
#
# Cross-repo push uses an SSH deploy key: the public half is registered on
# cap2UI5/cap2UI5 with write access, the private half is the secret
# ACTION_KEY_APP here. Without the secret the workflow still builds and
# tests, it just skips the publish.

on:
push:
branches: [main]
schedule:
- cron: "0 6 * * *" # 06:00 UTC nightly — after the builder-abap2UI5-js pipelines (03:00–05:00)
workflow_dispatch:

permissions:
contents: write

concurrency:
group: update-cap
cancel-in-progress: false

jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
ref: main
- uses: actions/setup-node@v5
with:
node-version: 22
- run: npm run mirror_core
- name: commit refreshed mirror
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add run/input
if git diff --cached --quiet; then
echo "mirror unchanged"
else
git commit -m "mirror: update run/input/core ($(cut -c1-12 run/input/UPSTREAM_COMMIT))"
git pull --rebase origin main
git push origin HEAD:main
fi
- run: npm run assemble
# one install covers the app AND the vendored core (its deps are part
# of the app lock, placed under core/node_modules/)
- run: npm ci
working-directory: run/output/cap2UI5
- run: npm test
- name: publish built app to cap2UI5/cap2UI5
env:
ACTION_KEY_APP: ${{ secrets.ACTION_KEY_APP }}
run: |
set -euo pipefail
if [ -z "$ACTION_KEY_APP" ]; then
echo "::warning::secret ACTION_KEY_APP not set — skipping publish to cap2UI5/cap2UI5"
exit 0
fi
UPSTREAM=$(cat run/input/UPSTREAM_COMMIT)
APP="$RUNNER_TEMP/cap2UI5"

mkdir -p ~/.ssh
echo "$ACTION_KEY_APP" > ~/.ssh/app_key
chmod 600 ~/.ssh/app_key
ssh-keyscan github.com >> ~/.ssh/known_hosts 2>/dev/null
export GIT_SSH_COMMAND="ssh -i ~/.ssh/app_key -o IdentitiesOnly=yes"

git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"

git clone --depth 1 --branch main git@github.com:cap2UI5/cap2UI5.git "$APP"

PUBLISH_TARGET="$APP" npm run publish

cd "$APP"
git add -A
if git diff --cached --quiet; then
echo "app unchanged — no publish commit" >> "$GITHUB_STEP_SUMMARY"
else
git commit -m "build: publish app (core @ ${UPSTREAM:0:12})" \
-m "builder: ${GITHUB_SHA}" \
-m "run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
git push origin HEAD:main
echo "published to cap2UI5/cap2UI5@main: $(git rev-parse --short HEAD)" >> "$GITHUB_STEP_SUMMARY"
fi
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
.mirror_tmp/
.DS_Store
run/output/
83 changes: 83 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# AGENTS.md — builder-cap2UI5

Guidance for AI agents and contributors. Single source of truth for this
repository's structure and rules. Read before making any change.

## The cap2UI5 ecosystem (where this repo fits)

cap2UI5 brings the [abap2UI5](https://github.com/abap2UI5/abap2UI5) concept
to CAP/Node.js, organized as six repos under the `cap2UI5` org (since the
monorepo split, 2026-07):

| Repo | Role |
|---|---|
| [builder-abap2UI5-js](https://github.com/cap2UI5/builder-abap2UI5-js) | generates the platform-neutral core package `abap2UI5` (engine, transpiled classes, webapp, samples) into its `core/` |
| **builder-cap2UI5** (this repo) | generates the full CAP app from `src/` + the mirrored core and publishes it 1:1 into the app repo |
| [cap2UI5](https://github.com/cap2UI5/cap2UI5) | the finished, deployable CAP app — fully generated by this repo (except `.github/`) |
| [builder-cap2UI5-web](https://github.com/cap2UI5/builder-cap2UI5-web) | browser build tooling → [web-cap2UI5-build](https://github.com/cap2UI5/web-cap2UI5-build) → GitHub Pages |
| [docs](https://github.com/cap2UI5/docs) | VitePress documentation |

Upstream trigger: builder-abap2UI5-js:`trigger_cap` pushes a new
`UPSTREAM_HEAD` here (deploy key `ACTION_KEY_CAP`) after a core rebuild;
that push starts `update_cap`.

## Layout

| Path | What it is | Hand-edit? |
|---|---|---|
| `src/` | hand-written SOURCE of the CAP app: skeleton (`server.js`, `z2ui5-service.*`, `db/`, `mta.yaml`, `xs-security.json`), platform wiring (draft store → CDS entity `cap2ui5.z2ui5_t_01`, app discovery → `srv/app/`), the custom app `srv/app/z2ui5_cl_app_read_odata.js`, app `README.md`, `.devcontainer/`, `AGENTS.md` (published into the app repo) | **yes — app changes go here** |
| `run/input/core/` | mirror of builder-abap2UI5-js:`core/` (committed; upstream sha in `run/input/UPSTREAM_COMMIT`) | never — rewritten by `npm run mirror_core` |
| `run/output/cap2UI5/` | the assembled app (gitignored staging; `node_modules` preserved across builds) | never |
| `scripts/` | `mirror-core.js`, `assemble-cap.js`, `publish-cap.js` | yes |
| `UPSTREAM_HEAD` | trigger slot written by builder-abap2UI5-js | never by hand |

## Build & test

```bash
npm run mirror_core # snapshot builder-abap2UI5-js:core/ → run/input/core
# (MIRROR_SOURCE=/path/to/builder-abap2UI5-js for a local checkout)
npm run assemble # src/ + vendored core + webapp overlay → run/output/cap2UI5
cd run/output/cap2UI5 && npm ci && cd ../../.. # ONE install covers app + core
npm test # the app's jest suite, run inside run/output/cap2UI5
npm run publish # 1:1 copy → ../cap2UI5 (sibling checkout) or PUBLISH_TARGET=…
npm run build_cap # assemble + publish
```

## The dependency mechanics (important, easy to get wrong)

- In `src/`, the app links the framework as
`"abap2UI5": "file:../run/input/core"` (run `mirror_core` before working
in `src/` standalone).
- The assemble step **vendors** the core into the app (`core/`) and rewrites
the dependency to `file:./core` in `package.json`/`package-lock.json`
(ordered string rewrites in `scripts/assemble-cap.js`).
- Because the vendored core is INSIDE the app's package root, npm manages
its deps as part of the app tree: assemble merges the core's own lock
entries into the app lock under `core/node_modules/`. That keeps the
published lock deterministic and offline (no registry roundtrip) and one
`npm ci` at the app root installs everything.
- `publish-cap.js` wipes the target except `node_modules`, `.git` and
`.github` — the app repo's workflows (`test`, `trigger_web`) are owned by
the app repo and survive every publish; **everything else there is
regenerated**, including README, devcontainer and AGENTS.md (all shipped
from `src/`).

## Workflows (.github/workflows)

- `update_cap`: on push to main (incl. `UPSTREAM_HEAD` triggers), nightly
06:00 UTC (after the builder-abap2UI5-js pipelines at 03:00–05:00) and
manual. Mirrors the core, commits the refreshed mirror, assembles, runs
the app's jest suite, then publishes to cap2UI5/cap2UI5 via deploy key
(secret `ACTION_KEY_APP`; skipped with a warning when unset).
- `test`: PR gate — assemble from the committed mirror + `npm ci` + jest.

## Rules

- Never edit the cap2UI5 app repo directly — change `src/` here and
republish. Your own apps go in `src/srv/app/` or an external
`Z2UI5_APP_DIRS` folder.
- Never edit `run/input/core` — it mirrors builder-abap2UI5-js; framework
changes go into builder-abap2UI5-js:`src/`.
- Keep the rewrite pairs in `assemble-cap.js` in sync with the dependency
paths if you ever move `run/input/core` or the vendored `core/`.
- The app's jest suite gates every publish; keep it green.
7 changes: 7 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# CLAUDE.md

All project guidance lives in **[AGENTS.md](AGENTS.md)** — the single source
of truth for this repository (ecosystem overview, folder scheme, build and
publish flow, generated-vs-hand-written rules).

Read `AGENTS.md` before making any change.
101 changes: 100 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,100 @@
# builder-cap2UI5
# builder-cap2UI5 — the CAP app build project

[![test](https://github.com/cap2UI5/builder-cap2UI5/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/cap2UI5/builder-cap2UI5/actions/workflows/test.yml)
[![update cap](https://github.com/cap2UI5/builder-cap2UI5/actions/workflows/update_cap.yml/badge.svg?branch=main)](https://github.com/cap2UI5/builder-cap2UI5/actions/workflows/update_cap.yml)

This project **generates the deployable CAP app**
[cap2UI5](https://github.com/cap2UI5/cap2UI5) from the hand-maintained source
in [`src/`](src/) plus the published core package (the npm package
`abap2UI5`), mirrored from
[builder-abap2UI5-js](https://github.com/cap2UI5/builder-abap2UI5-js).

> [!IMPORTANT]
> Everything in this project is generated automatically — by AI (Claude) and
> by a sync pipeline that consumes the upstream
> [abap2UI5](https://github.com/abap2UI5/abap2UI5) sources. Review and test
> before relying on it.

> [!IMPORTANT]
> **The cap2UI5 app repo is a build artifact — do not hand-edit it.** Every
> publish wipes and rewrites it. The hand-written source lives in
> [`src/`](src/); edit there and re-run the build. (Your own apps go in
> `src/srv/app/` or an external `Z2UI5_APP_DIRS` folder — never in the app
> repo's `srv/app/`, which is regenerated.)

cap2UI5 is organized as three repositories, from framework to finished app:

| Repository | What it is |
|---|---|
| [`builder-abap2UI5-js`](https://github.com/cap2UI5/builder-abap2UI5-js) | abap2UI5 ported to JavaScript — generates the core package `abap2UI5` |
| [`builder-cap2UI5`](https://github.com/cap2UI5/builder-cap2UI5) (here) | generates the full CAP app from `src/` + the published core |
| [`cap2UI5`](https://github.com/cap2UI5/cap2UI5) | the finished, deployable CAP app (**generated**) |

## Build

```bash
npm run mirror_core # snapshot builder-abap2UI5-js:core/ → run/input/core
# (MIRROR_SOURCE=/path/to/builder-abap2UI5-js uses a local checkout)

npm run build_cap # assemble + publish → regenerate ../cap2UI5 (a sibling
# checkout of cap2UI5/cap2UI5; override with PUBLISH_TARGET=…)
```

No dependencies — the scripts are plain Node. What the build does:

| Step | What it does |
|---|---|
| `npm run mirror_core` | snapshot the published core package from builder-abap2UI5-js into `run/input/core` (the upstream commit is recorded in `run/input/UPSTREAM_COMMIT`) |
| `npm run assemble` | `src/` → `run/output/cap2UI5` (verbatim), vendor `run/input/core` → `core/`, then overlay the core's `app/z2ui5/webapp` → `app/z2ui5/webapp` (the copy served by CDS statics and zipped by the mta html5 module — taken from the published core, so the two cannot drift) |
| `npm run publish` | 1:1 copy `run/output/cap2UI5` → the app checkout (`../cap2UI5` or `PUBLISH_TARGET`) — the very last step |
| `npm run build_cap` | `assemble` then `publish` |
| `npm test` | runs the CAP app's own jest suite in `run/output/cap2UI5` (needs one `npm ci` there first — it covers the app **and** the vendored core) |

The framework is consumed as the npm dependency `abap2UI5`: `src/` links the
mirrored package (`file:../run/input/core`), the published app the **vendored
copy** (`file:./core`) — the app repo is fully self-contained. The build's
only transformations: that dependency-path rewrite in `package.json` /
`package-lock.json`, plus merging the core's own lock entries into the app
lock (under `core/node_modules/`), since the vendored core sits inside the
app's package root and npm manages its deps as part of the app tree.

## `src/` — the hand-written source

`src/` is the **source of truth** for the CAP app: the skeleton (`server.js`,
`z2ui5-service.*`, `db/`, `package.json`, `mta.yaml`, `srv/external/`), the
platform wiring (draft store → CDS entity `cap2ui5.z2ui5_t_01`, app discovery
→ `srv/app/`), the bundled custom app `srv/app/z2ui5_cl_app_read_odata.js`,
and the docs. Published as the app repo minus the vendored core and the
generated webapp overlay.

`src/` is itself a fully functional minimal CAP project — the starting point
of cap2UI5 with the same basic setup as abap2UI5: a mini frontend
(`app/index.html`), the http service (`POST /rest/root/z2ui5`) and the draft
persistence. Run and test it standalone (it links the core via
`file:../run/input/core`, so run `npm run mirror_core` first):

```bash
cd src
npm install
npx cds watch # → http://localhost:4004/index.html
npm test # jest: boots the server via cds.test(), asserts all three layers
```

See the [minimal base section](src/README.md#the-minimal-base) in the source
README for details.

## Pipeline

The **update cap** workflow (`.github/workflows/update_cap.yml`) runs on
every push to `main` — including the trigger commits builder-abap2UI5-js
pushes here via deploy key (its `trigger_cap` workflow updates
`UPSTREAM_HEAD` after a core rebuild) — plus a nightly cron as safety net.
It mirrors the core, assembles the app, gates it with the app's jest suite,
commits the refreshed `run/input/core` here and publishes the built app 1:1
to [cap2UI5](https://github.com/cap2UI5/cap2UI5) (deploy key, secret
`ACTION_KEY_APP`). The **test** workflow runs the same assemble + jest gate
on every pull request.

## License

MIT.
1 change: 1 addition & 0 deletions UPSTREAM_HEAD
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
8d084d2437340b9588b48ccf1f4c728cb65b6617
16 changes: 16 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "builder-cap2ui5",
"version": "1.0.0",
"private": true,
"description": "Builder project for the full CAP app \u2014 mirrors the published core package (abap2UI5) from builder-abap2UI5-js, assembles src/ + the vendored core + its webapp and publishes the result 1:1 into the cap2UI5 app repository",
"license": "MIT",
"engines": {
"node": ">=20"
},
"scripts": {
"mirror_core": "node scripts/mirror-core.js",
"assemble": "node scripts/assemble-cap.js",
"publish": "node scripts/publish-cap.js",
"build_cap": "node scripts/assemble-cap.js && node scripts/publish-cap.js",
"test": "npm --prefix run/output/cap2UI5 test"
},
"repository": "github:cap2UI5/builder-cap2UI5"
}
1 change: 1 addition & 0 deletions run/input/UPSTREAM_COMMIT
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
8d084d2437340b9588b48ccf1f4c728cb65b6617
Loading