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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/check-catalogue.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: check-catalogue

# catalogue.json is the catalogue as data - the same facts SAMPLES.md carries
# as a page, committed as one JSON file so a program (an agent, an editor, a
# tool asking "which sample shows X with RAP") can answer from a single fetch
# of `main` without running anything. It is generated from the classes and the
# package index, and a committed generated file is a file that goes stale the
# first time somebody adds a sample and does not rerun the generator. This is
# what notices.

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

concurrency:
group: check-catalogue-${{ github.ref }}
cancel-in-progress: true

jobs:
check-catalogue:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '22'
- run: node scripts/generate-catalogue.mjs --check
45 changes: 35 additions & 10 deletions .github/workflows/deploy-web.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ name: deploy-web
# https://abap2ui5.github.io/samples-stack/ — every sample of this repository,
# searchable by the technology it plays with and by the release your system
# runs, with what it needs from that system on every card. It is static: three
# hand-written files plus web/apps.json, which this workflow generates from the
# tree on every deploy, so the page is never staler than the samples it
# describes and a sample pull request carries no diff of derived data.
# hand-written files plus two generated-at-deploy pieces, web/apps.json and
# the web/thumbs/ thumbnails, which this workflow writes from the tree on
# every deploy, so the page is never staler than the samples it describes and
# a sample pull request carries no diff of derived data.
#
# No dependencies are installed — the generator is plain node over the same
# scan SAMPLES.md is built from.
# The catalogue needs no dependencies — the generator is plain node over the
# same scan SAMPLES.md is built from. The thumbnails do: they are the
# abap2UI5-linter's render harness photographing each view (the same
# devDependencies and chromium the check-abap2UI5 workflow drives), which is
# why npm ci and the browser install sit below.
#
# This is the ONLY way the site is published: Settings -> Pages -> Source must
# be "GitHub Actions". A red "pages build and deployment" run on main is
Expand All @@ -30,6 +34,7 @@ on:
- 'README.md'
- '.github/packages.json'
- 'scripts/generate-web-index.mjs'
- 'scripts/generate-screenshots.mjs'
- 'scripts/lib/scan-samples.mjs'
- '.github/workflows/deploy-web.yaml'

Expand All @@ -46,21 +51,41 @@ concurrency:
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 10
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '22'
cache: 'npm'

- name: The catalogue behind the page
run: node scripts/generate-web-index.mjs

# The gate this pipeline needs now that there is nothing here that can
# fail to build: a page whose data file is missing deploys exactly like a
# working one — it just says "the catalogue could not be loaded" to every
# visitor. Assert the five files and a non-trivial index before uploading.
# A thumbnail per sample, photographed by the linter's render harness —
# the render gate's own view of each class, not a staged picture, and
# the closest thing to "seeing it run" a corpus without a playground
# link can offer. Best effort by design, in both directions: the script
# skips a view the headless harness cannot render (sap.ui.comp,
# z2ui5.cc custom controls — its card simply shows no picture, the page
# treats a missing file as exactly that), and a wholesale failure here —
# a browser download flaking, the runtime breaking — must not stop the
# samples themselves from publishing. continue-on-error is that second
# half; the step still fails visibly in the run when it happens.
- run: npm ci
- name: Install chromium for the render harness
run: npx playwright install chromium --with-deps
- name: One thumbnail per sample
continue-on-error: true
run: node scripts/generate-screenshots.mjs

# The gate this pipeline needs: a page whose data file is missing
# deploys exactly like a working one — it just says "the catalogue could
# not be loaded" to every visitor. Assert the five files and a
# non-trivial index before uploading. The thumbnails are deliberately
# NOT asserted — a card without its picture is complete (the <img>
# removes itself), which is what lets the step above be best effort.
- name: Check the artefact is complete
run: |
cd web
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ output
# derived: the catalogue behind the page in web/, written by
# scripts/generate-web-index.mjs on every deploy (AGENTS.md section 8)
web/apps.json

# derived: one thumbnail per sample, photographed by
# scripts/generate-screenshots.mjs on every deploy (AGENTS.md section 8)
web/thumbs/
49 changes: 43 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,14 @@ What that costs you when you edit:

```sh
npm ci
npm run check # abaplint + abap2UI5-linter + overview + keywords + abapdoc + SAMPLES.md + app-rules
npm run check # abaplint + abap2UI5-linter + overview + keywords + abapdoc + SAMPLES.md + catalogue.json + app-rules
```

Individually: `npm run lint` (abaplint), `npm run check:abap2ui5` (the app
class and the view it builds, including a headless render of every view),
`npm run check:overview` (the four consistency directions between the overview
app, the tree, `packages.json` and the README table).
`npm run check:overview` (the five consistency directions between the overview
app, the tree, `packages.json` and the two README tables — the package table
and the *Which package do I need?* decision table).

`npm run fmt:chains` applies the house chain layout. It rewrites whitespace
between chain segments only — but it needs the ABAP to be *balanced* to know
Expand Down Expand Up @@ -295,6 +296,19 @@ gone.
makes that a configuration change over there rather than a second parser.
Changing the shape is not a cosmetic decision — a row that stops matching is
a row that silently is not there.
- **[`catalogue.json`](catalogue.json) is the catalogue as data** — generated
by `npm run catalogue`, checked by `npm run check:catalogue`, **not** edited
by hand. SAMPLES.md is the reading copy for a person; this is the same
catalogue for a program: one entry per sample with class, path, package,
technology, `@summary`, `@keywords`, and the package's *Runs on* and *Plays
together with* facts repeated on the entry — so "which sample shows X with
RAP, and what does my system need for it" is answered from one committed
file, one `raw.githubusercontent.com` fetch away, without running anything.
It introduces no source of truth: everything in it comes out of the same
scan behind SAMPLES.md (`scripts/lib/scan-samples.mjs`) and the same package
merge behind the page (`scripts/lib/read-packages.mjs`). Committed, unlike
`web/apps.json`, because its reader runs no generator — which is exactly why
the freshness check exists.

## 7. When you add a sample

Expand All @@ -307,15 +321,17 @@ gone.
4. Give it a `" @keywords` line as its first line (§6) — what somebody would
type who does not know your sample exists.
5. Say what it needs in the package README if it needs anything new.
6. `npm run samples:md` and commit `SAMPLES.md` with it (§6).
6. `npm run samples:md` and `npm run catalogue`, and commit `SAMPLES.md` and
`catalogue.json` with it (§6).
7. `npm run check`.

## 8. The page in `web/`

**<https://abap2ui5.github.io/samples-stack/>** — the catalogue as a searchable
page, for somebody who has installed nothing yet and is asking whether their
system can run any of it. Four files: `index.html`, `stack.css`, `stack.js` and
the generated `web/apps.json`. [`web/README.md`](web/README.md) has the detail;
system can run any of it. Three hand-written files — `index.html`, `stack.css`,
`stack.js` — plus two generated-at-deploy pieces, `web/apps.json` and the
`web/thumbs/` thumbnails. [`web/README.md`](web/README.md) has the detail;
what matters here:

