Repository: ngi-nix/ngipkgs
Pull request #1001: overview: add devmode 
Author: login: eljamm
Created: 2025-05-05T14:30:40Z | Merged: 2025-05-05T16:32:52Z

```shellSession
nix-shell -A shell --run devmode
```
or
```shellSession
nix develop --command devmode
```


Diff:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bdc0bac15..e1e8ab5d0 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -240,6 +240,19 @@ The following information is needed to [open an issue for a new NGI project](htt
 1. Run the Nix code formatter with `nix fmt`
 1. Commit your changes and [create a new PR](#how-to-create-pull-requests-to-ngipkgs)
 
+## Running and testing the overview locally
+
+1. To run a local version of the overview, enter a development shell with:
+
+   ```
+   nix-shell -A shell
+   ```
+
+2. Execute the `devmode` command.
+3. The overview will automatically open in your default browser.
+
+If you make any changes to the overview while running `devmode`, the server will automatically be reloaded with the new contents in a few seconds, after you save.
+
 <!-- TODO: Add details about how to do more production-like deployments that require non-default config options. -->
 
 <!-- TODO: How to import all of NGIpkgs as an input to an existing NixOS configuration, in order to deploy a service alongside other services on the same virtual or physical machine. -->
diff --git a/default.nix b/default.nix
index 6ec4afe33..1240f1d4a 100644
--- a/default.nix
+++ b/default.nix
@@ -22,6 +22,46 @@ rec {
     sources
     ;
 
+  rawNixosModules = lib'.flattenAttrs "." (
+    with lib;
+    foldl recursiveUpdate { } (attrValues (mapAttrs (_: project: project.nixos.modules) projects))
+  );
+
+  nixosModules = {
+    # The default module adds the default overlay on top of Nixpkgs.
+    # This is so that `ngipkgs` can be used alongside `nixpkgs` in a configuration.
+    default.nixpkgs.overlays = [ overlays.default ];
+  } // rawNixosModules;
+
+  optionsDoc =
+    let
+      nixosSystem =
+        args:
+        import (sources.nixpkgs + "/nixos/lib/eval-config.nix") (
+          {
+            inherit lib;
+            system = null;
+          }
+          // args
+        );
+    in
+    pkgs.nixosOptionsDoc {
+      options =
+        (nixosSystem {
+          inherit system;
+          modules = [
+            {
+              networking = {
+                domain = "invalid";
+                hostName = "options";
+              };
+
+              system.stateVersion = "23.05";
+            }
+          ] ++ lib.attrValues nixosModules;
+        }).options;
+    };
+
   # TODO: we should be exporting our custom functions as `lib`, but refactoring
   # this to use `pkgs.lib` everywhere is a lot of movement
   lib' = {
@@ -206,7 +246,12 @@ rec {
     mapAttrs (name: project: hydrate project) raw-projects;
 
   shell = pkgs.mkShellNoCC {
-    packages = [ ];
+    packages = [
+      # live overview watcher
+      (pkgs.devmode.override {
+        buildArgs = "${toString ./overview/devmode.nix} --show-trace";
+      })
+    ];
   };
 
   demo = import ./overview/demo {
diff --git a/flake.nix b/flake.nix
index 8b87460e6..932501f79 100644
--- a/flake.nix
+++ b/flake.nix
@@ -45,34 +45,12 @@
         recursiveUpdate
         ;
 
-      nixosSystem =
-        args:
-        import (nixpkgs + "/nixos/lib/eval-config.nix") (
-          {
-            inherit lib;
-            system = null;
-          }
-          // args
-        );
-
       overlay = classic'.overlays.default;
 
       # Note that modules and examples are system-agnostic, so import them first.
       # TODO: get rid of these, it's extremely confusing to import the seemingly same thing twice
       rawNgiProjects = classic'.projects;
 
-      rawNixosModules = lib'.flattenAttrs "." (
-        lib.foldl recursiveUpdate { } (
-          attrValues (mapAttrs (_: project: project.nixos.modules) rawNgiProjects)
-        )
-      );
-
-      nixosModules = {
-        # The default module adds the default overlay on top of Nixpkgs.
-        # This is so that `ngipkgs` can be used alongside `nixpkgs` in a configuration.
-        default.nixpkgs.overlays = [ overlay ];
-      } // rawNixosModules;
-
       toplevel = machine: machine.config.system.build.toplevel;
 
       # Finally, define the system-agnostic outputs.
@@ -81,7 +59,7 @@
           makemake = import ./infra/makemake { inherit inputs; };
         };
 
-        inherit nixosModules;
+        inherit (classic') nixosModules;
 
         # Overlays a package set (e.g. Nixpkgs) with the packages defined in this flake.
         overlays.default = overlay;
@@ -95,26 +73,9 @@
             inherit system;
           };
 
-          inherit (classic) pkgs ngipkgs;
+          inherit (classic) pkgs ngipkgs optionsDoc;
 
           ngiProjects = classic.projects;
-
-          optionsDoc = pkgs.nixosOptionsDoc {
-            options =
-              (nixosSystem {
-                inherit system;
-                modules = [
-                  {
-                    networking = {
-                      domain = "invalid";
-                      hostName = "options";
-                    };
-
-                    system.stateVersion = "23.05";
-                  }
-                ] ++ attrValues nixosModules;
-              }).options;
-          };
         in
         rec {
           packages = ngipkgs // {
@@ -197,8 +158,7 @@
 
           devShells.default = pkgs.mkShell {
             inherit (checks."infra/pre-commit") shellHook;
-            # TODO use devmode
-            buildInputs = checks."infra/pre-commit".enabledPackages ++ [ pkgs.darkhttpd ];
+            buildInputs = checks."infra/pre-commit".enabledPackages ++ classic.shell.nativeBuildInputs;
           };
 
           formatter = pkgs.writeShellApplication {
diff --git a/overview/default.nix b/overview/default.nix
index 726c732fe..54bb1d43d 100644
--- a/overview/default.nix
+++ b/overview/default.nix
@@ -1,3 +1,4 @@
+# NOTE: run a live overview watcher by executing `devmode`, inside a nix shell
 {
   lib,
   lib',
diff --git a/overview/devmode.nix b/overview/devmode.nix
new file mode 100644
index 000000000..de3888d42
--- /dev/null
+++ b/overview/devmode.nix
@@ -0,0 +1,19 @@
+let
+  default = import ../. { };
+in
+import ./. {
+  inherit (default)
+    lib
+    lib'
+    system
+    projects
+    ;
+  pkgs = with default; pkgs // ngipkgs;
+  nixpkgs = default.sources.nixpkgs;
+  options = default.optionsDoc.optionsNix;
+  self = {
+    lastModifiedDate = "315532800"; # linux epoch
+    rev = "main";
+    shortRev = "main";
+  };
+}


Reviews:
user: login: eljamm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T14:37:20Z
user: login: fricklerhandwerk
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T14:46:31Z
user: login: eljamm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T15:22:36Z
user: login: eljamm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T15:33:33Z
user: login: fricklerhandwerk
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T15:43:06Z
user: login: fricklerhandwerk
body: This works but it also does

```
downloadable font: download failed (font-family: "IBM Plex Sans" style:normal weight:400 stretch:100 src index:0): status=2147746065 source: http://127.0.0.1:8080/fonts/IBMPlexSans-Regular.woff2 
```

We need to eventually figure out how to do the linking of paths properly, it should not be a thing to care about as a contributor.
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T15:48:20Z
user: login: eljamm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T15:54:22Z
user: login: fricklerhandwerk
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T15:57:52Z
user: login: fricklerhandwerk
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T16:01:33Z
user: login: eljamm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T16:06:26Z
user: login: fricklerhandwerk
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T16:32:36Z

Review comments:
diff_hunk: @@ -22,6 +22,51 @@ rec {
     sources
     ;
 
+  rawNixosModules = lib'.flattenAttrs "." (
+    with lib;
+    foldl recursiveUpdate { } (attrValues (mapAttrs (_: project: project.nixos.modules) projects))
+  );
+
+  nixosModules = {
+    # The default module adds the default overlay on top of Nixpkgs.
+    # This is so that `ngipkgs` can be used alongside `nixpkgs` in a configuration.
+    default.nixpkgs.overlays = [ overlays.default ];
+  } // rawNixosModules;
+
+  optionsDoc =
+    let
+      nixosSystem =
+        args:
+        import (sources.nixpkgs + "/nixos/lib/eval-config.nix") (
+          {
+            inherit lib;
+            system = null;
+          }
+          // args
+        );
+    in
+    pkgs.nixosOptionsDoc {
+      options =
+        (nixosSystem {
+          inherit system;
+          modules = [
+            {
+              networking = {
+                domain = "invalid";
+                hostName = "options";
+              };
+
+              system.stateVersion = "23.05";
+            }
+          ] ++ lib.attrValues nixosModules;
+        }).options;
+    };
+
+  # live overview watcher
+  devmode = pkgs.devmode.override {
path: default.nix
user: login: eljamm
body: It appears that the server does not find fonts?

```
server  GET /fonts/IBMPlexSans-Text.woff2 404 0.484 ms - 167
server  GET /fonts/IBMPlexSans-Bold.woff2 404 0.312 ms - 167
server  GET /fonts/IBMPlexSans-Medium.woff2 404 0.355 ms - 169
server  GET /fonts/IBMPlexMono-Text.woff2 404 0.446 ms - 167
server  GET /fonts/IBMPlexSans-Text.otf 404 0.650 ms - 165
server  GET /fonts/IBMPlexSans-Bold.otf 404 0.783 ms - 165
server  GET /fonts/IBMPlexSans-Medium.otf 404 0.935 ms - 167
server  GET /fonts/IBMPlexMono-Text.otf 404 0.857 ms - 165
server  GET /fonts/IBMPlexSans-Regular.woff2 404 0.414 ms - 170
server  GET /fonts/IBMPlexMono-Regular.woff2 404 0.429 ms - 170
server  GET /fonts/IBMPlexSans-Regular.otf 404 0.702 ms - 168
server  GET /fonts/IBMPlexMono-Regular.otf 404 0.794 ms - 168
```
created_at: 2025-05-05T14:37:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,6 +22,51 @@ rec {
     sources
     ;
 
+  rawNixosModules = lib'.flattenAttrs "." (
+    with lib;
+    foldl recursiveUpdate { } (attrValues (mapAttrs (_: project: project.nixos.modules) projects))
+  );
+
+  nixosModules = {
+    # The default module adds the default overlay on top of Nixpkgs.
+    # This is so that `ngipkgs` can be used alongside `nixpkgs` in a configuration.
+    default.nixpkgs.overlays = [ overlays.default ];
+  } // rawNixosModules;
+
+  optionsDoc =
+    let
+      nixosSystem =
+        args:
+        import (sources.nixpkgs + "/nixos/lib/eval-config.nix") (
+          {
+            inherit lib;
+            system = null;
+          }
+          // args
+        );
+    in
+    pkgs.nixosOptionsDoc {
+      options =
+        (nixosSystem {
+          inherit system;
+          modules = [
+            {
+              networking = {
+                domain = "invalid";
+                hostName = "options";
+              };
+
+              system.stateVersion = "23.05";
+            }
+          ] ++ lib.attrValues nixosModules;
+        }).options;
+    };
+
+  # live overview watcher
+  devmode = pkgs.devmode.override {
path: default.nix
user: login: fricklerhandwerk
body: @Erictapen?
created_at: 2025-05-05T14:46:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,6 +22,51 @@ rec {
     sources
     ;
 
+  rawNixosModules = lib'.flattenAttrs "." (
+    with lib;
+    foldl recursiveUpdate { } (attrValues (mapAttrs (_: project: project.nixos.modules) projects))
+  );
+
+  nixosModules = {
+    # The default module adds the default overlay on top of Nixpkgs.
+    # This is so that `ngipkgs` can be used alongside `nixpkgs` in a configuration.
+    default.nixpkgs.overlays = [ overlays.default ];
+  } // rawNixosModules;
+
+  optionsDoc =
+    let
+      nixosSystem =
+        args:
+        import (sources.nixpkgs + "/nixos/lib/eval-config.nix") (
+          {
+            inherit lib;
+            system = null;
+          }
+          // args
+        );
+    in
+    pkgs.nixosOptionsDoc {
+      options =
+        (nixosSystem {
+          inherit system;
+          modules = [
+            {
+              networking = {
+                domain = "invalid";
+                hostName = "options";
+              };
+
+              system.stateVersion = "23.05";
+            }
+          ] ++ lib.attrValues nixosModules;
+        }).options;
+    };
+
+  # live overview watcher
+  devmode = pkgs.devmode.override {
path: default.nix
user: login: eljamm
body: I think this happens because symlinks are currently [not being copied with rsync](https://github.com/NixOS/nixpkgs/blob/2bbb3ff841a34848018c1f0c42faf5630c1a9d63/pkgs/by-name/de/devmode/package.nix#L53-L59) in devmode.
created_at: 2025-05-05T15:22:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,6 +22,51 @@ rec {
     sources
     ;
 
+  rawNixosModules = lib'.flattenAttrs "." (
+    with lib;
+    foldl recursiveUpdate { } (attrValues (mapAttrs (_: project: project.nixos.modules) projects))
+  );
+
+  nixosModules = {
+    # The default module adds the default overlay on top of Nixpkgs.
+    # This is so that `ngipkgs` can be used alongside `nixpkgs` in a configuration.
+    default.nixpkgs.overlays = [ overlays.default ];
+  } // rawNixosModules;
+
+  optionsDoc =
+    let
+      nixosSystem =
+        args:
+        import (sources.nixpkgs + "/nixos/lib/eval-config.nix") (
+          {
+            inherit lib;
+            system = null;
+          }
+          // args
+        );
+    in
+    pkgs.nixosOptionsDoc {
+      options =
+        (nixosSystem {
+          inherit system;
+          modules = [
+            {
+              networking = {
+                domain = "invalid";
+                hostName = "options";
+              };
+
+              system.stateVersion = "23.05";
+            }
+          ] ++ lib.attrValues nixosModules;
+        }).options;
+    };
+
+  # live overview watcher
+  devmode = pkgs.devmode.override {
path: default.nix
user: login: eljamm
body: Tested with the fix in https://github.com/NixOS/nixpkgs/pull/404401 and can confirm that fonts are being copied, detected and loaded.
created_at: 2025-05-05T15:33:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -206,7 +246,12 @@ rec {
     mapAttrs (name: project: hydrate project) raw-projects;
 
   shell = pkgs.mkShellNoCC {
-    packages = [ ];
+    packages = [
+      # live overview watcher
+      (pkgs.devmode.override {
+        buildArgs = toString ./overview/devmode.nix;
path: default.nix
user: login: fricklerhandwerk
body: ```suggestion
        buildArgs = "${toString ./overview/devmode.nix} --show-trace";
```

This does help a bit with debugging
created_at: 2025-05-05T15:43:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,19 @@
+let
+  default = import ../. { };
+in
+import ./. {
+  inherit (default)
+    lib
+    lib'
+    system
+    projects
+    ;
+  pkgs = with default; pkgs // ngipkgs;
+  nixpkgs = default.sources.nixpkgs;
+  options = default.optionsDoc.optionsNix;
+  self = {
path: overview/devmode.nix
user: login: fricklerhandwerk
body: Oof. (not actionable)
created_at: 2025-05-05T15:47:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,19 @@
+let
+  default = import ../. { };
+in
+import ./. {
+  inherit (default)
+    lib
+    lib'
+    system
+    projects
+    ;
+  pkgs = with default; pkgs // ngipkgs;
+  nixpkgs = default.sources.nixpkgs;
+  options = default.optionsDoc.optionsNix;
+  self = {
path: overview/devmode.nix
user: login: eljamm
body: > We need to eventually figure out how to do the linking of paths properly

I can't reproduce the message you've posted, but the fonts should work with https://github.com/NixOS/nixpkgs/pull/404401

> Oof. (not actionable)

Do you mean how this is called or the `self` fallback?
created_at: 2025-05-05T15:54:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,19 @@
+let
+  default = import ../. { };
+in
+import ./. {
+  inherit (default)
+    lib
+    lib'
+    system
+    projects
+    ;
+  pkgs = with default; pkgs // ngipkgs;
+  nixpkgs = default.sources.nixpkgs;
+  options = default.optionsDoc.optionsNix;
+  self = {
path: overview/devmode.nix
user: login: fricklerhandwerk
body: Yes, this whole `self` stuff... Although this probably can be resolved mid to long term with an equivalent of flake-parts. As said, not actionable now.
created_at: 2025-05-05T15:57:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,19 @@
+let
+  default = import ../. { };
+in
+import ./. {
+  inherit (default)
+    lib
+    lib'
+    system
+    projects
+    ;
+  pkgs = with default; pkgs // ngipkgs;
+  nixpkgs = default.sources.nixpkgs;
+  options = default.optionsDoc.optionsNix;
+  self = {
path: overview/devmode.nix
user: login: fricklerhandwerk
body: > I can't reproduce the message you've posted, but the fonts should work with https://github.com/NixOS/nixpkgs/pull/404401

How's that? The fonts aren't symlinked. It's that the CSS refers to them by absolute path
created_at: 2025-05-05T16:01:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,19 @@
+let
+  default = import ../. { };
+in
+import ./. {
+  inherit (default)
+    lib
+    lib'
+    system
+    projects
+    ;
+  pkgs = with default; pkgs // ngipkgs;
+  nixpkgs = default.sources.nixpkgs;
+  options = default.optionsDoc.optionsNix;
+  self = {
path: overview/devmode.nix
user: login: eljamm
body: The `fonts` directory is a symlink. Without the `--links` flag, rsync will not copy it in the [build-and-copy](https://github.com/NixOS/nixpkgs/blob/3730d8a308f94996a9ba7c7138ede69c1b9ac4ae/pkgs/by-name/de/devmode/package.nix#L42) phase.

```shellSession
$ nix build .#overview
$ ls -l result/
lrwxrwxrwx   - root  1 Jan  1970  fonts -> /nix/store/05wkyrdmdr1n7mmlv6mpasbnxzvkqq3g-fonts
.r--r--r-- 18k root  1 Jan  1970  index.html
dr-xr-xr-x   - root  1 Jan  1970  project
.r--r--r-- 14k root  1 Jan  1970  style.css
```

With the change, the `fonts` symlink is copied and the server successfully finds the fonts:

```shellSession
server  GET /fonts/IBMPlexSans-Text.woff2 200 1.693 ms - 67928
server  GET /fonts/IBMPlexSans-Bold.woff2 200 1.985 ms - 64248
server  GET /fonts/IBMPlexSans-Medium.woff2 200 2.682 ms - 68732
server  GET /fonts/IBMPlexMono-Text.woff2 200 2.124 ms - 48588
```
created_at: 2025-05-05T16:06:26Z
author_association: CONTRIBUTOR

PR comments:
user: login: fricklerhandwerk
created_at: 2025-05-05T15:48:59Z
body: Ah and @eljamm please add a usage note to `CONTRIBUTING.md`
author_association: CONTRIBUTOR
user: login: erictapen
created_at: 2025-05-05T21:25:27Z
body: Super useful to have that approachable and documented now!
author_association: CONTRIBUTOR