Back to Skiasharp

SkiaSharp documentation — how the pieces fit together

documentation/dev/docs-overview.md

4.150.010.2 KB
Original Source

SkiaSharp documentation — how the pieces fit together

This is the map of SkiaSharp's documentation system: what gets generated, by which engine, driven by which skill, on which workflow, and in which repository. Start here for the big picture, then jump to a deep dive.

Start here — what are you trying to do?

If you want to…Go to
Understand the whole system at a glanceThis page (the map) — read on
Generate or edit docs by hand — prerequisites, local commands, editing API docs, the Cake target referencewriting-docs.md
Change how release notes or API diffs behave — the versioning model, versions.json, file layout, per-engine rulesrelease-notes-and-api-diffs.md (the behavior spec; read before changing either generator)
Work on the docs website — build, preview, themingsite.md

Contents


The four kinds of documentation

SkiaSharp ships four distinct documentation artifacts. They are produced by different tools and live in different places, which is the main reason the system can feel sprawling — this table is the whole story on one screen:

#ArtifactWhat it isSource of truthEngine
1Conceptual docsHand-written guides & tutorials on the docs sitedocumentation/conceptual/ (this repo)docfx (site.md)
2API reference docsPer-type/per-member XML reference (→ learn.microsoft.com)docs/SkiaSharpAPI/ (the docs submodule)docs.cake (mdoc)
3Release notesHuman "what's new" pages, AI-polisheddocumentation/docfx/releases/ (this repo)generate-release-notes.py
4API diffsMachine-generated public-API diffs, no AIdocumentation/docfx/releases/ (this repo)api-diff.cake

Artifacts 3 and 4 share one versioning model and one config file (versions.json); that shared model is the subject of the behavior spec. Artifact 2 is a separate concern (mdoc), but it shares the same NuGet-diff plumbing (api-diff-tools.cake) and lives next to the others.


Two repositories

mermaid
flowchart LR
  subgraph A["mono/SkiaSharp (this repo)"]
    conceptual["documentation/conceptual/ — concept docs"]
    releases["documentation/docfx/releases/ — release notes + API diffs"]
    engines["scripts/infra/docs/ — the engines"]
    submodule["docs/ (git submodule)"]
  end
  subgraph B["mono/SkiaSharp-API-docs (the docs repo)"]
    xml["SkiaSharpAPI/ — mdoc XML reference docs"]
  end
  submodule -. "points at" .-> B
  engines --> releases
  engines -. "mdoc writes into" .-> xml
  • mono/SkiaSharp (this repo) holds the conceptual docs, the release notes/api diffs tree, and all the generation engines (scripts/infra/docs/).
  • mono/SkiaSharp-API-docs (the docs repo) holds the large mdoc XML reference docs and their images/examples. It is pulled into this repo as a git submodule at docs/, so the XML lives at docs/SkiaSharpAPI/. The engines in this repo run mdoc into that submodule.

Because the engines live here but the XML output lives there, the API-reference path is inherently cross-repo — see the automation section below.


The engines (scripts/infra/docs/)

All generation code lives in one directory so local runs, CI, and the local Docker image all share exactly one copy and nothing can drift:

FileRole
docs.cakemdoc-based XML reference-doc generator (artifact 2)
api-diff.cakeAPI-diff engine (artifact 4)
generate-release-notes.pyrelease-notes engine (artifact 3)
api-diff-tools.cakeshared NuGet-diff comparer + layout helpers, #loaded by both api-diff.cake and docs.cake
versions.jsonthe only override surface — supersession + comparison baselines, honored identically by the Cake and Python engines
pr-authors.jsonPR-author cache for the release-notes engine
generate-api-diffs.shPath 1 runner → cake docs-api-diff-past
generate-release-notes.shPath 2 runner → python generate-release-notes.py
generate-api-docs.shPath 3 runner → cake update-docs (mdoc under mono)
docker/the local reproducibility image + run.sh wrapper

The three generate-*.sh scripts are the single source of truth for each path: a developer, the CI workflows, and the Docker wrapper all invoke the same script, so a command can never drift between local and CI. The shared, general-purpose Cake machinery (shared.cake, download.cake) stays under scripts/infra/shared/.

The three generation paths

