Skip to content

index: generate repository pages from a template and a sidebar module #19

Closed

Description

@lakruzz

Goal

Extend the gh docs index command to generate a useful page for every matching repository, rendered from a user-authored Markdown template, and generate a Starlight sidebar module linking to those pages.

The feature is intended for a GitHub-backed Astro/Starlight documentation site, such as the astro section of mindovermachine-dev.github.io. The pages provide the actual repository index; the sidebar provides navigation to those pages.

Keep YAML, JSON, and HTML as single-file exports for users who need data or a simple index in other contexts. Change the existing .mjs/sidebar output mode: it no longer means one sidebar-only listing. It means a paired output consisting of a directory of generated pages and one .mjs sidebar file.

User experience

gh docs index --owner mindovermachine-dev \
  --format sidebar \
  --file docs/sidebar/repositories.mjs \
  --pages-dir astro/src/content/docs/repositories \
  --template astro/repository-template.md

In sidebar mode, the command:

  1. Discovers repositories using the existing owner, visibility, topic, and limit behavior.
  2. Renders one Markdown page per matching repository into the configured pages directory.
  3. Writes the .mjs sidebar file with local links to those generated pages.

The page set and sidebar must be generated from the same collected repository data in one invocation. Missing output directories are created. Report both output locations on success.

Output modes

Format Output Behavior
yaml One .yml or .yaml file Serialize the repository list as YAML; unchanged
json One .json file Serialize the repository list as JSON; unchanged
html One .html file Render the existing simple HTML index; unchanged
sidebar One .mjs file plus a pages directory Render a Markdown page per repository and a local-link Starlight sidebar module

An output path ending in .mjs continues to infer the sidebar format. --format sidebar and an .mjs --file path both select the paired behavior. YAML, JSON, and HTML modes do not generate pages or require a template.

Keep the current precedence and mutual-exclusion rules for --file and --format unless a small change is necessary to support this behavior. Do not rename unrelated flags as part of this feature.

Page and template configuration

Add settings under the existing index: configuration node and corresponding CLI flags:

  • pages-dir / --pages-dir: directory for generated Markdown pages; default docs/content/repo.
  • template / --template: path to a Markdown template; in sidebar mode, use the maintained Starlight starter template when omitted.
  • --starlight: copy the maintained Starlight starter template to a file and exit without querying GitHub. Use --template as the destination path; default to repository-template.md.

Flags override .docs.yml; .docs.yml overrides built-in defaults. Relative paths are resolved from the working directory. These settings are meaningful only in sidebar mode; reject or clearly ignore them in other formats, consistently with existing validation conventions.

Example configuration:

index:
  owner: mindovermachine-dev
  visibility: public
  topic: documentation
  format: sidebar
  file: docs/sidebar/repositories.mjs
  pages-dir: astro/src/content/docs/repositories
  template: astro/repository-template.md
  limit: 1000

The user-authored template is a normal Markdown file (for example, repository-template.md) so editors retain Markdown support. Use Go text/template with custom delimiters [% and %], and do not HTML-escape the rendered Markdown. The delimiters are intentionally gh-docs-specific, not a generic template standard: Go's default {{ }} overlaps with Liquid/Jekyll syntax and can be mistaken for template syntax in other tools. Configure them with Go's Template.Delims before parsing. Provide a yaml template function for safely serializing values in YAML front matter.

Starlight starter template

Maintain the default page template as a standalone source file at internal/index/templates/starlight.md. It must demonstrate every supported keyword below using the [% ... %] delimiters. Embed or otherwise package that file with the CLI, then have --starlight copy its contents to the requested destination; do not construct the Markdown template in Go code. This keeps the example easy to edit with Markdown tooling and makes the checked-in file the single source of truth.

Example:

gh docs index --starlight --template astro/repository-template.md

