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
105 changes: 105 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Publishes the samples as the npm package `@cap2ui5/samples` when a tag
# v<version> is pushed. The version is package.json's - bump it, and move the
# changelog's Unreleased lines under it, in the pull request that prepares the
# release; the job refuses a tag that says something else.
#
# Trusted publishing (OIDC), as cap2UI5's own release.yml publishes the
# plugin: no NPM_TOKEN to leak or rotate, and npm records a provenance
# attestation tying the tarball to this run and commit. The package's
# Settings -> Trusted Publisher on npmjs.com has to name this repository and
# this workflow file - on the command line:
#
# npx npm@11 trust github @cap2ui5/samples --file release.yml \
# --repo cap2UI5/samples --allow-publish
#
# That setting can only be made for a package that ALREADY EXISTS, so the very
# first version is published by hand, once, after `npm login`:
#
# npm publish --access public
#
# (no --provenance there: npm generates an attestation only from a supported
# CI and aborts anywhere else) - and the Trusted Publisher is set right after:
# cap2UI5's v0.2.0 was tagged while its package existed without one, and its
# release failed with ENEEDAUTH. Until the package exists, a failed publish
# here is a warning naming that bootstrap; once it exists, a failed publish is
# an error.
name: release

on:
push:
tags:
- "v*.*.*"
# Everything except the publish: the same tests, the tarball listed, no
# registry write.
workflow_dispatch:

permissions:
contents: read

jobs:
# everything test.yml runs - the samples over the wire, the package in a
# throwaway project, the translation and the differential test - on the
# commit to publish
test:
uses: ./.github/workflows/test.yml

publish:
needs: test
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
id-token: write # trusted publishing + provenance
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: .nvmrc

# Node 22 ships npm 10.x, which has no OIDC support. PINNED, not
# @latest: this is the one job in the repository that holds a publishing
# identity. The same pin as cap2UI5's release.yml.
- name: Use an npm that can publish via OIDC
run: npm install -g npm@12.0.2

# The tag is the version claim; package.json is what gets published. A
# mismatch caught here costs a minute, caught afterwards a burned version
# number.
- name: The tag and package.json agree
if: github.ref_type == 'tag'
run: |
PKG="v$(node -p "require('./package.json').version")"
if [ "$PKG" != "$GITHUB_REF_NAME" ]; then
echo "::error::tag $GITHUB_REF_NAME does not match package.json ($PKG)"
exit 1
fi

# what the tarball will contain, in the log, before it is immutable
- name: What is in the tarball
run: npm pack --dry-run

- name: Publish
if: github.ref_type == 'tag'
run: |
set -u
NAME="$(node -p "require('./package.json').name")"
VERSION="$(node -p "require('./package.json').version")"
if npm view "$NAME@$VERSION" version >/dev/null 2>&1; then
echo "$NAME@$VERSION is already on the registry - nothing to do"
exit 0
fi
if npm publish --provenance --access public; then
echo "published $NAME@$VERSION"
exit 0
fi
if npm view "$NAME" version >/dev/null 2>&1; then
echo "::error::$NAME is on the registry, but publishing $VERSION failed - see the npm output above (the Trusted Publisher not pointing at this repository and release.yml?)"
exit 1
fi
echo "::warning::$NAME@$VERSION was NOT published: the package does not exist on the registry yet, and trusted publishing can only be configured for one that does. Bootstrap it once by hand (the header of this workflow), then every tag publishes here."

- name: Dry run only
if: github.ref_type != 'tag'
run: echo "Not a tag - everything ran except the publish."
33 changes: 15 additions & 18 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,32 @@ on:
push:
branches: [main]
pull_request:
# release.yml runs all of this on the tagged commit before it publishes
workflow_call:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: .nvmrc
# Until cap2ui5 0.2.0 is on npm, the samples run against cap2UI5's plugin:
# packed as `npm publish` packs it, and installed alone - which brings the
# other dependencies along, as the README says for a local checkout. Once
# it is published this goes, a package-lock.json pins it, and `cache: npm`
# and npm ci come back.
#
# The ref is the branch that adds abap2js (and t.numc, which
# z2ui5_cl_smp_app_067 needs) until that is on cap2UI5's main - then this
# line goes too.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: cap2UI5/cap2UI5
ref: claude/absp2ui5-cap2ui5-transfer-7pg6dg
path: .cap2ui5
persist-credentials: false
- run: npm pack --pack-destination "$RUNNER_TEMP"
working-directory: .cap2ui5/plugin
- run: npm install --no-save --no-audit --no-fund "$RUNNER_TEMP"/cap2ui5-*.tgz
# The plugin from npm, in the version the peer dependency names - what a
# project that adds the samples runs them on. No lockfile, as in
# cap2UI5: the run tests what an install gets today.
- run: npm install --no-audit --no-fund
- run: npm test

# ---- the package: packed as publish packs it, installed into a
# throwaway CAP project beside an app of its own, and served
- run: npm run consumer-test

# ---- the translation: srv/apps is what abap2js makes of the pinned samples
- id: pin
run: echo "sha=$(cat ABAP2UI5_SAMPLES_PIN)" >> "$GITHUB_OUTPUT"
Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Changelog

All notable changes to `@cap2ui5/samples` are recorded here. The format
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
versions [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.1.0] - 2026-09-29

The first release: abap2UI5's samples as a package a CAP project adds.

- 71 of abap2UI5's 129 samples as cap2UI5 apps in `srv/apps/`: 69 translated
by abap2js from abap2UI5/samples at the commit in `ABAP2UI5_SAMPLES_PIN`,
two ported by hand. The differential test holds every one of them to its
ABAP original.
- `npm add @cap2ui5/samples` in a project with `@cap2ui5/cds-plugin` 0.3 - a
peer dependency - and the samples run beside the project's own apps: the
package declares them for the plugin, `"cap2ui5": { "apps": "srv/apps" }`.

[Unreleased]: https://github.com/cap2UI5/samples/compare/v0.1.0...HEAD
[0.1.0]: https://www.npmjs.com/package/@cap2ui5/samples/v/0.1.0
72 changes: 54 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# cap2UI5 samples

The [abap2UI5 samples](https://github.com/abap2UI5/samples) as
[cap2UI5](https://github.com/cap2UI5/cap2UI5) apps: every sample a plain
JavaScript class in `srv/apps/`, in an ordinary CAP project - **translated from
its ABAP original, not rewritten.** cap2ui5's `abap2js` reads the ABAP class
[cap2UI5](https://github.com/cap2UI5/cap2UI5) apps, published as
**`@cap2ui5/samples`**: add the package to a CAP project and they run beside
its own apps, as abap2UI5's samples run in the system they are pulled into.
Every sample is a plain JavaScript class in `srv/apps/` - **translated from
its ABAP original, not rewritten.** cap2UI5's `abap2js` reads the ABAP class
and writes the module line for line: the same class name, the same attributes
and methods, the same calls - the client is abap2UI5's `z2ui5_if_client` by its
own names, the view is built with `z2ui5_cl_ui5_view_builder` - and the
Expand All @@ -14,26 +16,34 @@ original's comments and texts, all of them, as they are.
> **Status: 71 of the 129 samples.** 69 are generated by abap2js from
> abap2UI5/samples at the commit in [`ABAP2UI5_SAMPLES_PIN`](ABAP2UI5_SAMPLES_PIN),
> two are ported by hand; the [differential test](#the-differential-test) holds
> every one of them to its ABAP original. They need a cap2ui5 that is not on
> npm yet: 0.2.0 (the client under its ABAP names, the view builder) plus
> abap2js and `t.numc()`, which are on cap2UI5's branch
> `claude/absp2ui5-cap2ui5-transfer-7pg6dg` until they are on its `main`.
> Until then a plain `npm install` answers `ETARGET`; install the plugin packed
> from that branch instead, which brings the other dependencies along - CI does
> the same:
>
> ```bash
> git clone -b claude/absp2ui5-cap2ui5-transfer-7pg6dg https://github.com/cap2UI5/cap2UI5 ../cap2UI5
> (cd ../cap2UI5/plugin && npm pack --pack-destination /tmp)
> npm install --no-save /tmp/cap2ui5-*.tgz
> ```

## Run
> every one of them to its ABAP original.

## Add them to your project

In a CAP project with [`@cap2ui5/cds-plugin`](https://github.com/cap2UI5/cap2UI5)
0.3 or later:

```bash
npm add -D @cap2ui5/samples
cds watch # lists every sample beside your own apps
```

The plugin loads the apps a dependency declares in its `package.json` -
here `"cap2ui5": { "apps": "srv/apps" }` - beside the project's own, so every
sample starts under its ABAP name, e.g.
`/sap/bc/z2ui5?app_start=Z2UI5_CL_SMP_APP_493`. As a devDependency they are
there in development only: with `NODE_ENV=production` the plugin leaves a
devDependency's apps out, as CAP leaves out its plugins. `npm add` without
`-D` brings them to production too. An app of the project's own that has the
name of a sample stays the project's; the log says so.

## Run this repository

```bash
npm install
npm run watch # cds watch - prints the address of every sample
npm test # every sample driven over the wire
npm run consumer-test # the package: packed, installed into a throwaway CAP project, served
```

Log in as `alice` with an empty password (CAP's mocked development user), then
Expand Down Expand Up @@ -226,3 +236,29 @@ counterpart:
3. **Every field is part of the model.** ABAP keeps a `PROTECTED` attribute out
of it; a JavaScript field with an initial value is always bound. The client
is the exception: assigned in `main( )` and not declared, it stays out.

## Publishing

A tag `v<version>` publishes the package as `@cap2ui5/samples`
(`.github/workflows/release.yml`). It runs everything `test.yml` runs on the
tagged commit - the samples over the wire, the package in a throwaway
project, the translation and the differential test - and publishes by
trusted publishing, with provenance and no token. The version is
`package.json`'s: the pull request that prepares a release bumps it and moves
the changelog's `Unreleased` lines under it. The package carries `srv/apps/`,
this README, the changelog and the license - not the tests, the scripts or
the pin. A sample that needs a newer plugin raises the peer dependency on
`@cap2ui5/cds-plugin` with it.

npm points a package at a workflow only once the package exists, so the
first version goes out by hand, once, from a clean checkout of `main` whose
CI is green - and the Trusted Publisher is set right after, or the next tag
fails to publish:

```bash
npm install && npm test && npm run consumer-test
npm login
npm publish --access public
npx npm@11 trust github @cap2ui5/samples --file release.yml --repo cap2UI5/samples --allow-publish
git tag v0.1.0 && git push origin v0.1.0 # finds 0.1.0 on npm and publishes nothing
```
46 changes: 38 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,48 @@
{
"name": "cap2ui5-samples",
"private": true,
"description": "The abap2UI5 samples as cap2UI5 apps: plain JavaScript classes in a CAP project, views built with ViewBuilder.",
"name": "@cap2ui5/samples",
"version": "0.1.0",
"description": "abap2UI5's samples as cap2UI5 apps: add the package to a CAP project and they run beside its own apps. Every sample a plain JavaScript class, translated from its ABAP original line for line.",
"keywords": [
"cap",
"cds",
"sap",
"ui5",
"sapui5",
"abap2ui5",
"cap2ui5",
"samples"
],
"homepage": "https://github.com/cap2UI5/samples#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/cap2UI5/samples.git"
},
"bugs": {
"url": "https://github.com/cap2UI5/samples/issues"
},
"license": "MIT",
"type": "module",
"files": [
"srv/apps",
"README.md",
"CHANGELOG.md",
"LICENSE"
],
"cap2ui5": {
"apps": "srv/apps"
},
"engines": {
"node": ">=22"
},
"dependencies": {
"@cap-js/sqlite": "^3",
"@sap/cds": "^10",
"cap2ui5": "^0.2.0"
"peerDependencies": {
"@cap2ui5/cds-plugin": "^0.3.0"
},
"devDependencies": {
"@abaplint/transpiler-cli": "2.13.91",
"@cap-js/cds-test": "^1.0.2",
"@cap-js/sqlite": "^3",
"@cap2ui5/cds-plugin": "^0.3.0",
"@sap/cds": "^10",
"fast-xml-parser": "^5.11.1"
},
"scripts": {
Expand All @@ -23,7 +52,8 @@
"generate": "node scripts/generate.mjs",
"check:generated": "node scripts/generate.mjs --check",
"originals": "node scripts/originals.mjs",
"differential": "node scripts/originals.mjs && node --test test/differential.mjs"
"differential": "node scripts/originals.mjs && node --test test/differential.mjs",
"consumer-test": "node scripts/consumer-test.mjs"
},
"cds": {
"requires": {
Expand Down
Loading
Loading