Repository navigation
Expand file tree
/
Copy pathflake.nix
More file actions
3449 lines (3367 loc) · 178 KB
/
Copy pathflake.nix
File metadata and controls
3449 lines (3367 loc) · 178 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{
description = "trollshell — hytte-based Wayland desktop shell";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
treefmt-nix = {
url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
crane.url = "github:ipetkov/crane";
# Only used by the flake checks (hm-module) to evaluate homeModules.default
# against a real home-manager module set; not a runtime dependency.
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
{
self,
nixpkgs,
treefmt-nix,
crane,
home-manager,
...
}:
let
systems = [
"x86_64-linux"
"aarch64-linux"
];
# The bundled widget plugins (#558), by crate = binary = flake-output
# name. Each is packaged by nix/plugin.nix, which since #572 is a plain
# `cp` of one already-compiled binary out of the single whole-workspace
# compile (`trollshell.passthru.workspace`) — no cargo, no crane. Shared
# between the `packages` output (per-plugin flake outputs, so
# `programs.trollshell.plugins.<id>.package` has something in THIS flake to
# point at) and the `checks` output (build coverage, #449), so the list
# lives once — and since #1400 in its own file, because the modules read
# it too (`programs.trollshell.availablePlugins` defaults to every id in
# it); see nix/bundled-plugins.nix for what is in it and why.
bundledPluginNames = import ./nix/bundled-plugins.nix;
# The source revision this build came from (#601), threaded into the
# *wrapped* binaries' runtime environment as `TROLLSHELL_REV` so a running
# shell can answer "which commit am I?" — the question that has now cost
# two rounds of investigating already-fixed behaviour (#375, #566).
#
# `self.shortRev` exists only on a CLEAN git tree; a dirty working tree
# instead carries `self.dirtyShortRev` (e.g. "34e3d96-dirty"), and a
# non-git source (a `path:` flake, a tarball) has neither — hence the
# literal fallback. Verified against nix 2.34: the two attributes are
# mutually exclusive, never both present.
#
# Deliberately NOT a compile-time env on the `workspace` derivation: that
# would change the one expensive crane compile's hash on every commit and
# invalidate the artifact every package output slices from (see
# nix/package.nix). It is injected only by the cheap wrapper slices
# (nix/package.nix's `preFixup`, nix/control-center.nix), which are a `cp`
# plus a makeWrapper call. The Rust side (trollshell/src/revision.rs)
# reads it at runtime and falls back to "dev" when unset, which is exactly
# what a plain `cargo run` gets.
revision = self.shortRev or (self.dirtyShortRev or "unknown");
forAllSystems =
fn:
nixpkgs.lib.genAttrs systems (
system:
fn rec {
pkgs = import nixpkgs { inherit system; };
# crane drives the build on nixpkgs' own rust toolchain.
craneLib = crane.mkLib pkgs;
treefmt-eval = treefmt-nix.lib.evalModule pkgs ./nix/treefmt.nix;
}
);
in
{
packages = forAllSystems (
{ pkgs, craneLib, ... }:
let
trollshell = pkgs.callPackage ./nix/package.nix { inherit craneLib revision; };
# The single whole-workspace compile (#572). EVERY package output
# below is a slice of this one derivation — a `cp` of one binary out
# of its `$out/bin`, optionally wrapped — so there is exactly one
# cargo invocation on the package path and nothing that can miss a
# cross-derivation artifact cache. See nix/package.nix.
workspace = trollshell.passthru.workspace;
# The control-center companion app (#399): the workspace binary, GApps-
# wrapped, plus a .desktop launcher. No cargo (#572).
trollshell-control-center = pkgs.callPackage ./nix/control-center.nix {
inherit workspace revision;
};
# The per-agent companion window (#950, P2 of the agentic desktop):
# hyperhive's own agent page in a WebKitGTK view inside our chrome.
# Same slice-and-wrap shape as the control center — it is a windowed
# GTK app, and WebKit needs the GApplication env — but with no
# .desktop item, since it is meaningless without `--agent <name>`
# (nix/agent-window.nix says why). `programs.trollshell.agentWindow`
# installs it; the agents plugin launches it off `PATH`.
trollshell-agent-window = pkgs.callPackage ./nix/agent-window.nix {
inherit workspace revision;
};
# Per-plugin flake packages (#558): `packages.hytte-plugin-<id>` for
# each bundled plugin. Generated from `bundledPluginNames`
# (one attr each) rather than hand-written. Since #572 each is a `cp`
# of one already-compiled binary out of `workspace` — no cargo, no
# crane, no recompile.
bundledPlugins = pkgs.lib.genAttrs bundledPluginNames (
name:
pkgs.callPackage ./nix/plugin.nix (
{
inherit workspace name;
}
# `hytte-plugin-niri-layouts` is the one bundled plugin with a
# standalone-CLI hat (`apply <layout>`, #1019) and so the one
# with a `completions <shell>` subcommand to build (#1116); every
# other bundled plugin is driven entirely by the wire protocol
# and has no argv to complete.
// pkgs.lib.optionalAttrs (name == "hytte-plugin-niri-layouts") {
hasCompletions = true;
}
)
);
# The `hytte-infobroker` CLI (#562): the #487 consent-gated broker's
# second `[[bin]]` (crates/hytte-plugin-infobroker/Cargo.toml — a tool,
# not a widget plugin), used by the etc/skills/infobroker agent-bridge
# skill. Packaged via the same nix/plugin.nix derivation as the bundled
# plugins above, but deliberately kept OUT of `bundledPluginNames` —
# nothing in `programs.trollshell.plugins` should point at it; install
# it with a plain `home.packages` entry instead (see the skill docs).
hytte-infobroker = pkgs.callPackage ./nix/plugin.nix {
inherit workspace;
name = "hytte-infobroker";
description = "trollshell consent-gated agent-bridge broker CLI (#487)";
# The CLI's own `completions <shell>` subcommand (#1116).
hasCompletions = true;
};
# The `hytte-claude-bridge` daemon (#584): a keyless same-uid-socket shim
# putting an OpenAI-compatible face on headless Claude Code. Since #866
# it speaks the plugin protocol too (a status chip) and IS driven by
# `programs.trollshell.plugins` — `nix/hm-module.nix` renders the
# `claude-bridge` entry from `claudeBridge.*`, so nobody points
# `plugins.<id>.package` at this by hand. It still stays out of
# `bundledPluginNames`, which is a mechanical `hytte-plugin-<id>` →
# package map and this binary is not named that way. GTK-free, so
# nix/plugin.nix's unwrapped `cp` is exactly right; no wrapGAppsHook4
# needed.
hytte-claude-bridge = pkgs.callPackage ./nix/plugin.nix {
inherit workspace;
name = "hytte-claude-bridge";
description = "trollshell keyless same-uid-socket OpenAI-compatible bridge to headless Claude Code (#584, #993)";
};
# 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/index.html
options-doc = pkgs.callPackage ./nix/options-doc.nix { inherit self pkgs; };
in
{
inherit
trollshell
trollshell-control-center
trollshell-agent-window
options-doc
hytte-infobroker
hytte-claude-bridge
;
default = trollshell;
}
// bundledPlugins
);
devShells = forAllSystems (
{ pkgs, ... }:
{
default = import ./nix/devshell.nix {
inherit pkgs;
trollshell = self.packages.${pkgs.stdenv.hostPlatform.system}.trollshell;
};
}
);
formatter = forAllSystems ({ treefmt-eval, ... }: treefmt-eval.config.build.wrapper);
checks = forAllSystems (
{
pkgs,
treefmt-eval,
craneLib,
...
}:
let
system = pkgs.stdenv.hostPlatform.system;
trollshell = pkgs.callPackage ./nix/package.nix { inherit craneLib revision; };
# The single whole-workspace compile (#572), mirroring the `packages`
# output above.
workspace = trollshell.passthru.workspace;
# The checks-universe probe-examples compile (#1257) — see
# nix/package.nix's `probes` binding. `probe`/`wifiProbe` below take
# BOTH this and `workspace`: the binary from here, the GApps wrap's
# `buildInputs` from `workspace.passthru.devInputs`.
probes = trollshell.passthru.probes;
# The control-center companion app (#411), mirroring the `packages`
# output above — a slice of `workspace`, no cargo of its own (#572).
trollshell-control-center = pkgs.callPackage ./nix/control-center.nix {
inherit workspace revision;
};
# The #950 companion window, mirroring the `packages` output. Wired
# in here — the I1 gap the #1130 review measured — now that #1127 has
# landed and this region is stable: it is the one output whose
# **wrapper** derivation (nix/agent-window.nix) nothing else builds,
# and the wrapper is exactly where M1's closure bug lived.
trollshell-agent-window = pkgs.callPackage ./nix/agent-window.nix {
inherit workspace revision;
};
# The per-plugin packages (#558), mirroring the `packages` output.
# Merged into `checks` below so `nix flake check` actually *builds*
# each one — the same reason #449 wired the two existing packages into
# checks: flake check only builds what's listed here, so without this a
# broken plugin package could stay green until someone ran `nix build
# .#hytte-plugin-<id>`. Genuinely near-free since #572: each is a `cp`
# out of the one `workspace` output every other check already forces.
bundledPlugins = pkgs.lib.genAttrs bundledPluginNames (
name:
pkgs.callPackage ./nix/plugin.nix (
{
inherit workspace name;
}
# `hytte-plugin-niri-layouts` is the one bundled plugin with a
# standalone-CLI hat (`apply <layout>`, #1019) and so the one
# with a `completions <shell>` subcommand to build (#1116); every
# other bundled plugin is driven entirely by the wire protocol
# and has no argv to complete.
// pkgs.lib.optionalAttrs (name == "hytte-plugin-niri-layouts") {
hasCompletions = true;
}
)
);
# The `hytte-infobroker` CLI package (#562), mirroring the `packages`
# output above. Wired into `checks` below for the same #449 reason as
# `bundledPlugins`: without it, `nix flake check` could stay green
# while `nix build .#hytte-infobroker` was actually broken.
hytte-infobroker = pkgs.callPackage ./nix/plugin.nix {
inherit workspace;
name = "hytte-infobroker";
description = "trollshell consent-gated agent-bridge broker CLI (#487)";
# The CLI's own `completions <shell>` subcommand (#1116).
hasCompletions = true;
};
# The `hytte-claude-bridge` daemon package (#584), mirroring the
# `packages` output above and wired into `checks` below for the same
# #449 reason: without it, `nix flake check` could stay green while
# `nix build .#hytte-claude-bridge` was broken. Until #757 it was also
# the one place crane's *git*-dependency vendoring got exercised in CI:
# `hive-claude` was a rev pin that `builtins.fetchGit` resolved at
# eval, which a sandboxed build phase could not have done. It comes
# from crates.io now, so nothing in this flake reaches a third-party
# forge to evaluate any more (#671).
hytte-claude-bridge = pkgs.callPackage ./nix/plugin.nix {
inherit workspace;
name = "hytte-claude-bridge";
description = "trollshell keyless same-uid-socket OpenAI-compatible bridge to headless Claude Code (#584, #993)";
};
# A cheap stand-in for the real trollshell package so the module-eval
# checks don't force a full Rust crate build just to type-check the
# config bodies. It carries meta.mainProgram so `lib.getExe cfg.package`
# (used by the systemd ExecStart) still resolves.
stubPackage = pkgs.writeShellScriptBin "trollshell" "";
# Stand-in plugin binary for the programs.trollshell.plugins
# coverage in the two module-eval checks below (#350/#355).
stubPlugin = pkgs.writeShellScriptBin "hytte-plugin-demo" "";
# The one `programs.trollshell.config.places` example both fixture
# checks render (#1227 item 2). Shared rather than written twice
# precisely because the two checks exist to prove the NixOS and
# home-manager renders are byte-identical — two copies of the input
# would let that claim be about two different inputs.
#
# Chosen to exercise the whole schema in one document: both locked
# keys, every optional per-place field on the first entry and none of
# them on the second (which is what `prune` descending through a list
# has to produce), a float that keeps its `.0` (`radius_km`), a string
# needing no quoting drama and one with a `!` in it, and an inline
# array of each of the two list-valued fields.
placesExample = {
departures.endpoint = "vbb";
place = [
{
name = "Schöneweide";
lat = 52.4556;
lon = 13.5085;
station = "900192001";
walk_minutes = 10;
ssids = [
"Kabelsalat"
"FRITZ!Box 7590 XV"
];
match_min = 2;
radius_km = 12.0;
}
{
name = "Werkstatt";
lat = 52.5163;
lon = 13.4549;
lines = [
"S8"
"S9"
];
}
];
};
# Stand-ins for the two LLM backend daemons (#694), so the hm-module
# check below can turn both units on and force their bodies without
# pulling a full workspace compile (hytte-claude-bridge) or llama-cpp
# into the check's closure. Named after the binary each unit actually
# invokes, so `lib.getExe` (bridge, via meta.mainProgram) and
# `lib.getExe' … "llama-server"` (pet brain, by name) both resolve.
stubClaudeBridge = pkgs.writeShellScriptBin "hytte-claude-bridge" "";
stubLlamaCpp = pkgs.writeShellScriptBin "llama-server" "";
# The hytte-ecal `probe` example binary + fixture sources (a
# task-list and two calendars, one coloured and one not), for the
# eds-nixos-test below. Since #588
# this is a slice — a `cp` + a GApps wrap, no cargo and no crane —
# exactly like the plugin packages above; since #1257 the binary it
# copies is `probes`' (the checks-universe compile), not
# `workspace`'s. Before #588 it was its own `buildDepsOnly` +
# `buildPackage` pair with its own `src` filter, i.e. a second full
# dependency compile per cold flake check.
probe = pkgs.callPackage ./nix/probe.nix { inherit workspace probes; };
# The hytte-services `wifi_probe` example binary, for the
# wifi-nm-nixos-test below. Same slice treatment (#588/#1257) — it
# was the third full dependency compile.
wifiProbe = pkgs.callPackage ./nix/wifi-probe.nix { inherit workspace probes; };
taskSource = pkgs.writeText "test-tasks.source" ''
[Data Source]
DisplayName=Test Tasks
Enabled=true
[Task List]
BackendName=local
'';
# A writable local calendar the probe seeds FREQ=DAILY;COUNT=5
# VEVENTs into, then expands via generate_instances — exercising the
# RRULE-expansion path (#29) and the EXDATE recurrence-set modifier
# (the #29 follow-up: one series cancels a day via EXDATE).
calSource = pkgs.writeText "test-calendar.source" ''
[Data Source]
DisplayName=Test Calendar
Enabled=true
[Calendar]
BackendName=local
Color=#ff8800
'';
# The colourless twin (#1223 item 1 review, MED-5). With only the
# coloured fixture seeded, the VM's `color=#ff8800` assertion is
# satisfied by a `Source::color` that ignores the FFI read and returns
# that literal — measured: that mutation keeps all 44 hermetic tests
# AND the VM regex green. A second calendar that sets **no** `Color=`
# makes the pair discriminating: the same run must print `color=(none)`
# for this one, which no constant can do.
#
# `Uncoloured Calendar` sorts after `Test Calendar`, and the two
# assertions key off the distinct display names rather than off
# position, so neither the registry's ordering nor which calendar the
# recurrence probe picks to write into can flip them.
calSourceNoColor = pkgs.writeText "test-calendar-no-color.source" ''
[Data Source]
DisplayName=Uncoloured Calendar
Enabled=true
[Calendar]
BackendName=local
'';
in
{
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 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. 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
trollshell-agent-window
hytte-infobroker
hytte-claude-bridge
;
# The shell's runtime closure must carry no WebKitGTK 6.0 (#1130 M1).
# A `runCommand` over `exportReferencesGraph` — no compile, so it is
# red in seconds, and it reads nix's own answer about the closure
# rather than re-deriving one. See nix/checks/ for the whole story,
# including why it names the ABI.
shell-has-no-web-engine = pkgs.callPackage ./nix/checks/shell-has-no-web-engine.nix {
inherit trollshell trollshell-control-center;
};
# The two nixosTest probe examples (`probe`, `wifi_probe`) must
# never creep back into the `workspace` compile every package
# output slices from (#1257) — see nix/checks/workspace-ships-no-
# probes.nix for the full mechanism and how to falsify it.
workspace-ships-no-probes = pkgs.callPackage ./nix/checks/workspace-ships-no-probes.nix {
inherit workspace;
};
# Lint the entire workspace with pedantic-clean Clippy. Reuses
# cargoArtifacts from the package build so dependencies aren't
# recompiled from scratch. Must stay green because the workspace
# denies clippy::all + clippy::pedantic and forbids unsafe.
#
# `--features system-tests` pulls the whole-file-gated integration
# tests (crates/hytte-{bus,reactive,ui}/tests/*.rs) and the gated
# `mod` blocks (hytte-ui's widget_tests/gtk_tests) into the lint
# pass too — without it they're invisible to clippy, the same gap
# #232 found for `cargo test` itself. Verified clean locally
# (`cargo clippy --workspace --all-targets --features system-tests
# -- -D warnings`) before wiring this in.
clippy = craneLib.cargoClippy (
trollshell.passthru.commonArgs
// {
cargoArtifacts = trollshell.passthru.cargoArtifacts;
# `commonArgs.cargoExtraArgs` already carries `--workspace
# --locked` (#572), so only the lint-specific flags go here; the
# effective command is unchanged.
cargoClippyExtraArgs = "--all-targets --features system-tests -- -D warnings";
# This is a leaf/terminal check — nothing chains off its target
# dir as `cargoArtifacts` — so don't pack it. crane defaults
# `doInstallCargoArtifacts = true`, which would tar the whole
# (multi-GiB) target dir into $out for no consumer, burning build
# time and disk. Same fix + reason as system-tests below.
doInstallCargoArtifacts = false;
}
);
# The static counterpart to `clippy`, for a defect clippy cannot see
# (#831): a `bind*` call site that discards the closure's own widget
# parameter and uses a captured strong clone of the same widget
# instead, pinning it for the binding's lifetime and defeating the
# `WeakRef` contract in crates/hytte-reactive/src/bind.rs:16-22
# (#224).
#
# This exists because the defect has already recurred once by the
# only other means available: #772 fixed four sites and closed on a
# hand-read inventory of four; #831 re-derived the list and found
# twelve, so two thirds of it had been missed. #834 fixed all twelve
# with the scanner this check now runs. A unit test can only cover one
# site at a time and only where the widget is constructible without a
# registered `Registry` — ten of the twelve are not — whereas the bug
# is purely *syntactic*, which is exactly what a scanner sees.
#
# Deliberately not a crane derivation: it needs no compile, no
# cargoArtifacts and no target dir, so it goes red in seconds on a
# cold checkout instead of behind a full workspace build.
#
# `cd ${self}` rather than staging the three scanned trees into a
# sandbox: it keeps the scanned-root list in one place (the script's
# `DEFAULT_ROOTS`), makes every reported `file:line` a path a
# developer can open as-is, and costs nothing extra — `formatting`
# already realises `self` on every flake check. A root that goes
# missing (a renamed tree) exits 2 rather than passing vacuously, as
# does a scan that sees implausibly few call sites.
bind-pins = pkgs.runCommand "trollshell-bind-pin-check" { nativeBuildInputs = [ pkgs.python3 ]; } ''
cd ${self}
python3 nix/lint-bind-pins.py
touch $out
'';
# The root `[workspace.lints]` table and the two `unsafe` islands'
# hand-mirrored copies (`crates/hytte-ecal/Cargo.toml`,
# `crates/hytte-gl/Cargo.toml`) must agree on every lint except the
# one `unsafe_code` line — Cargo's workspace-lints inheritance is
# all-or-nothing, which is why the copies exist at all. Nothing
# enforced that: deleting `pedantic` from an island leaves `cargo
# clippy --workspace --all-targets -- -D warnings` and the whole of
# `nix flake check` green, and one of the two crates where `unsafe`
# is legal quietly stops being pedantic-checked (#1179, from the
# #1162 sweep). Same posture as `bind-pins` above: a source-level
# defect no compile in this flake can see, so a script rather than a
# test, with no cargoArtifacts so it goes red in seconds. It also
# asserts the other direction — every *non*-island member inherits
# with `[lints] workspace = true` — so a third island cannot appear
# unnoticed. `nix/lint-lints-tables.py`'s own header has the full
# story, including why the five documented FFI-only `allow`s in
# `hytte-ecal` are declared in the script rather than waved through.
lints-tables =
pkgs.runCommand "trollshell-lints-tables-check" { nativeBuildInputs = [ pkgs.python3 ]; }
''
cd ${self}
python3 nix/lint-lints-tables.py
touch $out
'';
# `programs.trollshell.config.core-leds`'s nix-side vocabulary
# (`style`/`fill` enums, `rows`'s cap) hand-mirrors Rust's
# `DisplayStyle`/`parse_core_leds_fill`/`MAX_ROWS` (#1041, #1081
# review M6) with nothing failing if the two drift — and this
# option is explicitly the template nine more subsystem families
# copy. Same posture as `bind-pins` above: a source-level defect no
# compile in this flake can see, so a script rather than a test,
# with no cargoArtifacts so it goes red in seconds.
#
# Deliberately NOT a `cargo test`: a first version of this lived in
# `trollshell/src/config/core_leds.rs` reading
# `nix/module-common.nix` off disk via `CARGO_MANIFEST_DIR`, and it
# passed locally while failing in CI — `nix/package.nix`'s crane
# source filter keeps only `.rs`/`.toml`/lockfile/CSS/shader files,
# so the sandboxed `workspace` compile `cargo test --workspace`
# runs inside has no `.nix` files at all (the same `include_str!`
# trap CLAUDE.md documents for `assets/`, reached by
# `std::fs::read_to_string` instead). Widening the crane filter to
# keep `*.nix` was rejected — every `.nix` edit would then
# invalidate `workspace`'s source hash and force a full recompile.
# `nix/lint-config-vocab.py`'s own header has the full story: since
# #1375 it parses the FOUR `hytte-config` `Subsystem` families' own
# `SCHEMA` consts (`core-leds`, `workspaces`, `agents`, `stats`)
# and compares the two of them with a
# `programs.trollshell.config.<family>` block today (`core-leds`,
# `agents`) — the leaf set both directions, bounds/enums/type
# shape, and each nix `description`'s first sentence against the
# schema's own `doc` — retiring the parser-function scrape
# (`fill_parser_vocab`, `agents_option_levels`, `DisplayStyle::ALL`,
# …) the script read before. `workspaces`/`stats` are parsed and
# leaf-counted but have no nix surface yet (#1374 is the standing
# proposal to give them one); the retained `places`/
# `plugins.<id>.mount` mirrors (#1339 item 2, #1161) are untouched.
config-vocab =
pkgs.runCommand "trollshell-config-vocab-check" { nativeBuildInputs = [ pkgs.python3 ]; }
''
cd ${self}
python3 nix/lint-config-vocab.py
touch $out
'';
# `programs.trollshell.claudeBridge.baseUrl` (`nix/module-common.nix`)
# is a plain nix string literal naming the same socket
# `crates/hytte-ai-providers/src/unix.rs`'s `BRIDGE_SOCKET_DIR`/
# `BRIDGE_SOCKET_FILE`/`BRIDGE_BASE_URL` constants define, and both
# that crate's and `crates/hytte-claude-bridge/src/main.rs`'s module
# docs restate in prose (#1099 review M1, #1100; hardened against a
# #1103 adversarial review's M1/M2/L1/L2 on the nix-to-Rust seam and
# the doc-prose scan). The Rust constants are already pinned against
# raw literals by plain `cargo test`
# (`bridge_url_resolves_to_the_bridge_socket_path`,
# `the_socket_path_is_the_one_the_client_dials`); nothing compiles
# the nix literal or either crate's doc prose against them — #1099's
# review measured that a rename of the Rust file name left
# `cargo test`, `cargo clippy` and every module-eval check green,
# because the daemon binds a path only a live session would notice
# went stale. Same posture as `bind-pins` above: a source-level
# defect no compile in this flake can see, so a script rather than a
# test, with no cargoArtifacts so it goes red in seconds.
# `nix/lint-bridge-socket.py`'s own header has the full story,
# including why this is not a `cargo test` (the `config-vocab`
# crane-filter reasoning applies unchanged).
bridge-socket =
pkgs.runCommand "trollshell-bridge-socket-check" { nativeBuildInputs = [ pkgs.python3 ]; }
''
cd ${self}
python3 nix/lint-bridge-socket.py
touch $out
'';
# The preem GL renderer's shaders, compiled in the dialect the shell
# compiles them in (#893 stage B). Same posture and same reasons as
# `bind-pins` above: a source-level defect no compile in this flake
# can see, checked by a script rather than a test, with no
# cargoArtifacts so it goes red in seconds.
#
# Nothing else looks inside those files. They are `include_str!`'d
# `&'static str`s until a *driver* compiles them, and no check here
# does — `system-tests` gained a software GL driver (llvmpipe, via
# `mesa`, #1036) so its own GL-context tests now run for real, but
# those tests exercise only inline dummy shaders defined in the test
# module, never these actual `preem_gl`/shader-widget source files.
# So without this a typo'd identifier ships green and surfaces as a
# blank chip on glass.
#
# The design spec's CI table named **naga** for this row. Measured, it
# cannot do the job: naga 26's GLSL frontend rejects the entire ES
# profile — `#version 300/310/320 es` each come back
# `InvalidVersion(N)` + `InvalidProfile("es")`, so the spec's own
# fallback (decision 6, "drop to 310 es") does not reach either — and
# at `#version 450` it stops on `NotImplemented("variable
# qualifier")` for the `flat in` / `precision` declarations these
# shaders are written with. The spec preferred naga because glslang is
# C++ FFI that would have to live in the `hytte-gl` unsafe island: a
# real objection to *linking* a validator in for #893's untrusted
# plugin shaders, and none at all to running the binary at build time.
# So this is glslang, it validates the real `#version 320 es`, and it
# adds zero `Cargo.lock` entries.
glsl =
pkgs.runCommand "trollshell-glsl-check"
{
nativeBuildInputs = [
pkgs.python3
pkgs.glslang
];
}
''
cd ${self}
python3 nix/lint-glsl.py --self-test
python3 nix/lint-glsl.py
touch $out
'';
# `crates/trollshell-control-center/src/plugins_tab.rs`'s
# `manifest_id_of_exec` is a hand transcription of this file's own
# `inferManifestId` above — the rule that turns a plugin's `exec`
# basename into the id it registers under, which the control-center
# reads back off a plugin to decide which settings-form family (if
# any) that plugin owns. Nothing checked the two agreed (#1372, from
# the #1365 adversarial review's L5): a plugin whose family lookup
# depends on the id (e.g. `stats`) could silently stop mounting a
# form if either side drifted, with no error — "no family" is a
# legitimate answer. Same posture as `bind-pins` above: a
# source-level defect no compile in this flake can see, so a script
# rather than a test, with no cargoArtifacts so it goes red in
# seconds; the Rust side is graded separately, by
# `checks.workspace-tests`'
# `manifest_id_of_exec_matches_the_shared_table` test — both read
# the SAME table, `nix/manifest-id-cases.txt`, whose own header has
# that table's provenance. `nix/lint-manifest-id.py`'s own header
# has the full story, including why this re-parses the nix rule's
# literals out of source on every run (so a changed literal is
# caught as real per-row drift) rather than shelling out to `nix
# eval` from inside the sandbox (recursive Nix, not enabled here).
lint-manifest-id =
pkgs.runCommand "trollshell-lint-manifest-id-check" { nativeBuildInputs = [ pkgs.python3 ]; }
''
cd ${self}
python3 nix/lint-manifest-id.py
touch $out
'';
# Run the hermetic internals suite (#1115): `cargo test --workspace`
# WITHOUT `--features system-tests` — the same command
# `nix/package.nix`'s `workspace` derivation ran under its own
# `doCheck = true` before #1115 turned that off so a consumer's
# `nix build` doesn't pay for it (and so the deps stage feeding it,
# `cargoArtifactsBinOnly`, doesn't have to compile the
# dev-dependency graph for every consumer either — see that file's
# `cargoArtifacts`/`cargoArtifactsBinOnly` split). Same tests, same
# gate, only moved to where the gate lives: `nix flake check`
# already builds the package (#449) and already runs
# `system-tests` as its own check below; the hermetic suite had no
# check of its own and rode the package instead.
#
# Shape matches hyperhive's own split for the identical problem
# (Mara, #1115): `hyperhive/nix/rust.nix:43-94` two named
# `buildDepsOnly` caches, one per audience;
# `hyperhive/nix/packages/default.nix:61-68` the deploy path takes
# the no-test-graph one; `hyperhive/nix/checks.nix:58-73` the check
# takes the one WITH the test graph via a plain `craneLib.cargoTest`
# rather than `mkCargoDerivation`. Same trade here: `cargoTest`
# hardcodes `checkPhaseCargoCommand` to `cargoWithProfile test
# ${cargoExtraArgs} ${cargoTestExtraArgs}`, which is fine for a
# plain hermetic run — `system-tests` below needs `mkCargoDerivation`
# directly only because it wraps that command in `xvfb-run` for a
# display, which this check doesn't need.
#
# `trollshell.passthru.commonArgs` already carries `cargoExtraArgs =
# "--workspace --locked"` (#572, one cargo scope for every stage),
# so — unlike hyperhive's own `cargo-test`, which has no such shared
# scope and spells `cargoTestExtraArgs = "--workspace";` itself —
# nothing further is needed here to get `--workspace`; leaving
# `cargoTestExtraArgs` at `cargoTest`'s own default (`""`) matches
# how `clippy` above already reuses the same `cargoExtraArgs` and
# only adds its own check-specific flags.
#
# `cargoArtifacts = trollshell.passthru.cargoArtifacts`, NOT
# `cargoArtifactsBinOnly`: since #1115 the former is the one built
# with `buildDepsOnly`'s own default `doCheck = true` (dev-deps +
# test harnesses compiled and cached), same cache `clippy` above
# already reuses — the latter has no dev-dependency graph for a
# `cargo test` run to build against.
workspace-tests = craneLib.cargoTest (
trollshell.passthru.commonArgs
// {
cargoArtifacts = trollshell.passthru.cargoArtifacts;
# The same icon-theme gate `nix/package.nix`'s `preCheck` carried
# before #1115 — see
# `every_icon_name_exists_in_the_adwaita_theme_on_the_search_path`
# (crates/hytte-plugin-niri-layouts/src/plugin.rs). nixpkgs puts
# no icon theme on a build's `XDG_DATA_DIRS` of its own accord,
# so without this the test silently *skips* instead of gating
# anything; `TROLLSHELL_REQUIRE_ICON_THEME=1` turns a missing
# theme into a failure instead, so it can never rot back into
# a silent no-op. `system-tests` below carries its own copy for
# the same reason (#1053 review) and this check inherits
# neither.
preCheck = ''
export XDG_DATA_DIRS="${pkgs.adwaita-icon-theme}/share''${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}"
export TROLLSHELL_REQUIRE_ICON_THEME=1
'';
# Leaf/terminal check: nothing consumes its target dir. Same
# `doInstallCargoArtifacts = false` reasoning as `system-tests`
# below — crane's default packs the whole (multi-GiB) target
# dir into `$out`, which is pure waste for a check nothing
# chains off of.
doInstallCargoArtifacts = false;
}
);
# Gate rustdoc warnings on the workspace's docs (#1328): a
# `private_intra_doc_links` or `broken_intra_doc_links` warning is a
# broken link on a **published** API page — the one place a plugin
# author reads the SDK — and nothing caught it before this: `cargo
# doc` was not part of `nix flake check`, so PR #1322 shipped ten
# `private_intra_doc_links` warnings on `hytte-plugin`'s public
# `effective_mount`/`effective_mount_from` docs unnoticed. Own leaf
# row, on the `workspace-tests` precedent above — not folded into
# it, since a doc build is its own failure mode and `glsl`/
# `bind-pins`/`lints-tables` already establish that a leaf check
# gets its own row rather than riding an existing one. Shares
# `cargoArtifacts` with the checks above instead of compiling its
# own dependency graph.
#
# `craneLib.cargoDoc` defaults `cargoDocExtraArgs` to `--no-deps`
# (only this workspace's own docs, not every dependency's) and
# `cargoExtraArgs` to `--locked`; `commonArgs.cargoExtraArgs`
# already carries `--workspace --locked` (#572), so its own call
# would run exactly `cargo doc --workspace --locked --no-deps`,
# matching the `cargo doc --workspace --no-deps` in the issue and
# CLAUDE.md — but `cargo doc` documents only a package's **lib**
# target, and only its `pub` items, so no private item anywhere in
# this workspace was ever rustdoc'd, and no module that lives only
# in a same-named **bin** target's own tree (`trollshell/src/
# main.rs`'s, `plugin_launcher.rs` among it) was reachable at all
# (#1403). Measuring `cargo rustdoc -p trollshell --bin trollshell
# -- --document-private-items` against `origin/main` gives 73
# warnings — 70 of them in modules `lib.rs` also compiles (missed
# only because the lib pass never turns on private items), and just
# 3 truly bin-only (`commands.rs:71`, `plugin_launcher.rs:150`,
# `:1477`) — #1403's own framing ("everything that lives only in
# `main.rs`'s tree went unchecked") named the smaller half; #1406's
# review measured the real one.
#
# `craneLib.cargoDoc` cannot host that second command: `lib/
# cargoDoc.nix` hardcodes `buildPhaseCargoCommand` to its one
# `cargo doc` call and silently drops any override, the same reason
# `system-tests` above reaches for `mkCargoDerivation` directly
# instead of `cargoTest`. `cargo rustdoc` also refuses `--workspace`
# outright (it only ever checks one target), so every per-package
# invocation below names `-p <pkg> --bin <pkg>` directly rather than
# reading `cargoExtraArgs`. `--document-private-items` is already
# what cargo passes a bin's own rustdoc invocation by default
# (`cargo rustdoc -v -p trollshell --bin trollshell` shows it on
# the `--crate-type bin` line with no flag at all) — spelled out
# anyway because it is the one thing this whole second command is
# *for*, and a reader should not have to know cargo's default to
# see that. `RUSTDOCFLAGS="-D warnings"` is what turns a warning
# into a build failure for **every** command here — `cargo doc`/
# `cargo rustdoc` alone exit 0 on one — since it is one env var the
# whole derivation shares, not a per-command flag.
#
# #1403 shipped checking only `trollshell` itself; #1406's review
# found the same gap open on four more packages — every package
# whose **bin** target shares its **lib** target's crate name is
# the exact condition `cargo doc` silently skips (rust-lang/
# cargo#6313's "output filename collision"), and that is not just
# `trollshell`. `same_name_bins` below asks `cargo metadata` for
# that condition directly rather than hand-listing the packages it
# is currently true for (`hytte-plugin-agents`, `hytte-plugin-
# stats`, `trollshell-agent-window` and `hytte-plugin-infobroker`,
# at the time of writing — `ls crates/hytte-plugin-*` rots, a nix
# attribute list would too), so a new package of this shape is
# covered the day it lands rather than the day someone remembers to
# add a line. Target names are normalized (hyphens to underscores)
# before comparing: a lib target's name is always its package name
# with hyphens replaced (crate identifiers cannot contain them), a
# bin target's keeps the package's own spelling, and `trollshell`'s
# own crate name happens to have no hyphens at all, which is why
# the original single-package line here never needed this step.
# The `grep -qx trollshell` guard is the "a static list needs a
# completeness check" rule turned around: rather than asserting a
# hand-kept list is complete, it asserts the *derivation* is —
# `trollshell` is the one package #1403 already proved belongs in
# this set, so its absence means the metadata/jq query broke, not
# that the workspace shrank.
#
# Every one of the four added packages writes into the exact same
# per-crate `target/doc/<crate>/` directory its own lib pass (in
# the workspace `cargo doc` above) already populated — the same
# collision `trollshell` has always had, just once per package
# instead of once. Left alone, each per-package `cargo rustdoc`
# call below would silently replace that package's lib pages with
# its bin pages, and the install phase would ship whichever ran
# last. Nothing in the tree **consumes** `$out/share/doc` (this
# check only builds it), so nothing was actually broken by that —
# but an earlier revision of this comment claimed "one move
# captures both invocations' output", which was not quite true (a
# #1406 review finding: the bin's pages really do replace the lib's
# for the one package that collides). The install phase below keeps
# both instead of asserting it doesn't need to: the workspace
# pass's output is copied aside into `workspace-doc/` before any
# bin pass can touch `target/doc`, and each bin pass's own
# `target/doc/<crate>/` subtree is copied into `bin-doc/<pkg>/`
# right after it runs, before the next package's pass reuses
# `target/doc` again. `installPhaseCommand` moves those two saved
# copies to `$out/share/doc` (every lib crate, `--no-deps` —
# unaffected by any of this) and `$out/share/doc-bins/<pkg>/
# <crate>/` (that one package's bin pages, private items included)
# rather than `lib/cargoDoc.nix`'s own single `mv`.
#
# This is not free: `-p <pkg>` resolves a narrower feature union
# than `--workspace` did (the #587 trap: `tokio` without `signal`,
# `glib` without `v2_68`/`v2_70`, `ureq` without `json`, for
# `-p trollshell` alone), so cargo re-checks a chunk of the
# dependency graph under every package's own feature set instead of
# reusing `cargoArtifacts` untouched — the PR that added the four
# extra packages measured and reported per-package numbers rather
# than fixing this here; `resolver.feature-unification =
# "workspace"` would, but this toolchain's cargo still answers
# `warning: ignoring resolver.feature-unification without
# -Zfeature-unification`. Affordable on this check's own runner, so
# left as a known, growing cost rather than a reason to check only
# one package.
#
# #1429: neither pass above ever checks a **lib** target's own
# private items. The workspace `cargo doc --no-deps` pass documents
# every lib crate but only its `pub` surface; `same_name_bins`'s
# `--document-private-items` pass documents a **bin** target's own
# private tree, but a bin target compiles its same-named lib as an
# ordinary external dependency — only the lib's `pub` items are
# visible to it, same as any other consumer — so a package whose
# `main.rs` is a thin wrapper around its own lib
# (`hytte-plugin-stats`, `hytte-plugin-agents`, …) had its lib's
# private items checked nowhere at all, and neither did a lib-only
# crate with no bin (`hytte-bus`, `hytte-preem`, …), which
# `same_name_bins` never reaches in the first place. Measured: 81
# warnings across the 19 workspace members that carry a `lib`
# target, none of them caught by either pass above (#1429/#1430).
#
# `lib_pkgs` below asks `cargo metadata` for that condition directly
# (`.targets[] | .kind | index("lib")`) instead of a hand-kept list,
# on `same_name_bins`'s own precedent just above — a new lib crate
# is covered the day it lands. The `grep -qx hytte` guard is the
# same "assert the derivation, not a hand list" check
# `same_name_bins` already makes, against a *different*
# always-true member on purpose: `hytte` carries no bin target at
# all, so its presence proves this query is finding lib crates and
# not silently reusing `same_name_bins`'s own list. Each pass writes
# into the same `docRoot/<crate>/` the workspace and bin passes
# already used, so — same reasoning as the bin loop above — the
# install phase below saves each package's output aside into
# `lib-doc/<pkg>/<crate>/` right after its own `cargo rustdoc` call,
# before the next package's call reuses `target/doc` again.
rustdoc = craneLib.mkCargoDerivation (
trollshell.passthru.commonArgs
// {
pnameSuffix = "-doc";
cargoArtifacts = trollshell.passthru.cargoArtifacts;
nativeBuildInputs = trollshell.passthru.commonArgs.nativeBuildInputs ++ [ pkgs.jq ];
RUSTDOCFLAGS = "-D warnings";
buildPhaseCargoCommand = ''
cargoWithProfile doc --workspace --locked --no-deps
docRoot="''${CARGO_TARGET_DIR:-target}/''${CARGO_BUILD_TARGET:-}/doc"
if ! [ -d "$docRoot" ]; then
docRoot="''${CARGO_TARGET_DIR:-target}/doc"
fi
mkdir -p workspace-doc
cp -r "$docRoot"/. workspace-doc/
same_name_bins=$(cargo metadata --offline --no-deps --format-version 1 | jq -r '
.packages[] as $p
| ($p.targets | map(select(.kind | index("lib")) | .name | gsub("-"; "_"))) as $libs
| ($p.targets | map(select(.kind | index("bin")) | .name | gsub("-"; "_"))) as $bins
| select(any($libs[]; . as $l | $bins | index($l) != null))
| $p.name
')
if ! grep -qx trollshell <<< "$same_name_bins"; then
echo "checks.rustdoc: same-name-bin derivation did not include trollshell (cargo metadata/jq query broken)" >&2
exit 1
fi
mkdir -p bin-doc
for pkg in $same_name_bins; do
cargoWithProfile rustdoc -p "$pkg" --locked --bin "$pkg" -- --document-private-items
crate="$(printf '%s' "$pkg" | tr '-' '_')"
mkdir -p "bin-doc/$pkg"
cp -r "$docRoot/$crate" "bin-doc/$pkg/$crate"
done
lib_pkgs=$(cargo metadata --offline --no-deps --format-version 1 | jq -r '
.packages[] | select(.targets[] | .kind | index("lib")) | .name
')
if ! grep -qx hytte <<< "$lib_pkgs"; then
echo "checks.rustdoc: lib-package derivation did not include hytte (cargo metadata/jq query broken)" >&2
exit 1
fi
mkdir -p lib-doc
for pkg in $lib_pkgs; do
cargoWithProfile rustdoc -p "$pkg" --locked --lib -- --document-private-items
crate="$(printf '%s' "$pkg" | tr '-' '_')"
mkdir -p "lib-doc/$pkg"
cp -r "$docRoot/$crate" "lib-doc/$pkg/$crate"
done
'';
# Leaf/terminal check: nothing consumes its target dir (this
# already matches `cargoDoc`'s own default, spelled out here
# for the same reason `workspace-tests` above does).
doInstallCargoArtifacts = false;
# See the long comment above `rustdoc` for why this installs
# three trees rather than `lib/cargoDoc.nix`'s single `mv`:
# `$out/share/doc` is the workspace pass's own output (every
# lib crate, `--no-deps`), saved aside before any bin or lib
# private-items pass could overwrite it; `$out/share/doc-bins/
# <pkg>/<crate>/` is one same-name-bin package's bin-only pages;
# `$out/share/doc-libs/<pkg>/<crate>/` is one lib package's own
# private-items pages (#1429) — both saved right after their own
# `cargo rustdoc` call and before the next package's call reuses
# `target/doc`.
installPhaseCommand = ''
mkdir -p $out/share
mv workspace-doc $out/share/doc
mv bin-doc $out/share/doc-bins
mv lib-doc $out/share/doc-libs
'';
}
);
# Run the `system-tests` cargo-feature bucket (#232): the
# whole-file-`#![cfg(feature = "system-tests")]` integration tests
# in hytte-bus/hytte-reactive/hytte-ui, plus the `#[cfg(all(test,
# feature = "system-tests"))]` GTK unit-test modules in hytte-ui,
# PLUS the #893 stage B CPU/GL parity harness. Split out to
# nix/checks/system-tests.nix (#1102), mirroring how `packages`
# already lives under `nix/*.nix` — see that file for the full
# rationale (why mkCargoDerivation, the llvmpipe/systemd preCheck,
# the parity gate). This call passes exactly what the block used
# to close over here: `craneLib` and the two
# `trollshell.passthru.*` values it read off the package
# derivation (`pkgs` is auto-supplied by `callPackage`) — plus,
# since #1413, the package's `assets` derivation, whose
# `share/trollshell` the check exports as `TROLLSHELL_DATA_DIR`.
system-tests = pkgs.callPackage ./nix/checks/system-tests.nix {
inherit craneLib;
commonArgs = trollshell.passthru.commonArgs;
cargoArtifacts = trollshell.passthru.cargoArtifacts;
assets = trollshell.passthru.assets;
};
# Evaluate homeModules.default against a real home-manager module set so
# the config bodies (systemd user units, session vars, the swaybg gate,
# the awww assertion) are actually forced — not just parsed. Builds a
# trivial derivation that deepSeq's the config attrs that hold those
# bodies, so a broken body fails the check rather than silently lurking.
hm-module =
let
hm = home-manager.lib.homeManagerConfiguration {
inherit pkgs;
modules = [
self.homeModules.default
{
home = {
username = "alice";
homeDirectory = "/home/alice";
stateVersion = "24.11";
# `nixpkgs.follows = "nixpkgs"` means HM and nixpkgs ride
# the same unstable channel but report different release
# numbers; the check is module-eval coverage, not a