docs: prepare the site build for hugo 0.166.0 (#13140)

Hugo v0.166.0 drops module mounts whose root, or any directory between
it and the project root, is a symlink. The isolated pnpm linker installs
every package as a symlink into `node_modules/.pnpm`, so all of the
Thulite, Tabler, Docsearch, and bootstrap-icons mounts disappear and the
build fails with `template for shortcode "callout" not found`. Switch
`docs` to the hoisted linker so packages are real directories, and
exclude `node_modules` from golangci-lint since the hoisted layout
exposes the Go sources shipped in `flatted`.

Migrate the deprecated configuration and template usage: `languageCode`
to `locale`, `languageName` to `label`, the global `imaging.quality` and
`imaging.hint` to the per format `avif`, `jpeg`, and `webp` settings,
and `.Language.LanguageName` to `.Language.Label`. The rendered site is
byte identical to the previous configuration on both v0.165.0 and
v0.166.0 apart from processed image file names.

Add docs suite tests asserting every `node_modules` mount resolves,
since a dropped mount does not always fail the build, and that the
locale and label still drive the document language and the English
homepage sections.

Signed-off-by: Amir Zarrinkafsh <3339418+nightah@users.noreply.github.com>
This commit is contained in:
Amir Zarrinkafsh authored and GitHub committed 2026-09-13 11:31:57 +00:00
1 parent b8c98fc9ae
commit 306bf9d118
7 files changed
+75 -11

No files matched your search

+2
View File
@@ -83,6 +83,7 @@ linters:
- builtin$
- examples$
- internal/mocks
- node_modules
issues:
max-issues-per-linter: 0
max-same-issues: 0
@@ -107,4 +108,5 @@ formatters:
- third_party$
- builtin$
- examples$
- node_modules
...
+9 -3
View File
@@ -10,7 +10,7 @@ disableHugoGeneratorInject = true
enableEmoji = true
enableGitInfo = true
enableRobotsTXT = true
languageCode = "en-US"
locale = "en-US"
rssLimit = 10
summarylength = 20 # 70 (default)
@@ -101,6 +101,12 @@ copyRight = "Copyright (c) 2016-%s Authelia"
[imaging]
anchor = "Center"
bgColor = "#ffffff"
hint = "photo"
quality = 85
resampleFilter = "Lanczos"
[imaging.avif]
hint = "photo"
quality = 85
[imaging.jpeg]
quality = 85
[imaging.webp]
hint = "photo"
quality = 85
+1 -1
View File
@@ -3,7 +3,7 @@
# SPDX-License-Identifier: Apache-2.0
[en]
languageName = "English"
label = "English"
contentDir = "content/en"
weight = 10
[en.params]
+4 -4
View File
@@ -186,7 +186,7 @@
<path d="M12 20l4 -9l4 9"></path>
<path d="M19.1 18h-6.2"></path>
</svg>
<span id="doks-language-current">{{ .Site.Language.LanguageName }}</span>
<span id="doks-language-current">{{ .Site.Language.Label }}</span>
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-chevron-down" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 9l6 6l6 -6"></path>
@@ -195,7 +195,7 @@
</button>
<ul class="dropdown-menu dropdown-menu-xl-end me-xl-2 shadow rounded border-0" aria-labelledby="doks-languages">
<li><span class="dropdown-item current" aria-current="true">{{ .Site.Language.LanguageName }}</span></li>
<li><span class="dropdown-item current" aria-current="true">{{ .Site.Language.Label }}</span></li>
<li><hr class="dropdown-divider"></li>
@@ -207,12 +207,12 @@
{{ range site.Languages -}}
{{ if and (ne $.Lang .Lang) (not (in $.Params.skipTranslations .Lang)) -}}
{{ $isTranslated := in $translatedLangs .Lang -}}
<li><a class="dropdown-item {{ if not $isTranslated }}untranslated{{ end }}" rel="alternate" href="{{ if $isTranslated }}{{ (index (where $.Translations "Lang" .Lang) 0).RelPermalink }}{{ else }}{{ .Lang | relURL }}{{ end }}" hreflang="{{ .Lang }}" lang="{{ .Lang }}">{{ .LanguageName }}</a></li>
<li><a class="dropdown-item {{ if not $isTranslated }}untranslated{{ end }}" rel="alternate" href="{{ if $isTranslated }}{{ (index (where $.Translations "Lang" .Lang) 0).RelPermalink }}{{ else }}{{ .Lang | relURL }}{{ end }}" hreflang="{{ .Lang }}" lang="{{ .Lang }}">{{ .Label }}</a></li>
{{- end }}
{{- end }}
{{ else -}}
{{ range .Translations -}}
<li><a class="dropdown-item" rel="alternate" href="{{ .RelPermalink }}" hreflang="{{ .Lang }}" lang="{{ .Lang }}">{{ .Language.LanguageName }}</a></li>
<li><a class="dropdown-item" rel="alternate" href="{{ .RelPermalink }}" hreflang="{{ .Lang }}" lang="{{ .Lang }}">{{ .Language.Label }}</a></li>
{{- end }}
{{- end }}
<!--
+3 -3
View File
@@ -20,19 +20,19 @@
<div class="bg-dots"></div>
</div>
{{ end -}}
{{ if eq $.Site.Language.LanguageName "English" }}
{{ if eq $.Site.Language.Label "English" }}
{{ partial "prefooter/features/en.html" . -}}
{{ end }}
{{ end }}
{{ define "sidebar-footer" }}
{{ if site.Params.doks.sectionFooter -}}
{{ if eq $.Site.Language.LanguageName "English" }}
{{ if eq $.Site.Language.Label "English" }}
{{ partial "prefooter/support/en.html" . -}}
{{ end }}
{{ end -}}
{{ if eq $.Site.Language.LanguageName "English" }}
{{ if eq $.Site.Language.Label "English" }}
{{ partial "prefooter/blog/en.html" . -}}
{{ partial "prefooter/certified/en.html" . -}}
{{ end }}
+1
View File
@@ -1,6 +1,7 @@
allowBuilds:
hugo-extended: true
preferSymlinkedExecutables: true
nodeLinker: hoisted
overrides:
adm-zip: 0.6.1
brace-expansion: 5.0.9
@@ -14,6 +14,7 @@ import (
"fmt"
"io"
"net/http"
"regexp"
"slices"
"strings"
"sync"
@@ -261,6 +262,60 @@ func (s *DocsSuite) TestStaticSvgAssetServed() {
require.True(s.T(), strings.Contains(string(body), "<svg"), "expected <svg root tag in body of %s", svgPath)
}
func (s *DocsSuite) TestModuleMountsResolve() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, home := s.httpFetch(ctx, "/")
docsearch := regexp.MustCompile(`href="(/css/docsearch/[^"]+\.css)"`).FindSubmatch(home)
require.NotNil(s.T(), docsearch, "expected the homepage to link the @docsearch/css stylesheet")
for _, asset := range []struct {
mount, path, contentType string
}{
{"@docsearch/css", string(docsearch[1]), "text/css"},
{"bootstrap-icons", "/fonts/bootstrap-icons.woff2", "font/woff2"},
{"@thulite/doks-core/static", "/fonts/vendor/jost/jost-v4-latin-regular.woff2", "font/woff2"},
} {
resp, body := s.httpFetch(ctx, asset.path)
require.Equal(s.T(), http.StatusOK, resp.StatusCode, "expected 200 fetching %s from the %s mount", asset.path, asset.mount)
require.True(s.T(), strings.HasPrefix(resp.Header.Get("Content-Type"), asset.contentType), "expected %s for %s, got %s", asset.contentType, asset.path, resp.Header.Get("Content-Type"))
require.NotEmpty(s.T(), body, "expected non-empty body for %s", asset.path)
}
for _, rendered := range []struct {
mount, path, contains string
}{
{"@thulite/doks-core/layouts", "/blog/authelia--traefik-setup-guide/", `class="callout callout-note`},
{"@tabler/icons and @thulite/inline-svg", "/", "icon-tabler-brand-github"},
{"@thulite/seo", "/manifest.webmanifest", `"name"`},
} {
resp, body := s.httpFetch(ctx, rendered.path)
require.Equal(s.T(), http.StatusOK, resp.StatusCode, "expected 200 fetching %s", rendered.path)
require.Contains(s.T(), string(body), rendered.contains, "expected %s to render %q from the %s mount", rendered.path, rendered.contains, rendered.mount)
}
}
func (s *DocsSuite) TestLanguageConfigurationRendered() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
_, home := s.httpFetch(ctx, "/")
_, manifest := s.httpFetch(ctx, "/manifest.webmanifest")
for _, expected := range []struct {
name, body, contains string
}{
{"html lang attribute", string(home), `<html lang="en-US"`},
{"web manifest lang", string(manifest), `"lang": "en-US"`},
{"features prefooter", string(home), "section-features"},
{"support prefooter", string(home), `class="section section-md container-fluid bg-light"`},
} {
require.Contains(s.T(), expected.body, expected.contains, "expected the %s to contain %q", expected.name, expected.contains)
}
}
func (s *DocsSuite) TestOpenIDConnectProviderShortcodes() {
page := s.doCreateTab(s.T(), s.docsURL("/configuration/identity-providers/openid-connect/provider/"))
defer page.MustClose()