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-web.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: check-web

# The page in web/ is generated from four things at once: the classes, the
# package index, the package table of the README and the scan they share. A
# pull request can break that without touching web/ at all - a package with no
# README row, a row whose cells no longer parse, an app in a directory that is
# no package. None of it shows up until a deploy, and a deploy happens after
# the merge. This check runs the generator with --check on every pull request
# instead. Plain node, no dependencies, so it stays a few seconds.

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

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

jobs:
check-web:
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-web-index.mjs --check
95 changes: 95 additions & 0 deletions .github/workflows/deploy-web.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: deploy-web

# Publishes the page in web/ to this repository's GitHub Pages.
#
# 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.
#
# No dependencies are installed — the generator is plain node over the same
# scan SAMPLES.md is built from.
#
# 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
# GitHub's built-in Jekyll builder rather than this workflow; re-saving the
# Pages source in Settings clears it.

on:
workflow_dispatch:
push:
branches: [ main ]
paths:
# the page is derived from all of these: the classes (@summary,
# @keywords, the ABAP-Doc header, the DESCRIPT beside them), the package
# index, the package table of the README, and the page itself
- 'src/**'
- 'web/**'
- 'README.md'
- '.github/packages.json'
- 'scripts/generate-web-index.mjs'
- 'scripts/lib/scan-samples.mjs'
- '.github/workflows/deploy-web.yaml'

permissions:
contents: read
pages: write
id-token: write

# one Pages deployment at a time; let a newer run supersede an in-flight one
concurrency:
group: pages
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

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

- 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 four files and a non-trivial index before uploading.
- name: Check the artefact is complete
run: |
cd web
for f in index.html stack.css stack.js apps.json; do
test -s "$f" || { echo "deploy-web: $f is missing or empty"; exit 1; }
done
node -e '
const x = require("./apps.json");
if (!Array.isArray(x.apps) || x.apps.length < 20) {
console.error(`deploy-web: apps.json holds ${x.apps?.length} samples, expected 20+`);
process.exit(1);
}
if (!Array.isArray(x.packages) || x.packages.length < 5) {
console.error("deploy-web: apps.json has no package list");
process.exit(1);
}
console.log(`deploy-web: ${x.apps.length} samples, ${x.packages.length} groups`);
'

- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: web

deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
node_modules
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
27 changes: 27 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,33 @@ gone.
6. `npm run samples:md` and commit `SAMPLES.md` 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;
what matters here:

- **It introduces no new source of truth.** Every fact on it is read out of
something the repository already keeps — the scan (`@summary`, `@keywords`,
the ABAP-Doc header, `DESCRIPT`), `.github/packages.json` (branch, release)
and the README's package table (what a package plays together with). Adding a
sample or a package therefore needs **no step for the page**: `npm run
samples:md`, and it is on both.
- **`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.
- **`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
that is no package. All three break the page without touching `web/`, and
none of them shows up before a deploy — which happens after the merge.
- **No playground link, unlike the sibling repositories' pages.** The playground
runs a class with no system behind it, and a system is what every sample here
needs. Each app would open there and fail, so the cards link to the source,
to the package README's *What you need*, and to the one-package branch.

<!-- The section below is SHARED. Its source is
abap2UI5/abap2UI5 .github/shared/agents-metadata.md - change it THERE
first, or the change is drift. abap2UI5's `npm run check:shared`
Expand Down
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
<br>
[![check-abapdoc](https://github.com/abap2UI5/samples-stack/actions/workflows/check-abapdoc.yaml/badge.svg)](https://github.com/abap2UI5/samples-stack/actions/workflows/check-abapdoc.yaml)
[![check-prose-names](https://github.com/abap2UI5/samples-stack/actions/workflows/check-prose-names.yaml/badge.svg)](https://github.com/abap2UI5/samples-stack/actions/workflows/check-prose-names.yaml)
[![check-web](https://github.com/abap2UI5/samples-stack/actions/workflows/check-web.yaml/badge.svg)](https://github.com/abap2UI5/samples-stack/actions/workflows/check-web.yaml)
<br>
[![create-package-branches](https://github.com/abap2UI5/samples-stack/actions/workflows/create-package-branches.yaml/badge.svg)](https://github.com/abap2UI5/samples-stack/actions/workflows/create-package-branches.yaml)
[![deploy-web](https://github.com/abap2UI5/samples-stack/actions/workflows/deploy-web.yaml/badge.svg)](https://github.com/abap2UI5/samples-stack/actions/workflows/deploy-web.yaml)

# abap2UI5 — samples-stack

Expand Down Expand Up @@ -42,6 +45,12 @@ install-and-run.
Every area is self-contained and brings its own README. Pick the one you came
for and try it out — the others can wait until you need them.

> **Looking for one particular sample?**
> **<https://abap2ui5.github.io/samples-stack/>** is this catalogue as a
> searchable page: filter by the technology you came for and by the release
> your system runs, and every card says what the sample needs from that system
> before you install anything. ([`web/`](web/README.md))

## What is in here

| Package | Topic | Plays together with | Runs on |
Expand Down Expand Up @@ -231,7 +240,9 @@ they take seconds.
| `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) |
| `check-prose-names` | every class name written in prose exists — including the sibling repositories' |
| `check-web` | the data behind the [page](https://abap2ui5.github.io/samples-stack/) can still be generated — every package has a README row that parses, every app sits in a package |
| `create-package-branches` | rebuilds the nine per-package branches, each verified with abaplint at its own release before it is pushed |
| `deploy-web` | publishes [`web/`](web/README.md) to GitHub Pages on every push to `main` that touches the tree it describes |

`check-overview` exists because the overview app names its samples as strings and
resolves them at runtime — that is what lets it survive a package the system cannot
Expand All @@ -242,6 +253,13 @@ where the generated branches take theirs from. It runs `node
scripts/check-overview.mjs`, needs no dependencies, and skips both
full-tree halves on a checkout that carries only part of the repository.

`check-web` and `deploy-web` are the two halves of the page: the check runs the
generator on every pull request without writing anything, the deploy runs it for
real and uploads `web/` as the Pages artefact. Nothing generated is committed, so
the page can never be staler than the tree — and a sample pull request carries no
diff of derived data. `deploy-web` is the *only* way the site is published:
*Settings → Pages → Source* has to be **GitHub Actions**.

`create-package-branches` runs on pull requests too, everything except the push —
so a change that would break one of the branches fails while it can still be
fixed. Adding a package is one entry in `.github/packages.json`; neither the
Expand Down Expand Up @@ -292,5 +310,10 @@ in front of it. None of these packages depends on another, and none of them is a
prerequisite for using abap2UI5 at all: they are options you can reach for when
they help.

If you are not sure which package that is, start at
**<https://abap2ui5.github.io/samples-stack/>**: pick the release your system runs
and it shows you what is within reach, with the setup each sample needs on its
card.

Something else you would like to see combined with abap2UI5? Open an issue or a
pull request — the collection grows with the scenarios people bring to it.
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
"check:abap2ui5": "abap2ui5lint",
"fmt:chains": "abap2ui5lint --fix",
"check:overview": "node scripts/check-overview.mjs",
"check": "npm run lint && npm run check:abap2ui5 && npm run check:overview && npm run check:keywords && npm run check:abapdoc && npm run check:samples-md && npm run check:app-rules && npm run check:prose",
"check": "npm run lint && npm run check:abap2ui5 && npm run check:overview && npm run check:keywords && npm run check:abapdoc && npm run check:samples-md && npm run check:app-rules && npm run check:prose && npm run check:web",
"check:keywords": "node scripts/check-keywords.mjs",
"check:abapdoc": "node scripts/check-abapdoc.mjs",
"samples:md": "node scripts/generate-samples-md.mjs",
"check:samples-md": "node scripts/generate-samples-md.mjs --check",
"check:app-rules": "node scripts/check-app-rules.mjs",
"check:prose": "node scripts/check-prose-names.mjs"
"check:prose": "node scripts/check-prose-names.mjs",
"web:index": "node scripts/generate-web-index.mjs",
"check:web": "node scripts/generate-web-index.mjs --check"
},
"repository": {
"type": "git",
Expand Down
10 changes: 5 additions & 5 deletions scripts/generate-samples-md.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { scanSamples, scanOverview } from './lib/scan-samples.mjs';
import { scanSamples, scanOverview, sampleTitle } from './lib/scan-samples.mjs';

const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const OUT = path.join(ROOT, 'SAMPLES.md');
Expand All @@ -54,10 +54,10 @@ const cell = (s) => s.replace(/\|/g, '\\|').trim();
* better than DESCRIPT alone and one step short of right: it put a sample's
* description in a different class from the sample. */
function row(s, section) {
const repeats = s.header === section;
const head = repeats
? (cell(s.sub) || `**${cell(s.header)}**`)
: (s.sub ? `**${cell(s.header)}** — ${cell(s.sub)}` : `**${cell(s.header)}**`);
const { title, sub, fromSub } = sampleTitle(s, section);
const head = sub
? `**${cell(title)}** — ${cell(sub)}`
: (fromSub ? cell(title) : `**${cell(title)}**`);
const summary = s.summary ? `<br>${cell(s.summary)}` : '';
const keywords = s.keywords ? `<br><sub>${cell(s.keywords)}</sub>` : '';
return `| ${head}${summary}${keywords} | [\`${s.cls.toUpperCase()}\`](${s.rel}) |`;
Expand Down
Loading