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
90 changes: 90 additions & 0 deletions .github/workflows/regenerate-references.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# The client API reference (resources/api.md, public/api/client-api.json, the
# user-exit block) and the custom-control reference (resources/controls.md)
# are generated from abap2UI5 main. check:api-reference and check:controls
# fail the moment an interface or a control changes over there - and with
# them every deploy, because deploy.yml runs the same gates: from 2026-11-03
# to 2026-11-04 the site was not published at all, because z2ui5_if_client
# had gained _bind_tab and _bind_tab_row and nobody had run
# `npm run generate:api` here.
#
# So, every day before the deploy (05:00): regenerate both, and when that
# changes anything, open (or update) one pull request with the result - the
# same move bump-linter.yaml makes for the linter.
name: regenerate-references

on:
schedule:
- cron: '23 4 * * *' # daily 04:23 UTC - before deploy.yml at 05:00
workflow_dispatch:

permissions:
contents: write
pull-requests: write
# To start check.yml on the PR's branch (below).
actions: write

concurrency:
group: regenerate-references
cancel-in-progress: false

jobs:
regenerate:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: '22'
cache: npm
- run: npm ci

- name: Regenerate the references from abap2UI5 main
id: gen
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
npm run generate:api
npm run generate:controls
if git diff --quiet; then
echo "changed=false" >> "$GITHUB_OUTPUT"
else
echo "changed=true" >> "$GITHUB_OUTPUT"
git diff --stat
fi

# The gates that read what was just written: both freshness checks (they
# must hold now), the prose the generated blocks add, and the build.
- if: steps.gen.outputs.changed == 'true'
run: npm run check:api-reference
- if: steps.gen.outputs.changed == 'true'
env:
GITHUB_TOKEN: ${{ github.token }}
run: npm run check:controls
- if: steps.gen.outputs.changed == 'true'
run: npm run check:vocabulary
- if: steps.gen.outputs.changed == 'true'
run: npm run docs:build

- if: steps.gen.outputs.changed == 'true'
id: pr
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
branch: regenerate-references
title: 'chore: regenerate the API and controls references'
commit-message: 'chore: regenerate the API and controls references from abap2UI5 main'
body: |
`z2ui5_if_client`, the user exit or a custom control changed on
abap2UI5 `main`; `npm run generate:api` and
`npm run generate:controls` rewrote the generated references.
Until this is merged, check:api-reference / check:controls fail
and deploy.yml does not publish the site.

