{ description = "oleks's personal Flake hub – a place to publish custom packages and overlays"; inputs = { fleet-pins.url = "git+https://git.oleks.space/oleks/fleet-pins?ref=main"; nixpkgs.follows = "fleet-pins/nixpkgs-projects"; flake-utils.url = "github:numtide/flake-utils"; # Shared per-archetype parity publish-app builders (cluster#104, emmett#44). # flake-hub is an ATTIC-CLOSURE repo: it builds package closures and warms # the Attic cache with them — there is NO registry artifact. # mkAtticClosurePublish makes that archetype explicit to pipeline-doctor. parity.url = "git+https://git.oleks.space/oleks/parity-lib"; # Hyprspace source; no flake.nix in the repo so we consume it as raw src. # Pin tracks the last v0.52-compatible commit of Hyprspace. hyprspace = { url = "github:KZDKM/Hyprspace/0467be86b18cfc324fab04afbd40fe9ef80f7fa9"; flake = false; }; # Google Antigravity packaging. Upstream auto-updates daily; we re-expose # the overlay and build into our attic cache so emmett pulls from there. antigravity-nix = { url = "github:jacopone/antigravity-nix"; inputs.nixpkgs.follows = "nixpkgs"; }; # nix-deps: "see the real cost of installing packages on NixOS". # Re-exposed through our overlay so CI builds it into attic. nix-deps = { url = "github:manelinux/nix-deps"; inputs.nixpkgs.follows = "nixpkgs"; }; # stalewood — find/reap merged git worktrees. Ships its own flake; # re-expose its package (mirrors the nix-deps pattern). stalewood = { url = "github:retif/stalewood"; inputs.nixpkgs.follows = "nixpkgs"; }; # woodpecker-peek — tray app for Woodpecker CI (on git.oleks.space). # Re-exposed so flake-hub CI warms attic and emmett pulls cached. woodpecker-peek = { url = "git+https://git.oleks.space/oleks/woodpecker-peek?ref=main"; inputs.nixpkgs.follows = "nixpkgs"; }; # mcp-chrome — Chrome MCP server + extension (English-localized fork of # hangwin/mcp-chrome). Re-exposes the from-source wasm-simd worker (proven # green, ~22 s) and the full chrome-mcp-extension build. The extension # target is KNOWN-BROKEN under nix-daemon at this pin (see oleks/mcp-chrome # issue #1 close comment); expect attic to miss it until that's resolved. mcp-chrome = { url = "git+https://git.oleks.space/oleks/mcp-chrome?ref=main"; inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = { self, nixpkgs, fleet-pins, flake-utils, parity, hyprspace, antigravity-nix, nix-deps, stalewood, woodpecker-peek, mcp-chrome, ... }: let # Systems that have native builders buildSystems = [ "x86_64-linux" "aarch64-linux" ]; # Cross-compilation targets from x86_64-linux crossTargets = { "s390x-linux" = "s390x-linux"; }; mkPackages = pkgs: let sys = pkgs.stdenv.hostPlatform.system; # Antigravity ships a Google-provided x86_64/aarch64 Linux binary. # Skip it for cross targets (e.g. s390x) where it can't run anyway. supportsAntigravity = sys == "x86_64-linux" || sys == "aarch64-linux"; xontribs = import ./packages/xontribs.nix { inherit (pkgs) python3Packages fetchurl; }; in { hello-world = pkgs.callPackage ./packages/hello-world.nix { }; geesefs = pkgs.callPackage ./packages/geesefs.nix { }; metamcp = pkgs.callPackage ./packages/metamcp.nix { }; xonsh = pkgs.callPackage ./packages/xonsh.nix { xonsh-unwrapped = import ./packages/xonsh-unwrapped.nix { inherit (pkgs) lib python3Packages fetchFromGitHub; }; }; } # Native-only packages — skip on s390x cross (gitea's pnpm step and # cgo+sqlite link don't cross-compile cleanly). // nixpkgs.lib.optionalAttrs (sys == "x86_64-linux" || sys == "aarch64-linux") { gitea-local-fork = let # Our fork's go.mod requires Go 1.26.3; nixpkgs at this pin has # only 1.26.0 (and unstable has 1.26.2). Bump the package's src. go = pkgs.go_1_26.overrideAttrs (_: rec { version = "1.26.3"; src = pkgs.fetchurl { url = "https://go.dev/dl/go${version}.src.tar.gz"; hash = "sha256-HGRoddCqh5kTMYTtV895/yS97+jIggRwYCqdPW2Rkrg="; }; }); in pkgs.callPackage ./packages/gitea-local-fork.nix { buildGoModule = pkgs.buildGoModule.override { inherit go; }; }; } # Xontribs: pass into `programs.xonsh.extraPackages` or # `pkgs.xonsh.override { extraPackages = ps: [...]; }`. // xontribs # Antigravity: re-expose jacopone/antigravity-nix's outputs so emmett # consumes a single flake-hub input and our CI builds into attic. // nixpkgs.lib.optionalAttrs supportsAntigravity { inherit (antigravity-nix.packages.${sys}) google-antigravity google-antigravity-no-fhs google-antigravity-ide google-antigravity-ide-no-fhs google-antigravity-cli ; # nix-deps' flake only outputs eachDefaultSystem (no s390x), so # gate it on the same native x86_64/aarch64 condition. nix-deps = nix-deps.packages.${sys}.default; # stalewood — re-exposed from its own flake. No s390x output, # so it rides the same native-only gate. stalewood = stalewood.packages.${sys}.default; # woodpecker-peek — same pattern; consumers (emmett) read the # attic-cached binary via flake-hub's overlay, then set # services.woodpecker-peek.package = pkgs.woodpecker-peek;. woodpecker-peek = woodpecker-peek.packages.${sys}.default; # mcp-chrome — Rust→wasm worker (proven green) plus the full # chrome-mcp-extension build. The latter is KNOWN-BROKEN under # nix-daemon at this pin; flake-hub CI will miss the cache on it # until upstream resolves that. wasm-simd alone is what consumers # actually pull cached today (commits 9534234, b276465 in # oleks/mcp-chrome sync the built wasm back into the tree). mcp-chrome-wasm-simd = mcp-chrome.packages.${sys}.wasm-simd; mcp-chrome-extension = mcp-chrome.packages.${sys}.chrome-mcp-extension; }; # Overlay providing Hyprspace. Requires `pkgs.hyprland` to be present # (consumer applies the Hyprland flake's overlay first). Kept out of # `mkPackages` because standalone `nix build .#hyprspace` has no # Hyprland in scope. hyprspaceOverlay = final: _prev: { hyprspace = final.callPackage ./packages/hyprspace.nix { src = hyprspace; }; }; # Rustc bootstrap: symlink_file panics with "File exists" during # s390x cross-compilation. Multiple call sites use t!(symlink_file(...)). # Patch the symlink_file method body to remove an existing dest first. # Exported as overlays.s390xRustcSymlink so nixos-ci consumes this single # definition (and the patch travels with it) instead of duplicating it. s390xRustcSymlinkOverlay = final: prev: let patchedRustcUnwrapped = prev.rustc-unwrapped.overrideAttrs (old: { patches = (old.patches or [ ]) ++ [ ./patches/rustc-symlink-file-eexist.patch ]; }); in { rustc-unwrapped = patchedRustcUnwrapped; rustc = prev.rustc.override { rustc-unwrapped = patchedRustcUnwrapped; }; }; # Overlays needed for s390x cross-compilation of attic-client s390xOverlays = [ # OpenSSL s390x assembly uses z10 instructions (cijne) that the # nix-bootstrapped assembler doesn't recognize (final: prev: { openssl = prev.openssl.overrideAttrs (old: { configureFlags = (old.configureFlags or [ ]) ++ [ "no-asm" ]; }); }) # musl doesn't support s390x long double (IBM double-double format: # LDBL_MANT_DIG=106, sizeof=16). Make musl appear unavailable so # busybox-sandbox-shell uses glibc static instead. (final: prev: { busybox-sandbox-shell = prev.busybox-sandbox-shell.override { musl = prev.musl // { meta = prev.musl.meta // { platforms = [ ]; }; }; }; }) # libarchive tests fail in k8s pod sandboxes (final: prev: { libarchive = prev.libarchive.overrideAttrs (_: { doCheck = false; }); }) # nix 2.28 is a direct dependency of attic-client — disable tests # and fix missing RPATH for boost/zstd/libarchive (final: prev: { nixVersions = prev.nixVersions // { nix_2_28 = prev.nixVersions.nix_2_28.overrideAttrs (old: { doCheck = false; doInstallCheck = false; nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ final.patchelf ]; postFixup = (old.postFixup or "") + '' for rpath in ${final.boost}/lib ${final.zstd.out}/lib ${final.libarchive.out}/lib; do patchelf --add-rpath "$rpath" $out/bin/nix for lib in $out/lib/lib*.so; do [ -f "$lib" ] && patchelf --add-rpath "$rpath" "$lib" done done ''; }); }; }) # LLVM test failures in CI pod environment — override libllvm # (not llvm) so it propagates through rustc bootstrap chain (final: prev: { llvmPackages = prev.llvmPackages // { libllvm = prev.llvmPackages.libllvm.overrideAttrs (old: { doCheck = false; doInstallCheck = false; nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ prev.gitMinimal ]; }); }; }) # Rustc symlink_file "File exists" fix (defined + exported above). s390xRustcSymlinkOverlay ]; # Native builds native = flake-utils.lib.eachSystem buildSystems ( system: let pkgs = import nixpkgs { inherit system; }; packages = mkPackages pkgs; in { packages = packages // { default = packages.hello-world; }; formatter = pkgs.nixfmt-rfc-style; devShells.default = pkgs.mkShell { name = "oleks-hub-shell"; buildInputs = [ self.packages.${system}.default ]; nativeBuildInputs = with pkgs; [ nix fmt ]; shellHook = '' echo "Welcome to the oleks Flake hub development shell." ''; }; } ); # Cross-compiled builds (built from x86_64-linux) cross = builtins.listToAttrs ( builtins.map ( target: let targetOverlays = { "s390x-linux" = s390xOverlays; } .${target} or [ ]; pkgs = import nixpkgs { system = "x86_64-linux"; crossSystem.config = nixpkgs.lib.systems.examples.${ { "s390x-linux" = "s390x"; } .${target} }.config; overlays = targetOverlays; }; packages = mkPackages pkgs; crossOnlyPackages = { "s390x-linux" = { inherit (pkgs) attic-client rustc cargo rustfmt sccache mold ; nix = pkgs.nixVersions.nix_2_28; }; } .${target} or { }; in { name = target; value = packages // crossOnlyPackages // { default = packages.hello-world; }; } ) (builtins.attrNames crossTargets) ); in native // { packages = (native.packages or { }) // cross; overlays = { default = final: _prev: mkPackages final; gcc15-fixes = import ./overlays/gcc15-fixes.nix; hyprspace = hyprspaceOverlay; # Single home for the s390x rustc symlink_file patch overlay so # consumers (nixos-ci) don't re-declare it + the patch file. s390xRustcSymlink = s390xRustcSymlinkOverlay; }; # `nix run .#` — local-parity entrypoints (emmett#44, cluster#192, # ATTIC-CLOSURE archetype, cluster#104). The stage/publish/push-staged # apps come straight from parity-lib's mkAtticClosurePublish, so CI and a # local run share one audited implementation and cannot drift. There is NO # registry artifact — these apps build the package closures and warm the # Attic cache with them. # # TWO HALVES (emmett#44): STAGE `nix build`s every package in the arch's # list into the local /nix store (cluster-independent, runs on emmett); # PUBLISH additionally `attic push`es each closure to the cache that lives # next to the cluster. Local runs DRY-RUN (stage + show the pushes) unless # `--publish`/PUBLISH=1. # # nix run .#stage-x86_64-linux stage amd64 closures, no publish # nix run .#publish stage amd64 then push if PUBLISH=1 # nix run .#publish -- --publish actually push to attic # nix run .#push-staged replay already-staged amd64 paths # # arm64 leg: aarch64-linux cannot be built on emmett (linux/amd64) and the # native packages have no cross path, so it MUST run on an aarch64 node # (.woodpecker/arm64.yaml runs `PUBLISH=1 nix run .#publish-aarch64-linux`). # # The package set per arch mirrors flake-hub's CI warm list (was # ci/publish.py packages_for): the always-on core plus arch-conditional # extras. Cross-only/known-broken targets stay out (see comments). apps = let # The Attic cache token lives at `pass infra/attic/ci_token` and the # cache is reached via the armer hairpin endpoint — preserve both so # the push is byte-for-byte the pre-parity behaviour. atticEndpoint = "https://nix-cache-upload.oleks.space"; atticPass = "infra/attic/ci_token"; # Package names to warm into Attic for a given native arch. # Native arches only (amd64/arm64); mcp-chrome-extension stays OUT # (known-broken under nix-daemon at this pin, see oleks/mcp-chrome #1). packageNamesFor = arch: [ "hello-world" "geesefs" "xonsh" ] ++ nixpkgs.lib.optionals (arch == "x86_64-linux" || arch == "aarch64-linux") [ "woodpecker-peek" "mcp-chrome-wasm-simd" "gitea-local-fork" "google-antigravity" "google-antigravity-no-fhs" "google-antigravity-ide" "google-antigravity-ide-no-fhs" "google-antigravity-cli" ]; drvsFor = arch: map (n: self.packages.${arch}.${n}) (packageNamesFor arch); buildersFor = arch: parity.lib.mkParityBuilders (import nixpkgs { system = arch; }); atticAppsFor = arch: (buildersFor arch).mkAtticClosurePublish { drvs = drvsFor arch; inherit arch; endpoint = atticEndpoint; passEntry = atticPass; }; amd64Apps = atticAppsFor "x86_64-linux"; arm64Apps = atticAppsFor "aarch64-linux"; in nixpkgs.lib.genAttrs buildSystems (system: # amd64 is the emmett-buildable arch: expose its stage/publish/ # push-staged plus the top-level `publish`. Also surface the arm64 # stage/publish under their arch-suffixed names for the node-bound # CI leg (.woodpecker/arm64.yaml). { inherit (amd64Apps) "stage-x86_64-linux" "publish-x86_64-linux" "publish" "push-staged" ; "stage-aarch64-linux" = arm64Apps."stage-aarch64-linux"; "publish-aarch64-linux" = arm64Apps."publish-aarch64-linux"; }); }; }