Refactor CI/CD workflow to utilize NixOS VM for testing
Some checks failed
Test Suite / test (push) Failing after 16s
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:
@@ -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
|
||||||
|
|
||||||
|
|||||||
116
nix/test-vm.nix
116
nix/test-vm.nix
@@ -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
9
nix/vm-config.nix
Normal 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
64
scripts/run-tests-in-vm.sh
Executable 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 ==="
|
||||||
|
|
||||||
Reference in New Issue
Block a user