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
|
## Development Environment
|
||||||
|
|
||||||
This project uses Nix for reproducible development environments:
|
This project uses Nix flakes for reproducible development environments:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Enter development shell
|
# Enter development shell (from flake.nix)
|
||||||
nix-shell
|
nix develop
|
||||||
|
|
||||||
# Or use direnv for automatic activation
|
# Or use direnv for automatic activation
|
||||||
echo "use nix" > .envrc
|
direnv allow # .envrc already configured
|
||||||
direnv allow
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Included tools:**
|
**Included tools:**
|
||||||
- Python 3 with setuptools
|
- Python 3.12 with all backend dependencies (FastAPI, SQLAlchemy, pytest, etc.)
|
||||||
- uv (fast Python package manager)
|
- Node.js + npm for frontend development
|
||||||
|
- PostgreSQL client tools
|
||||||
|
- MinIO client
|
||||||
|
- Ruff (Python linter/formatter)
|
||||||
|
- All project dependencies from flake.nix
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
@@ -44,7 +47,19 @@ webref/
|
|||||||
│ ├── tasks-template.md # Task tracking template
|
│ ├── tasks-template.md # Task tracking template
|
||||||
│ └── commands/
|
│ └── commands/
|
||||||
│ └── constitution.md # Constitution amendment workflow
|
│ └── 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
|
└── README.md # This file
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -94,16 +109,37 @@ All code must meet these requirements before merge:
|
|||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
```bash
|
### Unit Tests
|
||||||
# Run tests
|
|
||||||
pytest
|
|
||||||
|
|
||||||
# With coverage report
|
```bash
|
||||||
pytest --cov=webref --cov-report=html
|
# Backend tests
|
||||||
|
cd backend && pytest --cov=app --cov-report=html
|
||||||
|
|
||||||
|
# Frontend tests
|
||||||
|
cd frontend && npm test
|
||||||
|
|
||||||
# Coverage must be ≥80% per Constitutional Principle 2
|
# 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
|
## Contributing
|
||||||
|
|
||||||
1. Read the [constitution](.specify/memory/constitution.md)
|
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)
|
# Clone repository (if not already)
|
||||||
cd /home/jawz/Development/Projects/personal/webref
|
cd /home/jawz/Development/Projects/personal/webref
|
||||||
|
|
||||||
# Enter Nix development shell (installs all dependencies)
|
# Enter Nix development shell (from flake.nix)
|
||||||
nix develop
|
nix develop
|
||||||
|
|
||||||
# Verify tools are available
|
# Verify tools are available
|
||||||
python --version # Should show Python 3.12+
|
python --version # Python 3.12
|
||||||
node --version # Should show Node.js latest
|
node --version # Node.js 20+
|
||||||
psql --version # PostgreSQL client
|
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
|
### Backend
|
||||||
```bash
|
```bash
|
||||||
|
# All commands run inside nix develop shell
|
||||||
|
|
||||||
# Run API server
|
# Run API server
|
||||||
uvicorn app.main:app --reload
|
cd backend && uvicorn app.main:app --reload
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
pytest
|
cd backend && pytest
|
||||||
|
|
||||||
# Run with coverage
|
# Run with coverage
|
||||||
pytest --cov=app --cov-report=html
|
cd backend && pytest --cov=app --cov-report=html
|
||||||
|
|
||||||
# Check linting
|
# Check linting
|
||||||
ruff check app/
|
cd backend && ruff check app/
|
||||||
|
|
||||||
# Format code
|
# Format code
|
||||||
ruff format app/
|
cd backend && ruff format app/
|
||||||
|
|
||||||
# Run migrations
|
# Run migrations
|
||||||
alembic upgrade head
|
cd backend && alembic upgrade head
|
||||||
|
|
||||||
# Create migration
|
# 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
|
### Frontend
|
||||||
|
|||||||
@@ -654,7 +654,126 @@ The recommended stack (Svelte + Konva.js + FastAPI + PostgreSQL) provides the op
|
|||||||
- ✅ Developer experience (modern tooling, fast feedback)
|
- ✅ Developer experience (modern tooling, fast feedback)
|
||||||
- ✅ Maintainability (clear architecture, good docs)
|
- ✅ Maintainability (clear architecture, good docs)
|
||||||
- ✅ Scalability (can grow from MVP to production)
|
- ✅ 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.
|
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