Refactor CI/CD workflow to utilize NixOS VM for testing
Some checks failed
Test Suite / test (push) Failing after 16s

- Changed the CI workflow to run tests inside a NixOS virtual machine instead of directly on the runner.
- Updated the NixOS VM configuration to include necessary dependencies and services for testing.
- Added a script to handle test execution within the VM, including setup for Python and Node.js environments.
- Implemented SSH access to the VM for remote operations and streamlined the process of starting and stopping the VM during tests.
- Enhanced the workflow to build the VM and copy the codebase for testing, ensuring a more isolated and consistent testing environment.
This commit is contained in:
Danilo Reyes
2025-12-28 22:53:25 -06:00
parent 2b1a92fb49
commit 4709a05ad4
4 changed files with 191 additions and 134 deletions

View File

@@ -8,90 +8,84 @@ on:
jobs: jobs:
test: test:
runs-on: ubuntu-latest runs-on: nixos
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Setup Nix - name: Build NixOS VM
uses: cachix/install-nix-action@v20
with:
nix: 2.18.1
extra_nix_config: |
experimental-features = nix-command flakes
- name: Install Nix dependencies
run: | run: |
nix profile install nixpkgs#postgresql # Build the VM configuration using nixos-rebuild
nix profile install nixpkgs#python3 # This creates a VM that can be run with QEMU
nix profile install nixpkgs#nodejs_20 nixos-rebuild build-vm \
nix profile install nixpkgs#npm -I nixos-config=./nix/test-vm.nix \
-I nixpkgs=<nixpkgs> \
-o vm-result
- name: Setup Python - name: Start VM
run: | run: |
python3 -m pip install --upgrade pip # Find the VM run script (name may vary)
pip install -r backend/requirements.txt VM_SCRIPT=$(find vm-result/bin -name "run-*-vm" -type f | head -1)
if [ -z "$VM_SCRIPT" ]; then
echo "VM script not found in vm-result/bin"
ls -la vm-result/bin/
exit 1
fi
echo "Starting VM with script: $VM_SCRIPT"
- name: Setup Node.js # Start VM in background
# The VM will expose SSH on port 2222
$VM_SCRIPT > vm.log 2>&1 &
VM_PID=$!
echo "VM_PID=$VM_PID" >> $GITHUB_ENV
# Wait for VM to boot and SSH to be available
echo "Waiting for VM to boot..."
for i in {1..120}; do
if ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=2 \
-p 2222 root@127.0.0.1 "echo 'VM is ready'" 2>/dev/null; then
echo "VM is ready!"
break
fi
if [ $i -eq 120 ]; then
echo "VM failed to start after 4 minutes"
echo "VM log:"
tail -100 vm.log
exit 1
fi
sleep 2
done
- name: Copy code to VM
run: | run: |
cd frontend # Copy the entire repository to the VM
npm ci SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222"
ssh $SSH_OPTS root@127.0.0.1 "mkdir -p /tmp/moviemap"
- name: Start PostgreSQL # Use tar to copy files (more reliable than rsync in CI)
tar --exclude='.git' \
--exclude='node_modules' \
--exclude='__pycache__' \
--exclude='*.pyc' \
--exclude='.pytest_cache' \
--exclude='dist' \
--exclude='vm-result' \
-czf - . | ssh $SSH_OPTS root@127.0.0.1 "cd /tmp/moviemap && tar -xzf -"
- name: Run all tests in VM
run: | run: |
sudo systemctl start postgresql || sudo service postgresql start # Execute the test script inside the VM
sudo -u postgres psql -c "CREATE DATABASE moviemap_test;" SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222"
sudo -u postgres psql -c "CREATE USER moviemap WITH PASSWORD 'test';" ssh $SSH_OPTS root@127.0.0.1 "bash /tmp/moviemap/scripts/run-tests-in-vm.sh"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE moviemap_test TO moviemap;"
sudo -u postgres psql -c "ALTER USER moviemap CREATEDB;"
env:
POSTGRES_HOST: localhost
POSTGRES_PORT: 5432
- name: Run database migrations - name: Stop VM
run: |
cd backend
export POSTGRES_SOCKET_PATH=/var/run/postgresql
export POSTGRES_DB=moviemap_test
export POSTGRES_USER=moviemap
alembic upgrade head
env:
TEST_POSTGRES_DB: moviemap_test
TEST_POSTGRES_USER: moviemap
TEST_POSTGRES_SOCKET_PATH: /var/run/postgresql
- name: Run backend tests
run: |
cd backend
export POSTGRES_SOCKET_PATH=/var/run/postgresql
export POSTGRES_DB=moviemap_test
export POSTGRES_USER=moviemap
export TEST_POSTGRES_DB=moviemap_test
export TEST_POSTGRES_USER=moviemap
export TEST_POSTGRES_SOCKET_PATH=/var/run/postgresql
pytest tests/ -v --cov=app --cov-report=term-missing
env:
# Mock *arr URLs for testing (tests will mock the actual API calls)
RADARR_URL: http://localhost:7878
SONARR_URL: http://localhost:8989
LIDARR_URL: http://localhost:8686
RADARR_API_KEY: test-key
SONARR_API_KEY: test-key
LIDARR_API_KEY: test-key
- name: Build frontend
run: |
cd frontend
npm run build
- name: Run frontend tests
run: |
cd frontend
npm test -- --run
- name: Cleanup
if: always() if: always()
run: | run: |
sudo systemctl stop postgresql || sudo service postgresql stop || true if [ ! -z "$VM_PID" ]; then
kill $VM_PID || true
wait $VM_PID 2>/dev/null || true
fi
# Also try to kill any remaining QEMU processes
pkill -f "moviemap-test-vm" || true

View File

@@ -1,12 +1,20 @@
# NixOS VM configuration for testing Movie Map # NixOS VM configuration for testing Movie Map in CI/CD
# This VM includes PostgreSQL, Radarr, Sonarr, and Lidarr with test data # This VM includes all dependencies needed for testing
# Usage: nixos-rebuild build-vm -I nixos-config=./nix/test-vm.nix
{ config, pkgs, lib, ... }: { config, pkgs, lib, ... }:
{ {
# Enable QEMU guest agent for better VM management imports = [
services.qemuGuest.enable = true; <nixpkgs/nixos/modules/virtualisation/qemu-vm.nix>
];
# VM-specific configuration
virtualisation = {
memorySize = 2048; # 2GB RAM
cores = 2;
graphics = false; # Headless
};
# Networking - allow external access
networking = { networking = {
hostName = "moviemap-test-vm"; hostName = "moviemap-test-vm";
firewall = { firewall = {
@@ -14,13 +22,23 @@
allowedTCPPorts = [ allowedTCPPorts = [
8080 # Movie Map backend 8080 # Movie Map backend
5432 # PostgreSQL 5432 # PostgreSQL
7878 # Radarr 22 # SSH
8989 # Sonarr
8686 # Lidarr
]; ];
}; };
}; };
# Enable SSH for remote access
services.openssh = {
enable = true;
settings = {
PermitRootLogin = "yes";
PasswordAuthentication = true;
};
};
# Set root password for SSH access
users.users.root.password = "test";
# PostgreSQL configuration # PostgreSQL configuration
services.postgresql = { services.postgresql = {
enable = true; enable = true;
@@ -40,63 +58,35 @@
}; };
}; };
# Radarr configuration # Python and Node.js for testing
services.radarr = {
enable = true;
openFirewall = true;
user = "radarr";
group = "radarr";
};
# Sonarr configuration
services.sonarr = {
enable = true;
openFirewall = true;
user = "sonarr";
group = "sonarr";
};
# Lidarr configuration
services.lidarr = {
enable = true;
openFirewall = true;
user = "lidarr";
group = "lidarr";
};
# Create test API keys for *arr services
# These will be set via environment variables in the CI/CD
# For now, we'll create a script that generates them
systemd.services.setup-arr-services = {
description = "Setup *arr services with test API keys";
wantedBy = [ "multi-user.target" ];
after = [ "radarr.service" "sonarr.service" "lidarr.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
# Wait for services to be ready
sleep 10
# Note: In a real setup, you would configure API keys via the *arr APIs
# For testing, we'll use environment variables set by CI/CD
echo "Test VM setup complete"
'';
};
# Environment variables for test configuration
environment.variables = {
TEST_RADARR_URL = "http://localhost:7878";
TEST_SONARR_URL = "http://localhost:8989";
TEST_LIDARR_URL = "http://localhost:8686";
};
# System packages
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
python3
python3Packages.pip
nodejs_20
nodePackages.npm
postgresql
curl curl
jq jq
postgresql git
vim
# Testing tools
python3Packages.pytest
python3Packages.pytest-asyncio
python3Packages.pytest-cov
python3Packages.httpx
]; ];
}
# Create a test user
users.users.test = {
isNormalUser = true;
extraGroups = [ "wheel" ];
password = "test";
openssh.authorizedKeys.keys = [];
};
# Allow passwordless sudo for test user
security.sudo.wheelNeedsPassword = false;
# System configuration
system.stateVersion = "23.11";
}

9
nix/vm-config.nix Normal file
View File

@@ -0,0 +1,9 @@
# VM configuration entry point for CI/CD
{ pkgs, lib, ... }:
{
imports = [
./test-vm.nix
];
}

64
scripts/run-tests-in-vm.sh Executable file
View File

@@ -0,0 +1,64 @@
#!/usr/bin/env bash
# Helper script to run tests inside the VM
# This script is executed inside the VM
set -e
cd /tmp/moviemap
echo "=== Setting up test environment ==="
# Setup Python
cd backend
export PATH="/root/.local/bin:$PATH"
python3 -m pip install --user --upgrade pip
python3 -m pip install --user -r requirements.txt
# Setup Node.js
cd ../frontend
npm ci
echo "=== Waiting for PostgreSQL ==="
# Wait for PostgreSQL to be ready
for i in {1..30}; do
if sudo -u postgres psql -c "SELECT 1;" > /dev/null 2>&1; then
echo "PostgreSQL is ready"
break
fi
if [ $i -eq 30 ]; then
echo "PostgreSQL failed to start"
exit 1
fi
sleep 1
done
echo "=== Running database migrations ==="
cd ../backend
export POSTGRES_SOCKET_PATH=/var/run/postgresql
export POSTGRES_DB=moviemap_test
export POSTGRES_USER=moviemap
export PATH="/root/.local/bin:$PATH"
alembic upgrade head
echo "=== Running backend tests ==="
export TEST_POSTGRES_DB=moviemap_test
export TEST_POSTGRES_USER=moviemap
export TEST_POSTGRES_SOCKET_PATH=/var/run/postgresql
export RADARR_URL=http://localhost:7878
export SONARR_URL=http://localhost:8989
export LIDARR_URL=http://localhost:8686
export RADARR_API_KEY=test-key
export SONARR_API_KEY=test-key
export LIDARR_API_KEY=test-key
export PATH="/root/.local/bin:$PATH"
pytest tests/ -v --cov=app --cov-report=term-missing
echo "=== Building frontend ==="
cd ../frontend
npm run build
echo "=== Running frontend tests ==="
npm test -- --run
echo "=== All tests completed successfully ==="