From c723e8b4cea30b4f2a615704c616b30e3b522e5e Mon Sep 17 00:00:00 2001 From: Michael Melanson Date: Thu, 4 Jun 2026 12:46:04 -0400 Subject: [PATCH] Add Nix development environment. Provide a flake-based dev shell and direnv config so contributors can get a reproducible Go toolchain without manual setup. Co-authored-by: Cursor --- .envrc | 37 ++++++++++ .gitignore | 3 + nix/README.md | 25 +++++++ nix/flake.lock | 61 ++++++++++++++++ nix/flake.nix | 19 +++++ nix/overlays/default.nix | 7 ++ nix/overlays/mkNiceShell.nix | 134 +++++++++++++++++++++++++++++++++++ nix/shells.nix | 18 +++++ 8 files changed, 304 insertions(+) create mode 100644 .envrc create mode 100644 nix/README.md create mode 100644 nix/flake.lock create mode 100644 nix/flake.nix create mode 100644 nix/overlays/default.nix create mode 100644 nix/overlays/mkNiceShell.nix create mode 100644 nix/shells.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..45cb135 --- /dev/null +++ b/.envrc @@ -0,0 +1,37 @@ +# Direnv setup for a dev environment with Nix. +# See ./nix/README.md for more info. +# +# If you'd like to use your own direnv file, you can +# stop git from tracking this file in your work tree and +# then customize it as you please. +# +# To stop tracking this file: +# > git update-index --skip-worktree .envrc +# To begin tracking again: +# > git update-index --no-skip-worktree .envrc +# +# If instead of completely ignoring this project wide .envrc file +# you'd like to insert your own environment customizations, then you +# can create an .envrc.local file that will be loaded. + + +# Minimizes the amount of log output coming from direnv +export DIRENV_LOG_FORMAT= + +flake_dir=./nix + +if type use_flake &>/dev/null; then + if type watch_file &>/dev/null; then + while IFS= read -r f; do + watch_file "$flake_dir/${f#./}" + done <<< "$(cd "$flake_dir" && git ls-files)" + fi + + use flake "$flake_dir#dev" +else + >&2 printf '\n!! Please install Nix to use this direnv config\n\n' + exit 1 +fi + +# Load the user's .envrc.local file if it exists +source_env_if_exists .envrc.local diff --git a/.gitignore b/.gitignore index 35e2c9b..ec20a44 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ .env +.direnv/ +.envrc.local + .DS_Store .vscode/ diff --git a/nix/README.md b/nix/README.md new file mode 100644 index 0000000..af2f826 --- /dev/null +++ b/nix/README.md @@ -0,0 +1,25 @@ +# Nix + +This directory contains the Nix configuration for the render-mcp-server +development environment. From the repository root, run `direnv allow` or +`nix develop ./nix#dev` to enter the shell. + + +## Upgrading Nix Packages + +### Performing the upgrade + +To upgrade nix packages run: +```console +$ nix flake update +``` + +This will update all inputs. + +### Test that things continue to work + +1. Open a shell at the root of this repository. +2. Build the environment and perform some tests: + ```console + $ nix develop path:nix#dev --command go test ./... + ``` diff --git a/nix/flake.lock b/nix/flake.lock new file mode 100644 index 0000000..54675ba --- /dev/null +++ b/nix/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1780336545, + "narHash": "sha256-vhVhuXzFrIOfcssC/9hDHx7MHzDKjF3keHuREOQqQiQ=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "4df1b885d76a54e1aa1a318f8d16fd6005b6401f", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/nix/flake.nix b/nix/flake.nix new file mode 100644 index 0000000..a4ee68f --- /dev/null +++ b/nix/flake.nix @@ -0,0 +1,19 @@ +{ + description = "render-mcp-server"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: { + devShells = import ./shells.nix { + pkgs = import nixpkgs { + inherit system; + config.allowUnfree = true; + overlays = import ./overlays; + }; + }; + }); +} diff --git a/nix/overlays/default.nix b/nix/overlays/default.nix new file mode 100644 index 0000000..8ba7e0b --- /dev/null +++ b/nix/overlays/default.nix @@ -0,0 +1,7 @@ +[ + (self: super: + { + mkNiceShell = self.callPackage ./mkNiceShell.nix { }; + } + ) +] diff --git a/nix/overlays/mkNiceShell.nix b/nix/overlays/mkNiceShell.nix new file mode 100644 index 0000000..00f8e5c --- /dev/null +++ b/nix/overlays/mkNiceShell.nix @@ -0,0 +1,134 @@ +# A helper to make a more user friendly shell. Regular Nix shells +# export a ton of unnecessary environment variables. Shells created with +# this helper are much more clean. +# +# This is based off of the following files: +# - https://github.com/numtide/devshell/ +# blob/3e0e60ab37cd0bf7ab59888f5c32499d851edb47/nix/mkNakedShell.nix +# - https://github.com/thenonameguy/devenv/ +# blob/4717da802b1868318ab60758c244c4b37774f426/src/modules/mkNakedShell.nix +# +# Consequently, this code is goverened by the MIT license: +# ----------------------------------------------------------------------------- +# MIT License +# +# Copyright (c) 2021 Numtide and contributors +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# ----------------------------------------------------------------------------- + +{ pkgs, system, ... }: +let + inherit (pkgs) bashInteractive coreutils lib writeTextFile; + + bashPath = "${bashInteractive}/bin/bash"; + stdenv = writeTextFile { + name = "naked-stdenv"; + destination = "/setup"; + text = '' + # Fix for `nix develop` + : ''${outputs:=out} + + runHook() { + eval "$shellHook" + unset runHook + } + ''; + }; +in + +{ name ? "nice-shell" +, shellHook ? "" +, envVars ? { } +, profilePackages ? [ ] +, meta ? { } +, passthru ? { } +}: +let + + maybeEscape = value: + let + str = toString value; + in + if (builtins.match ''".*"'' str != null) + then str else lib.escapeShellArg str; + + exportEnvVars = + envVars: builtins.concatStringsSep "\n" ( + lib.mapAttrsToList + (name: value: "export ${name}=${maybeEscape value}") + envVars); + + profile = pkgs.buildEnv { + name = "${name}-profile"; + paths = builtins.concatMap + (p: builtins.map (o: lib.getOutput o p) p.outputs) + profilePackages; + ignoreCollisions = true; + }; + +in +(derivation { + inherit name; + system = pkgs.stdenv.hostPlatform.system; + + # `nix develop` actually checks and uses builder. And it must be bash. + builder = bashPath; + + # Bring in the dependencies on `nix-build` + args = [ "-ec" "${coreutils}/bin/ln -s ${profile} $out; exit 0" ]; + + # $stdenv/setup is loaded by nix-shell during startup. + # https://github.com/nixos/nix/blob/377345e26f1ac4bbc87bb21debcc52a1d03230aa/src/nix-build/nix-build.cc#L429-L432 + inherit stdenv; + + # The shellHook is loaded directly by `nix develop`. But nix-shell + # requires that other trampoline. + shellHook = '' + # Remove all the unnecessary noise that is set by the build env + unset NIX_BUILD_TOP NIX_BUILD_CORES NIX_STORE + unset TEMP TEMPDIR TMP TMPDIR + # $name variable is preserved to keep it compatible with pure shell https://github.com/sindresorhus/pure/blob/47c0c881f0e7cfdb5eaccd335f52ad17b897c060/pure.zsh#L235 + unset builder out shellHook stdenv system + # Flakes stuff + unset dontAddDisableDepTrack outputs + + # For `nix develop`. We get /noshell on Linux and /sbin/nologin on macOS. + if [[ "$SHELL" == "/noshell" || "$SHELL" == "/sbin/nologin" ]]; then + export SHELL=${bashPath} + fi + + # add the profile packages to path + export PATH="${profile}/bin:$PATH" + + # prepend common compilation lookup paths + export PKG_CONFIG_PATH="${profile}/lib/pkgconfig:$PKG_CONFIG_PATH" + export LD_LIBRARY_PATH="${profile}/lib:$LD_LIBRARY_PATH" + export LIBRARY_PATH="${profile}/lib:$LIBRARY_PATH" + export C_INCLUDE_PATH="${profile}/include:$C_INCLUDE_PATH" + + # shell completions and default data dirs + export XDG_DATA_DIRS="${profile}/share:$XDG_DATA_DIRS" + export XDG_CONFIG_DIRS="${profile}/etc/xdg:$XDG_CONFIG_DIRS" + + ${exportEnvVars envVars} + + ${shellHook} + ''; +}) // { inherit meta passthru; } // passthru diff --git a/nix/shells.nix b/nix/shells.nix new file mode 100644 index 0000000..dfad922 --- /dev/null +++ b/nix/shells.nix @@ -0,0 +1,18 @@ +{ pkgs, ... }: +{ + dev = pkgs.mkNiceShell { + envVars = { + RENDER_MCP_SERVER_PATH = ''"$PWD"''; + }; + + profilePackages = with pkgs; [ + # Golang + go_1_26 + goreleaser + + # Basic unix tools + coreutils + findutils + ]; + }; +}