Skip to content

De-version PPE latest docs to bare product path - #1506

Open
DanPiazza-Netwrix wants to merge 7 commits into
devfrom
DanPiazza-Netwrix/ppe-deversion-12-0
Open

De-version PPE latest docs to bare product path#1506
DanPiazza-Netwrix wants to merge 7 commits into
devfrom
DanPiazza-Netwrix/ppe-deversion-12-0

Conversation

@DanPiazza-Netwrix

Copy link
Copy Markdown
Contributor

Summary

  • De-versions Password Policy Enforcer's latest (12.0) docs so they live at the bare product path instead of a versioned URL
  • Adds redirects mapping old versioned PPE URLs to their unversioned equivalents, including folder-index pages
  • Fixes folder-index redirects not matching no-trailing-slash URLs
  • Only loads Google Analytics in production builds

Test plan

  • Run DOCS_PRODUCT=passwordpolicyenforcer npm run build and confirm no broken links
  • Spot-check old versioned PPE URLs redirect correctly to the bare product path
  • Confirm PPE 12.0 docs render correctly at the de-versioned path
  • Confirm GA doesn't load in local dev

12.0 now serves at /docs/passwordpolicyenforcer instead of
/docs/passwordpolicyenforcer/12_0. Older versions (11.2, 11.1, 11.0, 10.2)
are removed from the active build but left untouched on disk. Old versioned
URLs redirect to the unversioned docs root: known version roots get a real
static redirect, and any other stale deep link falls back to a client-side
redirect via a new opt-in redirectStaleVersionsToRoot mechanism that leaves
other de-versioned products' path-preserving redirects unchanged.
12.0, 11.2, 11.1, and 11.0 share the same section layout, so old links
into them now resolve to the exact same page under the new unversioned
root (derived by walking the 12.0 doc tree and checking each file also
exists in the older version, rather than hand-listing every page).