PathProducesEntry scriptNeeds
1 — API diffsartifact 4generate-api-diffs.shdotnet (+ NuGet feed)
2 — Release notesartifact 3generate-release-notes.shdotnet, python3, git history, gh (PR authors)
3 — API reference (mdoc)artifact 2generate-api-docs.shdotnet, mono (to run mdoc.exe)

mdoc.exe is a .NET Framework executable. docs.cake invokes it under mono when not on Windows, so Path 3 runs on any host with mono-complete installed — a Linux CI runner or the local Docker image, not only Windows.


The skills

Three Copilot skills drive or assist these paths (.agents/skills/):

SkillOwnsPhase it performs
release-notesartifacts 3 + 4Runs Prepare (the deterministic engines, via scripts/generate.sh) then Polish (AI rewrites only the prose of the listed release-notes pages). See spec §2.2/§4.4.
api-docsartifact 2Fills the "To be added." placeholders mdoc leaves in the XML with real prose, following the .NET doc guidelines.
skia-analystanalysisDiffs versions / surfaces what changed and what's missing; used for release announcements and gap analysis, not for writing the committed artifacts.

The release-notes skill stays deliberately thin: its scripts/generate.sh owns no commands of its own — it just calls generate-api-diffs.sh then generate-release-notes.sh in order. All real logic lives in the engines under scripts/infra/docs/.


CI automation (cross-repo)

mermaid
flowchart TB
  subgraph this["mono/SkiaSharp"]
    urn["update-release-notes\n(ubuntu-latest)"]
    sync["auto-docs-submodule-sync\n(ubuntu-latest)"]
  end
  subgraph docsrepo["mono/SkiaSharp-API-docs"]
    writer["auto-api-docs-writer\n(Linux + mono)"]
  end
  urn -->|"Prepare engines 3+4,\nthen AI Polish → one PR"| prRel["PR: release notes + api diffs\n(this repo)"]
  writer -->|"mdoc stubs +\nAI placeholder fill → PR"| prDocs["PR: XML docs\n(docs repo)"]
  prDocs -->|"merged"| sync
  sync -->|"bump docs/ submodule → PR"| prBump["PR: update submodule\n(this repo)"]
WorkflowRepoRunnerProducesPaths
update-release-notesthis repoubuntu-latest (native dotnet + python)one PR with release notes and API diffs1 + 2
auto-api-docs-writerdocs repoLinux + monoPR with regenerated + AI-filled XML docs3
auto-docs-submodule-syncthis repoubuntu-latestPR bumping the docs/ submodule pointer

Key points:

PointWhat it means
Paths 1 + 2 = one pipeline, one PRupdate-release-notes runs the deterministic Prepare phase (Cake API diffs + Python notes) in its own job, hands off to the AI Polish phase via an artifact, and opens a single rolling PR with both. Triggers: pushes to main and a daily cron — main walks every release/* ref and reads v* tags itself, so release branches/tags need no trigger of their own. If nothing changed, no PR is opened (spec §2.3).
Path 3 is cross-repoThe engines live in this repo but the XML lives in the docs repo, so auto-api-docs-writer lives in the docs repo, checks SkiaSharp out to borrow the engines, regenerates the stubs, AI-fills placeholders, and opens its PR there.
auto-docs-submodule-sync closes the loopOnce the docs-repo PR merges, it bumps this repo's docs/ submodule pointer so the new XML is picked up here.

Local generation & Docker

Everything CI does can be reproduced locally by calling the same generate-*.sh scripts. The only host requirement beyond the .NET SDK is mono for Path 3, and gh for Path 2's PR-author lookup.

ModeWhat it isWhen to use
NativeInstall the deps yourself and call the generate-*.sh scripts (or the underlying Cake targets in writing-docs.md).When you've installed the deps yourself.
Docker (scripts/infra/docs/docker/run.sh)A local-only convenience image that pre-installs dotnet + mono + python + gh and runs the same scripts against a bind-mounted checkout, so a local run reproduces what CI produces.A one-command reproducible run without installing the deps yourself.

Docker is for local reproducibility only — CI does not use it. The CI runners install the same dependencies natively on Linux and call the same scripts. The image simply mirrors that dependency surface so the two stay identical.


Where to go next