- Home
- Docs Control
- Onboarding
Onboarding
This page covers the procedure to enroll a new repository in the docs-control governance system and optionally migrate existing documentation to Astro Starlight.
Prerequisites
Section titled “Prerequisites”- Org membership in
f5xc-salesdemos REPO_SETTINGS_TOKENandREPO_SYNC_TOKENsecrets configured as organization-level secrets (or added to the new repository individually)- Access to the
ghcr.io/f5xc-salesdemos/docs-buildercontainer image
Enrollment steps
Section titled “Enrollment steps”1. Add to downstream-repos.json
Section titled “1. Add to downstream-repos.json”Add the repository’s owner/repo string to .github/config/downstream-repos.json in docs-control. This registers it for dispatch and enforcement.
2. Optionally add to docs-sites.json
Section titled “2. Optionally add to docs-sites.json”If the repository will publish documentation, add an entry to .github/config/docs-sites.json with:
label— human-readable site nameurl— URL to the site’sllms-full.txtendpointdescription— short description used in the generated README
If no entry is added, the README generator falls back to a capitalized repository name and the GitHub API description.
3. Consolidate .gitignore
Section titled “3. Consolidate .gitignore”The enforcement workflow overwrites the downstream .gitignore entirely. Before onboarding, merge any repository-specific entries into the template .gitignore in docs-control so they are not lost.
# Fetch the downstream .gitignoregh api repos/f5xc-salesdemos/<repo-name>/contents/.gitignore \ --jq '.content' | base64 -d > /tmp/downstream-gitignore
# Compare with the templatediff /tmp/downstream-gitignore .gitignoreAdd any missing entries to the docs-control .gitignore and commit the change before proceeding. Extra ignore patterns for file types that do not exist in a repository are harmless.
4. Install caller workflows
Section titled “4. Install caller workflows”Copy the five caller workflow templates from workflows/ in docs-control into the new repository’s .github/workflows/ directory:
enforce-repo-settings.yml— triggers enforcement and file syncgithub-pages-deploy.yml— triggers docs build and deployrequire-linked-issue.yml— enforces PR-to-issue linkingsuper-linter.yml— runs the super-linter suite on PRsdependabot-auto-merge.yml— auto-merges green dependabot PRs
These files are also synced automatically by the file sync workflow, but installing them manually bootstraps the process.
5. Trigger enforcement
Section titled “5. Trigger enforcement”Run the enforcement workflow manually in the new repository:
gh workflow run enforce-repo-settings.yml --repo f5xc-salesdemos/<repo-name>This applies all repository settings, creates any missing governance files, and opens a sync PR if needed.
6. Verify settings
Section titled “6. Verify settings”Confirm that enforcement succeeded:
gh run list --repo f5xc-salesdemos/<repo-name> --workflow enforce-repo-settings.yml --limit 1Check that branch protection, Actions permissions, and Pages are configured correctly:
gh api repos/f5xc-salesdemos/<repo-name>/branches/main/protection \ --jq '.required_status_checks.contexts'7. Verify docs site
Section titled “7. Verify docs site”If the repository has a docs/ directory, confirm the docs site is accessible after the first successful deploy:
curl -sf "https://f5xc-salesdemos.github.io/<repo-name>/" \ && echo "OK" || echo "FAIL"Fork-fidelity: opting out of managed files
Section titled “Fork-fidelity: opting out of managed files”Most governed repositories take every managed file as-is, but some
repositories are active forks whose coding style (indentation, line
width), license, or CONTRIBUTING content is inherited from an upstream
project. Overwriting those files would create constant merge conflicts
against the fork’s source. Use the skip_files mechanism on
.github/config/repo-settings.json under managed_files to exempt
the repository from sync on specific file paths:
"managed_files": { "source_repo": "f5xc-salesdemos/docs-control", "skip_files": { "xcsh": [ "biome.json", "LICENSE", "CONTRIBUTING.md", "ruff.toml", ".ruff.toml", ".python-lint", ".mypy.ini" ] }, "files": [ ... ]}The same skip_files map must be mirrored into .claude/governance.json
so the protect-managed-files.sh hook allows edits on opted-out paths
in the downstream repository (otherwise Claude-driven edits to files
the repo owns would still be blocked). The test suite at
tests/test-linter-configs.sh (Section 7) asserts every skip_files
entry references a real baseline managed_files entry.
An opted-out repository typically also ships its own version of
each opted-out config (e.g., xcsh ships a permissive ruff.toml and
.python-lint) so the relevant linter still has something to read.
Deferring required status checks: excluded_required_contexts
Section titled “Deferring required status checks: excluded_required_contexts”When onboarding an existing codebase that is not yet compatible with the
ecosystem’s lint rules, immediately marking Lint Code Base (or any other
reusable-workflow status check) as a required context will block every
PR until the codebase is cleaned up. Use
repo_overrides.<repo>.excluded_required_contexts in
repo-settings.json to defer specific contexts:
"repo_overrides": { "xcsh": { "additional_contexts": ["typecheck", "unit (linux)"], "excluded_required_contexts": ["Lint Code Base"] }}enforce-repo-settings.yml subtracts excluded_required_contexts from
the base required_status_checks.contexts list at enforcement time, so
the check still runs on PRs (the workflow is installed) but does not
gate merges. Once the codebase is compatible — typically after a
linter-compatibility audit — remove the entry and the next enforcement
run promotes the check to required.
Setting up docs content
Section titled “Setting up docs content”Every repository that publishes documentation needs a docs/ directory with at least an index.mdx file containing YAML frontmatter.
Landing page (docs/index.mdx):
---title: Site Titledescription: Site descriptiontemplate: splashsidebar: hidden: truehero: tagline: Your tagline actions: - text: Get Started link: getting-started/ icon: right-arrow variant: primary---Content pages (docs/01-page-name.mdx):
---title: Page Titledescription: One-line description of the page content---
Content here in standard Markdown.Sidebar order is controlled by numeric prefixes in filenames (e.g., 01-getting-started.mdx) or by adding sidebar: \{ order: N \} to the frontmatter. See the content authoring guide for full MDX conventions.
Migrating from MkDocs
Section titled “Migrating from MkDocs”If the repository currently uses MkDocs Material, follow these additional steps to convert the documentation to Starlight MDX format.
Rename files
Section titled “Rename files”Rename all .md files under docs/ to .mdx:
find docs/ -name '*.md' -exec bash -c 'mv "$0" "${0%.md}.mdx"' \{} \;Add Starlight frontmatter
Section titled “Add Starlight frontmatter”Every .mdx file needs YAML frontmatter with at least a title field. Remove any MkDocs-specific frontmatter keys (hide:, icon:, status:).
Convert admonitions
Section titled “Convert admonitions”MkDocs Material uses !!! type "Title" with indented content. Starlight uses fenced directives:
| MkDocs syntax | Starlight syntax |
|---|---|
!!! note "Title" | :::note[Title] … ::: |
!!! tip "Title" | :::tip[Title] … ::: |
!!! warning "Title" | :::caution[Title] … ::: |
!!! danger "Title" | :::danger[Title] … ::: |
??? info "Title" (collapsible) | <details><summary>Title</summary> … </details> |
Type mapping:
| MkDocs type | Starlight directive |
|---|---|
note, info, abstract, summary, tldr, quote, cite, question, help, faq | :::note |
tip, hint, success, check, example | :::tip |
warning, attention | :::caution |
danger, error, fail, bug | :::danger |
Remove the 4-space content indentation that MkDocs requires — Starlight directives do not use it.
Replace Material icons
Section titled “Replace Material icons”MkDocs Material uses :material-icon-name: syntax. Replace with emoji or plain text:
| Material icon | Replacement |
|---|---|
:material-check: | ✓ |
:material-close: | ✗ |
:material-arrow-right: | → |
:material-arrow-left: | ← |
:material-information: | ℹ |
:material-alert: | ⚠ |
:material-cog:, :material-wrench: | ⚙ |
For any icon not listed, use descriptive text.
Delete legacy MkDocs files
Section titled “Delete legacy MkDocs files”Remove previous docs tooling that is no longer needed:
mkdocs.ymlrequirements-docs.txtdocs/overrides/docs/stylesheets/docs/javascripts/docs/includes/
Validate the conversion
Section titled “Validate the conversion”# Find remaining Material icon referencesgrep -r ':material-' docs/ --include='*.mdx'
# Find remaining MkDocs admonitionsgrep -r '^!!!' docs/ --include='*.mdx'grep -r '^???' docs/ --include='*.mdx'
# Find files missing frontmatterfor file in $(find docs/ -name '*.mdx'); do if ! head -1 "$file" | grep -q '^---'; then echo "Missing frontmatter: $file" fidoneCheck for common MDX issues:
- Bare
<is treated as a JSX tag — use&lt;or wrap in backtick inline code \{and\}are JSX expressions — escape them or wrap in backtick inline code- Self-close HTML void tags:
<br />,<img /> - Use
classNameinstead ofclassin HTML elements
Dynamic doc generation
Section titled “Dynamic doc generation”For repositories where docs are generated from pipeline artifacts (e.g., validation reports), add a workflow job that generates MDX files and opens a PR. The commit to docs/ on main then triggers the standard github-pages-deploy.yml workflow.
update-docs: name: Update Documentation needs: [validate] runs-on: ubuntu-latest permissions: contents: write pull-requests: write steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: name: validation-reports path: reports/ - run: python -m scripts.generate_docs --output docs/01-validation-report.mdx - name: Commit and PR if changed env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add docs/ if git diff --cached --quiet; then echo "No docs changes" exit 0 fi BRANCH="docs/update-validation-report-$(date +%s)" git checkout -b "$BRANCH" git commit -m "docs: update validation report" git push origin "$BRANCH" gh pr create --title "docs: update validation report" \ --body "Auto-generated from validation pipeline run."Post-onboarding cleanup
Section titled “Post-onboarding cleanup”Delete the gh-pages branch
Section titled “Delete the gh-pages branch”If the repository previously deployed docs via a gh-pages branch, it is no longer needed since Pages now uses build_type: workflow. Delete it after verifying the new pipeline works:
gh api repos/f5xc-salesdemos/<repo-name>/git/refs/heads/gh-pages --method DELETEClean stale branches
Section titled “Clean stale branches”Remove merged feature branches and old automation branches:
# Delete local branches already merged to maingit branch --merged main | grep -v '^\*\|main' | xargs -r git branch -d
# Prune remote tracking referencesgit fetch --pruneTroubleshooting
Section titled “Troubleshooting”Chicken-and-egg problem
Section titled “Chicken-and-egg problem”The enforcement workflow requires the caller workflows to be installed, but the file sync that installs them needs the enforcement workflow to run. Break the cycle by manually installing the three caller workflows first (Step 4), then triggering enforcement (Step 5).
Pages not building
Section titled “Pages not building”Ensure:
- The repository has a
docs/directory with at least anindex.mdxfile - GitHub Pages is enabled with
build_type: workflow(enforcement handles this) - The
GITHUB_TOKENhaspages: writeandid-token: writepermissions
Verify Pages config:
gh api repos/f5xc-salesdemos/<repo-name>/pages --jq '.build_type'Starlight frontmatter titles containing colons
Section titled “Starlight frontmatter titles containing colons”A YAML bare scalar like title: Porting From pi-mono: A Practical Guide
parses as an error because the second : is read as a mapping
separator. Wrap any title containing a : in double quotes:
---title: "Porting From pi-mono: A Practical Guide"sidebar: order: 9 label: Porting from pi-mono---Plain titles with no : are fine unquoted. The symptom when this is
missed is a Starlight frontmatter warning at build time and the
affected page shipping without its sidebar entry.
Linter-compatibility audit cadence
Section titled “Linter-compatibility audit cadence”When a new repository produces a non-trivial volume of super-linter
findings, work the linters in severity order
(zizmor → shellcheck → yamllint → Python trio → codespell →
textlint → markdownlint → jscpd) and hold one case-by-case
decision per rule family:
- If the violation reflects a legitimate convention of the downstream
repo (tech-prose terminology, fork-preserved style, valid technical
abbreviation), relax the global config and land the relaxation
with a corresponding assertion in
tests/test-linter-configs.sh. - If the violation is a genuine bug (typo, dead variable, missing YAML document-start, escaped pipe in a markdown table cell), fix it in the downstream repo.
Write the assertion first (must go red against the current config),
then apply the config change until green. Never ship a global
relaxation without the paired assertion — future edits will drift
the config back to a state that re-introduces the noise. The 79
assertions currently in test-linter-configs.sh are the baseline
regression net.
Stale sync branches
Section titled “Stale sync branches”If a governance/sync-managed-files branch exists from a previous run, the file sync workflow will attempt to update it. If it’s stale, delete it manually:
gh api repos/f5xc-salesdemos/<repo-name>/git/refs/heads/governance/sync-managed-files \ --method DELETERelease automation: release PR pattern
Section titled “Release automation: release PR pattern”Governed repos that auto-publish releases (npm, Homebrew, GitHub Releases)
must not push directly to main. With enforce_admins: true plus required
status checks, any direct push from a CI token is rejected by GitHub with
GH006: Protected branch update failed. The governance-compatible flow —
the release PR pattern — is two decoupled workflows:
-
Version-bump PR. On
fix:/feat:merges tomain, a job runs the repo’s release script, which creates arelease/v<X.Y.Z>branch, commitschore: bump version to v<X.Y.Z>, pushes the branch, opens a PR, and enables auto-merge. The PR name falls under therelease/**branch exemption already listed in.github/workflows/require-linked-issue.ymlexclude-branches, so no linked-issue friction. -
Tag on merge. A separate workflow fires on
mainpushes whose head commit starts withchore: bump version to v, extracts the version, and pushes the git tag. Tag pushes are not subject to branch protection, so this step always succeeds.
The downstream release chain (build, sign, publish) remains triggered by
the tag push — no changes needed there. See xcsh’s
scripts/release.ts cmdAutoRelease and
.github/workflows/tag-on-version-bump.yml for the reference
implementation.
Further reading
Section titled “Further reading”- Content authoring guide from docs-builder for page content and MDX conventions