tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

flake.nix (46358B)


      1 {
      2   description = "tricu";
      3 
      4   inputs = {
      5     nixpkgs.url = "github:NixOS/nixpkgs";
      6     browser-wasi = { url = "github:haskell-wasm/browser_wasi_shim/2f86b49dce50916e2984029c535321e34b234229"; flake = false; };
      7     ghc-wasm.url = "github:haskell-wasm/ghc-wasm-meta/8079eb96bbc63823d99ad8acaff0765532625474";
      8     flake-utils.url = "github:numtide/flake-utils";
      9   };
     10 
     11   outputs = { self, nixpkgs, flake-utils, ghc-wasm, browser-wasi }:
     12     flake-utils.lib.eachDefaultSystem (system:
     13       let
     14         pkgs = nixpkgs.legacyPackages.${system};
     15         siteRelease = import ./ext/demo-site/release.nix { inherit pkgs; haskellRuntime = self.packages.${system}.haskellWasmBrowser; };
     16         packageName = "tricu";
     17         containerPackageName = "${packageName}-container";
     18 
     19         # Keep active build/test source hashes scoped to current sources.
     20         tricuSource = pkgs.lib.cleanSourceWith {
     21           name = "tricu-source";
     22           src = self;
     23           filter = path: type:
     24             let
     25               relative = pkgs.lib.removePrefix "${toString self}/" path;
     26               top = builtins.head (pkgs.lib.splitString "/" relative);
     27             in pkgs.lib.cleanSourceFilter path type && builtins.elem top [
     28               "src" "lib" "test" "bench" "bootstrap" "demos" "ext"
     29               "tricu.cabal" "LICENSE" "README.md"
     30             ];
     31         };
     32 
     33         # All Haskell components now share the source-free runtime. The boundary
     34         # check below rejects reintroducing retired frontend files into inputs.
     35         runtimeSource = tricuSource;
     36 
     37         # Narrow GHC 9.14 compatibility changes; upstream test suites stay enabled.
     38         nativeHaskellOverrides = final: previous: {
     39           # The upstream completion-order fix preserves the diagnostic-message test.
     40           sandwich = hsLib.overrideCabal previous.sandwich (old: {
     41             patches = (old.patches or []) ++ [ ./ext/haskell/patches/sandwich-introduce-with-completion.patch ];
     42           });
     43           string-interpolate = hsLib.overrideCabal previous.string-interpolate (old: {
     44             postPatch = (old.postPatch or "") + ''
     45               substituteInPlace string-interpolate.cabal \
     46                 --replace-fail 'template-haskell <2.24' 'template-haskell <2.25'
     47             '';
     48           });
     49           brick = hsLib.overrideCabal previous.brick (old: {
     50             postPatch = (old.postPatch or "") + ''
     51               substituteInPlace brick.cabal \
     52                 --replace-fail 'base >= 4.9.0.0 && < 4.22.0.0' 'base >= 4.9.0.0 && < 4.23.0.0'
     53             '';
     54           });
     55           config-ini = hsLib.overrideCabal previous.config-ini (old: {
     56             postPatch = (old.postPatch or "") + ''
     57               substituteInPlace config-ini.cabal \
     58                 --replace-fail 'containers            >=0.5   && <0.8' 'containers            >=0.5   && <0.9'
     59             '';
     60           });
     61         };
     62         haskellPackages = pkgs.haskell.packages.ghc914.override {
     63           overrides = nativeHaskellOverrides;
     64         };
     65         picHaskellPackages = haskellPackages.override {
     66           # Build tools and dependencies must use the same boot-library IDs.
     67           buildHaskellPackages = picHaskellPackages;
     68           overrides = pkgs.lib.composeExtensions nativeHaskellOverrides (final: previous: {
     69             mkDerivation = args: previous.mkDerivation (args // {
     70               configureFlags = (args.configureFlags or []) ++ [
     71                 "--ghc-option=-fPIC" "--ghc-option=-optc-fPIC"
     72               ];
     73             });
     74           });
     75           ghc = haskellPackages.ghc.override {
     76             enableRelocatedStaticLibs = true;
     77             # These build-only variants are unused by the embedding experiment.
     78             enableProfiledLibs = false;
     79             enableDocs = false;
     80           };
     81         };
     82         hsLib = pkgs.haskell.lib;
     83 
     84         staticPkgs = pkgs.pkgsStatic;
     85         staticHaskellPackages = staticPkgs.haskellPackages;
     86         staticHsLib = staticPkgs.haskell.lib;
     87 
     88         tricuMuslStatic =
     89           staticHsLib.justStaticExecutables (
     90             staticHsLib.dontCheck (
     91               staticHaskellPackages.callCabal2nix packageName runtimeSource {}
     92             )
     93           );
     94 
     95         tricuStatic = pkgs.runCommand "${packageName}-static-upx" {
     96           nativeBuildInputs = [ pkgs.upx ];
     97         } ''
     98             mkdir -p $out/bin
     99             cp ${tricuMuslStatic}/bin/tricu $out/bin/tricu
    100             chmod +w $out/bin/tricu
    101 
    102             # Good compression, slower build.
    103             upx --best --lzma $out/bin/tricu
    104 
    105             chmod 755 $out/bin/tricu
    106           '';
    107 
    108         tricuPackageTests =
    109           haskellPackages.callCabal2nix packageName tricuSource {};
    110 
    111         tricuPackage =
    112           hsLib.dontCheck (
    113             haskellPackages.callCabal2nix packageName runtimeSource {}
    114           );
    115 
    116         # Compile once with the packaged stable compiler. Runtime needs neither
    117         # source lookup nor a writable compiler/application cache.
    118         mkRegistryBundle = name: source: pkgs.runCommand "${name}-bundle" {
    119           nativeBuildInputs = [ tricuPackage ];
    120         } ''
    121           mkdir -p $out
    122           tricu arboricx compile --file ${source} --store build-store \
    123             --output $out/server.arboricx --compile-timeout 900 \
    124             +RTS -N1 -M8192m -RTS
    125           tricu arboricx import --file $out/server.arboricx --store $out/runtime \
    126             > import.log
    127           sed -n 's/^Created module alias .* -> //p' import.log > $out/manifest
    128           test "$(wc -l < $out/manifest)" -eq 1
    129           grep -Eq '^[0-9a-f]{64}$' $out/manifest
    130         '';
    131 
    132         arboricxServerBundle = mkRegistryBundle "arboricx-server" "${./.}/tricu-apps/arboricxServer.tri";
    133         arboricxServerRunner = mkRegistryRunner "arboricx-server" arboricxServerBundle;
    134         arboricxStreamingBundle = mkRegistryBundle "arboricx-streaming" "${./.}/tricu-apps/arboricxStreaming.tri";
    135         mkRegistryRunner = name: bundle: pkgs.writeShellScriptBin name ''
    136           exec ${tricuPackage}/bin/tricu arboricx run \
    137             --module "$(${pkgs.coreutils}/bin/cat ${bundle}/manifest)" --name main \
    138             --store ${bundle}/runtime --io --no-result \
    139             +RTS -N1 -M2048m -RTS
    140         '';
    141         arboricxStreamingRunner = mkRegistryRunner "arboricx-streaming" arboricxStreamingBundle;
    142         arboricxStreamingFaultBundle = mkRegistryBundle "arboricx-streaming-fault" "${./.}/test/io/registry-log-failure.tri";
    143         arboricxStreamingFaultRunner = mkRegistryRunner "arboricx-streaming-fault" arboricxStreamingFaultBundle;
    144         mkRegistryTest = name: runner: pkgs.runCommand "${name}-test" {
    145           nativeBuildInputs = [ pkgs.python3 ];
    146         } ''
    147           mkdir -p $out
    148           python3 ${./test/arboricx_deployment.py} ${runner}/bin/${name} \
    149             --io-version 2 --handler-policy \
    150             --fault-runner ${arboricxStreamingFaultRunner}/bin/arboricx-streaming-fault \
    151             --report $out/report.json
    152           python3 ${./test/streaming_protocol.py} ${runner}/bin/${name} \
    153             --io-version 2 > $out/protocol.txt
    154           PYTHONDONTWRITEBYTECODE=1 python3 ${./.}/test/registry_download.py \
    155             ${runner}/bin/${name} ${./test/fixtures/compiler-library.arboricx} \
    156             --report $out/compiler-download.json > $out/compiler-download-summary.json
    157           PYTHONDONTWRITEBYTECODE=1 python3 ${./.}/test/body_retention.py \
    158             --runner ${runner}/bin/${name} \
    159             --phases streaming-publish streaming-download --bounded-streaming \
    160             --sizes 65536 1048576 --repeats 3 \
    161             --directory $out/retention > $out/retention-summary.json
    162         '';
    163 
    164         tricuBench =
    165           (hsLib.overrideCabal
    166             (hsLib.doBenchmark (hsLib.dontCheck (
    167               haskellPackages.callCabal2nix packageName runtimeSource {}
    168             )))
    169             (oldAttrs: {
    170               postInstall = (oldAttrs.postInstall or "") + ''
    171                 mkdir -p $out/bin
    172                 cp dist/build/tricu-bench/tricu-bench $out/bin/
    173               '';
    174             })).overrideAttrs (_: {
    175               # Require a caller-selected worker bundle for the timing run.
    176               benchmarkPhase = "true";
    177             });
    178 
    179         # Opt-in application build with its own acceptance gate. Retain the exact
    180         # sources, compiler metadata and receipts beside the worker bundle.
    181         benchmarkWorkers = pkgs.runCommand "tricu-benchmark-workers" {
    182           nativeBuildInputs = [ tricuPackage ];
    183         } ''
    184           mkdir -p $out/workspace
    185           ${pkgs.lib.concatMapStringsSep "\n" (name:
    186             "cp ${./lib}/${name}.tri $out/workspace/${name}.tri") [
    187               "base" "prelude" "bytes" "conversions" "io" "parser" "http" "socket"
    188             ]}
    189           cp ${./lib/math/native.tri} $out/workspace/native_math.tri
    190           cp ${./lib/intensionalContracts.tri} $out/workspace/intensional.tri
    191           cp ${./lib/arboricx/server.tri} $out/workspace/server.tri
    192           cp ${./bench/workers.tri} $out/workspace/workers.tri
    193           cp ${./bootstrap/stable-compiler.json} $out/compiler.json
    194           timeout 930 tricu arboricx compile --file $out/workspace/workers.tri \
    195             --store $out/store --output $out/workers.arboricx --compile-timeout 900 \
    196             +RTS -N1 -M8192m -RTS
    197           sha256sum $out/workers.arboricx | cut -d ' ' -f 1 > $out/sha256
    198         '';
    199 
    200         siteJavaScript = import ./ext/demo-site/javascript.nix { inherit pkgs; };
    201 
    202         tricuHaskell = import ./ext/haskell/build.nix {
    203           inherit pkgs haskellPackages;
    204           source = tricuSource;
    205         };
    206         tricuHaskellArchive = import ./ext/haskell/build.nix {
    207           inherit pkgs;
    208           source = tricuSource;
    209           haskellPackages = picHaskellPackages;
    210           archiveHaskell = true;
    211         };
    212         embeddingIntegration = libraryPackage: pkgs.runCommand "tricu-haskell-ffi-integration" {
    213           nativeBuildInputs = [ pkgs.python3 tricuPackage ];
    214         } ''
    215           export PYTHONDONTWRITEBYTECODE=1
    216           export PYTHONPATH=${tricuSource}/ext/haskell/python
    217           mkdir -p $out workspace
    218           cp ${tricuSource}/lib/base.tri workspace/base.tri
    219           cp ${tricuSource}/lib/math/native.tri workspace/native_math.tri
    220           cp ${tricuSource}/lib/io.tri workspace/io.tri
    221           cp ${tricuSource}/lib/handlers.tri workspace/handlers.tri
    222           cp ${tricuSource}/demos/io/copy.tri workspace/copy.tri
    223           tricu arboricx compile --file workspace/copy.tri --store copy-store \
    224             --output $out/copy.arboricx --compile-timeout 120 +RTS -N1 -M1024m -RTS
    225           library=${libraryPackage}/lib/libtricu-haskell${pkgs.stdenv.hostPlatform.extensions.sharedLibrary}
    226           python3 ${tricuSource}/ext/haskell/test_embedding.py "$library" \
    227             ${tricuSource}/test/fixtures --copy-bundle $out/copy.arboricx > $out/integration.json
    228           python3 ${tricuSource}/ext/haskell/compiler_probe.py "$library" \
    229             ${tricuSource}/bootstrap/build-stable.arboricx --tricu ${tricuPackage}/bin/tricu \
    230             --directory compiler-probe > $out/compiler.log
    231           cp compiler-probe/report.json $out/compiler.json
    232         '';
    233         tricuPython = pkgs.python3Packages.buildPythonPackage {
    234           pname = "tricu-runtime";
    235           version = "2026.10.24";
    236           src = ./ext/haskell/python;
    237           pyproject = true;
    238           build-system = [ pkgs.python3Packages.setuptools ];
    239           postInstall = ''
    240             ln -s ${tricuHaskell}/lib/libtricu-haskell${pkgs.stdenv.hostPlatform.extensions.sharedLibrary} \
    241               $out/${pkgs.python3.sitePackages}/libtricu-haskell${pkgs.stdenv.hostPlatform.extensions.sharedLibrary}
    242           '';
    243           pythonImportsCheck = [ "tricu" ];
    244         };
    245         pythonWithTricu = pkgs.python3.withPackages (_: [ tricuPython ]);
    246 
    247         tricuFilecheckBundle = pkgs.runCommand "tricu-filecheck-worker" {
    248           nativeBuildInputs = [ tricuPackage ];
    249         } ''
    250           mkdir -p $out workspace
    251           cp ${tricuSource}/lib/base.tri workspace/base.tri
    252           cp ${tricuSource}/lib/math/native.tri workspace/native_math.tri
    253           cp ${tricuSource}/lib/io.tri workspace/io.tri
    254           cp ${tricuSource}/demos/python-filecheck/worker.tri workspace/worker.tri
    255           tricu arboricx compile --file workspace/worker.tri --store build-store \
    256             --output $out/worker.arboricx --compile-timeout 120 +RTS -N1 -M1024m -RTS
    257           sha256sum $out/worker.arboricx | cut -d' ' -f1 > $out/worker.sha256
    258         '';
    259         tricuFilecheck = pkgs.python3Packages.buildPythonPackage {
    260           pname = "tricu-filecheck";
    261           version = "2026.10.24";
    262           src = ./demos/python-filecheck;
    263           pyproject = true;
    264           build-system = [ pkgs.python3Packages.setuptools ];
    265           dependencies = [ tricuPython ];
    266           postPatch = ''
    267             cp ${tricuFilecheckBundle}/worker.arboricx tricu_filecheck/worker.arboricx
    268             printf 'BUNDLE_SHA256 = "%s"\n' "$(cat ${tricuFilecheckBundle}/worker.sha256)" \
    269               > tricu_filecheck/_bundle_pin.py
    270           '';
    271           pythonImportsCheck = [ "tricu_filecheck" ];
    272         };
    273         pythonWithFilecheck = pkgs.python3.withPackages (_: [ tricuFilecheck ]);
    274 
    275         tricuWorkflowBundle = pkgs.runCommand "tricu-workflow-demo-bundle" {
    276           nativeBuildInputs = [ tricuPackage ];
    277         } ''
    278           mkdir -p $out workspace
    279           cp ${tricuSource}/lib/base.tri workspace/base.tri
    280           cp ${tricuSource}/lib/math/native.tri workspace/native_math.tri
    281           cp ${tricuSource}/demos/workflow-planning/workflow.tri workspace/workflow.tri
    282           cp ${tricuSource}/demos/workflow-planning/pipeline.tri workspace/pipeline.tri
    283           tricu arboricx compile --file workspace/pipeline.tri --store build-store \
    284             --output $out/pipeline.arboricx --compile-timeout 120 +RTS -N1 -M1024m -RTS
    285           sha256sum $out/pipeline.arboricx | cut -d' ' -f1 > $out/pipeline.sha256
    286         '';
    287         tricuWorkflow = pkgs.python3Packages.buildPythonPackage {
    288           pname = "tricu-workflow-demo";
    289           version = "2026.10.24";
    290           src = ./demos/workflow-planning;
    291           pyproject = true;
    292           build-system = [ pkgs.python3Packages.setuptools ];
    293           dependencies = [ tricuPython ];
    294           postPatch = ''
    295             cp ${tricuWorkflowBundle}/pipeline.arboricx tricu_workflow_demo/pipeline.arboricx
    296             printf 'BUNDLE_SHA256 = "%s"\n' "$(cat ${tricuWorkflowBundle}/pipeline.sha256)" \
    297               > tricu_workflow_demo/_bundle_pin.py
    298           '';
    299           pythonImportsCheck = [ "tricu_workflow_demo" ];
    300         };
    301         pythonWithWorkflow = pkgs.python3.withPackages (_: [ tricuWorkflow ]);
    302 
    303         # ------------------------------------------------------------------
    304         # PHP FFI host
    305         # ------------------------------------------------------------------
    306         tricuPhp = pkgs.stdenv.mkDerivation {
    307           pname = "tricu-php";
    308           version = "2026.10.24";
    309           src = ./ext/php;
    310           nativeBuildInputs = [ pkgs.makeWrapper ];
    311           dontBuild = true;
    312           installPhase = ''
    313             mkdir -p $out/share/tricu-php $out/bin
    314             cp -r src public run.php $out/share/tricu-php/
    315             cp ${tricuHaskell}/include/tricu*.h $out/share/tricu-php/
    316             ln -s ${tricuHaskell}/lib/libtricu-haskell${pkgs.stdenv.hostPlatform.extensions.sharedLibrary} $out/share/tricu-php/
    317             makeWrapper ${phpWithFfi}/bin/php $out/bin/tricu-php \
    318               --add-flags "$out/share/tricu-php/run.php" \
    319               --set TRICU_LIB "${tricuHaskell}/lib/libtricu-haskell${pkgs.stdenv.hostPlatform.extensions.sharedLibrary}"
    320           '';
    321         };
    322 
    323         tricuJs = pkgs.buildNpmPackage {
    324           pname = "tricu-js";
    325           version = "2026.10.24";
    326           src = ./ext/js;
    327           npmDepsHash = "sha256-81C7tsNcbyZVhm3uqiWdDQxp5LAXXO9aueHdMDztCfM=";
    328           dontNpmBuild = true;
    329           installPhase = ''
    330             mkdir -p $out/lib/ $out/bin
    331             cp -r . $out/lib/
    332             ln -s ${tricuHaskell}/lib/libtricu-haskell${pkgs.stdenv.hostPlatform.extensions.sharedLibrary} $out/lib/src/
    333             makeWrapper ${pkgs.nodejs}/bin/node $out/bin/arboricx-run \
    334               --add-flags "$out/lib/src/cli.js"
    335           '';
    336           nativeBuildInputs = [ pkgs.makeWrapper ];
    337         };
    338 
    339         tricuJsTests = pkgs.runCommand "tricu-js-tests" {
    340           nativeBuildInputs = [ pkgs.nodejs ];
    341         } ''
    342           export TRICU_LIB=${tricuHaskell}/lib/libtricu-haskell${pkgs.stdenv.hostPlatform.extensions.sharedLibrary}
    343           cp -r ${tricuSource} source
    344           chmod -R u+w source
    345           cd source/ext/js
    346           ln -s ${tricuJs}/lib/node_modules node_modules
    347           timeout 90s npm test
    348           test "$(${tricuJs}/bin/arboricx-run run ../../test/fixtures/id.arboricx id hello)" = hello
    349           test "$(${tricuJs}/bin/arboricx-run run ../../test/fixtures/true.arboricx true --format bool)" = true
    350           if ${tricuJs}/bin/arboricx-run run ../../test/fixtures/id.arboricx missing; then exit 1; fi
    351           mkdir -p $out
    352         '';
    353 
    354         phpWithFfi = pkgs.php.withExtensions (exts: [ pkgs.phpExtensions.ffi ]);
    355         tricuPhpTests = pkgs.runCommand "tricu-php-tests" {
    356           nativeBuildInputs = [ phpWithFfi pkgs.python3 ];
    357         } ''
    358           export TRICU_LIB=${tricuHaskell}/lib/libtricu-haskell${pkgs.stdenv.hostPlatform.extensions.sharedLibrary}
    359           mkdir -p $out
    360           timeout 90s php ${tricuSource}/ext/php/tests/conformance.php "$TRICU_LIB" \
    361             ${tricuSource}/test/fixtures ${tricuSource}/ext/haskell/bindings/fixtures.json > $out/report.json
    362           test "$(${tricuPhp}/bin/tricu-php run ${tricuSource}/test/fixtures/id.arboricx id hello)" = hello
    363           test "$(${tricuPhp}/bin/tricu-php run ${tricuSource}/test/fixtures/false.arboricx false --format bool)" = false
    364           if ${tricuPhp}/bin/tricu-php run ${tricuSource}/test/fixtures/id.arboricx missing; then exit 1; fi
    365           timeout 60s python3 ${tricuSource}/ext/php/tests/web.py ${phpWithFfi}/bin/php \
    366             ${tricuPhp}/share/tricu-php/public ${tricuSource}/test/fixtures/id.arboricx
    367         '';
    368         tricuPythonTests = pkgs.runCommand "tricu-python-tests" {
    369           nativeBuildInputs = [ pythonWithTricu ];
    370         } ''
    371           export PYTHONDONTWRITEBYTECODE=1
    372           mkdir -p $out
    373           # Exercise installed-module discovery through its default library lookup.
    374           python3 -c 'from tricu import Library; c = Library().context(); c.close()'
    375           timeout 90s python3 ${tricuSource}/ext/haskell/bindings/python.py \
    376             ${tricuHaskell}/lib/libtricu-haskell${pkgs.stdenv.hostPlatform.extensions.sharedLibrary} \
    377             ${tricuSource}/test/fixtures ${tricuSource}/ext/haskell/bindings/fixtures.json > $out/report.json
    378         '';
    379       in {
    380         packages.haskellWasmBrowser = import ./ext/haskell-wasm/browser.nix {
    381           inherit pkgs;
    382           probe = self.packages.${system}.haskellWasmProbe;
    383           toolchain = ghc-wasm.packages.${system}.all_9_14;
    384           shimSource = browser-wasi;
    385         };
    386         packages.haskellWasmProbe = import ./ext/haskell-wasm/build.nix {
    387           inherit pkgs;
    388           toolchain = ghc-wasm.packages.${system}.all_9_14;
    389         };
    390         packages.${packageName} = tricuPackage;
    391         packages.default = tricuPackage;
    392         packages.tricu-haskell-ffi = tricuHaskell;
    393         packages.tricu-haskell-ffi-archive = tricuHaskellArchive;
    394         packages.tricu-redistribution = import ./ext/haskell/redistribution.nix {
    395           inherit pkgs;
    396           host = tricuHaskellArchive;
    397           source = self;
    398           nixpkgsSource = nixpkgs;
    399           flakeUtilsSource = flake-utils;
    400           systemsSource = flake-utils.inputs.systems;
    401         };
    402         packages.tricu-portable = pkgs.runCommand "tricu-portable-linux-x86_64" {
    403           nativeBuildInputs = [ pkgs.python3 pkgs.patchelf pkgs.binutils pkgs.glibc.bin ];
    404           meta.platforms = [ "x86_64-linux" ];
    405         } ''
    406           mkdir -p $out
    407           python3 ${./ext/haskell/portable.py} --host ${tricuHaskellArchive} \
    408             --glibc ${pkgs.glibc} --output $out/tricu \
    409             --materials ${self.packages.${system}.tricu-redistribution}
    410           cp ${./LICENSE} $out/tricu/LICENSE
    411           cp ${./ext/haskell/portable-README.md} $out/tricu/README.md
    412           tar --hard-dereference --sort=name --mtime=@1 --owner=0 --group=0 --numeric-owner \
    413             -C $out -czf $out/tricu-linux-x86_64.tar.gz tricu
    414           tar --hard-dereference --sort=name --mtime=@1 --owner=0 --group=0 --numeric-owner \
    415             --transform='flags=r;s,^\.,tricu-source-materials,' \
    416             -C ${self.packages.${system}.tricu-redistribution} -cf - . \
    417             | gzip -n -6 > $out/tricu-source-materials.tar.gz
    418           python3 ${./ext/haskell/verify_redistribution.py} \
    419             --materials ${self.packages.${system}.tricu-redistribution} \
    420             --runtime $out/tricu --archives $out
    421         '';
    422         packages.tricu-portable-test-system = pkgs.runCommand "tricu-portable-test-system" {
    423           nativeBuildInputs = [ pkgs.python3 pkgs.patchelf pkgs.glibc.bin ];
    424           meta.platforms = [ "x86_64-linux" ];
    425         } ''
    426           mkdir tools
    427           cp ${./ext/haskell/portable.py} tools/portable.py
    428           cp ${./ext/haskell/portable_fixture.py} tools/fixture.py
    429           python3 tools/fixture.py --python ${pkgs.python3}/bin/python3 \
    430             --stdlib ${pkgs.python3}/lib/python${pkgs.python3.pythonVersion} \
    431             --loader ${pkgs.glibc}/lib/ld-linux-x86-64.so.2 --output $out
    432         '';
    433         packages.tricu-portable-test = pkgs.runCommand "tricu-portable-test" {
    434           nativeBuildInputs = [ pkgs.python3 pkgs.bubblewrap ];
    435           meta.platforms = [ "x86_64-linux" ];
    436         } ''
    437           python3 ${./ext/haskell/test_portable.py} \
    438             --release ${self.packages.${system}.tricu-portable}/tricu-linux-x86_64.tar.gz \
    439             --system ${self.packages.${system}.tricu-portable-test-system} \
    440             --source ${tricuSource} --worker ${tricuFilecheckBundle} \
    441             --copier ${self.packages.${system}.tricu-haskell-ffi-archive-integration}/copy.arboricx \
    442             --output $out
    443         '';
    444         packages.tricu-distro-test-kit = pkgs.runCommand "tricu-distro-test-kit" {
    445           nativeBuildInputs = [ pkgs.python3 pkgs.nodejs phpWithFfi ];
    446         } ''
    447           mkdir -p $out/tests
    448           cp ${self.packages.${system}.tricu-portable}/tricu-linux-x86_64.tar.gz $out/
    449           cp ${./ext/haskell/container_matrix.py} $out/container_matrix.py
    450           cp ${./ext/haskell/container_probe.py} $out/container_probe.py
    451           cp ${./ext/haskell/container_bindings.py} $out/container_bindings.py
    452           mkdir -p $out/bindings/ext/{js,php,haskell/bindings}
    453           cp -r ${./ext/js/src} $out/bindings/ext/js/src
    454           cp -r ${./ext/js/test} $out/bindings/ext/js/test
    455           cp ${./ext/js/package.json} $out/bindings/ext/js/package.json
    456           cp ${./ext/js/package-lock.json} $out/bindings/ext/js/package-lock.json
    457           cp -r ${./ext/php/src} $out/bindings/ext/php/src
    458           cp -r ${./ext/php/tests} $out/bindings/ext/php/tests
    459           cp ${./ext/haskell/test_embedding.py} $out/bindings/ext/haskell/test_embedding.py
    460           cp ${./ext/haskell/bindings/python.py} $out/bindings/ext/haskell/bindings/python.py
    461           cp ${./ext/haskell/bindings/fixtures.json} $out/bindings/ext/haskell/bindings/fixtures.json
    462           cp ${./ext/haskell/portable_workload.py} $out/tests/portable_workload.py
    463           cp ${./ext/haskell/test_embedding.py} $out/tests/test_embedding.py
    464           cp ${./ext/haskell/test_scopes.py} $out/tests/test_scopes.py
    465           cp ${./test/python_filecheck.py} $out/tests/python_filecheck.py
    466           cp ${./test/process_rss.py} $out/tests/process_rss.py
    467           mkdir $out/tests/fixtures
    468           for fixture in id append true false notQ; do
    469             cp ${tricuSource}/test/fixtures/$fixture.arboricx $out/tests/fixtures/
    470           done
    471           cp -r ${tricuSource}/demos/python-filecheck/tricu_filecheck $out/tests/tricu_filecheck
    472           chmod -R u+w $out/tests/tricu_filecheck
    473           cp ${tricuFilecheckBundle}/worker.arboricx $out/tests/tricu_filecheck/worker.arboricx
    474           printf 'BUNDLE_SHA256 = "%s"\n' "$(cat ${tricuFilecheckBundle}/worker.sha256)" \
    475             > $out/tests/tricu_filecheck/_bundle_pin.py
    476           cp ${self.packages.${system}.tricu-haskell-ffi-archive-integration}/copy.arboricx $out/tests/
    477           PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=$out python3 -c \
    478             'import sys; from pathlib import Path; from container_matrix import validate_bindings_kit; validate_bindings_kit(Path(sys.argv[1]))' "$out"
    479           # Exercise the actual staged consumers and their helper imports. Nix
    480           # supplies these smoke runtimes; distro acceptance remains external.
    481           cp -r $out/bindings source
    482           chmod -R u+w source
    483           mkdir -p source/test
    484           cp -r $out/tests/fixtures source/test/fixtures
    485           ln -s ${tricuJs}/lib/node_modules source/ext/js/node_modules
    486           cp ${self.packages.${system}.tricu-portable}/tricu/include/tricu.h source/ext/php/tricu.h
    487           PYTHONDONTWRITEBYTECODE=1 \
    488           PYTHONPATH=${self.packages.${system}.tricu-portable}/tricu/share/tricu \
    489             python3 ${./ext/haskell/bindings/check.py} --source "$PWD/source" \
    490               --library ${self.packages.${system}.tricu-portable}/tricu/lib/libtricu-haskell.so \
    491               --python ${pkgs.python3}/bin/python3 --node ${pkgs.nodejs}/bin/node \
    492               --php ${phpWithFfi}/bin/php --copier $out/tests/copy.arboricx \
    493               --report $out/bindings-smoke.json
    494         '';
    495         packages.tricu-distro-test = pkgs.writeShellApplication {
    496           name = "tricu-distro-test";
    497           runtimeInputs = [ pkgs.python3 ];
    498           text = ''
    499             exec python3 ${self.packages.${system}.tricu-distro-test-kit}/container_matrix.py "$@"
    500           '';
    501         };
    502         packages.tricu-vm-test-kit = pkgs.runCommand "tricu-vm-test-kit" {} ''
    503           mkdir -p $out
    504           cp -r ${self.packages.${system}.tricu-distro-test-kit}/. $out/
    505           chmod u+w $out
    506           cp ${./ext/haskell/vm_matrix.py} $out/vm_matrix.py
    507           cp ${./ext/haskell/vm_guest.py} $out/vm_guest.py
    508         '';
    509         packages.tricu-vm-test = pkgs.writeShellApplication {
    510           name = "tricu-vm-test";
    511           runtimeInputs = [ pkgs.python3 pkgs.qemu pkgs.cdrkit ];
    512           text = ''
    513             exec python3 ${self.packages.${system}.tricu-vm-test-kit}/vm_matrix.py "$@"
    514           '';
    515         };
    516         checks.portableVMHarness = pkgs.runCommand "tricu-portable-vm-harness" {
    517           nativeBuildInputs = [ pkgs.python3 pkgs.cdrkit ];
    518         } ''
    519           mkdir tools
    520           cp ${./ext/haskell/vm_matrix.py} tools/vm_matrix.py
    521           cp ${./ext/haskell/vm_guest.py} tools/vm_guest.py
    522           cp ${./ext/haskell/container_matrix.py} tools/container_matrix.py
    523           cp ${./ext/haskell/test_vm_harness.py} tools/test_vm_harness.py
    524           PYTHONDONTWRITEBYTECODE=1 python3 tools/test_vm_harness.py
    525           mkdir -p $out
    526         '';
    527         checks.redistribution = pkgs.runCommand "tricu-redistribution-controls" {
    528           nativeBuildInputs = [ pkgs.python3 ];
    529         } ''
    530           mkdir tools
    531           cp ${./ext/haskell/redistribution.py} tools/redistribution.py
    532           cp ${./ext/haskell/portable.py} tools/portable.py
    533           cp ${./ext/haskell/verify_redistribution.py} tools/verify_redistribution.py
    534           cp ${./ext/haskell/test_redistribution.py} tools/test_redistribution.py
    535           PYTHONDONTWRITEBYTECODE=1 python3 tools/test_redistribution.py
    536           mkdir -p $out
    537         '';
    538         packages.tricu-portable-replacement-test = pkgs.runCommand "tricu-portable-replacement-test" {
    539           nativeBuildInputs = [ pkgs.python3 pkgs.bubblewrap pkgs.binutils ];
    540           meta.platforms = [ "x86_64-linux" ];
    541         } ''
    542           cp -r ${self.packages.${system}.tricu-portable}/tricu .
    543           chmod -R u+w tricu
    544           printf 'User-modified ABI-compatible ELF metadata\n' > modification.txt
    545           for library in libgmp.so.10 libelf.so.1 libdw.so.1 libnuma.so.1; do
    546             objcopy --add-section .tricu-user-modification=modification.txt tricu/lib/$library
    547           done
    548           python3 - <<'PY'
    549           import hashlib, json
    550           from pathlib import Path
    551           manifest = Path('tricu/share/tricu/portable.json')
    552           data = json.loads(manifest.read_text())
    553           replacements = {}
    554           for name in ('libgmp.so.10', 'libelf.so.1', 'libdw.so.1', 'libnuma.so.1'):
    555               library = Path('tricu/lib')/name
    556               checksum = hashlib.sha256(library.read_bytes()).hexdigest()
    557               assert checksum != data['libraries'][name]['sha256']
    558               replacements[name] = dict(original_sha256=data['libraries'][name]['sha256'], replacement_sha256=checksum)
    559               data['libraries'][name].update(sha256=checksum, bytes=library.stat().st_size)
    560           manifest.write_text(json.dumps(data))
    561           Path('replacement.json').write_text(json.dumps(dict(
    562               method='Added a non-allocated ELF metadata section; ABI preserved; not a source rebuild',
    563               libraries=replacements), indent=2))
    564           PY
    565           tar -czf modified.tar.gz tricu
    566           python3 ${./ext/haskell/test_portable.py} \
    567             --release "$PWD/modified.tar.gz" \
    568             --system ${self.packages.${system}.tricu-portable-test-system} \
    569             --source ${tricuSource} --worker ${tricuFilecheckBundle} \
    570             --copier ${self.packages.${system}.tricu-haskell-ffi-archive-integration}/copy.arboricx \
    571             --output $out
    572           cp replacement.json $out/
    573         '';
    574         checks.portableContainerHarness = pkgs.runCommand "tricu-portable-container-harness" {
    575           nativeBuildInputs = [ pkgs.python3 ];
    576         } ''
    577           mkdir tools
    578           cp ${./ext/haskell/container_matrix.py} tools/container_matrix.py
    579           cp ${./ext/haskell/container_probe.py} tools/container_probe.py
    580           cp ${./ext/haskell/test_container_harness.py} tools/test_container_harness.py
    581           cp ${./ext/haskell/container_bindings.py} tools/container_bindings.py
    582           PYTHONDONTWRITEBYTECODE=1 python3 tools/test_container_harness.py
    583           mkdir -p $out
    584         '';
    585         packages.tricu-generation-ffi-test = pkgs.runCommand "tricu-generation-ffi-test" {
    586           nativeBuildInputs = [ pkgs.python3 ];
    587         } ''
    588           export PYTHONDONTWRITEBYTECODE=1
    589           mkdir -p $out
    590           python3 ${tricuSource}/test/shared_library_generation.py \
    591             --tricu ${tricuPackage}/bin/tricu \
    592             --library ${tricuHaskell}/lib/libtricu-haskell${pkgs.stdenv.hostPlatform.extensions.sharedLibrary} \
    593             --directory $out/compatibility --compatibility
    594           python3 ${tricuSource}/test/shared_library_smoke.py \
    595             --tricu ${tricuPackage}/bin/tricu \
    596             --library ${tricuHaskell}/lib/libtricu-haskell${pkgs.stdenv.hostPlatform.extensions.sharedLibrary} \
    597             --directory $out/programs
    598           python3 ${tricuSource}/test/io_generation.py ${tricuPackage}/bin/tricu \
    599             --kit ${tricuSource}/bootstrap/rebuild \
    600             --haskell-library ${tricuHaskell}/lib/libtricu-haskell${pkgs.stdenv.hostPlatform.extensions.sharedLibrary} \
    601             --directory $out/scoped
    602         '';
    603 
    604         packages.tricu-haskell-linkage-smoke = pkgs.runCommand "tricu-haskell-linkage-smoke" {
    605           nativeBuildInputs = [ pkgs.python3 pkgs.stdenv.cc ];
    606           meta.platforms = [ "x86_64-linux" ];
    607         } ''
    608           mkdir -p $out/bin
    609           $CC -O2 -Wall -Wextra -Werror ${tricuSource}/ext/haskell/core_bench.c \
    610             -I${tricuHaskell}/include -L${tricuHaskell}/lib -ltricu-haskell \
    611             -Wl,-rpath,${tricuHaskell}/lib -o $out/bin/dynamic-core-bench
    612           $CC -O2 -Wall -Wextra -Werror ${tricuSource}/ext/haskell/core_bench.c \
    613             -I${tricuHaskellArchive}/include -L${tricuHaskellArchive}/lib -ltricu-haskell \
    614             -Wl,-rpath,${tricuHaskellArchive}/lib -o $out/bin/archive-core-bench
    615           PYTHONDONTWRITEBYTECODE=1 python3 ${tricuSource}/ext/haskell/core_bench.py \
    616             --dynamic $out/bin/dynamic-core-bench --archive $out/bin/archive-core-bench \
    617             --output $out/report.json > $out/summary.json
    618         '';
    619         packages.tricu-python = tricuPython;
    620         packages.tricu-python-tests = tricuPythonTests;
    621         packages.tricu-workflow-demo = tricuWorkflow;
    622         packages.tricu-workflow-demo-test = pkgs.runCommand "tricu-workflow-demo-test" {
    623           nativeBuildInputs = [ pythonWithWorkflow ];
    624         } ''
    625           export PYTHONDONTWRITEBYTECODE=1
    626           mkdir -p $out
    627           cp ${(pkgs.closureInfo { rootPaths = [ tricuWorkflow ]; })}/store-paths $out/runtime-closure
    628           if grep -Fx '${tricuPackage}' $out/runtime-closure; then exit 1; fi
    629           if grep -Fx '${tricuSource}' $out/runtime-closure; then exit 1; fi
    630           timeout 90s python3 ${tricuSource}/test/workflow_planning.py \
    631             --app ${tricuWorkflow}/bin/tricu-workflow-demo --report $out/report.json
    632         '';
    633         packages.tricu-filecheck = tricuFilecheck;
    634         packages.tricu-filecheck-test = pkgs.runCommand "tricu-filecheck-test" {
    635           nativeBuildInputs = [ pythonWithFilecheck ];
    636         } ''
    637           export PYTHONDONTWRITEBYTECODE=1
    638           mkdir -p $out
    639           # Runtime closure must contain neither the source compiler executable
    640           # nor the source snapshot used to build it.
    641           cp ${(pkgs.closureInfo { rootPaths = [ tricuFilecheck ]; })}/store-paths $out/runtime-closure
    642           if grep -Fx '${tricuPackage}' $out/runtime-closure; then exit 1; fi
    643           if grep -Fx '${tricuSource}' $out/runtime-closure; then exit 1; fi
    644           timeout 120s python3 ${tricuSource}/test/python_filecheck.py \
    645             --app ${tricuFilecheck}/bin/tricu-filecheck --report $out/report.json
    646         '';
    647 
    648         # Source-compiling embedding acceptance stays opt-in, like the other
    649         # application compilation checks. Basic C/Python checks run in the library.
    650         packages.tricu-haskell-ffi-integration = embeddingIntegration tricuHaskell;
    651         packages.tricu-haskell-ffi-archive-integration = embeddingIntegration tricuHaskellArchive;
    652 
    653         packages.tricu-native-bindings-integration = pkgs.runCommand "tricu-native-bindings-integration" {
    654           nativeBuildInputs = [ pythonWithTricu pkgs.nodejs phpWithFfi ];
    655         } ''
    656           export PYTHONDONTWRITEBYTECODE=1
    657           cp -r ${tricuSource} source
    658           chmod -R u+w source
    659           ln -s ${tricuJs}/lib/node_modules source/ext/js/node_modules
    660           mkdir -p $out
    661           python3 source/ext/haskell/bindings/check.py \
    662             --source "$PWD/source" \
    663             --library ${tricuHaskell}/lib/libtricu-haskell${pkgs.stdenv.hostPlatform.extensions.sharedLibrary} \
    664             --python ${pythonWithTricu}/bin/python3 --node ${pkgs.nodejs}/bin/node --php ${phpWithFfi}/bin/php \
    665             --copier ${self.packages.${system}.tricu-haskell-ffi-integration}/copy.arboricx \
    666             --report $out/report.json
    667         '';
    668 
    669         # Native reducer smoke preserves full-result and wrong-worker controls.
    670         packages.reducerSmoke = pkgs.stdenv.mkDerivation {
    671           pname = "tricu-reducer-smoke";
    672           version = "2026.10.24";
    673           src = tricuSource;
    674           nativeBuildInputs = [ (haskellPackages.ghcWithPackages (p: [
    675             p.cryptonite p.memory p.base16-bytestring p.vector
    676           ])) ];
    677           buildPhase = ''
    678             ghc -O2 -threaded -rtsopts -XOverloadedStrings -XScopedTypeVariables \
    679               -XMultiWayIf -XLambdaCase -isrc -outputdir objects \
    680               bench/ReducerSmoke.hs -o haskell-reducer-smoke
    681           '';
    682           doCheck = true;
    683           checkPhase = ''
    684             for kind in id append deep distribution; do
    685               fixture=test/fixtures/id.arboricx
    686               if [ "$kind" = append ]; then fixture=test/fixtures/append.arboricx; fi
    687               ./haskell-reducer-smoke "$kind" 64 2 "$fixture" +RTS -N1 -M512m -K8m -RTS
    688             done
    689             if ./haskell-reducer-smoke id 256 1 test/fixtures/append.arboricx \
    690                 +RTS -N1 -M512m -K8m -RTS >bad-worker.log 2>&1; then exit 1; fi
    691             grep -q 'incorrect complete result' bad-worker.log
    692           '';
    693           installPhase = ''
    694             mkdir -p $out/bin
    695             cp haskell-reducer-smoke $out/bin/
    696             ghc --version > $out/compiler-version
    697           '';
    698           meta.mainProgram = "haskell-reducer-smoke";
    699         };
    700         packages.inspectorOracle = pkgs.runCommand "arboricx-inspector-oracle" {
    701           nativeBuildInputs = [ (haskellPackages.ghcWithPackages (p: [
    702             p.cryptonite p.memory p.base16-bytestring p.vector
    703           ])) ];
    704         } ''
    705           mkdir -p $out/bin
    706           ghc -O1 -XOverloadedStrings -XScopedTypeVariables -XMultiWayIf -XLambdaCase \
    707             -i${./src} -outputdir "$TMPDIR/objects" ${./test/InspectorOracle.hs} \
    708             -o $out/bin/inspector-oracle
    709         '';
    710         packages.arboricxSiteJavaScript = siteJavaScript;
    711         packages.arboricxArtwork = import ./ext/demo-site/artwork.nix { inherit pkgs; };
    712         packages.arboricxWebkitCheck = import ./ext/demo-site/webkit.nix { inherit pkgs; };
    713         packages.arboricxSite = siteRelease.arboricxSite;
    714         packages.arboricxSiteDeps = siteRelease.arboricxSiteDeps;
    715 				packages.tricu-static = tricuMuslStatic;
    716 				packages.tricu-static-upx = tricuStatic;
    717         packages.tricu-bench = tricuBench;
    718         packages.benchmarkWorkers = benchmarkWorkers;
    719         packages.benchmarkSmoke = pkgs.runCommand "tricu-benchmark-smoke" {} ''
    720           ${tricuBench}/bin/tricu-bench ${benchmarkWorkers}/workers.arboricx \
    721             "$(cat ${benchmarkWorkers}/sha256)" --verify-only +RTS -N1 -M8192m -RTS
    722           if ${tricuBench}/bin/tricu-bench ${benchmarkWorkers}/workers.arboricx \
    723               "$(printf '%064d' 0)" --verify-only >bad-hash.log 2>&1; then
    724             echo "benchmark accepted a wrong digest" >&2; exit 1
    725           fi
    726           grep -q 'SHA256 mismatch' bad-hash.log
    727           if ${tricuBench}/bin/tricu-bench ${./test/fixtures/id.arboricx} \
    728               "$(sha256sum ${./test/fixtures/id.arboricx} | cut -d ' ' -f 1)" \
    729               --verify-only >bad-interface.log 2>&1; then
    730             echo "benchmark accepted the wrong interface" >&2; exit 1
    731           fi
    732           grep -q 'exactly the benchmark export interface' bad-interface.log
    733           printf 'not a bundle' > malformed.arboricx
    734           if ${tricuBench}/bin/tricu-bench malformed.arboricx \
    735               "$(sha256sum malformed.arboricx | cut -d ' ' -f 1)" \
    736               --verify-only >malformed.log 2>&1; then
    737             echo "benchmark accepted authenticated malformed bytes" >&2; exit 1
    738           fi
    739           ${tricuBench}/bin/tricu-bench ${benchmarkWorkers}/workers.arboricx \
    740             "$(cat ${benchmarkWorkers}/sha256)" --list >names
    741           test "$(grep -c '^contracts/' names)" -eq 12
    742           test "$(grep -c '^registry-hash/' names)" -eq 15
    743           ${tricuBench}/bin/tricu-bench ${benchmarkWorkers}/workers.arboricx \
    744             "$(cat ${benchmarkWorkers}/sha256)" --iters 1 +RTS -N1 -M8192m -RTS
    745           mkdir -p $out
    746         '';
    747 
    748         # Current-source storage semantics and allocation guard, compiled with
    749         # the packaged stable compiler. Full compiler-pair acceptance is separate.
    750         packages.ioStorageTest = pkgs.runCommand "tricu-io-storage-test" {
    751           nativeBuildInputs = [ pkgs.python3 ];
    752         } ''
    753           mkdir -p $out
    754           export PYTHONDONTWRITEBYTECODE=1
    755           python3 ${./.}/test/io_compiler_store.py ${tricuPackage}/bin/tricu \
    756             --directory $out/controls > $out/controls.log
    757           python3 ${./.}/test/io_storage_cost.py ${tricuPackage}/bin/tricu \
    758             --directory $out/cost > $out/cost.log
    759         '';
    760 
    761         packages.tricu-php = tricuPhp;
    762         packages.tricu-php-tests = tricuPhpTests;
    763         packages.tricu-js = tricuJs;
    764         packages.tricu-js-tests = tricuJsTests;
    765 
    766         checks.haskellRuntimeSelection = pkgs.runCommand "tricu-haskell-runtime-selection" {} ''
    767           test ! -e ${tricuSource}/ext/zig
    768           test ! -e ${tricuSource}/ext/wasm
    769           test '${builtins.toJSON (builtins.filter (name: pkgs.lib.hasInfix "zig" name || pkgs.lib.hasPrefix "tricu-wasm" name) (builtins.attrNames self.packages.${system}))}' = '[]'
    770           mkdir -p $out
    771         '';
    772 
    773         checks.currentIONames = pkgs.runCommand "tricu-current-io-names" {
    774           nativeBuildInputs = [ pkgs.python3 ];
    775         } ''
    776           python3 ${./.}/test/current_io_names.py
    777           mkdir -p $out
    778         '';
    779 
    780         packages.libraryFormatting = pkgs.runCommand "tricu-library-formatting" {
    781           nativeBuildInputs = [ pkgs.python3 ];
    782         } ''
    783           export PYTHONDONTWRITEBYTECODE=1
    784           python3 ${tricuSource}/test/prepare_formatter.py ${tricuPackage}/bin/tricu --directory $out/formatter
    785           python3 ${tricuSource}/test/library_format.py ${tricuPackage}/bin/tricu --formatter-directory $out/formatter
    786         '';
    787 
    788         checks.runtimeBoundary = pkgs.runCommand "tricu-runtime-source-boundary" {} ''
    789           test -f ${runtimeSource}/src/Runtime/Tree.hs
    790           for path in Research.hs Eval.hs FileEval.hs Lexer.hs Parser.hs \
    791               Frontend Module/Resolver.hs Module/Workspace.hs; do
    792             test ! -e "${runtimeSource}/src/$path"
    793           done
    794           mkdir -p $out
    795         '';
    796 
    797         checks.${packageName} = tricuPackageTests;
    798         checks.default = tricuPackageTests;
    799         checks.haskellEmbedding = tricuHaskell;
    800         checks.pythonBinding = tricuPythonTests;
    801         checks.jsBinding = tricuJsTests;
    802         checks.phpBinding = tricuPhpTests;
    803         # Fast packaging and filesystem integrity checks.
    804         checks.arboricxSite = pkgs.runCommand "arboricx-site-release-check" {
    805           nativeBuildInputs = [ pkgs.python3 ];
    806         } ''
    807           python3 ${./ext/demo-site/test/asset-release.py} ${./ext/demo-site/asset-release.py}
    808           python3 ${./ext/demo-site/test/release.py} \
    809             --site ${siteRelease.arboricxSite} --deps ${siteRelease.arboricxSiteDeps} \
    810             --seeder ${./ext/demo-site/seed.py}
    811           mkdir -p $out
    812         '';
    813         checks.rtsMetrics = pkgs.runCommand "tricu-rts-metrics-tests" {
    814           nativeBuildInputs = [ pkgs.python3 ];
    815         } ''
    816           export PYTHONDONTWRITEBYTECODE=1
    817           cd ${tricuSource}
    818           timeout 30s python3 test/rts_metrics_test.py
    819           timeout 30s python3 test/process_rss_test.py
    820           mkdir -p $out
    821         '';
    822         checks.bootstrapRetention = pkgs.runCommand "tricu-bootstrap-retention-tests" {
    823           nativeBuildInputs = [ pkgs.python3 ];
    824         } ''
    825           export PYTHONDONTWRITEBYTECODE=1
    826           cd ${tricuSource}
    827           python3 test/bootstrap_retention_test.py
    828           mkdir -p $out
    829         '';
    830         checks.hostPolicy = pkgs.stdenv.mkDerivation {
    831           pname = "tricu-host-policy-tests";
    832           version = "0.1.0";
    833           src = tricuSource;
    834           nativeBuildInputs = [ pkgs.python3 tricuPackage ];
    835           buildPhase = "true";
    836           doCheck = true;
    837           checkPhase = ''
    838             test ! -e archive
    839             export PYTHONDONTWRITEBYTECODE=1
    840             python3 test/unit.py
    841             python3 test/shipped_compiler.py
    842             python3 test/executable_arguments.py ${tricuPackage}/bin/tricu
    843             python3 test/package_registry.py ${tricuPackage}/bin/tricu
    844             python3 test/project_dependencies.py ${tricuPackage}/bin/tricu
    845             python3 test/project_inspection.py ${tricuPackage}/bin/tricu
    846             python3 test/interaction_inspection.py ${tricuPackage}/bin/tricu
    847             python3 test/provenance.py ${tricuPackage}/bin/tricu
    848           '';
    849           installPhase = "mkdir -p $out";
    850         };
    851         # Source compilation and bootstrap generations remain explicit opt-in
    852         # jobs. Haskell package tests above gate the supported host.
    853 
    854         devShells.default = pkgs.mkShell {
    855           buildInputs = with pkgs; [
    856             # Package these build tools as separate executables.
    857             pkgs.cabal-install
    858             pkgs.haskellPackages.ghc-events
    859             pkgs.ghcid
    860             haskellPackages.ghc
    861             upx
    862             gcc
    863             python3
    864           ];
    865 
    866           inputsFrom = [
    867             tricuPackage
    868             tricuPhp
    869           ];
    870         };
    871 
    872         packages.${containerPackageName} = pkgs.dockerTools.buildImage {
    873           name = "tricu";
    874           tag = "latest";
    875 
    876           copyToRoot = pkgs.buildEnv {
    877             name = "image-root";
    878             paths = [ tricuStatic ];
    879             pathsToLink = [ "/bin" ];
    880           };
    881 
    882           config = {
    883             Cmd = [ "/bin/tricu" ];
    884             WorkingDir = "/app";
    885           };
    886         };
    887 
    888         # Opt-in acceptance runs the actual default entrypoint and common v2 gates.
    889         packages.arboricxServerTest = mkRegistryTest "arboricx-server" arboricxServerRunner;
    890         packages.arboricxStreamingRunner = arboricxStreamingRunner;
    891         packages.arboricxStreamingTest = mkRegistryTest "arboricx-streaming" arboricxStreamingRunner;
    892         packages.arboricxServerBundle = arboricxServerBundle;
    893         packages.arboricxServerRunner = arboricxServerRunner;
    894         packages.arboricxServer = pkgs.dockerTools.buildImage {
    895           name = "arboricx-server";
    896           tag = "latest";
    897 
    898           copyToRoot = pkgs.runCommand "arboricx-server-root" {} ''
    899             mkdir -p $out/app/store
    900           '';
    901 
    902           config = {
    903             Entrypoint = [ "${arboricxServerRunner}/bin/arboricx-server" ];
    904             WorkingDir = "/app";
    905             ExposedPorts = { "8080/tcp" = {}; };
    906           };
    907         };
    908       });
    909 }