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 763f1525ca feat(locale): the panel is translated into Simplified Chinese
The Chinese catalog had not been extracted since 2023: of the panel's
1749 messages, 878 were missing and 455 carried old fuzzy guesses, and
some of those marked done said something else ("Delete API token" read
"Delete SSH key").

Every message is translated again, with one glossary for the panel's
terms, placeholders and markup checked against the English, and the
panel offers Simplified Chinese again. The CI check covers it too.

One message of the VM page (the console password's help) never reached
any catalog: its escaped quotes cut it short at extraction. It is
written with single quotes now, and translated in both catalogs.
2026-11-02 23:03:09 +03:00

217 lines
8.7 KiB
YAML

name: Test Suite
# a branch with a pull request (also one stacked on another) is tested by its
# pull_request run; push runs for every branch tested each commit twice
on:
push:
branches: [master]
pull_request:
# 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 \
gettext
- 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
# Each catalog of LANGUAGES translates every message, and its compiled
# .mo is the .po's: a changed text needs its translations
- name: Check Translation Catalogs
if: matrix.python-version == '3.12' && matrix.database == 'sqlite'
run: |
python manage.py makemessages -l tr -l zh --no-obsolete --ignore=staticfiles -v0
for dir in locale/tr/LC_MESSAGES locale/zh/LC_MESSAGES; do
msgfmt --check -o /tmp/django.mo "$dir/django.po"
missing=$(msgattrib --untranslated "$dir/django.po"; msgattrib --only-fuzzy "$dir/django.po")
if [ -n "$missing" ]; then echo "$missing"; echo "::error file=$dir/django.po::untranslated or fuzzy messages"; exit 1; fi
cmp <(msgunfmt "$dir/django.mo") <(msgunfmt /tmp/django.mo) || { echo "::error file=$dir/django.mo::not compiled from django.po (compilemessages)"; exit 1; }
done
# 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":{}}' ]
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; }