This file provides guidance for working with the Union.ai documentation repositories. It is shared between unionai-docs (parent) and unionai-docs-infra (submodule).
For selfhosted or selfmanaged context, refer to the Notion guide: https://www.notion.so/3108cc06513d81a08bb1d3b9135385f1
Multi-variant Hugo documentation site for Flyte (open-source) and Union.ai products. A single source generates two variants:
- flyte — Open-source Flyte orchestration platform
- union — Union.ai commercial product (covers both BYOC and Self-managed deployments)
# Development (requires hugo.local.toml setup first)
cp hugo.local.toml~sample hugo.local.toml # First time only
make dev # Start dev server at localhost:1313
# Production build
make dist # Build all variants to dist/
make serve PORT=4444 # Serve dist/ locally
# Examples submodule
make init-examples # Initialize unionai-examples
make update-examples # Update to latest
# API documentation regeneration
make -f unionai-docs-infra/Makefile.api.sdk # SDK API + CLI docs
make -f unionai-docs-infra/Makefile.api.plugins # Plugin API docs
# Validation
make check-images # Validate image references
make check-jupyter # Validate Jupyter notebooks
make validate-urls # Check for broken URLsThe repo separates version-specific content/config (top level) from shared build infrastructure (unionai-docs-infra/):
Top level — files that differ between main (v2) and v1 branches:
makefile.inc— VERSION, VARIANTSapi-packages.toml— API package registrycontent/,data/,linkmap/,include/— Content and generated data
unionai-docs-infra/ — shared build infrastructure (identical across branches).
Policy (DOC-1329): content is versioned; chrome is promoted. Cut tags snapshot content only;
builds wrap every version tree (latest, stable, all pins) in the infra named by the branch tip's
submodule pointer. Infra/theme changes ship via a pointer bump — never a cut; the pin inside a tag
is provenance only. Details: unionai-docs-infra/VERSIONING.md.
Makefile— Real build logic (top-level Makefile forwards to this)hugo.toml,hugo.site.toml,hugo.ver.toml,config.{variant}.toml— Hugo configstatic/— Shared static assets (CSS, JS, images)scripts/— Build shell scriptstools/— Python build toolslayouts/— Hugo templates, partials, shortcodesthemes/— Hugo themeredirects.csv— Redirect data
Configs merge in order:
unionai-docs-infra/hugo.toml— Core settings (directory remapping for layouts, etc.)hugo.site.toml— Site-wide settings (version-specific)unionai-docs-infra/hugo.ver.toml— Version definitionsunionai-docs-infra/config.{variant}.toml— Variant-specific settingshugo.local.toml— Local dev overrides (not committed)
Every page MUST declare which variants it appears in via frontmatter:
---
title: My Page
weight: 3
variants: +flyte +union
---+includes,-excludes- All variants must be explicitly listed (no defaults)
{{< variant union >}}
{{< markdown >}}
This appears only in the Union variant.
{{< /markdown >}}
{{< /variant >}}Hugo quirk: Inside container shortcodes, wrap Markdown content with {{< markdown >}}.
For inline text that varies by variant:
The {{< key product_name >}} platform...Keys defined in hugo.site.toml under [params.key]. Common keys: product_name, product_full_name, cli, kit_name, kit_remote, docs_home.
{{< variant ... >}}— Variant-conditional content{{< key ... >}}— Product name replacements{{< docs_home {variant} >}}— Doc root links (required for cross-doc links){{< tabs >}}/{{< tab >}}— Tabbed content{{< code file="..." fragment=name lang=python >}}— Code inclusion from external files{{< link-card >}}— Clickable cards{{< py_class_ref class.name >}}— Python API refs
Fragments in source files:
# {{docs-fragment name}}
code here
# {{/docs-fragment}}Examples at: http://localhost:1313/__docs_builder__/shortcodes/ (dev mode only)
---
title: Page Title
weight: 3 # Lower weight = higher in nav
variants: +flyte ... # Variant visibility (all must be listed)
top_menu: true # Makes this a top tab
sidebar_expanded: true # Expands section by default
toc_max: 3 # Max heading level in TOC
mermaid: true # Enable Mermaid diagrams
---Navigation: lower weight = higher position. weight: 0 or missing = alphabetical at end.
Inline `code` and Python code blocks are automatically linked to their API reference at runtime by inline-code-linker.js and codeblock-linker.js (loaded for every page; data comes from linkmap/*-linkmap.json).
Do not write explicit Markdown links for identifiers the autolinker already handles. Write the bare backticked identifier and let the linker wrap it.
✅ A `flyte.io.File` is a reference to an offloaded file.
✅ Call `flyte.init()` before submitting a run.
❌ A [`flyte.io.File`](../../api-reference/flyte-sdk/packages/flyte.io/file) is a reference …
❌ Call [`flyte.init()`](../../api-reference/flyte-sdk/packages/flyte/_index#init) …What the linker matches (in inline code, exact <code> text):
- Fully-qualified identifiers from any loaded linkmap:
flyte.io.File,flyte.report.log(),flyte.errors.OOMError,flyteplugins.bigquery.BigQueryConfig, … - A trailing
()is stripped before lookup, so`flyte.init()`and`flyte.init`both link. - A leading
@is stripped (decorator form). ClassName.methodfalls back to<class-url>#methodwhen the class is in the linkmap.
What it does not match — keep an explicit link in these cases:
- Link text that isn't a pure single backticked identifier (e.g.
[`Resources` API reference](…),[`Trigger` and `Cron`](…)). - Bare short names (e.g.
`Trigger`,`Resources`) — the SDK linkmap only emits fully-qualified keys. Prefer`flyte.Trigger`. (Plugin linkmaps do emit both forms.) - Links to specific anchors that aren't
#methodname(e.g. arbitrary#section-ids). - Cross-page links (
./other-page) and non-API-ref URLs.
To check whether an identifier is autolinkable, grep linkmap/*.json for it. If it's there, drop the explicit [...](…) wrapper.
> [!NOTE] Title
> Content here
> [!WARNING] Title
> Warning content---
layout: py_example
example_file: /path/to/file.py
run_command: union run --remote path/to/file.py main
source_location: https://github.com/unionai/unionai-examples/tree/main/path
------
jupyter_notebook: /path/to/notebook.ipynb
---- Install Hugo (extended) at the pinned version in
unionai-docs-infra/.hugoversion(currently 0.161.1):brew install hugo - Copy config:
cp hugo.local.toml~sample hugo.local.toml - Run:
make dev
Dev settings in hugo.local.toml:
variant = "union" # Active variant
show_inactive = true # Show other variants grayed out
highlight_active = true # Highlight active variant content
highlight_keys = true # Show key replacements- Pre-build checks block absolute URLs to union.ai/docs — use
{{< docs_home {variant} >}}instead - Hugo version must be >= the pin in
unionai-docs-infra/.hugoversion(currently 0.161.1). The floor equals the pin so local dev and CI build with the same Hugo;pre-flight.shfails below it and warns above it (brew tracks latest, so running ahead of CI is the common skew and the one a floor cannot catch) - Python >= 3.10 required for the build tools (
requires-pythoninunionai-docs-infra/pyproject.toml); CI runs 3.12
Generated from Python packages using tools/api_generator:
- Build with
make -f unionai-docs-infra/Makefile.api.sdkorMakefile.api.plugins - Respects
__all__in packages - Ignores
_prefixed items and imports (unless in__all__)
Managed in unionai-docs-infra/redirects.csv and deployed automatically by the
deploy-redirects.yml workflow; nobody applies them by hand. Retired version pins need no row
(their redirects are derived from versions.toml), and the CSV cannot express patterns, since it
becomes a Cloudflare Bulk Redirect List. Pattern redirects are dynamic redirect rules in the
Cloudflare dashboard. See unionai-docs-infra/ROUTING-ARCHITECTURE.md.
For each variant the build generates a clean Markdown twin of every page at <path>.md
(served at the page's own URL with .md appended), plus llms.txt (the page index) and
llms-full.txt (the whole variant in one file). A section landing page's twin ends with a
## Subpages list, so it is the index for its section, and every twin opens with an identity
block naming the product and version line.
One shape: <path>.md. The older names page.md, section.md and _section.md are retired
and no longer generated; Cloudflare 301s them to the page twin. Do not describe them as current.
Readers and agents also reach a twin by sending Accept: text/markdown to the ordinary page URL.
Details: unionai-docs-infra/README.md and the LLM-optimized documentation page in the docs.