# A push or a PR made with the workflow's own GITHUB_TOKEN starts no
# workflow runs - GitHub's rule against loops - so check.yml never ran on
# this PR. workflow_dispatch is exempt.
- if: steps.pr.outputs.pull-request-operation == 'created' || steps.pr.outputs.pull-request-operation == 'updated'
run: gh workflow run check.yml --ref regenerate-references
env:
GH_TOKEN: ${{ github.token }}
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ in it are decidable, and all fifteen are decided before a merge:
| `docs:build` | a page that does not build is a page nobody can read |
| `check:examples` | the ABAP in the fenced blocks, against the real framework: does it compile, and does the view it builds name controls and properties that exist on the UI5 floor this documentation targets. **It ran no abaplint rules at all until 2026-09-04** — the generated config had no `rules` block, so abaplint walked 139 files, ran nothing, and printed `0 issue(s) found` for years. Ten examples with an unbalanced parenthesis were sitting behind that. The three compile rules it runs now (`parser_error`, `check_syntax`, `unknown_types`) are the sample repositories' own; the reasoning for stopping there, measured against the full 188-rule set, is in the file. A fragment cannot be compiled, so every fence is also read for the two things decidable without a system: a call to a method of the frozen builder, and a `|…|` string template whose literal text runs over a line break (only a `{ … }` may) - nine binding strings were written that way and none compiled |
| `check:api-names` | every `client->` name on the site — method, parameter, `cs_*` constant — against `z2ui5_if_client` on `main`, plus every `blob/main/` link into the framework's tree, plus **no name from the frozen package** (`src/99`: `z2ui5_cl_util*`, `z2ui5_cl_pop_*`, `z2ui5_cl_xml_view*`, `z2ui5_if_exit`, `z2ui5_if_types`, …) anywhere but on the deprecations page and in the changelog. `check:examples` compiles the fenced blocks that are whole CLASSES; this is the rest of the page: the sentence, the two-line snippet, the constant block a page reproduces, the source link. Four pages taught API that 1.143.0 had deleted and nothing was red |
| `check:api-reference` | the committed client API reference — the generated block in `resources/api.md` and `docs/public/api/client-api.json` — regenerated from `z2ui5_if_client` on `main` and compared byte for byte. Goes stale whenever the interface changes over there and the committed reference still describes the shape before it. `npm run generate:api` rewrites both. The same run renders the user exit — `z2ui5_if_ui5_exit` plus the defaults `z2ui5_cl_ui5_user_exit` seeds, read out of the class, not retyped (`scripts/lib/user-exit.mjs`, pinned by `test/user-exit.test.mjs`) — into the block between the same `api:` markers on `advanced/extensibility/user_exits.md`, and `--check` holds that block too. Three artifacts, not two |
| `check:api-reference` | the committed client API reference — the generated block in `resources/api.md` and `docs/public/api/client-api.json` — regenerated from `z2ui5_if_client` on `main` and compared byte for byte. Goes stale whenever the interface changes over there and the committed reference still describes the shape before it. `npm run generate:api` rewrites both; `regenerate-references.yaml` runs it (and `generate:controls`) every day before the deploy and opens a pull request when anything moved. The same run renders the user exit — `z2ui5_if_ui5_exit` plus the defaults `z2ui5_cl_ui5_user_exit` seeds, read out of the class, not retyped (`scripts/lib/user-exit.mjs`, pinned by `test/user-exit.test.mjs`) — into the block between the same `api:` markers on `advanced/extensibility/user_exits.md`, and `--check` holds that block too. Three artifacts, not two |
| `check:controls` | the committed custom-control reference — the generated block in `resources/controls.md` — regenerated from `app/webapp/cc/*.js` on `main` and compared byte for byte. The metadata (properties with type and default, events with parameters, aggregations) is read by RUNNING each module in an empty vm with stand-in dependencies (`scripts/lib/controls.mjs` says why a regex is the wrong tool: a default is sometimes an expression, and one control builds its definition first and hands it to `extend( )` by name). Each section links the pages of this site that use the control, found by walking them — so a cookbook page that starts using one changes the block, and the gate says to run `npm run generate:controls`. Lists the directory through the GitHub contents API (with `GITHUB_TOKEN` in CI) or `git ls-tree` with `A2UI5_HOME`; unreachable means SKIPPED, never a false OK. Pinned by `test/controls.test.mjs` |
| `check:audience` | every prose line of the manual against the phrases of the site for decision makers — `license fee`, `per-user fee`, `subscription`, `ROI`, `total cost`, `vendor lock`, `SLA`, `decision maker`, `IT management`, `business case` (whole words, any case, plural allowed; `scripts/lib/audience.mjs` is the list) — on every page but the four whose subject they are: `resources/license.md`, `resources/support.md`, `resources/sponsor.md` and `get_started/about.md` (the hand-off sentence). **The manual is for developers and links https://abap2ui5.github.io/why/**; both AGENTS.md files said so and nothing held it, and the business case had grown back on a dozen pages (two whole essays, the comparison tables of two more, the openings of Performance and Security). A page that argues the case is a second, unmaintained copy of that site. The prose is `scripts/lib/prose.mjs`'s, so a fence, a link target or a comment is never a hit; plain "per user" is not on the list because a work process per user is a technical fact. Floor: exit 1 when no page was walked. Pinned by `test/audience.test.mjs` |
| `check:conventions` | the fenced ABAP against the house style the reader meets next: the view-chain layout, and the three section blocks of an app class. `check:examples` asks whether an example compiles and names real API — both questions about the framework; neither can see that a snippet is written in a different style from every sample. Measured against [samples-controls](https://github.com/abap2UI5/samples-controls) (637 classes, gated, at zero): five chains here showed the reader a different tree than the one that renders, and 57 of 86 app classes carried neither `PROTECTED SECTION.` nor `PRIVATE SECTION.`. What this gate deliberately does NOT take over is the blank-line and `t_arg` continuation rules — those are pattern-lint *warnings* over there and that corpus carries 382 of them. And, third, the lifecycle: in every runnable app class (the Run button's own decision, so this and `check:playground` never disagree) a `client->view_display( )` in `main( )` must sit under an `IF`/`ELSEIF`/`WHEN` on `client->check_on_init( )` or `check_on_navigated( )` — a nested IF inherits the branch; `ELSE`, an event `WHEN` and a negated check (`= abap_false`, `NOT`) do not. Only `main( )` is read: the canonical shape calls a `view_display( )` method of the class from the branch, and following that call would be a compiler. Twenty cookbook classes sent their view on every roundtrip, the one thing Life Cycle, Common Failures and the tutorial tell the reader not to do, and nothing was red. A page that means the exception says so directly above the fence, `<!-- lifecycle: allow unconditional display — reason -->` (Step 2 of the tutorial, the first screen before events exist), and a marker whose example no longer needs it fails as loudly as a missing one. `scripts/lib/lifecycle.mjs`, pinned by `test/lifecycle.test.mjs` |
Expand Down
Loading