If --template is omitted with --starlight, write repository-template.md in the current directory. Do not overwrite an existing destination; return a clear error and leave it untouched. --starlight is a scaffold-only mode: it must not discover repositories or generate pages/sidebar output. The copied file can then be edited and supplied to a normal --format sidebar invocation.

Page generation

  • Generate one .md file per repository.
  • Use a stable, filesystem-safe slug based on the repository name. The owner is fixed for one invocation, so repository name is sufficient; detect collisions and fail with a useful error rather than overwriting another repository's page.
  • Write each page to <pages-dir>/<slug>.md.
  • For Starlight links, derive the local page target from the generated path relative to the configured content root. Document the default relationship between docs/content/repo and its sidebar URL; allow a base-url setting if the filesystem layout alone cannot determine the public route.
  • Sort repositories before rendering and sidebar generation so repeated runs produce stable output.
  • Overwrite generated pages on later runs. Do not delete pages for repositories that are no longer selected; document that stale files require manual cleanup.

Template contract

Render the template once per repository with a Repo value and the configured owner. Use explicit field names with Go template syntax wrapped in [% ... %]. The template must be able to include optional sections with if and iterate over topics.

Available fields:

Template expression Meaning
[% .Owner %] GitHub owner or organization
[% .Name %] Repository name
[% .Title %] Display title; defaults to repository name
[% .Description %] Repository description; empty if absent
[% .Slug %] Generated page slug
[% .RepoURL %] Canonical GitHub repository URL
[% .HomepageURL %] Configured documentation/homepage URL; empty if absent
[% .Topics %] Repository topics
[% .BadgeURL %] Configured status badge URL; empty if not configured
[% .DefaultBranch %] Default branch; empty if unavailable
[% .LatestCommit.SHA %] Short SHA of latest default-branch commit; empty if unavailable
[% .LatestCommit.Date %] Latest commit date in RFC 3339 format; empty if unavailable
[% .LatestCommit.Message %] First line of latest commit message; empty if unavailable
[% .LatestVersion %] Highest stable SemVer release/tag; empty if unavailable
[% .Stars %] Star count, when collected
[% .License %] SPDX license identifier; empty if unavailable
[% .UpdatedAt %] Repository update timestamp in RFC 3339 format, when available

Optional values must be safe to test with if; absent metadata must not make template execution fail. A parse or execution error must identify the template and repository. Unknown struct fields should produce a clear template error.

The maintained starter example is internal/index/templates/starlight.md. Keep that file in sync with the documented template contract; do not duplicate its body in Go code or maintain a second copy of the example in this spec.

Metadata collection

Keep the current gh repo list discovery and topic filtering. Extend the collected data only as needed for the template contract.

  • Fetch commit and version details only in sidebar mode, not for YAML, JSON, or HTML exports.
  • Select the highest stable SemVer tag/release by SemVer precedence, not lexical order. Ignore prereleases.
  • Missing optional metadata is not an error. A per-repository lookup failure should warn and leave that field empty while allowing other pages to be generated.
  • Failure to discover repositories, render a page, or write either required output is fatal.
  • Keep GitHub authentication delegated to the gh CLI. Do not accept, log, or persist credentials.
  • If a badge is supported, require it to be configured; do not guess a workflow filename.

Use bounded concurrency for extra GitHub API calls if needed, and retain deterministic repository ordering regardless of response order.

Sidebar module

The .mjs file must export a Starlight-compatible sidebar value with one group and one item per generated page. Use repository title/name for labels and local links to the generated pages, never external repository or homepage URLs. Use JSON-safe encoding for generated labels and links.

Keep the existing group label or make it configurable under index:. Do not rewrite site configuration. Document how the site imports the generated module and combines it with any existing sidebar groups.

Safety and output integrity

  • Sanitize slugs derived from remote repository data and verify each resolved page path stays inside pages-dir.
  • Do not execute arbitrary code from templates. text/template is limited to the data and explicitly registered template functions.
  • Validate the template and output paths before writing. Prepare all page and sidebar content before replacing output files so a rendering failure does not leave a partial generation.
  • Create directories with appropriate permissions and write files with ordinary non-executable permissions.

