mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
webui: bundle on-device User's Guide
Build the mkdocs User's Guide into WebUI images and surface it in the UI. post-build.sh runs `mkdocs build` from the top-level mkdocs.yml into /var/www/guide when the webui package is selected and mkdocs is on the build host; nginx serves it read-only at /guide/. The build is best-effort — a missing mkdocs warns and ships without the guide rather than failing — and minimal images (no webui) skip it. The WebUI gates a book icon in the topbar and a User Guide item in the user menu on the docs being present on disk (os.Stat, fail-closed), both opening /guide/ in a new tab. CI: a local setup-mkdocs composite action installs mkdocs and the plugins from mkdocs.yml; the build and release workflows run it before the build so images produced in CI include the guide. developers-guide documents the new build dependency and restores the missing mkdocs-glightbox plugin. Fixes #633 Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
name: 'Setup MkDocs'
|
||||
description: "Install MkDocs and the plugins required to build the Infix User's Guide (post-build.sh bundles it into WebUI images)."
|
||||
|
||||
# Mirrors the dependency set in .github/workflows/docs.yml. Kept in-repo
|
||||
# (rather than an external action) so any checkout of a commit needing
|
||||
# docs-in-image can reproduce it without an out-of-tree dependency.
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install MkDocs and plugins
|
||||
shell: bash
|
||||
run: |
|
||||
command -v pipx >/dev/null 2>&1 || python3 -m pip install --user pipx
|
||||
python3 -m pipx install --force mkdocs
|
||||
python3 -m pipx inject mkdocs \
|
||||
mkdocs-material pymdown-extensions mkdocs-callouts mike mkdocs-to-pdf mkdocs-glightbox
|
||||
# Make the pipx-installed mkdocs visible to later steps (the build,
|
||||
# where post-build.sh invokes it).
|
||||
python3 -m pipx environment --value PIPX_BIN_DIR >> "$GITHUB_PATH" 2>/dev/null \
|
||||
|| echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
@@ -45,6 +45,9 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
enabled: ${{ inputs.use_cache }}
|
||||
|
||||
# WebUI images bundle the mkdocs User's Guide via post-build.sh.
|
||||
- uses: ./.github/actions/setup-mkdocs
|
||||
|
||||
- name: Configure & Build
|
||||
env:
|
||||
INFIX_RELEASE: ${{ steps.vars.outputs.ver }}
|
||||
|
||||
@@ -124,6 +124,9 @@ jobs:
|
||||
with:
|
||||
target: ${{ env.TARGET }}
|
||||
|
||||
# WebUI images bundle the mkdocs User's Guide via post-build.sh.
|
||||
- uses: ./.github/actions/setup-mkdocs
|
||||
|
||||
- name: Configure ${{ env.TARGET }}
|
||||
run: |
|
||||
make ${{ env.TARGET }}_defconfig
|
||||
|
||||
@@ -120,3 +120,29 @@ grep -qsE '^/bin/clish$$' "$TARGET_DIR/etc/shells" \
|
||||
if [ "$BR2_PACKAGE_HOST_PYTHON_YANGDOC" = "y" ]; then
|
||||
mkyangdoc "$BINARIES_DIR/yangdoc.html"
|
||||
fi
|
||||
|
||||
# Bundle the mkdocs User's Guide into the rootfs, served by the WebUI's
|
||||
# nginx at /guide/. Only when the WebUI is present (nothing serves it
|
||||
# otherwise, and it keeps minimal images small) and mkdocs is on the build
|
||||
# host. Best-effort: a failed build warns but does not abort the image
|
||||
# build, and the WebUI hides its User Guide entry when the docs are absent.
|
||||
mkuserguide()
|
||||
{
|
||||
local cfg dst
|
||||
cfg="$(readlink -f "$common/../..")/mkdocs.yml"
|
||||
dst="$TARGET_DIR/var/www/guide"
|
||||
|
||||
if ! command -v mkdocs >/dev/null 2>&1; then
|
||||
ixmsg "mkdocs not found, skipping User's Guide bundling"
|
||||
return
|
||||
fi
|
||||
ixmsg "Building User's Guide into $dst"
|
||||
if ! mkdocs build -f "$cfg" -d "$dst" --clean --quiet; then
|
||||
ixmsg "WARNING: mkdocs build failed, shipping without on-device User's Guide"
|
||||
rm -rf "$dst"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$BR2_PACKAGE_WEBUI" = "y" ]; then
|
||||
mkuserguide
|
||||
fi
|
||||
|
||||
+13
-5
@@ -122,13 +122,21 @@ recommend using `pipx` to install the necessary tooling:
|
||||
```bash
|
||||
$ sudo apt install pipx
|
||||
$ pipx install mkdocs
|
||||
$ pipx inject mkdocs mkdocs-material pymdown-extensions mkdocs-callouts mike mkdocs-to-pdf
|
||||
$ pipx inject mkdocs mkdocs-material pymdown-extensions mkdocs-callouts mike mkdocs-to-pdf mkdocs-glightbox
|
||||
```
|
||||
|
||||
The last two packages, `mike` and `mkdocs-to-pdf`, are used for online
|
||||
versioning and PDF generation by GitHub Actions, but since they are in
|
||||
the `mkdocs.yml` file, everyone who wants to preview the documentation
|
||||
have to install all the tooling.
|
||||
The `mike` and `mkdocs-to-pdf` packages are used for online versioning
|
||||
and PDF generation by GitHub Actions, but since every plugin is listed
|
||||
in `mkdocs.yml`, anyone who wants to preview the documentation has to
|
||||
install all the tooling.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> MkDocs is also required to **build a WebUI image**. The build bundles
|
||||
> the User's Guide into the image (served on-device at `/guide/`), so
|
||||
> `make` runs `mkdocs build` from `post-build.sh` when the `webui`
|
||||
> package is selected. If MkDocs is missing the build still succeeds,
|
||||
> but the image ships without the on-device guide. Minimal images and
|
||||
> any build without the `webui` package skip this step entirely.
|
||||
|
||||
Preview with:
|
||||
|
||||
|
||||
@@ -55,6 +55,15 @@ location = /maintenance/support-bundle {
|
||||
include /etc/nginx/webui-proxy.conf;
|
||||
}
|
||||
|
||||
# On-device User's Guide: the mkdocs site bundled into the rootfs at
|
||||
# /var/www/guide by post-build.sh. Served as static files (no upstream
|
||||
# call), public like the published guide. Present only when the build
|
||||
# host had mkdocs; the WebUI hides its User Guide entry when it's absent.
|
||||
location /guide/ {
|
||||
alias /var/www/guide/;
|
||||
index index.html;
|
||||
}
|
||||
|
||||
# Liveness probe — nginx-only, no upstream call. Used by the watchdog
|
||||
# div in base.html and the reboot-overlay poller.
|
||||
location = /device-status {
|
||||
|
||||
@@ -80,6 +80,9 @@ func (h *LoginHandler) DoLogin(w http.ResponseWriter, r *http.Request) {
|
||||
console, netbrowse := handlers.DetectWebShortcuts(ctx, h.RC)
|
||||
caps.Features()["console"] = console
|
||||
caps.Features()["netbrowse"] = netbrowse
|
||||
// The User's Guide is bundled at build time (a filesystem check, not
|
||||
// config); gate the Help entry on its presence.
|
||||
caps.Features()["docs"] = handlers.DetectDocs()
|
||||
|
||||
// Trigger any post-login hooks (e.g. schema sync) with full credentials.
|
||||
if h.OnLogin != nil {
|
||||
|
||||
@@ -5,10 +5,22 @@ package handlers
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"infix/webui/internal/restconf"
|
||||
)
|
||||
|
||||
// docsIndexPath is the entry point of the on-device User's Guide bundled
|
||||
// by post-build.sh (mkdocs → /var/www/guide), served by nginx at /guide/.
|
||||
const docsIndexPath = "/var/www/guide/index.html"
|
||||
|
||||
// DetectDocs reports whether the User's Guide was bundled into this image
|
||||
// (the build host had mkdocs), so the UI can show or hide the entry.
|
||||
func DetectDocs() bool {
|
||||
_, err := os.Stat(docsIndexPath)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
type feature struct {
|
||||
Name string // key used in Has() and session cookie
|
||||
Module string // YANG module that carries the feature
|
||||
|
||||
@@ -24,6 +24,13 @@
|
||||
</button>
|
||||
<img src="/assets/img/logo.png" alt="Infix" class="topbar-logo">
|
||||
<div class="topbar-right">
|
||||
{{if .Capabilities.Has "docs"}}
|
||||
{{/* On-device User's Guide (static mkdocs site at /guide/). */}}
|
||||
<a class="topbar-link" href="/guide/" target="_blank" rel="noopener"
|
||||
title="Open the User Guide in a new tab" aria-label="User Guide">
|
||||
{{template "icon-book"}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .Capabilities.Has "netbrowse"}}
|
||||
{{/* mDNS network browser — fixed network.local vhost, so a static
|
||||
href (no JS, no CSP concern). */}}
|
||||
@@ -85,6 +92,13 @@
|
||||
Off
|
||||
{{template "icon-check"}}
|
||||
</button>
|
||||
{{if .Capabilities.Has "docs"}}
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="/guide/" target="_blank" rel="noopener">
|
||||
{{template "icon-book"}}
|
||||
User Guide
|
||||
</a>
|
||||
{{end}}
|
||||
<div class="dropdown-divider"></div>
|
||||
<form method="POST" action="/logout">
|
||||
<input type="hidden" name="csrf" value="{{.CsrfToken}}">
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
{{define "icon-menu"}}<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M4 5h16" /><path d="M4 12h16" /><path d="M4 19h16" /></svg>{{end}}
|
||||
{{define "icon-terminal"}}<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" aria-hidden="true"><polyline points="4 17 10 11 4 5" /><line x1="12" x2="20" y1="19" y2="19" /></svg>{{end}}
|
||||
{{define "icon-radar"}}<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" aria-hidden="true"><path d="M19.07 4.93A10 10 0 0 0 6.99 3.34" /><path d="M4 6h.01" /><path d="M2.29 9.62A10 10 0 1 0 21.31 8.35" /><path d="M16.24 7.76A6 6 0 1 0 8.23 16.67" /><path d="M12 18h.01" /><path d="M17.99 11.66A6 6 0 0 1 15.77 16.67" /><circle cx="12" cy="12" r="2" /><path d="m13.41 10.59 5.66-5.66" /></svg>{{end}}
|
||||
{{define "icon-book"}}<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" aria-hidden="true"><path d="M12 7v14" /><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z" /></svg>{{end}}
|
||||
{{define "icon-user"}}<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" aria-hidden="true"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" /></svg>{{end}}
|
||||
{{define "icon-user-lg"}}<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" aria-hidden="true"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" /></svg>{{end}}
|
||||
{{define "icon-chevron-down"}}<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" class="chevron" aria-hidden="true" stroke-width="2.5"><path d="m6 9 6 6 6-6" /></svg>{{end}}
|
||||
|
||||
Reference in New Issue
Block a user