{ description = "Development and build environment for singularity-forge"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; flake-utils.url = "github:numtide/flake-utils"; }; nixConfig = { extra-substituters = [ "https://cache.centralcloud.com/default" ]; extra-trusted-public-keys = [ "default:ywfU21WX06iOn2Ec2lae1jYh4w8LO4IQkmp06vJzsk8=" ]; }; outputs = { self, nixpkgs, flake-utils, }: flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; }; lib = pkgs.lib; nodejs26 = pkgs.stdenv.mkDerivation { pname = "nodejs"; version = "26.1.0"; src = pkgs.fetchurl { url = "https://nodejs.org/dist/v26.1.0/node-v26.1.0-linux-x64.tar.xz"; hash = "sha256-n8byG2xKYkOXJxI+UQ6cOf67L1Y3OPSSfNPgsojJs8k="; }; nativeBuildInputs = [pkgs.autoPatchelfHook]; buildInputs = [pkgs.stdenv.cc.cc.lib]; installPhase = '' runHook preInstall mkdir -p "$out" cp -R . "$out/" ln -s "$out/bin/node" "$out/bin/nodejs" substituteInPlace "$out/lib/node_modules/npm/bin/npm-cli.js" \ --replace-fail "#!/usr/bin/env node" "#!$out/bin/node" substituteInPlace "$out/lib/node_modules/npm/bin/npx-cli.js" \ --replace-fail "#!/usr/bin/env node" "#!$out/bin/node" runHook postInstall ''; passthru.python = pkgs.python3; }; source = lib.cleanSourceWith { src = ./.; filter = path: type: let rel = lib.removePrefix "${toString ./.}/" (toString path); base = baseNameOf path; in !(lib.hasPrefix ".git/" rel) && !(lib.hasPrefix "node_modules/" rel) && !(lib.hasPrefix "web/node_modules/" rel) && !(lib.hasPrefix "dist/" rel) && !(lib.hasPrefix ".next/" rel) && !(lib.hasPrefix "web/.next/" rel) && !(lib.hasPrefix "rust-engine/target/" rel) && base != ".direnv"; }; nativeNpmBuildInputs = with pkgs; [ git libsecret makeWrapper pkg-config python3 stdenv.cc ]; rootNodeModules = pkgs.importNpmLock.buildNodeModules { npmRoot = ./.; nodejs = nodejs26; derivationArgs = { nativeBuildInputs = nativeNpmBuildInputs; buildInputs = [pkgs.libsecret pkgs.zlib pkgs.stdenv.cc.cc.lib]; npmInstallFlags = ["--ignore-scripts"]; npm_config_ignore_scripts = "true"; }; }; webNodeModules = pkgs.importNpmLock.buildNodeModules { npmRoot = ./web; nodejs = nodejs26; derivationArgs = { nativeBuildInputs = nativeNpmBuildInputs; buildInputs = [pkgs.libsecret pkgs.zlib pkgs.stdenv.cc.cc.lib]; npmInstallFlags = ["--ignore-scripts"]; npm_config_ignore_scripts = "true"; }; }; sfServerRoot = pkgs.stdenv.mkDerivation { pname = "sf-server-root"; version = "2.75.4"; src = source; nativeBuildInputs = nativeNpmBuildInputs ++ [nodejs26]; buildInputs = [pkgs.libsecret pkgs.zlib pkgs.stdenv.cc.cc.lib]; SF_GIT_SHA = self.rev or self.dirtyRev or "dirty"; SF_GIT_REF = self.ref or "main"; SF_IMAGE_REPOSITORY = "registry.infra.centralcloud.com/singularity/sf-server"; buildPhase = '' runHook preBuild export CI=1 export HOME="$TMPDIR/home" export npm_config_cache="$TMPDIR/npm-cache" mkdir -p "$HOME" "$npm_config_cache" mkdir -p node_modules ln -s ${rootNodeModules}/node_modules/.bin node_modules/.bin for entry in ${rootNodeModules}/node_modules/*; do name="$(basename "$entry")" if [ "$name" = "@singularity-forge" ]; then mkdir -p node_modules/@singularity-forge for scoped_entry in "$entry"/*; do ln -s "$scoped_entry" "node_modules/@singularity-forge/$(basename "$scoped_entry")" done else ln -s "$entry" "node_modules/$name" fi done ln -s ${webNodeModules}/node_modules web/node_modules node scripts/link-workspace-packages.cjs npm run build:core npm run build:web-host npm run release:manifest -- --out dist/sf-release-manifest.json runHook postBuild ''; installPhase = '' runHook preInstall prune_runtime_node_modules() { local modules="$1" [ -d "$modules" ] || return 0 chmod -R u+w "$modules" # Production server target: Linux x64 glibc on Vega. Keep runtime # Linux x64 GNU native packages and remove optional packages for # other OS/CPU/libc targets that npm lockfiles fetch for portability. rm -rf \ "$modules/playwright" \ "$modules/playwright-core" \ "$modules/@playwright" \ "$modules/next/dist/experimental/testmode/playwright" \ "$modules/next/experimental/testmode/playwright" \ "$modules/node-pty/deps/winpty" rm -f \ "$modules/.bin/playwright" \ "$modules/.bin/playwright-core" \ "$modules/next/experimental/testmode/playwright.js" \ "$modules/next/experimental/testmode/playwright.d.ts" \ "$modules/node-pty"/src/windows* \ "$modules/node-pty"/lib/windows* \ "$modules/@lydell/node-pty"/windows* find "$modules" -mindepth 1 -maxdepth 8 -type d \( \ -name "*android*" -o \ -name "*darwin*" -o \ -name "*freebsd*" -o \ -name "*openbsd*" -o \ -name "*win32*" -o \ -name "*windows*" -o \ -name "*linux_ia32*" -o \ -name "*linux_arm*" -o \ -name "*linux-arm*" -o \ -name "*linux-arm64*" -o \ -name "*linux_loong*" -o \ -name "*linux-loong*" -o \ -name "*linux_riscv*" -o \ -name "*linux-riscv*" -o \ -name "*linuxmusl*" -o \ -name "*musl_x64*" -o \ -name "*arm64*" -o \ -name "*armhf*" -o \ -name "*armv7*" -o \ -name "*riscv*" -o \ -name "*loong*" -o \ -name "*ppc64*" -o \ -name "*s390x*" -o \ -name "*ia32*" -o \ -name "*musl*" -o \ -name "*wasm32*" \ \) ! \( \ -name "*linux-x64-gnu*" -o \ -name "*linux_x64*" -o \ -name "*linux-x64" -o \ -name "linux-x64" \ \) -prune -exec rm -rf {} + find "$modules" -type d \( \ -name ".cache" -o \ -name "__tests__" -o \ -name "test" -o \ -name "tests" -o \ -name "docs" -o \ -name "examples" \ \) -prune -exec rm -rf {} + } mkdir -p "$out/opt/sf" cp package.json package-lock.json README.md "$out/opt/sf/" cp -R packages dist pkg src scripts rust-engine web "$out/opt/sf/" cp -R ${rootNodeModules}/node_modules "$out/opt/sf/node_modules" prune_runtime_node_modules "$out/opt/sf/node_modules" rm -rf \ "$out/opt/sf/web/node_modules" \ "$out/opt/sf/web/.next/cache" \ "$out/opt/sf/web/.next/standalone" cp -R ${webNodeModules}/node_modules "$out/opt/sf/web/node_modules" prune_runtime_node_modules "$out/opt/sf/web/node_modules" if [ -d "$out/opt/sf/dist/web/standalone/node_modules/@singularity-forge" ]; then for pkg in "$out/opt/sf/dist/web/standalone/node_modules/@singularity-forge"/*; do [ -e "$pkg" ] || continue name="$(basename "$pkg")" source="$out/opt/sf/packages/$name" if [ -d "$source" ]; then rm -rf "$pkg" cp -R "$source" "$pkg" fi done fi prune_runtime_node_modules "$out/opt/sf/dist/web/standalone/node_modules" find "$out/opt/sf" -name tsconfig.tsbuildinfo -delete runHook postInstall ''; }; node26SlimBase = pkgs.dockerTools.pullImage { imageName = "node"; imageDigest = "sha256:424cafd2a035ed2b2d74acc3142b68b426fb62a47742c80a75e7117db02d6b30"; finalImageName = "node"; finalImageTag = "26.1-slim"; sha256 = lib.fakeSha256; }; in { packages = { inherit nodejs26 rootNodeModules webNodeModules sfServerRoot; sf-server-image = pkgs.dockerTools.streamLayeredImage { name = "registry.infra.centralcloud.com/singularity/sf-server"; tag = self.rev or self.dirtyRev or "dirty"; fromImage = node26SlimBase; contents = [ sfServerRoot pkgs.ca-certificates pkgs.git pkgs.libsecret pkgs.procps pkgs.tini ]; config = { WorkingDir = "/workspace"; ExposedPorts = { "4000/tcp" = {}; }; Env = [ "NODE_ENV=production" "SF_RELEASE_MANIFEST=/opt/sf/dist/sf-release-manifest.json" "SF_WEB_PACKAGE_ROOT=/opt/sf" "SF_WEB_PREFER_SOURCE=0" "SF_WEB_HOST=0.0.0.0" "SF_WEB_PORT=4000" "HOSTNAME=0.0.0.0" "PORT=4000" ]; Entrypoint = ["${pkgs.tini}/bin/tini" "--"]; Cmd = ["node" "/opt/sf/dist/web/standalone/server.js"]; }; }; default = sfServerRoot; }; devShells.default = pkgs.mkShell { packages = with pkgs; [ bash cargo clippy git just libsecret pkg-config protobuf rust-analyzer rustc rustfmt uv zlib nodejs26 ]; shellHook = '' export SF_SOURCE_DIR="${toString ./.}" if [ -x "$HOME/.local/bin/mise" ]; then MISE_NODE_BIN="$(cd "$SF_SOURCE_DIR" && "$HOME/.local/bin/mise" which node 2>/dev/null || true)" if [ -n "$MISE_NODE_BIN" ]; then CLEAN_PATH="$(printf '%s' "$PATH" | tr ':' '\n' | grep -v '/mise/installs/node/.*/bin' | paste -sd: -)" export PATH="$(dirname "$MISE_NODE_BIN"):$CLEAN_PATH" fi fi export PATH="$SF_SOURCE_DIR/bin:$PATH" export RUST_BACKTRACE=1 echo "singularity-forge development shell" echo " cargo: $(command -v cargo)" echo " node : repo-pinned by mise after direnv activation" echo " protoc: $(command -v protoc)" echo " rustc: $(command -v rustc)" echo "" echo "Build native addon:" echo " node rust-engine/scripts/build.js" ''; }; }); }