You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Onboarding a documentation app into the knowledge base currently requires a repo to own a
full static-site toolchain: a headless build, a marketplace.json manifest, a release
workflow that packs dist.tar.gz, and compliance with contract/HEADLESS_RULES.md. That
is the right cost for a real docs site, but it is far too much for the most common case we
keep hitting: a team has one or two markdown files they want published in the knowledge
base.
Today those teams either don't onboard at all, or they get parked behind type: "iframe"
— the explicit stopgap — pointing at a README rendered somewhere else. Neither outcome is
good: iframe entries don't get marketplace typography, don't participate in SPA
navigation, and can be blocked by frame-ancestors.
We want a third onboarding type where the docs repo adds one workflow file and nothing
else.
Design summary
Part A — reusable GitHub Action (actions/publish-docs/)
A composite action in this repo, referenced by external repos as AbsaOSS/knowledge-base/actions/publish-docs@master.
Input docs (required): YAML or JSON list of doc definitions. Each entry: md (path to a markdown file in the repo), title, description, slug
(lowercase kebab, validated), optional icon (enum from contract/schema.json,
default book-open), optional tags (max 5).
Input github-token: defaults to ${{ github.token }}.
Behaviour:
Parse and validate the list, failing with a message that names the offending entry
and tells the onboarding repo exactly what to fix (bad slug, missing md file,
duplicate slug, unknown icon, too many tags…).
Convert each markdown file to HTML inside the action — GitHub-flavoured markdown
(tables, task lists, autolinks, footnotes), syntax-highlighted code blocks (light
theme, per contract/STYLE_GUIDE.md), and ```mermaid blocks rendered
client-side from a vendored mermaid bundle (no CDN — the marketplace pages must
stay self-contained).
Wrap each in a minimal headless document that complies with contract/HEADLESS_RULES.md: data-mp-headless="true" on <html>, relative asset
paths, no <base>, light-only.
Emit one bundle: a staging dir with a subdir per slug (index.html + assets)
plus a root bundle.json manifest listing every doc.
Pack as dist.tar.gz and upload it to the repo's latest existing release,
replacing any existing dist.tar.gz asset. Fail clearly if the repo has no release.
Part B — knowledge base support for type: "single-page"
apps.json gains a third entry type. The entry carries no per-doc metadata: { "repo": "org/repo", "type": "single-page", "version": "latest" }. Docs are
discovered from the bundle manifest at build time.
scripts/fetch-apps.js downloads the bundle (same latest/pinned logic), reads bundle.json, and expands the single registry entry into N app entries — one per
doc — extracting each subdir to apps/{slug}/. Slug collisions with other registered
apps fail the build loudly.
The prebuilt path in scripts/build-vite.js (preparePrebuilt) understands
single-page bundles too, so the hermetic offline E2E harness can exercise the type.
Expansion logic is shared between the two entry points, not duplicated.
src/utils/apps.jsgetAppPages() emits single-page routes with a singlePage: true
flag, mirroring how iframe: true flows through today.
Rendering: masthead as on every page, no sidebar, content in a centred reading
column (~72ch / 800px max width) with generous vertical rhythm. Light only, typography
per contract/STYLE_GUIDE.md.
The landing catalog lists each expanded doc as a normal card.
Part C — tests and docs
Hermetic fixture: a small single-page bundle (2 docs, exercising headings, a code block,
a table and a mermaid block) generated by scripts/setup-test-apps.mjs and registered
in the test apps.json.
tests/build-integrity.spec.js gains assertions for the expanded apps, absence of
sidebar markup, the centred-column class, and absolute URL rewriting; the web-fragment
suite gains a rendering/navigation assertion.
New contract/SINGLE_PAGE.md documents the bundle.json format and the copy-paste
onboarding workflow; CLAUDE.md and README.md are updated to describe all three
onboarding types.
What an onboarding repo adds — the whole thing
# .github/workflows/publish-docs.ymlon:
release:
types: [published]workflow_dispatch:
jobs:
publish:
runs-on: ubuntu-latestpermissions:
contents: writesteps:
- uses: actions/checkout@v4
- uses: AbsaOSS/knowledge-base/actions/publish-docs@masterwith:
docs: | - md: docs/overview.md title: Service Overview description: What the service does and how to use it. slug: my-service
Acceptance criteria
actions/publish-docs/ exists with action.yml, pinned self-contained deps
(package.json + package-lock.json inside the action dir — the root package.json is untouched) and an implementation runnable on node20.
The action validates docs defensively; every failure message names the entry and
the fix.
Markdown → HTML supports tables, task lists, autolinks, footnotes, highlighted code
and mermaid, and the output passes contract/HEADLESS_RULES.md.
The action produces a single dist.tar.gz containing one dir per slug plus bundle.json, and replaces the asset on the latest release.
apps.json supports type: "single-page" with no per-doc metadata; the build
expands it from bundle.json for both the GitHub-fetch and prebuilt paths via
shared code.
Duplicate slugs across the registry fail the build with a clear error.
Single-page pages render with the masthead, no sidebar, and a centred reading column.
Expanded docs appear as cards on the landing catalog.
Existing default and iframe types are unaffected — the full Playwright suite
still passes, plus new single-page coverage.
contract/SINGLE_PAGE.md, README.md and CLAUDE.md document the new type.
Motivation
Onboarding a documentation app into the knowledge base currently requires a repo to own a
full static-site toolchain: a headless build, a
marketplace.jsonmanifest, a releaseworkflow that packs
dist.tar.gz, and compliance withcontract/HEADLESS_RULES.md. Thatis the right cost for a real docs site, but it is far too much for the most common case we
keep hitting: a team has one or two markdown files they want published in the knowledge
base.
Today those teams either don't onboard at all, or they get parked behind
type: "iframe"— the explicit stopgap — pointing at a README rendered somewhere else. Neither outcome is
good: iframe entries don't get marketplace typography, don't participate in SPA
navigation, and can be blocked by
frame-ancestors.We want a third onboarding type where the docs repo adds one workflow file and nothing
else.
Design summary
Part A — reusable GitHub Action (
actions/publish-docs/)A composite action in this repo, referenced by external repos as
AbsaOSS/knowledge-base/actions/publish-docs@master.docs(required): YAML or JSON list of doc definitions. Each entry:md(path to a markdown file in the repo),title,description,slug(lowercase kebab, validated), optional
icon(enum fromcontract/schema.json,default
book-open), optionaltags(max 5).github-token: defaults to${{ github.token }}.and tells the onboarding repo exactly what to fix (bad slug, missing
mdfile,duplicate slug, unknown icon, too many tags…).
(tables, task lists, autolinks, footnotes), syntax-highlighted code blocks (light
theme, per
contract/STYLE_GUIDE.md), and```mermaidblocks renderedclient-side from a vendored mermaid bundle (no CDN — the marketplace pages must
stay self-contained).
contract/HEADLESS_RULES.md:data-mp-headless="true"on<html>, relative assetpaths, no
<base>, light-only.index.html+ assets)plus a root
bundle.jsonmanifest listing every doc.dist.tar.gzand upload it to the repo's latest existing release,replacing any existing
dist.tar.gzasset. Fail clearly if the repo has no release.bundle.jsonformat:{ "marketplaceVersion": "1", "type": "single-page", "docs": [ { "slug": "my-service", "title": "Service Overview", "description": "…", "icon": "book-open", "tags": ["platform"], "entryPoint": "index.html" } ] }Part B — knowledge base support for
type: "single-page"apps.jsongains a third entry type. The entry carries no per-doc metadata:{ "repo": "org/repo", "type": "single-page", "version": "latest" }. Docs arediscovered from the bundle manifest at build time.
scripts/fetch-apps.jsdownloads the bundle (same latest/pinned logic), readsbundle.json, and expands the single registry entry into N app entries — one perdoc — extracting each subdir to
apps/{slug}/. Slug collisions with other registeredapps fail the build loudly.
prebuiltpath inscripts/build-vite.js(preparePrebuilt) understandssingle-page bundles too, so the hermetic offline E2E harness can exercise the type.
Expansion logic is shared between the two entry points, not duplicated.
src/utils/apps.jsgetAppPages()emits single-page routes with asinglePage: trueflag, mirroring how
iframe: trueflows through today.column (~72ch / 800px max width) with generous vertical rhythm. Light only, typography
per
contract/STYLE_GUIDE.md.Part C — tests and docs
a table and a mermaid block) generated by
scripts/setup-test-apps.mjsand registeredin the test
apps.json.tests/build-integrity.spec.jsgains assertions for the expanded apps, absence ofsidebar markup, the centred-column class, and absolute URL rewriting; the web-fragment
suite gains a rendering/navigation assertion.
contract/SINGLE_PAGE.mddocuments thebundle.jsonformat and the copy-pasteonboarding workflow;
CLAUDE.mdandREADME.mdare updated to describe all threeonboarding types.
What an onboarding repo adds — the whole thing
Acceptance criteria
actions/publish-docs/exists withaction.yml, pinned self-contained deps(
package.json+package-lock.jsoninside the action dir — the rootpackage.jsonis untouched) and an implementation runnable onnode20.docsdefensively; every failure message names the entry andthe fix.
and mermaid, and the output passes
contract/HEADLESS_RULES.md.dist.tar.gzcontaining one dir per slug plusbundle.json, and replaces the asset on the latest release.apps.jsonsupportstype: "single-page"with no per-doc metadata; the buildexpands it from
bundle.jsonfor both the GitHub-fetch andprebuiltpaths viashared code.
defaultandiframetypes are unaffected — the full Playwright suitestill passes, plus new single-page coverage.
contract/SINGLE_PAGE.md,README.mdandCLAUDE.mddocument the new type.