1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00
webvirtcloud/.github/workflows/test.yml
catborise 9f6ce7dc63 ci: the audit checks the packages installations get, not the CI tools
pip-audit audited the whole CI environment, so on Python 3.10 it failed
on filelock 4.1.0, a dependency of pip-audit itself: six CVEs published
on 2026-10-23 are fixed only in filelock 4.2.1, which needs Python 3.11.
No installation gets filelock. The step now audits conf/requirements.txt
and conf/requirements-ldap.txt, resolved for the leg's Python, which is
what the audit is for.
2026-10-24 09:52:07 +03:00

203 lines
7.8 KiB
YAML

name: Test Suite
on:
push:
branches: ['**']
pull_request:
branches: [master]
# the jobs only read the code
permissions:
contents: read
jobs:
test:
name: Python ${{ matrix.python-version }} / ${{ matrix.database }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13']
database: [sqlite]
include:
- python-version: '3.12'
database: postgresql
# Used by the postgresql leg only; settings fall back to sqlite when
# WEBVIRTCLOUD_DB_HOST is empty.
services:
postgres:
image: postgres:17-alpine
env:
POSTGRES_USER: webvirtcloud
POSTGRES_PASSWORD: webvirtcloud
POSTGRES_DB: webvirtcloud
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 5s
--health-timeout 5s
--health-retries 10
env:
WEBVIRTCLOUD_DB_HOST: ${{ matrix.database == 'postgresql' && 'localhost' || '' }}
WEBVIRTCLOUD_DB_PASSWORD: webvirtcloud
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: |
conf/requirements.txt
dev/requirements.txt
# Build headers for the sdist-only bindings (libvirt-python, python-ldap)
# A runner's package mirror sometimes stops answering: time out and
# retry instead of hanging until the job's 6-hour limit
- name: Install System Dependencies
timeout-minutes: 10
run: |
APT="-o Acquire::Retries=3 -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30"
sudo apt-get $APT update -qq
sudo apt-get $APT install -y --no-install-recommends \
libvirt-dev \
libldap2-dev \
libsasl2-dev \
pkg-config \
gcc
- name: Install Python Dependencies
run: |
python -m pip install --upgrade pip setuptools
pip install -r dev/requirements.txt
- name: Configure WebVirtCloud Settings
run: |
cp webvirtcloud/settings.py.template webvirtcloud/settings.py
SECRET_KEY=$(python -c 'import secrets; print(secrets.token_urlsafe(50))')
sed -i "s|^SECRET_KEY = .*|SECRET_KEY = \"${SECRET_KEY}\"|" webvirtcloud/settings.py
- name: Check For Missing Migrations
run: python manage.py makemigrations --check --dry-run
- name: Run Django Migrations
run: python manage.py migrate --noinput
# Includes the vrtManager tests; fails below the coverage floor in pyproject.toml
- name: Run Test Suite
run: |
coverage run manage.py test
coverage report --skip-covered
# The project test runner (webvirtcloud/test_runner.py) on one leg
- name: Run Test Suite In Parallel
if: matrix.python-version == '3.13' && matrix.database == 'sqlite'
run: python manage.py test --parallel
# The per-file report of one leg, to see what a change left untested
- name: Store Coverage Report
if: always() && matrix.python-version == '3.12' && matrix.database == 'sqlite'
run: coverage html -d coverage-html --fail-under=0
- name: Upload Coverage Report
if: always() && matrix.python-version == '3.12' && matrix.database == 'sqlite'
uses: actions/upload-artifact@v4
with:
name: coverage-html
path: coverage-html
- name: Validate OpenAPI Schema
run: python manage.py spectacular --validate --fail-on-warn --file /dev/null
# Security warnings (HSTS, secure cookies) depend on the deployment;
# gate on errors only.
- name: Deployment Checks
run: python manage.py check --deploy --fail-level ERROR
# What installations get, resolved for this Python; not the CI tools
# (their fixes can need a newer Python than the leg's, as filelock's did)
- name: Audit Shipped Packages
run: pip-audit -r conf/requirements.txt -r conf/requirements-ldap.txt
docker:
name: Docker Image
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
# Files that exist in a used checkout and must never enter the image
- name: Plant Secret Sentinels
run: |
mkdir -p data
for f in data/secret_key data/admin_password webvirtcloud/settings.py.bak \
'webvirtcloud/settings.py~' webvirtcloud/.settings.py.swp; do
echo sentinel > "$f"
done
- name: Build Image
run: docker build -t webvirtcloud:ci .
- name: No Runtime Data In Image
run: |
docker run --rm --entrypoint sh webvirtcloud:ci -c '
cd /srv/webvirtcloud
for f in data/secret_key data/admin_password webvirtcloud/settings.py.bak \
"webvirtcloud/settings.py~" webvirtcloud/.settings.py.swp; do
if [ -e "$f" ]; then echo "$f is in the image"; exit 1; fi
done'
# The default profile, and the HTTPS profile behind a TLS proxy
- name: Start Containers
run: |
docker run -d --name wvc -p 127.0.0.1:8080:80 webvirtcloud:ci
docker run -d --name wvc-https -e WEBVIRTCLOUD_HTTPS=1 webvirtcloud:ci
- name: Wait Until Healthy
run: |
for c in wvc wvc-https; do
for i in $(seq 40); do
[ "$(docker inspect -f '{{.State.Health.Status}}' $c)" = healthy ] && continue 2
sleep 5
done
docker inspect -f '{{json .State.Health}}' $c || true
docker exec $c sv status /etc/service/nginx /etc/service/webvirtcloud /etc/service/novnc || true
docker logs $c || true
exit 1
done
- name: Import Compiled Dependencies
run: docker exec wvc /srv/webvirtcloud/venv/bin/python3 -c "import libvirt, ldap, lxml.etree, psycopg"
# Static URLs carry no version: browsers must revalidate them
- name: Static Files Revalidated
run: |
url=http://127.0.0.1:8080/static/css/themes/zephyr.min.css
header() { tr -d '\r' | awk -v h="$1" 'tolower($0) ~ "^" h ":" {sub(/^[^:]*: */, ""); print}'; }
h=$(curl -s -m 5 -D - -o /dev/null "$url")
[ "$(echo "$h" | header cache-control)" = no-cache ] || { echo "$h"; exit 1; }
etag=$(echo "$h" | header etag)
[ -n "$etag" ]
h=$(curl -s -m 5 -D - -o /dev/null -H "If-None-Match: $etag" "$url")
echo "$h" | head -1 | grep -q " 304" || { echo "$h"; exit 1; }
[ "$(echo "$h" | header cache-control)" = no-cache ]
docker exec wvc sh -c 'echo "/* changed */" >> /srv/webvirtcloud/staticfiles/css/themes/zephyr.min.css'
h=$(curl -s -m 5 -D - -o /dev/null -H "If-None-Match: $etag" "$url")
echo "$h" | head -1 | grep -q " 200" || { echo "$h"; exit 1; }
new=$(echo "$h" | header etag)
[ -n "$new" ] && [ "$new" != "$etag" ]
# The browser reaches the console through nginx on the panel's port
- name: Console Through Nginx Only
run: |
[ "$(docker inspect -f '{{json .Config.ExposedPorts}}' webvirtcloud:ci)" = '{"80/tcp":{}}' ]
docker exec wvc grep -qx 'WS_PUBLIC_PORT = 80' /srv/webvirtcloud/webvirtcloud/settings.py
code=$(curl -s -m 5 -o /dev/null -w '%{http_code}' -H 'Connection: Upgrade' -H 'Upgrade: websocket' \
-H 'Sec-WebSocket-Version: 13' -H 'Sec-WebSocket-Key: dGVzdGtleXRlc3RrZXk9PQ==' http://127.0.0.1:8080/novncd/ || true)
[ "$code" = 101 ] || { echo "websocket upgrade through nginx: $code"; exit 1; }