mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-10-24 03:58:05 +09:00
CONTRIBUTING asks contributors to target dev, but every workflow only triggered on PRs into main, so contributor PRs merged with no checks and failures surfaced only after the push to dev.
171 lines
6.5 KiB
YAML
171 lines
6.5 KiB
YAML
name: Test System Utilities
|
|
|
|
on:
|
|
push:
|
|
branches: [dev]
|
|
paths:
|
|
- 'backend/app/utils/system.py'
|
|
- 'backend/app/services/**'
|
|
- 'backend/tests/test_utils_system*.py'
|
|
- '.github/workflows/test-system-utils.yml'
|
|
pull_request:
|
|
branches: [main, dev]
|
|
paths:
|
|
- 'backend/app/utils/system.py'
|
|
- 'backend/app/services/**'
|
|
- 'backend/tests/test_utils_system*.py'
|
|
|
|
# The mocked `unit-tests` job that used to lead this file was removed: it ran
|
|
# `pytest tests/test_utils_system.py`, and Backend CI's
|
|
# `pytest tests -v` already collects that exact file — there is no pytest.ini,
|
|
# addopts, or collect_ignore narrowing it. What is left here is the part
|
|
# Backend CI genuinely cannot do: exercise the package-manager detection
|
|
# against real apt/dnf inside real distro images.
|
|
jobs:
|
|
# ──────────────────────────────────────────────────────────────────
|
|
# Job 1: Integration tests on real distros (no mocks)
|
|
# ──────────────────────────────────────────────────────────────────
|
|
integration-tests:
|
|
name: Integration (${{ matrix.distro }})
|
|
runs-on: ubuntu-latest
|
|
# A wedged apt mirror once held the ubuntu job "in progress" for 2 h, and
|
|
# Version Bump waits on every sibling — fail fast instead of blocking dev.
|
|
timeout-minutes: 15
|
|
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- distro: ubuntu
|
|
image: ubuntu:24.04
|
|
expected_manager: apt
|
|
- distro: debian
|
|
image: debian:12
|
|
expected_manager: apt
|
|
- distro: fedora
|
|
image: fedora:41
|
|
expected_manager: dnf
|
|
- distro: rocky
|
|
image: rockylinux:9
|
|
expected_manager: dnf
|
|
|
|
container:
|
|
image: ${{ matrix.image }}
|
|
|
|
env:
|
|
DEBIAN_FRONTEND: noninteractive
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Install Python & pytest
|
|
shell: bash
|
|
run: |
|
|
if command -v apt-get >/dev/null 2>&1; then
|
|
rm -rf /var/lib/apt/lists/*
|
|
apt-get -o Acquire::Retries=3 -o Acquire::http::Timeout=30 update
|
|
apt-get -o Acquire::Retries=3 -o Acquire::http::Timeout=30 -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold install -y python3 python3-pip
|
|
elif command -v dnf >/dev/null 2>&1; then
|
|
dnf install -y python3 python3-pip
|
|
elif command -v yum >/dev/null 2>&1; then
|
|
yum install -y python3 python3-pip
|
|
fi
|
|
pip3 install --break-system-packages pytest 2>/dev/null || pip3 install pytest
|
|
|
|
- name: Run integration smoke tests
|
|
working-directory: backend
|
|
env:
|
|
EXPECTED_MANAGER: ${{ matrix.expected_manager }}
|
|
run: python3 -m pytest tests/test_utils_system_integration.py -v
|
|
|
|
# ──────────────────────────────────────────────────────────────────
|
|
# Job 2: Audit — grep for raw subprocess patterns in services
|
|
# ──────────────────────────────────────────────────────────────────
|
|
audit-raw-patterns:
|
|
name: Audit Raw Subprocess Patterns
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.11'
|
|
|
|
# The ratchet (plan 75 §G1). The grep steps below check a hand-listed set
|
|
# of files for three known-bad patterns; this counts EVERY raw
|
|
# subprocess call site under backend/app and fails if the total grew.
|
|
# A grep cannot do this honestly — it also matches comments, docstrings
|
|
# and `except subprocess.SubprocessError` — so the census is AST-based.
|
|
- name: Raw subprocess census (ceiling may only go down)
|
|
working-directory: backend
|
|
run: python3 tests/raw_subprocess_census.py
|
|
|
|
- name: Check for raw 'sudo systemctl' calls in migrated services
|
|
run: |
|
|
MIGRATED_FILES=(
|
|
backend/app/services/firewall_service.py
|
|
backend/app/services/security_service.py
|
|
backend/app/services/python_service.py
|
|
backend/app/services/deployment_service.py
|
|
backend/app/services/ftp_service.py
|
|
backend/app/services/nginx_service.py
|
|
backend/app/services/php_service.py
|
|
backend/app/services/ssl_service.py
|
|
)
|
|
|
|
EXIT_CODE=0
|
|
|
|
echo "=== Checking for raw subprocess patterns ==="
|
|
echo ""
|
|
|
|
# Pattern 1: subprocess.run with 'sudo', 'systemctl'
|
|
echo "--- Pattern: 'sudo', 'systemctl' ---"
|
|
for f in "${MIGRATED_FILES[@]}"; do
|
|
if [ -f "$f" ]; then
|
|
MATCHES=$(grep -n "'sudo', 'systemctl'" "$f" || true)
|
|
if [ -n "$MATCHES" ]; then
|
|
echo "FAIL: $f"
|
|
echo "$MATCHES"
|
|
EXIT_CODE=1
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Pattern 2: subprocess.run with dpkg
|
|
echo ""
|
|
echo "--- Pattern: subprocess.run.*dpkg ---"
|
|
for f in "${MIGRATED_FILES[@]}"; do
|
|
if [ -f "$f" ]; then
|
|
MATCHES=$(grep -n "subprocess\.run.*dpkg" "$f" || true)
|
|
if [ -n "$MATCHES" ]; then
|
|
echo "FAIL: $f"
|
|
echo "$MATCHES"
|
|
EXIT_CODE=1
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Pattern 3: os.path.exists('/usr/bin/apt')
|
|
echo ""
|
|
echo "--- Pattern: os.path.exists('/usr/bin/apt') ---"
|
|
for f in "${MIGRATED_FILES[@]}"; do
|
|
if [ -f "$f" ]; then
|
|
MATCHES=$(grep -n "os\.path\.exists.*'/usr/bin/apt'" "$f" || true)
|
|
if [ -n "$MATCHES" ]; then
|
|
echo "FAIL: $f"
|
|
echo "$MATCHES"
|
|
EXIT_CODE=1
|
|
fi
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
if [ $EXIT_CODE -eq 0 ]; then
|
|
echo "All migrated files are clean."
|
|
else
|
|
echo "Raw subprocess patterns found — see failures above."
|
|
fi
|
|
exit $EXIT_CODE
|