- **It introduces no new source of truth.** Every fact on it is read out of
Expand All @@ -327,6 +343,27 @@ what matters here:
- **`web/apps.json` is generated and not committed** — `npm run web:index`
writes it, `deploy-web` writes it again on every deploy. A committed copy
would put a diff of derived data on every sample pull request.
- **`web/thumbs/` follows the same rule: one thumbnail per sample, generated
at deploy, never committed.** `scripts/generate-screenshots.mjs` (`npm run
screenshots`) photographs each app's main view with the abap2UI5-linter's
render harness — the view statically, seeded with mock data, no Gateway,
RAP or APC anywhere — so what a card shows is what the render gate checks.
It is a deploy step, not a gate, and it is in no check aggregate; it is the
one script here that needs the devDependencies and a playwright chromium.
A view the harness cannot render is reported and skipped, and the page
treats the missing file as "no picture" (the `<img>` removes itself), so a
card without a thumbnail is normal, not broken. Measured over the corpus
(2026-08): **19 of 32 app views render.** The 13 skips are three stable
categories — `sap.ui.comp` controls (SAPUI5-only, absent from the
harness's OpenUI5 runtime: 7 of the 9 Smart Controls samples), `z2ui5.cc`
custom controls that do not load headless (the WebSocket, MIME-audio and
Smart Multi Input samples), and three RAP samples whose `ObjectStatus`
gets an empty `state` from the mock model. So the Smart Controls, AMC/APC
and MIME cards are mostly or wholly picture-less, and that is expected —
a change to the harness, not to those classes, is what would fix it. Only
a run that photographs *nothing* fails, because that is a harness problem;
even then the deploy publishes (`continue-on-error`), since a page without
pictures beats no page.
- **`npm run check:web` is the gate** (its own workflow, and part of `npm run
check`): it runs the generator with `--check`, which fails on a package with
no README row, a row whose cells no longer parse, or an app in a directory
Expand Down
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,27 @@ for and try it out — the others can wait until you need them.
> your system runs, and every card says what the sample needs from that system
> before you install anything. ([`web/`](web/README.md))

## Which package do I need?

Most readers need exactly one. The nine areas are unrelated technologies, none
depends on another — so find the row that says what you came to do, take that
package, and skip the rest:

| You want to … | Package | It needs |
|---|---|---|
| Bind a view straight to an OData V2 service you already run | [`src/01`](src/01/README.md) — OData | an activated OData V2 service |
| Get columns, filters and value help for free from OData metadata — SmartTable, SmartField, SmartFilterBar | [`src/02`](src/02/README.md) — Smart Controls | SAPUI5 + an activated Gateway service |
| Put a screen in front of a RAP business object, in plain ABAP with EML | [`src/03`](src/03/README.md) — RAP | ≥ 1909; the business object ships with the package |
| Add draft handling on top — edit, resume, discard, activate | [`src/04`](src/04/README.md) — RAP with Draft | ≥ 1909; the draft business object ships with the package |
| React to what a business object announces, while it happens | [`src/05`](src/05/README.md) — Business Events | ≥ 2021, the release that carries RAP business events |
| Keep session state and an ABAP `ENQUEUE` lock alive between two clicks | [`src/06`](src/06/README.md) — Stateful Sessions / Locks | on-premise; the lock table ships with the package |
| Push messages from ABAP into every open browser tab, without JavaScript | [`src/07`](src/07/README.md) — AMC/APC | on-premise; activate one ICF node in `SICF` |
| Play or serve a file the MIME repository already holds | [`src/08`](src/08/README.md) — MIME Play Audio | on-premise; activate one ICF service in `SICF` |
| Expose your app in the Fiori Launchpad — tile, startup parameters, cross-app navigation | [`src/09`](src/09/README.md) — Launchpad | a launchpad with a tile pointing at abap2UI5 |

Each package README opens with a **What you need** section that turns the last
column into concrete steps; the table below adds the exact release floors.

## What is in here

| Package | Topic | Plays together with | Runs on |
Expand Down Expand Up @@ -234,8 +255,9 @@ they take seconds.
|---|---|
| `abap-standard` | `abaplint ./abaplint.jsonc` — syntax `v757`, the on-premise release |
| `check-abap2UI5` | [`abap2ui5lint`](https://github.com/abap2UI5/linter) — the app class and the view it produces, together; also writes the two badges above |
| `check-overview` | the two hand-kept indexes: every sample is listed in the overview app, and the package table matches `.github/packages.json` |
| `check-overview` | the hand-kept indexes: every sample is listed in the overview app, the package table matches `.github/packages.json`, and the *Which package do I need?* table routes to every package |
| `check-samples-md` | [`SAMPLES.md`](SAMPLES.md) still is what the generator would write — and every app that exists is in an entry |
| `check-catalogue` | [`catalogue.json`](catalogue.json) still is what the generator would write — the same catalogue as data, committed for tooling that fetches one file instead of scanning the tree |
| `check-keywords` | every app carries `@keywords` and `@summary`, and the overview's detail line still is the class's `@summary` |
| `check-abapdoc` | every `"!` block documents the declaration below it, rather than attaching to nothing |
| `check-app-rules` | the shared abaplint rule block still matches its source in [abap2UI5](https://github.com/abap2UI5/abap2UI5) |
Expand Down
Loading