Skip to content

Implement multi-version documentation support (follow-up to #320) #325

Description

@miharp

Summary

Add multi-version documentation support, following the copy-on-major-release model
agreed in #320 (versioned directories on one branch, per-product independent latest,
pinned reference generation, a version selector in the UI).

Driver: OpenVox 9 is approaching and we can't yet stand up _openvox_9x while
freezing _openvox_8x. This issue tracks the work.

Scope is split deliberately: a small must-have track to ship 9.0 docs, and a
hardening backlog of correctness/automation items that are good ideas but are
not release-blocking and should not pad the critical path. Follow-up to #320.


Must-have track (ship OpenVox 9 docs)

Three pieces. This is the whole critical path.

1. Reference generator: per-version pinning (the only real engineering)

Today output dirs and emitted URLs are fixed at load time (OUTPUT_DIR
lib/puppet_references.rb:15, puppet/type.rb:19; OpenBolt hardcodes _openbolt_5x
in openbolt/docs.rb:9), and repo.newest_release rejects prereleases (repo.rb:50).
We need to build a chosen version into a chosen collection without cross-writing.

  • Fix the OpenFact URL bug first: generators write into _openfact_* but stamp
    @latest = '/openvox/latest' (facter/core_facts.rb:12, facter/facter_cli.rb:10).
    Wrong product URL today, independent of versioning.
  • Move output paths from load-time constants to instance/config-level values so
    one process can target a specific collection (don't drive it by mutating
    ENV['COLLECTION'] in a loop — the constants freeze at require time).
  • Generator emits intra-product links/canonical: relative to its target
    collection, not hardcoded /<product>/latest/.
  • Rakefile accepts COLLECTION=<dir> + VERSION=; an explicit VERSION bypasses
    the prerelease filter (so we can build 9.x from an RC). No-arg behavior unchanged.
  • A references:all-style task builds each pinned (tag → version dir) pair;
    build.yaml calls it. latest is an alias, never its own build row — only
    real version dirs are built; nothing writes through the _latest symlink.

2. The cutover + runbook

  • Write the "add a major version" runbook: copy _openvox_8x_openvox_9x,
    repoint the _latest symlink, register the collection + defaults in
    _config.yml, add nav files (_data/nav/, nav_map.yml, navigation.yml),
    build both versions.
  • Include a rollback line (repoint symlink back, drop the new collection/nav
    entries, republish).
  • Dry-run against an OpenVox 9 prerelease tag before the real release — this is
    the gate the timeline is really about. Eyeball both trees; confirm 8.x didn't move.
    (Done in Add maintainer runbook for the major-version cutover #337: full two-phase cutover dry-run against 9.0.0-alpha1, 8.x confirmed frozen. A final dry-run at the real 9.0 tag is still worth doing at cutover time.)

3. Version selector

  • _includes/version-selector.html at the top of sidebar.html; reuse the existing
    current_nav_page_subpath / current_nav_base logic for path mapping.
  • Show only the current product's versions (from a small _data/products.yml);
    mark current; keep the reader on the same page path when switching, falling back
    to the version root when the target page doesn't exist.
    (Shipped in Add a per-product documentation version selector #335: per-product picker from products.yml, marks current, badges the
    latest-aliased version, hidden until a product has 2+ versions. It links to each
    version's root, not the same page path — path-preserving switching was
    deferred as a follow-up and is tracked in the hardening backlog below.)

_data/products.yml is introduced here as a lightweight list the selector reads (and a
handy home for the reference pins in piece 1) — not a heavyweight source-of-truth that
everything else is generated from.


Hardening backlog (not release-blocking)

Good ideas surfaced in review. Do them incrementally; none should gate 9.0. Most exist
because we're optimizing an event that happens ~once a year — a runbook checklist and a
human in the loop cover the risk for now.

  • Intra-product relative-link sweep. Convert /<product>/latest/ authored body
    links to relative so frozen docs stay internally coherent (~148 in OpenVox).
    Cheaper before the copy, but non-blocking: if left as latest, frozen 8.x links
    drift to 9.x — a real but fixable-later UX bug. Depth-aware (subdir pages need ../).
  • Cross-product link policy. Relative links can't span products. For v1, accept
    cross-product links following latest; later, optionally pin partner versions via
    a cross_product_affinity map for lockstep products (e.g. openvoxdb 8x → openvox 8x).
  • Drift validation. (Partial: rake test:products_data runs in CI, but only
    validates products.yml internally.)
    Optional Rake task asserting _config.yml / navigation.yml
    / nav_map.yml / _latest symlinks agree with products.yml, run in CI before
    jekyll build. A runbook checklist covers the once-a-year case until then. Note:
    _config.yml collections can't be generated within Jekyll (read before _data),
    so generation isn't an option for the highest-drift surface anyway.
  • Lint gate flagging new hardcoded intra-product /latest/ body links
    (pattern-based, not existence-based). Pairs with the relative-link sweep.
  • Cross-write check. When building piece 1, diff touched paths from a two-version
    build once to confirm no cross-write. Don't need a standing CI gate.
    (Done: Add maintainer runbook for the major-version cutover #337 dry run and the Add "Component versions in recent OpenVox releases" reference page #339 throwaway 9.x cutover; Add OpenVox 9.x docs collection as a preview (latest stays on 8.x) #430 in prod confirms.)
  • Frozen-version link proofing. Extend rake test:links (today checks /latest/
    only, Rakefile:76) to also proof frozen version trees.
  • Canonical on frozen builds. Decide whether frozen versions keep
    canonical: /<product>/latest/... (SEO → current) or get per-version canonicals.
    Default: keep pointing at latest.
    (Decided in Add pinned out-of-date banner and /latest/ canonicals for versioned docs #437: the numbered copy of the latest version canonicalizes to its
    /latest/ twin; frozen older versions keep the theme's self-canonical.)
  • No-redirect awareness. When latest moves 8→9, pages removed/renamed in 9.x
    404 at /<product>/latest/<page> for old bookmarks (content still at /8.x/).
    Note in the runbook; we have no redirect mechanism. (In MAINTAINING.md, Phase 2 step 5.)
  • OpenVox Containers edge case. It has a real _openvox-containers_latest dir
    (not a symlink) and no numbered collection — selector/validator logic must not
    assume the uniform model. (single_version: true in products.yml; selector, banner,
    and test:products_data all skip it.)
  • Shared/version-invariant content (link vs. copy): explicitly out of scope; revisit.
  • Path-preserving version switch. The selector (Add a per-product documentation version selector #335) links to each version's
    root; keeping the reader on the same page path across a switch (falling back to
    the root when the target page doesn't exist) needs per-navigation JS. Deferred
    follow-up from piece 3.

Part of #320.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions