{ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; devshell.url = "github:numtide/devshell"; # Only ever used to evaluate the home-manager module in `checks`; nothing # in the package or the NixOS module depends on it. home-manager = { url = "github:nix-community/home-manager"; inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = inputs@{ flake-parts, ... }: flake-parts.lib.mkFlake { inherit inputs; } { imports = [ inputs.devshell.flakeModule ]; flake.nixosModules = { streamline = ./nix/module.nix; default = inputs.self.nixosModules.streamline; }; flake.homeModules = { streamline = ./nix/hm-module.nix; default = inputs.self.homeModules.streamline; }; flake.overlays.default = final: _prev: { streamline = final.callPackage ./nix/package.nix { }; }; systems = [ "x86_64-linux" "aarch64-linux" ]; perSystem = { pkgs, self', ... }: let go = pkgs.callPackage ./nix/go.nix { }; nodejs = pkgs.callPackage ./nix/node.nix { }; pnpm = pkgs.callPackage ./nix/pnpm.nix { }; in { packages = { streamline = pkgs.callPackage ./nix/package.nix { }; default = self'.packages.streamline; }; formatter = pkgs.nixfmt-tree; checks = { build = self'.packages.default; # Boots the NixOS module in a VM and drives the real service: # health probe, seeded admin login, SPA shell. This is what makes # the module a supported surface rather than an untested one. nixos = pkgs.testers.runNixOSTest { imports = [ ./nix/test.nix ]; node.specialArgs.streamlineModule = inputs.self.nixosModules.streamline; # The nodes take the package from the overlay rather than from a # specialArg, so the wiring the README documents is the wiring # under test. Needs pkgsReadOnly off — a test node otherwise # inherits this `pkgs` and refuses its own `nixpkgs.overlays`. node.pkgsReadOnly = false; defaults.nixpkgs.overlays = [ inputs.self.overlays.default ]; }; # The home-manager module shares nix/options.nix and nix/common.nix # with the NixOS one, so building its activation package is enough # to catch the drift that matters — a unit that will not evaluate. home-manager = (inputs.home-manager.lib.homeManagerConfiguration { inherit pkgs; modules = [ inputs.self.homeModules.streamline { home = { username = "streamline"; homeDirectory = "/home/streamline"; stateVersion = "26.05"; }; services.streamline = { enable = true; package = self'.packages.streamline; settings.library.movie_path = "/home/streamline/media/movies"; }; } ]; }).activationPackage; # Derived from the package rather than a runCommand of its own: # golangci-lint type-checks, so it needs the module cache and the # //go:embed assets that buildGoModule already arranges. A # standalone check has neither and only ever reports "could not # import entgo.io/ent" from a sandbox with no network. lint = self'.packages.streamline.overrideAttrs (old: { pname = "streamline-lint"; buildPhase = '' ${old.preBuild} export HOME=$TMPDIR go tool golangci-lint run --timeout 15m ''; installPhase = "touch $out"; postInstall = ""; dontFixup = true; }); }; devshells.default = { env = [ { name = "CGO_ENABLED"; value = 0; } # buildGoModule sets this for `nix build`, nothing sets it for # `nix develop` — and the default, `auto`, downloads whatever # toolchain go.mod asks for. That would paper over exactly the # mismatch nix/go.nix exists to refuse, in the one shell that is # supposed to be the reproduction. { name = "GOTOOLCHAIN"; value = "local"; } { name = "CHROME_PATH"; value = "${pkgs.chromium}/bin/chromium"; } { name = "PLAYWRIGHT_BROWSERS_PATH"; value = pkgs.playwright-driver.browsers; } { name = "PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS"; value = true; } { name = "PLAYWRIGHT_NODEJS_PATH"; value = "${nodejs}/bin/node"; } { name = "KUBECONFIG"; eval = "\${PWD}/deploy/helm/streamline/kubeconfig.yaml"; } ]; packages = with pkgs; [ act git-cliff # The let-bound pins from nix/{go,node}.nix, not `pkgs.go` and # `pkgs.nodejs` — a `let` binding wins over `with pkgs`. The # devshell must be on the same compiler and runtime as CI. go gopls # The let-bound pin from nix/node.nix, not `pkgs.nodejs` — a # `let` binding wins over `with pkgs`. The devshell and the # package build must agree with what CI installs. nodejs # The let-bound pin from nix/pnpm.nix, for the same reason. pnpm go-task openssl playwright-mcp pre-commit kind kubernetes-helm kubectl grype syft biome nixfmt # `task migrate:diff` writes the migration; rehashing atlas.sum # afterwards needs the CLI, which the ent library does not ship. atlas jq # headless: the e2e transcode spec drives ffmpeg/ffprobe, and # nothing here needs the X11/SDL outputs the full build pulls in. ffmpeg-headless sqlite ]; }; }; }; }