# this entry-point exposes all packages, hosts, etc, but with no purity guarnatees. # the intended way to use this is to first copy every .nix file and dependency in this repo to the nix store, then enter this file. # entering this file *before* copying anything into the nix store can cause interesting # race conditions or eval failures. # # see default.nix for a wrapper around this with better purity guarantees. { localSystem ? builtins.currentSystem, }: let mkPkgs = branch: config: let mkNixpkgs = import ./pkgs/by-name/nixpkgs-bootstrap/mkNixpkgs.nix config; in ( import ./pkgs/by-name/nixpkgs-bootstrap/${branch}.nix { inherit mkNixpkgs; } ).pkgs.extend (import ./overlays/all.nix); pkgs = mkPkgs "master" { inherit localSystem; }; inherit (pkgs) lib; evalHost = { name, localSystem, system, branch ? "master", variant ? null }: let pkgs = mkPkgs branch { inherit localSystem system; }; in pkgs.nixos [ (import ./hosts/instantiate.nix { hostName = name; inherit variant; }) (import ./modules) pkgs.sops-nix.nixosModules.sops ]; mkFlavoredHost = args: let plainHost = evalHost args; # expose the toplevel nixos system as the toplevel attribute itself, # with nested aliases for other common build targets addPassthru = host: host.config.system.build.toplevel.overrideAttrs (base: { passthru = (base.passthru or {}) // { inherit (host) config; inherit (host.config.sane) fs; inherit (host.config.system.build) img pkgs; programs = builtins.mapAttrs (_: p: p.package) host.config.sane.programs; toplevel = host.config.system.build.toplevel; #< self extendModules = arg: addPassthru (host.extendModules arg); }; }); in addPassthru plainHost; mkHost = args: { "${args.name}" = mkFlavoredHost args; "${args.name}-light" = mkFlavoredHost (args // { variant = "light"; }); "${args.name}-min" = mkFlavoredHost (args // { variant = "min"; }); "${args.name}-staging" = mkFlavoredHost (args // { branch = "staging"; }); "${args.name}-staging-light" = mkFlavoredHost (args // { branch = "staging"; variant = "light"; }); "${args.name}-staging-min" = mkFlavoredHost (args // { branch = "staging"; variant = "min"; }); "${args.name}-next" = mkFlavoredHost (args // { branch = "staging-next"; }); "${args.name}-next-light" = mkFlavoredHost (args // { branch = "staging-next"; variant = "light"; }); "${args.name}-next-min" = mkFlavoredHost (args // { branch = "staging-next"; variant = "min"; }); }; # this exists to unify my kernel configs across different platforms. # ordinarily, certain kernel config options are derived by nixpkgs using the `system` parameter, # via . # but i want these a little more normalized, which is possible either here, or # by assigning `boot.kernelPackages`. # elaborate = system: system; elaborate = system: { static ? false }: let abi = if static then "musl" else "gnu"; e = lib.systems.elaborate { # N.B.: "static" can mean a few things: # 1. binaries should be linked statically (stdenv.hostPlatform.isStatic == true). # 2. host libc doesn't support dlopen (stdenv.hostPlatform.hasSharedLibraries == false). # # nixpkgs' `pkgsStatic` is the stricter meaning of "static": no `.so` files, period. # this means plugin-heavy frameworks like `gobject-introspection` probably just cannot ever work (?). # TODO: i'd _prefer_ to use the weaker kind of "static", and support all the traditional packages, # but for now that actually results in fewer working packages (e.g. `xdg-dbus-proxy`). system = "${system}-${abi}"; isStatic = static; # hasSharedLibraries = true; }; in e // { extensions = e.extensions // { # when `hasSharedLibraries` == false, need to explicitly set this here to fix eval of many packages. this is not a long-term fix. sharedLibrary = ".so"; }; linux-kernel = { inherit (e.linux-kernel) name baseConfig target; } // (lib.optionalAttrs (e.linux-kernel ? DTB) { inherit (e.linux-kernel) DTB; }) // { # explicitly ignore nixpkgs' extraConfig and preferBuiltin options autoModules = true; # build all features as modules where possible, especially because # 1. some bootloaders fail on large payloads and this allows the kernel/initrd to be smaller. # 2. building as module means i can override that module very cheaply as i develop. preferBuiltin = false; # `target` support matrix: # Image: aarch64:yes (nixpkgs default) x86_64:no # Image.gz: aarch64:yes, if capable bootloader x86_64:no # zImage aarch64:no x86_64:yes # bzImage aarch64:no x86_64:yes (nixpkgs default) # vmlinux aarch64:? x86_64:no? # vmlinuz aarch64:? x86_64:? # uImage aarch64:bootloader? x86_64:probably not # # target = if system == "x86_64-linux" then "bzImage" else "Image"; }; }; hosts = builtins.foldl' # XXX: i `elaborate` localSystem the same as i do `system` because otherwise nixpkgs # sees they're (slightly) different and forces everything down the (expensive) cross-compilation path. (acc: host: acc // mkHost ({ localSystem = elaborate localSystem {}; } // host)) {} [ # real hosts: # { name = "crappy"; system = "armv7l-linux"; } { name = "cadey"; system = elaborate "aarch64-linux" {}; } { name = "desko"; system = elaborate "x86_64-linux" {}; } { name = "flowy"; system = elaborate "x86_64-linux" {}; } { name = "lappy"; system = elaborate "x86_64-linux" {}; } { name = "moby"; system = elaborate "aarch64-linux" {}; } { name = "servo"; system = elaborate "x86_64-linux" {}; } { name = "rescue"; system = elaborate "x86_64-linux" {}; } # pseudo hosts used for debugging { name = "baseline-x86_64"; system = elaborate "x86_64-linux" {}; } { name = "baseline-aarch64"; system = elaborate "aarch64-linux" {}; } { name = "static-x86_64"; system = elaborate "x86_64-linux" { static = true; }; } { name = "static-aarch64"; system = elaborate "aarch64-linux" { static = true; }; } ]; # subAttrNames :: AttrSet -> [ String ] # returns the names of all items in `attrs` which are themselves attrsets. # presumably, this is the list of items which we may wish to descend into. subAttrNames = attrs: builtins.attrNames (subAttrs attrs); subAttrs = attrs: lib.filterAttrs (name: value: let # many attributes in the `pkgs` set do not evaluate (even `pkgs.sane`, thanks to e.g. linuxPackages). # wrap in `tryEval` to deal with that, and not descend into such attributes. isAttrs = builtins.tryEval (builtins.isAttrs value); in isAttrs.success && isAttrs.value ) attrs; # update only packages maintained by me: # shouldUpdate = pkg: let # maintainers = ((pkg.meta or {}).maintainers or []); # in # pkg ? updateScript && # (lib.elem lib.maintainers.colinsane maintainers || maintainers == []) # ; # update any package possible, and just rely on good namespacing: shouldUpdate = pkg: pkg ? updateScript; # given the path to a package, and that package, returns a list of all attr-paths (stringified) # which should be updated as part of that package (including the package in question). mkUpdateList = prefix: pkg: (lib.optionals (shouldUpdate pkg) [ prefix ]) ++ lib.concatMap (nestedName: mkUpdateListIfAuto "${prefix}.${nestedName}" pkg."${nestedName}") (lib.optionals (pkg.recurseForDerivations or false) (subAttrNames pkg)) ; # a package can set `passthru.updateWithSuper = false;` if it doesn't want to be auto-updated. mkUpdateListIfAuto = prefix: pkg: lib.optionals (pkg.updateWithSuper or true) (mkUpdateList prefix pkg); mkUpdateInfo = prefix: pkg: let # the actual shell command which can update the package, after an environment has been configured for the updater: updateArgv = lib.optionals (pkg ? updateScript) ( if builtins.isList pkg.updateScript then pkg.updateScript else if pkg.updateScript ? command then builtins.map builtins.toString pkg.updateScript.command else if (pkg.updateScript.meta or {}) ? mainProgram then # raw derivation like `writeShellScriptBin` [ "${lib.getExe pkg.updateScript}" ] else if builtins.isPath pkg.updateScript then # in-tree update script like `updateScript = ./update.sh` [ pkg.updateScript ] else [] ); in { "${prefix}" = { subPackages = mkUpdateList prefix pkg; updateScript = let pname = lib.escapeURL (pkg.pname or (pkg.name or "unknown")); script = pkgs.writeShellScriptBin "update-${pname}" '' # update script assumes $PWD is an entry point to a writable copy of my nix config, # so provide that: SELF_PATH=$PWD/$0 REPO_ROOT=$(${lib.getExe pkgs.git} -C "$(dirname SELF_PATH)" rev-parse --show-toplevel) pushd $REPO_ROOT/integrations/nix-update UPDATE_NIX_NAME=${pkg.name or ""} \ UPDATE_NIX_PNAME=${pkg.pname or ""} \ UPDATE_NIX_OLD_VERSION=${pkg.version or ""} \ UPDATE_NIX_ATTR_PATH=${prefix} \ ${lib.escapeShellArgs updateArgv} popd ''; in lib.optionalString (shouldUpdate pkg && updateArgv != []) (lib.getExe script); }; } // builtins.foldl' (acc: subPkgName: acc // mkUpdateInfo "${prefix}.${subPkgName}" pkg."${subPkgName}") {} (if pkg.recurseForDerivations or false then subAttrNames pkg else []) ; updateInfo = mkUpdateInfo "sane" pkgs.sane; in { inherit hosts; inherit updateInfo; # AttrSet mapping attrpath to a list of attrpaths representing all updatable packages beneath it updateTargets = builtins.mapAttrs (_: v: v.subPackages) (lib.filterAttrs (_: v: v.subPackages != []) updateInfo); # AttrSet mapping attrpath to the path of a shell script which can be used to update the package at that attrpath updateScripts = builtins.mapAttrs (_: v: v.updateScript) (lib.filterAttrs (_: v: v.updateScript != "") updateInfo); } // pkgs