{ description = "Reference Board Viewer - Web-based visual reference management"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixos-generators = { url = "github:nix-community/nixos-generators"; inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = { self, nixpkgs, nixos-generators, }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; # Shared Python dependencies - used by both dev environment and package pythonDeps = ps: withTests: with ps; [ # Core backend dependencies fastapi uvicorn sqlalchemy alembic pydantic pydantic-settings # Settings management psycopg2 # PostgreSQL driver (sync) asyncpg # PostgreSQL driver (async) # Auth & Security python-jose passlib bcrypt # Password hashing backend for passlib email-validator # Email validation for pydantic # Image processing pillow python-magic # File type detection via magic bytes # Storage boto3 # HTTP & uploads httpx python-multipart ] ++ ( if withTests then [ # Testing (dev only) pytest pytest-cov pytest-asyncio ] else [ ] ); pythonEnv = pkgs.python3.withPackages (ps: pythonDeps ps true); in { # Development shell devShells.${system}.default = pkgs.mkShell { buildInputs = with pkgs; [ # Python environment pythonEnv uv ruff # Database postgresql # Frontend nodejs nodePackages.npm eslint # Image processing imagemagick file # Required for python-magic to detect file types # Storage minio minio-client # Development tools git direnv tmux ]; shellHook = '' echo "šŸš€ Reference Board Viewer Development Environment" echo "" echo "šŸ“¦ Versions:" echo " Python: $(python --version)" echo " Node.js: $(node --version)" echo " PostgreSQL: $(psql --version | head -n1)" echo " MinIO: $(minio --version | head -n1)" echo "" echo "šŸ”§ Development Services:" echo " Start: ./scripts/dev-services.sh start" echo " Stop: ./scripts/dev-services.sh stop" echo " Status: ./scripts/dev-services.sh status" echo "" echo "šŸ“š Quick Commands:" echo " Dev (tmux): nix run .#dev" echo " Backend: cd backend && uvicorn app.main:app --reload" echo " Frontend: cd frontend && npm run dev" echo " Database: psql -h localhost -U webref webref" echo " Tests: cd backend && pytest --cov" echo "" echo "šŸ“– Documentation:" echo " API Docs: http://localhost:8000/docs" echo " App: http://localhost:5173" echo " MinIO UI: http://localhost:9001" echo "" # Set up environment variables export DATABASE_URL="postgresql://webref@localhost:5432/webref" export MINIO_ENDPOINT="localhost:9000" export MINIO_ACCESS_KEY="minioadmin" export MINIO_SECRET_KEY="minioadmin" export PYTHONPATH="$PWD/backend:$PYTHONPATH" ''; }; # Apps - Scripts that can be run with `nix run` apps.${system} = { default = { type = "app"; program = "${pkgs.writeShellScript "help" '' echo "Available commands:" echo " nix run .#dev - Start backend + frontend in tmux" echo " nix run .#lint - Run all linting checks" echo " nix run .#lint-backend - Run backend linting only" echo " nix run .#lint-frontend - Run frontend linting only" echo " nix run .#lint-fix - Auto-fix linting issues" ''}"; }; # Development runner with tmux dev = { type = "app"; program = "${pkgs.writeShellScript "dev-tmux" '' set -e # Check if we're in the project root if [ ! -d "backend" ] || [ ! -d "frontend" ]; then echo "āŒ Error: Not in project root directory" echo "Please run this command from the webref project root" exit 1 fi # Check if frontend dependencies are installed if [ ! -d "frontend/node_modules" ]; then echo "šŸ“¦ Installing frontend dependencies..." cd frontend ${pkgs.nodejs}/bin/npm install cd .. fi # Set environment variables export DATABASE_URL="postgresql://webref@localhost:5432/webref" export MINIO_ENDPOINT="localhost:9000" export MINIO_ACCESS_KEY="minioadmin" export MINIO_SECRET_KEY="minioadmin" export PYTHONPATH="$PWD/backend:$PYTHONPATH" export PATH="${pythonEnv}/bin:${pkgs.nodejs}/bin:$PATH" # Session name SESSION_NAME="webref-dev" # Kill existing session if it exists ${pkgs.tmux}/bin/tmux has-session -t $SESSION_NAME 2>/dev/null && ${pkgs.tmux}/bin/tmux kill-session -t $SESSION_NAME echo "šŸš€ Starting development environment in tmux..." echo "" echo "šŸ“‹ Tmux Controls:" echo " Switch panes: Ctrl+b → arrow keys" echo " Scroll mode: Ctrl+b → [" echo " Exit scroll: q" echo " Detach session: Ctrl+b → d" echo " Kill session: Ctrl+b → :kill-session" echo "" echo "Starting in 2 seconds..." sleep 2 # Create new tmux session with backend ${pkgs.tmux}/bin/tmux new-session -d -s "$SESSION_NAME" -n "webref" -c "$PWD/backend" \ "printf '\nšŸ Starting Backend (uvicorn)...\n\n' && ${pythonEnv}/bin/uvicorn app.main:app --reload --host 0.0.0.0 --port 8000; read -p 'Backend stopped. Press Enter to exit...'" # Split window vertically and run frontend ${pkgs.tmux}/bin/tmux split-window -h -t "$SESSION_NAME":0 -c "$PWD/frontend" \ "printf '\n⚔ Starting Frontend (Vite)...\n\n' && ${pkgs.nodejs}/bin/npm run dev; read -p 'Frontend stopped. Press Enter to exit...'" # Set pane titles ${pkgs.tmux}/bin/tmux select-pane -t "$SESSION_NAME":0.0 -T "Backend (uvicorn)" ${pkgs.tmux}/bin/tmux select-pane -t "$SESSION_NAME":0.1 -T "Frontend (vite)" # Balance panes ${pkgs.tmux}/bin/tmux select-layout -t "$SESSION_NAME":0 even-horizontal # Focus on backend pane ${pkgs.tmux}/bin/tmux select-pane -t "$SESSION_NAME":0.0 # Attach to session ${pkgs.tmux}/bin/tmux attach-session -t "$SESSION_NAME" ''}"; }; # Unified linting - calls both backend and frontend lints lint = { type = "app"; program = "${pkgs.writeShellScript "lint" '' set -e # Run backend linting ${self.apps.${system}.lint-backend.program} echo "" # Run frontend linting ${self.apps.${system}.lint-frontend.program} echo "" echo "āœ… All linting checks passed!" ''}"; }; # Auto-fix linting issues lint-fix = { type = "app"; program = "${pkgs.writeShellScript "lint-fix" '' set -e echo "šŸ”§ Auto-fixing backend Python code..." if [ -d "backend" ]; then cd backend ${pkgs.ruff}/bin/ruff check --fix --no-cache app/ || true ${pkgs.ruff}/bin/ruff format app/ cd .. else echo "⚠ Not in project root (backend/ not found)" exit 1 fi if [ -d "frontend/node_modules" ]; then echo "" echo "šŸ”§ Auto-fixing frontend code..." cd frontend ${pkgs.nodePackages.prettier}/bin/prettier --write src/ cd .. fi echo "" echo "āœ… Auto-fix complete!" ''}"; }; # Backend linting only lint-backend = { type = "app"; program = "${pkgs.writeShellScript "lint-backend" '' set -e echo "šŸ” Linting backend Python code..." if [ -d "backend" ]; then cd backend ${pkgs.ruff}/bin/ruff check --no-cache app/ ${pkgs.ruff}/bin/ruff format --check app/ cd .. else echo "⚠ Not in project root (backend/ not found)" exit 1 fi echo "āœ… Backend linting passed!" ''}"; }; # Frontend linting only lint-frontend = { type = "app"; program = "${pkgs.writeShellScript "lint-frontend" '' set -e # Add nodejs to PATH for npm scripts export PATH="${pkgs.nodejs}/bin:$PATH" echo "šŸ” Linting frontend TypeScript/Svelte code..." if [ -d "frontend/node_modules" ]; then cd frontend npm run lint ${pkgs.nodePackages.prettier}/bin/prettier --check src/ npm run check cd .. else echo "⚠ Frontend node_modules not found" echo "Run 'cd frontend && npm install' first" exit 1 fi echo "āœ… Frontend linting passed!" ''}"; }; # Run development VM dev-vm = { type = "app"; program = "${self.packages.${system}.dev-vm}/bin/run-nixos-vm"; }; }; # Package definitions (for production deployment) packages.${system} = { # Backend package backend = pkgs.python3Packages.buildPythonApplication { pname = "webref-backend"; version = "1.0.0"; pyproject = true; src = ./backend; build-system = with pkgs.python3Packages; [ setuptools ]; propagatedBuildInputs = pythonDeps pkgs.python3Packages false; meta = { description = "Reference Board Viewer - Backend API"; homepage = "https://github.com/yourusername/webref"; license = pkgs.lib.licenses.mit; }; }; # QEMU VM for development services dev-vm = nixos-generators.nixosGenerate { system = "x86_64-linux"; modules = [ ./nixos/dev-services.nix ]; format = "vm"; }; # VM for CI testing ci-vm = nixos-generators.nixosGenerate { system = "x86_64-linux"; modules = [ ./nixos/dev-services.nix { # CI-specific configuration services.openssh.enable = true; services.openssh.settings.PermitRootLogin = "yes"; users.users.root.password = "test"; } ]; format = "vm"; }; # Container for lightweight testing dev-container = nixos-generators.nixosGenerate { system = "x86_64-linux"; modules = [ ./nixos/dev-services.nix ]; format = "lxc"; }; default = self.packages.${system}.backend; }; # NixOS VM tests checks.${system} = import ./nixos/tests.nix { inherit pkgs; }; # NixOS configurations nixosConfigurations = { # Development services VM dev-services = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ ./nixos/dev-services.nix { # Minimal system configuration fileSystems."/" = { device = "tmpfs"; fsType = "tmpfs"; options = [ "mode=0755" ]; }; boot.loader.systemd-boot.enable = true; system.stateVersion = "24.05"; } ]; }; }; }; }