From e75b801a31258d474580349ee2fca197ba86565b Mon Sep 17 00:00:00 2001 From: Bryton Hall Date: Sun, 28 Aug 2022 15:22:43 -0400 Subject: [PATCH] create static docs site with module options --- .github/workflows/ci.yml | 4 +- .github/workflows/pages.yml | 43 +++++++ .gitmodules | 3 + .vscode/settings.json | 5 +- CONTRIBUTING.md | 8 ++ README.md | 4 +- docs/.gitignore | 5 + docs/assets/_custom.scss | 91 +++++++++++++++ docs/config.toml | 10 ++ docs/content/_index.md | 1 + docs/content/modules/.gitkeep | 0 docs/data/.gitkeep | 0 docs/default.nix | 122 ++++++++++++++++++++ docs/layouts/partials/details.html | 40 +++++++ docs/layouts/partials/docs/inject/body.html | 24 ++++ docs/layouts/partials/docs/inject/head.html | 1 + docs/layouts/partials/highlight.html | 10 ++ docs/layouts/shortcodes/options.html | 22 ++++ docs/{ => static}/logo.svg | 0 docs/themes/hugo-book | 1 + flake.nix | 43 ++++++- modules/k8s.nix | 13 ++- pkgs/kubenix.nix | 14 +-- treefmt.toml | 3 + 24 files changed, 448 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/pages.yml create mode 100644 .gitmodules create mode 100644 docs/.gitignore create mode 100644 docs/assets/_custom.scss create mode 100644 docs/config.toml create mode 120000 docs/content/_index.md create mode 100644 docs/content/modules/.gitkeep create mode 100644 docs/data/.gitkeep create mode 100644 docs/default.nix create mode 100644 docs/layouts/partials/details.html create mode 100644 docs/layouts/partials/docs/inject/body.html create mode 100644 docs/layouts/partials/docs/inject/head.html create mode 100644 docs/layouts/partials/highlight.html create mode 100644 docs/layouts/shortcodes/options.html rename docs/{ => static}/logo.svg (100%) create mode 160000 docs/themes/hugo-book diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21b239b..4ef7f91 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,9 @@ -name: CI +name: tests + on: pull_request: push: + jobs: tests: runs-on: ubuntu-latest diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..8ef62d8 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,43 @@ +name: pages + +on: + push: + branches: + - main + pull_request: + +jobs: + deploy: + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - uses: actions/checkout@v3 + with: + submodules: true + fetch-depth: 0 + + - uses: cachix/install-nix-action@v16 + with: + install_url: https://github.com/numtide/nix-unstable-installer/releases/download/nix-2.8.0pre20220311_d532269/install + extra_nix_config: | + experimental-features = nix-command flakes + allow-import-from-derivation = true + + - name: build + run: nix run '.#docs' -- --minify + + - name: upload + uses: actions/upload-pages-artifact@v1 + with: + path: './docs/public' + + - name: deploy + id: deployment + uses: actions/deploy-pages@v1 + +permissions: + contents: read + pages: write + id-token: write \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..47029a0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "docs/themes/hugo-book"] + path = docs/themes/hugo-book + url = https://github.com/alex-shpak/hugo-book diff --git a/.vscode/settings.json b/.vscode/settings.json index 6b6fd46..33e411a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,6 @@ { - "nix.formatterPath": "alejandra" + "nix.formatterPath": "alejandra", + "search.exclude": { + "docs/themes": true + } } \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b85f368..f158cc1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,3 +38,11 @@ To support a new Kubernetes version: ## Tests Tests are executed through GitHub actions; see the [workflow definition](../kubenix/.github/workflows/ci.yml) for commands. + +## Docs + +Build and serve the static site + + nix run '.#docs' serve + +which will be available at . diff --git a/README.md b/README.md index 7dbb12e..c1797a7 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Kubernetes resource management with Nix

- nixos logo in kubernetes blue + nixos logo in kubernetes blue

> **WARN**: this is a work in progress, expect breaking changes @@ -49,7 +49,7 @@ See [./docs/examples](./docs/examples) for more. ## CLI -> **NOTE**: this is a WIP CLI which currently reads the `k8s` attribute on a local flake +> **NOTE**: this is a WIP CLI which currently reads the `k8s` package on a local flake Render all resources with diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..cbbd1d9 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,5 @@ +public/ +resources/ +*.lock +options.json +content/modules/*.md diff --git a/docs/assets/_custom.scss b/docs/assets/_custom.scss new file mode 100644 index 0000000..80f53d5 --- /dev/null +++ b/docs/assets/_custom.scss @@ -0,0 +1,91 @@ +$foreground-color: #f8f8f2; +$background-color: #333333; + +// create an arrow rotated by $angle +@mixin arrow($angle) { + content: ""; + display: inline-block; + position: fixed; + // size + padding: 3px; + // position + margin: 18px 15px; + border: solid $background-color; + border-width: 0 2px 2px 0; + + transform: rotate($angle); + -webkit-transform: rotate($angle); +} + +details { + padding: 0rem 1rem !important; + margin: 1rem; + padding: 0 !important; + + > summary { + padding: 0rem !important; + margin: 0 !important; + + // do not show arrow bullet point + list-style: none; + ::-webkit-details-marker { + display: none; + } + + // replace builtin arrows with custom ones + pre { + display: inline-block; + width: 100%; + margin: 0 !important; + padding-left: 42px !important; + vertical-align: middle; + } + &:before { + @include arrow(-45deg); + } + } + + &[open] summary:before { + @include arrow(45deg); + } + + table, tbody { + // fill entire width + width: 100% !important; + display: table; + margin: 0 !important; + + tr { + // make code blocks a little smaller + pre { + margin: 0 !important; + padding: 0.2rem !important; + + &.highlight { + color: $foreground-color; + background-color: $background-color; + }; + + code { + vertical-align: text-bottom; + padding-left: 0.2rem !important; + } + } + + // field name + td:first-child { + font-weight: bold; + font-size: 90%; + + width: 8em; + min-width: 8em; + max-width: 8em; + word-break: break-all; + } + } + } +} + +aside.book-menu span { + font-weight: bold; +} \ No newline at end of file diff --git a/docs/config.toml b/docs/config.toml new file mode 100644 index 0000000..b424ca7 --- /dev/null +++ b/docs/config.toml @@ -0,0 +1,10 @@ +title = "kubenix" +theme = "hugo-book" +baseURL = "https://kubenix.org/" + +[params] +BookLogo = "logo.svg" +BookRepo = "https://github.com/hall/kubenix" +BookSection = '*' +# most pages at this time are auto-generated so editing doesn't make sense +# BookEditPath = 'edit/main/docs' diff --git a/docs/content/_index.md b/docs/content/_index.md new file mode 120000 index 0000000..fe84005 --- /dev/null +++ b/docs/content/_index.md @@ -0,0 +1 @@ +../../README.md \ No newline at end of file diff --git a/docs/content/modules/.gitkeep b/docs/content/modules/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/data/.gitkeep b/docs/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/default.nix b/docs/default.nix new file mode 100644 index 0000000..87ea28b --- /dev/null +++ b/docs/default.nix @@ -0,0 +1,122 @@ +# adapted from: https://discourse.nixos.org/t/franken-script-to-generate-nixos-options-docs-with-custom-modules/1674/4 +{ + pkgs, + options, +}: let + extraSources = []; + lib = pkgs.lib; + + optionsListVisible = + lib.filter (opt: opt.visible && !opt.internal) + (lib.optionAttrSetToDocList options); + + # Replace functions by the string + substFunction = x: + if builtins.isAttrs x + then lib.mapAttrs (name: substFunction) x + else if builtins.isList x + then map substFunction x + else if lib.isFunction x + then "" + else if isPath x + then toString x + else x; + + isPath = x: (builtins.typeOf x) == "path"; + + optionsListDesc = lib.flip map optionsListVisible ( + opt: + opt + // { + description = let + attempt = builtins.tryEval opt.description; + in + if attempt.success + then attempt.value + else "N/A"; + declarations = map stripAnyPrefixes opt.declarations; + } + // lib.optionalAttrs (opt ? example) { + example = substFunction opt.example; + } + // lib.optionalAttrs (opt ? default) { + default = substFunction opt.default; + } + // lib.optionalAttrs (opt ? type) { + type = substFunction opt.type; + } + // lib.optionalAttrs + (opt ? relatedPackages && opt.relatedPackages != []) + { + relatedPackages = genRelatedPackages opt.relatedPackages; + } + ); + + genRelatedPackages = packages: let + unpack = p: + if lib.isString p + then {name = p;} + else if lib.isList p + then {path = p;} + else p; + describe = args: let + title = args.title or null; + name = args.name or (lib.concatStringsSep "." args.path); + path = args.path or [args.name]; + package = + args.package + or (lib.attrByPath path + (throw + "Invalid package attribute path '${toString path}'") + pkgs); + in + "" + + "${lib.optionalString (title != null) + "${title} aka "}pkgs.${name} (${package.meta.name})" + + lib.optionalString (!package.meta.available) + " [UNAVAILABLE]" + + ": ${package.meta.description or "???"}." + + lib.optionalString (args ? comment) + "\n${args.comment}" + + lib.optionalString (package.meta ? longDescription) + "\n${package.meta.longDescription}" + + "" + + ""; + in "${lib.concatStringsSep "\n" (map (p: + describe (unpack p)) + packages)}"; + + optionLess = a: b: let + ise = lib.hasPrefix "enable"; + isp = lib.hasPrefix "package"; + cmp = + lib.splitByAndCompare ise lib.compare + (lib.splitByAndCompare isp lib.compare lib.compare); + in + lib.compareLists cmp a.loc b.loc < 0; + + prefixesToStrip = map (p: "${toString p}/") ([../../..] ++ extraSources); + stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix) prefixesToStrip; + + ############################################################################### + + # This is the REAL meat of what we were after. + # Output this however you want. + optionsList = lib.sort optionLess optionsListDesc; + + optionsJSON = builtins.unsafeDiscardStringContext (builtins.toJSON + (builtins.listToAttrs (map + (o: { + name = o.name; + value = removeAttrs o [ + # Select the fields you want to drop here: + "name" + "visible" + "internal" + "loc" + "readOnly" + ]; + }) + optionsList))); +in + pkgs.writeText "options.json" optionsJSON diff --git a/docs/layouts/partials/details.html b/docs/layouts/partials/details.html new file mode 100644 index 0000000..f249d1f --- /dev/null +++ b/docs/layouts/partials/details.html @@ -0,0 +1,40 @@ +{{ $name := index . "name" }} +{{ $option := index . "option" }} +{{ $repo := index . "repo" }} + +{{ $path := path.Join (after 2 (split (index $option.declarations 0) "/")) }} + +
+ +
{{ $name }}
+
+ + + + + + + + + + + + + + + + + + {{ with $option.example }} + + + + + {{ end }} + + + + + +
Description{{ $option.description }}
Type
{{ $option.type }}
Default{{ partial "highlight" $option.default }}
Example{{ partial "highlight" . }}
Declared in{{ $path }}
+
\ No newline at end of file diff --git a/docs/layouts/partials/docs/inject/body.html b/docs/layouts/partials/docs/inject/body.html new file mode 100644 index 0000000..7b674e3 --- /dev/null +++ b/docs/layouts/partials/docs/inject/body.html @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/docs/layouts/partials/docs/inject/head.html b/docs/layouts/partials/docs/inject/head.html new file mode 100644 index 0000000..d7e644f --- /dev/null +++ b/docs/layouts/partials/docs/inject/head.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/layouts/partials/highlight.html b/docs/layouts/partials/highlight.html new file mode 100644 index 0000000..dd99c89 --- /dev/null +++ b/docs/layouts/partials/highlight.html @@ -0,0 +1,10 @@ +{{ $text := . }} + +{{/* if text is a multiline string add nix's double single-quotes */}} +{{ if in $text "\n" }} + {{ $text = print "''\n " (strings.TrimSuffix " " (replace $text "\n" "\n ") ) "''" }} +{{ else }} + {{ $text = jsonify $text }} +{{ end }} + +
{{ $text }}
\ No newline at end of file diff --git a/docs/layouts/shortcodes/options.html b/docs/layouts/shortcodes/options.html new file mode 100644 index 0000000..f596493 --- /dev/null +++ b/docs/layouts/shortcodes/options.html @@ -0,0 +1,22 @@ +{{ $module := $.Page.File.BaseFileName }} +{{ $repo := $.Site.Params.BookRepo }} +{{ range $name, $option := .Site.Data.options }} + + {{/* some module options are nested under others */}} + {{ if and (hasPrefix $name "kubernetes.helm.") }} + {{ if (eq $module "helm") }} + {{ partial "details" (dict "name" $name "option" $option "repo" $repo) }} + {{ end }} + {{ else }} + + {{/* only show options for the current module */}} + {{/* but don't list _all_ kubernetes resources */}} + {{ if and + (not (hasPrefix $name "kubernetes.api.resources.")) + (hasPrefix $name (print $module ".")) + }} + {{ partial "details" (dict "name" $name "option" $option "repo" $repo) }} + {{ end }} + {{ end }} + +{{ end }} \ No newline at end of file diff --git a/docs/logo.svg b/docs/static/logo.svg similarity index 100% rename from docs/logo.svg rename to docs/static/logo.svg diff --git a/docs/themes/hugo-book b/docs/themes/hugo-book new file mode 160000 index 0000000..317ccae --- /dev/null +++ b/docs/themes/hugo-book @@ -0,0 +1 @@ +Subproject commit 317ccae23b73f5e49d7341ad57227bd64a89ab38 diff --git a/flake.nix b/flake.nix index a4acbee..adf0d55 100644 --- a/flake.nix +++ b/flake.nix @@ -84,6 +84,7 @@ k9s kube3d kubie + hugo ]; packages = [ (pkgs.writeShellScriptBin "evalnix" '' @@ -100,6 +101,28 @@ formatter = pkgs.treefmt; apps = { + docs = inputs.flake-utils.lib.mkApp { + drv = pkgs.writeShellScriptBin "gen-docs" '' + set -eo pipefail + + # generate json object of module options + nix build '.#docs' -o ./docs/data/options.json + + # remove all old module pages + rm ./docs/content/modules/*.md || true + + # create a page for each module in hugo + for mod in ${builtins.toString (builtins.attrNames self.nixosModules.kubenix)}; do + [[ $mod == "base" ]] && mod=kubenix + [[ $mod == "k8s" ]] && mod=kubernetes + [[ $mod == "submodule"* ]] && continue + echo "  {{< options >}}" > ./docs/content/modules/$mod.md + done + + # build the site + cd docs && ${pkgs.hugo}/bin/hugo $@ + ''; + }; generate = inputs.flake-utils.lib.mkApp { drv = pkgs.writeShellScriptBin "gen-modules" '' set -eo pipefail @@ -120,8 +143,23 @@ // { cli = pkgs.callPackage ./pkgs/kubenix.nix {}; default = self.packages.${system}.cli; + docs = import ./docs { + inherit pkgs; + options = + (self.evalModules.${system} { + modules = + builtins.attrValues (builtins.removeAttrs + # the submodules module currently doesn't evaluate: + # error: No module found ‹name›/latest + # not sure how important that documentation is a this time + self.nixosModules.kubenix ["submodule" "submodules"]); + }) + .options; + }; } - // import ./jobs {inherit pkgs;}; + // import ./jobs { + inherit pkgs; + }; checks = let wasSuccess = suite: @@ -139,7 +177,8 @@ # TODO: access "success" derivation with nice testing utils for nice output nginx-example = wasSuccess (mkExamples {}).nginx-deployment.config.testing; } - // builtins.listToAttrs (builtins.map (v: { + // builtins.listToAttrs (builtins.map + (v: { name = "test-k8s-${builtins.replaceStrings ["."] ["_"] v}"; value = wasSuccess (mkK8STests {k8sVersion = v;}); }) diff --git a/modules/k8s.nix b/modules/k8s.nix index d94eea2..e4e0f1a 100644 --- a/modules/k8s.nix +++ b/modules/k8s.nix @@ -126,11 +126,12 @@ with lib; let }; indexOf = lst: value: - head (filter (v: v != -1) (imap0 (i: v: - if v == value - then i - else -1) - lst)); + head (filter (v: v != -1) (imap0 + (i: v: + if v == value + then i + else -1) + lst)); compareVersions = ver1: ver2: let getVersion = substring 1 10; @@ -302,7 +303,7 @@ in { type = types.submodule { imports = [ - (./generated + ''/v'' + cfg.version + ".nix") + ./generated/v${cfg.version}.nix apiOptions ] ++ customResourceOptions; diff --git a/pkgs/kubenix.nix b/pkgs/kubenix.nix index a994cf5..64da93f 100644 --- a/pkgs/kubenix.nix +++ b/pkgs/kubenix.nix @@ -1,9 +1,9 @@ -{ jq -, kubectl -, kubernetes-helm -, nix -, writeShellScriptBin -, +{ + jq, + kubectl, + kubernetes-helm, + nix, + writeShellScriptBin, }: writeShellScriptBin "kubenix" '' set -Eeuo pipefail @@ -53,7 +53,7 @@ writeShellScriptBin "kubenix" '' render) cat $MANIFESTS;; *) - ${kubectl}/bin/kubectl $@ -f $MANIFESTS;; + ${kubectl}/bin/kubectl $@ -f $MANIFESTS || true;; esac } diff --git a/treefmt.toml b/treefmt.toml index c5469bd..0103e8b 100644 --- a/treefmt.toml +++ b/treefmt.toml @@ -1,3 +1,6 @@ +[global] +excludes = ["./docs/themes/*"] + [formatter.nix] command = "alejandra" includes = ["*.nix"]