initial commit
9 files changed,  +482, -0
A .gitignore
+8, -0
1@@ -0,0 +1,8 @@
2+# Devenv
3+.devenv*
4+devenv.local.nix
5+devenv.local.yaml
6+result
7+
8+# direnv
9+.direnv
A README.md
+67, -0
 1@@ -0,0 +1,67 @@
 2+# devkit
 3+
 4+Shared [devenv](https://devenv.sh) module: my personal dev tools (`bug`,
 5+`secrets`, `local-code-review`), a jujutsu/bash toolchain, and the customized
 6+interactive bash environment (fish-like completion, history search, etc.).
 7+
 8+Importing devkit keeps every project's shell identical and lets a single edit
 9+here propagate to all of them.
10+
11+## Use it in a project
12+
13+devenv.yaml:
14+
15+```yaml
16+inputs:
17+  nixpkgs:
18+    url: github:cachix/devenv-nixpkgs/rolling
19+  # devenv#2205: an imported input's devenv.yaml is NOT evaluated, so the tool
20+  # inputs must be declared here even though devkit wires the packages.
21+  bug:
22+    url: git+https://git.kilimanjaro.io/bug
23+  local-code-review:
24+    url: git+https://git.kilimanjaro.io/local-code-review
25+  secrets:
26+    url: git+https://git.kilimanjaro.io/secrets
27+  devkit:
28+    url: path:../devkit        # sibling-repo layout, or a git+https URL
29+    flake: false
30+imports:
31+  - devkit
32+```
33+
34+That's the whole wiring. A project's own `devenv.nix` only needs its
35+project-specific packages/env; devkit's `enterShell` is concatenated with the
36+project's.
37+
38+## Live updates (why `path:`)
39+
40+With a `path:` input, edits to `devenv.nix` take effect on the **next**
41+`devenv shell` — no `devenv update`. (Verified on devenv 2.4.0.)
42+
43+## Promoting to a shared git input
44+
45+When the module stabilizes, push it to `git+https://git.kilimanjaro.io/devenv-base`
46+and switch each project's `devkit.url` to that. Then changes are pulled with
47+`devenv update devkit` per project (pinned in `devenv.lock`, reproducible).
48+
49+## New project scaffold
50+
51+`devenv init` has no template flag (devenv #1718); its `--from` flag only
52+affects `devenv shell`/`test`/`build`. Use the Nix flake template instead:
53+
54+```sh
55+mkdir ~/projects/newthing && cd ~/projects/newthing
56+nix flake init -t path:/home/btburke/projects/devkit   # copies devenv.nix + devenv.yaml (+ .envrc)
57+```
58+
59+(Once pushed: `nix flake init -t git+https://git.kilimanjaro.io/devenv-base`.)
60+
61+The scaffold's `devenv.yaml` uses `path:../devkit`, so the new project must be
62+a sibling of devkit under `~/projects` — adjust otherwise.
63+
64+## Gotchas
65+
66+- Not for the tool repos themselves (`bug`, `secrets`, `local-code-review`):
67+  importing devkit there would be circular.
68+- devkit's own `devenv.yaml` is only for standalone use in this directory.
A devenv.lock
+238, -0
  1@@ -0,0 +1,238 @@
  2+{
  3+  "nodes": {
  4+    "bug": {
  5+      "inputs": {
  6+        "flake-utils": "flake-utils",
  7+        "nixpkgs": "nixpkgs"
  8+      },
  9+      "locked": {
 10+        "lastModified": 1789299135,
 11+        "narHash": "sha256-eU2EKLWUG7+LOdd6xlLELcXaK9yGyS4Hi1NWWuJPyj0=",
 12+        "ref": "refs/heads/main",
 13+        "rev": "c150b552f4b084877dd7386a5543934d7ad0c7e9",
 14+        "revCount": 12,
 15+        "type": "git",
 16+        "url": "https://git.kilimanjaro.io/bug"
 17+      },
 18+      "original": {
 19+        "type": "git",
 20+        "url": "https://git.kilimanjaro.io/bug"
 21+      }
 22+    },
 23+    "devenv": {
 24+      "locked": {
 25+        "dir": "src/modules",
 26+        "lastModified": 1792071910,
 27+        "narHash": "sha256-DUCfod12b6e/r9FCjE3+x7mJlVEmIpC10BZKGz+Nrsw=",
 28+        "owner": "cachix",
 29+        "repo": "devenv",
 30+        "rev": "1566050ec14ce238983376e118af9fb220454d39",
 31+        "type": "github"
 32+      },
 33+      "original": {
 34+        "dir": "src/modules",
 35+        "owner": "cachix",
 36+        "repo": "devenv",
 37+        "type": "github"
 38+      }
 39+    },
 40+    "flake-utils": {
 41+      "inputs": {
 42+        "systems": "systems"
 43+      },
 44+      "locked": {
 45+        "lastModified": 1731533236,
 46+        "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
 47+        "owner": "numtide",
 48+        "repo": "flake-utils",
 49+        "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
 50+        "type": "github"
 51+      },
 52+      "original": {
 53+        "owner": "numtide",
 54+        "repo": "flake-utils",
 55+        "type": "github"
 56+      }
 57+    },
 58+    "flake-utils_2": {
 59+      "inputs": {
 60+        "systems": "systems_2"
 61+      },
 62+      "locked": {
 63+        "lastModified": 1731533236,
 64+        "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
 65+        "owner": "numtide",
 66+        "repo": "flake-utils",
 67+        "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
 68+        "type": "github"
 69+      },
 70+      "original": {
 71+        "owner": "numtide",
 72+        "repo": "flake-utils",
 73+        "type": "github"
 74+      }
 75+    },
 76+    "local-code-review": {
 77+      "inputs": {
 78+        "nixpkgs": "nixpkgs_2"
 79+      },
 80+      "locked": {
 81+        "lastModified": 1790664642,
 82+        "narHash": "sha256-Pl7zVsnoxxrbrg/up/5Ue2a9ajtROQptM4AOuo7AYts=",
 83+        "ref": "refs/heads/main",
 84+        "rev": "95ae244a839007c6aa89fa5230be193d878ee56e",
 85+        "revCount": 17,
 86+        "type": "git",
 87+        "url": "https://git.kilimanjaro.io/local-code-review"
 88+      },
 89+      "original": {
 90+        "type": "git",
 91+        "url": "https://git.kilimanjaro.io/local-code-review"
 92+      }
 93+    },
 94+    "nixpkgs": {
 95+      "locked": {
 96+        "lastModified": 1789149629,
 97+        "narHash": "sha256-H6GwaZzZf+4npqv0tph94w9tZddSjFjmQrVsW0z78uk=",
 98+        "owner": "NixOS",
 99+        "repo": "nixpkgs",
100+        "rev": "eaad089433ca2bb662274377d33df3d0e51ef28b",
101+        "type": "github"
102+      },
103+      "original": {
104+        "owner": "NixOS",
105+        "ref": "nixos-unstable",
106+        "repo": "nixpkgs",
107+        "type": "github"
108+      }
109+    },
110+    "nixpkgs-src": {
111+      "flake": false,
112+      "locked": {
113+        "lastModified": 1791487292,
114+        "narHash": "sha256-tqM7xDEsaeK1p6VvRygeZ9LkQRpUUkbRT02BHv6hnTM=",
115+        "owner": "NixOS",
116+        "repo": "nixpkgs",
117+        "rev": "8edc0c72e3a38faf5434e40d1f19431125fe4b30",
118+        "type": "github"
119+      },
120+      "original": {
121+        "owner": "NixOS",
122+        "ref": "nixpkgs-unstable",
123+        "repo": "nixpkgs",
124+        "type": "github"
125+      }
126+    },
127+    "nixpkgs_2": {
128+      "locked": {
129+        "lastModified": 1790323409,
130+        "narHash": "sha256-VVTPf+Hyd5ebpjBMHmrLMSBIeW6ls48Bqtosj7CNKLA=",
131+        "owner": "NixOS",
132+        "repo": "nixpkgs",
133+        "rev": "e94cb152ed51bd6e24eb4a41f1460252beb52cd2",
134+        "type": "github"
135+      },
136+      "original": {
137+        "owner": "NixOS",
138+        "ref": "nixos-unstable",
139+        "repo": "nixpkgs",
140+        "type": "github"
141+      }
142+    },
143+    "nixpkgs_3": {
144+      "inputs": {
145+        "nixpkgs-src": "nixpkgs-src"
146+      },
147+      "locked": {
148+        "lastModified": 1791812882,
149+        "narHash": "sha256-4AdYH2HoD21mys5lk9NoDcM2Z7iPeEUjxJeGE3Pw278=",
150+        "owner": "cachix",
151+        "repo": "devenv-nixpkgs",
152+        "rev": "760b85220489f69855e6df78a6d1dad5873f8bf6",
153+        "type": "github"
154+      },
155+      "original": {
156+        "owner": "cachix",
157+        "ref": "rolling",
158+        "repo": "devenv-nixpkgs",
159+        "type": "github"
160+      }
161+    },
162+    "nixpkgs_4": {
163+      "locked": {
164+        "lastModified": 1790578696,
165+        "narHash": "sha256-ZoxIApko70jCdbH3l20HWXOBaT2HZd87orzd2yJ9dVE=",
166+        "owner": "NixOS",
167+        "repo": "nixpkgs",
168+        "rev": "7a0f122f5090cf4c2ade2a13a0e229d4e19ba71f",
169+        "type": "github"
170+      },
171+      "original": {
172+        "owner": "NixOS",
173+        "ref": "nixos-unstable",
174+        "repo": "nixpkgs",
175+        "type": "github"
176+      }
177+    },
178+    "root": {
179+      "inputs": {
180+        "bug": "bug",
181+        "devenv": "devenv",
182+        "local-code-review": "local-code-review",
183+        "nixpkgs": "nixpkgs_3",
184+        "secrets": "secrets"
185+      }
186+    },
187+    "secrets": {
188+      "inputs": {
189+        "flake-utils": "flake-utils_2",
190+        "nixpkgs": "nixpkgs_4"
191+      },
192+      "locked": {
193+        "lastModified": 1790680131,
194+        "narHash": "sha256-p3xPff0d3O4T8AW0Zbv7sZDZcJk61f5LEHRKcSTRF58=",
195+        "ref": "refs/heads/main",
196+        "rev": "96a2b2da934d769c76c19073e89e19e231964868",
197+        "revCount": 1,
198+        "type": "git",
199+        "url": "https://git.kilimanjaro.io/secrets"
200+      },
201+      "original": {
202+        "type": "git",
203+        "url": "https://git.kilimanjaro.io/secrets"
204+      }
205+    },
206+    "systems": {
207+      "locked": {
208+        "lastModified": 1681028828,
209+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
210+        "owner": "nix-systems",
211+        "repo": "default",
212+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
213+        "type": "github"
214+      },
215+      "original": {
216+        "owner": "nix-systems",
217+        "repo": "default",
218+        "type": "github"
219+      }
220+    },
221+    "systems_2": {
222+      "locked": {
223+        "lastModified": 1681028828,
224+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
225+        "owner": "nix-systems",
226+        "repo": "default",
227+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
228+        "type": "github"
229+      },
230+      "original": {
231+        "owner": "nix-systems",
232+        "repo": "default",
233+        "type": "github"
234+      }
235+    }
236+  },
237+  "root": "root",
238+  "version": 7
239+}
A devenv.nix
+100, -0
  1@@ -0,0 +1,100 @@
  2+# devkit — shared devenv module: personal tools + the customized interactive
  3+# bash environment.
  4+#
  5+# Consumers import this from their devenv.yaml:
  6+#
  7+#   inputs:
  8+#     devkit:
  9+#       url: path:../devkit        # or git+https://git.kilimanjaro.io/devenv-base
 10+#       flake: false
 11+#     # devenv#2205: an imported input's devenv.yaml is NOT evaluated, so each
 12+#     # consumer must declare the tool inputs its own config here.
 13+#     bug: { url: git+https://git.kilimanjaro.io/bug }
 14+#     local-code-review: { url: git+https://git.kilimanjaro.io/local-code-review }
 15+#     secrets: { url: git+https://git.kilimanjaro.io/secrets }
 16+#   imports:
 17+#     - devkit
 18+#
 19+# Each tool is added automatically when its input is declared. Disable one with
 20+# e.g. `devkit.bug.enable = false` (a project that ships its own wrapper).
 21+#
 22+# Changes to this file are picked up on the next `devenv shell` (path inputs,
 23+# devenv 2.2+). Once promoted to a git input, consumers pull them with
 24+# `devenv update devkit`.
 25+{ pkgs, lib, config, inputs, ... }:
 26+
 27+let
 28+  cfg = config.devkit;
 29+  hasInput = name: lib.hasAttr name inputs;
 30+in
 31+
 32+{
 33+  options.devkit = {
 34+    enable = lib.mkOption {
 35+      type = lib.types.bool;
 36+      default = true;
 37+      description = "Add the devkit toolchain and customized bash environment.";
 38+    };
 39+    bug.enable = lib.mkOption {
 40+      type = lib.types.bool;
 41+      default = hasInput "bug";
 42+      description = "Add the `bug` CLI (set false if the project ships its own wrapper).";
 43+    };
 44+    local-code-review.enable = lib.mkOption {
 45+      type = lib.types.bool;
 46+      default = hasInput "local-code-review";
 47+      description = "Add the `lcr` CLI.";
 48+    };
 49+    secrets.enable = lib.mkOption {
 50+      type = lib.types.bool;
 51+      default = hasInput "secrets";
 52+      description = "Add the `secrets` CLI.";
 53+    };
 54+  };
 55+
 56+  config = lib.mkIf cfg.enable {
 57+    packages = [
 58+      # toolchain the interactive bash environment needs
 59+      pkgs.bash
 60+      pkgs.bash-completion
 61+      pkgs.bashInteractive
 62+      pkgs.jujutsu
 63+    ]
 64+    # tools; the inputs are declared by the consumer (see header), so only add
 65+    # each one when it is present and not explicitly disabled.
 66+    ++ lib.optional cfg.bug.enable (inputs.bug.packages.${pkgs.system}.bug)
 67+    ++ lib.optional cfg.local-code-review.enable (inputs.local-code-review.packages.${pkgs.system}.default)
 68+    ++ lib.optional cfg.secrets.enable (inputs.secrets.packages.${pkgs.system}.default);
 69+
 70+    # The customized bash environment. It is `types.lines`, so each project's
 71+    # own enterShell is concatenated onto this.
 72+    enterShell = ''
 73+      # Interactive shells only: `devenv shell -- <cmd>` has no line editing,
 74+      # and bind warns there.
 75+      if [[ $- == *i* ]]; then
 76+        # Configure bash with fish-like features
 77+        bind 'set completion-ignore-case on'  # Case-insensitive completion
 78+        bind 'set show-all-if-ambiguous on'   # Show all completions on first tab
 79+        bind 'set menu-complete-display-prefix on'
 80+        bind 'TAB:menu-complete'              # Tab cycles through completions
 81+        bind '"\e[A":history-search-backward' # Up arrow searches history
 82+        bind '"\e[B":history-search-forward'  # Down arrow searches history
 83+        bind 'set colored-completion-prefix on'  # Color the completion prefix
 84+        bind 'set colored-stats on'           # Color the completion stats
 85+
 86+        # Enable bash-completion if available
 87+        if [ -f ${pkgs.bash-completion}/etc/profile.d/bash_completion.sh ]; then
 88+          source ${pkgs.bash-completion}/etc/profile.d/bash_completion.sh
 89+        fi
 90+
 91+        # Better directory navigation
 92+        shopt -s autocd 2>/dev/null          # Type directory name to cd
 93+        shopt -s dirspell 2>/dev/null        # Correct spelling of dir names
 94+        shopt -s cdspell 2>/dev/null         # Correct spelling of cd args
 95+
 96+        # Enable ** globbing for recursive directory matching
 97+        shopt -s globstar 2>/dev/null
 98+      fi
 99+    '';
100+  };
101+}
A devenv.yaml
+14, -0
 1@@ -0,0 +1,14 @@
 2+# Standalone config for working on devkit itself (`cd devkit && devenv shell`).
 3+#
 4+# NOTE: when a project imports devkit, THIS FILE IS IGNORED (devenv#2205).
 5+# The consuming project declares these same inputs in its own devenv.yaml.
 6+inputs:
 7+  nixpkgs:
 8+    url: github:cachix/devenv-nixpkgs/rolling
 9+  bug:
10+    url: git+https://git.kilimanjaro.io/bug
11+  local-code-review:
12+    url: git+https://git.kilimanjaro.io/local-code-review
13+  secrets:
14+    url: git+https://git.kilimanjaro.io/secrets
15+reload: false
A flake.nix
+19, -0
 1@@ -0,0 +1,19 @@
 2+{
 3+  # devkit is also usable as a `nix flake init` template:
 4+  #
 5+  #   mkdir newthing && cd newthing
 6+  #   nix flake init -t path:/home/btburke/projects/devkit
 7+  #
 8+  # (or `-t git+https://git.kilimanjaro.io/devenv-base` once published).
 9+  # NOTE: `devenv init` has no template flag (#1718); `--from` only affects
10+  # `devenv shell`/`test`/`build`, not `init`. Use the flake template above.
11+  description = "devkit - personal devenv module and new-project template";
12+
13+  outputs = { self }: {
14+    templates.default = {
15+      description = "New project wired to devkit (tools + customized bash env)";
16+      path = ./template;
17+    };
18+    templates.devkit = self.templates.default;
19+  };
20+}
A template/.envrc
+2, -0
1@@ -0,0 +1,2 @@
2+eval "$(devenv direnvrc)"
3+use devenv
A template/devenv.nix
+14, -0
 1@@ -0,0 +1,14 @@
 2+{ ... }:
 3+
 4+# Scaffold for a new project. devkit (imported in devenv.yaml) already provides
 5+# bug, secrets, local-code-review, jujutsu, and the customized interactive bash
 6+# environment, so don't repeat those here.
 7+{
 8+  packages = [
 9+    # project-specific packages
10+  ];
11+
12+  # enterShell = ''
13+  #   project-specific setup; concatenated onto devkit's
14+  # '';
15+}
A template/devenv.yaml
+20, -0
 1@@ -0,0 +1,20 @@
 2+# New-project scaffold. Copy alongside devenv.nix.
 3+#
 4+# `path:../devkit` assumes the project is a sibling of devkit under ~/projects.
 5+# devenv#2205: the three tool inputs must be declared here (devkit's own
 6+# devenv.yaml is not evaluated when imported).
 7+inputs:
 8+  nixpkgs:
 9+    url: github:cachix/devenv-nixpkgs/rolling
10+  bug:
11+    url: git+https://git.kilimanjaro.io/bug
12+  local-code-review:
13+    url: git+https://git.kilimanjaro.io/local-code-review
14+  secrets:
15+    url: git+https://git.kilimanjaro.io/secrets
16+  devkit:
17+    url: path:../devkit
18+    flake: false
19+imports:
20+  - devkit
21+reload: false