10.2's layout was reorganized for 11.x/12.0, so its old pages use an
explicit, content-verified old-path -> new-path map instead of a blind
path rewrite. Anything with no clear one-to-one match on either version
still falls back to the docs root via the existing client-side redirect.
A folder-index route (e.g. admin/cmdlets, whose file shares its parent
folder's name) was only registered as a redirect with a trailing slash,
so the plain /admin/cmdlets URL a reader actually types missed it and
fell through to the generic root fallback instead.
npm run start runs in development mode, where the gtag script often
can't load (network, ad blockers), leaving window.gtag undefined and
throwing a runtime error overlay on every route change. Production
builds are unaffected.
Read each previously-unmapped 10.2 page's title/content and matched it
to whichever 12.0 page now covers that topic: mailer/license settings
consolidated into Settings, uninstall steps into the server installer,
Support Tools into System Audit and Support, first/last-character and
username-similarity rules into Character rules and Similarity, and
password client template/insert/multilingual customization into its
configuration page.

Only administration/password_reset.md is still left unmapped — it
covered integration with the separate Password Reset product and has
no PPE-side equivalent in 12.0.
It was an intro/comparison blurb for the separate Netwrix Password
Reset product, not integration documentation — that same PPE Web vs.
Password Reset comparison now lives in the Web overview page, so
redirect there instead of falling back to the docs root.
Docusaurus serves a doc whose filename matches its parent folder at the
folder URL, dropping the repeated segment. Four TEN_TWO_PAGE_MAP keys
still had the repeated segment, so they never matched the URLs 10.2
actually served (e.g. /administration/rules/rules instead of
/administration/rules).

Generated with AI

Co-Authored-By: Claude Code <ai@netwrix.com>
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Code Review

Reviewed for correctness only (no doc content/style). No security issues, no workflow/permission/secret changes, and the Docusaurus wiring is sound: excluding PPE from the base-path redirect list avoids the self-redirect no-op, getLatestVersionUrlMap() correctly skips a product with no version segment to alias, and the to targets in the new 10.2 map all resolve to real 12.0 pages (I checked all 73 keys against the files on disk — the 10.2 map is complete, and the repeated-segment keys like administration/rules and administration/installation are right).

Findings below, most significant first.

1. 11.1 and 11.0 are not structurally identical to 12.0, so ~9 pages each get no redirect

src/config/redirects/passwordpolicyenforcer-deversion.js:110 lists 11.1 and 11.0 in STRUCTURALLY_IDENTICAL_VERSIONS. That holds for 11.2 (its file tree matches 12.0 exactly), but 11.1 and 11.0 still use the pre-reorg layout for a chunk of pages:

  • installation/hibpupdater (12.0: admin/hibpupdater)
  • installation/writeback (12.0: admin/writeback)
  • installation/installationweb (12.0: web-overview/installationweb)
  • installation/uninstall, installation/installationmailer, installation/installationconfigconsole
  • admin/command_line_interface, admin/windowseventviewer
  • gettingstarted

docExistsInVersion() filters all of these out, so they fall through to the client-side root bounce — which, per the comment in src/utils/versionlessRedirect.js:29-33, still answers HTTP 404 and passes no link equity. The 10.2 hand map already contains equivalent targets for most of them (administration/mailer -> admin/settings, administration/uninstall -> installation/installationserver, administration/support_tools -> admin/systemaudit), so a small per-version override map layered on top of the exact-match pass would close the gap cheaply.

2. PPE pages now emit an empty product-version meta tag, breaking the search version facet

src/components/ProductMetaTags/index.js:33-56 derives the version from the URL segment after the product path, with a fallback that only fires for products whose single version is literally current. PPE is now a single-version product whose version is 12.0 and whose URLs have no version segment, so every PPE page renders <meta name="product-version" content="">. That tag is the only source of the product_version attribute the search code relies on:

  • src/theme/SearchBar/index.js:112 builds product_version:12.0 filters from getVersionsForProducts(), which still returns 12.0 for PPE — so filtering search by version now matches zero PPE records.
  • dedupeToLatestVersion() in src/theme/searchUtils.js:66 ranks an unknown/empty version as Infinity, so freshly indexed 12.0 hits can no longer outrank leftover 11.x/10.2 index entries for the same article.

Extending the fallback to cover "single-version product whose route base path equals the product path" (the same signal already encoded as redirectStaleVersionsToRoot / customRoutePath === product.path) would fix it.

3. Old /12_0/kb/... URLs lose their redirects

EXCLUDED_DIRS (line 34) skips kb, so none of the ~19 PPE KB articles get a redirect entry. Those URLs are live today at /docs/passwordpolicyenforcer/12_0/kb/<article>, and the identical article exists at /docs/passwordpolicyenforcer/kb/<article> after this change, but readers get bounced to the product root instead. Skipping kb for 11.x is correct — those folders only contain KB content when the copy script has run for them, which it no longer does, so including them would make the redirect set depend on leftover local state. The 12.0 pass could include kb safely though: prebuild/prestart run copy-kb-to-versions.mjs before the config is loaded, and the destination is docs/passwordpolicyenforcer/12.0/kb.

4. Latent: a subfolder index.md would compute a route that is not a real route

listDocRoutes() (lines 78-84) gives <folder>/<folder>.md a trailing slash but gives index.md none (route = relPrefix). Docusaurus applies the conventional-doc-index rule identically to index.md, README.md, and <folderName>.md — see src/config/redirects/accessanalyzer-26.1.js:86-93, whose SECTION_INDEXES (agents, scans, service-accounts, ...) are all index.md folders and all need the trailing slash. plugin-client-redirects throws on a to that is not an existing route, so the day someone adds docs/passwordpolicyenforcer/12.0/<section>/index.md the build breaks with "client-side redirections to paths that do not exist". Only the root index.md exists today (route "" -> bare prefix, which is correct), so nothing breaks right now. Suggest folding the two cases together: route = (base === "index" || base === parentFolderName) ? (relPrefix ? relPrefix + "/" : "") : docPath.

5. The gtag gate probably does not fix the reported dev error

docusaurus.config.js:200 gates the plugin on NODE_ENV === "production". @docusaurus/plugin-google-gtag already self-gates on the same variable — getClientModules() returns [] and injectHtmlTags() returns {} outside production — so the plugin never loads the gtag script or the client module under npm run start, and there is no window.gtag call anywhere in this repo outside the plugin (grepped src/ and static/). Worth double-checking where the overlay actually comes from. Meanwhile the gate adds a silent failure mode: any invocation that loads the config without NODE_ENV=production drops analytics with no warning. It does work for npm run build (the Docusaurus build command sets NODE_ENV before loading the config), but note I could not verify against node_modules, since deps are not installed in this checkout.

6. Stale guidance file

docs/passwordpolicyenforcer/CLAUDE.md still says 11.2/11.1/11.0 are "still fully built and linkable" and lists all five versions as active. Worth updating in this PR so the next agent editing PPE docs does not go looking for versioned routes that no longer exist.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant