Skip to content

feat: single-page documentation type with zero-config publish action #35

Description

@oto-macenauer-absa

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.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:
    1. 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…).
    2. 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).
    3. 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.
    4. Emit one bundle: a staging dir with a subdir per slug (index.html + assets)
      plus a root bundle.json manifest listing every doc.
    5. 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.

bundle.json format:

{
  "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.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.js getAppPages() 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.yml
on:
  release:
    types: [published]
  workflow_dispatch:

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
      - uses: AbsaOSS/knowledge-base/actions/publish-docs@master
        with:
          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.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions