diff --git a/.github/actions/setup-mkdocs/action.yml b/.github/actions/setup-mkdocs/action.yml new file mode 100644 index 00000000..69a5568f --- /dev/null +++ b/.github/actions/setup-mkdocs/action.yml @@ -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" diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 2e600039..059ef3e7 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -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 }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 47cc271d..afc9e145 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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 diff --git a/board/common/post-build.sh b/board/common/post-build.sh index 2f9e665e..305f77ad 100755 --- a/board/common/post-build.sh +++ b/board/common/post-build.sh @@ -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 diff --git a/doc/developers-guide.md b/doc/developers-guide.md index 032fc69d..b1cdd2e4 100644 --- a/doc/developers-guide.md +++ b/doc/developers-guide.md @@ -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: diff --git a/package/webui/webui.conf b/package/webui/webui.conf index ac37f626..62c27838 100644 --- a/package/webui/webui.conf +++ b/package/webui/webui.conf @@ -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 { diff --git a/src/webui/internal/auth/login.go b/src/webui/internal/auth/login.go index 8274814a..733445a4 100644 --- a/src/webui/internal/auth/login.go +++ b/src/webui/internal/auth/login.go @@ -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 { diff --git a/src/webui/internal/handlers/capabilities.go b/src/webui/internal/handlers/capabilities.go index 21cd497c..403df1cc 100644 --- a/src/webui/internal/handlers/capabilities.go +++ b/src/webui/internal/handlers/capabilities.go @@ -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 diff --git a/src/webui/templates/layouts/base.html b/src/webui/templates/layouts/base.html index a3bfa4f4..2f960a9c 100644 --- a/src/webui/templates/layouts/base.html +++ b/src/webui/templates/layouts/base.html @@ -24,6 +24,13 @@
+ {{if .Capabilities.Has "docs"}} + {{/* On-device User's Guide (static mkdocs site at /guide/). */}} + + {{template "icon-book"}} + + {{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"}} + {{if .Capabilities.Has "docs"}} + + + {{template "icon-book"}} + User Guide + + {{end}}
diff --git a/src/webui/templates/layouts/icons.html b/src/webui/templates/layouts/icons.html index 95d472f8..935741a1 100644 --- a/src/webui/templates/layouts/icons.html +++ b/src/webui/templates/layouts/icons.html @@ -12,6 +12,7 @@ {{define "icon-menu"}}{{end}} {{define "icon-terminal"}}{{end}} {{define "icon-radar"}}{{end}} +{{define "icon-book"}}{{end}} {{define "icon-user"}}{{end}} {{define "icon-user-lg"}}{{end}} {{define "icon-chevron-down"}}{{end}}