Skip to content

Commit 3abf494

Browse files
Merge pull request #24 from solo-io/adb-2026-7-lts
accept 2026.7.1 as a URL
2 parents d711d5f + 12f0b13 commit 3abf494

4 files changed

Lines changed: 116 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ how to verify it, e.g. view-source or a validator). State how the change was ver
2222

2323
---
2424

25+
## [v0.1.24] — 2026-07-31
26+
27+
### Fix — infer fully qualified (LTS) versions in `link-hextra` (`layouts/_shortcodes/link-hextra.html`, `tests/link-hextra-lts-version.spec.ts`)
28+
29+
- **Every reuse-nested cross-doc link on a fully qualified version tree — e.g. agentgateway's new `2026.7.1` LTS docs — resolves to `/latest/…` instead of `/2026.7.1/…`, and the build logs a `link-hextra called with no version and could not infer one from …` WARN for each one (which fails `hugo-warnings.spec.ts`, so the LTS branch can't go green).** `link-hextra` infers the target version by regex-matching the page's permalink when no `version=` was passed, and both of its patterns accepted only `\d+\.\d+\.x`, `latest`, or `main`. A three-segment version matched neither, so inference fell through to the last-resort `$ver = "latest"` fallback. This is not a version-tree-only concern: the pages that hit it are ordinary `{{< reuse "…" >}}` stubs, and `reuse.html` injects `version=` into nested `link-hextra` calls only when the page passed a version POSITIONALLY (`$parentVersion`) — the version it resolves from the permalink for asset lookup (`$resolvedVersion`, which matches against `params.versions` and therefore handled `2026.7.1` fine) is never injected. So on any product whose reuse stubs omit the positional version, the shortcode's own regex is the only thing standing between the page and a wrong-version link. `2.3.x`-style trees were unaffected only because they happen to match the narrow pattern. Fix: widen both alternations to `\d+\.\d+\.(?:x|\d+)`, so `X.Y.x` and `X.Y.Z` both infer. Nothing else in the shortcode changes — the language-prefix strip, the `reference/api` subpage routing, and the baseURL handling are untouched, and a version that still can't be inferred keeps warning and falling back to `latest`.
30+
- **Consumer note — `solo-io/docs` carries a LOCAL `layouts/_shortcodes/link-hextra.html` override with the identical widening**, because the LTS branch there can't wait on a pin bump (it pins v0.1.23). That override is marked for deletion once the hub bumps to a release carrying this fix; while it exists, module-side `link-hextra` changes do NOT reach the hub. Same shape as the v0.1.22 `reuse.html` override note.
31+
- Observable in production: on the [agentgateway 2026.7.1 WAF overview](https://docs.solo.io/agentgateway/2026.7.1/security/waf/overview/), the in-body "Custom rules" / "IP filtering" links point at `/agentgateway/latest/security/waf/…` before the fix and `/agentgateway/2026.7.1/security/waf/…` after — links that silently retarget as soon as `latest` moves on. Verified against the hub's real build (`make build-preview PRODUCT=agentgateway`): the pre-fix log carried 60+ `link-hextra called with no version` WARNs across the `2026.7.1` tree and zero after, the only remaining WARN being the allowlisted `.Site.Data` deprecation; the rendered page's `custom-rules` hrefs are `/agentgateway/2026.7.1/security/waf/custom-rules/` (plus the `#body-inspection` anchor), with `/latest/` left only in the version-dropdown and nav index where it belongs; and the hub's `content` project passes 130/130. `tests/link-hextra-lts-version.spec.ts` extracts both `findRE` patterns from the shipped shortcode and exercises them directly (source-level, since the bundled fixture has no fully qualified version tree and adding one would shift the URLs the rest of the suite asserts on) — it fails if either alternation is narrowed back to `X.Y.x`-only. Full `static` project green (1307 passed) on the enterprise fixture. Takes effect on the hub via its override; for other consumers, when they bump their extras pin.
32+
2533
## [v0.1.23] — 2026-07-29
2634

2735
### Fix — actually hide the llms.txt discovery directive from sighted readers (`layouts/_partials/docs-llms-directive.html`)

layouts/_shortcodes/link-hextra.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@
2424
{{- with .Site.LanguagePrefix -}}
2525
{{- $relURL = replace $relURL (printf "%s/" .) "/" -}}
2626
{{- end -}}
27-
{{- with findRE `(?:kgateway|agentgateway|gateway|envoy)/(\d+\.\d+\.x|latest|main)` $relURL 1 -}}
27+
{{- /* Versions are either `X.Y.x` (2.3.x) or fully qualified `X.Y.Z` (2026.7.1, LTS). */ -}}
28+
{{- with findRE `(?:kgateway|agentgateway|gateway|envoy)/(\d+\.\d+\.(?:x|\d+)|latest|main)` $relURL 1 -}}
2829
{{- $ver = replaceRE `^.*/` "" (index . 0) -}}
2930
{{- end -}}
3031
{{- if not $ver -}}
31-
{{- with findRE `^/(\d+\.\d+\.x|latest|main)` $relURL 1 -}}
32+
{{- with findRE `^/(\d+\.\d+\.(?:x|\d+)|latest|main)` $relURL 1 -}}
3233
{{- $ver = index . 0 | strings.TrimPrefix "/" -}}
3334
{{- end -}}
3435
{{- end -}}

playwright.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export default defineConfig({
5858
{
5959
name: "static",
6060
testMatch:
61-
/static\.spec\.ts$|versioning\.spec\.ts$|versioned-image-auto\.spec\.ts$|version-nested-list\.spec\.ts$|version-inside-fence\.spec\.ts$|version-table-row\.spec\.ts$|version-cards\.spec\.ts$|shortcode-contexts\.spec\.ts$|conditional-block\.spec\.ts$|cond-reuse-table\.spec\.ts$|reuse-version-remap\.spec\.ts$|llms-directive\.spec\.ts$|callout-in-table-cell\.spec\.ts$|callout-in-reuse-tab\.spec\.ts$|auto-cards\.spec\.ts$|card-image\.spec\.ts$|presence\.spec\.ts$|github-shortcode\.spec\.ts$|language-switch\.spec\.ts$|redirect\.spec\.ts$|sidebar-linktitle\.spec\.ts$|sidebar-flat\.spec\.ts$|link-hextra-apiref\.spec\.ts$|build-resilience\.spec\.ts$|page-feedback\.spec\.ts$|footnotes-after-cards\.spec\.ts$|callout-icon\.spec\.ts$|custom-alert\.spec\.ts$|docs-tabs\.spec\.ts$|meta-description\.spec\.ts$|link-hextra-lang-prefix\.spec\.ts$|warn-missing-description\.spec\.ts$/,
61+
/static\.spec\.ts$|versioning\.spec\.ts$|versioned-image-auto\.spec\.ts$|version-nested-list\.spec\.ts$|version-inside-fence\.spec\.ts$|version-table-row\.spec\.ts$|version-cards\.spec\.ts$|shortcode-contexts\.spec\.ts$|conditional-block\.spec\.ts$|cond-reuse-table\.spec\.ts$|reuse-version-remap\.spec\.ts$|llms-directive\.spec\.ts$|callout-in-table-cell\.spec\.ts$|callout-in-reuse-tab\.spec\.ts$|auto-cards\.spec\.ts$|card-image\.spec\.ts$|presence\.spec\.ts$|github-shortcode\.spec\.ts$|language-switch\.spec\.ts$|redirect\.spec\.ts$|sidebar-linktitle\.spec\.ts$|sidebar-flat\.spec\.ts$|link-hextra-apiref\.spec\.ts$|build-resilience\.spec\.ts$|page-feedback\.spec\.ts$|footnotes-after-cards\.spec\.ts$|callout-icon\.spec\.ts$|custom-alert\.spec\.ts$|docs-tabs\.spec\.ts$|meta-description\.spec\.ts$|link-hextra-lang-prefix\.spec\.ts$|link-hextra-lts-version\.spec\.ts$|warn-missing-description\.spec\.ts$/,
6262
},
6363
// Consumer-content specs. Every spec here reads the CONSUMER's own content
6464
// — either the built HTML tree (target.builtRoot) or the markdown source
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { test, expect } from "@playwright/test";
2+
import fs from "node:fs";
3+
import path from "node:path";
4+
5+
// Source-level guard for fully qualified (LTS) versions in link-hextra.
6+
//
7+
// link-hextra infers the target version by regex-matching the page's permalink.
8+
// The regexes originally accepted only `X.Y.x` (2.3.x), `latest`, and `main`.
9+
// When a product ships a fully qualified LTS tree (e.g.
10+
// `/agentgateway/2026.7.1/...`), inference fell through to the "latest"
11+
// fallback: every reuse-nested link on those pages pointed at `/latest/...`
12+
// instead of the LTS tree, and the build emitted a `link-hextra called with no
13+
// version` WARN for each one (which hugo-warnings.spec.ts fails on).
14+
//
15+
// Why a SOURCE check, not a rendered-output check: the bundled fixture has no
16+
// fully qualified version tree, and adding one shifts the page URLs the rest of
17+
// the suite asserts on. So this extracts the version alternation from the
18+
// shipped shortcode and exercises it directly. Self-skips when the file isn't
19+
// at the module-relative path (a consumer build, where the module lives under
20+
// hugo_cache rather than ../layouts).
21+
22+
const SHORTCODE = path.resolve(
23+
__dirname,
24+
"../layouts/_shortcodes/link-hextra.html",
25+
);
26+
27+
// Strip Go/Hugo template comments (`{{- /* … */ -}}`) so the assertions match
28+
// ACTIVE code, not the explanatory comments (which also spell out version
29+
// patterns).
30+
function activeSrc(): string {
31+
return fs
32+
.readFileSync(SHORTCODE, "utf8")
33+
.replace(/\{\{-?\s*\/\*[\s\S]*?\*\/\s*-?\}\}/g, "");
34+
}
35+
36+
// The two version-inference regexes, as written in the template:
37+
// 1. product-prefixed, e.g. `/agentgateway/2026.7.1/security/waf/overview/`
38+
// 2. root-relative, e.g. `/2026.7.1/security/waf/overview/` (preview builds
39+
// whose baseURL path is the product)
40+
const PATTERNS: Array<{ label: string; extract: RegExp }> = [
41+
{
42+
label: "product-prefixed",
43+
extract: /findRE\s+`((?:\(\?:)?kgateway[^`]*)`/,
44+
},
45+
{
46+
label: "root-relative",
47+
extract: /findRE\s+`(\^\/\([^`]*)`/,
48+
},
49+
];
50+
51+
// Versions that must infer, and the permalinks they appear in.
52+
const MUST_MATCH: Array<[string, string, string]> = [
53+
["2026.7.1", "/agentgateway/2026.7.1/security/waf/overview/", "/2026.7.1/security/waf/overview/"],
54+
["2.3.x", "/agentgateway/2.3.x/security/waf/overview/", "/2.3.x/security/waf/overview/"],
55+
["latest", "/agentgateway/latest/security/waf/overview/", "/latest/security/waf/overview/"],
56+
["main", "/kgateway/main/security/waf/overview/", "/main/security/waf/overview/"],
57+
];
58+
59+
test.describe("link-hextra infers fully qualified (LTS) versions", () => {
60+
test.skip(
61+
!fs.existsSync(SHORTCODE),
62+
"link-hextra.html not at the module-relative path (consumer build)",
63+
);
64+
65+
for (const { label, extract } of PATTERNS) {
66+
test(`the ${label} version regex accepts X.Y.Z as well as X.Y.x`, () => {
67+
const src = activeSrc();
68+
const m = src.match(extract);
69+
expect(
70+
m,
71+
`${label} version-inference \`findRE\` not found — the shortcode ` +
72+
"changed shape; re-check that fully qualified versions still infer.",
73+
).not.toBeNull();
74+
75+
// Go's regexp syntax is RE2, but these patterns use only constructs JS
76+
// shares, so they can be exercised directly.
77+
const re = new RegExp(m![1]);
78+
const idx = label === "product-prefixed" ? 1 : 2;
79+
80+
for (const row of MUST_MATCH) {
81+
const [version, ...permalinks] = row;
82+
const permalink = permalinks[idx - 1];
83+
const hit = permalink.match(re);
84+
expect(
85+
hit?.[1],
86+
`\`${version}\` did not infer from ${permalink} — links on that ` +
87+
"version tree fall back to `latest` and the build WARNs.",
88+
).toBe(version);
89+
}
90+
});
91+
}
92+
93+
test("the version alternation is not narrowed back to X.Y.x only", () => {
94+
const src = activeSrc();
95+
// Both regexes must allow a numeric third segment. Guards against a
96+
// revert of one branch while the other keeps working.
97+
const narrowed = src.match(/findRE\s+`[^`]*\\d\+\\\.\\d\+\\\.x[|)]/g) ?? [];
98+
expect(
99+
narrowed,
100+
"a version-inference regex still accepts only `\\d+\\.\\d+\\.x` — " +
101+
"fully qualified LTS versions (e.g. 2026.7.1) will not infer.",
102+
).toEqual([]);
103+
});
104+
});

0 commit comments

Comments
 (0)