001-reference-board-viewer #1
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