From f21ace82ff59d896914de343490ee15550ce840d Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sun, 1 Mar 2026 12:59:10 -0800 Subject: [PATCH] C2(authentik-source-build): impl web UI derivation Two-stage Nix build for the authentik web frontend: - webui-deps.nix: FOD for npm dependencies (platform-specific hash) - webui.nix: esbuild/wireit build + rollup SFE, outputs dist/ and authentik/ Verified on ringtail: build completes in ~33s, output has correct structure. Co-Authored-By: Claude Opus 4.6 --- containers/authentik/test-build.nix | 4 ++ containers/authentik/webui-deps.nix | 51 ++++++++++++++++++ containers/authentik/webui.nix | 80 +++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 containers/authentik/webui-deps.nix create mode 100644 containers/authentik/webui.nix diff --git a/containers/authentik/test-build.nix b/containers/authentik/test-build.nix index aaeaced..ee9711a 100644 --- a/containers/authentik/test-build.nix +++ b/containers/authentik/test-build.nix @@ -7,6 +7,8 @@ # nix-build test-build.nix -A client-go --extra-experimental-features 'nix-command flakes' # nix-build test-build.nix -A client-ts --extra-experimental-features 'nix-command flakes' # nix-build test-build.nix -A authentik-server --extra-experimental-features 'nix-command flakes' +# nix-build test-build.nix -A webui-deps --extra-experimental-features 'nix-command flakes' +# nix-build test-build.nix -A webui --extra-experimental-features 'nix-command flakes' let pkgs = (builtins.getFlake "nixpkgs").legacyPackages.x86_64-linux; sources = import ./sources.nix { inherit pkgs; }; @@ -17,4 +19,6 @@ in client-go = import ./client-go.nix { inherit pkgs sources; }; client-ts = import ./client-ts.nix { inherit pkgs sources; }; authentik-server = import ./authentik-server.nix { inherit pkgs sources; }; + webui-deps = import ./webui-deps.nix { inherit pkgs sources; }; + webui = import ./webui.nix { inherit pkgs sources; }; } diff --git a/containers/authentik/webui-deps.nix b/containers/authentik/webui-deps.nix new file mode 100644 index 0000000..5364a4b --- /dev/null +++ b/containers/authentik/webui-deps.nix @@ -0,0 +1,51 @@ +# Fixed-output derivation for authentik web UI npm dependencies +# +# Runs `npm ci` in the web/ directory to fetch all Node.js dependencies. +# This is a FOD (fixed-output derivation) so it has network access during build +# but the output hash must match exactly. +# +# The output hash is platform-specific because npm downloads platform-specific +# native binaries for esbuild, rollup, and SWC. +# +# Workspace packages (under web/packages/*) have their own node_modules, +# so we collect all node_modules directories via find. +# +# Output: all node_modules directories from the web/ tree +{ pkgs ? import { }, sources ? import ./sources.nix { inherit pkgs; } }: + +pkgs.stdenvNoCC.mkDerivation { + pname = "authentik-webui-deps"; + inherit (sources) version src meta; + + sourceRoot = "${sources.src.name}/web"; + + outputHash = + { + "x86_64-linux" = "sha256-+4cWvFuixCcO7P+z701/0H+Ah/Z5sbLNsdx2Uowqwf4="; + } + .${pkgs.stdenvNoCC.hostPlatform.system} + or (throw "authentik-webui-deps: unsupported host platform ${pkgs.stdenvNoCC.hostPlatform.system}"); + outputHashMode = "recursive"; + + nativeBuildInputs = with pkgs; [ + nodejs_24 + cacert + ]; + + buildPhase = '' + npm ci --cache ./cache --ignore-scripts + rm -r ./cache node_modules/.package-lock.json + ''; + + # Workspace packages install dependencies into separate node_modules + # directories with symlinks between them — copy all of them + installPhase = '' + mkdir $out + find -type d -name node_modules -prune -print \ + -exec mkdir -p $out/{} \; \ + -exec cp -rT {} $out/{} \; + ''; + + dontCheckForBrokenSymlinks = true; + dontPatchShebangs = true; +} diff --git a/containers/authentik/webui.nix b/containers/authentik/webui.nix new file mode 100644 index 0000000..43b4177 --- /dev/null +++ b/containers/authentik/webui.nix @@ -0,0 +1,80 @@ +# Authentik web UI build +# +# Builds the Lit-based TypeScript frontend from the web/ directory. +# Uses esbuild (via wireit) for the main build and rollup for the SFE +# (Standalone Frontend Engine) sub-package. +# +# Inputs: +# - webui-deps: FOD with npm dependencies (node_modules trees) +# - client-ts: generated TypeScript API client from schema.yml +# +# Output: +# $out/dist/ esbuild bundle (admin, user, flow, rac, etc.) +# $out/authentik/ static icons for authentication sources/connectors +{ pkgs ? import { } +, sources ? import ./sources.nix { inherit pkgs; } +, webui-deps ? import ./webui-deps.nix { inherit pkgs sources; } +, client-ts ? import ./client-ts.nix { inherit pkgs sources; } +}: + +pkgs.stdenvNoCC.mkDerivation { + pname = "authentik-webui"; + inherit (sources) version src meta; + + sourceRoot = "${sources.src.name}/web"; + + nativeBuildInputs = with pkgs; [ + nodejs_24 + ]; + + # Hardcode version string instead of importing from package.json + # (the JSON import-with-assertion may not resolve in the Nix build sandbox) + postPatch = '' + substituteInPlace packages/core/version/node.js \ + --replace-fail \ + 'import PackageJSON from "../../../../package.json" with { type: "json" };' \ + "" \ + --replace-fail \ + '(PackageJSON.version);' \ + '"${sources.version}";' + ''; + + buildPhase = '' + runHook preBuild + + # Copy node_modules from the FOD into the build tree + buildRoot=$PWD + pushd ${webui-deps} + find -type d -name node_modules -prune -print \ + -exec cp -rT {} $buildRoot/{} \; + popd + + # Replace the npm-published @goauthentik/api with our generated client + chmod -R +w node_modules/@goauthentik + rm -rf node_modules/@goauthentik/api + ln -sn ${client-ts} node_modules/@goauthentik/api + + # Patch shebangs on build tool binaries so they can run in the sandbox + pushd node_modules/.bin + for tool in rollup wireit lit-localize esbuild; do + [ -L "$tool" ] && patchShebangs "$(readlink "$tool")" 2>/dev/null || true + done + popd + + npm run build + npm run build:sfe + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + mkdir $out + cp -r dist $out/dist + cp -r authentik $out/authentik + runHook postInstall + ''; + + NODE_ENV = "production"; + NODE_OPTIONS = "--openssl-legacy-provider"; +}