|
8 | 8 | # ----------------------------- |
9 | 9 | # This library uses SUBDIRECTORY NAMES, not full paths: |
10 | 10 | # - rootDir = ".stackpanel" (the stackpanel home directory) |
11 | | -# - stateDir = "state" (subdirectory name, NOT ".stack/state") |
12 | | -# - genDir = "gen" (subdirectory name, NOT ".stack/gen") |
| 11 | +# - stateDir = "state" (subdirectory name, NOT ".stackpanel/state") |
| 12 | +# - genDir = "gen" (subdirectory name, NOT ".stackpanel/gen") |
13 | 13 | # |
14 | 14 | # Full paths are computed as: $root/${rootDir}/${stateDir} |
15 | | -# For example: /path/to/project/.stack/state |
| 15 | +# For example: /path/to/project/.stackpanel/state |
16 | 16 | # |
17 | | -# If you're getting duplicate path segments like ".stack/.stack/state", |
| 17 | +# If you're getting duplicate path segments like ".stackpanel/.stackpanel/state", |
18 | 18 | # you're passing a full path where a subdirectory name is expected! |
19 | 19 | # |
20 | | -# Uses a root marker file pattern (similar to devenv's .devenv-root) to allow |
21 | | -# tools to find the project root from any subdirectory. |
22 | | -# |
23 | 20 | # Features: |
24 | 21 | # - Shell functions for finding project root (stackpanel_find_root) |
25 | 22 | # - Path resolution utilities (stackpanel_resolve_paths) |
|
38 | 35 | # STACKPANEL_ROOT=$(stackpanel_find_root) |
39 | 36 | # ============================================================================== |
40 | 37 | # ============================================================================== |
41 | | -{ lib }: |
42 | | -let |
| 38 | +{lib}: let |
43 | 39 | # Default configuration (can be overridden) |
44 | 40 | defaults = { |
45 | | - rootDir = ".stack"; |
46 | | - rootMarker = ".stackpanel-root"; |
47 | | - stateDir = "profile"; |
48 | | - keysDir = "keys"; |
| 41 | + rootDir = ".stackpanel"; |
| 42 | + stateDir = "state"; |
49 | 43 | genDir = "gen"; |
50 | 44 | }; |
51 | 45 |
|
52 | | - # Shell function to find project root by looking for root marker |
53 | | - mkShellFindRoot = |
54 | | - { |
55 | | - rootDir ? defaults.rootDir, |
56 | | - rootMarker ? defaults.rootMarker, |
57 | | - }: |
58 | | - '' |
59 | | - stackpanel_find_root() { |
60 | | - local dir="$PWD" |
61 | | - local marker="${rootMarker}" |
| 46 | + # Shell function to find project root |
| 47 | + mkShellFindRoot = {rootDir ? defaults.rootDir}: '' |
| 48 | + stackpanel_find_root() { |
| 49 | + local dir="$PWD" |
62 | 50 |
|
63 | | - # First, try to find the marker file by walking up the directory tree |
64 | | - while [[ "$dir" != "/" ]]; do |
65 | | - if [[ -f "$dir/$marker" ]]; then |
66 | | - cat "$dir/$marker" |
67 | | - return 0 |
68 | | - fi |
69 | | - dir="$(dirname "$dir")" |
70 | | - done |
| 51 | + # First, check if STACKPANEL_ROOT is already set (preferred) |
| 52 | + if [[ -n "''${STACKPANEL_ROOT:-}" ]]; then |
| 53 | + echo "$STACKPANEL_ROOT" |
| 54 | + return 0 |
| 55 | + fi |
71 | 56 |
|
72 | | - # Fallback 1: check if STACKPANEL_ROOT is already set |
73 | | - if [[ -n "''${STACKPANEL_ROOT:-}" ]]; then |
74 | | - echo "$STACKPANEL_ROOT" |
| 57 | + # Fallback 1: use git repository root if available |
| 58 | + if command -v git >/dev/null 2>&1; then |
| 59 | + local git_root |
| 60 | + git_root="$(git rev-parse --show-toplevel 2>/dev/null)" || true |
| 61 | + if [[ -n "$git_root" ]]; then |
| 62 | + echo "$git_root" |
75 | 63 | return 0 |
76 | 64 | fi |
| 65 | + fi |
77 | 66 |
|
78 | | - # Fallback 2: use git repository root if available |
79 | | - # This handles running `nix develop` from subdirectories before marker exists |
80 | | - if command -v git >/dev/null 2>&1; then |
81 | | - local git_root |
82 | | - git_root="$(git rev-parse --show-toplevel 2>/dev/null)" || true |
83 | | - if [[ -n "$git_root" ]]; then |
84 | | - echo "$git_root" |
85 | | - return 0 |
86 | | - fi |
| 67 | + # Fallback 2: look for .stackpanel directory by walking up from PWD |
| 68 | + dir="$PWD" |
| 69 | + while [[ "$dir" != "/" ]]; do |
| 70 | + if [[ -d "$dir/${rootDir}" ]]; then |
| 71 | + echo "$dir" |
| 72 | + return 0 |
87 | 73 | fi |
| 74 | + dir="$(dirname "$dir")" |
| 75 | + done |
88 | 76 |
|
89 | | - # Fallback 3: look for flake.nix by walking up from PWD |
90 | | - dir="$PWD" |
91 | | - while [[ "$dir" != "/" ]]; do |
92 | | - if [[ -f "$dir/flake.nix" ]]; then |
93 | | - echo "$dir" |
94 | | - return 0 |
95 | | - fi |
96 | | - dir="$(dirname "$dir")" |
97 | | - done |
| 77 | + # Fallback 3: look for flake.nix by walking up from PWD |
| 78 | + dir="$PWD" |
| 79 | + while [[ "$dir" != "/" ]]; do |
| 80 | + if [[ -f "$dir/flake.nix" ]]; then |
| 81 | + echo "$dir" |
| 82 | + return 0 |
| 83 | + fi |
| 84 | + dir="$(dirname "$dir")" |
| 85 | + done |
98 | 86 |
|
99 | | - echo "Error: Could not find stackpanel root (no $marker, git repo, or flake.nix found)" >&2 |
100 | | - return 1 |
101 | | - } |
102 | | - ''; |
| 87 | + echo "Error: Could not find stackpanel root (no STACKPANEL_ROOT env var, git repo, ${rootDir} dir, or flake.nix found)" >&2 |
| 88 | + return 1 |
| 89 | + } |
| 90 | + ''; |
103 | 91 |
|
104 | 92 | # Shell function to resolve all stackpanel paths |
105 | | - mkShellResolvePaths = |
106 | | - { |
107 | | - rootDir ? defaults.rootDir, |
108 | | - stateDir ? defaults.stateDir, |
109 | | - keysDir ? defaults.keysDir, |
110 | | - genDir ? defaults.genDir, |
111 | | - rootMarker ? defaults.rootMarker, |
112 | | - }: |
113 | | - '' |
114 | | - stackpanel_resolve_paths() { |
115 | | - local root="''${1:-$(stackpanel_find_root)}" |
116 | | - if [[ -z "$root" ]]; then |
117 | | - return 1 |
118 | | - fi |
119 | | - if [[ ! -d "$root" ]]; then |
120 | | - echo "Error: Resolved stackpanel root is not a directory: $root" |
121 | | - echo "You may need to run on your stackpanel root dir:" |
122 | | - echo |
123 | | - echo " echo \"\$PWD\" > ${rootMarker}" |
124 | | - echo |
125 | | - return 1 |
126 | | - fi |
127 | | - export STACKPANEL_ROOT="$root" |
128 | | - export STACKPANEL_ROOT_DIR="$root/${rootDir}" |
129 | | - export STACKPANEL_STATE_DIR="$root/${rootDir}/${stateDir}" |
130 | | - export STACKPANEL_KEYS_DIR="$root/${rootDir}/${keysDir}" |
131 | | - export STACKPANEL_GEN_DIR="$root/${rootDir}/${genDir}" |
132 | | - } |
133 | | - ''; |
134 | | -in |
135 | | -{ |
| 93 | + mkShellResolvePaths = { |
| 94 | + rootDir ? defaults.rootDir, |
| 95 | + stateDir ? defaults.stateDir, |
| 96 | + genDir ? defaults.genDir, |
| 97 | + }: '' |
| 98 | + stackpanel_resolve_paths() { |
| 99 | + local root="''${1:-$(stackpanel_find_root)}" |
| 100 | + if [[ -z "$root" ]]; then |
| 101 | + return 1 |
| 102 | + fi |
| 103 | + if [[ ! -d "$root" ]]; then |
| 104 | + echo "Error: Resolved stackpanel root is not a directory: $root" |
| 105 | + echo "You may need to set STACKPANEL_ROOT or run from a git repository" |
| 106 | + return 1 |
| 107 | + fi |
| 108 | + export STACKPANEL_ROOT="$root" |
| 109 | + export STACKPANEL_ROOT_DIR="$root/${rootDir}" |
| 110 | + export STACKPANEL_STATE_DIR="$root/${rootDir}/${stateDir}" |
| 111 | + export STACKPANEL_GEN_DIR="$root/${rootDir}/${genDir}" |
| 112 | + } |
| 113 | + ''; |
| 114 | +in { |
136 | 115 | inherit defaults mkShellFindRoot mkShellResolvePaths; |
137 | 116 |
|
138 | 117 | # Combined shell setup script with all path utilities |
139 | 118 | # IMPORTANT: stateDir and genDir must be SUBDIRECTORY NAMES, not full paths! |
140 | | - mkShellPathUtils = |
141 | | - cfg: |
142 | | - let |
143 | | - rootDir = cfg.rootDir or defaults.rootDir; |
144 | | - rootMarker = cfg.rootMarker or defaults.rootMarker; |
145 | | - stateDir = cfg.stateDir or defaults.stateDir; |
146 | | - keysDir = cfg.keysDir or defaults.keysDir; |
147 | | - genDir = cfg.genDir or defaults.genDir; |
| 119 | + mkShellPathUtils = cfg: let |
| 120 | + rootDir = cfg.rootDir or defaults.rootDir; |
| 121 | + stateDir = cfg.stateDir or defaults.stateDir; |
| 122 | + genDir = cfg.genDir or defaults.genDir; |
148 | 123 |
|
149 | | - validatedStateDir = |
150 | | - if lib.hasPrefix "." stateDir && stateDir != "." then |
151 | | - throw '' |
152 | | - paths.nix: stateDir should be a subdirectory name (e.g., "profile"), not a full path! |
153 | | - Got: "${stateDir}" |
154 | | - Expected: just the subdirectory name like "profile" |
155 | | - The full path is computed as: $root/${rootDir}/${stateDir} |
156 | | - '' |
157 | | - else |
158 | | - stateDir; |
| 124 | + # Validate that stateDir doesn't look like a full path (starts with ".") |
| 125 | + validatedStateDir = |
| 126 | + if lib.hasPrefix "." stateDir && stateDir != "." |
| 127 | + then |
| 128 | + throw '' |
| 129 | + paths.nix: stateDir should be a subdirectory name (e.g., "state"), not a full path! |
| 130 | + Got: "${stateDir}" |
| 131 | + Expected: just the subdirectory name like "state" |
| 132 | + The full path is computed as: $root/${rootDir}/${stateDir} |
| 133 | + '' |
| 134 | + else stateDir; |
159 | 135 |
|
160 | | - validatedKeysDir = |
161 | | - if lib.hasPrefix "." keysDir && keysDir != "." then |
162 | | - throw '' |
163 | | - paths.nix: keysDir should be a subdirectory name (e.g., "keys"), not a full path! |
164 | | - Got: "${keysDir}" |
165 | | - '' |
166 | | - else |
167 | | - keysDir; |
168 | | - |
169 | | - validatedGenDir = |
170 | | - if lib.hasPrefix "." genDir && genDir != "." then |
171 | | - throw '' |
172 | | - paths.nix: genDir should be a subdirectory name (e.g., "gen"), not a full path! |
173 | | - Got: "${genDir}" |
174 | | - Expected: just the subdirectory name like "gen" |
175 | | - The full path is computed as: $root/${rootDir}/${genDir} |
176 | | - '' |
177 | | - else |
178 | | - genDir; |
179 | | - in |
180 | | - '' |
181 | | - # Stackpanel path utilities |
182 | | - ${mkShellFindRoot { inherit rootDir rootMarker; }} |
183 | | - ${mkShellResolvePaths { |
184 | | - rootDir = rootDir; |
185 | | - stateDir = validatedStateDir; |
186 | | - keysDir = validatedKeysDir; |
187 | | - genDir = validatedGenDir; |
188 | | - }} |
189 | | - ''; |
| 136 | + # Validate that genDir doesn't look like a full path (starts with ".") |
| 137 | + validatedGenDir = |
| 138 | + if lib.hasPrefix "." genDir && genDir != "." |
| 139 | + then |
| 140 | + throw '' |
| 141 | + paths.nix: genDir should be a subdirectory name (e.g., "gen"), not a full path! |
| 142 | + Got: "${genDir}" |
| 143 | + Expected: just the subdirectory name like "gen" |
| 144 | + The full path is computed as: $root/${rootDir}/${genDir} |
| 145 | + '' |
| 146 | + else genDir; |
| 147 | + in '' |
| 148 | + # Stackpanel path utilities |
| 149 | + ${mkShellFindRoot {inherit rootDir;}} |
| 150 | + ${mkShellResolvePaths { |
| 151 | + rootDir = rootDir; |
| 152 | + stateDir = validatedStateDir; |
| 153 | + genDir = validatedGenDir; |
| 154 | + }} |
| 155 | + ''; |
190 | 156 |
|
191 | 157 | # ============================================================================ |
192 | 158 | # NIX-TIME PATH HELPERS |
193 | 159 | # Pure functions for computing paths at Nix evaluation time |
194 | 160 | # ============================================================================ |
195 | 161 |
|
196 | 162 | # Compute derived paths from config |
197 | | - mkPaths = |
198 | | - { |
199 | | - rootDir ? defaults.rootDir, |
200 | | - stateDir ? defaults.stateDir, |
201 | | - keysDir ? defaults.keysDir, |
202 | | - genDir ? defaults.genDir, |
203 | | - configDir ? null, |
204 | | - }: |
205 | | - { |
206 | | - root = rootDir; |
207 | | - state = "${rootDir}/${stateDir}"; |
208 | | - keys = "${rootDir}/${keysDir}"; |
209 | | - gen = "${rootDir}/${genDir}"; |
210 | | - config = if configDir != null then toString configDir else null; |
211 | | - }; |
| 163 | + mkPaths = { |
| 164 | + rootDir ? defaults.rootDir, |
| 165 | + stateDir ? defaults.stateDir, |
| 166 | + genDir ? defaults.genDir, |
| 167 | + configDir ? null, |
| 168 | + }: { |
| 169 | + root = rootDir; |
| 170 | + state = "${rootDir}/${stateDir}"; |
| 171 | + gen = "${rootDir}/${genDir}"; |
| 172 | + config = |
| 173 | + if configDir != null |
| 174 | + then toString configDir |
| 175 | + else null; |
| 176 | + }; |
212 | 177 |
|
213 | 178 | # ============================================================================ |
214 | 179 | # GITIGNORE HELPERS |
215 | 180 | # ============================================================================ |
216 | 181 |
|
217 | 182 | # Generate .gitignore content for the stackpanel root directory |
218 | | - mkGitignore = |
219 | | - { |
220 | | - rootMarker ? defaults.rootMarker, |
221 | | - stateDir ? defaults.stateDir, |
222 | | - keysDir ? defaults.keysDir, |
223 | | - extraEntries ? [ ], |
224 | | - }: |
| 183 | + mkGitignore = { |
| 184 | + stateDir ? defaults.stateDir, |
| 185 | + extraEntries ? [], |
| 186 | + }: |
225 | 187 | lib.concatStringsSep "\n" ( |
226 | 188 | [ |
227 | 189 | "${stateDir}/" |
228 | | - "${keysDir}/" |
229 | | - rootMarker |
230 | 190 | ] |
231 | 191 | ++ extraEntries |
232 | 192 | ); |
|
0 commit comments