Documentation

Update the command help and docs/ pages to describe:

  • YAML/JSON/HTML as single-file formats and .mjs/sidebar as the paired pages-plus-sidebar mode.
  • --pages-dir, --template, their defaults, and .docs.yml settings/precedence.
  • The --starlight scaffold mode, its destination behavior, and how to use the copied file for page generation.
  • The template keywords, optional values, yaml helper, and a complete Markdown page example.
  • The custom [% ... %] template delimiters and their distinction from generic Astro/Liquid/Jekyll syntax.
  • How to import the generated sidebar module into Starlight and how page paths map to local links.
  • The fact that stale generated pages are not deleted automatically.

Tests and acceptance criteria

  • Preserve existing YAML, JSON, and HTML output behavior and golden files.
  • Test that .mjs path inference and --format sidebar both select paired output generation.
  • Test default and configured pages directories, template selection, config/flag precedence, and invalid combinations.
  • Test --starlight copies the checked-in template file, uses the default destination, refuses to overwrite, and exits without invoking gh.
  • Test custom and built-in templates with the custom delimiters, all documented keywords, optional metadata, YAML quoting, and clear template errors.
  • Test stable safe slugs, collisions, deterministic output, and path traversal rejection.
  • Test that the generated .mjs contains valid Starlight entries linking to the pages written in the same run.
  • Test SemVer selection and that one repository's metadata failure does not prevent other pages from being generated.
  • Verify YAML/JSON/HTML modes do not create pages or make extra GitHub API calls.
  • Run make test, make lint, and make build.

Activity

  1. lakruzz commented on Nov 5, 2026

    @lakruzz
    ContributorAuthor

    Follow-up requirements

    • Move Starlight starter generation from gh docs index to gh docs template --starlight.
    • Use the existing template --output option for the destination. With the default output folder docs, create docs/repository-template.md.
    • Remove --starlight and --template from index, including its config/help documentation. Sidebar indexing should use the maintained template at docs/repository-template.md.
    • Do not silently run index --format sidebar with an embedded template. If docs/repository-template.md is missing, fail with an actionable message directing the user to create it with gh docs template --starlight --output docs.
    • Keep the template as a standalone Markdown file in the repository and copy that source file when scaffolding; do not generate its contents with imperative Go code.
    • Add two prominent action links at the top of the Starlight page: one to the documentation homepage (.HomepageURL) and one to the GitHub repository (.RepoURL). Omit the documentation action when no homepage URL is available.
    • Update command help, docs, and tests for the new command ownership, default template location, missing-template behavior, and action links.

    Acceptance checks

    • gh docs template --starlight writes docs/repository-template.md by default, using the checked-in starter template.
    • A custom template --output location is honored and existing files are not overwritten without explicit force behavior.
    • gh docs index --format sidebar uses the docs-folder template and exits with a clear scaffold instruction if it is absent; it does not silently use an embedded fallback.
    • Generated pages show documentation and repository actions near the top, with the documentation action conditional on .HomepageURL.
    • Index help and config no longer advertise --starlight or --template.
    • Focused tests, lint, and build checks pass.
  2. added a commit that references this issue on Nov 5, 2026
  3. lakruzz commented on Nov 5, 2026

    @lakruzz
    ContributorAuthor

    Commit summary: 79d4b67

    • Added support for docs/repository-template.mdx alongside docs/repository-template.md for sidebar page generation.
    • When both templates exist, the .md template is selected first.
    • Generated repository pages now use the same extension as the selected template (.md or .mdx).
    • Updated index help, docs, and tests to cover the supported extensions and generated filenames.
  4. github-actions commented on Nov 5, 2026

    @github-actions
    Contributor

    Closed by Ready action after PR #20 was delivered to 'main'.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Fields

Priority

None yet

Effort

None yet

Progress

None yet

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions