Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions .github/workflows/options-doc.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
name: options doc → GitHub Pages

# Publishes the autogenerated `programs.trollshell.*` options reference
# (`nix build .#options-doc`, added in #533) to GitHub Pages. Phase 2 of
# #533 — see #540. Deliberately a leaf: it is NOT part of `nix flake check`
# and doesn't gate any other workflow.
# Publishes the trollshell docs site (`nix build .#options-doc`): the
# autogenerated `programs.trollshell.*` options reference (#533, phase 2 —
# see #540) plus the plugin env-knob reference (#573/#614) — to GitHub
# Pages. Deliberately a leaf: it is NOT part of `nix flake check` and
# doesn't gate any other workflow. The derivation itself renders every page
# (including the root landing page); this workflow only copies files out of
# `result/` into `_site/`.
on:
push:
branches: [main]
Expand Down Expand Up @@ -37,11 +40,14 @@ jobs:
- name: Assemble Pages site
run: |
mkdir -p _site
cp result/share/doc/trollshell/options.html _site/options.html
cp result/share/doc/trollshell/options.md _site/options.md
# So the Pages root (https://<org>.github.io/trollshell/) lands
# directly on the rendered doc instead of a 404.
cp result/share/doc/trollshell/options.html _site/index.html
# Whatever the derivation puts in share/doc/trollshell/ is what
# gets published — a whole-directory copy so a new output file
# never silently fails to ship (and the workflow header comment
# above stays true without needing to be kept in sync by hand).
# The Pages root (https://<org>.github.io/trollshell/) is the
# derivation's own generated index.html (#614), linking the other
# pages — not a byte-copy of options.html anymore.
cp -R result/share/doc/trollshell/. _site/
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v5
with:
Expand Down
95 changes: 29 additions & 66 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -125,70 +125,15 @@
description = "trollshell consent-gated agent-bridge broker CLI (#487)";
};

# Autogenerated `programs.trollshell.*` options reference (#533).
# Evaluate BOTH platform modules together: they share the
# module-common base, deduped by its stable `key`, so the merged
# option tree is the full cross-platform + NixOS + home-manager
# surface in one place. `nixosOptionsDoc` renders it to CommonMark,
# which we also wrap into a standalone HTML page via `cmark` (tiny —
# no pandoc/Haskell closure). `_module.check = false` stops the config
# bodies (which set options owned by nixpkgs/home-manager that we do
# not declare here) from tripping the unknown-option check; the doc
# only forces option *metadata*, never the config, so nothing
# platform-specific is actually built. This is pure evaluation — it
# does not compile the shell. Build + read with:
# Autogenerated `programs.trollshell.*` options reference (#533) +
# the plugin env-knob reference (#573/#614), both rendered onto one
# docs site. Its own file (nix/options-doc.nix) — see there for the
# full rationale — so the `checks` output below can build it too
# (#614) via the same `pkgs.callPackage` call rather than
# duplicating the derivation body. Build + read with:
# nix build .#options-doc
# $BROWSER result/share/doc/trollshell/options.html # or options.md
options-doc =
let
eval = pkgs.lib.evalModules {
specialArgs = { inherit pkgs; };
modules = [
{ _module.check = false; }
(import ./nix/hm-module.nix self)
(import ./nix/nixos-module.nix self)
];
};
doc = pkgs.nixosOptionsDoc {
inherit (eval) options;
transformOptions =
opt:
opt
// {
# Document only the shell's own options — hide the
# module-system internals (`_module.*`) the bare evalModules
# introduces. Preserve the original visibility (true /
# "shallow") for our options; force everything else invisible.
visible = if pkgs.lib.hasPrefix "programs.trollshell" opt.name then opt.visible else false;
# Point "Declared by" at this repo. Our `_file` values are
# repo-relative paths (e.g. "nix/module-common.nix"); without
# this, nixosOptionsDoc assumes they live under nixpkgs and
# links to github.com/NixOS/nixpkgs, which is wrong.
declarations = map (decl: {
name = decl;
url = "https://github.com/vibec0re/trollshell/blob/main/${decl}";
}) opt.declarations;
};
};
in
pkgs.runCommand "trollshell-options-doc"
{
nativeBuildInputs = [ pkgs.cmark ];
meta.description = "Autogenerated programs.trollshell options reference (#533)";
}
''
mkdir -p "$out/share/doc/trollshell"
cp ${doc.optionsCommonMark} "$out/share/doc/trollshell/options.md"
{
echo '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">'
echo '<meta name="viewport" content="width=device-width, initial-scale=1">'
echo '<title>trollshell — programs.trollshell options</title>'
echo '<style>body{max-width:55rem;margin:2rem auto;padding:0 1rem;font-family:system-ui,sans-serif;line-height:1.5}code{background:rgba(127,127,127,.15);padding:.1em .3em;border-radius:3px}pre{overflow-x:auto}h1,h2,h3{line-height:1.2}</style>'
echo '</head><body><h1>programs.trollshell options</h1>'
cmark --unsafe "$out/share/doc/trollshell/options.md"
echo '</body></html>'
} > "$out/share/doc/trollshell/options.html"
'';
# $BROWSER result/share/doc/trollshell/index.html
options-doc = pkgs.callPackage ./nix/options-doc.nix { inherit self pkgs; };
in
{
inherit
Expand Down Expand Up @@ -300,14 +245,32 @@
{
formatting = treefmt-eval.config.build.check self;

# `options-doc` (#533/#614): pure eval plus one tiny C binary
# (cmark-gfm) — no crane, nothing expensive — so there's no cost
# excuse for `options-doc.yml`'s actual render+copy pipeline to go
# unbuilt on every PR. Mirrors the `packages` output's binding (its
# own `let`, hence the separate `pkgs.callPackage` call rather than
# an `inherit` — same reason `trollshell` etc. below are
# recomputed here too instead of reused across outputs).
options-doc = pkgs.callPackage ./nix/options-doc.nix { inherit self pkgs; };

# `nix flake check` only *builds* the derivations listed in `checks`
# — it does not build `packages` just because they're evaluable.
# Without these two entries, CI (which runs flake check, not
# Without these entries, CI (which runs flake check, not
# `nix build`) can stay green while `nix build .#trollshell` or
# `.#trollshell-control-center` is actually broken — the release
# profile, `nix/package.nix`'s src filter, the assets derivation,
# and the wrapper derivations are never exercised. See #449.
inherit trollshell trollshell-control-center hytte-infobroker;
# and the wrapper derivations are never exercised. See #449. Same
# argument extends to `options-doc` (#614, bound above — already
# in this attrset, so it isn't repeated in this `inherit`): without
# it here, a build break in it (a bad cmark-gfm flag, a quoting
# bug) stays green and first shows up as a red Pages deploy after
# merge.
inherit
trollshell
trollshell-control-center
hytte-infobroker
;

# Lint the entire workspace with pedantic-clean Clippy. Reuses
# cargoArtifacts from the package build so dependencies aren't
Expand Down
8 changes: 6 additions & 2 deletions nix/module-common.nix
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,12 @@ self:
instead of per-plugin here).

The full inventory — swept from source, all 12 bundled
plugins including the ones with zero knobs — lives in
[`docs/plugin-env.md`](https://github.com/vibec0re/trollshell/blob/main/docs/plugin-env.md).
plugins including the ones with zero knobs — is published at
<https://vibec0re.github.io/trollshell/plugin-env.html>
(source: `docs/plugin-env.md`). An absolute URL rather than a
relative one because this description is also rendered by
`nixos-option` and `man home-configuration.nix`, where a
relative `plugin-env.html` link would resolve to nothing.

Precedence for a plugin that also reads a config file (e.g.
usage's `~/.config/trollshell/usage.toml`): environment (this
Expand Down
139 changes: 139 additions & 0 deletions nix/options-doc.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Autogenerated `programs.trollshell.*` options reference (#533), plus
# (#614) the hand-written per-plugin env-knob reference
# (`docs/plugin-env.md`, #573) rendered onto the same docs site so it stops
# being repo-only. Evaluate BOTH platform modules together: they share the
# module-common base, deduped by its stable `key`, so the merged option tree
# is the full cross-platform + NixOS + home-manager surface in one place.
# `nixosOptionsDoc` renders it to CommonMark; both it and `plugin-env.md` are
# wrapped into standalone HTML pages via the same `cmark-gfm` call (tiny — no
# pandoc/Haskell closure). The `-gfm` flavor over plain `cmark` is only so
# `plugin-env.md`'s pipe tables render as actual `<table>`s — plain
# CommonMark has no table syntax and would print the `|` characters
# literally. `_module.check = false` stops the config bodies (which set
# options owned by nixpkgs/home-manager that we do not declare here) from
# tripping the unknown-option check; the doc only forces option *metadata*,
# never the config, so nothing platform-specific is actually built. This is
# pure evaluation — it does not compile the shell. Build + read with:
# nix build .#options-doc
# $BROWSER result/share/doc/trollshell/index.html
#
# Its own file (rather than inlined per-output in flake.nix) so both the
# `packages` and `checks` outputs can `pkgs.callPackage` it without
# duplicating the derivation body — `checks` needs its own copy of this
# building (#614) for the same #449 reason `trollshell` et al. are
# recomputed in both outputs' `let` blocks rather than shared: each
# `forAllSystems` callback is its own scope, and `nix flake check` only
# *builds* what `checks` lists, not everything `packages` merely evaluates.
{
pkgs,
# The flake's own `self`, threaded through so the module-eval below can
# `import` the two platform modules — not a `pkgs` attribute, so it has to
# be passed explicitly rather than auto-filled by `callPackage`.
self,
}:
let
eval = pkgs.lib.evalModules {
specialArgs = { inherit pkgs; };
modules = [
{ _module.check = false; }
(import ./hm-module.nix self)
(import ./nixos-module.nix self)
];
};
doc = pkgs.nixosOptionsDoc {
inherit (eval) options;
transformOptions =
opt:
opt
// {
# Document only the shell's own options — hide the module-system
# internals (`_module.*`) the bare evalModules introduces. Preserve
# the original visibility (true / "shallow") for our options; force
# everything else invisible.
visible = if pkgs.lib.hasPrefix "programs.trollshell" opt.name then opt.visible else false;
# Point "Declared by" at this repo. Our `_file` values are
# repo-relative paths (e.g. "nix/module-common.nix"); without this,
# nixosOptionsDoc assumes they live under nixpkgs and links to
# github.com/NixOS/nixpkgs, which is wrong.
declarations = map (decl: {
name = decl;
url = "https://github.com/vibec0re/trollshell/blob/main/${decl}";
}) opt.declarations;
};
};
# The hand-written doc #614 is publishing. A plain repo-relative path (this
# file lives in nix/, so it's one level up from the repo root) — nix
# copies the single tracked file into the store, giving a store path just
# like `doc.optionsCommonMark` above.
pluginEnvMd = ../docs/plugin-env.md;
in
pkgs.runCommand "trollshell-options-doc"
{
nativeBuildInputs = [ pkgs.cmark-gfm ];
meta.description = "Autogenerated programs.trollshell options reference (#533) + plugin env-knob reference (#573/#614)";
}
''
mkdir -p "$out/share/doc/trollshell"
cp ${doc.optionsCommonMark} "$out/share/doc/trollshell/options.md"
cp ${pluginEnvMd} "$out/share/doc/trollshell/plugin-env.md"

# Shared page chrome — ONE style string for every page on the site
# (including the index landing page below) so a future tweak reaches all
# of them, rather than the landing page silently carrying its own
# divergent copy.
page_style='body{max-width:55rem;margin:2rem auto;padding:0 1rem;font-family:system-ui,sans-serif;line-height:1.5}code{background:rgba(127,127,127,.15);padding:.1em .3em;border-radius:3px}pre{overflow-x:auto}h1,h2,h3{line-height:1.2}table{border-collapse:collapse;width:100%}th,td{border:1px solid rgba(127,127,127,.35);padding:.4em .6em;text-align:left;vertical-align:top}nav.docs-nav{margin-bottom:1.5rem;font-size:.9em}li{margin:.5em 0}'

page_head() {
echo '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">'
echo '<meta name="viewport" content="width=device-width, initial-scale=1">'
echo "<title>$1</title>"
echo "<style>$page_style</style>"
echo '</head><body>'
}
page_foot() {
echo '</body></html>'
}

# `render_page TITLE MD_FILE [H1]` writes the finished HTML to stdout;
# --extension table is harmless on options.md (which has no pipe
# tables) so both pages go through the exact same cmark-gfm call. H1 is
# optional: options.md has no top-level heading of its own
# (nixosOptionsDoc starts straight at `##`), so it's injected here;
# plugin-env.md already opens with its own `# …` heading, so it's
# omitted for that page.
render_page() {
title="$1"
md="$2"
heading="$3"
page_head "$title"
echo '<nav class="docs-nav"><a href="index.html">&larr; trollshell docs</a></nav>'
if [ -n "$heading" ]; then
echo "<h1>$heading</h1>"
fi
cmark-gfm --unsafe --extension table "$md"
page_foot
}

render_page 'trollshell — programs.trollshell options' \
"$out/share/doc/trollshell/options.md" \
'programs.trollshell options' \
> "$out/share/doc/trollshell/options.html"

render_page 'trollshell — plugin environment-variable reference' \
"$out/share/doc/trollshell/plugin-env.md" \
"" \
> "$out/share/doc/trollshell/plugin-env.html"

# The root page (#614): a small landing page linking both documents,
# rather than a byte-copy of options.html now that there are two of
# them. Shares page_head/page_foot/page_style above rather than its own
# copy.
{
page_head 'trollshell docs'
echo '<h1>trollshell docs</h1><ul>'
echo '<li><a href="options.html">programs.trollshell options reference</a> — every Nix option the module exposes.</li>'
echo '<li><a href="plugin-env.html">Plugin environment-variable reference</a> — every env knob the bundled widget plugins read.</li>'
echo '</ul>'
page_foot
} > "$out/share/doc/trollshell/index.html"
''