postjevsql.git / flake.nix
1{
2  # nix provisions toolchains and Postgres; buck2 owns the target
3  # graph. Same division of labour as deizel/buck2-cakeml.
4  description = "postjevsql: a Rust Postgres extension for TypeSafe's Jev";
5
6  inputs = {
7    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
8    # The estate package set (buck2, see below); nixpkgs follows ours.
9    nix-pkgs.url = "git+ssh://git@github.com/deizel/nix-pkgs";
10    nix-pkgs.inputs.nixpkgs.follows = "nixpkgs";
11    # third-party/jevcrates is a git submodule; without this the flake's
12    # source omits it and the sandbox build cannot find the jev crates.
13    self.submodules = true;
14  };
15
16  outputs =
17    {
18      self,
19      nixpkgs,
20      nix-pkgs,
21    }:
22    let
23      system = "x86_64-linux";
24      pkgs = nixpkgs.legacyPackages.${system};
25      inherit (nixpkgs) lib;
26      # The supported majors (PG_MAJORS in build/defs.bzl). The devshell's
27      # Postgres is the default, the one buck's graph builds against.
28      majors = import ./nix/majors.nix { inherit lib; };
29      postgresql = pkgs."postgresql_${majors.default}";
30      # Every supported major's server and pg_config, for targets
31      # configured for //platforms:pgNN (build/defs.bzl's
32      # pg_major_select); PATH and PGRX_PG_CONFIG_PATH carry only the
33      # default's.
34      perMajor = lib.concatMapStringsSep "\n" (
35        m:
36        let
37          pg = pkgs."postgresql_${m}";
38        in
39        "postgres_bin_${m} = ${pg}/bin\npg_config_${m} = ${pg.pg_config}/bin/pg_config"
40      ) majors.all;
41      # buck2 2026-08-22 drops its own gRPC connections under bursts of
42      # tiny messages (h2's small-DATA-frame budget; tests/CLAUDE.md):
43      # the forkserver exits and every later local action fails. Fixed in
44      # facebook/buck2 1cc3e05f85, first released in 2026-09-15. nix-pkgs
45      # carries that release for the whole estate, and its package throws
46      # once nixpkgs catches up (header there has the full account).
47      buck2 = nix-pkgs.packages.${system}.buck2;
48    in
49    {
50      # The extension per supported major (contract §3, nix/package.nix),
51      # as nixpkgs names its sets: postgresqlNNPackages.postjevsql.
52      legacyPackages.${system} = lib.listToAttrs (
53        map (major: {
54          name = "postgresql${major}Packages";
55          value.postjevsql = pkgs."postgresql_${major}".pkgs.callPackage ./nix/package.nix { inherit buck2; };
56        }) majors.all
57      );
58      packages.${system} = {
59        default = self.legacyPackages.${system}."postgresql${majors.default}Packages".postjevsql;
60        # The sidecar CLI (nix/sidecar-cli.nix), the one engine behind
61        # every sidecar launcher.
62        postjevsql-sidecar = pkgs.callPackage ./nix/sidecar-cli.nix {
63          postjevsql = self.packages.${system}.default;
64        };
65        # The sidecar as a `docker load`-able image (nix/sidecar-image.nix).
66        postjevsql-sidecar-image = pkgs.callPackage ./nix/sidecar-image.nix {
67          extension = ps: ps.callPackage ./nix/package.nix { inherit buck2; };
68          cli = self.packages.${system}.postjevsql-sidecar;
69        };
70      };

Sidecar mode (CLAUDE.md Deployment); nix/sidecar.nix. The flake's module builds the extension with the pinned buck2.

74      nixosModules.sidecar =
75        { lib, ... }:
76        {
77          imports = [ ./nix/sidecar.nix ];
78          services.postjevsql-sidecar.extension = lib.mkDefault (
79            ps: ps.callPackage ./nix/package.nix { inherit buck2; }
80          );
81        };

The module booted against a loopback target (nix/sidecar-test.nix).

84      checks.${system} = {
85        sidecar = import ./nix/sidecar-test.nix {
86          inherit pkgs;
87          sidecar = self.nixosModules.sidecar;
88        };
89        # The image booted in docker against the same target (nix/sidecar-image-test.nix).
90        sidecar-image = import ./nix/sidecar-image-test.nix {
91          inherit pkgs;
92          image = self.packages.${system}.postjevsql-sidecar-image;
93        };
94      };
96      devShells.${system}.default = pkgs.mkShell {
97        packages = [
98          buck2
99          pkgs.reindeer
100          pkgs.rustc
101          pkgs.cargo
102          pkgs.clippy
103          pkgs.rustfmt
104          pkgs.python3
105          # The prelude's system_cxx_toolchain drives clang/clang++.
106          pkgs.clang
107          pkgs.lld
108          # pgrx-pg-sys runs bindgen against the server headers; the hook
109          # sets LIBCLANG_PATH and the libc include flags libclang lacks.
110          # Buck's local actions inherit the daemon's environment.
111          pkgs.rustPlatform.bindgenHook
112          postgresql
113          postgresql.pg_config
114          postgresql.dev
115        ];
116        PGRX_PG_CONFIG_PATH = "${postgresql.pg_config}/bin/pg_config";
117        # Buck execs tools by absolute path (the prelude's cc shim uses
118        # os.execl, which never searches PATH), so the toolchain cell reads
119        # store paths from here rather than trusting the environment.
120        # Written only when it changes, and by rename: buck logs any write as a
121        # changed file, and would read a half-written one as a changed config.
122        shellHook = ''
123          local_cfg="$(git rev-parse --show-toplevel)/.buckconfig.local"
124          cat > "$local_cfg.tmp" <<EOF
125          [nix]
126          cc = ${pkgs.clang}/bin/clang
127          cxx = ${pkgs.clang}/bin/clang++
128          ar = ${pkgs.clang}/bin/ar
129          python = ${pkgs.python3}/bin/python3
130          [rust]
131          rustc_identity = ${pkgs.rustc}
132          [postjevsql]
133          postgres_bin = ${postgresql}/bin
134          ${perMajor}
135          EOF
136          if cmp -s "$local_cfg.tmp" "$local_cfg"; then
137            rm "$local_cfg.tmp"
138          else
139            mv "$local_cfg.tmp" "$local_cfg"
140          fi
141        '';
142      };
143    };
144}