Skip to content

feat: single-page documentation type - #36

Merged
oto-macenauer-absa merged 3 commits into
masterfrom
feat/single-page-docs
Aug 4, 2026
Merged

feat: single-page documentation type#36
oto-macenauer-absa merged 3 commits into
masterfrom
feat/single-page-docs

Conversation

@oto-macenauer-absa

@oto-macenauer-absa oto-macenauer-absa commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

feat: single-page documentation type (#35)

Closes #35.

Adds a third onboarding type to the knowledge base: single-page — zero-config documentation published straight from plain markdown. A docs repo onboards by adding a single workflow file; no mkdocs, no build config, no manifest files.

Reusable action — actions/publish-single-page-docs

  • Input: a docs list (YAML) — each entry md, title, description, slug, optional icon + tags
  • Renders markdown → headless HTML inside the action: GFM (tables, task lists, footnotes, autolinks), syntax highlighting (light theme per contract/STYLE_GUIDE.md), Mermaid diagrams (vendored, no CDN — pages stay self-contained)
  • Packs one bundle dist.tar.gz (subdir per slug + bundle.json manifest) and uploads it to the repo's latest release (--clobber); optional release-tag input defaults to the triggering release
  • Self-contained dependency tree (own package.json/lockfile), covered by a new CI self-test job

Knowledge base

  • apps.json entry: { "repo": "org/repo", "type": "single-page" } — no per-doc metadata; the build reads bundle.json and expands the entry into one app per doc (slug-collision guarded, expansion recorded in apps/.single-page.json and spliced back via loadRegistry() so Astro sees what the build saw)
  • Rendering: masthead as everywhere, no sidebar, content in a centred main.mp-single-page reading column, light-only
  • prebuilt bundles supported for hermetic/offline builds; registry entries may be "optional": true (skip-with-warning when the local artifact is absent — keeps CI green while enabling local visual testing against a sibling example repo)

Onboarding (copy-paste for a docs repo)

# .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-single-page-docs@master
        with:
          docs: |
            - md: docs/overview.md
              title: Service Overview
              description: What the service does and how to use it.
              slug: my-service

Docs & tests

  • New contract/SINGLE_PAGE.md (bundle format + onboarding guide); README, CLAUDE.md, contract/HEADLESS_RULES.md updated
  • Hermetic single-page fixture; +8 build-integrity and +3 web-fragment tests
  • Suites: 43/43 embedded, 15/15 standalone, 11/11 action self-test

Notes for reviewers

  • Mermaid rendering inside the reframed web-fragment shadow DOM is asserted at asset/markup level only (source survives, vendored JS resolves, no CDN); an actual draw-test would require the full 2.5 MB bundle in the fixture
  • A local sibling repo knowledge-base-example-single-page (not part of this PR) exercises the real action output visually via npm run build:local && npm run preview

🤖 Generated with Claude Code

https://claude.ai/code/session_01CRZk4ZQsApxLANJtzQpVXb

Release Notes

  • Add reusable GitHub workflow for smooth single page docs onboarding
  • Add new type of documentation - single page

oto-macenauer-absa and others added 3 commits August 4, 2026 12:59
Onboarding a doc app previously required a full static-site toolchain: a
headless build, a marketplace.json manifest, and a release workflow. That is
the right cost for a real docs site and far too much for the common case of a
team with one or two markdown files, who ended up parked behind an iframe entry
or not onboarded at all.

A docs repo now onboards by adding ONE workflow file and nothing else.

actions/publish-docs/ — reusable composite action

  Takes a YAML/JSON list of doc definitions (md, title, description, slug, plus
  optional icon and tags), validates it defensively — every failure names the
  entry and the fix, and all problems are reported in one run — then renders
  each markdown file to headless HTML inside the action: GFM tables, task
  lists, autolinks and footnotes, highlight.js code fences on the slate surface
  from STYLE_GUIDE.md, and ```mermaid blocks rendered client-side.

  Mermaid is vendored into the artifact rather than pulled from a CDN, because
  marketplace pages are re-hosted inside a web fragment behind a strict CSP; if
  the script never runs the diagram source stays visible.

  Every doc in the repo is packed into ONE dist.tar.gz — a bundle.json manifest
  plus a directory per slug — and uploaded to the repo's latest release,
  replacing any existing asset.

  Dependencies are pinned inside the action dir so it stays self-contained and
  the root package.json is untouched. src/template.js is deliberately
  dependency-free: the knowledge base's fixture generator imports it, so the
  test fixture cannot drift from real published output.

Knowledge base support for type: "single-page"

  A registry entry carries no per-doc metadata at all — { repo, type, version }
  — so adding or renaming a doc never touches this repo again. The build reads
  bundle.json and expands that one entry into N apps, extracting each doc into
  apps/{slug}/. Expansion is recorded in apps/.single-page.json and spliced back
  into the registry by loadRegistry(), so Astro resolves exactly what the build
  did without mutating the committed apps.json.

  Manifest parsing, expansion and the global slug-collision guard live in
  src/utils/single-page.js, shared by the GitHub fetch path, the prebuilt/local
  path and Astro, so the three cannot drift. A doc bundle can never quietly take
  over another app's URL prefix.

  Rendering mirrors the iframe type: getAppPages() emits one route flagged
  singlePage, and the catchall wraps the body in main.mp-single-page — masthead,
  no sidebar, ~800px of centred content. Expanded docs appear as normal catalog
  cards.

Tests and docs

  A hermetic single-page bundle fixture (two docs, exercising headings, a table,
  a code block, a task list and mermaid) is generated by setup-test-apps.mjs and
  committed unpacked so it stays reviewable and CI needs no setup step. Build
  integrity covers expansion, the absent sidebar, the centred-column class,
  absolute URL rewriting and vendored mermaid; the fragment suite covers card
  listing, navigation and shadow-DOM rendering. A CI job runs the action's own
  self-test, which the Playwright suites deliberately do not depend on.

  contract/SINGLE_PAGE.md documents the bundle format and the copy-paste
  workflow; README, CLAUDE.md and HEADLESS_RULES.md describe all three
  onboarding types.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRZk4ZQsApxLANJtzQpVXb
The action only ever produces single-page bundles — bundle.json, one directory
per doc, the centred reading column. Calling it "publish-docs" implied it was
the general way to publish documentation, which would send teams with a real
docs site down the wrong path; the packaged type still goes through their own
headless build and marketplace.json.

Renamed with git mv so history follows. Every reference updated: the CI job id
and its working directories, the action's own README and package name, the
onboarding `uses:` in contract/SINGLE_PAGE.md and README.md, the setup-test-apps
import of template.js, the fixture text, and the error messages in
build-vite.js / fetch-apps.js that point onboarding repos at the action.

The onboarding repo's own workflow filename stays publish-docs.yml — that is the
consuming repo's file, not ours.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRZk4ZQsApxLANJtzQpVXb
The single-page type had no way to look at real action output in a browser —
the E2E fixture is hand-written body HTML by design, so it proves the pipeline
but not the rendering of genuinely converted markdown.

Registers the sibling knowledge-base-example-single-page repo (three markdown
docs, dist.tar.gz produced by the real action, vendored mermaid included) via
the existing `prebuilt` mechanism:

    npm run build:local && npm run preview
    http://localhost:4321/knowledge-base/

That artifact lives outside this repo, so a committed entry pointing at it would
break CI's build job, which only checks out this repo. Rather than leaving the
wiring undocumented in a README, entries may now carry `"optional": true`: the
build skips them with a warning when their prebuilt/localPath artifact is
missing. Entries without the flag still hard-fail, so a lost fixture can never
quietly produce an empty deployment — the safety that matters is kept, and only
entries that opt in are allowed to be absent.

Emitted from setup-test-apps.mjs so apps.json stays stable across test runs
(the Playwright webServer regenerates it before every build). Kept last so the
hermetic fixture remains the first single-page entry the suites inspect.

Verified: clean-tree headless build with the sibling repo absent exits 0, warns,
and emits no example pages; with it present, build:local renders all three docs
in the centred column with no sidebar, and preview serves them plus the vendored
mermaid bundle at 200.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRZk4ZQsApxLANJtzQpVXb
@oto-macenauer-absa
oto-macenauer-absa merged commit 679ec07 into master Aug 4, 2026
6 checks passed
@oto-macenauer-absa
oto-macenauer-absa deleted the feat/single-page-docs branch August 4, 2026 11:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant