Add NixOS VM integration tests and update CI/CD pipeline configuration. Introduce checks for backend integration, full-stack, performance, and security tests using native NixOS services. Remove legacy GitHub Actions workflow and replace with Gitea Actions runner configuration. Update README and quickstart guide to reflect new development environment setup and testing commands.
This commit is contained in:
221
.gitea/workflows/ci.yml
Normal file
221
.gitea/workflows/ci.yml
Normal file
@@ -0,0 +1,221 @@
|
||||
# CI/CD Pipeline - NixOS VM Tests Only
|
||||
# All tests run in isolated NixOS VMs with native services (no Docker)
|
||||
|
||||
name: CI/CD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop, '001-*']
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
|
||||
jobs:
|
||||
# NixOS VM integration tests (PostgreSQL + MinIO native services)
|
||||
nixos-vm-tests:
|
||||
name: VM Test - ${{ matrix.test }}
|
||||
runs-on: nix
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
test:
|
||||
- backend-integration # Backend + PostgreSQL + MinIO
|
||||
- full-stack # Complete API stack
|
||||
- performance # Benchmarks
|
||||
- security # Security suite
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Configure Attic binary cache
|
||||
- name: Configure Attic cache
|
||||
run: |
|
||||
attic login lan http://127.0.0.1:2343 ${{ secrets.ATTIC_TOKEN }}
|
||||
attic use lan:webref
|
||||
|
||||
# Cache Nix store for faster VM builds
|
||||
- name: Cache Nix store
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/nix
|
||||
key: nix-vm-${{ matrix.test }}-${{ hashFiles('flake.nix', 'flake.lock', 'nixos/tests.nix') }}
|
||||
restore-keys: |
|
||||
nix-vm-${{ matrix.test }}-
|
||||
nix-vm-
|
||||
|
||||
# Run NixOS VM test
|
||||
- name: Run ${{ matrix.test }}
|
||||
run: |
|
||||
echo "🚀 Starting NixOS VM test: ${{ matrix.test }}"
|
||||
nix build .#checks.${{ matrix.test }} -L --accept-flake-config
|
||||
echo "✅ Test passed"
|
||||
|
||||
# Push to Attic cache
|
||||
- name: Push to Attic cache
|
||||
if: success()
|
||||
run: |
|
||||
attic push lan:webref result
|
||||
|
||||
# Archive logs on failure
|
||||
- name: Archive test logs
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: vm-logs-${{ matrix.test }}
|
||||
path: result/
|
||||
retention-days: 3
|
||||
|
||||
# Quick checks (no VM needed)
|
||||
lint:
|
||||
name: Linting & Formatting
|
||||
runs-on: nix
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Configure Attic cache
|
||||
- name: Configure Attic cache
|
||||
run: |
|
||||
attic login lan http://127.0.0.1:2343 ${{ secrets.ATTIC_TOKEN }}
|
||||
attic use lan:webref
|
||||
|
||||
# Cache node_modules for linting
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: frontend/node_modules
|
||||
key: npm-${{ hashFiles('frontend/package-lock.json') }}
|
||||
restore-keys: npm-
|
||||
|
||||
- name: Backend - Ruff check
|
||||
run: nix develop --command bash -c "cd backend && ruff check app/"
|
||||
|
||||
- name: Backend - Ruff format check
|
||||
run: nix develop --command bash -c "cd backend && ruff format --check app/"
|
||||
|
||||
- name: Frontend - Install deps (if needed)
|
||||
run: nix develop --command bash -c "cd frontend && [ -d node_modules ] || npm ci"
|
||||
|
||||
- name: Frontend - ESLint
|
||||
run: nix develop --command bash -c "cd frontend && npm run lint"
|
||||
|
||||
- name: Frontend - Prettier check
|
||||
run: nix develop --command bash -c "cd frontend && npx prettier --check ."
|
||||
|
||||
- name: Frontend - Svelte check
|
||||
run: nix develop --command bash -c "cd frontend && npm run check"
|
||||
|
||||
- name: Nix - Flake check
|
||||
run: nix flake check --accept-flake-config
|
||||
|
||||
# Unit tests (fast, no services needed)
|
||||
unit-tests:
|
||||
name: Unit Tests
|
||||
runs-on: nix
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Configure Attic cache
|
||||
- name: Configure Attic cache
|
||||
run: |
|
||||
attic login lan http://127.0.0.1:2343 ${{ secrets.ATTIC_TOKEN }}
|
||||
attic use lan:webref
|
||||
|
||||
# Cache pytest discovery
|
||||
- name: Cache pytest
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: backend/.pytest_cache
|
||||
key: pytest-${{ hashFiles('backend/tests/**/*.py') }}
|
||||
|
||||
# Cache node_modules
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: frontend/node_modules
|
||||
key: npm-${{ hashFiles('frontend/package-lock.json') }}
|
||||
restore-keys: npm-
|
||||
|
||||
- name: Backend unit tests
|
||||
run: |
|
||||
nix develop --command bash -c "
|
||||
cd backend &&
|
||||
pytest tests/unit/ -v \
|
||||
--cov=app \
|
||||
--cov-report=xml \
|
||||
--cov-report=term-missing \
|
||||
--cov-fail-under=80
|
||||
"
|
||||
|
||||
- name: Frontend - Install deps (if needed)
|
||||
run: nix develop --command bash -c "cd frontend && [ -d node_modules ] || npm ci"
|
||||
|
||||
- name: Frontend unit tests
|
||||
run: nix develop --command bash -c "cd frontend && npm run test:coverage"
|
||||
|
||||
- name: Upload coverage
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-reports
|
||||
path: |
|
||||
backend/coverage.xml
|
||||
backend/htmlcov/
|
||||
frontend/coverage/
|
||||
retention-days: 7
|
||||
|
||||
# Verify packages build
|
||||
build:
|
||||
name: Build Packages
|
||||
runs-on: nix
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Configure Attic cache
|
||||
- name: Configure Attic cache
|
||||
run: |
|
||||
attic login lan http://127.0.0.1:2343 ${{ secrets.ATTIC_TOKEN }}
|
||||
attic use lan:webref
|
||||
|
||||
- name: Build backend package
|
||||
run: nix build .#backend -L --accept-flake-config
|
||||
|
||||
- name: Push backend to Attic
|
||||
if: success()
|
||||
run: attic push lan:webref result
|
||||
|
||||
- name: Build frontend package
|
||||
run: nix build .#frontend -L --accept-flake-config
|
||||
|
||||
- name: Push frontend to Attic
|
||||
if: success()
|
||||
run: attic push lan:webref result
|
||||
|
||||
# Summary
|
||||
summary:
|
||||
name: CI Summary
|
||||
runs-on: nix
|
||||
needs: [nixos-vm-tests, lint, unit-tests, build]
|
||||
if: always()
|
||||
|
||||
steps:
|
||||
- name: Results
|
||||
run: |
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📊 CI Pipeline Results"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "NixOS VMs: ${{ needs.nixos-vm-tests.result }}"
|
||||
echo "Linting: ${{ needs.lint.result }}"
|
||||
echo "Unit Tests: ${{ needs.unit-tests.result }}"
|
||||
echo "Build: ${{ needs.build.result }}"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
if [[ "${{ needs.nixos-vm-tests.result }}" != "success" ]] || \
|
||||
[[ "${{ needs.lint.result }}" != "success" ]] || \
|
||||
[[ "${{ needs.unit-tests.result }}" != "success" ]] || \
|
||||
[[ "${{ needs.build.result }}" != "success" ]]; then
|
||||
echo "❌ Pipeline Failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ All Checks Passed"
|
||||
181
.github/workflows/ci.yml
vendored
181
.github/workflows/ci.yml
vendored
@@ -1,181 +0,0 @@
|
||||
name: CI/CD Pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop, '**']
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
|
||||
jobs:
|
||||
backend-tests:
|
||||
name: Backend Tests
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16
|
||||
env:
|
||||
POSTGRES_DB: webref_test
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v27
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
|
||||
- name: Setup Python dependencies
|
||||
run: |
|
||||
cd backend
|
||||
python -m pip install --upgrade pip
|
||||
pip install -e ".[dev]"
|
||||
|
||||
- name: Run Ruff linter
|
||||
run: |
|
||||
cd backend
|
||||
ruff check app/
|
||||
|
||||
- name: Run Ruff formatter check
|
||||
run: |
|
||||
cd backend
|
||||
ruff format --check app/
|
||||
|
||||
- name: Run tests with coverage
|
||||
env:
|
||||
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/webref_test
|
||||
run: |
|
||||
cd backend
|
||||
pytest --cov=app --cov-report=xml --cov-report=term
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
file: ./backend/coverage.xml
|
||||
flags: backend
|
||||
name: backend-coverage
|
||||
|
||||
frontend-tests:
|
||||
name: Frontend Tests
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: frontend/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd frontend
|
||||
npm ci
|
||||
|
||||
- name: Run ESLint
|
||||
run: |
|
||||
cd frontend
|
||||
npm run lint
|
||||
|
||||
- name: Run Prettier check
|
||||
run: |
|
||||
cd frontend
|
||||
npx prettier --check .
|
||||
|
||||
- name: Run Svelte check
|
||||
run: |
|
||||
cd frontend
|
||||
npm run check
|
||||
|
||||
- name: Run tests with coverage
|
||||
run: |
|
||||
cd frontend
|
||||
npm run test:coverage
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
file: ./frontend/coverage/coverage-final.json
|
||||
flags: frontend
|
||||
name: frontend-coverage
|
||||
|
||||
integration-tests:
|
||||
name: Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
needs: [backend-tests, frontend-tests]
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16
|
||||
env:
|
||||
POSTGRES_DB: webref_test
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
minio:
|
||||
image: minio/minio
|
||||
env:
|
||||
MINIO_ROOT_USER: minioadmin
|
||||
MINIO_ROOT_PASSWORD: minioadmin
|
||||
ports:
|
||||
- 9000:9000
|
||||
options: >-
|
||||
--health-cmd "curl -f http://localhost:9000/minio/health/live"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v27
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
|
||||
- name: Run integration tests
|
||||
env:
|
||||
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/webref_test
|
||||
MINIO_ENDPOINT: localhost:9000
|
||||
MINIO_ACCESS_KEY: minioadmin
|
||||
MINIO_SECRET_KEY: minioadmin
|
||||
run: |
|
||||
cd backend
|
||||
pytest tests/integration/
|
||||
|
||||
nix-build:
|
||||
name: Nix Build Check
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v27
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
|
||||
- name: Check flake
|
||||
run: nix flake check
|
||||
|
||||
- name: Build dev shell
|
||||
run: nix develop --command echo "Dev shell OK"
|
||||
|
||||
62
README.md
62
README.md
@@ -16,20 +16,23 @@ This project follows a formal constitution that establishes binding principles f
|
||||
|
||||
## Development Environment
|
||||
|
||||
This project uses Nix for reproducible development environments:
|
||||
This project uses Nix flakes for reproducible development environments:
|
||||
|
||||
```bash
|
||||
# Enter development shell
|
||||
nix-shell
|
||||
# Enter development shell (from flake.nix)
|
||||
nix develop
|
||||
|
||||
# Or use direnv for automatic activation
|
||||
echo "use nix" > .envrc
|
||||
direnv allow
|
||||
direnv allow # .envrc already configured
|
||||
```
|
||||
|
||||
**Included tools:**
|
||||
- Python 3 with setuptools
|
||||
- uv (fast Python package manager)
|
||||
- Python 3.12 with all backend dependencies (FastAPI, SQLAlchemy, pytest, etc.)
|
||||
- Node.js + npm for frontend development
|
||||
- PostgreSQL client tools
|
||||
- MinIO client
|
||||
- Ruff (Python linter/formatter)
|
||||
- All project dependencies from flake.nix
|
||||
|
||||
## Project Structure
|
||||
|
||||
@@ -44,7 +47,19 @@ webref/
|
||||
│ ├── tasks-template.md # Task tracking template
|
||||
│ └── commands/
|
||||
│ └── constitution.md # Constitution amendment workflow
|
||||
├── shell.nix # Nix development environment
|
||||
├── backend/ # FastAPI backend application
|
||||
│ ├── app/ # Application code
|
||||
│ ├── tests/ # pytest test suite
|
||||
│ └── pyproject.toml # Python dependencies
|
||||
├── frontend/ # Svelte + Konva.js frontend
|
||||
│ ├── src/ # Application code
|
||||
│ ├── tests/ # Vitest test suite
|
||||
│ └── package.json # Node dependencies
|
||||
├── nixos/ # NixOS configuration and tests
|
||||
│ ├── tests.nix # NixOS VM integration tests
|
||||
│ └── gitea-runner.nix # Gitea Actions runner config
|
||||
├── flake.nix # Nix flake (dependencies & dev shell)
|
||||
├── .envrc # direnv configuration
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
@@ -94,16 +109,37 @@ All code must meet these requirements before merge:
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# Run tests
|
||||
pytest
|
||||
### Unit Tests
|
||||
|
||||
# With coverage report
|
||||
pytest --cov=webref --cov-report=html
|
||||
```bash
|
||||
# Backend tests
|
||||
cd backend && pytest --cov=app --cov-report=html
|
||||
|
||||
# Frontend tests
|
||||
cd frontend && npm test
|
||||
|
||||
# Coverage must be ≥80% per Constitutional Principle 2
|
||||
```
|
||||
|
||||
### NixOS VM Integration Tests
|
||||
|
||||
```bash
|
||||
# Run all integration tests in isolated VMs
|
||||
nix flake check
|
||||
|
||||
# Run specific test
|
||||
nix build .#checks.backend-integration
|
||||
nix build .#checks.full-stack
|
||||
nix build .#checks.performance
|
||||
nix build .#checks.security
|
||||
|
||||
# Interactive debugging
|
||||
nix build .#checks.backend-integration.driverInteractive
|
||||
./result/bin/nixos-test-driver
|
||||
```
|
||||
|
||||
See [Tech Research](specs/001-reference-board-viewer/tech-research.md) for CI/testing architecture details.
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Read the [constitution](.specify/memory/constitution.md)
|
||||
|
||||
@@ -129,6 +129,9 @@
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
# NixOS VM tests
|
||||
checks = import ./nixos/tests.nix { inherit pkgs; };
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
112
nixos/gitea-runner.nix
Normal file
112
nixos/gitea-runner.nix
Normal file
@@ -0,0 +1,112 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
{
|
||||
# Gitea Actions Runner Configuration
|
||||
# This module configures a Gitea runner for CI/CD with Nix support
|
||||
|
||||
services.gitea-actions-runner = {
|
||||
package = pkgs.gitea-actions-runner;
|
||||
|
||||
instances = {
|
||||
# Main runner instance for webref project
|
||||
webref-runner = {
|
||||
enable = true;
|
||||
|
||||
# Runner name (will appear in Gitea)
|
||||
name = "nixos-runner-webref";
|
||||
|
||||
# Gitea instance URL
|
||||
url = "https://your-gitea-instance.com";
|
||||
|
||||
# Runner token - Generate this from Gitea:
|
||||
# Settings -> Actions -> Runners -> Create New Runner
|
||||
# Store the token in a file and reference it here
|
||||
tokenFile = "/var/secrets/gitea-runner-token";
|
||||
|
||||
# Labels define what jobs this runner can handle
|
||||
# Format: "label:docker_image" or just "label" for host execution
|
||||
labels = [
|
||||
# Native execution with Nix
|
||||
"nix:native"
|
||||
|
||||
# Ubuntu-like for compatibility
|
||||
"ubuntu-latest:docker://node:20-bookworm"
|
||||
|
||||
# Specific for this project
|
||||
"webref:native"
|
||||
];
|
||||
|
||||
# Host packages available to the runner
|
||||
hostPackages = with pkgs; [
|
||||
# Essential tools
|
||||
bash
|
||||
coreutils
|
||||
curl
|
||||
git
|
||||
nix
|
||||
|
||||
# Project-specific
|
||||
nodejs
|
||||
python3
|
||||
postgresql
|
||||
|
||||
# Binary cache
|
||||
attic-client
|
||||
|
||||
# Container runtime (optional)
|
||||
docker
|
||||
docker-compose
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Enable Docker for service containers (PostgreSQL, MinIO, etc.)
|
||||
virtualisation.docker = {
|
||||
enable = true;
|
||||
autoPrune.enable = true;
|
||||
autoPrune.dates = "weekly";
|
||||
};
|
||||
|
||||
# Ensure the runner user has access to Docker
|
||||
users.users.gitea-runner = {
|
||||
isSystemUser = true;
|
||||
group = "gitea-runner";
|
||||
extraGroups = [ "docker" ];
|
||||
};
|
||||
|
||||
users.groups.gitea-runner = {};
|
||||
|
||||
# Allow runner to use Nix
|
||||
nix.settings = {
|
||||
allowed-users = [ "gitea-runner" ];
|
||||
trusted-users = [ "gitea-runner" ];
|
||||
|
||||
# Enable flakes for the runner
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
|
||||
# Optimize for CI performance
|
||||
max-jobs = "auto";
|
||||
cores = 0; # Use all available cores
|
||||
};
|
||||
|
||||
# Network access for downloading packages
|
||||
networking.firewall = {
|
||||
# If your runner needs to expose ports, configure them here
|
||||
# allowedTCPPorts = [ ];
|
||||
};
|
||||
|
||||
# Systemd service optimizations
|
||||
systemd.services."gitea-runner-webref-runner" = {
|
||||
serviceConfig = {
|
||||
# Resource limits (adjust based on your hardware)
|
||||
MemoryMax = "8G";
|
||||
CPUQuota = "400%"; # 4 cores
|
||||
|
||||
# Restart policy
|
||||
Restart = "always";
|
||||
RestartSec = "10s";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
211
nixos/tests.nix
Normal file
211
nixos/tests.nix
Normal file
@@ -0,0 +1,211 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
let
|
||||
# Import the flake to get our packages
|
||||
webref = builtins.getFlake (toString ../.);
|
||||
in
|
||||
{
|
||||
# Backend integration tests with PostgreSQL and MinIO
|
||||
backend-integration = pkgs.nixosTest {
|
||||
name = "webref-backend-integration";
|
||||
|
||||
nodes = {
|
||||
machine = { config, pkgs, ... }: {
|
||||
# PostgreSQL service
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
ensureDatabases = [ "webref_test" ];
|
||||
ensureUsers = [{
|
||||
name = "webref";
|
||||
ensureDBOwnership = true;
|
||||
}];
|
||||
authentication = ''
|
||||
local all all trust
|
||||
host all all 127.0.0.1/32 trust
|
||||
host all all ::1/128 trust
|
||||
'';
|
||||
};
|
||||
|
||||
# MinIO service
|
||||
services.minio = {
|
||||
enable = true;
|
||||
rootCredentialsFile = pkgs.writeText "minio-credentials" ''
|
||||
MINIO_ROOT_USER=minioadmin
|
||||
MINIO_ROOT_PASSWORD=minioadmin
|
||||
'';
|
||||
};
|
||||
|
||||
# Ensure our dev environment is available
|
||||
environment.systemPackages = with pkgs; [
|
||||
webref.devShells.${system}.default.inputDerivation
|
||||
];
|
||||
|
||||
# Network configuration
|
||||
networking.firewall.enable = false;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
# Wait for PostgreSQL
|
||||
machine.wait_for_unit("postgresql.service")
|
||||
machine.wait_for_open_port(5432)
|
||||
|
||||
# Wait for MinIO
|
||||
machine.wait_for_unit("minio.service")
|
||||
machine.wait_for_open_port(9000)
|
||||
|
||||
# Create test database
|
||||
machine.succeed("sudo -u postgres psql -c 'CREATE DATABASE webref_test;'")
|
||||
|
||||
# Run backend tests
|
||||
machine.succeed("""
|
||||
cd /tmp/webref
|
||||
export DATABASE_URL="postgresql://webref@localhost/webref_test"
|
||||
export MINIO_ENDPOINT="localhost:9000"
|
||||
export MINIO_ACCESS_KEY="minioadmin"
|
||||
export MINIO_SECRET_KEY="minioadmin"
|
||||
export MINIO_BUCKET="webref"
|
||||
export MINIO_SECURE="false"
|
||||
|
||||
${pkgs.python3}/bin/python -m pytest backend/tests/ -v
|
||||
""")
|
||||
|
||||
machine.succeed("echo '✅ Backend integration tests passed'")
|
||||
'';
|
||||
};
|
||||
|
||||
# Full stack test with backend + frontend + database
|
||||
full-stack = pkgs.nixosTest {
|
||||
name = "webref-full-stack";
|
||||
|
||||
nodes = {
|
||||
server = { config, pkgs, ... }: {
|
||||
# PostgreSQL
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
ensureDatabases = [ "webref" ];
|
||||
ensureUsers = [{
|
||||
name = "webref";
|
||||
ensureDBOwnership = true;
|
||||
}];
|
||||
};
|
||||
|
||||
# MinIO
|
||||
services.minio = {
|
||||
enable = true;
|
||||
rootCredentialsFile = pkgs.writeText "minio-credentials" ''
|
||||
MINIO_ROOT_USER=minioadmin
|
||||
MINIO_ROOT_PASSWORD=minioadmin
|
||||
'';
|
||||
};
|
||||
|
||||
# Backend API (FastAPI)
|
||||
systemd.services.webref-backend = {
|
||||
description = "WebRef Backend API";
|
||||
after = [ "postgresql.service" "minio.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
environment = {
|
||||
DATABASE_URL = "postgresql://webref@localhost/webref";
|
||||
MINIO_ENDPOINT = "localhost:9000";
|
||||
MINIO_ACCESS_KEY = "minioadmin";
|
||||
MINIO_SECRET_KEY = "minioadmin";
|
||||
SECRET_KEY = "test-secret-key-do-not-use-in-production";
|
||||
};
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = "${pkgs.python3}/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000";
|
||||
WorkingDirectory = "/tmp/webref/backend";
|
||||
Restart = "always";
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 8000 9000 ];
|
||||
};
|
||||
|
||||
client = { config, pkgs, ... }: {
|
||||
environment.systemPackages = [ pkgs.curl pkgs.jq ];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
# Wait for all services
|
||||
server.wait_for_unit("postgresql.service")
|
||||
server.wait_for_unit("minio.service")
|
||||
server.wait_for_unit("webref-backend.service")
|
||||
server.wait_for_open_port(8000)
|
||||
|
||||
# Test API health
|
||||
client.wait_for_unit("multi-user.target")
|
||||
client.succeed("curl -f http://server:8000/health")
|
||||
|
||||
# Test API endpoints
|
||||
response = client.succeed("curl -s http://server:8000/health | jq -r .status")
|
||||
assert "healthy" in response, f"Expected 'healthy', got {response}"
|
||||
|
||||
server.succeed("echo '✅ Full stack test passed'")
|
||||
'';
|
||||
};
|
||||
|
||||
# Performance benchmarks
|
||||
performance = pkgs.nixosTest {
|
||||
name = "webref-performance";
|
||||
|
||||
nodes = {
|
||||
machine = { config, pkgs, ... }: {
|
||||
services.postgresql.enable = true;
|
||||
services.minio.enable = true;
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
apache-bench
|
||||
wrk
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
machine.wait_for_unit("postgresql.service")
|
||||
|
||||
# Run performance tests
|
||||
machine.succeed("""
|
||||
cd /tmp/webref/backend
|
||||
${pkgs.python3}/bin/pytest tests/performance/ --benchmark-only
|
||||
""")
|
||||
|
||||
machine.succeed("echo '✅ Performance tests passed'")
|
||||
'';
|
||||
};
|
||||
|
||||
# Security tests
|
||||
security = pkgs.nixosTest {
|
||||
name = "webref-security";
|
||||
|
||||
nodes = {
|
||||
machine = { config, pkgs, ... }: {
|
||||
services.postgresql.enable = true;
|
||||
environment.systemPackages = with pkgs; [
|
||||
sqlmap
|
||||
nmap
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
# Run security test suite
|
||||
machine.succeed("""
|
||||
cd /tmp/webref/backend
|
||||
${pkgs.python3}/bin/pytest tests/security/ -v
|
||||
""")
|
||||
|
||||
machine.succeed("echo '✅ Security tests passed'")
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
71
shell.nix
71
shell.nix
@@ -1,71 +0,0 @@
|
||||
{
|
||||
pkgs ? import <nixpkgs> { },
|
||||
}:
|
||||
|
||||
pkgs.mkShell {
|
||||
packages =
|
||||
[
|
||||
# Python with development packages
|
||||
(pkgs.python3.withPackages (
|
||||
ps:
|
||||
builtins.attrValues {
|
||||
inherit (ps)
|
||||
setuptools
|
||||
pip
|
||||
# Core backend dependencies
|
||||
fastapi
|
||||
uvicorn
|
||||
sqlalchemy
|
||||
alembic
|
||||
pydantic
|
||||
# Auth & Security
|
||||
python-jose
|
||||
passlib
|
||||
# Image processing
|
||||
pillow
|
||||
# Storage
|
||||
boto3
|
||||
# HTTP & uploads
|
||||
httpx
|
||||
python-multipart
|
||||
# Testing
|
||||
pytest
|
||||
pytest-cov
|
||||
pytest-asyncio
|
||||
;
|
||||
}
|
||||
))
|
||||
]
|
||||
++ builtins.attrValues {
|
||||
inherit (pkgs)
|
||||
# Python tools
|
||||
uv
|
||||
ruff
|
||||
# Database
|
||||
postgresql
|
||||
# Frontend
|
||||
nodejs
|
||||
# Image processing
|
||||
imagemagick
|
||||
# Version control
|
||||
git
|
||||
# Development tools
|
||||
direnv
|
||||
;
|
||||
};
|
||||
|
||||
buildInputs = [ ];
|
||||
|
||||
shellHook = ''
|
||||
echo "🚀 Reference Board Viewer Development Environment"
|
||||
echo " Python: $(python --version)"
|
||||
echo " Node.js: $(node --version)"
|
||||
echo " PostgreSQL: $(psql --version | head -n1)"
|
||||
echo ""
|
||||
echo "📚 Quick Commands:"
|
||||
echo " Backend: cd backend && uvicorn app.main:app --reload"
|
||||
echo " Frontend: cd frontend && npm run dev"
|
||||
echo " Tests: cd backend && pytest --cov"
|
||||
echo ""
|
||||
'';
|
||||
}
|
||||
@@ -16,16 +16,17 @@ This guide will get you from zero to a running development environment for the R
|
||||
# Clone repository (if not already)
|
||||
cd /home/jawz/Development/Projects/personal/webref
|
||||
|
||||
# Enter Nix development shell (installs all dependencies)
|
||||
# Enter Nix development shell (from flake.nix)
|
||||
nix develop
|
||||
|
||||
# Verify tools are available
|
||||
python --version # Should show Python 3.12+
|
||||
node --version # Should show Node.js latest
|
||||
python --version # Python 3.12
|
||||
node --version # Node.js 20+
|
||||
psql --version # PostgreSQL client
|
||||
ruff --version # Python linter
|
||||
```
|
||||
|
||||
**What this does:** Nix installs all verified dependencies from nixpkgs (see VERIFICATION-COMPLETE.md)
|
||||
**What this does:** `flake.nix` provides all dependencies (Python, Node.js, PostgreSQL, MinIO, etc.)
|
||||
|
||||
---
|
||||
|
||||
@@ -240,26 +241,42 @@ webref/
|
||||
|
||||
### Backend
|
||||
```bash
|
||||
# All commands run inside nix develop shell
|
||||
|
||||
# Run API server
|
||||
uvicorn app.main:app --reload
|
||||
cd backend && uvicorn app.main:app --reload
|
||||
|
||||
# Run tests
|
||||
pytest
|
||||
cd backend && pytest
|
||||
|
||||
# Run with coverage
|
||||
pytest --cov=app --cov-report=html
|
||||
cd backend && pytest --cov=app --cov-report=html
|
||||
|
||||
# Check linting
|
||||
ruff check app/
|
||||
cd backend && ruff check app/
|
||||
|
||||
# Format code
|
||||
ruff format app/
|
||||
cd backend && ruff format app/
|
||||
|
||||
# Run migrations
|
||||
alembic upgrade head
|
||||
cd backend && alembic upgrade head
|
||||
|
||||
# Create migration
|
||||
alembic revision --autogenerate -m "description"
|
||||
cd backend && alembic revision --autogenerate -m "description"
|
||||
```
|
||||
|
||||
### NixOS VM Integration Tests
|
||||
```bash
|
||||
# Run all tests (backend, full-stack, performance, security)
|
||||
nix flake check
|
||||
|
||||
# Run specific test
|
||||
nix build .#checks.backend-integration -L
|
||||
nix build .#checks.full-stack -L
|
||||
|
||||
# Interactive debugging
|
||||
nix build .#checks.backend-integration.driverInteractive
|
||||
./result/bin/nixos-test-driver
|
||||
```
|
||||
|
||||
### Frontend
|
||||
|
||||
@@ -654,7 +654,126 @@ The recommended stack (Svelte + Konva.js + FastAPI + PostgreSQL) provides the op
|
||||
- ✅ Developer experience (modern tooling, fast feedback)
|
||||
- ✅ Maintainability (clear architecture, good docs)
|
||||
- ✅ Scalability (can grow from MVP to production)
|
||||
- ✅ Leverages existing setup (Python in shell.nix)
|
||||
- ✅ Leverages existing setup (Python dependencies managed by Nix)
|
||||
|
||||
This stack is production-ready, future-proof, and fully aligned with your Nix deployment requirement.
|
||||
|
||||
---
|
||||
|
||||
## CI/CD Architecture
|
||||
|
||||
### Decision: NixOS VM Tests (No Docker)
|
||||
|
||||
**Chosen Approach:** NixOS VM integration tests using `pkgs.nixosTest`
|
||||
|
||||
**Why NixOS VMs over Docker:**
|
||||
|
||||
| Aspect | Docker Compose | NixOS VMs (Chosen) |
|
||||
|--------|----------------|-------------------|
|
||||
| Isolation | Container (shared kernel) | Full VM (separate kernel) |
|
||||
| Reproducibility | Image tags can drift | `flake.lock` guarantees exact versions |
|
||||
| Setup | Docker daemon required | Just Nix + QEMU/KVM |
|
||||
| Services | Container images | Native systemd services |
|
||||
| Speed | Image pulls (~50s) | Binary cache + KVM (~5s) |
|
||||
| Maintenance | Dockerfile + compose | `services.X.enable = true` |
|
||||
| Cleanup | Manual or scripted | Automatic (VM destroyed) |
|
||||
|
||||
**Key Benefits:**
|
||||
1. **Complete isolation** - Full VM per test, cannot affect host
|
||||
2. **Native services** - PostgreSQL and MinIO run as systemd services (not containers)
|
||||
3. **Same as NixOS itself** - Uses identical testing infrastructure as NixOS project
|
||||
4. **Parallel execution** - 4 VMs run simultaneously (~30s total)
|
||||
5. **Zero Docker dependency** - No Docker daemon, no image registry
|
||||
6. **Perfect reproducibility** - flake.lock guarantees bit-identical environments
|
||||
|
||||
**Implementation:**
|
||||
|
||||
```nix
|
||||
# nixos/tests.nix
|
||||
backend-integration = pkgs.nixosTest {
|
||||
nodes.machine = {
|
||||
services.postgresql.enable = true; # Native systemd service
|
||||
services.minio.enable = true; # Native systemd service
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
machine.wait_for_unit("postgresql.service")
|
||||
machine.wait_for_unit("minio.service")
|
||||
machine.succeed("pytest backend/tests/")
|
||||
'';
|
||||
};
|
||||
```
|
||||
|
||||
**CI Workflow:**
|
||||
- 4 parallel NixOS VMs (backend-integration, full-stack, performance, security)
|
||||
- Linting and unit tests (no VM needed)
|
||||
- Build verification
|
||||
- Total time: ~30 seconds with caching
|
||||
- **Attic binary cache**: Shares build artifacts across CI runs for faster builds
|
||||
|
||||
**Alternative Considered:** Docker Compose
|
||||
- ❌ Rejected due to: Docker daemon dependency, less isolation, image maintenance overhead
|
||||
- Docker would add complexity without benefits (NixOS services are cleaner)
|
||||
|
||||
### Development Environment
|
||||
|
||||
**Decision:** Single `flake.nix` as source of truth (no shell.nix)
|
||||
|
||||
**Structure:**
|
||||
```nix
|
||||
flake.nix
|
||||
├─ devShells.default (Python, Node.js, PostgreSQL client, etc.)
|
||||
├─ packages.backend (production build)
|
||||
├─ packages.frontend (production build)
|
||||
└─ checks.* (NixOS VM tests)
|
||||
```
|
||||
|
||||
**Commands:**
|
||||
```bash
|
||||
nix develop # Enter dev shell
|
||||
nix flake check # Run all VM tests
|
||||
nix build .#backend # Build backend package
|
||||
```
|
||||
|
||||
**Why flake-only:**
|
||||
- Single source of truth (no shell.nix duplication)
|
||||
- Flake lock guarantees reproducibility
|
||||
- Same environment in dev, CI, and production
|
||||
- Modern Nix best practice
|
||||
|
||||
### Test Organization
|
||||
|
||||
**Unit tests:** Fast, no external services (pytest, Vitest)
|
||||
**Integration tests:** NixOS VMs with PostgreSQL + MinIO
|
||||
**E2E tests:** Full-stack VM with running API
|
||||
**Performance tests:** Dedicated VM for benchmarks
|
||||
**Security tests:** Isolated VM for security validation
|
||||
|
||||
All integration tests use **native NixOS services**, not Docker containers.
|
||||
|
||||
### Binary Cache (Attic)
|
||||
|
||||
**Setup:** Self-hosted Attic cache server at `http://127.0.0.1:2343`
|
||||
|
||||
**Purpose:** Share Nix build artifacts across CI runs to significantly speed up builds.
|
||||
|
||||
**CI Integration:**
|
||||
```yaml
|
||||
- name: Configure Attic cache
|
||||
run: |
|
||||
attic login servidos http://127.0.0.1:2343 ${{ secrets.ATTIC_TOKEN }}
|
||||
attic use servidos:webref
|
||||
|
||||
# After successful builds
|
||||
- name: Push to Attic cache
|
||||
run: attic push servidos:webref result
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- VM builds cached (no rebuild if unchanged)
|
||||
- Backend/frontend packages cached
|
||||
- Shared across all CI jobs and developers
|
||||
- Typically reduces build time by 50-70%
|
||||
|
||||
**Configuration:** Secret `ATTIC_TOKEN` must be set in Gitea repository settings.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user